Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,10 @@ lint:
uv run ruff check .

leaderboard:
uv run -- streamlit run agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py
uv run -- streamlit run agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py

adk_basic_ui:
uv run adk web agents_mcp_usage/basic_mcp

adk_multi_ui:
uv run adk web agents_mcp_usage/multi_mcp
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ Run an Agent framework script e.g.:
- `uv run agents_mcp_usage/basic_mcp/basic_mcp_use/oai-agent_mcp.py`
- Requires `OPENAI_API_KEY` by default

Check console or Logfire for output
- Launch the ADK web UI for visual interaction with the agents:
- `make adk_basic_ui`

Check console, Logfire, or the ADK web UI for output

## Project Overview

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

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

# Pydantic-AI example
uv run agents_mcp_usage/basic_mcp/basic_mcp_use/pydantic_mcp.py

# Launch ADK web UI for visual interaction
make adk_basic_ui
```

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

# Run multi-model benchmarking
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
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

# Launch the evaluation dashboard
uv run streamlit run agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py
Expand Down Expand Up @@ -258,7 +264,7 @@ uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py

# Multi-model parallel benchmarking
uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py \
--models "gemini-2.5-pro,gemini-2.0-flash,gemini-2.5-flash-preview-04-17" \
--models "gemini-2.5-pro-preview-06-05,gemini-2.0-flash,gemini-2.5-flash" \
--runs 5 \
--parallel \
--output-dir ./results
Expand Down Expand Up @@ -288,6 +294,11 @@ A key advantage highlighted is flexibility; MCP allows developers to more easily
```bash
make install
```

To use the ADK web UI, run:
```bash
make adk_basic_ui
```
3. Set up your environment variables in a `.env` file:
```
LOGFIRE_TOKEN=your_logfire_token
Expand Down
14 changes: 12 additions & 2 deletions agents_mcp_usage/basic_mcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ Each script demonstrates how to connect to a single local MCP server and use it

# Run the Google ADK example
uv run agents_mcp_usage/basic_mcp/basic_mcp_use/adk_mcp.py

