主题
Day 8 — MCP 协议 (Model Context Protocol)
读完这章你能获得什么:理解 MCP 协议的设计思想、消息格式、客户端/服务端实现,掌握如何让 Agent 调用外部工具服务,以及如何将自己的工具暴露给外部使用。
一、前情提要
经过 Day 1-7,我们的 Agent 已经是一个功能完整的 AI 助手了。但它的工具都是自己写的——如果社区里有人已经写好了一个"数据库查询工具"或"日历管理工具",我们怎么用?
反过来,如果别人想用我们 Agent 的工具,怎么提供给他们?
这就是 MCP(Model Context Protocol,模型上下文协议) 要解决的问题——让不同的 Agent 系统之间能共享工具。
二、生活类比:跨公司合作协议
想象两家公司要合作:
甲方(你的公司) 乙方(外部公司)
│ │
│ "你们有翻译服务吗?" │
│────────────────────────────────→ │
│ │
│ "有,我有翻译、摘要两个服务" │
│←──────────────────────────────── │
│ │
│ "请帮我翻译这段话" │
│────────────────────────────────→ │
│ │
│ "翻译结果:..." │
│←──────────────────────────────── │要想合作顺利,需要一份标准合同格式——双方都按这个格式来沟通,不会产生歧义。
MCP 就是这份"标准合同格式":
- 合同编号:每次请求都有唯一 ID,方便对账
- 合同类型:请求/回复/通知三种
- 格式规范:JSON-RPC 2.0(一种广泛使用的 RPC 标准)
角色对应:
| 合作角色 | MCP 角色 | 说明 |
|---|---|---|
| 甲方(需要服务的人) | MCPClient | 连接外部 MCP Server,使用它的工具 |
| 乙方(提供服务的人) | MCPServer | 把本地工具暴露出去,让别人调用 |
| 标准合同格式 | MCPMessage (JSON-RPC 2.0) | 统一的通信协议 |
| 甲方的员工使用乙方的工具 | RemoteTool | 远程工具的本地代理 |
三、核心概念详解
3.1 MCPMessage——标准合同
MCP 基于 JSON-RPC 2.0 协议。如果你没听过 JSON-RPC,可以这样理解:
普通的 JSON 就是"一堆数据",JSON-RPC 是"一套请求-响应的规则"——规定了"怎么问、怎么答、怎么报错"。
三种消息类型:
| 类型 | 说明 | 类比 |
|---|---|---|
| 请求(Request) | "请帮我做这件事" | 甲方发出的需求单 |
| 响应(Response) | "做好了,结果是..." | 乙方返回的成果 |
| 通知(Notification) | "告诉你一声,不用回复" | 信息通报 |
消息长什么样?
json
// 请求:询问有什么工具
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}
// 响应:告诉你有哪些工具
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{"name": "translate", "description": "翻译文本", ...}
]
}
}
// 错误响应:出问题了
{
"jsonrpc": "2.0",
"id": 1,
"error": {"code": -32601, "message": "Method not found"}
}3.2 MCPClient——甲方(使用外部工具)
MCPClient 连接到外部 MCP Server,做三件事:
- 发现:问对方"你有什么工具?"(
tools/list) - 代理:为每个远程工具创建一个
RemoteTool本地代理 - 调用:通过代理调用远程工具(
tools/call)
RemoteTool 是什么?
远程工具不在本地,但 Agent 不应该关心"这个工具是本地的还是远程的"。所以 MCPClient 为每个远程工具创建一个 RemoteTool 代理——它实现了和本地 Tool 一样的接口,Agent 像用本地工具一样用它。
Agent 的视角:
registry.get("translate") → 得到一个 Tool 对象
tool.execute(text="hello") → 得到结果 "你好"
Agent 根本不知道(也不需要知道)这个工具实际上是远程调用的!3.3 MCPServer——乙方(暴露本地工具)
MCPServer 做的事情正好相反——把我们本地 ToolRegistry 里的工具暴露出去:
| 请求方法 | MCPServer 的处理 |
|---|---|
initialize | 返回服务器信息(名称、版本) |
tools/list | 遍历 ToolRegistry,返回所有工具的定义 |
tools/call | 找到工具、执行、返回结果 |
3.4 通信方式:Stdio
当前实现使用 Stdio(标准输入/输出)通信——MCPClient 启动 MCPServer 作为子进程,通过 stdin/stdout 收发 JSON 消息。
为什么用 Stdio?
| 方式 | 优点 | 缺点 |
|---|---|---|
| Stdio | 零配置,不需要端口;安全,不暴露网络 | 只能本机通信 |
| HTTP/WebSocket | 可以跨机器 | 需要配置端口、网络 |
教学阶段用 Stdio 最简单。理解了协议后,把传输层换成 HTTP 非常容易——因为消息格式(JSON-RPC)是一样的。
四、MCP 的全景流程
Agent 的视角:ToolRegistry 里有 4 个工具(calculator、datetime、translate、summarize),其中两个是本地的、两个是远程的——但 Agent 不需要区分。
五、动手实验指南
5.1 运行示例
bash
# 启动 MCP Server(作为工具提供方)
python -m miniclaw.mcp.server
# 在另一个终端,用 MCP Client 连接并调用
python -m miniclaw.mcp.client5.2 改一改,看看会怎样
实验 1:在 MCP Server 端注册一个新工具(比如 random_number),然后用 Client 发现并调用它。
实验 2:故意调用一个不存在的工具名,观察 JSON-RPC 的错误响应格式。
实验 3:在 MCPClient 连接后,打印 discover_tools() 返回的列表,看看远程工具的元信息(名称、描述、参数)。
5.3 运行测试
bash
pytest miniclaw/mcp/ -v六、常见问题 FAQ
Q1:MCP 和普通的 HTTP API 有什么区别?
A:普通 API 是你自己定义的接口格式,每个服务都不一样。MCP 是一个标准化的协议——所有遵循 MCP 的工具服务都用同样的方式发现和调用。类比:普通 API 像各国的电源插头(每个不一样),MCP 像 USB-C(统一标准)。
Q2:RemoteTool 和本地 Tool 性能差异大吗?
A:远程调用必然比本地调用慢(多了进程间通信或网络延迟)。但在 AI Agent 场景中,LLM 调用本身就要 1-10 秒,工具调用多几十毫秒的延迟通常可以忽略不计。
Q3:MCP 协议是 miniOpenClaw 自己发明的吗?
A:MCP 的概念来自 Anthropic 提出的开放标准。miniOpenClaw 的实现是一个简化的教学版本,核心理念一致,但省略了一些高级特性(如资源管理、采样等)。
恭喜你!
你已经完成了 miniOpenClaw 的全部 8 个章节!现在你拥有了从零构建 AI Agent 框架的完整知识:
| 章节 | 你学到了 |
|---|---|
| Day 1 | 消息协议和路由——系统的"神经系统" |
| Day 2 | 多渠道适配——系统的"感官" |
| Day 3 | 会话管理——系统的"短期记忆" |
| Day 4 | ReAct 推理循环——系统的"大脑" |
| Day 5 | 工具系统——系统的"双手" |
| Day 6 | 技能系统——系统的"知识储备" |
| Day 7 | 记忆管理——系统的"笔记本" |
| Day 8 | MCP 协议——系统的"社交能力" |
接下来你可以:
- 给 Agent 写更多实用工具
- 定义有趣的技能包
- 接入真实的 LLM API 做测试
- 阅读 LangChain 等框架的源码,对比我们的实现
- 参与 MCP 社区,体验更多工具生态