返回《Claude Code 架构设计指南》

MCP 集成,外部能力的统一收编

IT周瑜

MCP 是一个开放协议,让外部进程以标准方式提供工具、提示词和资源。对 harness 来说,它解决的问题是接入成本。模型能用的工具数以百计,每家的接入方式各写一遍没有出路,统一收进一个协议,外部的能力就能批量变成内部的一份子。这份源码的 MCP 实现在 src/services/mcp/,核心是连接管理和一层转换包装。

连接这一层由 MCPConnectionManager 负责,每个服务器一条连接,传输走标准输入输出或者 HTTP。连接做了记忆化,同一服务器不重复握手,断线有重连逻辑。批量连接在启动时并行展开,哪个服务器慢了不拖累别人。服务器的工具清单拉回来之后,fetchToolsForClient 把每一个 MCP 工具包装成内部 Tool 接口的实现,命名规则是 mcp__ 服务器名 工具名,双下划线分隔,从名字就能反查来源。包装层做几件关键的事,服务器的 readOnlyHint 映射成内部的并发安全标记,直接进入第 5 章的调度体系,参数 schema 转成 zod,结果按内部约定转换。

MCP 服务器 stdio 或 HTTP 外部进程 连接管理 MCPConnection Manager 连接记忆化 断线重连 工具 命名 mcp__服务器__工具 包装成内部 Tool 斜杠命令 MCP prompts 注册成命令 资源 resources 挂进资源表 fetchTools readOnlyHint 映射为并发安全标记 大量工具按需加载 靠 ToolSearch 拉取
图 10-1 一个服务器进来,三种能力各走各的管道

工具之外的两条管道顺带说一下。MCP 协议的 prompts 能力被注册成斜杠命令,用户在输入框敲斜杠能直接调服务器提供的提示词模板,命令系统里 type 为 prompt 的那一类就是干这个的。resources 资源挂进全局资源表,供需要的机制按名取用。于是单个服务器的一次接入,同时扩充了工具、命令、资源三个池子。

工具的调用回路还有双向的细节。MCP 服务器可以反过来向客户端发起 elicitation 请求,比如打开一个链接完成授权,上下文对象上有个专门的 handleElicitation 回调,终端模式和无头模式各自提供实现。工具名本身也成了元数据来源,源码里有一组工具函数从 mcp__ 前缀的名字里反查服务器、判断服务器类型,权限建议和连接管理都靠这套解析。包装出来的工具同样支持上一章的输出结构声明,服务器给的 outputSchema 原样透传。

工具数量一多,新的矛盾出现了。全量 schema 塞进每次请求,提示词预算吃不消,模型的注意力也被稀释。这套源码的解法是按需加载,配套的 ToolSearch 工具让模型自己搜索和加载工具,服务器可以声明哪些工具应该延迟加载、哪些必须常驻。加载状态还要扛住上下文压缩,第 8 章压缩后的工具增量附件会把已发现的工具名补回去,免得压缩一轮之后模型忘了自己加载过什么。normalizeMessagesForAPI 那边也有配套处理,工具引用块只在启用工具搜索时保留。

权限上 MCP 工具没有豁免。包装出来的 Tool 同样进第 6 章的决策链,服务器可以建议前缀规则帮用户预填白名单,但建议归建议,裁决照旧。这个安排让外部能力再庞杂,安全模型始终只有一套。连接的生命周期也纳入统一管理,服务器中途掉线,连接层负责重连和状态同步,工具清单有变化时通知相关的缓存失效,第 9 章的 ConfigChange 事件也让用户脚本能感知到配置层面的增减。对模型来说这一切都不可见,它只知道自己多了一批名字带双下划线的工具。