## Launch ADK web UI for visual interaction
make adk_basic_ui
```

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

**File:** `adk_mcp.py`

This example demonstrates how to use MCP with Google's Agent Development Kit (ADK).
This example demonstrates how to use MCP with Google's Agent Development Kit (ADK 1.3.0).

```bash
uv run agents_mcp_usage/basic_mcp/basic_mcp_use/adk_mcp.py
```

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

To run with the ADK web UI:
```bash
make adk_basic_ui
```

### LangGraph

**File:** `langgraph_mcp.py`
Expand Down
3 changes: 3 additions & 0 deletions agents_mcp_usage/basic_mcp/basic_mcp_use/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
# Basic MCP usage examples package

# Allow discovery of the ADK agent via the ADK web UI
from .adk_mcp import root_agent as root_agent
173 changes: 137 additions & 36 deletions agents_mcp_usage/basic_mcp/basic_mcp_use/adk_mcp.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
"""ADK-based agent using MCP protocol for tool access.

This module provides an ADK agent that can be used both in the ADK web UI
and directly from the command line. The agent uses MCP tools to access
external functionality.
"""

import asyncio
import os
import logfire
from typing import List, Tuple, Any

from dotenv import load_dotenv
from google.adk.agents.llm_agent import LlmAgent
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset, StdioServerParameters
from google.adk.tools.mcp_tool.mcp_toolset import (
MCPToolset,
StdioServerParameters,
StdioConnectionParams,
)
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types

from agents_mcp_usage.utils import get_mcp_server_path

load_dotenv()

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

# Global variable to store toolset instances for cleanup
_ACTIVE_TOOLSETS = []

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

This function sets up the MCP server, creates an LLM agent, and runs it
with a specified query. It also handles the cleanup of the MCP server
connection.
# Initialize the MCP tools and agent for ADK web UI
async def init_mcp_toolset() -> Tuple[List[Any], List[MCPToolset]]:
"""Initialize and return the MCP tools and toolsets.

Args:
query: The query to run the agent with.
Returns:
Tuple[List[Any], List[MCPToolset]]: A tuple of (tools, toolsets)
"""
# Set up MCP server connection
global _ACTIVE_TOOLSETS

# Use absolute path to MCP server based on project root
mcp_server_path = get_mcp_server_path("example_server.py")

server_params = StdioServerParameters(
command="uv",
args=["run", "mcp_servers/example_server.py", "stdio"],
args=["run", str(mcp_server_path), "stdio"],
)
connection_params = StdioConnectionParams(server_params=server_params)
toolset = MCPToolset(connection_params=connection_params)

tools, exit_stack = await MCPToolset.from_server(connection_params=server_params)
print(f"Connected to MCP server. Found {len(tools)} tools.")
try:
tools = await toolset.get_tools()
_ACTIVE_TOOLSETS.append(toolset)
return tools, [toolset]
except Exception as e:
# Clean up in case of initialization error
try:
await toolset.close()
except Exception:
pass
raise e


async def cleanup_toolsets():
"""Clean up any active MCP toolset connections."""
global _ACTIVE_TOOLSETS

for toolset in _ACTIVE_TOOLSETS:
try:
await toolset.close()
print("MCP toolset connection closed.")
except asyncio.CancelledError:
print("MCP cleanup cancelled - this is normal")
except Exception as e:
print(f"Warning: Error during toolset cleanup: {e}")

_ACTIVE_TOOLSETS = []


# Define a before_agent_callback to attach tools
async def attach_tools_callback(callback_context):
"""Callback to attach tools to the agent before it runs.

# Create the agent
root_agent = LlmAgent(
model="gemini-2.5-pro-preview-03-25",
name="mcp_adk_assistant",
tools=tools,
)
Args:
callback_context: The callback context from ADK.

# Set up session
session_service = InMemorySessionService()
session = session_service.create_session(
app_name="mcp_adk_app",
user_id="aginns",
)
Returns:
None: The callback doesn't modify the content.
"""
await ensure_tools_attached()
return None


# This is the agent that will be imported by the ADK web UI
root_agent = LlmAgent(
model="gemini-2.0-flash",
name="mcp_adk_assistant",
instruction="You are an assistant that uses MCP tools to help users.",
before_agent_callback=attach_tools_callback, # This ensures tools are attached
)

# Create the runner
runner = Runner(
app_name="mcp_adk_app",
agent=root_agent,
session_service=session_service,
)

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

print("Running agent...")

# Function to dynamically attach tools to the agent
async def ensure_tools_attached():
"""Ensures that tools are attached to the agent before it's used."""
global TOOLS_ATTACHED

if not TOOLS_ATTACHED:
try:
tools, _ = await init_mcp_toolset()
print(f"✓ Connected to MCP server. Found {len(tools)} tools.")
# Update the agent's tools
root_agent.tools = tools
TOOLS_ATTACHED = True
except Exception as e:
print(f"Error attaching MCP tools: {e}")
# Set empty tools to avoid errors
root_agent.tools = []


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

This function sets up a runner for the agent and runs it with a specified query.
It also handles the cleanup of the MCP server connection.

Args:
query: The query to run the agent with.
"""
try:
# Ensure tools are attached to the agent
await ensure_tools_attached()

# Set up session with async service
session_service = InMemorySessionService()
session = await session_service.create_session(
app_name="mcp_adk_app",
user_id="aginns",
)

# Create the runner using the globally defined agent
runner = Runner(
app_name="mcp_adk_app",
agent=root_agent,
session_service=session_service,
)

# Format the query as content
content = types.Content(role="user", parts=[types.Part(text=query)])

print("Running agent...")
events_async = runner.run_async(
session_id=session.id, user_id=session.user_id, new_message=content
)

async for event in events_async:
print(f"Event received: {event}")

except Exception as e:
print(f"Error during agent execution: {e}")
print(f"Error type: {type(e).__name__}")
raise
finally:
print("Closing MCP server connection...")
await exit_stack.aclose()
print("Cleanup complete.")
# Clean up MCP toolsets to prevent asyncio shutdown errors
await cleanup_toolsets()
print("Agent execution completed successfully.")


if __name__ == "__main__":
Expand Down
6 changes: 4 additions & 2 deletions agents_mcp_usage/basic_mcp/basic_mcp_use/langgraph_mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

from agents_mcp_usage.utils import get_mcp_server_path

load_dotenv()

# Configure logging if LOGFIRE_TOKEN is set
Expand All @@ -21,11 +23,11 @@
# Create server parameters for stdio connection
server = StdioServerParameters(
command="uv",
args=["run", "mcp_servers/example_server.py", "stdio"],
args=["run", str(get_mcp_server_path("example_server.py")), "stdio"],
)

model = ChatGoogleGenerativeAI(
model="gemini-2.5-pro-preview-03-25", google_api_key=os.getenv("GEMINI_API_KEY")
model="gemini-2.5-pro-preview-06-05", google_api_key=os.getenv("GEMINI_API_KEY")
)


Expand Down
4 changes: 3 additions & 1 deletion agents_mcp_usage/basic_mcp/basic_mcp_use/oai-agent_mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from agents.mcp import MCPServerStdio
from dotenv import load_dotenv

from agents_mcp_usage.utils import get_mcp_server_path

load_dotenv()

# Configure Logfire
Expand All @@ -26,7 +28,7 @@ async def main(query: str = "Greet Andrew and give him the current time") -> Non
async with MCPServerStdio(
params={
"command": "uv",
"args": ["run", "mcp_servers/example_server.py", "stdio"],
"args": ["run", str(get_mcp_server_path("example_server.py")), "stdio"],
}
) as server:
# Initialise the agent with the server
Expand Down
6 changes: 4 additions & 2 deletions agents_mcp_usage/basic_mcp/basic_mcp_use/pydantic_mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from pydantic_ai import Agent
from pydantic_ai.mcp import MCPServerStdio

from agents_mcp_usage.utils import get_mcp_server_path

load_dotenv()

# Configure logging to logfire if LOGFIRE_TOKEN is set in environment
Expand All @@ -16,11 +18,11 @@
command="uv",
args=[
"run",
"mcp_servers/example_server.py",
str(get_mcp_server_path("example_server.py")),
"stdio",
],
)
agent = Agent("gemini-2.5-pro-preview-03-25", mcp_servers=[server])
agent = Agent("gemini-2.5-pro-preview-06-05", mcp_servers=[server])
Agent.instrument_all()


Expand Down
Loading