乐于分享
好东西不私藏

Claude Code 源码揭秘:MCP 协议集成全拆解,1 个协议 7 种传输,AI 编程工具如何连接整个开发工具链

Claude Code 源码揭秘:MCP 协议集成全拆解,1 个协议 7 种传输,AI 编程工具如何连接整个开发工具链

🚩 2026 年「术哥无界」系列实战文档 X 篇原创计划 第 75 篇,Claude Code 源码揭秘系列第 7 篇

大家好,欢迎来到 术哥无界 | ShugeX | 运维有术

我是术哥,一名专注于 AI 编程、AI 智能体、Agent Skills、MCP、云原生、AIOps、Milvus 向量数据库的技术实践者与开源布道者

Talk is cheap, let’s explore。无界探索,有术而行。

本系列已更新至第 7 篇,往期精彩:

封面图 – MCP 架构全景图

图 1:Claude Code MCP 架构全景:从配置发现到连接管理,从 Elicitation 交互到安全权限的完整生态

AI 编程工具有一个绕不开的最后一公里问题:不是 AI 不够聪明,是它碰不到你的工具。

你能读代码、能写代码、能理解架构,但你需要调用 GitHub API、需要操作 Kubernetes 集群、需要连接数据库、需要触发 CI/CD 流水线——这些事情,传统 AI 只能告诉你怎么做,没法帮你做。就像一个只带脑子没带手的顾问。

MCP(Model Context Protocol)就是 Anthropic 给出的答案:定义一套 AI Agent 与外部工具之间的标准化通信协议。而 Claude Code 不只是实现了 MCP 客户端——它围绕这个协议搭建了一套完整的生态系统:多层级配置、7 种传输方式、自动重连、OAuth 认证、Elicitation 双向交互……翻完 services/mcp/ 目录下几千行源码,这套集成的工程密度相当高。

今天这篇,就来完整拆解 Claude Code 的 MCP 协议集成。

1. MCP 是什么:AI Agent 的 USB 协议

协议定义

MCP 是 Anthropic 定义的 AI-Agent-to-Tool 通信协议。如果打个比方:MCP 就是 AI Agent 世界的 USB 协议

在 USB 出现之前,键盘用 PS/2 接口,打印机用并口,鼠标用串口,每个外设一种线。USB 统一了这一切:一套协议,所有设备通用。MCP 做的是同一件事——在 MCP 之前,每个 AI 工具集成都是定制开发:GitHub 有 GitHub 的 API,Jira 有 Jira 的 API,Slack 有 Slack 的 SDK。MCP 统一了接入方式:AI Agent 只需要实现一个 MCP 客户端,就能和任何实现了 MCP Server 的工具通信。

底层协议是 JSON-RPC 2.0,支持三种核心能力:

能力
说明
示例
Tools
工具调用
执行 SQL 查询、创建 GitHub Issue
Resources
资源读取
读取数据库 Schema、获取文件内容
Prompts
提示模板
预定义的 Prompt 模板

传输方式也做了标准化:stdio(子进程)、SSE(Server-Sent Events)、HTTP(Streamable HTTP)、WebSocket,以及 Claude Code 自己扩展的 SDK、InProcess、claudeai-proxy。

Claude Code 中的 MCP 架构

Claude Code 的 MCP 实现不是简单的客户端封装,而是分成了清晰的 5 层:

配置层(config.ts / officialRegistry.ts)  → 连接层(useManageMCPConnections.ts / client.ts)    → 交互层(elicitationHandler.ts)      → 安全层(auth.ts / channelPermissions.ts)        → 工具层(MCPTool.ts / vscodeSdkMcp.ts)

每一层都有独立的责任和复杂度。接下来逐层拆解。

2. 配置与发现:6 层优先级 + 官方注册表

多层级配置系统

config.ts 有 1578 行,核心入口是 getClaudeCodeMcpConfigs()。它负责合并所有层级的 MCP 配置,层级从低到高:

优先级
来源
说明
1(低)
claude.ai 连接器
远程 Web 平台配置
2
插件 MCP 服务器
来自插件系统
3
全局用户配置
~/.claude

 全局设置
4
项目配置
.mcp.json

