On this page
Cross-Agent Skill Alias Generalization
Skills shared across Claude Code, Codex, and Gemini fail when they hardcode one agent's MCP tool alias. Two-tier pattern — declare both alias families in frontmatter; use generic names in prose.
A skill I shipped under 3B’s .agents/skills/ directory worked fine under
Claude Code and silently no-op’d under Codex. The frontmatter listed the right
tool — mcp__plugin_context-mode_context-mode__ctx_batch_execute — but Codex
had registered the same tool as mcp__context_mode__ctx_batch_execute. Same
underlying MCP server, different alias family, and the skill body had hardcoded
the Claude form everywhere.
Round 1 review caught it. The fix wasn’t a rename — that would just shift the breakage to whichever agent I didn’t pick. The fix was admitting that aliases are runtime-resolved and stopping the skill body from caring which form is live.
The problem
Skills shared across Claude Code, Codex, and Gemini CLI sessions can’t hardcode
agent-specific MCP tool aliases without breaking on the agents that don’t
register that exact alias. Today’s example: context-mode tool exposed as mcp__plugin_context-mode_context-mode__ctx_batch_execute under Claude’s plugin
discovery, but mcp__context_mode__ctx_batch_execute under Codex’s newer
registration. A skill that hardcodes one form silently fails to invoke the tool
on the other agent.
Context
3B’s .agents/skills/<skill>/SKILL.md files are consumed by all three CLIs via
per-agent skill discovery (Claude’s native Skill tool, Codex’s activate_skill tool, Gemini’s skill registry). Tool invocations from inside a
skill body must work on whichever agent the skill activates under.
Hardcoding aliases:
- Bind the skill to one agent’s plugin layout
- Break silently on alias renames (which happen as MCP servers evolve)
- Force the skill maintainer to track every consumer agent’s discovery conventions
The broken case is silent because Claude / Codex / Gemini don’t error on
“unknown tool in allowed-tools”. They just don’t invoke it. From the user’s
side, the skill runs to completion, the tool call doesn’t happen, and the
output looks subtly off rather than catastrophically wrong.
The solution
The fix is a two-tier alias declaration. Frontmatter carries every alias the skill might encounter; prose stays generic and never names a specific alias.
Tier 1 — Frontmatter allowed-tools (formal alias list)
Declare ALL alias families the skill might encounter, plus all tool names within each:
allowed-tools:
Read, Grep, Glob, Bash,
mcp__plugin_context-mode_context-mode__ctx_batch_execute,
mcp__plugin_context-mode_context-mode__ctx_search,
mcp__plugin_context-mode_context-mode__ctx_execute,
mcp__plugin_context-mode_context-mode__ctx_execute_file,
mcp__context_mode__ctx_batch_execute, mcp__context_mode__ctx_search,
mcp__context_mode__ctx_execute, mcp__context_mode__ctx_execute_file The binary consumes this list. Whichever alias is registered at runtime is the one the agent invokes. Both forms in the list = both agents work.
Tier 2 — Body prose (generic names only)
In the workflow body, refer to tools by the trailing generic name:
Use `ctx_batch_execute` for parallel gathering of the four core sources. Not:
Use `mcp__plugin_context-mode_context-mode__ctx_batch_execute` for ... Generic prose:
- Reads naturally — the alias prefix is implementation detail
- Works for whichever alias the runtime resolves to
- Survives alias renames without prose churn
Tier 3 — Alias-resolution note
Add a short note explaining the mechanism so future readers don’t bind prose back to a specific alias:
**Note on tool aliases:** Workflow text below uses generic context-mode names
(`ctx_batch_execute`, `ctx_search`, `ctx_execute`, `ctx_execute_file`). The
binary resolves these to whichever alias family is registered — Claude's
`mcp__plugin_context-mode_context-mode__*` family or Codex's newer
`mcp__context_mode__*` family. Both forms are declared in the frontmatter
`allowed-tools` block; runtime picks one based on the active agent. Avoid
binding prose to a specific alias in this skill — it's cross-agent. That note paragraph is the only place in the body where the formal aliases appear by name. Everywhere else, the generic name is the canonical reference.
Difficulties encountered
- The frontmatter list grows fast — 4 tools × 2 families = 8 entries. Acceptable cost for cross-agent compatibility. The list is declarative; it’s only annoying if you also list each in prose.
- Initial reflex was “list both aliases everywhere prose mentions a tool” — this DOUBLES every prose mention and creates churn on every alias rename. Generic prose + frontmatter list scales better.
- Smoke-audit script needed an explicit allowlist for the alias-resolution note (which DOES mention both family prefixes by design). Generic check “no plugin-prefixed alias in body” produced false positives until the note paragraph was carved out.
Key points
- Frontmatter declares; prose describes. Frontmatter
allowed-toolsis the formal alias list (binary consumes this). Body prose is human-readable description (use generic names). - Both alias families in frontmatter — Claude’s
mcp__plugin_<server>_<server>__<tool>AND Codex’smcp__<server>__<tool>. Runtime picks the one that’s registered. - Generic names in body —
ctx_batch_execute, not full alias. - One alias-resolution note per skill that mentions both family prefixes is the only place the formal aliases appear in body prose. Smoke audit must allowlist this paragraph.
- Aliases rename; prose shouldn’t — every alias rename should be a frontmatter-only change.
- The binary is the resolver, not you — both agents’ MCP loaders match
allowed-toolsentries against registered tools. Listing both families means whichever runtime is live picks the matching entry.
When to use this
- Any skill in
.agents/skills/(or equivalent cross-agent skill directory) consumed by multiple agents. - Any skill that invokes MCP tools (vs. only built-in tools like Read, Bash, Grep).
- Skills shipped to the public via plugin marketplaces — assume both agent families.
When not to use this
- Single-agent skills under
.claude/only — keep the prose specific to the agent’s alias family for clarity. - Skills that only use built-in tools (Read, Bash, Grep, Glob, Edit) — no aliasing concern.
Takeaway
The mental model that took the longest to internalize: the alias is not the tool. The alias is how a specific runtime addresses the tool. If the skill body binds to one alias, it binds the skill to one runtime, and “cross-agent” is a lie.
Frontmatter is where the binding lives because the binary is the part that needs to know which alias is registered. Prose is for humans, and humans don’t need the prefix to understand which tool you’re talking about. Splitting those two concerns is what makes the skill survive every alias rename without touching the workflow text.
See also
knowledge/general/schema-versioned-helper-json-envelope.md— when the skill calls a helper, the helper’s output should be cross-agent too.knowledge/devops/stdlib-only-helper-portability.md— what helpers should be built with to stay cross-agent compatible.knowledge/ai-ml/ai-review-cross-agent-convergence.md— three agents independently flagging the same finding gives near-100% confidence signal.