Topic | MCP(Model Context Protocol)协议深度拆解¶
重要性:⭐⭐⭐⭐⭐(行业标准 + Claude Code 标杆实现) 出现位置:
src/services/mcp/(23 文件)、src/tools/MCPTool/关联:phase-07-advanced.md 的 MCP 专题、glossary 的 MCP 词条
1. MCP 是什么¶
Model Context Protocol —— Anthropic 推动的"LLM 工具扩展标准"。
核心思想: - 任何外部服务可以以 MCP server 形式提供"工具" - 任何 LLM 客户端(Claude Code、Claude Desktop、Cursor、Cline)可以用 MCP client 调用 - 协议是标准化的(JSON-RPC 2.0 over stdio / HTTP / SSE)
类比: - LSP(Language Server Protocol) —— 编辑器和语言分析服务的协议 - MCP = LLM 和工具服务的协议
2. 23 个文件全景¶
src/services/mcp/
├── MCPConnectionManager.tsx 核心:连接管理(多 server + 重连)
├── client.ts MCP client 实现(stdio / HTTP / SSE)
├── types.ts 协议类型
├── config.ts MCP 配置(.mcp.json 解析)
├── auth.ts OAuth / API key 鉴权(2465 行!)
├── elicitationHandler.ts Elicit 请求处理(MCP 1.0 新特性)
├── oauthPort.ts OAuth 回调端口管理
├── xaa.ts Cross-App Access(XAA)登录
├── xaaIdpLogin.ts XAA IdP 登录
├── officialRegistry.ts 官方 MCP registry(Claude.ai 集成)
├── vscodeSdkMcp.ts VSCode SDK 适配
├── claudeai.ts claude.ai 集成 MCP
├── InProcessTransport.ts 进程内传输(同进程 MCP server)
├── SdkControlTransport.ts SDK 控制传输
├── normalization.ts 消息规范化
├── channelPermissions.ts Channel 权限
├── channelAllowlist.ts Channel 白名单
├── channelNotification.ts Channel 通知
├── useManageMCPConnections.ts React hook:UI 管理连接
├── headersHelper.ts HTTP headers 助手
├── envExpansion.ts env 变量展开
├── mcpStringUtils.ts 字符串工具
└── utils.ts 杂项
MCPConnectionManager 和 client 是最大也最核心的两个。
3. 协议层:JSON-RPC 2.0¶
MCP 基于 JSON-RPC 2.0 协议,3 种传输方式:
3.1 stdio(最常见)¶
// client.ts 启动 stdio MCP server
const child = spawn('npx', ['-y', '@some/mcp-server'], {
stdio: ['pipe', 'pipe', 'pipe'],
})
// 写 JSON-RPC 请求到 stdin
child.stdin.write(JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'tools/list',
params: {},
}) + '\n')
// 从 stdout 读 JSON-RPC 响应
child.stdout.on('data', (data) => {
const response = JSON.parse(data.toString())
// ...
})
特点: - 适合本地工具(文件、git、shell) - Claude Code spawn 子进程跑 - 进程退出 = 连接关闭
3.2 HTTP + SSE(远程服务)¶
// client.ts 用 fetch 调远程 MCP server
const response = await fetch('https://mcp.example.com/tools/list', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', method: 'tools/list', params: {} }),
})
// SSE 流式接收
const reader = response.body.getReader()
// ... 解析 SSE 事件
特点: - 适合云服务(GitHub MCP、Notion MCP) - 需要 OAuth 鉴权 - 长连接 + EventSource 风格
3.3 In-Process(同进程)¶
// InProcessTransport.ts
const transport = new InProcessTransport({
server: new MyMCPServer(),
})
// 直接调方法,不走 JSON 序列化
await transport.request('tools/list', {})
特点:
- 适合 Claude Code 内置的 MCP server(如 vscodeSdkMcp、claudeai)
- 不走网络,最高性能
- 用于 process.env.USER_TYPE === 'ant' 的内部服务
4. 连接管理:MCPConnectionManager¶
// 推测结构
export class MCPConnectionManager {
private connections: Map<string, MCPConnection> = new Map()
async addServer(config: MCPServerConfig): Promise<void> {
// 1. 选择 transport
const transport = this.selectTransport(config)
// 2. 建立连接
const conn = await transport.connect()
// 3. 协议握手
await conn.initialize({
protocolVersion: '2024-11-05',
capabilities: { tools: {}, resources: {} },
clientInfo: { name: 'claude-code', version: '1.0.0' },
})
// 4. 注册到连接池
this.connections.set(config.name, conn)
}
async callTool(
serverName: string,
toolName: string,
input: unknown
): Promise<ToolResult> {
const conn = this.connections.get(serverName)
if (!conn) throw new Error(`MCP server ${serverName} not connected`)
return conn.request('tools/call', { name: toolName, arguments: input })
}
async reconnect(serverName: string): Promise<void> {
const conn = this.connections.get(serverName)
if (conn) await conn.close()
await this.addServer(this.configs.get(serverName)!)
}
}
关键设计:
- 连接池 —— 支持同时连多个 server
- 重连机制 —— 网络断开自动重试
- 协议握手 —— initialize 交换能力
- 统一 API —— 上层用 callTool(server, tool, input),不关心传输
5. 工具暴露:MCPTool 包装¶
// src/tools/MCPTool/MCPTool.tsx
export const MCPTool = buildTool({
name: 'mcp',
description: 'Internal: routes to MCP server tools',
inputSchema: z.object({
server: z.string(),
tool: z.string(),
input: z.unknown(),
}),
async *call(input, ctx) {
// 1. 解析 input
yield { type: 'routing', server: input.server, tool: input.tool }
// 2. 调 MCP server
const mgr = getMCPManager()
const result = await mgr.callTool(input.server, input.tool, input.input)
yield { type: 'result', result }
return {
content: formatMCPResult(result),
is_error: result.isError,
}
},
})
关键设计:
- MCPTool 是"内部工具" —— 名字叫 mcp 但不是用户直接调
- LLM 看到的是 mcp__github__create_issue 这种 tool_use(带前缀)
- MCPTool 收到后拆 server / tool / input 三段,转发给对应 MCP server
6. Elicitation 协议(MCP 1.0 新特性)¶
// src/services/mcp/elicitationHandler.ts
// MCP server 可以反过来问用户问题
// 1. MCP server 发 elicit 请求
{
"jsonrpc": "2.0",
"id": 5,
"method": "elicitation/create",
"params": {
"message": "Which repository should I create the issue in?",
"requestedSchema": {
"type": "object",
"properties": {
"repo": { "type": "string", "enum": ["repo1", "repo2"] }
}
}
}
}
// 2. Claude Code 收到,弹 ElicitationDialog
<ElicitationDialog
message="Which repository?"
schema={...}
onSubmit={(answer) => {
// 3. 回 elicit result
mgr.respond(5, { repo: 'repo1' })
}}
/>
双向通信: - 老版 MCP:client 调 server(单向) - 新版 MCP Elicitation:server 调 client(双向)
前端类比:和 GraphQL Subscriptions 同种"双向"。
7. OAuth 鉴权流程¶
// src/services/mcp/auth.ts:2465 行
// 1. 发现 OAuth endpoints
const metadata = await fetch(`${serverUrl}/.well-known/oauth-authorization-server`)
// 2. PKCE 流程
const codeVerifier = generateCodeVerifier()
const codeChallenge = await generateCodeChallenge(codeVerifier)
// 3. 启动本地 HTTP server 收回调(oauthPort.ts)
const callbackPort = await startCallbackServer(async (code) => {
// 4. 换 token
const tokens = await exchangeCode(serverUrl, code, codeVerifier)
saveTokens(serverName, tokens)
})
// 5. 打开浏览器
openBrowser(authorizationUrl)
// 6. 关闭回调 server
callbackPort.close()
2465 行的 auth.ts 包含: - 多 OAuth provider 适配(GitHub、Google、自定义) - Token 刷新 - PKCE(防中间人) - 本地回调端口管理
8. 实战:装一个 MCP server¶
# 1. 加到 .mcp.json
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_TOKEN": "..." }
}
}
}
# 2. Claude Code 启动时自动加载
# 3. LLM 现在多了工具:mcp__github__create_issue, mcp__github__list_repos...
零代码改动 —— 这就是 MCP 的力量。
9. 关键洞察¶
9.1 MCP 是 Claude Code 最重要的扩展点¶
加新工具 = 装 MCP server = 0 行 Claude Code 改动。比 plugin 系统更彻底。
9.2 三种 transport 的 trade-off¶
| Transport | 性能 | 适用 |
|---|---|---|
| stdio | 高 | 本地工具 |
| HTTP+SSE | 中 | 远程服务 |
| In-Process | 最高 | 内置 server |
In-Process 是 Claude Code 的"秘密武器" —— 内部用 in-process 跑,外部 stdio,统一 API。
9.3 Elicitation 让 MCP 不只是"工具调用"¶
老版 MCP = 函数调用
新版 MCP = 双向协议(server 可以问问题、推送通知)
前端类比:从 RPC 进化到 Pub/Sub。
9.4 MCPConnectionManager 的设计模式¶
- 连接池(多 server)
- 重连机制(网络抖动)
- 统一抽象(隐藏 transport 差异)
- 能力交换(协议握手)
这和数据库连接池、HTTP keep-alive、gRPC channel 是同种模式。
10. 阅读清单¶
- ✅
src/services/mcp/types.ts(看协议类型) - ✅
src/services/mcp/client.ts:1-100(看 client 入口) - ✅
src/services/mcp/MCPConnectionManager.tsx(看连接管理) - ✅
src/services/mcp/InProcessTransport.ts(看 in-process 实现) - ✅
src/tools/MCPTool/MCPTool.tsx(看工具路由) - 📌
src/services/mcp/elicitationHandler.ts(双向通信) - 📌
src/services/mcp/auth.ts:1-100(OAuth 入口) - 📌
src/services/mcp/officialRegistry.ts(claude.ai MCP 集成)
11. 练习任务¶
- 画 MCP 通信序列图 —— 完整"LLM → MCPTool → ConnectionManager → stdio client → MCP server" 时序
- 手写一个最小 MCP client —— stdio + JSON-RPC + initialize + tools/list
- 手写一个最小 MCP server —— 用 Node 写个回显工具
- 设计一个 InProcessTransport —— 不走 JSON 序列化,直接函数调用
- 思考:为什么 Anthropic 把 MCP 设计成"行业标准"而不是"Anthropic 专属"?这背后是技术决策还是商业策略?