How to Implement the MCP Protocol for Tool Integration
The MCP (Modular Communication Protocol) enables LLM-based agents to discover, invoke, and manage external tools through a standardized JSON schema and HTTP API, implemented via a three-layer architecture of tool schemas, server proxies, and client-side loaders.
To implement the MCP protocol for tool integration, you must bridge the gap between large language models and external capabilities using a declarative schema and async execution model. The bojieli/ai-agent-book repository provides a complete reference implementation that separates tool declaration from execution, allowing agents to dynamically load and call Python functions over HTTP without modifying core agent logic.
Understanding the MCP Architecture
The implementation follows a three-layer architecture that decouples tool definition from execution:
| Layer | Responsibility | Main Implementation |
|---|---|---|
| Tool Schema | Declares each tool’s name, input/output JSON schema, and endpoint type. | mcp_tool_schema.json (generated by the server) |
| MCP Server | Hosts concrete tool implementations behind a lightweight HTTP API. It loads the schema, registers executors, and forwards calls to the actual logic. | mcp_server_proxy.py in chapter8/gaia-experience/AWorld/env/virtualpc-mcp/mcp_server/mcp_server_proxy/src/mcp_server_proxy/ |
| Agent Side Loader | Reads the server’s schema, builds a client façade, and injects the tools into the LLM’s toolbox. | agent.load_mcp_tools() in chapter4/agent-with-event-trigger/agent.py |
Step 1 – Define the Tool Schema
Create a JSON entry describing the tool’s contract following OpenAPI-style parameters (for requests) and responses (for results). The server merges all tool definitions into mcp_tool_schema.json at startup.
YouTube Subtitle Crawler Example
{
"name": "youtube_subtitle_crawler",
"description": "Fetch subtitles for a YouTube video given its URL.",
"parameters": {
"type": "object",
"properties": {
"url": { "type": "string", "description": "YouTube video URL" },
"language": { "type": "string", "default": "en", "description": "Subtitle language code" }
},
"required": ["url"]
},
"responses": {
"type": "object",
"properties": {
"subtitle_text": { "type": "string", "description": "Full subtitle transcript" }
}
}
}
Step 2 – Implement the Server-Side Tool
Each MCP tool lives in a server package (e.g., execution-tools, perception-tools). Implement a class with an async run method that receives the parsed JSON payload and returns a JSON-serializable response.
# chapter8/gaia-experience/AWorld/env/virtualpc-mcp/mcp_server/mcp_server_proxy/src/mcp_server_proxy/tools/youtube_subtitle_crawler.py
class YouTubeSubtitleCrawler:
async def run(self, payload: dict) -> dict:
url = payload["url"]
lang = payload.get("language", "en")
# Insert actual YouTube API or scraping logic here
subtitle = await fetch_subtitle(url, lang)
return {"subtitle_text": subtitle}
Step 3 – Configure the MCP Server
Register the class in mcp_servers_config.yaml (located under mcp_server_proxy/src/configs/). The loader reads this configuration to instantiate MCPServerExecutor instances for each tool.
# mcp_servers_config.yaml
youtube_subtitle_crawler:
module: tools.youtube_subtitle_crawler
class: YouTubeSubtitleCrawler
host: 0.0.0.0
port: 8001
Step 4 – Start the MCP Server
Launch the server via run_with_experience.py in chapter8/gaia-experience. The startup sequence performs three critical actions:
- Loads the consolidated tool schema from
mcp_tool_schema.json. - Instantiates executors defined in
mcp_servers_config.yaml. - Exposes the unified endpoint
http://HOST:PORT/mcpthat accepts JSON RPC calls:
{
"tool_name": "youtube_subtitle_crawler",
"arguments": { "url": "https://www.youtube.com/watch?v=abc123", "language": "en" }
}
The proxy forwards requests to the appropriate executor and returns responses to callers.
Step 5 – Load MCP Tools in the Agent
On the agent side, invoke await agent.load_mcp_tools() as implemented in chapter4/agent-with-event-trigger/agent.py. This method:
- Contacts the MCP server’s
/mcpendpoint viaMCPServerProxy. - Retrieves the tool schema and builds thin Python wrappers for each tool.
- Populates
self.toolsso LLM prompts can reference tools by name.
# chapter4/agent-with-event-trigger/agent.py
class Agent:
async def load_mcp_tools(self):
from mcp_server_proxy.mcp_server_proxy import MCPServerProxy
proxy = MCPServerProxy(self.mcp_server_url, self.mcp_server_token)
await proxy.fetch_schema() # Pulls the JSON schema
await proxy.register_tools(self) # Creates callable stubs
logger.info("MCP tools loaded")
Step 6 – Invoke Tools from LLM Prompts
Once loaded, the agent can execute tools directly. The LLM emits a function-call JSON block that the agent parses and forwards to the MCP proxy.
# chapter4/agent-with-event-trigger/example_with_mcp.py
async def demo():
await agent.load_mcp_tools()
result = await agent.tools["youtube_subtitle_crawler"](
url="https://www.youtube.com/watch?v=abc123",
language="en"
)
print(result["subtitle_text"])
The agent automatically serializes the call into MCP JSON RPC format, sends it over HTTP, and returns the JSON response to the caller.
Best Practices for MCP Implementation
Follow these guidelines when implementing the MCP protocol for production tool integration:
- Stateless tool implementations – Design each
runmethod to be stateless for horizontal scaling; each request must be independent. - Explicit JSON schemas – Define strict
parametersandresponsesschemas to guarantee type safety on both client and server. - Versioned schema files – Maintain versioned
mcp_tool_schema.jsonfiles to enable backward-compatible updates when tools evolve. - Secure token authentication – Use
MCP_SERVER_TOKENto prevent unauthorized tool usage via the HTTP endpoint. - Asynchronous execution – Always implement
async def runto keep the agent responsive during I/O-heavy operations.
Summary
To implement the MCP protocol for tool integration:
- Define tool contracts using OpenAPI-style JSON schemas that specify parameters and responses.
- Implement tool logic as async Python classes with a
runmethod that accepts and returns dictionaries. - Configure tools in
mcp_servers_config.yamland launch themcp_server_proxy.pyto expose an HTTP API. - Load tools dynamically in the agent using
agent.load_mcp_tools()which fetches schemas and creates callable stubs. - Invoke tools through the agent’s toolbox dictionary, which handles JSON RPC serialization and HTTP transport automatically.
Frequently Asked Questions
What is the MCP protocol used for in AI agents?
The MCP (Modular Communication Protocol) provides a standardized interface for LLM-based agents to discover and invoke external tools without hard-coding function definitions. It separates tool declaration (JSON schema) from execution (HTTP API), allowing developers to add new capabilities to agents by simply deploying new MCP servers.
How does the MCP server handle security and authentication?
According to the ai-agent-book implementation, the MCPServerProxy class accepts a token parameter (mcp_server_token) that must match the server’s configured secret. This token is validated on every request to the /mcp endpoint, preventing unauthorized tool invocation while allowing distributed deployment of tool servers.
What format should the MCP tool schema follow?
The schema follows OpenAPI-style specifications with parameters (input) and responses (output) objects. Each tool definition must include a name, description, and JSON Schema definitions for its arguments and return values, enabling automatic validation and type inference on both the server and agent sides.
Can I implement synchronous tools with the MCP protocol?
While the protocol technically supports synchronous execution, the reference implementation in agent.py and mcp_server_proxy.py uses async def run methods exclusively. You should implement tools as asynchronous functions to prevent blocking the agent’s event loop during network I/O or heavy computation, ensuring the agent remains responsive to other events.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →