跳转至

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                     杂项

MCPConnectionManagerclient 是最大也最核心的两个。

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. 阅读清单

  1. src/services/mcp/types.ts(看协议类型)
  2. src/services/mcp/client.ts:1-100(看 client 入口)
  3. src/services/mcp/MCPConnectionManager.tsx(看连接管理)
  4. src/services/mcp/InProcessTransport.ts(看 in-process 实现)
  5. src/tools/MCPTool/MCPTool.tsx(看工具路由)
  6. 📌 src/services/mcp/elicitationHandler.ts(双向通信)
  7. 📌 src/services/mcp/auth.ts:1-100(OAuth 入口)
  8. 📌 src/services/mcp/officialRegistry.ts(claude.ai MCP 集成)

11. 练习任务

  1. 画 MCP 通信序列图 —— 完整"LLM → MCPTool → ConnectionManager → stdio client → MCP server" 时序
  2. 手写一个最小 MCP client —— stdio + JSON-RPC + initialize + tools/list
  3. 手写一个最小 MCP server —— 用 Node 写个回显工具
  4. 设计一个 InProcessTransport —— 不走 JSON 序列化,直接函数调用
  5. 思考:为什么 Anthropic 把 MCP 设计成"行业标准"而不是"Anthropic 专属"?这背后是技术决策还是商业策略?