Skip to content
源码分析手册

MCP 协议与客户端架构:插件化的统一语言

Model Context Protocol (MCP) 是 Claude Code 的工具扩展协议。它用 JSON-RPC 统一了工具调用、资源读取和交互反馈,让大型语言模型能与外部数据源和工具进行结构化交互。

从定义开始

MCP 协议本质上是 基于 JSON-RPC 的工具、资源与 Prompt 的分发协议(JSON-RPC Dispatch Protocol)

它在 Claude Code 内部(Client)和外部扩展(Server)之间建立了一个双向、实时的通信管道。通过它,Claude 能够发现并调用各种“超能力”——无论是本地的 shell 脚本、通过 SSE 连接的远程服务,还是托管在 claude.ai 上的连接器。MCP 并不是简单的 API 调用,它将“工具定义”、“资源读取”和“交互式反馈”统一在了一套异步消息框架下。

代码里的真实逻辑

整个客户端架构围绕生命周期管理和消息路由展开,其核心逻辑落在了 src/services/mcp/client.ts

  1. 多协议传输层 (Transports): Claude Code 支持多种连接方式,由不同的 Transport 类实现:

    • Stdio Transport:通过 shell 派生子进程(如 nodepython),利用标准输入输出进行通信。这是本地插件的主流方式。
    • SSE (Server-Sent Events) Transport:用于连接远程托管的 MCP 服务,支持持久化连接和动态认证。
    • In-process Transport:用于加载内建的高性能插件,减少进程间通信开销。
    • WebSocket Transport (src/utils/mcpWebSocketTransport.ts):提供了另一种远程连接选择。
  2. 连接生命周期管理 (Session Management)src/services/mcp/MCPConnectionManager.tsx 负责维护所有活跃的 MCP 连接。它处理初始握手(Handshake),并负责在连接失败时触发重连逻辑,或在配置文件变动时热重启对应的 Server 进程。

  3. 工具动态注入与规范化: 一旦连接建立,客户端会通过 list_tools 获取 Server 提供的工具集。

    • 名称规范化 (Normalization)src/services/mcp/normalization.ts 通过 normalizeNameForMCP() 将 Server 名称统一为 ^[a-zA-Z0-9_-]{1,64}$ 格式,确保工具调用的路由前缀合法。
    • 路由分发:当模型决定调用工具时,mcp_call 会根据工具名中的前缀(如 github__)将请求精确路由到对应的 Client 句柄。
  4. 资源面与采样机制: 除了主动执行的工具,架构中还包含 resources/listresources/read。这允许模型通过 URI 寻址读取外部静态数据。

边界条件

  • 物理进程隔离:Stdio 类型的 Server 运行在独立的子进程中。Server 的奔溃不会拖垮 Claude Code,但会导致对应的工具前缀瞬间从上下文消失。
  • 配置与状态的分离.mcp.json 只负责告诉 Claude “去哪里连”,而连接后的 needs-authconnectederror 状态则是由 MCPConnectionManager 在内存中实时维护的。
  • 版本降级:协议遵循语义化版本。如果 Client 与 Server 版本不匹配,系统会尝试回退到基础功能,但这可能导致某些高级特性(如 Tool Search)失效。
  • 性能开销:远程 SSE 连接引入了网络延迟。Claude Code 内部通过大量的缓存机制(如 fetchResourcesForClient)来尽量减少重复的协议往返。

接下来看什么

源码锚点

  • claude-code-opensource/src/services/mcp/client.ts — 客户端核心逻辑与 JSON-RPC 实现。
📄 src/services/mcp/client.ts — 客户端核心逻辑与 JSON-RPC 实现。L28-43 of 3349
typescript
  type JSONRPCMessage,
  type ListPromptsResult,
  ListPromptsResultSchema,
  ListResourcesResultSchema,
  ListRootsRequestSchema,
  type ListToolsResult,
  ListToolsResultSchema,
  McpError,
  type PromptMessage,
  type ResourceLink,
} from '@modelcontextprotocol/sdk/types.js'
import mapValues from 'lodash-es/mapValues.js'
import memoize from 'lodash-es/memoize.js'
import zipObject from 'lodash-es/zipObject.js'
import pMap from 'p-map'
import { getOriginalCwd, getSessionId } from '../../bootstrap/state.js'
  • claude-code-opensource/src/services/mcp/MCPConnectionManager.tsx — 连接生命周期与热重载管理。
📄 src/services/mcp/MCPConnectionManager.tsx — 连接生命周期与热重载管理。L7-15 of 73
tsx
interface MCPConnectionContextValue {
  reconnectMcpServer: (serverName: string) => Promise<{
    client: MCPServerConnection;
    tools: Tool[];
    commands: Command[];
    resources?: ServerResource[];
  }>;
  toggleMcpServer: (serverName: string) => Promise<void>;
}
  • claude-code-opensource/src/services/mcp/normalization.ts — MCP Server 名称的格式规范化(normalizeNameForMCP)。
📄 src/services/mcp/normalization.ts — MCP Server 名称的格式规范化(`normalizeNameForMCP`)。L17-23 of 24
typescript
export function normalizeNameForMCP(name: string): string {
  let normalized = name.replace(/[^a-zA-Z0-9_-]/g, '_')
  if (name.startsWith(CLAUDEAI_SERVER_PREFIX)) {
    normalized = normalized.replace(/_+/g, '_').replace(/^_|_$/g, '')
  }
  return normalized
}
  • claude-code-opensource/src/services/mcp/InProcessTransport.ts — 内建插件的内存级通信实现。
📄 src/services/mcp/InProcessTransport.ts — 内建插件的内存级通信实现。L11-40 of 64
typescript
class InProcessTransport implements Transport {
  private peer: InProcessTransport | undefined
  private closed = false

  onclose?: () => void
  onerror?: (error: Error) => void
  onmessage?: (message: JSONRPCMessage) => void

  /** @internal */
  _setPeer(peer: InProcessTransport): void {
    this.peer = peer
  }

  async start(): Promise<void> {}

  async send(message: JSONRPCMessage): Promise<void> {
    if (this.closed) {
      throw new Error('Transport is closed')
    }
    // Deliver to the other side asynchronously to avoid stack depth issues
    // with synchronous request/response cycles
    queueMicrotask(() => {
      this.peer?.onmessage?.(message)
    })
  }

  async close(): Promise<void> {
    if (this.closed) {
      return
    }
  • claude-code-opensource/src/utils/mcpWebSocketTransport.ts — WebSocket 传输层的封装。
📄 src/utils/mcpWebSocketTransport.ts — WebSocket 传输层的封装。L11-20 of 201
typescript
// WebSocket readyState constants (same for both native and ws)
const WS_CONNECTING = 0
const WS_OPEN = 1

// Minimal interface shared by globalThis.WebSocket and ws.WebSocket
type WebSocketLike = {
  readonly readyState: number
  close(): void
  send(data: string): void
}

�层的封装。

基于 Claude Code v2.1.88 开源快照的深度分析