模块:T3 解剖 OpenAI Codex:把安全观写进类型系统
来源:xueai.miyang.cn(小山学堂 · 洛小山《学 AI 产品,从入门到精通》)
本页为《学 AI 产品,从入门到精通》配套解读,音频为二次演绎配音版。
外部 server 的工具要先过一层翻译才进模型眼睛。skill 目录常在,缺 MCP 时另问人。
搬家只搬对得上的字段;对不上的,丢掉。
本集包含两节课:MCP 接进来与搬家只搬对得上的字段。共同主题是"边界"——外部能力如何进来,旧家当能带过来多少。
| 能力 | 落点 | 判断标准 |
|---|---|---|
| 区分对外入口与对内目录 | mcp-server / codex-mcp 两个 crate | 能说清为什么两者不共享 MessageProcessor |
| 掌握命名翻译流水线 | normalize_tools_for_model_with_prefix | 能复述四步顺序并说明顺序不可换 |
| 理解两层名字 | ToolInfo 保留原始 server_name 与 tool.name | 能解释为何回程走原名不会进错店 |
| 目录常在、正文按需 | skills catalog 只看 enabled / prompt_visible | 能说明为什么要按依赖存活过滤目录 |
| 搬家三条出口 | external-agent-migration 白名单 | 对任一条字段能说出原样/改写/丢掉 |
| 安装权威落在用户级 | detect 只在 home 扫插件 | 能解释仓库 settings 为何不能当安装权威 |
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 行MessageProcessor。codex-rs/mcp-server/src/message_processor.rs 第 274–277 行、第 336–348 行危险场景:把codex mcp-server写进 Cursor 的 MCP 配置,Cursor 是 client、Codex 是 server。若这一次tools/list把内部工具一并交出,IDE 调一次就摸到内部能力——权限边界从"调一次 Codex"扩成"直接调内部工具"。
normalize_tools_for_model_with_prefix 的固定顺序:
mcp__ 前缀;_;server_name 与 tool.name 留在 ToolInfo 上,协议调用走原名。codex-rs/codex-mcp/src/tools.rs 第 105–117、134–137、166–194、226–227 行;codex-rs/codex-mcp/src/mcp/mod.rs 第 477–485 行要点:
basic-server 与 basic_server 清洗后命名空间相同 → 加哈希;两家都报 search、前缀可分时不动哈希。哈希是消歧,不是装饰。codex-rs/codex-mcp/src/runtime.rs 第 246 行;codex-rs/codex-mcp/src/tool_catalog.rs 第 153 行$。目录只看 enabled 与 prompt_visible;用户点名或任务与描述匹配,本轮才读 SKILL.md 正文。$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、808–817 行Install MCP servers;审批为 Never 则静默跳过;用户选 Continue anyway,目录仍在但对应工具可能不可用。codex-rs/core/src/mcp_skill_dependencies.rs 第 47–60、268–270 行反面设计:按 MCP 存活过滤目录 → 冷启动几秒模型以为 skill 不存在,下一轮又突然出现;整份灌进每轮 → 上下文被吃光。
Cla(Claude Code)与 Cur(Cursor)。字符串对不上 cursor 就落到 Claude Code;调用方漏传源 → 去翻 ~/.claude。codex-rs/external-agent-migration/src/migration_source.rs 第 51–67 行| 字段 | 规则 | 出口 |
|---|---|---|
| hook 事件名 | Codex 认 11 个,Claude Code 发出 27 个 | 对不上的组整组消失 |
| 单条 hook type | 默认当 command | 对不上跳过;prompt 整条跳过 |
| MCP command / url | 出现 ${ | 整台 server 不要 |
| 市场来源 | 只收 github、git、本地目录 | file / url / npm / settings 直接丢掉 |
| 说明文件 | CLAUDE.md → AGENTS.md | 改写 |
| 产品名 | 按词边界改写为 Codex | 改写(Cursor 仅大小写敏感匹配) |
| 会话 | 30 天 / 50 条过滤 | 太旧丢掉 |
| memory | 需源支持 + 特性开关 | Cursor 不支持;开关关则拒绝 |
codex-rs/external-agent-migration/src/model.rs 第 53–64 行;codex-rs/hooks/src/lib.rs 第 23–35 行;codex-rs/external-agent-migration/src/hooks_cla.rs 第 131–137 行;mcp.rs 第 204–210 行;source_cla.rs 第 270–274 行;rewrite.rs 第 38–49 行;detect/mod.rs 第 59、330–332 行;sessions/common.rs 第 43 行repository-scoped plugin migration is not allowed。detect/mod.rs 第 330–332 行;migration_source.rs 第 116–125 行;plugins.rs 第 26–45 行import() 对 Sessions 直接返回成功、什么都不写;真正写成 Codex thread 的是 app-server 后台任务,调用方先拿 import_id。service.rs 第 437 行;app-server/src/external_agent_migration/session_importer.rs 第 100–111 行codex-rs/core-plugins/src/marketplace.rs 第 20–25 行config.toml 只补缺失键;同名 MCP server 保留旧的。第一次搬家像填空,第二次多数条目不再出现。codex-rs/external-agent-migration/src/hooks_common.rs 第 13–19 行UnderDevelopment;检测函数不看开关,导入前处理器会查,关着回 external agent memory import is disabled。导入按字节拷贝、不脱敏。codex-rs/features/src/lib.rs 第 998–1003 行;app-server/.../processor.rs 第 180–185 行原样 / 改写 / 丢掉,没有"改写成近似物"。tools/list 对外是否只暴露两个入口?内部工具是否被一并交出?MessageProcessor?(共享即边界失效)ToolInfo 是否保留原始 server_name 与 tool.name?回程是否走原名?$skill 是否还能触发注入?import() 是否对 Sessions 立即返回而不写盘?后台任务是否可追踪?实现时最容易犯的错是"复用同一份工具表,只是过滤一下"。过滤是可以被绕过、被忘记、被后来者改坏的。正确做法是让对外清单由一个独立函数返回,硬编码两个入口;内部目录由另一个模块维护。两者不共享结构,边界才是真的。
四步顺序(加前缀 → 洗字符 → 按原始身份去重 → 撞名加哈希 → 超长截断)必须显式固定,并在注释里写明"顺序不可换"。建议在单测里构造 basic-server 与 basic_server 两组输入,断言清洗后撞名、且哈希只加到其中一方。
检查自己的 skill / plugin 系统:目录是否常驻?是否因为某个依赖没起来就从列表里消失?正确实现是目录只按启用与可见过滤,缺依赖时弹提示并保留条目。可以在集成测试里模拟"依赖 server 未启动",断言目录条目数不变。
不要写"尽力转换"的迁移代码。把每条源字段、目标落点、出口(原样/改写/丢掉)列成一张表,作为代码与文档的共同来源。对不上的字段一律走"丢掉",并在导入报告里显式列出,让用户知道少了什么、为什么少。
会话导入、远程市场安装这类慢活,同步阶段只做"承认看见了",返回一个 import_id,落盘交给后台任务。调用方轮询或订阅 id 状态。避免让同步接口因等待外部网络而长时间阻塞。
basic-server 报 lookup,basic_server 报 query。写出模型看见的两个命名空间,并说明调回去时凭什么还能进对店。Never,打 $deploy 时 skill 目录还在不在?观察点在 is_model_visible 与 should_install_mcp_dependencies。PreToolUse 的 type: command hook、同事件的 type: prompt hook、一台 command 含 ${API_KEY} 的 MCP、一条 40 天前的会话。对照白名单列出应出现与不应出现的条目。import_id?这条会话何时变成 thread?开关关着的 memory 送去导入,会在哪一扇门被挡回?对外只交两个入口,对内另做外部目录。模型看见的是翻译过的名字,撞了就哈希,原名留给协议。skill 目录常在,点名再给正文,缺 MCP 另问人。
搬家是检测加映射加重写。对得上的字段走拷或改,对不上的丢掉。插件只从用户级装,会话和远程市场进后台。目标非空不覆盖。
来源:xueai.miyang.cn(小山学堂 · 洛小山《学 AI 产品,从入门到精通》)
本页正文为二次演绎配音版的配套解读,与音频逐段对应。