,可提交到 Git
5
本地配置
Gitignore 的项目级配置
6(高)
企业配置
独占模式,启用后其他层级全部失效

企业配置是独占模式:doesEnterpriseMcpConfigExist() 返回 true 时,只有企业配置生效。这个设计很明确:企业管控优先于个人偏好

配置去重机制

多层级配置带来一个现实问题:同一个 MCP 服务器可能在好几个层级里都配了。dedupPluginMcpServers() 和 dedupClaudeAiMcpServers() 负责去重,基于签名(URL 或 command 数组)判断是否重复。去重策略是手动配置优先:用户显式配置的版本覆盖自动发现的版本。

环境变量展开

expandEnvVars() 支持两种语法:

${VAR}# 直接引用${VAR:-default}# 带默认值

这个细节看着不起眼,但在企业环境里很关键:数据库连接串、API Key 这些敏感信息不会明文写在 .mcp.json 里,而是通过环境变量注入。

官方注册表

officialRegistry.ts 从 api.anthropic.com/mcp-registry/v0/servers 获取官方 MCP 服务器列表。启动时 prefetchOfficialMcpUrls() 以 fire-and-forget 方式预取,不阻塞主流程。isOfficialMcpUrl() 用来判断一个 URL 是否属于官方注册的服务器——这和后面的安全策略挂钩:官方服务器和第三方服务器的信任等级不同。

企业策略过滤

isMcpServerAllowedByPolicy() 支持按名称、命令、URL 三种维度匹配黑白名单。企业的 allowedMcpServers / deniedMcpServers 可以精确控制员工能用哪些 MCP 工具。这又是企业场景的现实需求:不是所有 SaaS 工具都允许接入公司内部数据。

3. 连接管理:7 种传输 + 2 阶段加载

这是整个 MCP 集成里代码量较大、设计密度较高的部分。useManageMCPConnections.ts 有 1141 行,client.ts 有 1343+ 行。两个文件加起来接近 2500 行,管理着 MCP 连接的完整生命周期。

传输层矩阵

Claude Code 实现了 7 种传输方式,覆盖从本地到远程、从子进程到进程内的所有场景:

传输方式
实现
适用场景
stdio StdioClientTransport
本地命令行工具,子进程通信
SSE SSEClientTransport
远程 HTTP 流式推送
HTTP StreamableHTTPClientTransport
标准 HTTP 请求/响应
WebSocket WebSocketTransport
实时双向通信
SDK
内部传输
插件集成
claudeai-proxy
通过 claude.ai 代理
Web 端远程访问
InProcess InProcessTransport
进程内直接调用,零网络开销

InProcessTransport 值得多说两句。它用 createLinkedTransportPair() 创建一对配对的传输端点,send() 的消息通过 queueMicrotask 异步投递到对端的 onmessage。Chrome MCP 和 Computer Use 就是通过这种方式集成的:不走网络,不走子进程,直接在进程内调用。零延迟,零序列化开销

// 源码路径:services/mcp/InProcessTransport.ts// 核心逻辑:配对传输,进程内直接通信send(message) → queueMicrotask → 对端 onmessage

两阶段加载策略

连接管理采用了两阶段加载:

Phase 1 — 本地配置(快速,纯文件读取):启动时立即加载本地所有层级的 MCP 配置,建立连接。

Phase 2 — claude.ai 远程配置(可能较慢,需要网络请求):异步拉取 Web 端的 MCP 配置,加载完成后补充或覆盖本地配置。

这个设计很务实:不让远程配置拖慢启动速度。用户打开 Claude Code 就能先用本地配置的工具,远程配置后台慢慢加载。

连接状态机

一个 MCP 连接从发现到可用,经历的状态变迁:

discovered → pending → connecting → connected                                          ↓ (error/onclose)                                     reconnecting → connected                                          ↓ (max retries)                                          failed                                          ↓ (auth required)                                       needs-auth                                          ↓ (user disable)                                        disabled

自动重连只针对 SSE/HTTP/WS 等远程传输。stdio 和 sdk 不重连——子进程挂了就是挂了,重连没有意义。

重连参数:

// 源码路径:services/mcp/useManageMCPConnections.tsconst MAX_RECONNECT_ATTEMPTS = 5const INITIAL_BACKOFF_MS = 1000// 初始退避 1 秒const MAX_BACKOFF_MS = 30000// 最大退避 30 秒

