模型看見的工具清單,是一次採樣算出來的
同一段對話、同一份配置,下一輪採樣卻可能多出 MCP 名字、協作入口或 tool_search。變的是這一次允許廣告哪些 handler。
tool_search。
- 解析這一步的 MCP 綁定mcp.rs L310
- 採集 MCP 目錄,交給規劃函式turn.rs L1494
- 按身份決定是否走完整來源spec_plan.rs L889
- 登記 shell、資源、實用、協作spec_plan.rs L930
- 追加 MCP 並套上暴露策略spec_plan.rs L148
- 有 deferred 才掛 tool_searchspec_plan.rs L335
- 只把 is_direct 的 spec 發給模型spec_plan.rs L484
- 凍進 StepContext.tool_routerstep_context.rs L44
- build_prompt 讀取可見表turn.rs L1320
build_tool_router。邏輯軌跡右側行號對應 openai/codex 倉庫 commit 4f39251a01。你剛接上 MCP filesystem,模型這一輪還在跑。要是按整輪用戶對話凍工具表,新連上的伺服器得等下一條用戶消息才進菜單。要是每調一次工具凍一次,同一次採樣裏並行的兩次調用可能看見兩份不同的表。
廣告一份、執行另一份,或者執行到一半表變了,是工具表最常見的翻車。
Codex 把這一次採樣的模型、審批、環境、MCP 綁定和已經算完的 tool_router 一起放進 StepContext。註釋寫明它是 request-scoped:採樣請求之間可以變,同一次採樣裏不變。tool_router 是 this exact sampling request 的計劃。
採樣進行期間,模型看見的名字和能 dispatch 的 runtime 都讀這份快照。MCP 伺服器在這次採樣中途掉綫,改不了已經算完的表。下一次採樣會再走 mcp_runtime_for_step,綁定可能複用,也可能換成空綁定。出處:codex-rs/core/src/session/step_context.rs 第 17 至 47 行;codex-rs/core/src/session/mcp.rs 第 348 至 357 行
所以凍結單位是 step,不是 turn。一個 turn 裏可以有多次採樣,每次重建一份 StepContext。出處:codex-rs/core/src/session/turn.rs 第 1318 至 1320 行
可見和可執行共用一份快照,下一輪再吸收新連接。換個語言重寫,最小形態仍是一個函式:輸入開關、MCP 目錄、身份,輸出可見表和可執行表,請求開始時算一次,算完就凍。
審批模型如果看見工作模型那整張 MCP 和協作工具表,它就能去 spawn_agent、讀外部資源,審批本身被繞開。先註冊全表再過濾,漏一條分支就會把不該給的名字送出去。
規劃函式按身份決定走哪些來源。Guardian 評審員的標籤必須恰好是 guardian。權限檔案不是 Managed,函式直接返回,註冊表是空的。Managed 且有環境時,最多三件:exec_command、write_stdin、可選 view_image。普通會話才走 shell、MCP 資源、實用工具、協作這四組,再追加 MCP、擴展和動態工具。出處:codex-rs/core/src/guardian/review.rs 第 212 至 220 行;codex-rs/core/src/tools/spec_plan.rs 第 144 至 164 行、第 889 至 934 行
讀盤也沒有一等入口。handlers 目錄裏沒有 ReadFileHandler。模型要讀檔案,走 exec_command、MCP filesystem,或 view_image 內部的 exec-server API。
身份一變,整組來源都不走。預設關上門發生在評審員、環境缺失、特性關閉這幾處,換語言也用得上。
MCP 工具的 schema 很貴。全掛上,初始請求的 token 會被描述文本吃掉。兩個 server 都提供 read_file,不消歧就會撞名。
暴露態把註冊表拆成幾條可見面。Direct 進初始表。Deferred 只留給 tool_search。Hidden 只留給 dispatch。搜索開着時,MCP 工具預設 Deferred,不進初始可見表。出處:codex-rs/tools/src/tool_executor.rs 第 51 至 80 行;codex-rs/core/src/mcp_tool_exposure.rs 第 90 至 94 行
finalize_tool_router 只在兩件事同時成立時才掛 tool_search:模型支持 search tool,並且註冊表裏還有至少一個 deferred 且帶 search_info 的工具。沒有 deferred,它不會進表。出處:codex-rs/core/src/tools/spec_plan.rs 第 335 至 370 行、第 578 至 580 行
兩個 MCP server 的同名工具,在規範化階段消歧:預設加 mcp__ 前綴,sanitize 後仍撞車再加 12 位哈希後綴。外部工具撞到已佔用的核心名,直接跳過。倉庫根 AGENTS.md 仍寫着 mcp_connection_manager.rs,當前樹裏沒有這個檔案,消歧在 tools.rs。出處:codex-rs/codex-mcp/src/tools.rs 第 1 至 5 行、第 113 至 151 行;AGENTS.md 第 35 行
可見、可檢索、可執行是三份集合。token 預算緊就推遲發現,核心名保留、外部讓路。這些不依賴 Rust。
DeepSeek Harness:插件往裝配器裏投 schema
DSH 沒有集中規劃函式。每個工具包在 assemble 時通過 systemPrompt.tools() 投遞 schema,裝配器再用部署配置的 toolOrder 排座位。未點名的工具插在保留標記 <unlisted-tools> 處,按名字字典序。漏寫這個標記,裝配期直接失敗。
裝上 @deepseek-ai/dsh-tool-fs,模型就看見一等工具 read。描述要求用它讀文本。Codex 把讀盤留給 shell、MCP 和內部 API,規劃函式裏沒有 read_file 這個入口。
Claude Code:靜態基表加 MCP,內置優先
getAllBaseTools 是一張寫死的陣列,FileReadTool 是一等成員。請求時 assembleToolPool 先過濾 deny 規則,再把內置和 MCP 各自按名字排序後拼接。內置在前,撞名時內置贏。註釋寫明這樣排是為了 prompt cache:MCP 插到內置中間,後面的 cache key 會全失效。
Claude Code 的基表打開一個檔案就能數完。Codex 要數清單必須走完 add_core_tool_sources,同一套規劃能按身份和預算裁剪。
這一輪菜單上還剩什麼
先開 MCP 和搜索,確認 mcp__filesystem__read_file 在灰色區、tool_search 在菜單上。然後推演三步:搜索仍開着,但 MCP 全部變成 Hidden;採樣中途掉綫;再走下一輪採樣。每一拍寫下可見表、灰色區和 tool_search 在不在。
進階一問:把身份改成 Guardian 評審員,權限先 Managed 再改成其他,菜單分別是什麼,為什麼不是少掛幾件工具。
StepContext 把它們凍在採樣邊界。模型仍可用 exec_command 讀檔案,即使表上沒有 read_file。