OpenAI Codex · MCP 與 Skills

MCP 接進來:模型看見翻譯過的名字

外部 server 的工具要先過一層翻譯才進模型眼睛。skill 目錄常在,缺 MCP 時另問人。

課程目標讀完能說清三件事。Codex 當 client 時,外部工具怎麼變成模型可見名。兩家店清洗後撞名,怎麼消歧。skill 目錄為什麼不看 MCP 活沒活著。
先玩一遍 · 一家店接進來,名字怎麼變
一個 MCP server 接進來:工具怎麼變成模型看得見的能力
接入場景
右邊兩檔會撞名。切一下,看清洗之後誰被加上雜湊。
門外 · 原始 tools/list進門 0
還沒接任何人。
模型眼前 · 翻譯後的名字可見 0
清單空著。
邏輯軌跡 · 動畫每一步對應原始碼裡的哪一段
  1. 連線集整份釋出,已有 binding 繼續拿自己那份連線runtime.rs L246
  2. 各家 tools/list 匯成一張表,再交給命名翻譯tool_catalog.rs L153
  3. 給名稱空間加上歷史前綴 mcp__tools.rs L228
  4. 非法字元洗成下劃線,只留字母數字和下劃線mcp/mod.rs L477
  5. 完全相同的原始身份丟掉一份tools.rs L134
  6. 清洗後名稱空間撞車,末尾加 12 位 SHA-1tools.rs L166
  7. 清洗後工具名撞車,同樣加 12 位雜湊tools.rs L193
  8. 合起來超過 128 位元組就截斷再雜湊,協議呼叫仍走原名tools.rs L226
點播放,看一家店接進來之後,工具名怎麼變成模型看得見的能力。
兩層名字左邊是協議上的原名,右邊是給模型看的翻譯。調回去的時候走左邊,不會因為右邊加了雜湊就進錯店。
撞名才雜湊乾淨兩家不會加字尾。連字元和工具名這兩檔,清洗之後才會撞,雜湊是消歧,不是裝飾。
自己改第二家店在連字元檔把店名改成和第一家清洗後一樣的字,就能看見名稱空間被拆開。
教學示意:雜湊取前 12 位,演算法是 SHA-1,演示裡用固定示意字尾。行號對應 openai/codex 倉庫 commit 4f39251a01。
思路一 · 對外一份清單,對內另一份
它解決什麼問題

你把 codex mcp-server 寫進 Cursor 的 MCP 配置。Cursor 當 client,Codex 當 server。若這一次 tools/list 把內部 GitHub 工具一併交出去,IDE 調一次就摸到內部能力。權限邊界從「調一次 Codex」擴成「直接調內部工具」。

思路是什麼

crate 拆成兩套。mcp-server 從 stdin 讀行,一行一條 JSON。initialize 只開啟 tools。tools/list 寫死兩個名字:codexcodex-replycodexstart_thread,nested thread 再起自己的 McpRuntimecodex-mcp 管連線集,外部 server 的工具另做一份目錄。

出處:codex-rs/mcp-server/src/lib.rs 第 131 至 152 行;codex-rs/mcp-server/src/codex_tool_runner.rs 第 66 至 90 行;codex-rs/codex-mcp/src/runtime.rs 第 88 至 98 行

同一份 JSON-RPC 線協議,處理器不是同一個。早期資料常把它們畫成同一個 runtime 的兩張臉。當前原始碼裡它們甚至不共享 MessageProcessor

出處:codex-rs/mcp-server/src/message_processor.rs 第 274 至 277 行;codex-rs/mcp-server/src/message_processor.rs 第 336 至 348 行

IDE 看見的入口 Cursor MCP client mcp-server codex · codex-reply nested thread 一次 tools/call 變成一條會話 會話裡看見的外部店 McpRuntime 連線集可整份 replace GitHub mcp__github__* Docs mcp__docs__* 自建 HTTP mcp__http__*
教學化結構圖:上面是交給 IDE 的兩個入口,下面是會話內部的外部工具目錄。
為什麼長期成立

對外承諾和對內能力分開,是閘道的通用形狀。換語言也是兩個函式:hosted 返回 run / continue,external 返回 mcp__*。IDE 只看見入口,會話裡才看見外部店。

思路二 · 模型看見的是翻譯過的名字
它解決什麼問題

兩家店都報 search,前綴還能分開。一家叫 basic-server,一家叫 basic_server,連字元洗成下劃線之後,名稱空間會撞。模型看見兩個同名工具,下一次呼叫就不知道進哪家店。API 還有位元組上限。

思路是什麼

server 接進來,先把各家 tools/list 匯成一張表,再走 normalize_tools_for_model_with_prefix。順序是固定的四步。

1. 給名稱空間加上 mcp__ 前綴。

2. 非法字元洗成下劃線,只留字母、數字和 _

