This blog is part of the ADK Masterclass - Hands-On Series. While we introduced MCP in the Third Party Tools module, this module provides a comprehensive exploration of the protocol's architecture, when to use it, real-world use cases, and step-by-step tutorials for production deployments.
Before MCP, connecting AI agents to external services meant writing custom integrations for each API—different authentication methods, data formats, and error handling for every service. MCP solves this by providing a universal "adapter" that works with any compatible service, similar to how USB-C replaced dozens of proprietary charging cables.
View Code on GitHubTable of Contents
1. What is MCP?
The Model Context Protocol (MCP) is an open standard developed by Anthropic that enables AI agents to connect to external data sources and tools through a unified interface. Think of it as a "USB-C for AI"—one protocol that works with hundreds of services, eliminating the need for custom integrations.
In the traditional approach, if we wanted our agent to interact with GitHub, Slack, and a database, we'd need to write three separate integrations with different APIs, authentication methods, and error handling. With MCP, we write one integration pattern that works with any MCP-compatible server.
2. Why Use MCP?
MCP provides several critical advantages for building production-ready agents:
- Standardization: One protocol to connect to any service. Learn it once, use it everywhere.
- Ecosystem: 100+ pre-built MCP servers available—GitHub, Slack, databases, web scrapers, and more.
- Security: Built-in authentication and permission models. Control exactly what your agent can access.
- Discoverability: Tools are dynamically discovered at runtime. The agent learns what's available automatically.
- Maintainability: When an external API changes, only the MCP server needs updating—not your agent code.
3. When to Use MCP
MCP is the right choice when:
| Use MCP When... | Consider Alternatives When... |
|---|---|
| An MCP server already exists for your service | You need a simple, one-off API call |
| You need to connect to multiple external services | The service has no MCP server and building one is complex |
| You want dynamic tool discovery | You need very low latency (MCP adds overhead) |
| Security and permission control is important | You're building a simple prototype |
4. Real-World Use Cases
MCP shines when agents need to orchestrate multiple services. Here are three patterns we see in production:
DevOps Automation Agent
An agent that monitors GitHub pull requests, automatically creates Jira tickets for bug fixes, and notifies the team on Slack. By using MCP servers for each service, the agent can be extended to support GitLab or Linear without changing the core logic.
Research Assistant
An agent that scrapes web content using Firecrawl MCP, summarizes findings, and organizes them in Notion. The standardized MCP interface means switching from Notion to Obsidian only requires changing the MCP server configuration.
Customer Support Agent
An agent that pulls customer history from a database MCP and ticket context from Zendesk MCP to provide personalized, context-aware responses to support requests.
5. MCP Architecture
MCP follows a client-server architecture where your agent acts as the client and external services run as servers:
The MCPToolset in ADK handles this entire flow automatically:
from google.adk.agents import Agent
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
# MCPToolset handles:
# 1. Connection management
# 2. Tool discovery
# 3. Request/response serialization
# 4. Error handling
root_agent = Agent(
model="gemini-2.5-flash",
name="mcp_agent",
instruction="You have access to external tools via MCP.",
tools=[
MCPToolset(connection_params=...) # Tools auto-discovered
],
)
5.1. Connection Types
MCP supports two primary connection types:
HTTP Connection (StreamableHTTPServerParams)
For cloud-hosted MCP servers with HTTP endpoints:
from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPServerParams
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
# HTTP connection for remote MCP servers
mcp_tools = MCPToolset(
connection_params=StreamableHTTPServerParams(
url="https://api.example.com/mcp/",
headers={
"Authorization": f"Bearer {API_TOKEN}",
"X-Custom-Header": "value"
},
),
)
# Best for:
# - Cloud-hosted services (GitHub Copilot, hosted APIs)
# - Production deployments
# - Services requiring authentication headers
Stdio Connection (StdioConnectionParams)
For local MCP servers that run as child processes:
from google.adk.tools.mcp_tool.mcp_session_manager import StdioConnectionParams
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
from mcp import StdioServerParameters
# Stdio connection for local MCP servers
mcp_tools = MCPToolset(
connection_params=StdioConnectionParams(
server_params=StdioServerParameters(
command="npx",
args=["-y", "firecrawl-mcp"],
env={"API_KEY": API_KEY}
),
timeout=30,
),
)
# Best for:
# - NPM-based MCP servers
# - Local development
# - Servers without hosted endpoints
5.2. MCP Primitives
MCP defines three core primitives that servers can expose:
| Primitive | Description | Example |
|---|---|---|
| Tools | Executable functions the agent can call | search_repos, create_issue |
| Resources | Read-only data the agent can access | File contents, database schemas |
| Prompts | Pre-defined prompt templates | Code review template, summary format |
6. Tutorial
Let's build two practical MCP-powered agents step by step.
Prerequisites
- Google AI Studio API Key
- Python 3.9+ installed
- Node.js 18+ (for npm-based MCP servers)
- GitHub Personal Access Token (for GitHub MCP)
Step 1: Setup Environment
# Create and activate virtual environment
python3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install dependencies
pip install google-adk python-dotenv
# Set environment variables
export GOOGLE_API_KEY=your_google_api_key
export GITHUB_TOKEN=your_github_token
6.1. Building a GitHub Code Review Agent
We'll build an agent that can review pull requests, analyze code changes, and suggest improvements using the GitHub MCP server.
Step 2: Create the Agent
import os
import asyncio
from dotenv import load_dotenv
from google.adk.agents import Agent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPServerParams
from google.genai import types
load_dotenv()
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", "")
# Create the GitHub MCP-powered agent
github_agent = Agent(
model="gemini-2.5-flash",
name="github_reviewer",
instruction="""You are an expert code reviewer with access to GitHub.
When asked to review a PR:
1. Fetch the PR details and diff
2. Analyze the code changes for:
- Potential bugs or errors
- Code style and best practices
- Security vulnerabilities
- Performance issues
3. Provide constructive feedback with specific line references
4. Suggest improvements with code examples when helpful
Be thorough but constructive. Focus on significant issues first.""",
tools=[
MCPToolset(
connection_params=StreamableHTTPServerParams(
url="https://api.githubcopilot.com/mcp/",
headers={
"Authorization": f"Bearer {GITHUB_TOKEN}",
"X-MCP-Toolsets": "repos,pulls,issues",
"X-MCP-Readonly": "true"
},
),
)
],
)
async def review_pr(repo: str, pr_number: int):
"""Review a GitHub pull request."""
session_service = InMemorySessionService()
runner = Runner(
agent=github_agent,
app_name="github_reviewer",
session_service=session_service,
)
session = await session_service.create_session(
app_name="github_reviewer",
user_id="developer"
)
message = types.Content(
role="user",
parts=[types.Part(text=f"Please review PR #{pr_number} in the {repo} repository. Focus on code quality, potential bugs, and security issues.")]
)
print(f"Reviewing PR #{pr_number} in {repo}...")
print("-" * 50)
async for event in runner.run_async(
session_id=session.id,
user_id="developer",
new_message=message
):
if hasattr(event, 'content') and event.content:
for part in event.content.parts:
if hasattr(part, 'text') and part.text:
print(part.text, end="", flush=True)
print("\n" + "-" * 50)
if __name__ == "__main__":
# Example: Review a PR
asyncio.run(review_pr("google/adk-python", 42))
Step 3: Run the Agent
# Run the code review agent
python github_reviewer.py
# Or use ADK web interface
adk web
6.2. Building a Multi-MCP Research Agent
Now let's build a more advanced agent that combines multiple MCP servers—GitHub for code analysis and Firecrawl for web research.
import os
import asyncio
from dotenv import load_dotenv
from google.adk.agents import Agent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
from google.adk.tools.mcp_tool.mcp_session_manager import (
StreamableHTTPServerParams,
StdioConnectionParams
)
from mcp import StdioServerParameters
from google.genai import types
load_dotenv()
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", "")
FIRECRAWL_API_KEY = os.environ.get("FIRECRAWL_API_KEY", "")
# GitHub MCP for code and repository access
github_mcp = MCPToolset(
connection_params=StreamableHTTPServerParams(
url="https://api.githubcopilot.com/mcp/",
headers={
"Authorization": f"Bearer {GITHUB_TOKEN}",
"X-MCP-Toolsets": "repos,code",
"X-MCP-Readonly": "true"
},
),
)
# Firecrawl MCP for web scraping
firecrawl_mcp = MCPToolset(
connection_params=StdioConnectionParams(
server_params=StdioServerParameters(
command="npx",
args=["-y", "firecrawl-mcp"],
env={"FIRECRAWL_API_KEY": FIRECRAWL_API_KEY}
),
timeout=60,
),
)
# Multi-MCP Research Agent
research_agent = Agent(
model="gemini-2.5-flash",
name="research_agent",
instruction="""You are a technical research assistant with access to:
1. **GitHub** - For code repositories, documentation, and open source projects
2. **Firecrawl** - For scraping and analyzing web content
When researching a topic:
1. First, search GitHub for relevant repositories and code examples
2. Then, scrape relevant documentation or blog posts for context
3. Synthesize findings into a comprehensive answer
4. Include code examples and links to sources
Always cite your sources and provide actionable insights.""",
tools=[github_mcp, firecrawl_mcp],
)
async def research(query: str):
"""Conduct research using multiple MCP servers."""
session_service = InMemorySessionService()
runner = Runner(
agent=research_agent,
app_name="research_agent",
session_service=session_service,
)
session = await session_service.create_session(
app_name="research_agent",
user_id="researcher"
)
message = types.Content(
role="user",
parts=[types.Part(text=query)]
)
print(f"Researching: {query}")
print("=" * 60)
async for event in runner.run_async(
session_id=session.id,
user_id="researcher",
new_message=message
):
if hasattr(event, 'content') and event.content:
for part in event.content.parts:
if hasattr(part, 'text') and part.text:
print(part.text, end="", flush=True)
print("\n" + "=" * 60)
if __name__ == "__main__":
asyncio.run(research(
"What are the best practices for building AI agents with ADK? "
"Include code examples and links to official documentation."
))
7. Best Practices
Building production MCP agents requires attention to security, performance, and reliability. Here's a quick reference:
from google.adk.agents import Agent
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
from google.adk.tools.mcp_tool.mcp_session_manager import (
StreamableHTTPServerParams,
StdioConnectionParams
)
from mcp import StdioServerParameters
# GitHub MCP (HTTP)
github_mcp = MCPToolset(
connection_params=StreamableHTTPServerParams(
url="https://api.githubcopilot.com/mcp/",
headers={"Authorization": f"Bearer {GITHUB_TOKEN}"},
),
)
# Firecrawl MCP (Stdio)
firecrawl_mcp = MCPToolset(
connection_params=StdioConnectionParams(
server_params=StdioServerParameters(
command="npx",
args=["-y", "firecrawl-mcp"],
env={"FIRECRAWL_API_KEY": FIRECRAWL_KEY}
),
),
)
# Agent with multiple MCP servers
root_agent = Agent(
model="gemini-2.5-flash",
name="multi_mcp_agent",
instruction="""You have access to:
- GitHub for code and repository operations
- Firecrawl for web scraping and content extraction
Choose the appropriate tool based on the user's request.""",
tools=[github_mcp, firecrawl_mcp],
)
Security
- Use read-only mode: Set
X-MCP-Readonly: truewhen agents don't need write access. This prevents accidental modifications. - Secure credentials: Use environment variables for API keys, never hardcode them in your source code.
- Scope tokens minimally: Create API tokens with only the permissions your agent needs.
Performance
- Set appropriate timeouts: Stdio connections can hang; always set a timeout (30-60 seconds is typical).
- Filter tools: Only expose the tools your agent needs. Fewer tools = faster discovery and clearer agent behavior.
- Reuse connections: When possible, reuse MCP connections across requests rather than creating new ones.
Reliability
- Handle errors gracefully: MCP tools can fail due to network issues, rate limits, or API errors. Your agent should handle these gracefully.
- Respect rate limits: Be aware of rate limits on external services. Consider implementing backoff strategies.
- Log MCP interactions: Log tool calls and responses for debugging and monitoring in production.
Tool Filtering Example
# Only expose specific tools from the MCP server
mcp_tools = MCPToolset(
connection_params=StreamableHTTPServerParams(
url="https://api.githubcopilot.com/mcp/",
headers={
"Authorization": f"Bearer {GITHUB_TOKEN}",
"X-MCP-Toolsets": "repos,issues", # Only repos and issues tools
"X-MCP-Readonly": "true" # Read-only mode
},
),
# Optional: filter tools by name pattern
tool_filter=lambda tool: tool.name.startswith("get_")
)
Next Steps
With MCP covered, we now move to Core Components—the internal mechanisms that power ADK agents, starting with Session, State & Memory.