MCP Tool Integration#
The run_shell / read_file / write_file from CH02 are native tools — both the tool definition and the implementation are written by you, and every new capability has to be built from scratch. This chapter introduces MCP: a standard protocol that plugs externally written community tools into the same dispatch flow alongside native tools, solving the tool-extension problem.
3.1 Why MCP Is Needed#
Pain Point: Hand-Written Tools Don’t Scale#
Native tools are one-offs. If you also want to:
- Connect to the GitHub API
- Operate Slack
- Query a database
- Run a scraping framework
Each one has to be implemented, maintained, and tested from scratch. The same tool has to be rewritten by every agent; tools you write can’t be reused by anyone else.
MCP Is a USB Standard for Tools#
MCP (Model Context Protocol) is a tool standard pushed by Anthropic. Think of it as “USB for LLMs”:
- Anyone who writes an MCP server produces a plug-and-play toolset
- Any MCP-aware client (Claude Desktop, Claude Code, your own agent) can plug right in
- Tool definitions, execution, and return formats are all standardized
Community-written MCP servers already cover fetch, filesystem, github, slack, postgres, puppeteer, various cloud APIs… When you need a new capability, check whether the community has an MCP server first; if yes, plug it in directly; if not, write your own (also as an MCP server, so others can use it too).
Where MCP Servers Run: Local vs Remote#
The MCP spec supports two transports, which determine which machine the server runs on:
| Transport | Where the server runs | Communication | Typical examples |
|---|---|---|---|
| stdio | Local subprocess | stdin/stdout JSON-RPC | mcp_server_fetch, mcp_server_filesystem |
| HTTP / SSE | Remote cloud service | HTTPS streamable connection | Notion, Linear, Cloudflare, various SaaS-hosted MCP servers |
From the agent’s point of view there is no difference between the two — only the transport differs; tool discovery, tool calls, and return formats are identical. Our minimal-agent uses stdio to connect to a local mcp_server_fetch, which is what the architecture diagram in 3.2 below depicts; to switch to a remote MCP server, only the transport section of the agent code needs to change.
Native vs MCP: How to Choose#
| Dimension | Native tools | MCP tools |
|---|---|---|
| Location | in-process (same Python process) | Separate subprocess |
| Communication | Direct function call | stdio JSON-RPC |
| Latency | Microseconds | Milliseconds |
| Extension | Edit code and restart | Swap the server, agent stays untouched |
| Sharing | Tied to this agent | Shared across multiple clients |
| Ecosystem | Maintain it yourself | Use existing community servers |
| Best for | High-frequency, private logic | Third-party integrations, cross-client sharing |
Bottom line: core, private, high-frequency → native; general, third-party, shareable → MCP. Our minimal agent runs both side by side.
3.2 The Role of MCP in the Agent#
Workflow After Integrating MCP Tools#
%%{init: {'sequence': {'noteAlign': 'left'}}}%%
sequenceDiagram
participant MCP as MCP Server (subprocess)
box AI Agent
participant Executor
end
participant Model
Executor->>MCP: (1) Start server (subprocess)
Executor->>MCP: (2) handshake
MCP-->>Executor: server info
Executor->>MCP: (3) list_tools (what tools do you have?)
MCP-->>Executor: (4) [fetch(url, max_length, ...)]
Note over Executor: (5) Got mcp tools<br/>(6) Merge with native tools into all_tools
loop Agent loop (until Model returns end_turn)
Executor->>Model: (7) messages list + all_tools
Model-->>Executor: (8) tool_use(name, args)
alt name in mcp_tool_names -> go MCP
Executor->>MCP: (9) call_tool(name, args)
MCP-->>Executor: (10) tool_result
else go native
Note over Executor: (9) Call native tools<br/>(10) Get tool_result
end
Note over Executor: (11) Append tool_result to messages list, back to (7)
endThree key points:
- MCP tools are discovered dynamically (steps 1-4) — At startup, the Executor asks the MCP server once via
list_tools: “what tools do you have?”, and gets back the MCP tool list. You don’t need to hard-code what the server provides; swap in a different MCP server and the agent automatically gains different capabilities, without changing a single line of code. - Merged with native tools into a single tool list (steps 5-6) — Once the MCP tools are retrieved, they’re merged with native tools into
all_toolsand handed to the Model. This is completely transparent to the Model — what it sees is a single unified tool list; it has no idea which ones are native and which are MCP. - Dispatch routing (step 9) — The Executor checks whether the
nameintool_useis inmcp_tool_namesto decide whether to route to MCP or native. The “Dispatch Routing Pattern” section below covers the implementation.
Dispatch Routing Pattern#
When the Model returns a tool_use, the executor needs to decide whether this tool is an MCP tool or a native tool, in order to choose between executing via the MCP server or directly calling a Python function itself.
Here is an example implementation that puts every MCP tool name into a set as the basis for the decision:
| |
This if/else is the branching node at step 9 in the workflow diagram — the tool_name the Model returns comes in, the executor glances at the set, and automatically takes the right path.
3.3 Try It#
$ python minimal_agent.py
[init] native tools: ['run_shell', 'read_file', 'write_file']
[init] mcp tools: ['fetch']
you> Fetch the contents of example.com and summarize
[mcp] fetch({'url': 'https://example.com'})
claude> This page is the IANA-reserved example domain ...
you> List the current directory
[native] run_shell({'command': 'ls'})
claude> The directory contains minimal_agent.py, README.md ...The two kinds of tools coexist, and the Model doesn’t need to know which is which — it only reads the description to decide who to call.
Recap#
By this point you should understand:
- What MCP solves — reusability of the tool ecosystem, sharing across clients
- The native vs MCP decision logic — high-frequency private vs general integration
- Dynamic tool discovery — handshake at startup to fetch the list, no need to hard-code
- Dispatch routing — use a set to separate the two kinds of tools, transparent to the Model
But whether native or MCP, so far the agent is still one-shot — one conversation runs and it’s done. The next chapter, CH04, upgrades it to a multi-turn conversation using a REPL.