Skip to content

feat(skills): add Skills-over-MCP host support (prototype)#1

Draft
olaservo wants to merge 2 commits intomainfrom
skills-over-mcp
Draft

feat(skills): add Skills-over-MCP host support (prototype)#1
olaservo wants to merge 2 commits intomainfrom
skills-over-mcp

Conversation

@olaservo
Copy link
Copy Markdown
Owner

@olaservo olaservo commented Apr 21, 2026

Summary

Host-side implementation of the Skills-over-MCP SEP
(modelcontextprotocol/experimental-ext-skills#69,
io.modelcontextprotocol/skills). Skills served by connected MCP
servers under skill:// (or any <scheme>:// the server publishes)
are discovered at session init, merged into the same ## Skills
markdown block that already lives inside user_instructions, and
loaded on demand through codex's existing read_mcp_resource(server, uri) tool.

  • Shares code paths with filesystem skills: one SkillsManager, one
    renderer, one dedup-by-name pass, one SkillMetadata.
  • No files under codex-rs/core/src/tools/ changed.
  • One new config key, mcp_skills, per MCP server, default true.

Config

[mcp_servers.github-skills]
url = "http://localhost:8082/mcp"
bearer_token_env_var = "GITHUB_TOKEN"
mcp_skills = false   # optional; default true

Catalog format

URI-backed entries render as (uri: <scheme>://..., server: <name>)
instead of (file: ...). When any MCP skill is present a conditional
preamble is prepended naming the read tool, the server-name
requirement, and the URI-rewrite prohibition. Filesystem-only sessions
see no prompt diff from upstream.

Code pointers

  • core/src/skills/mcp_loader.rsnew. Fetches
    skill://index.json and per-entry SKILL.md via resources/read;
    parses frontmatter through loader::parse_skill_frontmatter_text;
    builds SkillMetadata with SkillScope::Mcp. Parallel join_all
    across servers and per-server entries, 10s per-read timeout, 2048-byte
    URI cap, control-character rejection. Entry name/description are
    advisory — frontmatter is authoritative.
  • core/src/skills/loader.rsparse_skill_frontmatter_text
    extracted from parse_skill_file so filesystem and MCP share one
    YAML + length validation path.
  • core/src/skills/model.rs / protocol/src/protocol.rs
    SkillMetadata gains uri: Option<String> + server_name: Option<String>; SkillScope::Mcp variant; SkillLoadOutcome:: add_mcp_skills with filesystem-wins collision policy.
  • core/src/skills/render.rs — branches on
    skill.uri.zip(skill.server_name); conditional preamble.
  • core/src/skills/injection.rs$Mention of an MCP skill emits a
    SkillInstructions item naming the exact read_mcp_resource(server, uri) call instead of inlining SKILL.md.
  • core/src/codex.rsspawn_mcp_manager initializes one shared
    McpConnectionManager; merge_mcp_skills runs against it before
    user_instructions is frozen into SessionConfiguration. A
    tx_mcp/rx_mcp forwarder preserves SessionConfigured as the
    first event on the main stream.
  • core/src/config/types.rsMcpServerConfig.mcp_skills: bool
    (default true), threaded through the custom Deserialize impl.
  • core/src/mcp_connection_manager.rs / mcp-types/src/lib.rs
    ServerCapabilities gains extensions: Option<serde_json::Value>
    per SEP-2133; logs at info! when a server declares
    io.modelcontextprotocol/skills under extensions or
    experimental. Discovery is not gated on this log.

Tests

  • 9 new codex-core unit tests (4 mcp_loader, 4 render, 1
    config::types). codex-core lib tests, clippy, and rustfmt all
    clean.
  • E2E against olaservo/github-mcp-server@add-agent-skills with
    gpt-5.1-codex-max: first tool call is
    read_mcp_resource({"server":"github-skills","uri":"skill://pull-requests/SKILL.md"}),
    model then cites the three-step review workflow
    (pull_request_review_write (create)
    add_comment_to_pending_reviewpull_request_review_write (submit_pending)) — names that exist only in SKILL.md, proving the
    body reached the model. mcp_skills = false removes the entry +
    preamble on the next rollout.

Known caveats / follow-ups

  • rmcp v0.12 drops capabilities.extensions during deserialization
    before codex sees it, so the capability log fires only when a server
    mirrors the declaration under experimental. Discovery works either
    way per the SEP.
  • SkillMetadata stuffs the URI into path plus separate uri +
    server_name fields to keep the diff additive against ~20 consumer
    sites; a SkillSource::File | ::Mcp enum would be cleaner.
  • Deferred: type: "mcp-resource-template" entries, a per-skill TUI
    toggle, resources/subscribe on the index, eager-to-filesystem
    materialization.

Repo pointers

Adds the namespaced `extensions` field to `ServerCapabilities` so
servers can declare extension capabilities under reverse-DNS keys
(e.g. `io.modelcontextprotocol/skills`). Mirrors the existing
`experimental` field shape but is the spec-recognized location for
post-experimental extension declarations.
Implements the Codex host side of draft SEP openai#69
(`io.modelcontextprotocol/skills`). Each MCP server with `mcp_skills =
true` in its config is asked for `skill://index.json` at connect time;
listed `skill-md` entries have their SKILL.md frontmatter (name,
description, optional short-description) fetched and merged into the
existing filesystem-skills catalog with source annotations.

The model surface is unchanged — skills served by MCP appear in the
catalog as `(uri: <scheme>://..., server: <name>)` instead of
`(file: ...)`, and bodies are fetched on demand via the pre-existing
`read_mcp_resource(server, uri)` built-in tool. No new model-facing
tool is added.

Design choices:
- URI scheme detection is generic (`<scheme>://`); servers may publish
  domain-native schemes such as `github://` instead of `skill://`.
- Filesystem skills win on name collision; the masked MCP entry is
  logged with a user-visible warning.
- `$Mention` of an MCP skill pushes a `SkillInstructions` item naming
  the exact `read_mcp_resource(server, uri)` call so first-turn
  activation is deterministic rather than catalog-heuristic-dependent.
- A single session-scoped `McpConnectionManager` is shared between
  skill discovery and per-turn tool use (rather than a throwaway
  manager for discovery), avoiding duplicate stdio subprocess boots.
- `mcp-resource-template` entries and `resources/subscribe` are
  deferred per the SEP's MAY/SHOULD wording; entries with unrecognized
  `type` are logged-and-skipped as the SEP requires.

Server-side capability declaration under
`capabilities.extensions["io.modelcontextprotocol/skills"]` (or the
transitional `capabilities.experimental` location) is observed for
logging only — discovery is not gated on it, again per SEP guidance.

Tested against `olaservo/github-mcp-server@add-agent-skills` shipping
the `pull-requests` skill; 35 unit tests cover catalog rendering,
collision masking, URI validation, and frontmatter parsing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant