Model Context Protocol (MCP) 是 Claude Code 扩展能力的核心协议层,通过标准化的接口连接外部工具服务器、资源提供者和命令系统。该服务层不仅实现了 MCP 规范的完整协议栈,还提供了连接管理、资源发现、权限控制和错误恢复等企业级特性,使 Claude Code 能够动态集成来自不同来源的工具能力而无需硬编码依赖。
架构总览:从协议到工具的完整链路
MCP 服务采用分层架构设计,从底层的传输协议到上层的工具集成形成清晰的职责边界。核心连接管理器负责维护所有 MCP 服务器的生命周期状态,包括连接建立、健康检查、自动重连和资源清理。配置层支持多种作用域的服务器配置来源,从本地项目的 .mcp.json 到企业级的托管配置,通过统一的配置合并策略提供给运行时使用。
graph TB
subgraph "配置层 Configuration Layer"
A1[项目配置 .mcp.json]
A2[用户配置 ~/.claude/]
A3[企业配置 managed-mcp.json]
A4[动态配置 Dynamic Config]
A5[插件配置 Plugin MCP]
end
subgraph "连接管理层 Connection Management"
B1[MCPConnectionManager]
B2[useManageMCPConnections Hook]
B3[连接状态机<br/>connected/failed/pending/needs-auth]
end
subgraph "传输协议层 Transport Layer"
C1[StdioTransport<br/>子进程通信]
C2[SSETransport<br/>Server-Sent Events]
C3[HTTPTransport<br/>Streamable HTTP]
C4[WebSocketTransport<br/>双向实时通信]
C5[SDKTransport<br/>进程内调用]
end
subgraph "协议实现层 Protocol Implementation"
D1[JSON-RPC 2.0 消息处理]
D2[能力协商<br/>Capabilities Negotiation]
D3[资源发现<br/>Resource Discovery]
D4[工具调用<br/>Tool Invocation]
end
subgraph "集成层 Integration Layer"
E1[工具注册 Tool Registry]
E2[命令注册 Command Registry]
E3[资源管理 Resource Manager]
E4[权限检查 Permission Check]
end
A1 --> B2
A2 --> B2
A3 --> B2
A4 --> B2
A5 --> B2
B2 --> B1
B1 --> B3
B3 --> C1
B3 --> C2
B3 --> C3
B3 --> C4
B3 --> C5
C1 --> D1
C2 --> D1
C3 --> D1
C4 --> D1
C5 --> D1
D1 --> D2
D2 --> D3
D3 --> D4
D4 --> E1
D4 --> E2
D3 --> E3
D4 --> E4
配置层通过 getClaudeCodeMcpConfigs() 函数合并所有作用域的配置,按照优先级处理冲突:本地项目配置 > 用户配置 > 企业配置。连接管理层使用 React Context 提供全局访问点,通过 useManageMCPConnections Hook 实现声明式的连接管理。传输层根据服务器配置类型选择相应的传输实现,每种传输都实现了标准的 Transport 接口以保证协议层的统一性。
Sources: types.ts, MCPConnectionManager.tsx, config.ts
服务器连接管理:状态机与生命周期控制
MCP 服务器连接采用显式的状态机模型,每个服务器在任何时刻都处于明确的连接状态。MCPServerConnection 联合类型定义了五种可能的状态:connected 表示成功连接并可用的服务器,failed 表示连接失败且不可自动恢复,needs-auth 表示需要用户进行 OAuth 认证,pending 表示正在重连或初始化中,disabled 表示用户显式禁用的服务器。状态转换由 useManageMCPConnections Hook 管理,该 Hook 监听配置变化并自动触发相应的连接操作。
连接建立过程遵循能力协商模式:客户端首先通过传输层建立底层通信通道,然后发送 initialize 请求获取服务器的能力声明(ServerCapabilities),包括是否支持工具、资源、提示等特性。成功初始化后,连接管理器调用 fetchToolsForClient、fetchResourcesForClient 和 fetchCommandsForClient 分别获取服务器提供的工具、资源和命令。这三个函数使用 LRU 缓存(memoizeWithLRU)避免重复请求,缓存键为服务器名称,在连接关闭或收到列表变更通知时自动失效。
// 连接状态类型定义展示了五种可能的服务器状态
export type MCPServerConnection =
| ConnectedMCPServer // 已连接:client 可用,capabilities 已协商
| FailedMCPServer // 失败:error 字段包含错误信息
| NeedsAuthMCPServer // 需认证:等待 OAuth 流程
| PendingMCPServer // 待定:重连尝试计数和最大尝试次数
| DisabledMCPServer // 已禁用:用户显式禁用
自动重连机制针对 SSE 和 HTTP 等网络传输实现指数退避策略。初始退避时间为 1 秒,每次失败后加倍,最大退避时间 30 秒,最多尝试 5 次。重连逻辑通过 reconnectTimersRef 跟踪活动的重连定时器,在组件卸载或服务器配置变更时清理所有待处理的重连操作。当服务器返回 401 未授权错误时,连接状态转换为 needs-auth,并在本地缓存中记录该状态 15 分钟以避免频繁的认证提示。
Sources: types.ts, useManageMCPConnections.ts, client.ts
传输层实现:多协议支持与统一接口
传输层抽象了底层通信协议的差异,为上层协议实现提供统一的 Transport 接口。每种传输类型都实现了 start()、send(message)、close() 方法和 onmessage、onclose、onerror 事件处理器。Claude Code 支持五种传输类型,每种针对不同的部署场景和性能需求进行了优化。
传输类型对比
| 传输类型 | 配置标识 | 适用场景 | 特性 | 性能特征 |
|---|---|---|---|---|
| Stdio | stdio | 本地命令行工具 | 通过子进程标准输入输出通信,支持环境变量传递 | 低延迟,进程隔离 |
| SSE | sse | 远程 HTTP 服务器 | Server-Sent Events 长连接,支持自定义 Headers 和 OAuth | 自动重连,适合流式数据 |
| HTTP | http | MCP Streamable HTTP | 基于 HTTP POST 的请求-响应模式,支持 JSON 和 SSE 响应 | 无状态,易于负载均衡 |
| WebSocket | ws | 实时双向通信 | 全双工通信,支持 mTLS 双向认证 | 最低延迟,适合高频交互 |
| SDK | sdk | 进程内集成 | 直接函数调用,无需网络序列化 | 零开销,用于嵌入式场景 |
Stdio 传输通过 StdioClientTransport 实现,启动配置中指定的命令作为子进程,将 JSON-RPC 消息写入子进程的标准输入,从标准输出读取响应。这种模式适合本地工具的集成,如文件系统访问、代码分析等。SSE 传输使用 SSEClientTransport,建立到远程 URL 的持久化 HTTP 连接,服务器通过事件流推送消息,客户端通过 POST 请求发送消息。HTTP 传输实现了 MCP Streamable HTTP 规范,每个请求都携带 Accept: application/json, text/event-stream 头以支持两种响应格式。
WebSocket 传输通过自定义的 WebSocketTransport 类实现,同时支持 Bun 原生 WebSocket 和 Node.js 的 ws 包。构造函数根据运行时环境自动选择相应的事件绑定方式:Bun 使用 addEventListener,Node.js 使用 on 方法。消息到达后通过 JSONRPCMessageSchema 解析验证,确保协议合规性。WebSocket 连接支持通过 getWebSocketTLSOptions() 配置 mTLS 双向认证,适用于高安全要求的场景。
In-process 传输通过 createLinkedTransportPair() 创建一对互相连接的传输实例,消息在一个实例上的 send() 调用会异步投递到另一个实例的 onmessage 处理器。这种模式用于在同一进程内运行 MCP 服务器和客户端,避免了进程间通信的开销,特别适合测试和嵌入式集成场景。消息投递使用 queueMicrotask() 异步执行以避免同步请求-响应循环导致的栈深度问题。
Sources: types.ts, InProcessTransport.ts, mcpWebSocketTransport.ts, client.ts
资源管理:工具、命令与资源的统一发现
MCP 服务器可以提供三种类型的可发现资源:工具、命令和资源对象。资源发现发生在连接建立后的初始化阶段,通过标准化的 MCP 协议方法获取服务器的能力声明和资源列表。发现过程使用 LRU 缓存优化性能,缓存大小通过 MCP_FETCH_CACHE_SIZE 常量控制,在连接关闭或收到变更通知时自动失效。
工具发现与集成
fetchToolsForClient() 函数向已连接的服务器发送 tools/list 请求,获取服务器支持的所有工具定义。每个工具定义包含名称、描述、输入模式和其他元数据。函数将 MCP 工具格式转换为 Claude Code 的 Tool 类型,创建包装器对象继承 MCPTool 的基础实现并覆盖关键方法。工具名称通过 buildMcpToolName() 函数规范化为 mcp__<server>__<tool> 格式以避免命名冲突,但 SDK 类型的服务器可通过 CLAUDE_AGENT_SDK_MCP_NO_PREFIX 环境变量跳过前缀,允许 MCP 工具覆盖内置工具。
工具包装器从原始 MCP 工具定义中提取多个关键属性:description 和 prompt 方法返回工具的描述文本(截断到 2048 字符以防止过长的 OpenAPI 文档污染上下文),isConcurrencySafe 和 isReadOnly 从工具的 annotations 字段推断,searchHint 和 alwaysLoad 从 _meta 元数据中提取用于工具选择优化。工具的实际调用逻辑在包装器的 call() 方法中实现,该方法构建 JSON-RPC 请求并通过客户端连接发送到 MCP 服务器。
命令发现与提示转换
fetchCommandsForClient() 函数处理 MCP 服务器的提示功能,将 MCP 提示转换为 Claude Code 的命令系统。服务器通过 prompts/list 方法暴露可用的提示模板,每个提示包含名称、描述和参数定义。转换后的命令名称格式为 mcp__<server>__<prompt>,命令的 getPromptForCommand() 方法在用户调用时发送 prompts/get 请求到服务器,传递用户提供的参数并接收生成的提示内容。
提示参数通过 arguments 字段定义,转换后的命令使用空格分隔的参数列表。例如,一个接受 file 和 line 参数的 MCP 提示转换为命令后可通过 /mcp__server__prompt file.txt 42 的形式调用。提示内容通过 transformResultContent() 函数处理,将 MCP 的消息格式转换为 Claude Code 可理解的内容块数组,支持文本、图像等多种内容类型。
资源对象管理
fetchResourcesForClient() 函数获取服务器提供的资源列表,资源是 MCP 服务器暴露的可读数据对象,如文件、数据库记录、API 响应等。每个资源包含 URI、名称、可选的 MIME 类型 和描述。资源列表通过 ListMcpResourcesTool 工具暴露给模型,允许模型查询可用的资源目录。具体的资源内容通过 ReadMcpResourceTool 工具读取,该工具发送 resources/read 请求到服务器,传递资源的 URI 并接收内容。
资源内容支持文本和二进制两种形式:文本内容直接作为字符串返回,二进制内容(如图像、PDF)通过 persistBinaryContent() 函数持久化到临时文件,返回值中包含 blobSavedTo 字段指示文件路径。这种设计避免了大体积二进制数据污染消息上下文,同时保持了资源的可访问性。资源读取使用 ensureConnectedClient() 确保连接可用,如果连接已断开会自动重新建立连接后重试请求。
Sources: client.ts, client.ts, ListMcpResourcesTool.ts, ReadMcpResourceTool.ts
协议实现:JSON-RPC 2.0 消息处理与错误恢复
MCP 协议基于 JSON-RPC 2.0 规范构建,所有通信都通过标准化的请求-响应模式进行。客户端通过 Client 类(来自 @modelcontextprotocol/sdk)管理协议层的状态,包括请求 ID 生成、响应匹配、超时处理和错误传播。每个 JSON-RPC 消息都包含 jsonrpc: "2.0" 版本标识、唯一的 id 字段(用于请求)、method 字段指定操作类型,以及可选的 params 字段传递参数。
协议层实现了完整的错误分类和恢复机制。McpAuthError 错误类表示认证失败,触发状态转换为 needs-auth 并启动 OAuth 流程。McpSessionExpiredError 错误表示会话已过期(检测到 HTTP 404 和 JSON-RPC 错误码 -32001),连接管理器清理缓存并通过 ensureConnectedClient() 重新建立连接。McpToolCallError 错误表示工具调用返回 isError: true,该错误类继承自 TelemetrySafeError 以确保错误消息中的敏感信息在遥测中被过滤,同时保留 _meta 元数据供 SDK 消费者使用。
会话管理与超时控制
HTTP 和 SSE 传输使用会话 ID 管理有状态的连接。服务器在初始化响应中返回会话 ID,客户端在后续请求的 Mcp-Session-Id 头中携带该标识。会话超时由 getConnectionTimeoutMs() 函数控制,默认 30 秒,可通过 MCP_TIMEOUT 环境变量覆盖。工具调用的超时独立控制,默认约 27.8 小时(DEFAULT_MCP_TOOL_TIMEOUT_MS = 100_000_000),可通过 MCP_TOOL_TIMEOUT 环境变量调整,适应长时间运行的分析任务。
请求超时通过 wrapFetchWithTimeout() 函数实现,该函数包装底层的 fetch 调用,为每个请求创建新的 AbortSignal.timeout()。这种设计避免了单个超时信号在连接生命周期内过期导致后续请求立即失败的问题。GET 请求(用于 SSE 流)被排除在超时控制之外,因为这些是设计为无限期保持开放的长连接。POST 请求额外添加 Accept: application/json, text/event-stream 头以满足 MCP Streamable HTTP 规范要求。
消息验证与内容转换
接收到的 JSON-RPC 消息通过 Zod 模式验证确保结构合规性。例如,ListToolsResultSchema 验证工具列表响应包含正确的 tools 数组,ReadResourceResultSchema 验证资源读取响应包含有效的 contents 数组。验证失败会抛出详细的错误信息帮助调试。消息内容通过 recursivelySanitizeUnicode() 函数清理,移除或替换可能导致显示问题的 Unicode 字符,确保跨平台兼容性。
工具调用的结果内容通过 transformResultContent() 函数转换为 Claude Code 的内容块格式。该函数处理文本、图像和资源引用等多种内容类型:文本内容直接传递,图像内容(通过 MIME 类型识别)经过可选的缩放和降采样以控制大小,资源引用转换为嵌入式的资源链接。转换后的内容通过 truncateMcpContentIfNeeded() 检查大小,超过限制的内容被截断并添加省略标记,防止过大的工具输出耗尽上下文预算。
Sources: client.ts, client.ts, MCPTool.ts
认证机制:OAuth 2.0 流程与令牌管理
MCP 服务支持完整的 OAuth 2.0 认证流程,允许服务器要求用户授权后才能访问受保护的资源。认证配置通过服务器配置中的 oauth 字段指定,包含 clientId、callbackPort 和 authServerMetadataUrl 等参数。当服务器返回 401 未授权错误时,连接管理器将服务器状态设置为 needs-auth,用户界面显示认证提示,用户确认后启动完整的 OAuth 授权码流程。
OAuth 流程通过 ClaudeAuthProvider 类实现,该类实现了 OAuthClientProvider 接口。流程开始时,提供者通过 discoverAuthorizationServerMetadata() 发现授权服务器的元数据,获取授权端点、令牌端点和注册端点的 URL。客户端元数据通过 MCP_CLIENT_METADATA_URL 指向 Claude Code 的客户端信息,包括重定向 URI 和支持的作用域。提供者使用 PKCE(Proof Key for Code Exchange)扩展增强安全性,生成随机的代码验证器和挑战值。
令牌存储与刷新
OAuth 令牌通过安全的存储机制持久化,macOS 使用 Keychain,Linux 使用 secret-service,Windows 使用 Credential Vault。令牌数据通过 getSecureStorage() 获取的存储接口写入,包含访问令牌、刷新令牌、过期时间等信息。令牌刷新通过 refreshAuthorization() 函数实现,该函数检测访问令牌即将过期或已过期时自动使用刷新令牌获取新的访问令牌。刷新操作通过文件锁(lockfile)序列化,防止多个并发刷新请求导致竞争条件。
刷新失败根据错误类型进行分类处理:InvalidGrantError 表示刷新令牌已失效,需要用户重新授权;TooManyRequestsError 表示速率限制,等待一段时间后重试;TemporarilyUnavailableError 和 ServerError 表示临时性错误,指数退避后重试。所有刷新失败都记录到遥测中,包含失败原因(metadata_discovery_failed、no_client_info、invalid_grant 等)用于诊断和改进。成功刷新后,新的令牌写入安全存储并更新内存缓存。
跨应用访问(XAA)集成
跨应用访问允许 MCP 服务器通过用户的身份提供商(IdP)令牌访问其他应用的数据,实现应用间的安全数据共享。XAA 配置通过服务器配置的 oauth.xaa: true 标志启用,IdP 连接详情(issuer、clientId、callbackPort)从全局设置 settings.xaaIdp 读取,在所有 XAA 启用的服务器间共享。XAA 流程通过 performCrossAppAccess() 函数实现,该函数首先获取用户的 IdP 令牌,然后通过令牌交换协议获取目标应用的访问令牌。
XAA 令牌交换使用专门的 IdP 登录流程(xacIdpLogin.ts),支持 OIDC 兼容的身份提供商。用户的 IdP 令牌通过 acquireIdpIdToken() 获取,使用相同的 OAuth 授权码流程但针对 IdP 的端点。令牌交换失败抛出 XaaTokenExchangeError,包含详细的错误信息帮助诊断配置问题。XAA 功能通过 GrowthBook 功能标志(KAIROS 或 KAIROS_CHANNELS)控制发布,允许渐进式推出和快速回滚。
配置系统:多作用域配置合并与验证
MCP 服务器配置支持六种作用域,按照优先级从高到低排列:local(命令行参数)、project(项目的 .mcp.json)、dynamic(运行时动态添加)、user(用户全局配置)、enterprise(企业托管配置)、managed(系统管理配置)。配置合并通过 getAllMcpConfigs() 函数实现,该函数调用各个作用域的配置获取函数并合并结果,后加载的作用域配置覆盖先前的同名服务器配置。
配置作用域详解
| 作用域 | 来源文件 | 优先级 | 适用场景 | 修改方式 |
|---|---|---|---|---|
| local | 命令行 --mcp 参数 | 最高 | 临时测试、一次性连接 | CLI 参数 |
| project | .mcp.json | 高 | 项目特定的工具链 | 编辑文件 |
| dynamic | 运行时 API | 中高 | 程序化添加服务器 | addMcpServer() |
| user | ~/.claude/config.json | 中 | 用户个人偏好 | /config 命令 |
| enterprise | managed-mcp.json | 低 | 组织统一标准 | 管理员控制 |
| managed | 系统策略 | 最低 | 受限环境强制配置 | 系统管理员 |
项目配置文件 .mcp.json 使用 McpJsonConfigSchema 验证,结构为 { "mcpServers": { "<name>": <config> } }。配置写入通过 writeMcpjsonFile() 函数实现,该函数采用原子写入模式:先写入临时文件,调用 datasync() 确保数据落盘,然后通过 rename() 原子性地替换原文件。写入过程保留原文件的权限模式(通过 stat() 读取),确保配置文件的安全属性不被意外修改。
服务器配置类型
每种服务器类型都有对应的 Zod 模式验证配置结构。Stdio 配置(McpStdioServerConfig)要求 command 字段非空,可选的 args 数组和 env 对象。SSE 和 HTTP 配置(McpSSEServerConfig、McpHTTPServerConfig)要求 url 字段,可选的 headers 对象、headersHelper 字符串和 oauth 配置。WebSocket 配置(McpWebSocketServerConfig)结构类似但不支持 OAuth。SDK 配置(McpSdkServerConfig)仅需 name 字段,用于进程内集成。Claude.ai 代理配置(McpClaudeAIProxyServerConfig)包含 url 和 id 字段,用于连接 Claude.ai 平台的代理服务器。
配置验证在加载时执行,通过 McpServerConfigSchema 的联合类型确保配置符合其中一种类型的要求。验证失败会生成详细的错误消息,指出具体的字段问题和预期的类型。环境变量通过 expandEnvVarsInString() 函数在配置加载后展开,支持 ${VAR} 和 $VAR 两种语法,允许配置引用运行时环境中的值而不硬编码敏感信息。
错误处理与恢复策略
MCP 服务实现了多层错误处理机制,从网络级别的传输错误到协议级别的 JSON-RPC 错误,再到应用级别的工具执行错误。每层错误都通过特定的错误类封装,携带足够的上下文信息用于诊断和恢复。错误处理策略根据错误类型选择适当的恢复路径:瞬时错误(网络中断、服务暂时不可用)触发自动重试,持久错误(认证失败、配置错误)转换为相应的服务器状态并通知用户。
错误分类与处理
传输层错误通过 onerror 回调传递到协议层,常见的网络错误(ECONNREFUSED、ETIMEDOUT)触发指数退避重连。协议层错误通过 JSON-RPC 的 error 对象返回,包含 code、message 和可选的 data 字段。标准的 JSON-RPC 错误码(如 -32600 Invalid Request、-32601 Method Not Found)直接传播到调用者。MCP 特定的错误码(如 -32001 Session Not Found)触发特殊处理逻辑,如会话重建或缓存清理。
工具执行错误通过 McpToolCallError 封装,该错误类区分用户可见的消息和遥测安全的消息。用户可见消息包含完整的错误详情和可能的恢复建议,遥测消息过滤敏感信息(如文件路径、用户数据)以保护隐私。错误对象的 _meta 字段保留服务器的元数据,允许 SDK 消费者访问额外的上下文信息。工具调用超时通过 AbortController 管理,超时后取消底层的网络请求并清理资源,防止资源泄漏。
诊断与日志记录
MCP 服务通过专用的日志函数 logMCPDebug() 和 logMCPError() 记录操作日志,这些函数自动添加服务器名称前缀以便过滤和搜索。调试日志记录连接建立、能力协商、工具发现等关键步骤,错误日志记录失败的操作和异常情况。敏感信息(如 OAuth 令牌、会话 ID)在日志输出前被过滤或脱敏,通过 getLoggingSafeMcpBaseUrl() 函数移除 URL 中的查询参数和路径。
错误遥测通过 logEvent() 函数发送到分析平台,包含错误类型、服务器标识、传输类型等元数据。遥测事件使用稳定的枚举值(如 tengu_mcp_server_needs_auth、tengu_mcp_oauth_refresh_failure)而非自由文本,确保分析查询的可靠性。失败原因通过专门的类型定义(MCPRefreshFailureReason、MCPOAuthFlowErrorReason)约束,避免拼写错误和不一致的值。遥测数据帮助团队识别常见问题模式和优化错误恢复策略。
下一步学习路径
掌握 MCP 服务的架构和实现后,建议继续探索以下相关主题以深入理解 Claude Code 的完整工具生态:
- 工具架构:Tool 接口与权限模型 - 了解 MCP 工具如何融入整体的工具架构,以及权限检查在工具调用链中的作用
- MCPTool:Model Context Protocol 深度集成 - 深入 MCPTool 的具体实现,包括工具调用的参数转换和结果处理
- 权限服务:PermissionMode 与动态权限验证 - 探索 MCP 工具的权限检查机制,以及如何与整体的权限系统集成
- 配置管理:settings、环境变量与迁移系统 - 学习 MCP 配置如何与其他配置系统集成,以及配置迁移的最佳实践