MCP comes in: the model sees translated names
Tools from an external server pass a translation layer before they reach the model’s eye. The skill catalog stays up; if MCP is missing, ask a person.
- The connection set is published whole; existing bindings keep their own connectionruntime.rs L246
- Each shop’s tools/list is merged into one table, then handed to name translationtool_catalog.rs L153
- Give the namespace the historical prefix mcp__tools.rs L228
- Illegal characters become underscores; only letters, digits, and _ remainmcp/mod.rs L477
- Identical raw identities drop one copytools.rs L134
- After sanitizing, a namespace collision appends 12 SHA-1 hex digitstools.rs L166
- After sanitizing, a tool-name collision gets the same 12-digit hashtools.rs L193
- Over 128 bytes together, truncate then hash; protocol calls still use the original nametools.rs L226
You write codex mcp-server into Cursor’s MCP config. Cursor is the client; Codex is the server. If this tools/list also hands out internal GitHub tools, one IDE call touches internal capability. The permission boundary grows from “call Codex once” into “call internal tools directly.”
The crate splits into two. mcp-server reads lines from stdin, one JSON per line. initialize only opens tools. tools/list hardcodes two names: codex and codex-reply. codex will start_thread; the nested thread starts its own McpRuntime. codex-mcp owns the connection set; tools from external servers get a separate catalog.
Source:codex-rs/mcp-server/src/lib.rs lines 131–152; codex-rs/mcp-server/src/codex_tool_runner.rs lines 66–90; codex-rs/codex-mcp/src/runtime.rs lines 88–98
Same JSON-RPC wire protocol; the processors are not the same. Early notes often draw them as two faces of one runtime. In the current source they do not even share a MessageProcessor.
Source:codex-rs/mcp-server/src/message_processor.rs lines 274–277; codex-rs/mcp-server/src/message_processor.rs lines 336–348
Split the outward promise from inward capability — that is the usual gateway shape. In another language it is still two functions: hosted returns run / continue, external returns mcp__*. The IDE only sees the door; the session is where the external shops appear.
Both shops report search; the prefix can still split them. One is basic-server, one is basic_server. After hyphens become underscores, the namespaces collide. The model sees two same-named tools and the next call does not know which shop to enter. The API also has a byte cap.
When a server hooks up, each shop’s tools/list is first merged into one table, then sent through normalize_tools_for_model_with_prefix. The order is a fixed four steps.
1. Prefix the namespace with mcp__.
2. Illegal characters become underscores; only letters, digits, and _ remain.
3. Identical raw identities drop one copy. If namespace or tool name still collide after sanitizing, append 12 SHA-1 hex digits.
4. Over 128 bytes together, truncate then hash. The original server_name and tool.name stay on ToolInfo; protocol calls use the original names.
Source:codex-rs/codex-mcp/src/tools.rs lines 105–117; codex-rs/codex-mcp/src/tools.rs lines 134–137; codex-rs/codex-mcp/src/tools.rs lines 166–194; codex-rs/codex-mcp/src/tools.rs lines 226–227; codex-rs/codex-mcp/src/mcp/mod.rs lines 477–485
The name the model sees and the name on the wire were always two layers. One is for humans and the API; one is for addressing. Hashing to disambiguate is the usual answer to collisions. The cap number may change; this translation layer will not.
If the catalog were filtered by MCP liveness, for those cold-start seconds the model would think the skill does not exist, then it would pop up next turn. Pouring the whole manual into every turn would also eat the context.
The mention mark is $. The catalog only looks at enabled and prompt_visible. When the user names it, or the task matches the description, this turn reads the SKILL.md body. A Guardian review session returns empty injection; a $skill in the parent transcript cannot trigger a new manual.
Source:codex-rs/skills/src/mentions.rs line 41; codex-rs/ext/skills/src/catalog.rs lines 261–263; codex-rs/core/src/session/turn.rs lines 766–770; codex-rs/core/src/session/turn.rs lines 808–817
If MCP is missing, ask a person. First-party and the feature flag on is what pops Install MCP servers. Approval Never skips silently. If the user picks Continue anyway, the catalog stays; the matching tools may still be unavailable.
Source:codex-rs/core/src/mcp_skill_dependencies.rs lines 47–60; codex-rs/core/src/mcp_skill_dependencies.rs lines 268–270
Discovery and readiness are two facts. Give the index first, the full text on demand, and ask a person when a dependency is missing — do not wipe the entry from the catalog. Install or not is a config change; list or not is discovery.
DSH: only bridge tools, one plugin per server
DSH’s MCP client hardcodes the scope: connect one external server, register tools on ctx.tools, public name mcp__<serverName>__<rawName>. The clean case concatenates as-is. If characters or length were rewritten, append 12 SHA-256 hex digits. Cap is 64 characters. Uninstall disconnects, unregisters, and frees the namespace.
Source:packages/mcp/mcp-client/src/index.ts lines 1–14; packages/mcp/mcp-client/src/tools.ts lines 96–102
No elicitation, and it does not hand itself out as an MCP server. An external tool failure is still an ordinary tool failure. The hash length happens to be 12 too; the algorithm and join rules differ.
Checked against source · 2026-08-22 · DSH · MCP and extensionsClaude Code: a skill is a first-class tool
Claude Code gives the model a Skill tool. The model calls before it gets the body. A comment says only one skill runs at a time, because the tool expands the command into a full prompt.
Source:restored-src/src/tools/SkillTool/SkillTool.ts lines 331–344
An MCP prompt only enters the discovery list if it is marked loadedFrom === 'mcp' and type === 'prompt'. The direction flips: Codex skills need MCP; Claude Code MCP contributes skills. The trigger differs too. Codex scans $name and, on a hit, injects <skill> without a tool call.
Source:restored-src/src/tools/SkillTool/SkillTool.ts lines 81–94
After sanitizing, who still knows this shop
basic-server reports lookup; basic_server reports query. Write the two namespaces the model sees, and say what still sends a call back into the right shop.
Then ask: flip approval to Never and type $deploy — is the skill catalog still there? Watch is_model_visible and should_install_mcp_dependencies.