# 6. MCP

# 6.1 原理

MCP 把外部能力统一为 Server 暴露的 Tools、Resources、Prompts。Harness 作为 Client,负责连接、能力发现、命名空间、权限、调用、结果归一化和连接生命周期。

完整链路:

配置加载                                                       // 读取 MCP Server 地址、传输方式、认证和启停配置
 → 建立 stdio / SSE / Streamable HTTP / in-process transport   // 创建与 Server 匹配的双向通信通道
 → initialize 能力协商                                         // 交换协议版本及双方支持的能力集合
 → list_tools/list_resources/list_prompts                      // 发现 Server 当前暴露的工具、资源和提示模板
 → 转为模型 Tool Schema                                        // 将 MCP Tool 定义转换成模型可理解的函数 Schema
 → 模型生成 tool call                                          // 模型基于工具描述产生全限定名和结构化参数
 → 权限检查与审批                                              // 在访问外部 Server 前校验身份、范围和风险
 → call_tool                                                   // 通过已初始化连接发送实际工具调用请求
 → 进度/elicitation/错误/超时处理                              // 消费进度、补充信息请求及各种协议终态
 → 结果大小控制与上下文注入                                   // 对结果校验、裁剪,或保存到外部文件并只保留引用

关键问题:

  • 命名冲突:使用 mcp__server__tool 全限定名,权限规则也必须匹配全限定名,避免外部工具伪装成内建 Write
  • 能力与连接分离:工具清单可缓存,但连接断开、会话过期或授权过期要重新初始化。
  • 大结果限制:MCP 输出可能远超上下文,应分页、过滤、截断或存入外部文件,只给模型摘要和受控引用。
  • 不可信元数据:Tool 描述、schema、resource URI 和 _meta 都来自外部 Server,需要长度上限、协议校验和安全渲染。
  • 认证:远程 Server 可能使用 OAuth;Token 应放安全存储,支持过期刷新、撤销与 step-up authorization。

# 6.2 Claude Code 源码

  • MCP 名称解析与权限匹配显式使用全限定名,防止未加前缀的 MCP 工具碰撞内建工具:mcpStringUtils.ts:19mcpStringUtils.ts:55
  • Client 将远端工具转成内部 Tool,限制描述长度并设置权限检查;实际调用支持进度、超时、URL Elicitation 重试和结果归一化:client.ts:1772client.ts:2815client.ts:3031
  • 大输出会写入持久文件,并返回可继续按需读取的提示,而不是把全部内容塞入上下文:client.ts:2722

# 6.3 Codex 源码

Codex 在 Turn 开始时根据输入解析必须就绪的 MCP Server,再捕获 Step Context,避免工具清单尚未加载就发起采样:turn.rs:185。MCP 调用经过准备、审批决策、执行和指标上报;拒绝与取消会产生明确 ToolResult,而不是静默丢弃:mcp_tool_call.rs:228mcp_tool_call.rs:289

# 6.4 伪代码

async function invokeMcp(fqName, args):                               // 定义一次受控 MCP Tool 调用入口
    server, tool = parseQualifiedName(fqName)                         // 从全限定名解析 Server 和 Tool,避免名称碰撞
    conn = connectionManager.ensureReady(server)                      // 获取已完成 initialize 的健康连接,必要时重连
    spec = conn.cachedTools[tool]                                     // 读取协商后缓存的工具描述、Schema 和超时配置
    validatedArgs = validate(spec.inputSchema, args)                  // 按远端工具 Schema 严格验证模型参数
    decision = permission.check(fqName, validatedArgs)                // 按 Server、Tool 和参数范围执行权限判断
    requireAllowed(decision)                                         // 非 ALLOW 决策立即终止,不能向远端发送请求
    result = await withTimeout(                                      // 给整个远程调用设置可取消的超时边界
        conn.callTool(tool, validatedArgs, onProgress),              // 发起 call_tool,并把进度映射为本地事件
        spec.timeout)                                                 // 使用工具级或 Server 级配置的最大时长
    normalized = validateAndNormalizeMcpContent(result)               // 校验外部内容类型并转成内部统一结果结构
    if tokenEstimate(normalized) > outputBudget:                      // 判断完整结果是否会挤占过多模型上下文
        handle = persistLargeOutput(normalized)                       // 将大结果写入持久化存储并生成受控访问引用
        return summarizeWithHandle(normalized, handle)                // 只返回有界摘要和后续按需读取方式
    return normalized                                                 // 小结果可直接反馈给 Agent Loop

# 6.5 面试题

问:MCP 是不是 Function Calling 的替代品?

不是。Function Calling 是模型输出结构化工具意图的机制;MCP 是 Harness 与外部能力提供方之间的标准协议。常见链路是“模型 Function Call → Harness 路由 → MCP call_tool”。

# 6.6 原理深化:MCP 的一致性边界是“连接版本 + 工具快照”

MCP Client 不能只缓存一张工具表。initialize 协商得到的是当前这次连接支持的能力,list_tools 得到的是该连接版本下的工具快照;模型看到 Schema 后,到实际 call_tool 前如果 Server 重连、工具被禁用或 Schema 改变,就会出现“判断时和执行时使用了不同工具定义”的问题。可靠实现应给连接和工具目录增加 generation/version,Step 保存对应快照,执行时确认连接版本仍然兼容;不兼容时终止当前调用并重新采样,而不是拿旧参数调用新工具。

Codex 在 Turn 前先解析显式提及所依赖的 Server,再构造 Skill、Plugin 与工具注入,这体现“依赖就绪先于 Prompt 固化”的顺序:turn.rs:186turn.rs:728。Claude Code 则在远端 Tool 转换为内部 Tool 时完成命名空间、描述长度和权限元数据治理,调用后再对外部内容做大小控制。两者共同说明 MCP 只是能力来源,进入 Harness 后必须服从统一 Tool 生命周期,不能建立一条绕过审批、结果上限和 Trace 的“协议捷径”。

远程调用还包含三种独立失败:Transport 失败表示消息未可靠到达,Protocol 失败表示请求/响应不满足 MCP 契约,Tool 失败表示 Server 已执行但业务动作失败。重试策略必须据此区分:Transport 断线只有在确认未执行或工具幂等时才能重放;业务失败应作为 ToolResult 返回模型;认证或 Elicitation 则进入显式等待状态。把三者都包装成字符串 error,会同时破坏恢复、UI 和可观测归因。

最后更新: 2026/8/5 22:05:21