Codex 源码解析 · 第 33 讲
Codex 源码解析系列 第 33 讲:MCP 集成——目录冻结、审批裁决与结果治理 基于 OpenAI Codex 源码 · 2026-10-27 💡 本讲一句话: Codex 把每次 MCP 工具调用变成一次"带租约的快照执行"——先冻结目录与客户端绑定,审批裁决通过后才在版本锁内真正发起 tools/call;目录中途刷新会直接拒绝这次调用。读完你能看懂 PreparedMcpCall、catalog revision 和结果治理三道防线各自防什么。 一、入口:一次 MCP 调用的第一道闸 模型在响应里生成一个 function call,工具层把它路由到 McpHandler (core/src/tools/handlers/mcp.rs)。这个 handler 做了一件反直觉的事: 先"准备",后"执行" ——prepare_mcp_call() 在任何网络请求之前就把当前 MCP 目录和客户端绑定冻结下来: 📄 codex-rs/core/src/tools/handlers/mcp.rs (第 176-241 行) impl McpHandler { // MCP 工具的统一 handler:模型侧的 function call 都从这进 async fn handle_call( // 一次调用的总入口(async,返回 ToolOutput) &self, // 借用 handler 自身(持有 tool_info / spec) invocation: ToolInvocation, // 调用上下文:session/step_context/call_id/payload… ) -> Result<Box<dyn crate::tools::context::ToolOutput>, FunctionCallError> { // 成功=工具输出,失败=回给模型的错误 let prepared_mcp_call = invocation // ★ 先"准备"这次调用——注意不是直接发请求 .session // 从 session… .prepare_mcp_call( // …冻结当前 MCP 目录 + 客户端绑定 &self.tool_info.server_name, // 目标 server(codex_apps / 用户配置的 server) self.tool_info.tool.name.as_ref(), // 目标工具名 ) // 返回 Option:None=该工具此刻对模型不可用 .await; // 内部会先 refresh_mcp_if_dirty() let result_metadata_capture_allowed = false; // 结果元数据捕获默认关闭(安全面收敛) … // (中间是 payload 解包、originating_item_id 解析,略) let truncation_policy = prepared_mcp_call // ★ 截断策略也从"同一份快照"里取——关键! .as_ref() // Option → 引用 .and_then(codex_mcp::PreparedMcpCall::output_token_limit) // server 级 token 上限(若配置了) .map(TruncationPolicy::Tokens) // 有则按 token 截断… .unwrap_or(step_context.settings.model_info.truncation_policy.into()); // …没有则回退模型默认策略 let started = Instant::now(); // 计时起点(后面算 wall_time) let result = handle_mcp_tool_call( // 进入真正的调用流水线(审批/执行/治理) Arc::clone(&session), &step_context, &cancellation_token, // session + step 上下文 + 取消令牌 call_id.clone(), originating_item_id, &self.tool_info,…
阅读全文:Codex 源码解析 第 33 讲