"MCP in practice: giving your agents hands"
Before the Model Context Protocol, every agent framework had its own tool format, and every tool had to be rewritten for every framework. N frameworks × M tools = a lot of glue code that nobody wanted to maintain.
MCP collapses that into a protocol: a tool provider ships one MCP server, and any MCP-capable client — Claude, ADK agents, your own loop — can discover and call its tools over a standard transport. USB-C for agent capabilities, as the cliché goes. After several months of running MCP servers in real projects, here's what the cliché leaves out.
What a server actually looks like
A minimal server is embarrassingly small. With the Python SDK:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("deploys")
@mcp.tool()
def get_deploy_status(service: str) -> str:
"""Returns the status of the most recent deploy for a service."""
return check_ci(service)
mcp.run()
The client lists tools at connect time, the model sees their schemas, and calls flow over JSON-RPC. Resources (readable data) and prompts (reusable templates) ride the same channel.
Lessons from running it in anger
1. Tool inventories bloat context. Connect three MCP servers and your model is suddenly staring at 60 tool schemas before the user says a word. Filter aggressively — expose the five tools this agent needs, not the fifty the server offers. Some clients now lazy-load schemas on demand; if yours doesn't, curate by hand.
2. The security model is "trust the server", so don't. An MCP server is arbitrary code with a friendly handshake. Tool descriptions are injected straight into your model's context, which makes a malicious server a prompt-injection vector with a badge. Pin versions, review what you install, and run third-party servers with the least privilege you can get away with.
3. Design tools for models, not for APIs. The temptation is to mirror your REST API one-to-one. Resist it. Models do better with fewer, chunkier tools that match intents (find_customer, summarize_ticket_thread) than with thin wrappers over CRUD endpoints. Every tool result should answer "what would the model want to do next?" — include IDs it can chain, omit fields it can't use.
4. Errors are part of the interface. A good MCP tool returns machine-readable failure that steers the model: what went wrong, and what to try instead. Silent empty results are how agents spiral.
Where this composes with ADK
Google ADK speaks MCP through its MCPToolset — point it at a server and the tools appear alongside native Python tools. That's the promise landing: write the integration once, use it from whichever orchestration layer wins. The frameworks are becoming interchangeable; the tools are becoming the asset.
Build your moat one well-designed MCP server at a time.