3. 完全相同的原始身份丟掉一份。清洗後名稱空間或工具名還撞,就在末尾加 12 位 SHA-1。

4. 合起來超過 128 位元組,截斷再雜湊。原始 server_nametool.name 留在 ToolInfo 上,協議呼叫走原名。

出處:codex-rs/codex-mcp/src/tools.rs 第 105 至 117 行;codex-rs/codex-mcp/src/tools.rs 第 134 至 137 行;codex-rs/codex-mcp/src/tools.rs 第 166 至 194 行;codex-rs/codex-mcp/src/tools.rs 第 226 至 227 行;codex-rs/codex-mcp/src/mcp/mod.rs 第 477 至 485 行

原始身份 server + tool.name 清洗 mcp__ 加下劃線 消歧 撞了再加雜湊 模型眼前 唯一且夠短 協議呼叫仍帶原名 翻譯層只管給模型看,定址還走 server_name 和 tool.name
教學化流水線:給模型看的名字和調回去的名字是兩層。
給模型看的是翻譯,調回去走原名。
為什麼長期成立

給模型看的名字和協議上的名字本來就是兩層。一層給人讀、給 API 用,一層用來定址。雜湊消歧是撞名問題的通用答法。上限數字會變,這層翻譯不會變。

思路三 · 目錄常在,點名再給正文
它解決什麼問題

若按 MCP 存活過濾目錄,冷啟動那幾秒模型會以為 skill 不存在,下一輪又突然出現。說明書整份灌進每一輪,上下文也會被吃光。

思路是什麼

點名記號是 $。目錄只看 enabledprompt_visible。使用者點了名,或者任務和描述對得上,這一輪才讀 SKILL.md 正文。Guardian 評審會話直接返回空注入,父 transcript 裡的 $skill 不能再觸發新說明書。

出處:codex-rs/skills/src/mentions.rs 第 41 行;codex-rs/ext/skills/src/catalog.rs 第 261 至 263 行;codex-rs/core/src/session/turn.rs 第 766 至 770 行;codex-rs/core/src/session/turn.rs 第 808 至 817 行

缺 MCP 時另問人。first-party 且功能開關開,才彈出 Install MCP servers。審批是 Never 就靜默跳過。使用者選 Continue anyway,目錄還在,對應工具可能仍不可用。

出處:codex-rs/core/src/mcp_skill_dependencies.rs 第 47 至 60 行;codex-rs/core/src/mcp_skill_dependencies.rs 第 268 至 270 行

為什麼長期成立

發現和就緒是兩件事。索引先給,全文按需再給,缺依賴問人,不要把條目從目錄裡抹掉。裝不裝是配置變更,列不列是發現。

橫向對比 · 同一道題的另一種答法

DSH:只橋 tools,一條外掛對一臺 server

DSH 的 MCP 客戶端把範圍寫死:連一臺外部 server,工具註冊到 ctx.tools,公開名是 mcp__<serverName>__<rawName>。乾淨情況原樣拼接。字元或長度被改過,就在末尾加 12 位 SHA-256。上限 64 字元。解除安裝就斷連、登出、放名稱空間。

出處:packages/mcp/mcp-client/src/index.ts 第 1 至 14 行;packages/mcp/mcp-client/src/tools.ts 第 96 至 102 行

沒有 elicitation,也不把自己交出去當 MCP server。外部工具失敗仍按普通 tool 失敗處理。雜湊長度碰巧也是 12,演算法和拼接規則不同。

已核對原始碼 · 2026-08-22 · DSH · MCP 與擴充

Claude Code:skill 是一等 tool

Claude Code 給模型一個 Skill tool。模型 call 才拿正文。註釋寫明同一時間只跑一個 skill,因為 tool 會把命令展開成整份 prompt。

出處:restored-src/src/tools/SkillTool/SkillTool.ts 第 331 至 344 行

MCP 上的 prompt 要標成 loadedFrom === 'mcp'type === 'prompt',才進發現列表。方向相反:Codex 是 skill 需要 MCP,Claude Code 是 MCP 貢獻 skill。觸發器也不同。Codex 掃 $name,命中就注入 <skill>,不經過一次 tool call。

出處:restored-src/src/tools/SkillTool/SkillTool.ts 第 81 至 94 行

已核對原始碼 · 2026-08-22
課堂練習
01

清洗之後誰還認得這家店

basic-serverlookupbasic_serverquery。寫出模型看見的兩個名稱空間,並說明調回去時憑什麼還能進對的店。

再問一問:把審批改成 Never,打 $deploy 的時候,skill 目錄還在不在。觀察點在 is_model_visibleshould_install_mcp_dependencies

Takeaway:對外只交兩個入口,對內另做外部目錄。模型看見的是翻譯過的名字,撞了就雜湊,原名留給協議。skill 目錄常在,點名再給正文,缺 MCP 另問人。