指数退避,5 次上限。第一次 1 秒后重试,第二次 2 秒,第三次 4 秒……超过 5 次标记为 failed。这和网络协议的重连策略一模一样,不算新奇,但该有的都有了。

连接生命周期状态机

图 2:MCP 连接生命周期状态机:从 discovered 到 connected/failed/disabled 的完整变迁路径

批量更新优化

MCP_BATCH_FLUSH_MS = 16ms——一个容易被忽略但很精巧的优化。16ms 是一帧的时间(60fps)。当多个 MCP 连接的状态在同一帧内发生变化时,不会逐个触发 React 状态更新,而是在 16ms 窗口内合并成一次批量更新。这对有十几个 MCP 服务器同时连接的场景很关键:避免状态更新风暴导致 UI 卡顿。

动态启停

toggleMcpServer 支持运行时动态启用/禁用 MCP 服务器,不需要重启 Claude Code。禁用的服务器进入 disabled 状态,工具列表被清空,不再参与后续的工具发现和调用。

超时设计的有意思之处

请求超时 60 秒(MCP_REQUEST_TIMEOUT_MS = 60000),这个很正常。但工具调用超时设了 100,000,000 毫秒(约 27.8 小时)

// 源码路径:services/mcp/client.tsconst DEFAULT_MCP_TOOL_TIMEOUT_MS = 100_000_000  // ~27.8 hours

乍一看有点离谱,但仔细想想合理:MCP 工具可以是任何东西,包括长时间运行的任务(比如跑测试套件、训练模型、执行大型数据库迁移)。如果超时设太短,这些合理的长任务会被误杀。27.8 小时基本上等于”不限制”——但留了一个上限,防止真正的死循环。

4. Elicitation:工具也可以跟用户聊天

这是整个 MCP 集成里让我觉得设计密度相当高的功能。

什么是 Elicitation

传统的工具调用模型是单向的:AI 调用工具 → 工具返回结果。但现实里很多工具需要和用户交互:OAuth 授权需要用户在浏览器里点确认,支付工具需要用户选择支付方式,部署工具可能需要用户确认目标环境。

Elicitation 就是解决这个问题的:MCP 服务器可以主动向用户提问,用户回答后,工具拿到答案继续执行

两种模式

模式
行为
典型场景
form
弹出表单让用户填写
工具需要用户输入参数
url
打开 URL 让用户完成操作
OAuth 授权、外部服务确认

三阶段流程

阶段 1:MCP 服务器发送 ElicitRequest  ↓阶段 2:Claude Code 通过 AppState.elicitation.queue 弹出 UI  ↓ (Hook 预处理:runElicitationHooks)  ↓阶段 3:用户响应 → Hook 后处理 → 返回 ElicitResult → MCP 服务器

elicitationHandler.ts(313 行)实现了完整的三阶段流程。几个关键细节:

Hook 系统runElicitationHooks() 可以在 UI 展示前做预处理,甚至程序化自动响应——不需要用户干预。runElicitationResultHooks() 可以在返回前修改用户的响应。这套 Hook 机制意味着 Elicitation 可以被完全自动化:对于已知的安全操作,Hook 直接返回预设答案,用户全程无感。

URL 模式的完成确认:URL 模式下,用户被导向外部页面完成操作(比如 OAuth)。但问题是:Claude Code 怎么知道用户已经操作完了?答案是 ElicitationCompleteNotificationSchema——MCP 服务器在检测到用户操作完成后,主动发送完成通知。还有一个 onWaitingDismiss 回调,处理用户在等待期间关闭了弹窗的情况。

// 源码路径:services/mcp/elicitationHandler.ts// 三阶段核心流程(简化)MCP Server → ElicitRequest  → runElicitationHooks()     // 预处理,可程序化自动响应  → UI 展示(form/url)  → 用户响应  → runElicitationResultHooks()  // 后处理,可修改响应  → ElicitResult  → MCP Server
Elicitation 三阶段交互流程

图 3:Elicitation 三阶段交互流程:从 MCP Server 发起请求,到 Hook 预处理、UI 展示、用户响应、Hook 后处理,再到最终返回结果

