3 minutes
MCP 协议详解
到这里,你已经能让 Agent 在自己的进程里跑工具了。但工具是"私"的——你写一个 bash tool,只有你的 Agent 能用。让任何 Agent 都能用同一份"工具集",需要的是协议。Anthropic 在 2024 年底发布了 Model Context Protocol(MCP),2026 年中已成了 de-facto 标准。本章把 MCP 的协议机制讲透。
15.1 MCP 要解决的问题
Pre-MCP 混乱:
Cursor 自己定义一套 tool spec,跟 Claude Code 不同
Claude Code 自己一套,跟 LangChain 不同
你给团队写个内部 API agent 工具,要实现 3-5 遍
Post-MCP:
┌───────────┐ ┌──────────────┐
│ MCP Client │ ─── MCP ────→ │ MCP Server │
│ (任何 Agent)│ │ (工具实现者) │
└───────────┘ └──────────────┘
客户端只问:"你能干啥?" → 服务端回答一组标准 schema
客户端说:"调用 foo 工具" → 服务端返回结果
MCP = “USB-C 接口” for AI Tools。一个 server 实现一次,Any Client 都能用。
15.2 MCP 的三大原语
| 原语 | 作用 | 谁发起 | 形态 |
|---|---|---|---|
| Tools | 模型可调用的函数(带副作用) | 模型(tool_use) | JSON Schema 输入 + 任意输出 |
| Resources | 可读的"数据源"(无副作用) | 客户端按需 fetch | URI + mime_type + 内容 |
| Prompts | 预制的对话模板 | 用户主动选择 | name + arguments + messages |
理解差别:Tools 是模型决定调;Resources 是客户端主动读、展示给模型;Prompts 是用户主动选的快捷指令。三者职责清晰,混着实现就乱。
15.3 传输方式:stdio / SSE / WebSocket
MCP 不限定网络层,常见三种:
| 传输 | 用法 | 适合 |
|---|---|---|
| stdio | harness fork 一个 subprocess,stdin/stdout 走 JSON-RPC | 本地工具(filesystem、git) |
| SSE | server 跑 HTTP,长连接 server-sent events | 远程 server、云端服务 |
| WebSocket | server 跑 ws://,双向 | 需要双向低延迟(如机器人控制) |
stdio 的好处是零部署——client 多开一个 subprocess 就行。OpenCode / Claude Code 的项目内 MCP 都是 stdio。
15.4 消息结构:JSON-RPC 2.0
MCP 用 JSON-RPC 2.0 包消息。看一个完整握手序列:
// Client → Server : initialize 握手
{"jsonrpc":"2.0","id":1,"method":"initialize","params":
{"protocolVersion":"2025-06-18",
"clientInfo":{"name":"opencode","version":"0.x"},
"capabilities":{}}}
// Server → Client : 返回 server 能力
{"jsonrpc":"2.0","id":1,"result":
{"protocolVersion":"2025-06-18",
"serverInfo":{"name":"filesystem","version":"1.0"},
"capabilities":{"tools":{},"resources":{}}}}
// Client → Server : 列举工具
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}
// Server → Client : 工具列表
{"jsonrpc":"2.0","id":2,"result":{"tools":[
{"name":"read_file",
"description":"Read a file from disk",
"inputSchema":{"type":"object","properties":{"path":{"type":"string"}},"required":["path"]}}
]}}
// Client → Server : 调用工具
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":
{"name":"read_file","arguments":{"path":"/tmp/foo.txt"}}}
// Server → Client : 工具结果
{"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"hello"}]}}
不再学更多——任何 MCP 客户端 / 服务端交互都是这几个原语的循环。
15.5 三大致命陷阱
15.5.1 Tools vs Resources 混淆
错误:把"读文件"做成 tool。应该是 resource:客户端按需读、缓存、显示给模型。Tools 应有副作用(写磁盘、调 API、跑命令)。
15.5.2 协议版本不匹配
MCP 协议在迭代(2024-11 → 2025-03 → 2025-06-18 …)。Client 与 Server 协议版本不一致时,老版可能缺 sampling、notifications 等能力。生产配置:显式写 protocolVersion,不要依赖自动 negotiate。
15.5.3 客户端盲信 schema
服务端的 inputSchema 是给模型看的,但客户端不应假设 schema 一定合法。加重试 + 校验,不要把一个被恶意 server 灌入的奇怪 schema 直接转发给 LLM。
15.6 一个极简 stdio MCP Server
Python 用官方 SDK 30 行能写一个:
# server.py
from mcp.server.stdio import stdio_server
from mcp.server import Server
import mcp.types as types
server = Server("hello-mcp")
@server.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name="greet",
description="给指定名字打招呼",
inputSchema={
"type": "object",
"properties": {"name": {"type": "string"}},
"required": ["name"],
},
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
if name == "greet":
who = arguments["name"]
return [types.TextContent(type="text", text=f"Hello, {who}!")]
raise ValueError(f"unknown tool: {name}")
async def main():
async with stdio_server() as (r, w):
await server.run(r, w, server.create_initialization_opts())
if __name__ == "__main__":
import asyncio
asyncio.run(main())
在 OpenCode 配置:
{
"mcp": {
"hello": {
"type": "stdio",
"command": "python",
"args": ["server.py"]
}
}
}
启动后,模型就能调到 greet 工具。下一章会展开实战开发。
15.7 MCP vs Function Calling:什么关系
Function calling = 模型能力:模型能输出 {"name":"foo","arguments":{...}}。
MCP = 工具发现 + 调用协议:可跨 harness 通用,不绑死某家模型 API。
┌──────────┐ ┌──────────┐
│Harness/ │ has tools/list + tools/call │MCP Server│
│LLM API │ ──────────── JSON-RPC ─────────→ │(any) │
└──────────┘ └──────────┘
↑
│ function calling (provider API 自己的)
│
┌──────────┐
│ LLM │
└──────────┘
一句话:MCP 服务端不直接函数模型,它把 tools 暴露给 harness,harness 用 function calling 让模型做选择。MCP 是 harness 的事,不是 LLM API 的事。
15.8 何时该写 MCP
| 场景 | 写 MCP 还是写普通 tool |
|---|---|
| 团队内部多处 harness 要复用 | ✅ MCP |
| 该工具/数据源会被分发给社区 | ✅ MCP |
| 仅你自己项目里临时用 | ❌ 普通 tool |
| 强 dependency on 内部业务模型 | ❌ 你会反复改 protocolVersion,得不偿失 |
| 流量特别大 / 延迟极敏感 | ❌ stdio 启动慢,先用普通 tool |
第三篇 17 章:MCP server 开发实战 会把这决策再展开。
15.9 小结
- MCP = USB-C for AI tools,三大原语:Tools / Resources / Prompts
- 协议版本要显式固定,schema 不能盲信
- 30 行 stdio server 能上手;stdio 适合本地、SSE 适合远程
- 何时写 MCP:跨 harness 复用 / 社区分发 / 解耦业务
下一篇:《16 常用 MCP Server 全景》——别自己写之前先看看现成有什么。
Summary: MCP = 协议标准化 + 三原语 + stdio/SSE 双传输;30 行起步,慎重选场景再写。