入门篇讲了 MCP 是什么。这篇拆协议本身——适合要 写 MCP Server 或 评估 MCP 是否适合你的架构 的工程师。
协议栈
应用层 Tools / Resources / Prompts(MCP 语义)
─────────────────────────────
传输层 stdio(本地) | SSE(远程 HTTP)
─────────────────────────────
消息层 JSON-RPC 2.0
MCP 基于 JSON-RPC 2.0。Host 和 Server 之间通过请求/响应消息通信,没有 RESTful URL 的概念。
生命周期
Host Server
│── initialize ──────────────→│
│←─ capabilities ────────────│ (Server 声明支持 Tools/Resources/Prompts)
│── initialized ─────────────→│
│ │
│── tools/list ───────────────→│ (Host 发现可用工具)
│←─ [tool definitions] ──────│
│ │
│── tools/call ──────────────→│ (Host 让 Server 执行工具)
│←─ result / error ──────────│
关键点:Server 是被动的——它不主动推消息,只响应 Host 的请求。Host(Cursor、Claude Desktop)控制整个交互节奏。
Tools 定义规范
Server 通过 tools/list 返回工具清单,每个工具包含:
{
"name": "query_database",
"description": "Execute a read-only SQL query against the production database",
"inputSchema": {
"type": "object",
"properties": {
"sql": { "type": "string", "description": "SQL query to execute" }
},
"required": ["sql"]
}
}
description 字段至关重要——LLM 靠它决定什么时候调用、传什么参数。写 MCP Server 时,description 的质量直接决定 Agent 的调用准确率。
传输层选择
| 传输 | 场景 | 特点 |
|---|---|---|
| stdio | 本地 Server,Claude Desktop / Cursor | Server 作为子进程启动,stdin/stdout 通信 |
| SSE | 远程 Server,Web 部署 | HTTP Server-Sent Events,支持多客户端 |
本地开发几乎都用 stdio。生产环境如果多个 Host 共享一个 Server,用 SSE + 鉴权。
最小 MCP Server(TypeScript)
使用官方 SDK @modelcontextprotocol/sdk:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "my-server", version: "1.0.0" });
server.tool(
"get_weather",
"Get current weather for a city",
{ city: z.string().describe("City name, e.g. Beijing") },
async ({ city }) => {
// 实际项目中调天气 API
const data = { city, temp: "25°C", condition: "晴" };
return { content: [{ type: "text", text: JSON.stringify(data) }] };
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
Host 配置:
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["./dist/server.js"]
}
}
}
Resources vs Tools:什么时候用哪个
| Tools | Resources | |
|---|---|---|
| 语义 | 「帮我做一件事」 | 「给我一些数据」 |
| 副作用 | 可以有(写文件、发消息) | 应该只读 |
| LLM 怎么用 | Function calling 主动调用 | 按需读取,类似 RAG |
| 例子 | 创建 PR、执行 SQL | 读取文件、获取 schema |
工程经验:读操作用 Resources,写操作用 Tools。混在一起会导致 Agent 误调用。
生产部署注意事项
- 权限最小化:MCP Server 只暴露必要的 Tools,database server 用 read-only 连接
- 输入校验:
inputSchema只是给 LLM 看的,Server 内部必须再校验 - 超时和重试:Tool 调用可能很慢,Host 侧要有 timeout
- 日志和审计:谁通过 MCP 做了什么操作,生产环境必须可追溯
- 版本管理:Tool schema 变更时要考虑向后兼容
生态现状(2026)
- 官方维护的 Server 仓库:
modelcontextprotocol/servers(GitHub、Postgres、Filesystem 等) - 社区 Server 数量快速增长,质量参差不齐
- Cursor、Claude Desktop、Windsurf、Zed 均已支持
- OpenAI 尚未原生支持 MCP,但通过第三方桥接可用
小结
MCP 的 engineering 核心:JSON-RPC 消息 + 清晰的 Tool/Resource 定义 + 合适的传输层。写 Server 时把 description 写好、权限控好,比追求功能数量重要得多。