设计哲学

Elicitation 打破了传统”AI 调用工具、工具返回结果”的单向模型。它让工具变成了有状态、有交互的参与者。这个设计的意义在于:MCP 服务器不需要把所有需要的输入都提前声明在参数里——它可以在执行过程中按需获取。

说到底,Elicitation 让 MCP 从”函数调用”升级成了”对话式调用”。工具不再是被动的执行者,而是可以主动和用户沟通的协作者。

5. 安全与权限:OAuth + Channel + 白名单三重门

MCP 连接涉及外部工具,安全问题不可能回避。Claude Code 在这一层投入的代码量不小:auth.ts 有 1426+ 行,channelPermissions.ts 有 240 行,channelAllowlist.ts 有 76 行。

OAuth 认证完整实现

ClaudeAuthProvider 实现了完整的 OAuth 2.0 流程,不是阉割版:

能力
说明
动态客户端注册(DCR)
不需要预先注册 client_id
PKCE
防止授权码拦截攻击
Token 刷新
Access Token 过期后自动续期
Token 撤销
RFC 7009 标准
Step-up 认证
检测 403 insufficient_scope,触发权限升级

认证发现也做了自动化:按照 RFC 9728 → RFC 8414 的顺序自动发现 OAuth 服务器元数据。这意味着 MCP 服务器只需要声明自己的 OAuth 端点,Claude Code 就能自动完成整个认证流程。

// 源码路径:services/mcp/auth.ts → ClaudeAuthProvider// 认证发现链RFC 9728 → RFC 8414 → 自动发现 OAuth Server Metadata// 支持 authServerMetadataUrl 显式配置覆盖

还有个有意思的细节:normalizeOAuthErrorBody() 专门处理 Slack 等非标准 OAuth 服务器的错误响应。现实世界不是所有服务都严格遵循 OAuth 规范,这种兼容性处理体现了实战经验。

Token 存储用了 macOS Keychain,基于 getServerKey() 生成唯一键(名称 + 配置哈希)。认证缓存 TTL 是 15 分钟(MCP_AUTH_CACHE_TTL_MS = 15 * 60 * 1000)。

XAA 认证

XAA(Cross-App Access)是跨应用访问的认证机制。当一个 MCP 服务器需要访问另一个应用的资源时,XAA 提供了安全的跨应用认证通道。这个场景在微服务架构和 SaaS 集成中很常见。

Channel 权限系统

Channel 是 MCP 服务器与 Claude Code 之间的消息通道。channelPermissions.ts 实现了权限中继:

// 源码路径:services/mcp/channelPermissions.ts → shortRequestId// 生成 5 字母 ID:a-z 减 l,25^5 ≈ 9.8M 空间// 还有 ID_AVOID_SUBSTRINGS 过滤不雅词汇

这个 ID 生成器有个细节:字母表去掉了 l(小写 L),因为 l 和 1 容易混淆。25 个字母的 5 次方约 980 万种组合,对于权限请求 ID 来说空间足够大。

白名单门控

channelAllowlist.ts 检查 MCP 插件是否在 GrowthBook 白名单中,isChannelsEnabled() 是全局开关。两层门控:全局开关控制 Channel 功能是否开启,白名单控制具体哪个插件可以使用 Channel。

安全权限体系

图 4:MCP 安全权限体系:OAuth 认证层、Channel 权限层、白名单门控的三重防护

你在项目中用过类似的 OAuth 动态注册 + 权限中继方案吗?欢迎在评论区聊聊。

6. 工具集成:MCPTool 桥接一切

MCPTool:统一适配器

MCPTool.ts 只有 77 行,但它做的事很关键:把 MCP 工具转换为 Claude Code 内部的 Tool 接口。

// 源码路径:services/mcp/MCPTool.ts// 核心属性{  isMcp: true,                          // 标记为 MCP 工具  inputSchema: z.object({}).passthrough(),  // 允许任意输入  checkPermissions: passthrough,          // 权限由上层系统控制}

passthrough() schema 意味着输入校验不在这一层做——MCP 工具的参数校验由 MCP 服务器自己负责。Claude Code 只负责转发。这是一个合理的职责划分:每个 MCP 服务器最清楚自己需要什么参数。

