Skip to content

Commit 0ba54fc

Browse files
authored
Merge pull request #7 from andrewginns/update-to-adk-1_3
Update to match Google ADK 1.3 patterns
2 parents fc556f5 + 265db91 commit 0ba54fc

File tree

17 files changed

+1377
-1081
lines changed

17 files changed

+1377
-1081
lines changed

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,10 @@ lint:
66
uv run ruff check .
77

88
leaderboard:
9-
uv run -- streamlit run agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py
9+
uv run -- streamlit run agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py
10+
11+
adk_basic_ui:
12+
uv run adk web agents_mcp_usage/basic_mcp
13+
14+
adk_multi_ui:
15+
uv run adk web agents_mcp_usage/multi_mcp

README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ Run an Agent framework script e.g.:
3838
- `uv run agents_mcp_usage/basic_mcp/basic_mcp_use/oai-agent_mcp.py`
3939
- Requires `OPENAI_API_KEY` by default
4040

41-
Check console or Logfire for output
41+
- Launch the ADK web UI for visual interaction with the agents:
42+
- `make adk_basic_ui`
43+
44+
Check console, Logfire, or the ADK web UI for output
4245

4346
## Project Overview
4447

@@ -55,7 +58,7 @@ This project aims to teach:
5558

5659
- **[agents_mcp_usage/basic_mcp/](agents_mcp_usage/basic_mcp/)** - Single MCP server integration examples
5760
- **basic_mcp_use/** - Contains basic examples of single MCP usage:
58-
- `adk_mcp.py` - Example of using MCP with Google's Agent Development Kit (ADK)
61+
- `adk_mcp.py` - Example of using MCP with Google's Agent Development Kit (ADK 1.3.0)
5962
- `langgraph_mcp.py` - Example of using MCP with LangGraph
6063
- `oai-agent_mcp.py` - Example of using MCP with OpenAI Agents
6164
- `pydantic_mcp.py` - Example of using MCP with Pydantic-AI
@@ -154,6 +157,9 @@ uv run agents_mcp_usage/basic_mcp/basic_mcp_use/oai-agent_mcp.py
154157

155158
# Pydantic-AI example
156159
uv run agents_mcp_usage/basic_mcp/basic_mcp_use/pydantic_mcp.py
160+
161+
# Launch ADK web UI for visual interaction
162+
make adk_basic_ui
157163
```
158164

159165
More details on basic MCP implementation can be found in the [basic_mcp README](agents_mcp_usage/basic_mcp/README.md).
@@ -218,7 +224,7 @@ uv run agents_mcp_usage/multi_mcp/multi_mcp_use/pydantic_mcp.py
218224
uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py
219225

220226
# Run multi-model benchmarking
221-
uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py --models "gemini-2.5-pro,gemini-2.0-flash" --runs 5 --parallel
227+
uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py --models "gemini-2.5-pro-preview-06-05,gemini-2.0-flash" --runs 5 --parallel
222228

223229
# Launch the evaluation dashboard
224230
uv run streamlit run agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py
@@ -258,7 +264,7 @@ uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py
258264

259265
# Multi-model parallel benchmarking
260266
uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py \
261-
--models "gemini-2.5-pro,gemini-2.0-flash,gemini-2.5-flash-preview-04-17" \
267+
--models "gemini-2.5-pro-preview-06-05,gemini-2.0-flash,gemini-2.5-flash" \
262268
--runs 5 \
263269
--parallel \
264270
--output-dir ./results
@@ -288,6 +294,11 @@ A key advantage highlighted is flexibility; MCP allows developers to more easily
288294
```bash
289295
make install
290296
```
297+
298+
To use the ADK web UI, run:
299+
```bash
300+
make adk_basic_ui
301+
```
291302
3. Set up your environment variables in a `.env` file:
292303
```
293304
LOGFIRE_TOKEN=your_logfire_token

agents_mcp_usage/basic_mcp/README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ Each script demonstrates how to connect to a single local MCP server and use it
2121

2222
# Run the Google ADK example
2323
uv run agents_mcp_usage/basic_mcp/basic_mcp_use/adk_mcp.py
24+
25+
## Launch ADK web UI for visual interaction
26+
make adk_basic_ui
2427
```
2528

2629
4. Check the console output or Logfire for results.
@@ -155,18 +158,25 @@ The sequence diagram illustrates the temporal flow of interactions between the u
155158

156159
**File:** `adk_mcp.py`
157160

158-
This example demonstrates how to use MCP with Google's Agent Development Kit (ADK).
161+
This example demonstrates how to use MCP with Google's Agent Development Kit (ADK 1.3.0).
159162

160163
```bash
161164
uv run agents_mcp_usage/basic_mcp/basic_mcp_use/adk_mcp.py
162165
```
163166

164167
Key features:
165-
- Uses `MCPToolset` for connecting to the MCP server
168+
- Uses `MCPToolset` with `StdioConnectionParams` for MCP server connection
166169
- Configures a Gemini model using ADK's `LlmAgent`
167170
- Sets up session handling and runner for agent execution
171+
- Implements proper resource management for MCP connections
172+
- Supports ADK web UI through module exports
168173
- Includes Logfire instrumentation for tracing
169174

175+
To run with the ADK web UI:
176+
```bash
177+
make adk_basic_ui
178+
```
179+
170180
### LangGraph
171181

172182
**File:** `langgraph_mcp.py`
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
# Basic MCP usage examples package
2+
3+
# Allow discovery of the ADK agent via the ADK web UI
4+
from .adk_mcp import root_agent as root_agent

agents_mcp_usage/basic_mcp/basic_mcp_use/adk_mcp.py

Lines changed: 137 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
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+
"""
7+
18
import asyncio
29
import os
310
import logfire
11+
from typing import List, Tuple, Any
412

513
from dotenv import load_dotenv
614
from google.adk.agents.llm_agent import LlmAgent
7-
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset, StdioServerParameters
15+
from google.adk.tools.mcp_tool.mcp_toolset import (
16+
MCPToolset,
17+
StdioServerParameters,
18+
StdioConnectionParams,
19+
)
820
from google.adk.runners import Runner
921
from google.adk.sessions import InMemorySessionService
1022
from google.genai import types
1123

24+
from agents_mcp_usage.utils import get_mcp_server_path
25+
1226
load_dotenv()
1327

1428
# Set API key for Google AI API from environment variable
@@ -18,62 +32,149 @@
1832
logfire.configure(send_to_logfire="if-token-present", service_name="adk-basic-mcp")
1933
logfire.instrument_mcp()
2034

35+
# Global variable to store toolset instances for cleanup
36+
_ACTIVE_TOOLSETS = []
2137

22-
async def main(query: str = "Greet Andrew and give him the current time") -> None:
23-
"""Runs the agent with a given query.
2438

25-
This function sets up the MCP server, creates an LLM agent, and runs it
26-
with a specified query. It also handles the cleanup of the MCP server
27-
connection.
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.
2842
29-
Args:
30-
query: The query to run the agent with.
43+
Returns:
44+
Tuple[List[Any], List[MCPToolset]]: A tuple of (tools, toolsets)
3145
"""
32-
# Set up MCP server connection
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+
3351
server_params = StdioServerParameters(
3452
command="uv",
35-
args=["run", "mcp_servers/example_server.py", "stdio"],
53+
args=["run", str(mcp_server_path), "stdio"],
3654
)
55+
connection_params = StdioConnectionParams(server_params=server_params)
56+
toolset = MCPToolset(connection_params=connection_params)
3757

38-
tools, exit_stack = await MCPToolset.from_server(connection_params=server_params)
39-
print(f"Connected to MCP server. Found {len(tools)} tools.")
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.
4090
41-
# Create the agent
42-
root_agent = LlmAgent(
43-
model="gemini-2.5-pro-preview-03-25",
44-
name="mcp_adk_assistant",
45-
tools=tools,
46-
)
91+
Args:
92+
callback_context: The callback context from ADK.
4793
48-
# Set up session
49-
session_service = InMemorySessionService()
50-
session = session_service.create_session(
51-
app_name="mcp_adk_app",
52-
user_id="aginns",
53-
)
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+
)
54108

55-
# Create the runner
56-
runner = Runner(
57-
app_name="mcp_adk_app",
58-
agent=root_agent,
59-
session_service=session_service,
60-
)
61109

62-
# Run the agent with a query
63-
content = types.Content(role="user", parts=[types.Part(text=query)])
110+
# Flag to track if tools have been attached
111+
TOOLS_ATTACHED = False
64112

65-
print("Running agent...")
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+
131+
132+
async def main(query: str = "Greet Andrew and give him the current time") -> None:
133+
"""Runs the agent with a given query.
134+
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.
137+
138+
Args:
139+
query: The query to run the agent with.
140+
"""
66141
try:
142+
# Ensure tools are attached to the agent
143+
await ensure_tools_attached()
144+
145+
# Set up session with async service
146+
session_service = InMemorySessionService()
147+
session = await session_service.create_session(
148+
app_name="mcp_adk_app",
149+
user_id="aginns",
150+
)
151+
152+
# Create the runner using the globally defined agent
153+
runner = Runner(
154+
app_name="mcp_adk_app",
155+
agent=root_agent,
156+
session_service=session_service,
157+
)
158+
159+
# Format the query as content
160+
content = types.Content(role="user", parts=[types.Part(text=query)])
161+
162+
print("Running agent...")
67163
events_async = runner.run_async(
68164
session_id=session.id, user_id=session.user_id, new_message=content
69165
)
70166

71167
async for event in events_async:
72168
print(f"Event received: {event}")
169+
170+
except Exception as e:
171+
print(f"Error during agent execution: {e}")
172+
print(f"Error type: {type(e).__name__}")
173+
raise
73174
finally:
74-
print("Closing MCP server connection...")
75-
await exit_stack.aclose()
76-
print("Cleanup complete.")
175+
# Clean up MCP toolsets to prevent asyncio shutdown errors
176+
await cleanup_toolsets()
177+
print("Agent execution completed successfully.")
77178

78179

79180
if __name__ == "__main__":

agents_mcp_usage/basic_mcp/basic_mcp_use/langgraph_mcp.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
from mcp import ClientSession, StdioServerParameters
1010
from mcp.client.stdio import stdio_client
1111

12+
from agents_mcp_usage.utils import get_mcp_server_path
13+
1214
load_dotenv()
1315

1416
# Configure logging if LOGFIRE_TOKEN is set
@@ -21,11 +23,11 @@
2123
# Create server parameters for stdio connection
2224
server = StdioServerParameters(
2325
command="uv",
24-
args=["run", "mcp_servers/example_server.py", "stdio"],
26+
args=["run", str(get_mcp_server_path("example_server.py")), "stdio"],
2527
)
2628

2729
model = ChatGoogleGenerativeAI(
28-
model="gemini-2.5-pro-preview-03-25", google_api_key=os.getenv("GEMINI_API_KEY")
30+
model="gemini-2.5-pro-preview-06-05", google_api_key=os.getenv("GEMINI_API_KEY")
2931
)
3032

3133

agents_mcp_usage/basic_mcp/basic_mcp_use/oai-agent_mcp.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from agents.mcp import MCPServerStdio
66
from dotenv import load_dotenv
77

8+
from agents_mcp_usage.utils import get_mcp_server_path
9+
810
load_dotenv()
911

1012
# Configure Logfire
@@ -26,7 +28,7 @@ async def main(query: str = "Greet Andrew and give him the current time") -> Non
2628
async with MCPServerStdio(
2729
params={
2830
"command": "uv",
29-
"args": ["run", "mcp_servers/example_server.py", "stdio"],
31+
"args": ["run", str(get_mcp_server_path("example_server.py")), "stdio"],
3032
}
3133
) as server:
3234
# Initialise the agent with the server

agents_mcp_usage/basic_mcp/basic_mcp_use/pydantic_mcp.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from pydantic_ai import Agent
66
from pydantic_ai.mcp import MCPServerStdio
77

8+
from agents_mcp_usage.utils import get_mcp_server_path
9+
810
load_dotenv()
911

1012
# Configure logging to logfire if LOGFIRE_TOKEN is set in environment
@@ -16,11 +18,11 @@
1618
command="uv",
1719
args=[
1820
"run",
19-
"mcp_servers/example_server.py",
21+
str(get_mcp_server_path("example_server.py")),
2022
"stdio",
2123
],
2224
)
23-
agent = Agent("gemini-2.5-pro-preview-03-25", mcp_servers=[server])
25+
agent = Agent("gemini-2.5-pro-preview-06-05", mcp_servers=[server])
2426
Agent.instrument_all()
2527

2628

0 commit comments

Comments
 (0)