1
- """ADK-based agent using MCP protocol for tool access."""
1
+ """ADK-based agent using MCP protocol for tool access.
2
+
3
+ This module provides an ADK agent that can be used both in the ADK web UI
4
+ and directly from the command line. The agent uses MCP tools to access
5
+ external functionality.
6
+ """
2
7
3
8
import asyncio
4
9
import os
5
10
import logfire
11
+ from typing import List , Tuple , Any
6
12
7
13
from dotenv import load_dotenv
8
14
from google .adk .agents .llm_agent import LlmAgent
15
21
from google .adk .sessions import InMemorySessionService
16
22
from google .genai import types
17
23
24
+ from agents_mcp_usage .utils import get_mcp_server_path
25
+
18
26
load_dotenv ()
19
27
20
28
# Set API key for Google AI API from environment variable
24
32
logfire .configure (send_to_logfire = "if-token-present" , service_name = "adk-basic-mcp" )
25
33
logfire .instrument_mcp ()
26
34
35
+ # Global variable to store toolset instances for cleanup
36
+ _ACTIVE_TOOLSETS = []
37
+
38
+
39
+ # Initialize the MCP tools and agent for ADK web UI
40
+ async def init_mcp_toolset () -> Tuple [List [Any ], List [MCPToolset ]]:
41
+ """Initialize and return the MCP tools and toolsets.
42
+
43
+ Returns:
44
+ Tuple[List[Any], List[MCPToolset]]: A tuple of (tools, toolsets)
45
+ """
46
+ global _ACTIVE_TOOLSETS
47
+
48
+ # Use absolute path to MCP server based on project root
49
+ mcp_server_path = get_mcp_server_path ("example_server.py" )
50
+
51
+ server_params = StdioServerParameters (
52
+ command = "uv" ,
53
+ args = ["run" , str (mcp_server_path ), "stdio" ],
54
+ )
55
+ connection_params = StdioConnectionParams (server_params = server_params )
56
+ toolset = MCPToolset (connection_params = connection_params )
57
+
58
+ try :
59
+ tools = await toolset .get_tools ()
60
+ _ACTIVE_TOOLSETS .append (toolset )
61
+ return tools , [toolset ]
62
+ except Exception as e :
63
+ # Clean up in case of initialization error
64
+ try :
65
+ await toolset .close ()
66
+ except Exception :
67
+ pass
68
+ raise e
69
+
70
+
71
+ async def cleanup_toolsets ():
72
+ """Clean up any active MCP toolset connections."""
73
+ global _ACTIVE_TOOLSETS
74
+
75
+ for toolset in _ACTIVE_TOOLSETS :
76
+ try :
77
+ await toolset .close ()
78
+ print ("MCP toolset connection closed." )
79
+ except asyncio .CancelledError :
80
+ print ("MCP cleanup cancelled - this is normal" )
81
+ except Exception as e :
82
+ print (f"Warning: Error during toolset cleanup: { e } " )
83
+
84
+ _ACTIVE_TOOLSETS = []
85
+
86
+
87
+ # Define a before_agent_callback to attach tools
88
+ async def attach_tools_callback (callback_context ):
89
+ """Callback to attach tools to the agent before it runs.
90
+
91
+ Args:
92
+ callback_context: The callback context from ADK.
93
+
94
+ Returns:
95
+ None: The callback doesn't modify the content.
96
+ """
97
+ await ensure_tools_attached ()
98
+ return None
99
+
100
+
101
+ # This is the agent that will be imported by the ADK web UI
102
+ root_agent = LlmAgent (
103
+ model = "gemini-2.0-flash" ,
104
+ name = "mcp_adk_assistant" ,
105
+ instruction = "You are an assistant that uses MCP tools to help users." ,
106
+ before_agent_callback = attach_tools_callback , # This ensures tools are attached
107
+ )
108
+
109
+
110
+ # Flag to track if tools have been attached
111
+ TOOLS_ATTACHED = False
112
+
113
+
114
+ # Function to dynamically attach tools to the agent
115
+ async def ensure_tools_attached ():
116
+ """Ensures that tools are attached to the agent before it's used."""
117
+ global TOOLS_ATTACHED
118
+
119
+ if not TOOLS_ATTACHED :
120
+ try :
121
+ tools , _ = await init_mcp_toolset ()
122
+ print (f"✓ Connected to MCP server. Found { len (tools )} tools." )
123
+ # Update the agent's tools
124
+ root_agent .tools = tools
125
+ TOOLS_ATTACHED = True
126
+ except Exception as e :
127
+ print (f"Error attaching MCP tools: { e } " )
128
+ # Set empty tools to avoid errors
129
+ root_agent .tools = []
130
+
27
131
28
132
async def main (query : str = "Greet Andrew and give him the current time" ) -> None :
29
133
"""Runs the agent with a given query.
30
134
31
- This function sets up the MCP server, creates an LLM agent, and runs it
32
- with a specified query. It also handles the cleanup of the MCP server
33
- connection.
135
+ This function sets up a runner for the agent and runs it with a specified query.
136
+ It also handles the cleanup of the MCP server connection.
34
137
35
138
Args:
36
139
query: The query to run the agent with.
37
140
"""
38
- toolset = None
39
141
try :
40
- # Set up MCP server connection with enhanced error handling
41
- server_params = StdioServerParameters (
42
- command = "uv" ,
43
- args = ["run" , "mcp_servers/example_server.py" , "stdio" ],
44
- )
45
-
46
- connection_params = StdioConnectionParams (server_params = server_params )
47
- toolset = MCPToolset (connection_params = connection_params )
48
- tools = await toolset .get_tools ()
49
- print (f"✓ Connected to MCP server. Found { len (tools )} tools." )
50
-
51
- # Create the agent
52
- root_agent = LlmAgent (
53
- model = "gemini-2.0-flash" ,
54
- name = "mcp_adk_assistant" ,
55
- tools = tools ,
56
- )
142
+ # Ensure tools are attached to the agent
143
+ await ensure_tools_attached ()
57
144
58
145
# Set up session with async service
59
146
session_service = InMemorySessionService ()
@@ -62,7 +149,7 @@ async def main(query: str = "Greet Andrew and give him the current time") -> Non
62
149
user_id = "aginns" ,
63
150
)
64
151
65
- # Create the runner
152
+ # Create the runner using the globally defined agent
66
153
runner = Runner (
67
154
app_name = "mcp_adk_app" ,
68
155
agent = root_agent ,
@@ -85,16 +172,8 @@ async def main(query: str = "Greet Andrew and give him the current time") -> Non
85
172
print (f"Error type: { type (e ).__name__ } " )
86
173
raise
87
174
finally :
88
- # Clean up MCP toolset to prevent asyncio shutdown errors
89
- if toolset :
90
- print ("Closing MCP server connection..." )
91
- try :
92
- await toolset .close ()
93
- print ("MCP connection closed successfully." )
94
- except asyncio .CancelledError :
95
- print ("MCP cleanup cancelled - this is normal" )
96
- except Exception as e :
97
- print (f"Warning: Error during cleanup: { e } " )
175
+ # Clean up MCP toolsets to prevent asyncio shutdown errors
176
+ await cleanup_toolsets ()
98
177
print ("Agent execution completed successfully." )
99
178
100
179
0 commit comments