工具调用完整链路

当 Claude 决定调用一个 MCP 工具时,请求走过这样一条链路:

Claude(LLM 输出 tool_use)  → MCPTool.call()    → MCP Client(参数序列化)      → Transport 层(stdio/SSE/HTTP/WS/...)        → MCP Server(执行具体操作)          → Result(返回结果)    ← Truncation(结果截断,防止超出上下文窗口)    ← Permission Check(权限检查)  ← 返回给 Claude

整条链路中最容易出问题的是 Truncation 环节:MCP 工具的返回结果可能很大(比如一整个数据库查询结果),需要截断后才能塞回 Claude 的上下文窗口。这个截断逻辑在 MCP Client 层处理。

MCP 工具调用链路

图 5:MCP 工具调用完整链路:从 Claude 的 tool_use 输出,经过 MCPTool 适配、Transport 传输、MCP Server 执行,再经过 Truncation 和 Permission Check 返回

VSCode SDK 双向通信

vscodeSdkMcp.ts(112 行)实现了 Claude Code 与 VSCode 扩展的双向 MCP 通信:

  • notifyVscodeFileUpdated():文件变更时通知 VSCode
  • setupVscodeSdkMcp():设置通知处理器,包括 log_event(从 VSCode 接收事件)和 experiment_gates(向 VSCode 发送实验开关)

这不是单向的”AI 使用工具”,而是双向的协同:VSCode 可以向 Claude Code 推送事件,Claude Code 也可以向 VSCode 发送控制指令。

MCP 资源(Resources)

除了工具调用,MCP 还支持资源读取。ResourceListChangedNotificationSchema 监听资源列表变更,ReadMcpResourceTool 提供资源读取能力。这允许 MCP 服务器暴露只读资源(如配置文件、数据库 Schema)给 Claude,而不需要经过工具调用。

连接错误处理

client.ts 定义了完整的错误类型体系:

错误类型
说明
McpAuthError
认证失败
McpSessionExpiredError
会话过期
McpToolCallError
工具调用错误

还有终端错误检测:ECONNRESET、ETIMEDOUT、EPIPE、EHOSTUNREACH 等网络错误被识别为致命错误,连续 3 次(MAX_ERRORS_BEFORE_RECONNECT = 3)触发重新连接。connectToServer 使用 memoize 缓存,避免重复建立连接。fetchToolsForClient 和 fetchCommandsForClient 也有缓存,同一连接的工具列表和命令列表不会重复拉取。

总结

拆完整个 MCP 集成,有三个观察。

MCP 的本质是 AI Agent 的 USB 协议。标准化接入方式带来的好处和 USB 一样:一次实现,到处使用。Claude Code 围绕 MCP 搭建的生态系统——多层级配置、7 种传输、自动重连、OAuth 认证——不是简单的客户端封装,而是一个完整的连接管理框架。这个框架的复杂度反映了一个现实:生产级的 AI Agent 工具集成,远比调一个 API 复杂得多。

Elicitation 是杀手级功能。它打破了工具调用的单向模型,让 MCP 服务器可以主动和用户交互。OAuth 授权、参数确认、外部操作确认——这些场景没有 Elicitation 根本做不了。从工程角度看,Hook 系统让 Elicitation 可以被完全自动化,这对用户体验很关键:普通用户看到弹窗交互,高级用户配置 Hook 实现无感操作。

对 AI Agent 开发者的启示:Claude Code 的 MCP 实现展示了一个成熟的 AI Agent 工具集成应该长什么样。不是简单的 SDK 封装,而是配置管理、连接生命周期、安全认证、用户交互每一层都要考虑到。特别是那个 27.8 小时的工具调用超时——它说明 AI Agent 调用的工具可能是任何东西,超时策略不能用传统 API 的思维来设计。

从趋势看,MCP 这类标准化协议会成为 AI Agent 生态的基础设施。就像 USB 让外设即插即用,MCP 让工具即插即用。Claude Code 的实现给出了一个相当完整的参考。

好啦,谢谢你观看我的文章,如果喜欢可以点赞转发给需要的朋友,我们下一期再见!敬请期待!

扫码关注,获取更多 AI 工具的实战经验和最佳实践。不错过每一篇干货!