OpenAI Codex · MCP 与 Skills MCP 接进来:模型看见翻译过的名字 外部 server 的工具要先过一层翻译才进模型眼睛。skill 目录常在,缺 MCP 时另问人。 一句话速览 外部 server 的工具要先过一层翻译才进模型眼睛。skill 目录常在,缺 MCP 时另问人。
课程目标 读完能说清三件事。Codex 当 client 时,外部工具怎么变成模型可见名。两家店清洗后撞名,怎么消歧。skill 目录为什么不看 MCP 活没活着。
先玩一遍 · 一家店接进来,名字怎么变
一个 MCP server 接进来:工具怎么变成模型看得见的能力
播放 单步 重置
接入场景
干净两家 连字符双胞胎 工具名双胞胎
右边两档会撞名。切一下,看清洗之后谁被加上哈希。
第二家店
门外 · 原始 tools/list 进门 0
还没接任何人。
模型眼前 · 翻译后的名字 可见 0
清单空着。
逻辑轨迹 · 动画每一步对应源码里的哪一段
连接集整份发布,已有 binding 继续拿自己那份连接 runtime.rs L246 各家 tools/list 汇成一张表,再交给命名翻译 tool_catalog.rs L153 给命名空间加上历史前缀 mcp__ tools.rs L228 非法字符洗成下划线,只留字母数字和下划线 mcp/mod.rs L477 完全相同的原始身份丢掉一份 tools.rs L134 清洗后命名空间撞车,末尾加 12 位 SHA-1 tools.rs L166 清洗后工具名撞车,同样加 12 位哈希 tools.rs L193 合起来超过 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 写死两个名字: codex 和 codex-reply 。 codex 会 start_thread ,nested thread 再起自己的 McpRuntime 。 codex-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 。顺序是固定的四步。
- 给命名空间加上 mcp__ 前缀。
- 非法字符洗成下划线,只留字母、数字和 _ 。
- 完全相同的原始身份丢掉一份。清洗后命名空间或工具名还撞,就在末尾加 12 位 SHA-1。
- 合起来超过 128 字节,截断再哈希。原始 server_name 和 tool.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 不存在,下一轮又突然出现。说明书整份灌进每一轮,上下文也会被吃光。
思路是什么 点名记号是 $ 。目录只看 enabled 和 prompt_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__
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 ,命中就注入
课堂练习
01
清洗之后谁还认得这家店 basic-server 报 lookup , basic_server 报 query 。写出模型看见的两个命名空间,并说明调回去时凭什么还能进对的店。 再问一问:把审批改成 Never ,打 $deploy 的时候,skill 目录还在不在。观察点在 is_model_visible 和 should_install_mcp_dependencies 。
Takeaway: 对外只交两个入口,对内另做外部目录。模型看见的是翻译过的名字,撞了就哈希,原名留给协议。skill 目录常在,点名再给正文,缺 MCP 另问人。