From 238a2f08fd2efc143d746a7fa041a0f1251822d9 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Thu, 23 Apr 2026 20:46:27 +0100 Subject: [PATCH 01/25] SEP: Skills Extension --- docs/seps/2640-skills-extension.mdx | 369 ++++++++++++++++++++++++++++ docs/seps/index.mdx | 80 +++--- seps/2640-skills-extension.md | 357 +++++++++++++++++++++++++++ 3 files changed, 762 insertions(+), 44 deletions(-) create mode 100644 docs/seps/2640-skills-extension.mdx create mode 100644 seps/2640-skills-extension.md diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx new file mode 100644 index 000000000..77ee1605f --- /dev/null +++ b/docs/seps/2640-skills-extension.mdx @@ -0,0 +1,369 @@ +--- +title: "SEP-2640: Skills Extension" +sidebarTitle: "SEP-2640: Skills Extension" +description: "Skills Extension" +--- + +
+ + Unknown + + + Extensions Track + +
+ +| Field | Value | +| ------------- | -------------------------------------------------------------------------------------------------------------------- | +| **SEP** | 2640 | +| **Title** | Skills Extension | +| **Status** | Unknown | +| **Type** | Extensions Track | +| **Created** | 2026-04-23 | +| **Author(s)** | Peter Alexander ([@pja-ant](https://github.com/pja-ant)), Ola Hungerford ([@olaservo](https://github.com/olaservo)), | +| **Sponsor** | [@pja-ant](https://github.com/pja-ant) | +| **PR** | [#2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) | + +--- + +## 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 well-known `skill://index.json` resource enumerates concrete skills and parameterized skill templates, but is not required — accommodating servers whose skill catalogs are large, generated, or otherwise unenumerable. 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. + +Because the extension adds no new protocol methods or capabilities, 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 load once at initialization 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, four 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. + +### 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:/// +``` + +A server MAY instead serve skills under another scheme native to its domain (e.g., `github://owner/repo/skills/refunds/SKILL.md`), provided each skill is listed in the [`skill://index.json`](#enumeration-via-skillindexjson) resource. The index is the authoritative record of which resources are skills; outside the index, hosts recognize skills by the `skill://` scheme prefix. + +The structural constraints below — `` ending in the skill name, `SKILL.md` explicit in the URI, no nesting — apply regardless of scheme. + +where: + +- `` 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`). +- `` 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.md`, and the skill's root directory is the URI obtained by stripping the trailing `SKILL.md`. + +The final segment of `` 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` MUST NOT appear in any descendant directory of a skill. The skill directory is the boundary; skills do not nest inside other skills. +- The final `` segment, being the skill `name`, MUST satisfy the Agent Skills specification's naming rules. Prefix segments SHOULD be valid URI path segments per [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986); no further constraints are imposed on them. + +Per RFC 3986, the first segment of `` occupies the authority component. This carries no special semantics under this convention and clients MUST NOT attempt DNS or network resolution of it. + +#### 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` | `templates/email.md` | `skill://acme/billing/refunds/templates/email.md` | + +#### Resource Metadata + +For each `skill:///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 ``. +- `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. Other files in the skill use the `mimeType` appropriate to their content. + +### 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 index, and hosts MUST support loading a skill given only its URI (see [Hosts: Model-Driven Resource Loading](#hosts-model-driven-resource-loading)). 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, two discovery mechanisms are defined. A server MAY support either or both. + +#### Enumeration via `skill://index.json` + +A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. The index format follows the [Agent Skills well-known URI discovery index](https://agentskills.io/well-known-uri#index-format), with two differences: the `url` field contains a full MCP resource URI (any scheme the server serves), and the `digest` field is omitted (integrity is the transport's concern over an authenticated MCP connection). This binding also defines one additional `type` value, `"mcp-resource-template"`, for entries that describe a parameterized skill namespace. + +```json +{ + "$schema": "https://schemas.agentskills.io/discovery/0.2.0/schema.json", + "skills": [ + { + "name": "git-workflow", + "type": "skill-md", + "description": "Follow this team's Git conventions for branching and commits", + "url": "skill://git-workflow/SKILL.md" + }, + { + "name": "refunds", + "type": "skill-md", + "description": "Process customer refund requests per company policy", + "url": "skill://acme/billing/refunds/SKILL.md" + }, + { + "name": "pdf-processing", + "type": "archive", + "description": "Extract, fill, and assemble PDF documents", + "url": "skill://pdf-processing.tar.gz" + }, + { + "type": "mcp-resource-template", + "description": "Per-product documentation skill", + "url": "skill://docs/{product}/SKILL.md" + } + ] +} +``` + +Index fields: + +| Field | Required | Description | +| ---------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `$schema` | Yes | Schema version URI. Clients SHOULD match against known URIs before processing. | +| `skills` | Yes | Array of skill entries. | +| `skills[].type` | Yes | MUST be `"skill-md"`, `"archive"`, or `"mcp-resource-template"`. | +| `skills[].description` | Yes | For `"skill-md"` and `"archive"`, the skill's `description` matching its `SKILL.md` frontmatter. For `"mcp-resource-template"`, a description of the addressable skill space. | +| `skills[].url` | Yes | Full resource URI. See per-type semantics below. | +| `skills[].name` | Conditional | Required for `"skill-md"` and `"archive"`; matches the `SKILL.md` frontmatter `name` and the final segment of the skill path. Omitted for `"mcp-resource-template"`. | + +Clients SHOULD ignore unrecognized fields and SHOULD skip entries with an unrecognized `type`. + +**Skill-md entries** (`type: "skill-md"`) point `url` at the full resource URI of the skill's `SKILL.md`. Supporting files are individually addressable as sibling resources under the same skill path. + +**Archive entries** (`type: "archive"`) point `url` at a single resource whose content is a packed skill directory. Per the [Agent Skills archive distribution format](https://agentskills.io/well-known-uri#archive-distribution), the archive MUST be `.tar.gz` (gzip-compressed tar, `mimeType` `application/gzip`) or `.zip` (`mimeType` `application/zip`); hosts MUST support both and SHOULD determine the format from the resource's `mimeType`, falling back to the URL suffix. Archive contents represent the skill directory directly — `SKILL.md` MUST be at the archive root, not nested inside a wrapper directory, and the archive MUST NOT contain path-traversal sequences (`..`) or absolute paths. + +The `` is the archive `url` with its archive suffix (`.tar.gz` or `.zip`) stripped — `skill://pdf-processing.tar.gz` unpacks to `skill://pdf-processing/`, `skill://acme/billing/refunds.zip` to `skill://acme/billing/refunds/` — and the archive contents populate `skill:///` exactly as if each file were served individually. A server chooses per-skill whether to distribute as individual files or as an archive; hosts observe an identical virtual namespace either way. Archive distribution delivers a multi-file skill atomically in one round trip and can carry UNIX file metadata (executable bits, symlinks) that individual-resource distribution cannot represent — robustly for `.tar.gz`, variably for `.zip`. + +Hosts unpacking an archive MUST apply the [archive safety](https://agentskills.io/well-known-uri#archive-safety) requirements of the Agent Skills specification: reject archives containing path-traversal sequences or absolute paths, reject symlinks or hard links that resolve outside the skill directory, and enforce a limit on total unpacked size. + +**Template entries** (`type: "mcp-resource-template"`) describe a parameterized skill namespace without materializing every entry. The `url` is an [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570) URI template that resolves to `SKILL.md` resource URIs. A server SHOULD register the same `url` value as an MCP [resource template](https://modelcontextprotocol.io/specification/2025-11-25/server/resources#resource-templates) so hosts can wire template variables to the [completion API](https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/completion). Hosts SHOULD surface template entries in their UI as interactive discovery points: the user fills in variables via completion, selects a skill, and the host passes the resolved URI into the conversation. This scales to servers with unbounded skill catalogs. + +The `skill://index.json` resource is served via `resources/read` like any other resource, with `mimeType` of `application/json`. A server MAY also surface it in `resources/list` so clients can detect its presence, but clients MAY attempt to read it directly without prior discovery. + +A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY decline to expose `skill://index.json`, or MAY expose a partial index. Hosts MUST NOT treat an absent or empty index as proof that a server has no skills. + +The URI `skill://index.json` is reserved and does not conflict with any valid ``: skill names may contain only lowercase letters, digits, and hyphens, so `index.json` cannot be a skill name. + +#### 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": {} + } + } +} +``` + +No extension-specific settings are currently defined; an empty object indicates support. + +### Reading + +Skill files are read via the standard `resources/read` method. No skill-specific read semantics are defined. + +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 `skill://` scheme root. + +## Implementation Guidelines + +The following are recommendations for interoperable implementations. They are not part of the normative specification. + +### Hosts: Model-Driven Resource Loading + +Hosts SHOULD expose a tool to the model that reads MCP resources by server and URI, enabling the model to load skill content on demand: + +```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, e.g. skill://git-workflow/SKILL.md" + } + }, + "required": ["server", "uri"] + } +} +``` + +The signature shown is illustrative. Including the server name is one disambiguation strategy for identical `skill://` URIs served by different connected servers; hosts MAY instead prefix URIs on conflict, scope by session, or use any other scheme appropriate to their architecture. The tool is general-purpose — it reads any MCP resource — and benefits resource use cases beyond skills. + +Hosts SHOULD load the frontmatter (`name`, `description`) of available and enabled skills into the model's context so the model can judge relevance and construct a `read_resource` call when a skill applies. Hosts SHOULD surface available skills in their UI for user inspection and per-skill enable/disable, analogous to how tools are typically exposed. + +A typical flow: the host reads `skill://index.json` from each connected server and surfaces the `name` and `description` of each entry in the model's context. The model calls `read_resource` with a concrete URI — one returned by enumeration, one handed to it by the user (who may have resolved it from a template entry in the host UI), or one obtained out-of-band — when a skill is relevant to the task. + +Because enumeration is optional, a `read_resource` call for a `skill://` URI that the host has never seen listed is normal and expected. The host forwards it to the named server; the server either serves the resource or returns a not-found error. + +### Hosts: Unified Treatment of Filesystem and MCP Skills + +Hosts that support both filesystem-based skills (loaded from local directories) and MCP-served skills SHOULD treat them identically, as though the set of connected servers' `skill://` resources were mounted into a virtual filesystem alongside local skill directories. + +Concretely: the same discovery surface, the same loading tool, and the same relative-path resolution. A model that has learned to follow `references/GUIDE.md` from a local `SKILL.md` should find that MCP-served skills behave the same way. Divergence between the two paths is a source of model confusion and implementation complexity. + +### 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, registering a `skill:///{+path}` resource template, serving file content on `resources/read`, and (where the server's skill set is bounded) generating the `skill://index.json` resource. + +**Client-side** — enumerate and fetch skills: + +```python +skills = await client.list_skills() # reads skill://index.json, may be empty or absent +content = await client.read_skill_uri( + "skill://acme/billing/refunds/SKILL.md") # wraps resources/read, works regardless of enumeration +``` + +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 + +### Why Resources Instead of a New Primitive? + +The Working Group's [decision log](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-02-26-prioritize-skills-as-resources-with-client-helper-tools) records this as settled. Skills are files; Resources exist to expose files. Reusing Resources inherits URI addressability, `resources/read`, `resources/subscribe`, templates, and the existing client tooling for free. A new primitive would duplicate most of this and add ecosystem complexity — a concern raised in [community discussion](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/14). + +[SEP-2076] proposes the new-primitive alternative. That approach offers cleaner capability negotiation and dedicated list-changed notifications, but at the cost of flattening skills to name-addressed blobs — losing the directory model that the Agent Skills specification defines and that supporting files depend on. + +### Why `skill:///` With an Explicit `SKILL.md`? + +Four independent implementations converged on `skill://` as the scheme without coordination — a strong signal. They diverged on structure. This SEP adopts the explicit-file form because: + +- It directly mirrors the Agent Skills specification's directory model. A skill _is_ a directory; its URI space should look like one. +- `SKILL.md` being explicit means supporting files are siblings at the same level, with no special casing for "the skill URI" versus "a file in the skill." +- Hosts implementing both filesystem and MCP skills can use one path-resolution codepath. + +The cost — `SKILL.md` is always typed out rather than implied — is small, and where discovery is supported the response already points clients at the right URI. + +### Why Allow a Path Prefix But Constrain the Final Segment? + +Earlier drafts required `` to be a single segment equal to the frontmatter `name`. That breaks down when a server needs hierarchy: an organization serving both `acme/billing/refunds` and `acme/support/refunds` cannot satisfy "single segment" without renaming one skill to dodge the collision. Allowing a prefix (`acme/billing/`, `acme/support/`) solves this — both skills can be named `refunds` and the prefix disambiguates. + +A subsequent draft went further and fully decoupled the path from the name. That was too loose: a URI like `skill://a/b/c/SKILL.md` tells you nothing about what the skill is called until you fetch and parse frontmatter. Clients listing skills, hosts displaying them in a picker, and models reasoning over URIs all want the name visible without a round trip. + +Constraining the final segment to match the frontmatter `name` gets both properties. The prefix carries the server's organizational structure; the final segment carries the skill's identity; and the two together form a locator from which the name can be read directly. + +### Why Is Enumeration Optional? + +Requiring every server to expose a complete `skill://index.json` fails for at least three server shapes: a documentation server that synthesizes a skill per API endpoint (thousands), a skill gateway fronting an external index (unbounded), and a server that generates skills from templates parameterized at read time (unenumerable by construction). For these, the list is either too large to be useful in the model's context or does not meaningfully exist. + +The baseline is therefore direct readability — a skill URI is always a valid argument to `resources/read`. The index (concrete entries and templates) is layered on top for servers where it makes sense. A host that assumes enumeration is exhaustive will miss skills on servers where it is not, hence the requirement that hosts MUST NOT treat empty enumeration as proof of absence. + +### Why Delegate the Format to agentskills.io? + +The Agent Skills specification already defines YAML frontmatter fields, naming rules, directory conventions, and the progressive-disclosure model. It has its own governance, contributing process, and multi-vendor participation. Redefining any of this in an MCP SEP would create a second source of truth and a drift risk. This SEP is a transport binding; the payload format is someone else's concern. + +### Why Permit Archive Distribution? + +An earlier draft excluded `type: "archive"` on the basis that supporting files are already individually addressable as resources. The Working Group [reversed this](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-04-19-archives-permitted-as-server-side-packaging-optimization) for four reasons: dropping a distribution type that the Agent Skills discovery format defines contradicts this SEP's framing as a pure transport binding; individual-file distribution cannot guarantee atomicity across a multi-file skill read; hosts that pre-materialize skills to a local filesystem pay N+1 round trips; and UNIX file metadata (executable bits, symlinks) has no representation when files are served individually. + +Reinstating archives as a server-side packaging choice — not a client-visible mode split — preserves the virtual-filesystem model: the post-unpack view is identical to individual-file distribution. The property given up is per-file `resources/subscribe` granularity, which is acceptable because subscription is not part of the skill reading model defined here. + +## Backward Compatibility + +This extension introduces no new protocol 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 (NimbleBrain's `skill://server/skill`, skilljack's implicit-`SKILL.md` `skill://name`) will need to adjust their URI paths to conform. These are small, mechanical changes. + +## Security Implications + +Skill content is instructional text delivered to a model, which makes it a prompt-injection surface. The Working Group's position, recorded 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), is: + +- **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. +- **Skills do not introduce a new trust tier.** A user who connects a server has already extended their trust boundary to it; a malicious server can do as much harm via tools as via a skill document. Serving skills over MCP adds no risk beyond what skills already carry in any transport — but the defensive posture above applies regardless. +- **No implicit local execution.** Hosts MUST NOT honor mechanisms in skill content that would cause local code execution without explicit user opt-in. This includes, non-exhaustively: hook declarations, pre/post-invocation scripts, shell commands embedded in frontmatter, or any field that a filesystem-sourced skill might use to register executable behavior on the host. Hosts MUST either ignore such fields entirely when the skill arrives over MCP, or gate them behind an explicit per-skill user approval that states what will execute and where. Silently executing server-provided code because it appeared in a skill directory is a remote code execution vector. +- **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. +- **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. +- **Archive safety.** Archive-distributed skills are an unpacking attack surface. Hosts MUST validate archives per the [Agent Skills archive safety requirements](https://agentskills.io/well-known-uri#archive-safety): reject path traversal and absolute paths, reject links resolving outside the skill directory, and bound total unpacked size to prevent decompression bombs. +- **Not a third-party marketplace.** This extension is for servers to ship skills that describe their own tools, not for distributing arbitrary third-party content through a connected server. + +The instructor-only scope of this extension ([decisions.md, 2026-02-14](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-02-14-skills-served-over-mcp-use-the-instructor-format)) deliberately excludes the helper model. A filesystem skill might reasonably carry scripts the user has audited; an MCP skill arrives from a remote party and MUST be handled as text that influences model behavior, not as code that executes on the host. + +## 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) + +**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 + +**Server implementation:** + +- GitHub MCP Server: [github/github-mcp-server#2360](https://github.com/github/github-mcp-server/pull/2360) + +## References + +- [Agent Skills specification](https://agentskills.io/specification) +- [Agent Skills well-known URI discovery](https://agentskills.io/well-known-uri) +- [SEP-2133]: Extensions +- [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 +- [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 diff --git a/docs/seps/index.mdx b/docs/seps/index.mdx index 658464479..70a073925 100644 --- a/docs/seps/index.mdx +++ b/docs/seps/index.mdx @@ -12,53 +12,45 @@ Specification Enhancement Proposals (SEPs) are the primary mechanism for proposi ## Summary -- **Final**: 41 +- **Unknown**: 1 +- **Accepted**: 2 +- **Final**: 28 ## All SEPs -| SEP | Title | Status | Type | Created | -| ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | ----------------------------------------------- | ---------------- | ---------- | -| [SEP-2663](/seps/2663-tasks-extension) | Tasks Extension | Final | Extensions Track | 2026-04-27 | -| [SEP-2596](/seps/2596-spec-feature-lifecycle-and-deprecation) | Specification Feature Lifecycle and Deprecation Policy | Final | Process | 2026-04-17 | -| [SEP-2577](/seps/2577-deprecate-roots-sampling-and-logging) | Deprecate Roots, Sampling, and Logging | Final | Standards Track | 2026-04-14 | -| [SEP-2575](/seps/2575-stateless-mcp) | Make MCP Stateless | Final | Standards Track | 2025-06-18 | -| [SEP-2567](/seps/2567-sessionless-mcp) | Sessionless MCP via Explicit State Handles | Final | Standards Track | 2026-03-11 | -| [SEP-2549](/seps/2549-TTL-for-list-results) | TTL for List Results | Final | Standards Track | 2026-04-09 | -| [SEP-2484](/seps/2484-conformance-tests-required-for-final-seps) | Require Conformance Tests for Standards Track SEPs to Reach Final Status | Final | Process | 2026-03-27 | -| [SEP-2468](/seps/2468-recommend-issuer-claim-for-auth) | Recommend Issuer (iss) Parameter in MCP Auth Responses | Final | Standards Track | 2026-03-25 | -| [SEP-2322](/seps/2322-MRTR) | Multi Round-Trip Requests | Final | Standards Track | 2026-02-03 | -| [SEP-2260](/seps/2260-Require-Server-requests-to-be-associated-with-Client-requests) | Require Server requests to be associated with a Client request. | Final | Standards Track | 2026-02-16 | -| [SEP-2243](/seps/2243-http-standardization) | HTTP Header Standardization for Streamable HTTP Transport | Final | Standards Track | 2026-02-04 | -| [SEP-2207](/seps/2207-oidc-refresh-token-guidance) | OIDC-Flavored Refresh Token Guidance | Final | Standards Track | 2026-02-04 | -| [SEP-2164](/seps/2164-resource-not-found-error) | Standardize Resource Not Found Error Code | Final | Standards Track | 2026-01-28 | -| [SEP-2149](/seps/2149-working-group-charter-template) | MCP Group Governance and Charter Template | Final | Process | 2025-01-15 | -| [SEP-2148](/seps/2148-contributor-ladder) | MCP Contributor Ladder | Final | Process | 2026-01-15 | -| [SEP-2133](/seps/2133-extensions) | Extensions | Final | Standards Track | 2025-01-21 | -| [SEP-2106](/seps/2106-json-schema-2020-12) | Tools `inputSchema` & `outputSchema` Conform to JSON Schema 2020-12 | Final | Standards Track | 2026-01-06 | -| [SEP-2085](/seps/2085-governance-succession-and-amendment) | Governance Succession and Amendment Procedures | Final | Process | 2025-12-05 | -| [SEP-1865](/seps/1865-mcp-apps-interactive-user-interfaces-for-mcp) | MCP Apps - Interactive User Interfaces for MCP | Final | Extensions Track | 2025-11-21 | -| [SEP-1850](/seps/1850-pr-based-sep-workflow) | PR-Based SEP Workflow | Final | Process | 2025-11-20 | -| [SEP-1730](/seps/1730-sdks-tiering-system) | SDKs Tiering System | Final | Standards Track | 2025-10-29 | -| [SEP-1699](/seps/1699-support-sse-polling-via-server-side-disconnect) | Support SSE polling via server-side disconnect | Final | Standards Track | 2025-10-22 | -| [SEP-1686](/seps/1686-tasks) | Tasks | Final | Standards Track | 2025-10-20 | -| [SEP-1613](/seps/1613-establish-json-schema-2020-12-as-default-dialect-f) | Establish JSON Schema 2020-12 as Default Dialect for MCP | Final | Standards Track | 2025-10-06 | -| [SEP-1577](/seps/1577--sampling-with-tools) | Sampling With Tools | Final | Standards Track | 2025-09-30 | -| [SEP-1330](/seps/1330-elicitation-enum-schema-improvements-and-standards) | Elicitation Enum Schema Improvements and Standards Compliance | Final | Standards Track | 2025-08-11 | -| [SEP-1319](/seps/1319-decouple-request-payload-from-rpc-methods-definiti) | Decouple Request Payload from RPC Methods Definition | Final | Standards Track | 2025-08-08 | -| [SEP-1303](/seps/1303-input-validation-errors-as-tool-execution-errors) | Input Validation Errors as Tool Execution Errors | Final | Standards Track | 2025-08-05 | -| [SEP-1302](/seps/1302-formalize-working-groups-and-interest-groups-in-mc) | Formalize Working Groups and Interest Groups in MCP Governance | Final | Standards Track | 2025-08-05 | -| [SEP-1046](/seps/1046-support-oauth-client-credentials-flow-in-authoriza) | Support OAuth client credentials flow in authorization | Final | Standards Track | 2025-07-23 | -| [SEP-1036](/seps/1036-url-mode-elicitation-for-secure-out-of-band-intera) | URL Mode Elicitation for secure out-of-band interactions | Final | Standards Track | 2025-07-22 | -| [SEP-1034](/seps/1034--support-default-values-for-all-primitive-types-in) | Support default values for all primitive types in elicitation schemas | Final | Standards Track | 2025-07-22 | -| [SEP-1024](/seps/1024-mcp-client-security-requirements-for-local-server-) | MCP Client Security Requirements for Local Server Installation | Final | Standards Track | 2025-07-22 | -| [SEP-994](/seps/994-shared-communication-practicesguidelines) | Shared Communication Practices/Guidelines | Final | Process | 2025-07-17 | -| [SEP-991](/seps/991-enable-url-based-client-registration-using-oauth-c) | Enable URL-based Client Registration using OAuth Client ID Metadata Documents | Final | Standards Track | 2025-07-07 | -| [SEP-990](/seps/990-enable-enterprise-idp-policy-controls-during-mcp-o) | Enable enterprise IdP policy controls during MCP OAuth flows | Final | Standards Track | 2025-06-04 | -| [SEP-986](/seps/986-specify-format-for-tool-names) | Specify Format for Tool Names | Final | Standards Track | 2025-07-16 | -| [SEP-985](/seps/985-align-oauth-20-protected-resource-metadata-with-rf) | Align OAuth 2.0 Protected Resource Metadata with RFC 9728 | Final | Standards Track | 2025-07-16 | -| [SEP-973](/seps/973-expose-additional-metadata-for-implementations-res) | Expose additional metadata for Implementations, Resources, Tools and Prompts | Final | Standards Track | 2025-07-15 | -| [SEP-932](/seps/932-model-context-protocol-governance) | Model Context Protocol Governance | Final | Process | 2025-07-08 | -| [SEP-414](/seps/414-request-meta) | Document OpenTelemetry Trace Context Propagation Conventions | Final | Standards Track | 2025-04-25 | +| SEP | Title | Status | Type | Created | +| ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | ------------------------------------------------- | ---------------- | ---------- | +| [SEP-2640](/seps/2640-skills-extension) | Skills Extension | Unknown | Extensions Track | 2026-04-23 | +| [SEP-2260](/seps/2260-Require-Server-requests-to-be-associated-with-Client-requests) | Require Server requests to be associated with a Client request. | Accepted | Standards Track | 2026-02-16 | +| [SEP-2243](/seps/2243-http-standardization) | HTTP Header Standardization for Streamable HTTP Transport | Final | Standards Track | 2026-02-04 | +| [SEP-2207](/seps/2207-oidc-refresh-token-guidance) | OIDC-Flavored Refresh Token Guidance | Accepted | Standards Track | 2026-02-04 | +| [SEP-2149](/seps/2149-working-group-charter-template) | MCP Group Governance and Charter Template | Final | Process | 2025-01-15 | +| [SEP-2148](/seps/2148-contributor-ladder) | MCP Contributor Ladder | Final | Process | 2026-01-15 | +| [SEP-2133](/seps/2133-extensions) | Extensions | Final | Standards Track | 2025-01-21 | +| [SEP-2085](/seps/2085-governance-succession-and-amendment) | Governance Succession and Amendment Procedures | Final | Process | 2025-12-05 | +| [SEP-1865](/seps/1865-mcp-apps-interactive-user-interfaces-for-mcp) | MCP Apps - Interactive User Interfaces for MCP | Final | Extensions Track | 2025-11-21 | +| [SEP-1850](/seps/1850-pr-based-sep-workflow) | PR-Based SEP Workflow | Final | Process | 2025-11-20 | +| [SEP-1730](/seps/1730-sdks-tiering-system) | SDKs Tiering System | Final | Standards Track | 2025-10-29 | +| [SEP-1699](/seps/1699-support-sse-polling-via-server-side-disconnect) | Support SSE polling via server-side disconnect | Final | Standards Track | 2025-10-22 | +| [SEP-1686](/seps/1686-tasks) | Tasks | Final | Standards Track | 2025-10-20 | +| [SEP-1613](/seps/1613-establish-json-schema-2020-12-as-default-dialect-f) | Establish JSON Schema 2020-12 as Default Dialect for MCP | Final | Standards Track | 2025-10-06 | +| [SEP-1577](/seps/1577--sampling-with-tools) | Sampling With Tools | Final | Standards Track | 2025-09-30 | +| [SEP-1330](/seps/1330-elicitation-enum-schema-improvements-and-standards) | Elicitation Enum Schema Improvements and Standards Compliance | Final | Standards Track | 2025-08-11 | +| [SEP-1319](/seps/1319-decouple-request-payload-from-rpc-methods-definiti) | Decouple Request Payload from RPC Methods Definition | Final | Standards Track | 2025-08-08 | +| [SEP-1303](/seps/1303-input-validation-errors-as-tool-execution-errors) | Input Validation Errors as Tool Execution Errors | Final | Standards Track | 2025-08-05 | +| [SEP-1302](/seps/1302-formalize-working-groups-and-interest-groups-in-mc) | Formalize Working Groups and Interest Groups in MCP Governance | Final | Standards Track | 2025-08-05 | +| [SEP-1046](/seps/1046-support-oauth-client-credentials-flow-in-authoriza) | Support OAuth client credentials flow in authorization | Final | Standards Track | 2025-07-23 | +| [SEP-1036](/seps/1036-url-mode-elicitation-for-secure-out-of-band-intera) | URL Mode Elicitation for secure out-of-band interactions | Final | Standards Track | 2025-07-22 | +| [SEP-1034](/seps/1034--support-default-values-for-all-primitive-types-in) | Support default values for all primitive types in elicitation schemas | Final | Standards Track | 2025-07-22 | +| [SEP-1024](/seps/1024-mcp-client-security-requirements-for-local-server-) | MCP Client Security Requirements for Local Server Installation | Final | Standards Track | 2025-07-22 | +| [SEP-994](/seps/994-shared-communication-practicesguidelines) | Shared Communication Practices/Guidelines | Final | Process | 2025-07-17 | +| [SEP-991](/seps/991-enable-url-based-client-registration-using-oauth-c) | Enable URL-based Client Registration using OAuth Client ID Metadata Documents | Final | Standards Track | 2025-07-07 | +| [SEP-990](/seps/990-enable-enterprise-idp-policy-controls-during-mcp-o) | Enable enterprise IdP policy controls during MCP OAuth flows | Final | Standards Track | 2025-06-04 | +| [SEP-986](/seps/986-specify-format-for-tool-names) | Specify Format for Tool Names | Final | Standards Track | 2025-07-16 | +| [SEP-985](/seps/985-align-oauth-20-protected-resource-metadata-with-rf) | Align OAuth 2.0 Protected Resource Metadata with RFC 9728 | Final | Standards Track | 2025-07-16 | +| [SEP-973](/seps/973-expose-additional-metadata-for-implementations-res) | Expose additional metadata for Implementations, Resources, Tools and Prompts | Final | Standards Track | 2025-07-15 | +| [SEP-932](/seps/932-model-context-protocol-governance) | Model Context Protocol Governance | Final | Process | 2025-07-08 | +| [SEP-414](/seps/414-request-meta) | Document OpenTelemetry Trace Context Propagation Conventions | Final | Standards Track | 2025-04-25 | ## SEP Status Definitions diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md new file mode 100644 index 000000000..4c7735434 --- /dev/null +++ b/seps/2640-skills-extension.md @@ -0,0 +1,357 @@ +# SEP-2640: Skills Extension + +- **Type**: Extensions Track +- **Created**: 2026-04-23 +- **Author(s)**: Peter Alexander (@pja-ant), Ola Hungerford (@olaservo), + 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 + + +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). + + +## 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 well-known `skill://index.json` resource enumerates concrete skills and parameterized skill templates, but is not required — accommodating servers whose skill catalogs are large, generated, or otherwise unenumerable. 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. + +Because the extension adds no new protocol methods or capabilities, 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 load once at initialization 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, four 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. + +### 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:/// +``` + +A server MAY instead serve skills under another scheme native to its domain (e.g., `github://owner/repo/skills/refunds/SKILL.md`), provided each skill is listed in the [`skill://index.json`](#enumeration-via-skillindexjson) resource. The index is the authoritative record of which resources are skills; outside the index, hosts recognize skills by the `skill://` scheme prefix. + +The structural constraints below — `` ending in the skill name, `SKILL.md` explicit in the URI, no nesting — apply regardless of scheme. + +where: + +- `` 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`). +- `` 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.md`, and the skill's root directory is the URI obtained by stripping the trailing `SKILL.md`. + +The final segment of `` 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` MUST NOT appear in any descendant directory of a skill. The skill directory is the boundary; skills do not nest inside other skills. +- The final `` segment, being the skill `name`, MUST satisfy the Agent Skills specification's naming rules. Prefix segments SHOULD be valid URI path segments per [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986); no further constraints are imposed on them. + +Per RFC 3986, the first segment of `` occupies the authority component. This carries no special semantics under this convention and clients MUST NOT attempt DNS or network resolution of it. + +#### 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` | `templates/email.md` | `skill://acme/billing/refunds/templates/email.md` | + +#### Resource Metadata + +For each `skill:///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 ``. +- `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. Other files in the skill use the `mimeType` appropriate to their content. + +### 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 index, and hosts MUST support loading a skill given only its URI (see [Hosts: Model-Driven Resource Loading](#hosts-model-driven-resource-loading)). 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, two discovery mechanisms are defined. A server MAY support either or both. + +#### Enumeration via `skill://index.json` + +A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. The index format follows the [Agent Skills well-known URI discovery index](https://agentskills.io/well-known-uri#index-format), with two differences: the `url` field contains a full MCP resource URI (any scheme the server serves), and the `digest` field is omitted (integrity is the transport's concern over an authenticated MCP connection). This binding also defines one additional `type` value, `"mcp-resource-template"`, for entries that describe a parameterized skill namespace. + +```json +{ + "$schema": "https://schemas.agentskills.io/discovery/0.2.0/schema.json", + "skills": [ + { + "name": "git-workflow", + "type": "skill-md", + "description": "Follow this team's Git conventions for branching and commits", + "url": "skill://git-workflow/SKILL.md" + }, + { + "name": "refunds", + "type": "skill-md", + "description": "Process customer refund requests per company policy", + "url": "skill://acme/billing/refunds/SKILL.md" + }, + { + "name": "pdf-processing", + "type": "archive", + "description": "Extract, fill, and assemble PDF documents", + "url": "skill://pdf-processing.tar.gz" + }, + { + "type": "mcp-resource-template", + "description": "Per-product documentation skill", + "url": "skill://docs/{product}/SKILL.md" + } + ] +} +``` + +Index fields: + +| Field | Required | Description | +| ---------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `$schema` | Yes | Schema version URI. Clients SHOULD match against known URIs before processing. | +| `skills` | Yes | Array of skill entries. | +| `skills[].type` | Yes | MUST be `"skill-md"`, `"archive"`, or `"mcp-resource-template"`. | +| `skills[].description` | Yes | For `"skill-md"` and `"archive"`, the skill's `description` matching its `SKILL.md` frontmatter. For `"mcp-resource-template"`, a description of the addressable skill space. | +| `skills[].url` | Yes | Full resource URI. See per-type semantics below. | +| `skills[].name` | Conditional | Required for `"skill-md"` and `"archive"`; matches the `SKILL.md` frontmatter `name` and the final segment of the skill path. Omitted for `"mcp-resource-template"`. | + +Clients SHOULD ignore unrecognized fields and SHOULD skip entries with an unrecognized `type`. + +**Skill-md entries** (`type: "skill-md"`) point `url` at the full resource URI of the skill's `SKILL.md`. Supporting files are individually addressable as sibling resources under the same skill path. + +**Archive entries** (`type: "archive"`) point `url` at a single resource whose content is a packed skill directory. Per the [Agent Skills archive distribution format](https://agentskills.io/well-known-uri#archive-distribution), the archive MUST be `.tar.gz` (gzip-compressed tar, `mimeType` `application/gzip`) or `.zip` (`mimeType` `application/zip`); hosts MUST support both and SHOULD determine the format from the resource's `mimeType`, falling back to the URL suffix. Archive contents represent the skill directory directly — `SKILL.md` MUST be at the archive root, not nested inside a wrapper directory, and the archive MUST NOT contain path-traversal sequences (`..`) or absolute paths. + +The `` is the archive `url` with its archive suffix (`.tar.gz` or `.zip`) stripped — `skill://pdf-processing.tar.gz` unpacks to `skill://pdf-processing/`, `skill://acme/billing/refunds.zip` to `skill://acme/billing/refunds/` — and the archive contents populate `skill:///` exactly as if each file were served individually. A server chooses per-skill whether to distribute as individual files or as an archive; hosts observe an identical virtual namespace either way. Archive distribution delivers a multi-file skill atomically in one round trip and can carry UNIX file metadata (executable bits, symlinks) that individual-resource distribution cannot represent — robustly for `.tar.gz`, variably for `.zip`. + +Hosts unpacking an archive MUST apply the [archive safety](https://agentskills.io/well-known-uri#archive-safety) requirements of the Agent Skills specification: reject archives containing path-traversal sequences or absolute paths, reject symlinks or hard links that resolve outside the skill directory, and enforce a limit on total unpacked size. + +**Template entries** (`type: "mcp-resource-template"`) describe a parameterized skill namespace without materializing every entry. The `url` is an [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570) URI template that resolves to `SKILL.md` resource URIs. A server SHOULD register the same `url` value as an MCP [resource template](https://modelcontextprotocol.io/specification/2025-11-25/server/resources#resource-templates) so hosts can wire template variables to the [completion API](https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/completion). Hosts SHOULD surface template entries in their UI as interactive discovery points: the user fills in variables via completion, selects a skill, and the host passes the resolved URI into the conversation. This scales to servers with unbounded skill catalogs. + +The `skill://index.json` resource is served via `resources/read` like any other resource, with `mimeType` of `application/json`. A server MAY also surface it in `resources/list` so clients can detect its presence, but clients MAY attempt to read it directly without prior discovery. + +A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY decline to expose `skill://index.json`, or MAY expose a partial index. Hosts MUST NOT treat an absent or empty index as proof that a server has no skills. + +The URI `skill://index.json` is reserved and does not conflict with any valid ``: skill names may contain only lowercase letters, digits, and hyphens, so `index.json` cannot be a skill name. + +#### 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": {} + } + } +} +``` + +No extension-specific settings are currently defined; an empty object indicates support. + +### Reading + +Skill files are read via the standard `resources/read` method. No skill-specific read semantics are defined. + +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 `skill://` scheme root. + +## Implementation Guidelines + +The following are recommendations for interoperable implementations. They are not part of the normative specification. + +### Hosts: Model-Driven Resource Loading + +Hosts SHOULD expose a tool to the model that reads MCP resources by server and URI, enabling the model to load skill content on demand: + +```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, e.g. skill://git-workflow/SKILL.md" + } + }, + "required": ["server", "uri"] + } +} +``` + +The signature shown is illustrative. Including the server name is one disambiguation strategy for identical `skill://` URIs served by different connected servers; hosts MAY instead prefix URIs on conflict, scope by session, or use any other scheme appropriate to their architecture. The tool is general-purpose — it reads any MCP resource — and benefits resource use cases beyond skills. + +Hosts SHOULD load the frontmatter (`name`, `description`) of available and enabled skills into the model's context so the model can judge relevance and construct a `read_resource` call when a skill applies. Hosts SHOULD surface available skills in their UI for user inspection and per-skill enable/disable, analogous to how tools are typically exposed. + +A typical flow: the host reads `skill://index.json` from each connected server and surfaces the `name` and `description` of each entry in the model's context. The model calls `read_resource` with a concrete URI — one returned by enumeration, one handed to it by the user (who may have resolved it from a template entry in the host UI), or one obtained out-of-band — when a skill is relevant to the task. + +Because enumeration is optional, a `read_resource` call for a `skill://` URI that the host has never seen listed is normal and expected. The host forwards it to the named server; the server either serves the resource or returns a not-found error. + +### Hosts: Unified Treatment of Filesystem and MCP Skills + +Hosts that support both filesystem-based skills (loaded from local directories) and MCP-served skills SHOULD treat them identically, as though the set of connected servers' `skill://` resources were mounted into a virtual filesystem alongside local skill directories. + +Concretely: the same discovery surface, the same loading tool, and the same relative-path resolution. A model that has learned to follow `references/GUIDE.md` from a local `SKILL.md` should find that MCP-served skills behave the same way. Divergence between the two paths is a source of model confusion and implementation complexity. + +### 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, registering a `skill:///{+path}` resource template, serving file content on `resources/read`, and (where the server's skill set is bounded) generating the `skill://index.json` resource. + +**Client-side** — enumerate and fetch skills: + +```python +skills = await client.list_skills() # reads skill://index.json, may be empty or absent +content = await client.read_skill_uri( + "skill://acme/billing/refunds/SKILL.md") # wraps resources/read, works regardless of enumeration +``` + +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 + +### Why Resources Instead of a New Primitive? + +The Working Group's [decision log](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-02-26-prioritize-skills-as-resources-with-client-helper-tools) records this as settled. Skills are files; Resources exist to expose files. Reusing Resources inherits URI addressability, `resources/read`, `resources/subscribe`, templates, and the existing client tooling for free. A new primitive would duplicate most of this and add ecosystem complexity — a concern raised in [community discussion](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/14). + +[SEP-2076] proposes the new-primitive alternative. That approach offers cleaner capability negotiation and dedicated list-changed notifications, but at the cost of flattening skills to name-addressed blobs — losing the directory model that the Agent Skills specification defines and that supporting files depend on. + +### Why `skill:///` With an Explicit `SKILL.md`? + +Four independent implementations converged on `skill://` as the scheme without coordination — a strong signal. They diverged on structure. This SEP adopts the explicit-file form because: + +- It directly mirrors the Agent Skills specification's directory model. A skill _is_ a directory; its URI space should look like one. +- `SKILL.md` being explicit means supporting files are siblings at the same level, with no special casing for "the skill URI" versus "a file in the skill." +- Hosts implementing both filesystem and MCP skills can use one path-resolution codepath. + +The cost — `SKILL.md` is always typed out rather than implied — is small, and where discovery is supported the response already points clients at the right URI. + +### Why Allow a Path Prefix But Constrain the Final Segment? + +Earlier drafts required `` to be a single segment equal to the frontmatter `name`. That breaks down when a server needs hierarchy: an organization serving both `acme/billing/refunds` and `acme/support/refunds` cannot satisfy "single segment" without renaming one skill to dodge the collision. Allowing a prefix (`acme/billing/`, `acme/support/`) solves this — both skills can be named `refunds` and the prefix disambiguates. + +A subsequent draft went further and fully decoupled the path from the name. That was too loose: a URI like `skill://a/b/c/SKILL.md` tells you nothing about what the skill is called until you fetch and parse frontmatter. Clients listing skills, hosts displaying them in a picker, and models reasoning over URIs all want the name visible without a round trip. + +Constraining the final segment to match the frontmatter `name` gets both properties. The prefix carries the server's organizational structure; the final segment carries the skill's identity; and the two together form a locator from which the name can be read directly. + +### Why Is Enumeration Optional? + +Requiring every server to expose a complete `skill://index.json` fails for at least three server shapes: a documentation server that synthesizes a skill per API endpoint (thousands), a skill gateway fronting an external index (unbounded), and a server that generates skills from templates parameterized at read time (unenumerable by construction). For these, the list is either too large to be useful in the model's context or does not meaningfully exist. + +The baseline is therefore direct readability — a skill URI is always a valid argument to `resources/read`. The index (concrete entries and templates) is layered on top for servers where it makes sense. A host that assumes enumeration is exhaustive will miss skills on servers where it is not, hence the requirement that hosts MUST NOT treat empty enumeration as proof of absence. + +### Why Delegate the Format to agentskills.io? + +The Agent Skills specification already defines YAML frontmatter fields, naming rules, directory conventions, and the progressive-disclosure model. It has its own governance, contributing process, and multi-vendor participation. Redefining any of this in an MCP SEP would create a second source of truth and a drift risk. This SEP is a transport binding; the payload format is someone else's concern. + +### Why Permit Archive Distribution? + +An earlier draft excluded `type: "archive"` on the basis that supporting files are already individually addressable as resources. The Working Group [reversed this](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-04-19-archives-permitted-as-server-side-packaging-optimization) for four reasons: dropping a distribution type that the Agent Skills discovery format defines contradicts this SEP's framing as a pure transport binding; individual-file distribution cannot guarantee atomicity across a multi-file skill read; hosts that pre-materialize skills to a local filesystem pay N+1 round trips; and UNIX file metadata (executable bits, symlinks) has no representation when files are served individually. + +Reinstating archives as a server-side packaging choice — not a client-visible mode split — preserves the virtual-filesystem model: the post-unpack view is identical to individual-file distribution. The property given up is per-file `resources/subscribe` granularity, which is acceptable because subscription is not part of the skill reading model defined here. + +## Backward Compatibility + +This extension introduces no new protocol 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 (NimbleBrain's `skill://server/skill`, skilljack's implicit-`SKILL.md` `skill://name`) will need to adjust their URI paths to conform. These are small, mechanical changes. + +## Security Implications + +Skill content is instructional text delivered to a model, which makes it a prompt-injection surface. The Working Group's position, recorded 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), is: + +- **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. +- **Skills do not introduce a new trust tier.** A user who connects a server has already extended their trust boundary to it; a malicious server can do as much harm via tools as via a skill document. Serving skills over MCP adds no risk beyond what skills already carry in any transport — but the defensive posture above applies regardless. +- **No implicit local execution.** Hosts MUST NOT honor mechanisms in skill content that would cause local code execution without explicit user opt-in. This includes, non-exhaustively: hook declarations, pre/post-invocation scripts, shell commands embedded in frontmatter, or any field that a filesystem-sourced skill might use to register executable behavior on the host. Hosts MUST either ignore such fields entirely when the skill arrives over MCP, or gate them behind an explicit per-skill user approval that states what will execute and where. Silently executing server-provided code because it appeared in a skill directory is a remote code execution vector. +- **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. +- **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. +- **Archive safety.** Archive-distributed skills are an unpacking attack surface. Hosts MUST validate archives per the [Agent Skills archive safety requirements](https://agentskills.io/well-known-uri#archive-safety): reject path traversal and absolute paths, reject links resolving outside the skill directory, and bound total unpacked size to prevent decompression bombs. +- **Not a third-party marketplace.** This extension is for servers to ship skills that describe their own tools, not for distributing arbitrary third-party content through a connected server. + +The instructor-only scope of this extension ([decisions.md, 2026-02-14](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-02-14-skills-served-over-mcp-use-the-instructor-format)) deliberately excludes the helper model. A filesystem skill might reasonably carry scripts the user has audited; an MCP skill arrives from a remote party and MUST be handled as text that influences model behavior, not as code that executes on the host. + +## 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) + +**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 + +**Server implementation:** + +- GitHub MCP Server: [github/github-mcp-server#2360](https://github.com/github/github-mcp-server/pull/2360) + +## References + +- [Agent Skills specification](https://agentskills.io/specification) +- [Agent Skills well-known URI discovery](https://agentskills.io/well-known-uri) +- [SEP-2133]: Extensions +- [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 +- [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 From b3f015a7929041dada4b0eaf5a657b30d4f5d6d1 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Thu, 23 Apr 2026 20:49:00 +0100 Subject: [PATCH 02/25] SEP-2640: set Status to Draft --- docs/seps/2640-skills-extension.mdx | 4 ++-- docs/seps/index.mdx | 4 ++-- seps/2640-skills-extension.md | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index 77ee1605f..22f273840 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -6,7 +6,7 @@ description: "Skills Extension"
- Unknown + Draft Extensions Track @@ -17,7 +17,7 @@ description: "Skills Extension" | ------------- | -------------------------------------------------------------------------------------------------------------------- | | **SEP** | 2640 | | **Title** | Skills Extension | -| **Status** | Unknown | +| **Status** | Draft | | **Type** | Extensions Track | | **Created** | 2026-04-23 | | **Author(s)** | Peter Alexander ([@pja-ant](https://github.com/pja-ant)), Ola Hungerford ([@olaservo](https://github.com/olaservo)), | diff --git a/docs/seps/index.mdx b/docs/seps/index.mdx index 70a073925..4b00483f0 100644 --- a/docs/seps/index.mdx +++ b/docs/seps/index.mdx @@ -12,7 +12,7 @@ Specification Enhancement Proposals (SEPs) are the primary mechanism for proposi ## Summary -- **Unknown**: 1 +- **Draft**: 2 - **Accepted**: 2 - **Final**: 28 @@ -20,7 +20,7 @@ Specification Enhancement Proposals (SEPs) are the primary mechanism for proposi | SEP | Title | Status | Type | Created | | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | ------------------------------------------------- | ---------------- | ---------- | -| [SEP-2640](/seps/2640-skills-extension) | Skills Extension | Unknown | Extensions Track | 2026-04-23 | +| [SEP-2640](/seps/2640-skills-extension) | Skills Extension | Draft | Extensions Track | 2026-04-23 | | [SEP-2260](/seps/2260-Require-Server-requests-to-be-associated-with-Client-requests) | Require Server requests to be associated with a Client request. | Accepted | Standards Track | 2026-02-16 | | [SEP-2243](/seps/2243-http-standardization) | HTTP Header Standardization for Streamable HTTP Transport | Final | Standards Track | 2026-02-04 | | [SEP-2207](/seps/2207-oidc-refresh-token-guidance) | OIDC-Flavored Refresh Token Guidance | Accepted | Standards Track | 2026-02-04 | diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index 4c7735434..f2cda29ff 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -1,5 +1,6 @@ # SEP-2640: Skills Extension +- **Status**: Draft - **Type**: Extensions Track - **Created**: 2026-04-23 - **Author(s)**: Peter Alexander (@pja-ant), Ola Hungerford (@olaservo), From 15473e8345badd41f65beac0a2b8277659817207 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Fri, 24 Apr 2026 15:41:48 +0100 Subject: [PATCH 03/25] SEP-2640: address review feedback from olaservo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - "four" → "several" implementations (count will rot) - Fix orphaned "where:" — alternative-scheme paragraph had been inserted between the URI form and its definitions; moved it after the constraints block - Add SHOULD for io.modelcontextprotocol.skills/ _meta prefix - Cite design-principles instead of a transient GH issue for the resources-over-primitive rationale - SEP-2076 "proposes" → "originally proposed" (PR is closed) - Backward Compat: replace named small projects with link to the WG related-work survey; call out FastMCP SkillsProvider explicitly as the high-impact migration - Drop redundant instructor-scope paragraph after the security bullets - Label host/server reference impls as "Prototype" - Mark Experimental Findings as (WIP); add Related Work and _meta Keys to References --- docs/seps/2640-skills-extension.mdx | 28 +++++++++++++--------------- seps/2640-skills-extension.md | 28 +++++++++++++--------------- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index 22f273840..e1513422a 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -38,7 +38,7 @@ Native skills support in host applications demonstrates strong demand for rich, - **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 load once at initialization 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, four independent implementations have each invented their own `skill://` URI structure, with diverging semantics for authority, path, and sub-resource addressing. +- **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 @@ -65,10 +65,6 @@ Each file within a skill directory is exposed as an MCP resource. Servers SHOULD skill:/// ``` -A server MAY instead serve skills under another scheme native to its domain (e.g., `github://owner/repo/skills/refunds/SKILL.md`), provided each skill is listed in the [`skill://index.json`](#enumeration-via-skillindexjson) resource. The index is the authoritative record of which resources are skills; outside the index, hosts recognize skills by the `skill://` scheme prefix. - -The structural constraints below — `` ending in the skill name, `SKILL.md` explicit in the URI, no nesting — apply regardless of scheme. - where: - `` 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`). @@ -85,6 +81,8 @@ Further constraints: Per RFC 3986, the first segment of `` 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 instead serve skills under another scheme native to its domain (e.g., `github://owner/repo/skills/refunds/SKILL.md`), provided each skill is listed in the [`skill://index.json`](#enumeration-via-skillindexjson) resource. The index is the authoritative record of which resources are skills; outside the index, hosts recognize skills by the `skill://` scheme prefix. The structural constraints above — `` ending in the skill name, `SKILL.md` explicit in the URI, no nesting — apply regardless of scheme. + #### Examples | Skill path | File | Resource URI | @@ -103,7 +101,7 @@ For each `skill:///SKILL.md` resource: - `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 ``. - `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. Other files in the skill use the `mimeType` appropriate to their content. +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. ### Discovery @@ -276,13 +274,13 @@ These wrappers are thin — each is a single underlying protocol call with a fix ### Why Resources Instead of a New Primitive? -The Working Group's [decision log](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-02-26-prioritize-skills-as-resources-with-client-helper-tools) records this as settled. Skills are files; Resources exist to expose files. Reusing Resources inherits URI addressability, `resources/read`, `resources/subscribe`, templates, and the existing client tooling for free. A new primitive would duplicate most of this and add ecosystem complexity — a concern raised in [community discussion](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/14). +The Working Group's [decision log](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-02-26-prioritize-skills-as-resources-with-client-helper-tools) records this as settled. Skills are files; Resources exist to expose files. Reusing Resources inherits URI addressability, `resources/read`, `resources/subscribe`, templates, and the existing client tooling for free. A new primitive would duplicate most of this and add ecosystem complexity. Using resources to describe files is also aligned with [Composability over specificity](https://modelcontextprotocol.io/community/design-principles#composability-over-specificity) and other MCP [design principles](https://modelcontextprotocol.io/community/design-principles). -[SEP-2076] proposes the new-primitive alternative. That approach offers cleaner capability negotiation and dedicated list-changed notifications, but at the cost of flattening skills to name-addressed blobs — losing the directory model that the Agent Skills specification defines and that supporting files depend on. +[SEP-2076] originally proposed the new-primitive alternative. That approach offers cleaner capability negotiation and dedicated list-changed notifications, but at the cost of flattening skills to name-addressed blobs — losing the directory model that the Agent Skills specification defines and that supporting files depend on. ### Why `skill:///` With an Explicit `SKILL.md`? -Four independent implementations converged on `skill://` as the scheme without coordination — a strong signal. They diverged on structure. This SEP adopts the explicit-file form because: +Several independent implementations converged on `skill://` as the scheme without coordination — a strong signal. They diverged on structure. This SEP adopts the explicit-file form because: - It directly mirrors the Agent Skills specification's directory model. A skill _is_ a directory; its URI space should look like one. - `SKILL.md` being explicit means supporting files are siblings at the same level, with no special casing for "the skill URI" versus "a file in the skill." @@ -318,7 +316,7 @@ Reinstating archives as a server-side packaging choice — not a client-visible This extension introduces no new protocol 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 (NimbleBrain's `skill://server/skill`, skilljack's implicit-`SKILL.md` `skill://name`) will need to adjust their URI paths to conform. These are small, mechanical changes. +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 @@ -332,8 +330,6 @@ Skill content is instructional text delivered to a model, which makes it a promp - **Archive safety.** Archive-distributed skills are an unpacking attack surface. Hosts MUST validate archives per the [Agent Skills archive safety requirements](https://agentskills.io/well-known-uri#archive-safety): reject path traversal and absolute paths, reject links resolving outside the skill directory, and bound total unpacked size to prevent decompression bombs. - **Not a third-party marketplace.** This extension is for servers to ship skills that describe their own tools, not for distributing arbitrary third-party content through a connected server. -The instructor-only scope of this extension ([decisions.md, 2026-02-14](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-02-14-skills-served-over-mcp-use-the-instructor-format)) deliberately excludes the helper model. A filesystem skill might reasonably carry scripts the user has audited; an MCP skill arrives from a remote party and MUST be handled as text that influences model behavior, not as code that executes on the host. - ## Reference Implementation Per [SEP-2133], an Extensions Track SEP requires at least one reference implementation in an official SDK prior to review. @@ -342,7 +338,7 @@ Per [SEP-2133], an Extensions Track SEP requires at least one reference implemen - TypeScript SDK convenience wrappers (`@server.skill()`, `client.listSkills()`, `client.readSkillUri()`): [experimental-ext-skills#71](https://github.com/modelcontextprotocol/experimental-ext-skills/pull/71) -**Host implementations** (reading `skill://` resources, surfacing skills alongside filesystem skills): +**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) @@ -350,7 +346,7 @@ Per [SEP-2133], an Extensions Track SEP requires at least one reference implemen - codex: [olaservo/codex#1](https://github.com/olaservo/codex/pull/1) - Claude Code: prototyped internally at Anthropic; not yet public -**Server implementation:** +**Prototype server implementation:** - GitHub MCP Server: [github/github-mcp-server#2360](https://github.com/github/github-mcp-server/pull/2360) @@ -362,7 +358,9 @@ Per [SEP-2133], an Extensions Track SEP requires at least one reference implemen - [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 +- [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 diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index f2cda29ff..bdef7de24 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -27,7 +27,7 @@ Native skills support in host applications demonstrates strong demand for rich, - **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 load once at initialization 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, four independent implementations have each invented their own `skill://` URI structure, with diverging semantics for authority, path, and sub-resource addressing. +- **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 @@ -54,10 +54,6 @@ Each file within a skill directory is exposed as an MCP resource. Servers SHOULD skill:/// ``` -A server MAY instead serve skills under another scheme native to its domain (e.g., `github://owner/repo/skills/refunds/SKILL.md`), provided each skill is listed in the [`skill://index.json`](#enumeration-via-skillindexjson) resource. The index is the authoritative record of which resources are skills; outside the index, hosts recognize skills by the `skill://` scheme prefix. - -The structural constraints below — `` ending in the skill name, `SKILL.md` explicit in the URI, no nesting — apply regardless of scheme. - where: - `` 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`). @@ -74,6 +70,8 @@ Further constraints: Per RFC 3986, the first segment of `` 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 instead serve skills under another scheme native to its domain (e.g., `github://owner/repo/skills/refunds/SKILL.md`), provided each skill is listed in the [`skill://index.json`](#enumeration-via-skillindexjson) resource. The index is the authoritative record of which resources are skills; outside the index, hosts recognize skills by the `skill://` scheme prefix. The structural constraints above — `` ending in the skill name, `SKILL.md` explicit in the URI, no nesting — apply regardless of scheme. + #### Examples | Skill path | File | Resource URI | @@ -92,7 +90,7 @@ For each `skill:///SKILL.md` resource: - `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 ``. - `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. Other files in the skill use the `mimeType` appropriate to their content. +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. ### Discovery @@ -265,13 +263,13 @@ These wrappers are thin — each is a single underlying protocol call with a fix ### Why Resources Instead of a New Primitive? -The Working Group's [decision log](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-02-26-prioritize-skills-as-resources-with-client-helper-tools) records this as settled. Skills are files; Resources exist to expose files. Reusing Resources inherits URI addressability, `resources/read`, `resources/subscribe`, templates, and the existing client tooling for free. A new primitive would duplicate most of this and add ecosystem complexity — a concern raised in [community discussion](https://github.com/modelcontextprotocol/experimental-ext-skills/issues/14). +The Working Group's [decision log](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-02-26-prioritize-skills-as-resources-with-client-helper-tools) records this as settled. Skills are files; Resources exist to expose files. Reusing Resources inherits URI addressability, `resources/read`, `resources/subscribe`, templates, and the existing client tooling for free. A new primitive would duplicate most of this and add ecosystem complexity. Using resources to describe files is also aligned with [Composability over specificity](https://modelcontextprotocol.io/community/design-principles#composability-over-specificity) and other MCP [design principles](https://modelcontextprotocol.io/community/design-principles). -[SEP-2076] proposes the new-primitive alternative. That approach offers cleaner capability negotiation and dedicated list-changed notifications, but at the cost of flattening skills to name-addressed blobs — losing the directory model that the Agent Skills specification defines and that supporting files depend on. +[SEP-2076] originally proposed the new-primitive alternative. That approach offers cleaner capability negotiation and dedicated list-changed notifications, but at the cost of flattening skills to name-addressed blobs — losing the directory model that the Agent Skills specification defines and that supporting files depend on. ### Why `skill:///` With an Explicit `SKILL.md`? -Four independent implementations converged on `skill://` as the scheme without coordination — a strong signal. They diverged on structure. This SEP adopts the explicit-file form because: +Several independent implementations converged on `skill://` as the scheme without coordination — a strong signal. They diverged on structure. This SEP adopts the explicit-file form because: - It directly mirrors the Agent Skills specification's directory model. A skill _is_ a directory; its URI space should look like one. - `SKILL.md` being explicit means supporting files are siblings at the same level, with no special casing for "the skill URI" versus "a file in the skill." @@ -307,7 +305,7 @@ Reinstating archives as a server-side packaging choice — not a client-visible This extension introduces no new protocol 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 (NimbleBrain's `skill://server/skill`, skilljack's implicit-`SKILL.md` `skill://name`) will need to adjust their URI paths to conform. These are small, mechanical changes. +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 @@ -321,8 +319,6 @@ Skill content is instructional text delivered to a model, which makes it a promp - **Archive safety.** Archive-distributed skills are an unpacking attack surface. Hosts MUST validate archives per the [Agent Skills archive safety requirements](https://agentskills.io/well-known-uri#archive-safety): reject path traversal and absolute paths, reject links resolving outside the skill directory, and bound total unpacked size to prevent decompression bombs. - **Not a third-party marketplace.** This extension is for servers to ship skills that describe their own tools, not for distributing arbitrary third-party content through a connected server. -The instructor-only scope of this extension ([decisions.md, 2026-02-14](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-02-14-skills-served-over-mcp-use-the-instructor-format)) deliberately excludes the helper model. A filesystem skill might reasonably carry scripts the user has audited; an MCP skill arrives from a remote party and MUST be handled as text that influences model behavior, not as code that executes on the host. - ## Reference Implementation Per [SEP-2133], an Extensions Track SEP requires at least one reference implementation in an official SDK prior to review. @@ -331,7 +327,7 @@ Per [SEP-2133], an Extensions Track SEP requires at least one reference implemen - TypeScript SDK convenience wrappers (`@server.skill()`, `client.listSkills()`, `client.readSkillUri()`): [experimental-ext-skills#71](https://github.com/modelcontextprotocol/experimental-ext-skills/pull/71) -**Host implementations** (reading `skill://` resources, surfacing skills alongside filesystem skills): +**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) @@ -339,7 +335,7 @@ Per [SEP-2133], an Extensions Track SEP requires at least one reference implemen - codex: [olaservo/codex#1](https://github.com/olaservo/codex/pull/1) - Claude Code: prototyped internally at Anthropic; not yet public -**Server implementation:** +**Prototype server implementation:** - GitHub MCP Server: [github/github-mcp-server#2360](https://github.com/github/github-mcp-server/pull/2360) @@ -351,7 +347,9 @@ Per [SEP-2133], an Extensions Track SEP requires at least one reference implemen - [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 +- [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 From f505d181095ee4aed40f9f15ac33c0b49d9fb816 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Fri, 24 Apr 2026 15:43:50 +0100 Subject: [PATCH 04/25] SEP-2640: regenerate nav/index after rebase onto main --- docs/docs.json | 6 ++++++ docs/seps/index.mdx | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/docs.json b/docs/docs.json index c844cecf5..60cd1b153 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -458,6 +458,12 @@ "seps/2596-spec-feature-lifecycle-and-deprecation", "seps/2663-tasks-extension" ] + }, + { + "group": "Draft", + "pages": [ + "seps/2640-skills-extension" + ] } ] }, diff --git a/docs/seps/index.mdx b/docs/seps/index.mdx index 4b00483f0..359023faa 100644 --- a/docs/seps/index.mdx +++ b/docs/seps/index.mdx @@ -12,7 +12,7 @@ Specification Enhancement Proposals (SEPs) are the primary mechanism for proposi ## Summary -- **Draft**: 2 +- **Draft**: 1 - **Accepted**: 2 - **Final**: 28 From 3b1da1e1b16037495bfe175effa8e8fea7f20b6b Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Thu, 30 Apr 2026 10:49:49 +0100 Subject: [PATCH 05/25] SEP-2640: rewrite host guidelines as end-to-end integration walkthrough MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the separate Model-Driven Resource Loading and Unified Treatment sections with a single illustrative flow: host-side name→origin registry, a name-keyed read_skill tool that routes to filesystem or resources/read, and read_resource positioned for supporting files. Addresses review feedback that the previous read_resource(server, uri) example did not actually unify with filesystem skills. --- docs/seps/2640-skills-extension.mdx | 49 ++++++++++++++++++----------- seps/2640-skills-extension.md | 49 ++++++++++++++++++----------- 2 files changed, 62 insertions(+), 36 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index e1513422a..ff5a832dd 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -105,7 +105,7 @@ Servers MAY expose additional frontmatter fields via the resource's `_meta` obje ### 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 index, and hosts MUST support loading a skill given only its URI (see [Hosts: Model-Driven Resource Loading](#hosts-model-driven-resource-loading)). This is the baseline: if a model has the URI — from server instructions, from another skill, from the user — it can read the skill. +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 index, 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, two discovery mechanisms are defined. A server MAY support either or both. @@ -203,9 +203,33 @@ Internal references within a skill (e.g., `SKILL.md` linking to `references/GUID The following are recommendations for interoperable implementations. They are not part of the normative specification. -### Hosts: Model-Driven Resource Loading +### Hosts: End-to-End Integration -Hosts SHOULD expose a tool to the model that reads MCP resources by server and URI, enabling the model to load skill content on demand: +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, from the model's perspective, an MCP-served skill is indistinguishable from a filesystem skill. + +**Registry.** At startup and on connection change, the host assembles a single internal skill registry from every origin it supports: filesystem skill directories, and `skill://index.json` from each connected MCP server that declares the `io.modelcontextprotocol/skills` extension. Each registry entry records the skill's `name`, `description`, and origin — for a filesystem skill, the local directory; for an MCP skill, the server identity and the `SKILL.md` resource URI. Origin is host bookkeeping; it is not exposed to the model. + +**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"] + } +} +``` + +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. The model neither knows nor cares which path was taken. 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 { @@ -218,29 +242,18 @@ Hosts SHOULD expose a tool to the model that reads MCP resources by server and U "type": "string", "description": "Name of the connected MCP server" }, - "uri": { - "type": "string", - "description": "The resource URI, e.g. skill://git-workflow/SKILL.md" - } + "uri": { "type": "string", "description": "The resource URI" } }, "required": ["server", "uri"] } } ``` -The signature shown is illustrative. Including the server name is one disambiguation strategy for identical `skill://` URIs served by different connected servers; hosts MAY instead prefix URIs on conflict, scope by session, or use any other scheme appropriate to their architecture. The tool is general-purpose — it reads any MCP resource — and benefits resource use cases beyond skills. - -Hosts SHOULD load the frontmatter (`name`, `description`) of available and enabled skills into the model's context so the model can judge relevance and construct a `read_resource` call when a skill applies. Hosts SHOULD surface available skills in their UI for user inspection and per-skill enable/disable, analogous to how tools are typically exposed. - -A typical flow: the host reads `skill://index.json` from each connected server and surfaces the `name` and `description` of each entry in the model's context. The model calls `read_resource` with a concrete URI — one returned by enumeration, one handed to it by the user (who may have resolved it from a template entry in the host UI), or one obtained out-of-band — when a skill is relevant to the task. - -Because enumeration is optional, a `read_resource` call for a `skill://` URI that the host has never seen listed is normal and expected. The host forwards it to the named server; the server either serves the resource or returns a not-found error. - -### Hosts: Unified Treatment of Filesystem and MCP Skills +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:///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 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. Either way the resolution rule is the same: relative references resolve against the skill's root directory, exactly as on a filesystem. -Hosts that support both filesystem-based skills (loaded from local directories) and MCP-served skills SHOULD treat them identically, as though the set of connected servers' `skill://` resources were mounted into a virtual filesystem alongside local skill directories. +**Unenumerated skills.** Because enumeration is optional, 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. A `read_resource` call for an unlisted `skill://` URI is forwarded to the named server, which either serves it or returns not-found. A host may additionally let `read_skill` accept a full URI for this case. -Concretely: the same discovery surface, the same loading tool, and the same relative-path resolution. A model that has learned to follow `references/GUIDE.md` from a local `SKILL.md` should find that MCP-served skills behave the same way. Divergence between the two paths is a source of model confusion and implementation complexity. +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 diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index bdef7de24..4612a0f85 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -94,7 +94,7 @@ Servers MAY expose additional frontmatter fields via the resource's `_meta` obje ### 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 index, and hosts MUST support loading a skill given only its URI (see [Hosts: Model-Driven Resource Loading](#hosts-model-driven-resource-loading)). This is the baseline: if a model has the URI — from server instructions, from another skill, from the user — it can read the skill. +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 index, 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, two discovery mechanisms are defined. A server MAY support either or both. @@ -192,9 +192,33 @@ Internal references within a skill (e.g., `SKILL.md` linking to `references/GUID The following are recommendations for interoperable implementations. They are not part of the normative specification. -### Hosts: Model-Driven Resource Loading +### Hosts: End-to-End Integration -Hosts SHOULD expose a tool to the model that reads MCP resources by server and URI, enabling the model to load skill content on demand: +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, from the model's perspective, an MCP-served skill is indistinguishable from a filesystem skill. + +**Registry.** At startup and on connection change, the host assembles a single internal skill registry from every origin it supports: filesystem skill directories, and `skill://index.json` from each connected MCP server that declares the `io.modelcontextprotocol/skills` extension. Each registry entry records the skill's `name`, `description`, and origin — for a filesystem skill, the local directory; for an MCP skill, the server identity and the `SKILL.md` resource URI. Origin is host bookkeeping; it is not exposed to the model. + +**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"] + } +} +``` + +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. The model neither knows nor cares which path was taken. 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 { @@ -207,29 +231,18 @@ Hosts SHOULD expose a tool to the model that reads MCP resources by server and U "type": "string", "description": "Name of the connected MCP server" }, - "uri": { - "type": "string", - "description": "The resource URI, e.g. skill://git-workflow/SKILL.md" - } + "uri": { "type": "string", "description": "The resource URI" } }, "required": ["server", "uri"] } } ``` -The signature shown is illustrative. Including the server name is one disambiguation strategy for identical `skill://` URIs served by different connected servers; hosts MAY instead prefix URIs on conflict, scope by session, or use any other scheme appropriate to their architecture. The tool is general-purpose — it reads any MCP resource — and benefits resource use cases beyond skills. - -Hosts SHOULD load the frontmatter (`name`, `description`) of available and enabled skills into the model's context so the model can judge relevance and construct a `read_resource` call when a skill applies. Hosts SHOULD surface available skills in their UI for user inspection and per-skill enable/disable, analogous to how tools are typically exposed. - -A typical flow: the host reads `skill://index.json` from each connected server and surfaces the `name` and `description` of each entry in the model's context. The model calls `read_resource` with a concrete URI — one returned by enumeration, one handed to it by the user (who may have resolved it from a template entry in the host UI), or one obtained out-of-band — when a skill is relevant to the task. - -Because enumeration is optional, a `read_resource` call for a `skill://` URI that the host has never seen listed is normal and expected. The host forwards it to the named server; the server either serves the resource or returns a not-found error. - -### Hosts: Unified Treatment of Filesystem and MCP Skills +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:///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 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. Either way the resolution rule is the same: relative references resolve against the skill's root directory, exactly as on a filesystem. -Hosts that support both filesystem-based skills (loaded from local directories) and MCP-served skills SHOULD treat them identically, as though the set of connected servers' `skill://` resources were mounted into a virtual filesystem alongside local skill directories. +**Unenumerated skills.** Because enumeration is optional, 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. A `read_resource` call for an unlisted `skill://` URI is forwarded to the named server, which either serves it or returns not-found. A host may additionally let `read_skill` accept a full URI for this case. -Concretely: the same discovery surface, the same loading tool, and the same relative-path resolution. A model that has learned to follow `references/GUIDE.md` from a local `SKILL.md` should find that MCP-served skills behave the same way. Divergence between the two paths is a source of model confusion and implementation complexity. +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 From 4af8bd67deb1ea3d7157dda7016b0b6beddd33ca Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Tue, 5 May 2026 17:28:16 +0100 Subject: [PATCH 06/25] SEP-2640: drop third-party marketplace bullet from security considerations --- docs/seps/2640-skills-extension.mdx | 1 - seps/2640-skills-extension.md | 1 - 2 files changed, 2 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index ff5a832dd..7c11d0fc6 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -341,7 +341,6 @@ Skill content is instructional text delivered to a model, which makes it a promp - **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. - **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. - **Archive safety.** Archive-distributed skills are an unpacking attack surface. Hosts MUST validate archives per the [Agent Skills archive safety requirements](https://agentskills.io/well-known-uri#archive-safety): reject path traversal and absolute paths, reject links resolving outside the skill directory, and bound total unpacked size to prevent decompression bombs. -- **Not a third-party marketplace.** This extension is for servers to ship skills that describe their own tools, not for distributing arbitrary third-party content through a connected server. ## Reference Implementation diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index 4612a0f85..ce36ebc3a 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -330,7 +330,6 @@ Skill content is instructional text delivered to a model, which makes it a promp - **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. - **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. - **Archive safety.** Archive-distributed skills are an unpacking attack surface. Hosts MUST validate archives per the [Agent Skills archive safety requirements](https://agentskills.io/well-known-uri#archive-safety): reject path traversal and absolute paths, reject links resolving outside the skill directory, and bound total unpacked size to prevent decompression bombs. -- **Not a third-party marketplace.** This extension is for servers to ship skills that describe their own tools, not for distributing arbitrary third-party content through a connected server. ## Reference Implementation From 165088b4b96195f21cf3cfb91c40dd86a17203ac Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Thu, 28 May 2026 16:17:02 +0100 Subject: [PATCH 07/25] Fix dead Agent Skills index-format link in discovery section --- docs/seps/2640-skills-extension.mdx | 2 +- seps/2640-skills-extension.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index 7c11d0fc6..0f49ca7e1 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -111,7 +111,7 @@ On top of that baseline, two discovery mechanisms are defined. A server MAY supp #### Enumeration via `skill://index.json` -A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. The index format follows the [Agent Skills well-known URI discovery index](https://agentskills.io/well-known-uri#index-format), with two differences: the `url` field contains a full MCP resource URI (any scheme the server serves), and the `digest` field is omitted (integrity is the transport's concern over an authenticated MCP connection). This binding also defines one additional `type` value, `"mcp-resource-template"`, for entries that describe a parameterized skill namespace. +A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. The index format follows the [Agent Skills well-known URI discovery index](https://github.com/agentskills/agentskills/pull/254), with two differences: the `url` field contains a full MCP resource URI (any scheme the server serves), and the `digest` field is omitted (integrity is the transport's concern over an authenticated MCP connection). This binding also defines one additional `type` value, `"mcp-resource-template"`, for entries that describe a parameterized skill namespace. ```json { diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index ce36ebc3a..20342dcf3 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -100,7 +100,7 @@ On top of that baseline, two discovery mechanisms are defined. A server MAY supp #### Enumeration via `skill://index.json` -A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. The index format follows the [Agent Skills well-known URI discovery index](https://agentskills.io/well-known-uri#index-format), with two differences: the `url` field contains a full MCP resource URI (any scheme the server serves), and the `digest` field is omitted (integrity is the transport's concern over an authenticated MCP connection). This binding also defines one additional `type` value, `"mcp-resource-template"`, for entries that describe a parameterized skill namespace. +A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. The index format follows the [Agent Skills well-known URI discovery index](https://github.com/agentskills/agentskills/pull/254), with two differences: the `url` field contains a full MCP resource URI (any scheme the server serves), and the `digest` field is omitted (integrity is the transport's concern over an authenticated MCP connection). This binding also defines one additional `type` value, `"mcp-resource-template"`, for entries that describe a parameterized skill namespace. ```json { From 184b22a8c939c23a93efd3bd238fc52884fa83a4 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Thu, 28 May 2026 17:32:57 +0100 Subject: [PATCH 08/25] Clarify mcp-resource-template description audience and hydration path --- docs/seps/2640-skills-extension.mdx | 18 ++++++++++-------- seps/2640-skills-extension.md | 18 ++++++++++-------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index 0f49ca7e1..a22ee9afd 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -146,14 +146,14 @@ A server SHOULD expose a resource at the well-known URI `skill://index.json` who Index fields: -| Field | Required | Description | -| ---------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `$schema` | Yes | Schema version URI. Clients SHOULD match against known URIs before processing. | -| `skills` | Yes | Array of skill entries. | -| `skills[].type` | Yes | MUST be `"skill-md"`, `"archive"`, or `"mcp-resource-template"`. | -| `skills[].description` | Yes | For `"skill-md"` and `"archive"`, the skill's `description` matching its `SKILL.md` frontmatter. For `"mcp-resource-template"`, a description of the addressable skill space. | -| `skills[].url` | Yes | Full resource URI. See per-type semantics below. | -| `skills[].name` | Conditional | Required for `"skill-md"` and `"archive"`; matches the `SKILL.md` frontmatter `name` and the final segment of the skill path. Omitted for `"mcp-resource-template"`. | +| Field | Required | Description | +| ---------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `$schema` | Yes | Schema version URI. Clients SHOULD match against known URIs before processing. | +| `skills` | Yes | Array of skill entries. | +| `skills[].type` | Yes | MUST be `"skill-md"`, `"archive"`, or `"mcp-resource-template"`. | +| `skills[].description` | Yes | For `"skill-md"` and `"archive"`, the skill's `description` matching its `SKILL.md` frontmatter. For `"mcp-resource-template"`, a description of the addressable skill space (surfaced in the host's discovery UX, not necessarily the model's context). | +| `skills[].url` | Yes | Full resource URI. See per-type semantics below. | +| `skills[].name` | Conditional | Required for `"skill-md"` and `"archive"`; matches the `SKILL.md` frontmatter `name` and the final segment of the skill path. Omitted for `"mcp-resource-template"`. | Clients SHOULD ignore unrecognized fields and SHOULD skip entries with an unrecognized `type`. @@ -167,6 +167,8 @@ Hosts unpacking an archive MUST apply the [archive safety](https://agentskills.i **Template entries** (`type: "mcp-resource-template"`) describe a parameterized skill namespace without materializing every entry. The `url` is an [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570) URI template that resolves to `SKILL.md` resource URIs. A server SHOULD register the same `url` value as an MCP [resource template](https://modelcontextprotocol.io/specification/2025-11-25/server/resources#resource-templates) so hosts can wire template variables to the [completion API](https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/completion). Hosts SHOULD surface template entries in their UI as interactive discovery points: the user fills in variables via completion, selects a skill, and the host passes the resolved URI into the conversation. This scales to servers with unbounded skill catalogs. +Unlike `skill-md` and `archive` entries — whose `name` and `description` a host typically surfaces in the model's context alongside filesystem skills — a `mcp-resource-template` entry's `description` characterizes a skill _space_ rather than a single skill, and is intended for the host's discovery UX (e.g. a completion menu the user browses), not for direct insertion into the model's context. Hydration and invocation collapse to the baseline: once a template variable is resolved to a concrete `skill://…/SKILL.md` URI, that URI is read via `resources/read` and staged into context exactly like any other skill — there is no template-specific invocation path. A host MAY instead expose the template and its completions to the model directly, letting the model resolve and explore the namespace autonomously; this is optional and expected to be uncommon. + The `skill://index.json` resource is served via `resources/read` like any other resource, with `mimeType` of `application/json`. A server MAY also surface it in `resources/list` so clients can detect its presence, but clients MAY attempt to read it directly without prior discovery. A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY decline to expose `skill://index.json`, or MAY expose a partial index. Hosts MUST NOT treat an absent or empty index as proof that a server has no skills. diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index 20342dcf3..f60904833 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -135,14 +135,14 @@ A server SHOULD expose a resource at the well-known URI `skill://index.json` who Index fields: -| Field | Required | Description | -| ---------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `$schema` | Yes | Schema version URI. Clients SHOULD match against known URIs before processing. | -| `skills` | Yes | Array of skill entries. | -| `skills[].type` | Yes | MUST be `"skill-md"`, `"archive"`, or `"mcp-resource-template"`. | -| `skills[].description` | Yes | For `"skill-md"` and `"archive"`, the skill's `description` matching its `SKILL.md` frontmatter. For `"mcp-resource-template"`, a description of the addressable skill space. | -| `skills[].url` | Yes | Full resource URI. See per-type semantics below. | -| `skills[].name` | Conditional | Required for `"skill-md"` and `"archive"`; matches the `SKILL.md` frontmatter `name` and the final segment of the skill path. Omitted for `"mcp-resource-template"`. | +| Field | Required | Description | +| ---------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `$schema` | Yes | Schema version URI. Clients SHOULD match against known URIs before processing. | +| `skills` | Yes | Array of skill entries. | +| `skills[].type` | Yes | MUST be `"skill-md"`, `"archive"`, or `"mcp-resource-template"`. | +| `skills[].description` | Yes | For `"skill-md"` and `"archive"`, the skill's `description` matching its `SKILL.md` frontmatter. For `"mcp-resource-template"`, a description of the addressable skill space (surfaced in the host's discovery UX, not necessarily the model's context). | +| `skills[].url` | Yes | Full resource URI. See per-type semantics below. | +| `skills[].name` | Conditional | Required for `"skill-md"` and `"archive"`; matches the `SKILL.md` frontmatter `name` and the final segment of the skill path. Omitted for `"mcp-resource-template"`. | Clients SHOULD ignore unrecognized fields and SHOULD skip entries with an unrecognized `type`. @@ -156,6 +156,8 @@ Hosts unpacking an archive MUST apply the [archive safety](https://agentskills.i **Template entries** (`type: "mcp-resource-template"`) describe a parameterized skill namespace without materializing every entry. The `url` is an [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570) URI template that resolves to `SKILL.md` resource URIs. A server SHOULD register the same `url` value as an MCP [resource template](https://modelcontextprotocol.io/specification/2025-11-25/server/resources#resource-templates) so hosts can wire template variables to the [completion API](https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/completion). Hosts SHOULD surface template entries in their UI as interactive discovery points: the user fills in variables via completion, selects a skill, and the host passes the resolved URI into the conversation. This scales to servers with unbounded skill catalogs. +Unlike `skill-md` and `archive` entries — whose `name` and `description` a host typically surfaces in the model's context alongside filesystem skills — a `mcp-resource-template` entry's `description` characterizes a skill _space_ rather than a single skill, and is intended for the host's discovery UX (e.g. a completion menu the user browses), not for direct insertion into the model's context. Hydration and invocation collapse to the baseline: once a template variable is resolved to a concrete `skill://…/SKILL.md` URI, that URI is read via `resources/read` and staged into context exactly like any other skill — there is no template-specific invocation path. A host MAY instead expose the template and its completions to the model directly, letting the model resolve and explore the namespace autonomously; this is optional and expected to be uncommon. + The `skill://index.json` resource is served via `resources/read` like any other resource, with `mimeType` of `application/json`. A server MAY also surface it in `resources/list` so clients can detect its presence, but clients MAY attempt to read it directly without prior discovery. A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY decline to expose `skill://index.json`, or MAY expose a partial index. Hosts MUST NOT treat an absent or empty index as proof that a server has no skills. From baec7b8b18ecf1b23c13ac55aef995ccfcf17b2a Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Wed, 3 Jun 2026 14:28:43 +0100 Subject: [PATCH 09/25] Restore digest field in skill index per WG decision --- docs/seps/2640-skills-extension.mdx | 20 ++++++++++++++++---- seps/2640-skills-extension.md | 20 ++++++++++++++++---- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index a22ee9afd..ed4d16889 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -111,7 +111,7 @@ On top of that baseline, two discovery mechanisms are defined. A server MAY supp #### Enumeration via `skill://index.json` -A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. The index format follows the [Agent Skills well-known URI discovery index](https://github.com/agentskills/agentskills/pull/254), with two differences: the `url` field contains a full MCP resource URI (any scheme the server serves), and the `digest` field is omitted (integrity is the transport's concern over an authenticated MCP connection). This binding also defines one additional `type` value, `"mcp-resource-template"`, for entries that describe a parameterized skill namespace. +A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. The index format follows the [Agent Skills well-known URI discovery index](https://github.com/agentskills/agentskills/pull/254), with one difference: the `url` field contains a full MCP resource URI (any scheme the server serves). This binding also defines one additional `type` value, `"mcp-resource-template"`, for entries that describe a parameterized skill namespace. ```json { @@ -121,19 +121,22 @@ A server SHOULD expose a resource at the well-known URI `skill://index.json` who "name": "git-workflow", "type": "skill-md", "description": "Follow this team's Git conventions for branching and commits", - "url": "skill://git-workflow/SKILL.md" + "url": "skill://git-workflow/SKILL.md", + "digest": "sha256:a1b2c3d4..." }, { "name": "refunds", "type": "skill-md", "description": "Process customer refund requests per company policy", - "url": "skill://acme/billing/refunds/SKILL.md" + "url": "skill://acme/billing/refunds/SKILL.md", + "digest": "sha256:b2c3d4e5..." }, { "name": "pdf-processing", "type": "archive", "description": "Extract, fill, and assemble PDF documents", - "url": "skill://pdf-processing.tar.gz" + "url": "skill://pdf-processing.tar.gz", + "digest": "sha256:c4d5e6f7..." }, { "type": "mcp-resource-template", @@ -154,6 +157,7 @@ Index fields: | `skills[].description` | Yes | For `"skill-md"` and `"archive"`, the skill's `description` matching its `SKILL.md` frontmatter. For `"mcp-resource-template"`, a description of the addressable skill space (surfaced in the host's discovery UX, not necessarily the model's context). | | `skills[].url` | Yes | Full resource URI. See per-type semantics below. | | `skills[].name` | Conditional | Required for `"skill-md"` and `"archive"`; matches the `SKILL.md` frontmatter `name` and the final segment of the skill path. Omitted for `"mcp-resource-template"`. | +| `skills[].digest` | Conditional | Required for `"skill-md"` and `"archive"`: SHA-256 content digest of the artifact, formatted as `sha256:{hex}` (64 lowercase hex characters). See [Integrity and verification](#integrity-and-verification). Omitted for `"mcp-resource-template"`. | Clients SHOULD ignore unrecognized fields and SHOULD skip entries with an unrecognized `type`. @@ -169,6 +173,14 @@ Hosts unpacking an archive MUST apply the [archive safety](https://agentskills.i Unlike `skill-md` and `archive` entries — whose `name` and `description` a host typically surfaces in the model's context alongside filesystem skills — a `mcp-resource-template` entry's `description` characterizes a skill _space_ rather than a single skill, and is intended for the host's discovery UX (e.g. a completion menu the user browses), not for direct insertion into the model's context. Hydration and invocation collapse to the baseline: once a template variable is resolved to a concrete `skill://…/SKILL.md` URI, that URI is read via `resources/read` and staged into context exactly like any other skill — there is no template-specific invocation path. A host MAY instead expose the template and its completions to the model directly, letting the model resolve and explore the namespace autonomously; this is optional and expected to be uncommon. +##### Integrity and verification + +Per the Agent Skills [integrity and verification](https://github.com/agentskills/agentskills/pull/254) requirements, each `skill-md` and `archive` entry includes a `digest` field containing the SHA-256 hash of the artifact's raw bytes, formatted as `sha256:{hex}` where `{hex}` is 64 lowercase hexadecimal characters. For `"skill-md"`, the artifact is the `SKILL.md` file; for `"archive"`, the artifact is the archive file. + +Hosts MUST verify retrieved content against the `digest` in the index. A mismatch indicates the content is corrupted or tampered with — hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. + +Template entries carry no `digest`: the skills they resolve to are not enumerated in the index, so a skill read via a resolved template URI is retrieved over the authenticated MCP transport without index-level verification. + The `skill://index.json` resource is served via `resources/read` like any other resource, with `mimeType` of `application/json`. A server MAY also surface it in `resources/list` so clients can detect its presence, but clients MAY attempt to read it directly without prior discovery. A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY decline to expose `skill://index.json`, or MAY expose a partial index. Hosts MUST NOT treat an absent or empty index as proof that a server has no skills. diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index f60904833..88e99dd28 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -100,7 +100,7 @@ On top of that baseline, two discovery mechanisms are defined. A server MAY supp #### Enumeration via `skill://index.json` -A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. The index format follows the [Agent Skills well-known URI discovery index](https://github.com/agentskills/agentskills/pull/254), with two differences: the `url` field contains a full MCP resource URI (any scheme the server serves), and the `digest` field is omitted (integrity is the transport's concern over an authenticated MCP connection). This binding also defines one additional `type` value, `"mcp-resource-template"`, for entries that describe a parameterized skill namespace. +A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. The index format follows the [Agent Skills well-known URI discovery index](https://github.com/agentskills/agentskills/pull/254), with one difference: the `url` field contains a full MCP resource URI (any scheme the server serves). This binding also defines one additional `type` value, `"mcp-resource-template"`, for entries that describe a parameterized skill namespace. ```json { @@ -110,19 +110,22 @@ A server SHOULD expose a resource at the well-known URI `skill://index.json` who "name": "git-workflow", "type": "skill-md", "description": "Follow this team's Git conventions for branching and commits", - "url": "skill://git-workflow/SKILL.md" + "url": "skill://git-workflow/SKILL.md", + "digest": "sha256:a1b2c3d4..." }, { "name": "refunds", "type": "skill-md", "description": "Process customer refund requests per company policy", - "url": "skill://acme/billing/refunds/SKILL.md" + "url": "skill://acme/billing/refunds/SKILL.md", + "digest": "sha256:b2c3d4e5..." }, { "name": "pdf-processing", "type": "archive", "description": "Extract, fill, and assemble PDF documents", - "url": "skill://pdf-processing.tar.gz" + "url": "skill://pdf-processing.tar.gz", + "digest": "sha256:c4d5e6f7..." }, { "type": "mcp-resource-template", @@ -143,6 +146,7 @@ Index fields: | `skills[].description` | Yes | For `"skill-md"` and `"archive"`, the skill's `description` matching its `SKILL.md` frontmatter. For `"mcp-resource-template"`, a description of the addressable skill space (surfaced in the host's discovery UX, not necessarily the model's context). | | `skills[].url` | Yes | Full resource URI. See per-type semantics below. | | `skills[].name` | Conditional | Required for `"skill-md"` and `"archive"`; matches the `SKILL.md` frontmatter `name` and the final segment of the skill path. Omitted for `"mcp-resource-template"`. | +| `skills[].digest` | Conditional | Required for `"skill-md"` and `"archive"`: SHA-256 content digest of the artifact, formatted as `sha256:{hex}` (64 lowercase hex characters). See [Integrity and verification](#integrity-and-verification). Omitted for `"mcp-resource-template"`. | Clients SHOULD ignore unrecognized fields and SHOULD skip entries with an unrecognized `type`. @@ -158,6 +162,14 @@ Hosts unpacking an archive MUST apply the [archive safety](https://agentskills.i Unlike `skill-md` and `archive` entries — whose `name` and `description` a host typically surfaces in the model's context alongside filesystem skills — a `mcp-resource-template` entry's `description` characterizes a skill _space_ rather than a single skill, and is intended for the host's discovery UX (e.g. a completion menu the user browses), not for direct insertion into the model's context. Hydration and invocation collapse to the baseline: once a template variable is resolved to a concrete `skill://…/SKILL.md` URI, that URI is read via `resources/read` and staged into context exactly like any other skill — there is no template-specific invocation path. A host MAY instead expose the template and its completions to the model directly, letting the model resolve and explore the namespace autonomously; this is optional and expected to be uncommon. +##### Integrity and verification + +Per the Agent Skills [integrity and verification](https://github.com/agentskills/agentskills/pull/254) requirements, each `skill-md` and `archive` entry includes a `digest` field containing the SHA-256 hash of the artifact's raw bytes, formatted as `sha256:{hex}` where `{hex}` is 64 lowercase hexadecimal characters. For `"skill-md"`, the artifact is the `SKILL.md` file; for `"archive"`, the artifact is the archive file. + +Hosts MUST verify retrieved content against the `digest` in the index. A mismatch indicates the content is corrupted or tampered with — hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. + +Template entries carry no `digest`: the skills they resolve to are not enumerated in the index, so a skill read via a resolved template URI is retrieved over the authenticated MCP transport without index-level verification. + The `skill://index.json` resource is served via `resources/read` like any other resource, with `mimeType` of `application/json`. A server MAY also surface it in `resources/list` so clients can detect its presence, but clients MAY attempt to read it directly without prior discovery. A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY decline to expose `skill://index.json`, or MAY expose a partial index. Hosts MUST NOT treat an absent or empty index as proof that a server has no skills. From c4382ce7dc9d55fad103b27cafc91e565d11f757 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Thu, 4 Jun 2026 07:10:21 +0100 Subject: [PATCH 10/25] Remove mcp-resource-template entries from skill index --- docs/seps/2640-skills-extension.mdx | 39 +++++++++++------------------ seps/2640-skills-extension.md | 39 +++++++++++------------------ 2 files changed, 28 insertions(+), 50 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index ed4d16889..a1c141041 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -28,7 +28,7 @@ description: "Skills Extension" ## 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 well-known `skill://index.json` resource enumerates concrete skills and parameterized skill templates, but is not required — accommodating servers whose skill catalogs are large, generated, or otherwise unenumerable. 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. +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 well-known `skill://index.json` resource enumerates the skills a server serves, but is not required — accommodating servers whose skill catalogs are large, generated, or otherwise unenumerable. 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. Because the extension adds no new protocol methods or capabilities, 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. @@ -111,7 +111,7 @@ On top of that baseline, two discovery mechanisms are defined. A server MAY supp #### Enumeration via `skill://index.json` -A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. The index format follows the [Agent Skills well-known URI discovery index](https://github.com/agentskills/agentskills/pull/254), with one difference: the `url` field contains a full MCP resource URI (any scheme the server serves). This binding also defines one additional `type` value, `"mcp-resource-template"`, for entries that describe a parameterized skill namespace. +A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. The index format follows the [Agent Skills well-known URI discovery index](https://github.com/agentskills/agentskills/pull/254), with one difference: the `url` field contains a full MCP resource URI (any scheme the server serves). ```json { @@ -137,11 +137,6 @@ A server SHOULD expose a resource at the well-known URI `skill://index.json` who "description": "Extract, fill, and assemble PDF documents", "url": "skill://pdf-processing.tar.gz", "digest": "sha256:c4d5e6f7..." - }, - { - "type": "mcp-resource-template", - "description": "Per-product documentation skill", - "url": "skill://docs/{product}/SKILL.md" } ] } @@ -149,15 +144,15 @@ A server SHOULD expose a resource at the well-known URI `skill://index.json` who Index fields: -| Field | Required | Description | -| ---------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `$schema` | Yes | Schema version URI. Clients SHOULD match against known URIs before processing. | -| `skills` | Yes | Array of skill entries. | -| `skills[].type` | Yes | MUST be `"skill-md"`, `"archive"`, or `"mcp-resource-template"`. | -| `skills[].description` | Yes | For `"skill-md"` and `"archive"`, the skill's `description` matching its `SKILL.md` frontmatter. For `"mcp-resource-template"`, a description of the addressable skill space (surfaced in the host's discovery UX, not necessarily the model's context). | -| `skills[].url` | Yes | Full resource URI. See per-type semantics below. | -| `skills[].name` | Conditional | Required for `"skill-md"` and `"archive"`; matches the `SKILL.md` frontmatter `name` and the final segment of the skill path. Omitted for `"mcp-resource-template"`. | -| `skills[].digest` | Conditional | Required for `"skill-md"` and `"archive"`: SHA-256 content digest of the artifact, formatted as `sha256:{hex}` (64 lowercase hex characters). See [Integrity and verification](#integrity-and-verification). Omitted for `"mcp-resource-template"`. | +| Field | Required | Description | +| ---------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `$schema` | Yes | Schema version URI. Clients SHOULD match against known URIs before processing. | +| `skills` | Yes | Array of skill entries. | +| `skills[].type` | Yes | MUST be `"skill-md"` or `"archive"`. | +| `skills[].description` | Yes | The skill's `description` matching its `SKILL.md` frontmatter. | +| `skills[].url` | Yes | Full resource URI. See per-type semantics below. | +| `skills[].name` | Yes | Matches the `SKILL.md` frontmatter `name` and the final segment of the skill path. | +| `skills[].digest` | Yes | SHA-256 content digest of the artifact, formatted as `sha256:{hex}` (64 lowercase hex characters). See [Integrity and verification](#integrity-and-verification). | Clients SHOULD ignore unrecognized fields and SHOULD skip entries with an unrecognized `type`. @@ -169,18 +164,12 @@ The `` is the archive `url` with its archive suffix (`.tar.gz` or `. Hosts unpacking an archive MUST apply the [archive safety](https://agentskills.io/well-known-uri#archive-safety) requirements of the Agent Skills specification: reject archives containing path-traversal sequences or absolute paths, reject symlinks or hard links that resolve outside the skill directory, and enforce a limit on total unpacked size. -**Template entries** (`type: "mcp-resource-template"`) describe a parameterized skill namespace without materializing every entry. The `url` is an [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570) URI template that resolves to `SKILL.md` resource URIs. A server SHOULD register the same `url` value as an MCP [resource template](https://modelcontextprotocol.io/specification/2025-11-25/server/resources#resource-templates) so hosts can wire template variables to the [completion API](https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/completion). Hosts SHOULD surface template entries in their UI as interactive discovery points: the user fills in variables via completion, selects a skill, and the host passes the resolved URI into the conversation. This scales to servers with unbounded skill catalogs. - -Unlike `skill-md` and `archive` entries — whose `name` and `description` a host typically surfaces in the model's context alongside filesystem skills — a `mcp-resource-template` entry's `description` characterizes a skill _space_ rather than a single skill, and is intended for the host's discovery UX (e.g. a completion menu the user browses), not for direct insertion into the model's context. Hydration and invocation collapse to the baseline: once a template variable is resolved to a concrete `skill://…/SKILL.md` URI, that URI is read via `resources/read` and staged into context exactly like any other skill — there is no template-specific invocation path. A host MAY instead expose the template and its completions to the model directly, letting the model resolve and explore the namespace autonomously; this is optional and expected to be uncommon. - ##### Integrity and verification -Per the Agent Skills [integrity and verification](https://github.com/agentskills/agentskills/pull/254) requirements, each `skill-md` and `archive` entry includes a `digest` field containing the SHA-256 hash of the artifact's raw bytes, formatted as `sha256:{hex}` where `{hex}` is 64 lowercase hexadecimal characters. For `"skill-md"`, the artifact is the `SKILL.md` file; for `"archive"`, the artifact is the archive file. +Per the Agent Skills [integrity and verification](https://github.com/agentskills/agentskills/pull/254) requirements, each skill entry includes a `digest` field containing the SHA-256 hash of the artifact's raw bytes, formatted as `sha256:{hex}` where `{hex}` is 64 lowercase hexadecimal characters. For `"skill-md"`, the artifact is the `SKILL.md` file; for `"archive"`, the artifact is the archive file. Hosts MUST verify retrieved content against the `digest` in the index. A mismatch indicates the content is corrupted or tampered with — hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. -Template entries carry no `digest`: the skills they resolve to are not enumerated in the index, so a skill read via a resolved template URI is retrieved over the authenticated MCP transport without index-level verification. - The `skill://index.json` resource is served via `resources/read` like any other resource, with `mimeType` of `application/json`. A server MAY also surface it in `resources/list` so clients can detect its presence, but clients MAY attempt to read it directly without prior discovery. A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY decline to expose `skill://index.json`, or MAY expose a partial index. Hosts MUST NOT treat an absent or empty index as proof that a server has no skills. @@ -325,9 +314,9 @@ Constraining the final segment to match the frontmatter `name` gets both propert ### Why Is Enumeration Optional? -Requiring every server to expose a complete `skill://index.json` fails for at least three server shapes: a documentation server that synthesizes a skill per API endpoint (thousands), a skill gateway fronting an external index (unbounded), and a server that generates skills from templates parameterized at read time (unenumerable by construction). For these, the list is either too large to be useful in the model's context or does not meaningfully exist. +Requiring every server to expose a complete `skill://index.json` fails for at least three server shapes: a documentation server that synthesizes a skill per API endpoint (thousands), a skill gateway fronting an external index (unbounded), and a server that generates skills dynamically at read time (unenumerable by construction). For these, the list is either too large to be useful in the model's context or does not meaningfully exist. -The baseline is therefore direct readability — a skill URI is always a valid argument to `resources/read`. The index (concrete entries and templates) is layered on top for servers where it makes sense. A host that assumes enumeration is exhaustive will miss skills on servers where it is not, hence the requirement that hosts MUST NOT treat empty enumeration as proof of absence. +The baseline is therefore direct readability — a skill URI is always a valid argument to `resources/read`. The index is layered on top for servers where it makes sense. A host that assumes enumeration is exhaustive will miss skills on servers where it is not, hence the requirement that hosts MUST NOT treat empty enumeration as proof of absence. ### Why Delegate the Format to agentskills.io? diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index 88e99dd28..3e0e6507c 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -17,7 +17,7 @@ Design history, experimental findings, and reference implementations are maintai ## 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 well-known `skill://index.json` resource enumerates concrete skills and parameterized skill templates, but is not required — accommodating servers whose skill catalogs are large, generated, or otherwise unenumerable. 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. +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 well-known `skill://index.json` resource enumerates the skills a server serves, but is not required — accommodating servers whose skill catalogs are large, generated, or otherwise unenumerable. 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. Because the extension adds no new protocol methods or capabilities, 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. @@ -100,7 +100,7 @@ On top of that baseline, two discovery mechanisms are defined. A server MAY supp #### Enumeration via `skill://index.json` -A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. The index format follows the [Agent Skills well-known URI discovery index](https://github.com/agentskills/agentskills/pull/254), with one difference: the `url` field contains a full MCP resource URI (any scheme the server serves). This binding also defines one additional `type` value, `"mcp-resource-template"`, for entries that describe a parameterized skill namespace. +A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. The index format follows the [Agent Skills well-known URI discovery index](https://github.com/agentskills/agentskills/pull/254), with one difference: the `url` field contains a full MCP resource URI (any scheme the server serves). ```json { @@ -126,11 +126,6 @@ A server SHOULD expose a resource at the well-known URI `skill://index.json` who "description": "Extract, fill, and assemble PDF documents", "url": "skill://pdf-processing.tar.gz", "digest": "sha256:c4d5e6f7..." - }, - { - "type": "mcp-resource-template", - "description": "Per-product documentation skill", - "url": "skill://docs/{product}/SKILL.md" } ] } @@ -138,15 +133,15 @@ A server SHOULD expose a resource at the well-known URI `skill://index.json` who Index fields: -| Field | Required | Description | -| ---------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `$schema` | Yes | Schema version URI. Clients SHOULD match against known URIs before processing. | -| `skills` | Yes | Array of skill entries. | -| `skills[].type` | Yes | MUST be `"skill-md"`, `"archive"`, or `"mcp-resource-template"`. | -| `skills[].description` | Yes | For `"skill-md"` and `"archive"`, the skill's `description` matching its `SKILL.md` frontmatter. For `"mcp-resource-template"`, a description of the addressable skill space (surfaced in the host's discovery UX, not necessarily the model's context). | -| `skills[].url` | Yes | Full resource URI. See per-type semantics below. | -| `skills[].name` | Conditional | Required for `"skill-md"` and `"archive"`; matches the `SKILL.md` frontmatter `name` and the final segment of the skill path. Omitted for `"mcp-resource-template"`. | -| `skills[].digest` | Conditional | Required for `"skill-md"` and `"archive"`: SHA-256 content digest of the artifact, formatted as `sha256:{hex}` (64 lowercase hex characters). See [Integrity and verification](#integrity-and-verification). Omitted for `"mcp-resource-template"`. | +| Field | Required | Description | +| ---------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `$schema` | Yes | Schema version URI. Clients SHOULD match against known URIs before processing. | +| `skills` | Yes | Array of skill entries. | +| `skills[].type` | Yes | MUST be `"skill-md"` or `"archive"`. | +| `skills[].description` | Yes | The skill's `description` matching its `SKILL.md` frontmatter. | +| `skills[].url` | Yes | Full resource URI. See per-type semantics below. | +| `skills[].name` | Yes | Matches the `SKILL.md` frontmatter `name` and the final segment of the skill path. | +| `skills[].digest` | Yes | SHA-256 content digest of the artifact, formatted as `sha256:{hex}` (64 lowercase hex characters). See [Integrity and verification](#integrity-and-verification). | Clients SHOULD ignore unrecognized fields and SHOULD skip entries with an unrecognized `type`. @@ -158,18 +153,12 @@ The `` is the archive `url` with its archive suffix (`.tar.gz` or `. Hosts unpacking an archive MUST apply the [archive safety](https://agentskills.io/well-known-uri#archive-safety) requirements of the Agent Skills specification: reject archives containing path-traversal sequences or absolute paths, reject symlinks or hard links that resolve outside the skill directory, and enforce a limit on total unpacked size. -**Template entries** (`type: "mcp-resource-template"`) describe a parameterized skill namespace without materializing every entry. The `url` is an [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570) URI template that resolves to `SKILL.md` resource URIs. A server SHOULD register the same `url` value as an MCP [resource template](https://modelcontextprotocol.io/specification/2025-11-25/server/resources#resource-templates) so hosts can wire template variables to the [completion API](https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/completion). Hosts SHOULD surface template entries in their UI as interactive discovery points: the user fills in variables via completion, selects a skill, and the host passes the resolved URI into the conversation. This scales to servers with unbounded skill catalogs. - -Unlike `skill-md` and `archive` entries — whose `name` and `description` a host typically surfaces in the model's context alongside filesystem skills — a `mcp-resource-template` entry's `description` characterizes a skill _space_ rather than a single skill, and is intended for the host's discovery UX (e.g. a completion menu the user browses), not for direct insertion into the model's context. Hydration and invocation collapse to the baseline: once a template variable is resolved to a concrete `skill://…/SKILL.md` URI, that URI is read via `resources/read` and staged into context exactly like any other skill — there is no template-specific invocation path. A host MAY instead expose the template and its completions to the model directly, letting the model resolve and explore the namespace autonomously; this is optional and expected to be uncommon. - ##### Integrity and verification -Per the Agent Skills [integrity and verification](https://github.com/agentskills/agentskills/pull/254) requirements, each `skill-md` and `archive` entry includes a `digest` field containing the SHA-256 hash of the artifact's raw bytes, formatted as `sha256:{hex}` where `{hex}` is 64 lowercase hexadecimal characters. For `"skill-md"`, the artifact is the `SKILL.md` file; for `"archive"`, the artifact is the archive file. +Per the Agent Skills [integrity and verification](https://github.com/agentskills/agentskills/pull/254) requirements, each skill entry includes a `digest` field containing the SHA-256 hash of the artifact's raw bytes, formatted as `sha256:{hex}` where `{hex}` is 64 lowercase hexadecimal characters. For `"skill-md"`, the artifact is the `SKILL.md` file; for `"archive"`, the artifact is the archive file. Hosts MUST verify retrieved content against the `digest` in the index. A mismatch indicates the content is corrupted or tampered with — hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. -Template entries carry no `digest`: the skills they resolve to are not enumerated in the index, so a skill read via a resolved template URI is retrieved over the authenticated MCP transport without index-level verification. - The `skill://index.json` resource is served via `resources/read` like any other resource, with `mimeType` of `application/json`. A server MAY also surface it in `resources/list` so clients can detect its presence, but clients MAY attempt to read it directly without prior discovery. A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY decline to expose `skill://index.json`, or MAY expose a partial index. Hosts MUST NOT treat an absent or empty index as proof that a server has no skills. @@ -314,9 +303,9 @@ Constraining the final segment to match the frontmatter `name` gets both propert ### Why Is Enumeration Optional? -Requiring every server to expose a complete `skill://index.json` fails for at least three server shapes: a documentation server that synthesizes a skill per API endpoint (thousands), a skill gateway fronting an external index (unbounded), and a server that generates skills from templates parameterized at read time (unenumerable by construction). For these, the list is either too large to be useful in the model's context or does not meaningfully exist. +Requiring every server to expose a complete `skill://index.json` fails for at least three server shapes: a documentation server that synthesizes a skill per API endpoint (thousands), a skill gateway fronting an external index (unbounded), and a server that generates skills dynamically at read time (unenumerable by construction). For these, the list is either too large to be useful in the model's context or does not meaningfully exist. -The baseline is therefore direct readability — a skill URI is always a valid argument to `resources/read`. The index (concrete entries and templates) is layered on top for servers where it makes sense. A host that assumes enumeration is exhaustive will miss skills on servers where it is not, hence the requirement that hosts MUST NOT treat empty enumeration as proof of absence. +The baseline is therefore direct readability — a skill URI is always a valid argument to `resources/read`. The index is layered on top for servers where it makes sense. A host that assumes enumeration is exhaustive will miss skills on servers where it is not, hence the requirement that hosts MUST NOT treat empty enumeration as proof of absence. ### Why Delegate the Format to agentskills.io? From 7a0f0746c8c8105f778d2ec151c4aaa749c47e49 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Thu, 4 Jun 2026 07:16:17 +0100 Subject: [PATCH 11/25] Remove remaining resource template mentions from SDK and rationale sections --- docs/seps/2640-skills-extension.mdx | 18 +++++++++--------- seps/2640-skills-extension.md | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index a1c141041..7e8a12048 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -85,13 +85,13 @@ A server MAY instead serve skills under another scheme native to its domain (e.g #### 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` | `templates/email.md` | `skill://acme/billing/refunds/templates/email.md` | +| 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 @@ -274,7 +274,7 @@ def refunds(): return Path("./skills/refunds") ``` -The SDK handles: reading `SKILL.md` frontmatter to populate resource metadata, registering a `skill:///{+path}` resource template, serving file content on `resources/read`, and (where the server's skill set is bounded) generating the `skill://index.json` resource. +The SDK handles: reading `SKILL.md` frontmatter to populate resource metadata, serving file content on `resources/read`, and (where the server's skill set is bounded) generating the `skill://index.json` resource. **Client-side** — enumerate and fetch skills: @@ -290,7 +290,7 @@ These wrappers are thin — each is a single underlying protocol call with a fix ### Why Resources Instead of a New Primitive? -The Working Group's [decision log](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-02-26-prioritize-skills-as-resources-with-client-helper-tools) records this as settled. Skills are files; Resources exist to expose files. Reusing Resources inherits URI addressability, `resources/read`, `resources/subscribe`, templates, and the existing client tooling for free. A new primitive would duplicate most of this and add ecosystem complexity. Using resources to describe files is also aligned with [Composability over specificity](https://modelcontextprotocol.io/community/design-principles#composability-over-specificity) and other MCP [design principles](https://modelcontextprotocol.io/community/design-principles). +The Working Group's [decision log](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-02-26-prioritize-skills-as-resources-with-client-helper-tools) records this as settled. Skills are files; Resources exist to expose files. Reusing Resources inherits URI addressability, `resources/read`, `resources/subscribe`, and the existing client tooling for free. A new primitive would duplicate most of this and add ecosystem complexity. Using resources to describe files is also aligned with [Composability over specificity](https://modelcontextprotocol.io/community/design-principles#composability-over-specificity) and other MCP [design principles](https://modelcontextprotocol.io/community/design-principles). [SEP-2076] originally proposed the new-primitive alternative. That approach offers cleaner capability negotiation and dedicated list-changed notifications, but at the cost of flattening skills to name-addressed blobs — losing the directory model that the Agent Skills specification defines and that supporting files depend on. diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index 3e0e6507c..05e0159cf 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -74,13 +74,13 @@ A server MAY instead serve skills under another scheme native to its domain (e.g #### 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` | `templates/email.md` | `skill://acme/billing/refunds/templates/email.md` | +| 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 @@ -263,7 +263,7 @@ def refunds(): return Path("./skills/refunds") ``` -The SDK handles: reading `SKILL.md` frontmatter to populate resource metadata, registering a `skill:///{+path}` resource template, serving file content on `resources/read`, and (where the server's skill set is bounded) generating the `skill://index.json` resource. +The SDK handles: reading `SKILL.md` frontmatter to populate resource metadata, serving file content on `resources/read`, and (where the server's skill set is bounded) generating the `skill://index.json` resource. **Client-side** — enumerate and fetch skills: @@ -279,7 +279,7 @@ These wrappers are thin — each is a single underlying protocol call with a fix ### Why Resources Instead of a New Primitive? -The Working Group's [decision log](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-02-26-prioritize-skills-as-resources-with-client-helper-tools) records this as settled. Skills are files; Resources exist to expose files. Reusing Resources inherits URI addressability, `resources/read`, `resources/subscribe`, templates, and the existing client tooling for free. A new primitive would duplicate most of this and add ecosystem complexity. Using resources to describe files is also aligned with [Composability over specificity](https://modelcontextprotocol.io/community/design-principles#composability-over-specificity) and other MCP [design principles](https://modelcontextprotocol.io/community/design-principles). +The Working Group's [decision log](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-02-26-prioritize-skills-as-resources-with-client-helper-tools) records this as settled. Skills are files; Resources exist to expose files. Reusing Resources inherits URI addressability, `resources/read`, `resources/subscribe`, and the existing client tooling for free. A new primitive would duplicate most of this and add ecosystem complexity. Using resources to describe files is also aligned with [Composability over specificity](https://modelcontextprotocol.io/community/design-principles#composability-over-specificity) and other MCP [design principles](https://modelcontextprotocol.io/community/design-principles). [SEP-2076] originally proposed the new-primitive alternative. That approach offers cleaner capability negotiation and dedicated list-changed notifications, but at the cost of flattening skills to name-addressed blobs — losing the directory model that the Agent Skills specification defines and that supporting files depend on. From 360123d0d43949d1bf5e1211a46f9c834ba26ae2 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Mon, 8 Jun 2026 11:15:51 +0100 Subject: [PATCH 12/25] Replace index format with MCP-specific schema carrying verbatim frontmatter The skill://index.json format is now defined by this extension rather than borrowing an external discovery format. Entries carry the skill's SKILL.md frontmatter verbatim, an optional direct url+digest pair, and optional archive alternatives, each with url, mimeType, and digest. Archive layout and unpacking safety requirements are stated inline. --- docs/seps/2640-skills-extension.mdx | 118 +++++++++++++++++++--------- seps/2640-skills-extension.md | 118 +++++++++++++++++++--------- 2 files changed, 166 insertions(+), 70 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index 7e8a12048..c1be71c3d 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -111,32 +111,54 @@ On top of that baseline, two discovery mechanisms are defined. A server MAY supp #### Enumeration via `skill://index.json` -A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. The index format follows the [Agent Skills well-known URI discovery index](https://github.com/agentskills/agentskills/pull/254), with one difference: the `url` field contains a full MCP resource URI (any scheme the server serves). +A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. ```json { - "$schema": "https://schemas.agentskills.io/discovery/0.2.0/schema.json", + "$schema": "https://static.modelcontextprotocol.io/schemas/2026-06-08/skill-index.schema.json", "skills": [ { - "name": "git-workflow", - "type": "skill-md", - "description": "Follow this team's Git conventions for branching and commits", "url": "skill://git-workflow/SKILL.md", - "digest": "sha256:a1b2c3d4..." + "digest": "sha256:a1b2c3d4...", + "frontmatter": { + "name": "git-workflow", + "description": "Follow this team's Git conventions for branching and commits" + } }, { - "name": "refunds", - "type": "skill-md", - "description": "Process customer refund requests per company policy", "url": "skill://acme/billing/refunds/SKILL.md", - "digest": "sha256:b2c3d4e5..." + "digest": "sha256:b2c3d4e5...", + "frontmatter": { + "name": "refunds", + "description": "Process customer refund requests per company policy", + "license": "Apache-2.0" + }, + "archives": [ + { + "url": "skill://acme/billing/refunds.tar.gz", + "mimeType": "application/gzip", + "digest": "sha256:c4d5e6f7..." + } + ] }, { - "name": "pdf-processing", - "type": "archive", - "description": "Extract, fill, and assemble PDF documents", - "url": "skill://pdf-processing.tar.gz", - "digest": "sha256:c4d5e6f7..." + "frontmatter": { + "name": "pdf-processing", + "description": "Extract, fill, and assemble PDF documents", + "metadata": { "version": "2.1.0" } + }, + "archives": [ + { + "url": "skill://pdf-processing.tar.gz", + "mimeType": "application/gzip", + "digest": "sha256:d5e6f7a8..." + }, + { + "url": "skill://pdf-processing.zip", + "mimeType": "application/zip", + "digest": "sha256:e6f7a8b9..." + } + ] } ] } @@ -144,29 +166,52 @@ A server SHOULD expose a resource at the well-known URI `skill://index.json` who Index fields: -| Field | Required | Description | -| ---------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `$schema` | Yes | Schema version URI. Clients SHOULD match against known URIs before processing. | -| `skills` | Yes | Array of skill entries. | -| `skills[].type` | Yes | MUST be `"skill-md"` or `"archive"`. | -| `skills[].description` | Yes | The skill's `description` matching its `SKILL.md` frontmatter. | -| `skills[].url` | Yes | Full resource URI. See per-type semantics below. | -| `skills[].name` | Yes | Matches the `SKILL.md` frontmatter `name` and the final segment of the skill path. | -| `skills[].digest` | Yes | SHA-256 content digest of the artifact, formatted as `sha256:{hex}` (64 lowercase hex characters). See [Integrity and verification](#integrity-and-verification). | +| Field | Required | Description | +| ------------------------------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------- | +| `$schema` | Yes | Schema version URI. Clients SHOULD match against known URIs before processing. | +| `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[].url` | No¹ | Resource URI of the skill's `SKILL.md`. See [Direct entries](#direct-entries). | +| `skills[].digest` | With `url` | SHA-256 digest of the `SKILL.md` file. MUST be present when `url` is present, absent otherwise. See [Integrity](#integrity-and-verification). | +| `skills[].archives` | No¹ | Array of archive forms of the skill. See [Archives](#archives). | +| `skills[].archives[].url` | Yes | Resource URI of the archive. | +| `skills[].archives[].mimeType` | Yes | The archive format's media type (e.g., `application/gzip`, `application/zip`). | +| `skills[].archives[].digest` | Yes | SHA-256 digest of the archive file. See [Integrity](#integrity-and-verification). | + +¹ Every entry MUST include `url`, a non-empty `archives`, or both. + +Clients SHOULD ignore unrecognized fields. + +##### 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 a single read of the index, without fetching each `SKILL.md`. + +The `frontmatter` object MUST be identical in content to the frontmatter of the `SKILL.md` it describes. When `url` is present, the final `` segment of the URI MUST equal `frontmatter.name`, per [Resource Mapping](#resource-mapping). -Clients SHOULD ignore unrecognized fields and SHOULD skip entries with an unrecognized `type`. +##### Direct entries -**Skill-md entries** (`type: "skill-md"`) point `url` at the full resource URI of the skill's `SKILL.md`. Supporting files are individually addressable as sibling resources under the same skill path. +When present, `url` 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). -**Archive entries** (`type: "archive"`) point `url` at a single resource whose content is a packed skill directory. Per the [Agent Skills archive distribution format](https://agentskills.io/well-known-uri#archive-distribution), the archive MUST be `.tar.gz` (gzip-compressed tar, `mimeType` `application/gzip`) or `.zip` (`mimeType` `application/zip`); hosts MUST support both and SHOULD determine the format from the resource's `mimeType`, falling back to the URL suffix. Archive contents represent the skill directory directly — `SKILL.md` MUST be at the archive root, not nested inside a wrapper directory, and the archive MUST NOT contain path-traversal sequences (`..`) or absolute paths. +##### Archives -The `` is the archive `url` with its archive suffix (`.tar.gz` or `.zip`) stripped — `skill://pdf-processing.tar.gz` unpacks to `skill://pdf-processing/`, `skill://acme/billing/refunds.zip` to `skill://acme/billing/refunds/` — and the archive contents populate `skill:///` exactly as if each file were served individually. A server chooses per-skill whether to distribute as individual files or as an archive; hosts observe an identical virtual namespace either way. Archive distribution delivers a multi-file skill atomically in one round trip and can carry UNIX file metadata (executable bits, symlinks) that individual-resource distribution cannot represent — robustly for `.tar.gz`, variably for `.zip`. +`archives` lists pre-packed forms of the entire skill directory. Each archive is a single resource whose content is the complete skill directory in an archive format; reading it via `resources/read` retrieves the skill — `SKILL.md` and all supporting files — in one round trip. When multiple archives are listed, they are alternative encodings of identical content; a host picks one whose format it supports, determined from `mimeType`. -Hosts unpacking an archive MUST apply the [archive safety](https://agentskills.io/well-known-uri#archive-safety) requirements of the Agent Skills specification: reject archives containing path-traversal sequences or absolute paths, reject symlinks or hard links that resolve outside the skill directory, and enforce a limit on total unpacked size. +`mimeType` MUST identify an archive format. Hosts SHOULD support at least gzip-compressed tar (`application/gzip`, conventionally `.tar.gz`) and ZIP (`application/zip`); servers MAY offer additional formats but SHOULD include at least one of these two so that any conforming host can retrieve the skill. + +Archive layout: + +- `SKILL.md` MUST be at the archive root, not nested inside a wrapper directory. +- Entry paths MUST use `/` as the separator and MUST be relative — no path-traversal sequences (`..`) and no absolute paths. + +Archive contents represent the skill directory directly. When an entry carries both `url` and `archives`, each archive MUST unpack to the same files that are individually addressable under the skill path — a host may fetch the skill either way and observes the same content. When an entry is archive-only, the skill's files are not individually addressable on the server; the host unpacks the archive locally and addresses its contents under `skill:///`, where `` is `frontmatter.name`. In both cases the archive `url` itself carries no path semantics — it is a download location, nothing more. + +Compared to individual-file distribution, an archive delivers a multi-file skill atomically and can carry UNIX file metadata (executable bits, symlinks) that individual resources cannot represent — robustly for `.tar.gz`, variably for `.zip`. + +Archives are an unpacking attack surface. Hosts unpacking an archive MUST reject archives containing path-traversal sequences or absolute paths, reject symlinks or hard links that resolve outside the skill directory, and enforce a limit on total unpacked size to prevent decompression bombs. ##### Integrity and verification -Per the Agent Skills [integrity and verification](https://github.com/agentskills/agentskills/pull/254) requirements, each skill entry includes a `digest` field containing the SHA-256 hash of the artifact's raw bytes, formatted as `sha256:{hex}` where `{hex}` is 64 lowercase hexadecimal characters. For `"skill-md"`, the artifact is the `SKILL.md` file; for `"archive"`, the artifact is the archive file. +Digests are SHA-256 hashes of an artifact's raw bytes, formatted as `sha256:{hex}` where `{hex}` is 64 lowercase hexadecimal characters. For a skill entry's `digest`, the artifact is the `SKILL.md` file; for an archive's `digest`, the artifact is the archive file. Hosts MUST verify retrieved content against the `digest` in the index. A mismatch indicates the content is corrupted or tampered with — hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. @@ -210,7 +255,7 @@ The following are recommendations for interoperable implementations. They are no 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, from the model's perspective, an MCP-served skill is indistinguishable from a filesystem skill. -**Registry.** At startup and on connection change, the host assembles a single internal skill registry from every origin it supports: filesystem skill directories, and `skill://index.json` from each connected MCP server that declares the `io.modelcontextprotocol/skills` extension. Each registry entry records the skill's `name`, `description`, and origin — for a filesystem skill, the local directory; for an MCP skill, the server identity and the `SKILL.md` resource URI. Origin is host bookkeeping; it is not exposed to the model. +**Registry.** At startup and on connection change, the host assembles a single internal skill registry from every origin it supports: filesystem skill directories, and `skill://index.json` from each connected MCP server that declares the `io.modelcontextprotocol/skills` extension. Each registry entry records the skill's `name` and `description` (from the index 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. Origin is host bookkeeping; it is not exposed to the model. **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. @@ -324,9 +369,13 @@ The Agent Skills specification already defines YAML frontmatter fields, naming r ### Why Permit Archive Distribution? -An earlier draft excluded `type: "archive"` on the basis that supporting files are already individually addressable as resources. The Working Group [reversed this](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-04-19-archives-permitted-as-server-side-packaging-optimization) for four reasons: dropping a distribution type that the Agent Skills discovery format defines contradicts this SEP's framing as a pure transport binding; individual-file distribution cannot guarantee atomicity across a multi-file skill read; hosts that pre-materialize skills to a local filesystem pay N+1 round trips; and UNIX file metadata (executable bits, symlinks) has no representation when files are served individually. +An earlier draft excluded archives on the basis that supporting files are already individually addressable as resources. The Working Group [reversed this](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-04-19-archives-permitted-as-server-side-packaging-optimization) for three reasons: individual-file distribution cannot guarantee atomicity across a multi-file skill read; hosts that pre-materialize skills to a local filesystem pay N+1 round trips; and UNIX file metadata (executable bits, symlinks) has no representation when files are served individually. + +Attaching archives to a skill entry as alternative retrieval forms — rather than as a separate kind of skill — preserves the virtual-filesystem model: the post-unpack view is identical to individual-file distribution. The property given up is per-file `resources/subscribe` granularity, which is acceptable because subscription is not part of the skill reading model defined here. + +### Why Verbatim Frontmatter in the Index? -Reinstating archives as a server-side packaging choice — not a client-visible mode split — preserves the virtual-filesystem model: the post-unpack view is identical to individual-file distribution. The property given up is per-file `resources/subscribe` granularity, which is acceptable because subscription is not part of the skill reading model defined here. +The index could instead carry a curated subset of skill metadata — `name` and `description` as dedicated top-level fields. That forces a choice every time the Agent Skills frontmatter grows a field: amend this SEP to mirror it, or leave index consumers to fetch and parse every `SKILL.md` for it. Copying the frontmatter verbatim removes the choice. The index carries exactly what the skill author wrote; the fields hosts need for a registry (`name`, `description`) are guaranteed present because the Agent Skills specification requires them; and new frontmatter fields flow through with no change to this extension. A host builds its complete skill registry from a single `resources/read` of the index. ## Backward Compatibility @@ -343,7 +392,7 @@ Skill content is instructional text delivered to a model, which makes it a promp - **No implicit local execution.** Hosts MUST NOT honor mechanisms in skill content that would cause local code execution without explicit user opt-in. This includes, non-exhaustively: hook declarations, pre/post-invocation scripts, shell commands embedded in frontmatter, or any field that a filesystem-sourced skill might use to register executable behavior on the host. Hosts MUST either ignore such fields entirely when the skill arrives over MCP, or gate them behind an explicit per-skill user approval that states what will execute and where. Silently executing server-provided code because it appeared in a skill directory is a remote code execution vector. - **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. - **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. -- **Archive safety.** Archive-distributed skills are an unpacking attack surface. Hosts MUST validate archives per the [Agent Skills archive safety requirements](https://agentskills.io/well-known-uri#archive-safety): reject path traversal and absolute paths, reject links resolving outside the skill directory, and bound total unpacked size to prevent decompression bombs. +- **Archive safety.** Archive-distributed skills are an unpacking attack surface. Hosts MUST validate archives per the requirements in [Archives](#archives): reject path traversal and absolute paths, reject links resolving outside the skill directory, and bound total unpacked size to prevent decompression bombs. ## Reference Implementation @@ -368,7 +417,6 @@ Per [SEP-2133], an Extensions Track SEP requires at least one reference implemen ## References - [Agent Skills specification](https://agentskills.io/specification) -- [Agent Skills well-known URI discovery](https://agentskills.io/well-known-uri) - [SEP-2133]: Extensions - [SEP-2076]: Agent Skills as first-class primitive (alternative approach) - [Skills Over MCP Working Group charter](https://modelcontextprotocol.io/community/skills-over-mcp/charter) diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index 05e0159cf..52411c6d3 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -100,32 +100,54 @@ On top of that baseline, two discovery mechanisms are defined. A server MAY supp #### Enumeration via `skill://index.json` -A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. The index format follows the [Agent Skills well-known URI discovery index](https://github.com/agentskills/agentskills/pull/254), with one difference: the `url` field contains a full MCP resource URI (any scheme the server serves). +A server SHOULD expose a resource at the well-known URI `skill://index.json` whose content is a JSON index of the skills it serves. ```json { - "$schema": "https://schemas.agentskills.io/discovery/0.2.0/schema.json", + "$schema": "https://static.modelcontextprotocol.io/schemas/2026-06-08/skill-index.schema.json", "skills": [ { - "name": "git-workflow", - "type": "skill-md", - "description": "Follow this team's Git conventions for branching and commits", "url": "skill://git-workflow/SKILL.md", - "digest": "sha256:a1b2c3d4..." + "digest": "sha256:a1b2c3d4...", + "frontmatter": { + "name": "git-workflow", + "description": "Follow this team's Git conventions for branching and commits" + } }, { - "name": "refunds", - "type": "skill-md", - "description": "Process customer refund requests per company policy", "url": "skill://acme/billing/refunds/SKILL.md", - "digest": "sha256:b2c3d4e5..." + "digest": "sha256:b2c3d4e5...", + "frontmatter": { + "name": "refunds", + "description": "Process customer refund requests per company policy", + "license": "Apache-2.0" + }, + "archives": [ + { + "url": "skill://acme/billing/refunds.tar.gz", + "mimeType": "application/gzip", + "digest": "sha256:c4d5e6f7..." + } + ] }, { - "name": "pdf-processing", - "type": "archive", - "description": "Extract, fill, and assemble PDF documents", - "url": "skill://pdf-processing.tar.gz", - "digest": "sha256:c4d5e6f7..." + "frontmatter": { + "name": "pdf-processing", + "description": "Extract, fill, and assemble PDF documents", + "metadata": { "version": "2.1.0" } + }, + "archives": [ + { + "url": "skill://pdf-processing.tar.gz", + "mimeType": "application/gzip", + "digest": "sha256:d5e6f7a8..." + }, + { + "url": "skill://pdf-processing.zip", + "mimeType": "application/zip", + "digest": "sha256:e6f7a8b9..." + } + ] } ] } @@ -133,29 +155,52 @@ A server SHOULD expose a resource at the well-known URI `skill://index.json` who Index fields: -| Field | Required | Description | -| ---------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `$schema` | Yes | Schema version URI. Clients SHOULD match against known URIs before processing. | -| `skills` | Yes | Array of skill entries. | -| `skills[].type` | Yes | MUST be `"skill-md"` or `"archive"`. | -| `skills[].description` | Yes | The skill's `description` matching its `SKILL.md` frontmatter. | -| `skills[].url` | Yes | Full resource URI. See per-type semantics below. | -| `skills[].name` | Yes | Matches the `SKILL.md` frontmatter `name` and the final segment of the skill path. | -| `skills[].digest` | Yes | SHA-256 content digest of the artifact, formatted as `sha256:{hex}` (64 lowercase hex characters). See [Integrity and verification](#integrity-and-verification). | +| Field | Required | Description | +| ------------------------------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------- | +| `$schema` | Yes | Schema version URI. Clients SHOULD match against known URIs before processing. | +| `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[].url` | No¹ | Resource URI of the skill's `SKILL.md`. See [Direct entries](#direct-entries). | +| `skills[].digest` | With `url` | SHA-256 digest of the `SKILL.md` file. MUST be present when `url` is present, absent otherwise. See [Integrity](#integrity-and-verification). | +| `skills[].archives` | No¹ | Array of archive forms of the skill. See [Archives](#archives). | +| `skills[].archives[].url` | Yes | Resource URI of the archive. | +| `skills[].archives[].mimeType` | Yes | The archive format's media type (e.g., `application/gzip`, `application/zip`). | +| `skills[].archives[].digest` | Yes | SHA-256 digest of the archive file. See [Integrity](#integrity-and-verification). | + +¹ Every entry MUST include `url`, a non-empty `archives`, or both. + +Clients SHOULD ignore unrecognized fields. + +##### 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 a single read of the index, without fetching each `SKILL.md`. + +The `frontmatter` object MUST be identical in content to the frontmatter of the `SKILL.md` it describes. When `url` is present, the final `` segment of the URI MUST equal `frontmatter.name`, per [Resource Mapping](#resource-mapping). -Clients SHOULD ignore unrecognized fields and SHOULD skip entries with an unrecognized `type`. +##### Direct entries -**Skill-md entries** (`type: "skill-md"`) point `url` at the full resource URI of the skill's `SKILL.md`. Supporting files are individually addressable as sibling resources under the same skill path. +When present, `url` 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). -**Archive entries** (`type: "archive"`) point `url` at a single resource whose content is a packed skill directory. Per the [Agent Skills archive distribution format](https://agentskills.io/well-known-uri#archive-distribution), the archive MUST be `.tar.gz` (gzip-compressed tar, `mimeType` `application/gzip`) or `.zip` (`mimeType` `application/zip`); hosts MUST support both and SHOULD determine the format from the resource's `mimeType`, falling back to the URL suffix. Archive contents represent the skill directory directly — `SKILL.md` MUST be at the archive root, not nested inside a wrapper directory, and the archive MUST NOT contain path-traversal sequences (`..`) or absolute paths. +##### Archives -The `` is the archive `url` with its archive suffix (`.tar.gz` or `.zip`) stripped — `skill://pdf-processing.tar.gz` unpacks to `skill://pdf-processing/`, `skill://acme/billing/refunds.zip` to `skill://acme/billing/refunds/` — and the archive contents populate `skill:///` exactly as if each file were served individually. A server chooses per-skill whether to distribute as individual files or as an archive; hosts observe an identical virtual namespace either way. Archive distribution delivers a multi-file skill atomically in one round trip and can carry UNIX file metadata (executable bits, symlinks) that individual-resource distribution cannot represent — robustly for `.tar.gz`, variably for `.zip`. +`archives` lists pre-packed forms of the entire skill directory. Each archive is a single resource whose content is the complete skill directory in an archive format; reading it via `resources/read` retrieves the skill — `SKILL.md` and all supporting files — in one round trip. When multiple archives are listed, they are alternative encodings of identical content; a host picks one whose format it supports, determined from `mimeType`. -Hosts unpacking an archive MUST apply the [archive safety](https://agentskills.io/well-known-uri#archive-safety) requirements of the Agent Skills specification: reject archives containing path-traversal sequences or absolute paths, reject symlinks or hard links that resolve outside the skill directory, and enforce a limit on total unpacked size. +`mimeType` MUST identify an archive format. Hosts SHOULD support at least gzip-compressed tar (`application/gzip`, conventionally `.tar.gz`) and ZIP (`application/zip`); servers MAY offer additional formats but SHOULD include at least one of these two so that any conforming host can retrieve the skill. + +Archive layout: + +- `SKILL.md` MUST be at the archive root, not nested inside a wrapper directory. +- Entry paths MUST use `/` as the separator and MUST be relative — no path-traversal sequences (`..`) and no absolute paths. + +Archive contents represent the skill directory directly. When an entry carries both `url` and `archives`, each archive MUST unpack to the same files that are individually addressable under the skill path — a host may fetch the skill either way and observes the same content. When an entry is archive-only, the skill's files are not individually addressable on the server; the host unpacks the archive locally and addresses its contents under `skill:///`, where `` is `frontmatter.name`. In both cases the archive `url` itself carries no path semantics — it is a download location, nothing more. + +Compared to individual-file distribution, an archive delivers a multi-file skill atomically and can carry UNIX file metadata (executable bits, symlinks) that individual resources cannot represent — robustly for `.tar.gz`, variably for `.zip`. + +Archives are an unpacking attack surface. Hosts unpacking an archive MUST reject archives containing path-traversal sequences or absolute paths, reject symlinks or hard links that resolve outside the skill directory, and enforce a limit on total unpacked size to prevent decompression bombs. ##### Integrity and verification -Per the Agent Skills [integrity and verification](https://github.com/agentskills/agentskills/pull/254) requirements, each skill entry includes a `digest` field containing the SHA-256 hash of the artifact's raw bytes, formatted as `sha256:{hex}` where `{hex}` is 64 lowercase hexadecimal characters. For `"skill-md"`, the artifact is the `SKILL.md` file; for `"archive"`, the artifact is the archive file. +Digests are SHA-256 hashes of an artifact's raw bytes, formatted as `sha256:{hex}` where `{hex}` is 64 lowercase hexadecimal characters. For a skill entry's `digest`, the artifact is the `SKILL.md` file; for an archive's `digest`, the artifact is the archive file. Hosts MUST verify retrieved content against the `digest` in the index. A mismatch indicates the content is corrupted or tampered with — hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. @@ -199,7 +244,7 @@ The following are recommendations for interoperable implementations. They are no 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, from the model's perspective, an MCP-served skill is indistinguishable from a filesystem skill. -**Registry.** At startup and on connection change, the host assembles a single internal skill registry from every origin it supports: filesystem skill directories, and `skill://index.json` from each connected MCP server that declares the `io.modelcontextprotocol/skills` extension. Each registry entry records the skill's `name`, `description`, and origin — for a filesystem skill, the local directory; for an MCP skill, the server identity and the `SKILL.md` resource URI. Origin is host bookkeeping; it is not exposed to the model. +**Registry.** At startup and on connection change, the host assembles a single internal skill registry from every origin it supports: filesystem skill directories, and `skill://index.json` from each connected MCP server that declares the `io.modelcontextprotocol/skills` extension. Each registry entry records the skill's `name` and `description` (from the index 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. Origin is host bookkeeping; it is not exposed to the model. **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. @@ -313,9 +358,13 @@ The Agent Skills specification already defines YAML frontmatter fields, naming r ### Why Permit Archive Distribution? -An earlier draft excluded `type: "archive"` on the basis that supporting files are already individually addressable as resources. The Working Group [reversed this](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-04-19-archives-permitted-as-server-side-packaging-optimization) for four reasons: dropping a distribution type that the Agent Skills discovery format defines contradicts this SEP's framing as a pure transport binding; individual-file distribution cannot guarantee atomicity across a multi-file skill read; hosts that pre-materialize skills to a local filesystem pay N+1 round trips; and UNIX file metadata (executable bits, symlinks) has no representation when files are served individually. +An earlier draft excluded archives on the basis that supporting files are already individually addressable as resources. The Working Group [reversed this](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-04-19-archives-permitted-as-server-side-packaging-optimization) for three reasons: individual-file distribution cannot guarantee atomicity across a multi-file skill read; hosts that pre-materialize skills to a local filesystem pay N+1 round trips; and UNIX file metadata (executable bits, symlinks) has no representation when files are served individually. + +Attaching archives to a skill entry as alternative retrieval forms — rather than as a separate kind of skill — preserves the virtual-filesystem model: the post-unpack view is identical to individual-file distribution. The property given up is per-file `resources/subscribe` granularity, which is acceptable because subscription is not part of the skill reading model defined here. + +### Why Verbatim Frontmatter in the Index? -Reinstating archives as a server-side packaging choice — not a client-visible mode split — preserves the virtual-filesystem model: the post-unpack view is identical to individual-file distribution. The property given up is per-file `resources/subscribe` granularity, which is acceptable because subscription is not part of the skill reading model defined here. +The index could instead carry a curated subset of skill metadata — `name` and `description` as dedicated top-level fields. That forces a choice every time the Agent Skills frontmatter grows a field: amend this SEP to mirror it, or leave index consumers to fetch and parse every `SKILL.md` for it. Copying the frontmatter verbatim removes the choice. The index carries exactly what the skill author wrote; the fields hosts need for a registry (`name`, `description`) are guaranteed present because the Agent Skills specification requires them; and new frontmatter fields flow through with no change to this extension. A host builds its complete skill registry from a single `resources/read` of the index. ## Backward Compatibility @@ -332,7 +381,7 @@ Skill content is instructional text delivered to a model, which makes it a promp - **No implicit local execution.** Hosts MUST NOT honor mechanisms in skill content that would cause local code execution without explicit user opt-in. This includes, non-exhaustively: hook declarations, pre/post-invocation scripts, shell commands embedded in frontmatter, or any field that a filesystem-sourced skill might use to register executable behavior on the host. Hosts MUST either ignore such fields entirely when the skill arrives over MCP, or gate them behind an explicit per-skill user approval that states what will execute and where. Silently executing server-provided code because it appeared in a skill directory is a remote code execution vector. - **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. - **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. -- **Archive safety.** Archive-distributed skills are an unpacking attack surface. Hosts MUST validate archives per the [Agent Skills archive safety requirements](https://agentskills.io/well-known-uri#archive-safety): reject path traversal and absolute paths, reject links resolving outside the skill directory, and bound total unpacked size to prevent decompression bombs. +- **Archive safety.** Archive-distributed skills are an unpacking attack surface. Hosts MUST validate archives per the requirements in [Archives](#archives): reject path traversal and absolute paths, reject links resolving outside the skill directory, and bound total unpacked size to prevent decompression bombs. ## Reference Implementation @@ -357,7 +406,6 @@ Per [SEP-2133], an Extensions Track SEP requires at least one reference implemen ## References - [Agent Skills specification](https://agentskills.io/specification) -- [Agent Skills well-known URI discovery](https://agentskills.io/well-known-uri) - [SEP-2133]: Extensions - [SEP-2076]: Agent Skills as first-class primitive (alternative approach) - [Skills Over MCP Working Group charter](https://modelcontextprotocol.io/community/skills-over-mcp/charter) From d469e9b5ed11dbb62bb9254d65ffc9728fe0554d Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Mon, 8 Jun 2026 11:28:38 +0100 Subject: [PATCH 13/25] Drop $schema from the skill index format The index is versioned by the extension capability itself; unrecognized fields are ignored, so the format can grow additively without a per-document version marker. --- docs/seps/2640-skills-extension.mdx | 4 +--- seps/2640-skills-extension.md | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index c1be71c3d..62eb1da9b 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -115,7 +115,6 @@ A server SHOULD expose a resource at the well-known URI `skill://index.json` who ```json { - "$schema": "https://static.modelcontextprotocol.io/schemas/2026-06-08/skill-index.schema.json", "skills": [ { "url": "skill://git-workflow/SKILL.md", @@ -168,7 +167,6 @@ Index fields: | Field | Required | Description | | ------------------------------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -| `$schema` | Yes | Schema version URI. Clients SHOULD match against known URIs before processing. | | `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[].url` | No¹ | Resource URI of the skill's `SKILL.md`. See [Direct entries](#direct-entries). | @@ -180,7 +178,7 @@ Index fields: ¹ Every entry MUST include `url`, a non-empty `archives`, or both. -Clients SHOULD ignore unrecognized fields. +Clients SHOULD ignore unrecognized fields. The index carries no version marker: the format is versioned by the extension itself, and an incompatible revision would be negotiated through the [capability declaration](#capability-declaration). Tolerating unrecognized fields is what allows the format to grow additively. ##### Frontmatter diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index 52411c6d3..1563000ae 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -104,7 +104,6 @@ A server SHOULD expose a resource at the well-known URI `skill://index.json` who ```json { - "$schema": "https://static.modelcontextprotocol.io/schemas/2026-06-08/skill-index.schema.json", "skills": [ { "url": "skill://git-workflow/SKILL.md", @@ -157,7 +156,6 @@ Index fields: | Field | Required | Description | | ------------------------------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -| `$schema` | Yes | Schema version URI. Clients SHOULD match against known URIs before processing. | | `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[].url` | No¹ | Resource URI of the skill's `SKILL.md`. See [Direct entries](#direct-entries). | @@ -169,7 +167,7 @@ Index fields: ¹ Every entry MUST include `url`, a non-empty `archives`, or both. -Clients SHOULD ignore unrecognized fields. +Clients SHOULD ignore unrecognized fields. The index carries no version marker: the format is versioned by the extension itself, and an incompatible revision would be negotiated through the [capability declaration](#capability-declaration). Tolerating unrecognized fields is what allows the format to grow additively. ##### Frontmatter From 518ca79ba65e9b7c13fa550dcf605b32d01c7379 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Mon, 8 Jun 2026 11:38:39 +0100 Subject: [PATCH 14/25] Regenerate SEP index after rebase --- docs/seps/index.mdx | 80 +++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/docs/seps/index.mdx b/docs/seps/index.mdx index 359023faa..bd258cb0b 100644 --- a/docs/seps/index.mdx +++ b/docs/seps/index.mdx @@ -12,45 +12,55 @@ Specification Enhancement Proposals (SEPs) are the primary mechanism for proposi ## Summary +- **Final**: 41 - **Draft**: 1 -- **Accepted**: 2 -- **Final**: 28 ## All SEPs -| SEP | Title | Status | Type | Created | -| ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | ------------------------------------------------- | ---------------- | ---------- | -| [SEP-2640](/seps/2640-skills-extension) | Skills Extension | Draft | Extensions Track | 2026-04-23 | -| [SEP-2260](/seps/2260-Require-Server-requests-to-be-associated-with-Client-requests) | Require Server requests to be associated with a Client request. | Accepted | Standards Track | 2026-02-16 | -| [SEP-2243](/seps/2243-http-standardization) | HTTP Header Standardization for Streamable HTTP Transport | Final | Standards Track | 2026-02-04 | -| [SEP-2207](/seps/2207-oidc-refresh-token-guidance) | OIDC-Flavored Refresh Token Guidance | Accepted | Standards Track | 2026-02-04 | -| [SEP-2149](/seps/2149-working-group-charter-template) | MCP Group Governance and Charter Template | Final | Process | 2025-01-15 | -| [SEP-2148](/seps/2148-contributor-ladder) | MCP Contributor Ladder | Final | Process | 2026-01-15 | -| [SEP-2133](/seps/2133-extensions) | Extensions | Final | Standards Track | 2025-01-21 | -| [SEP-2085](/seps/2085-governance-succession-and-amendment) | Governance Succession and Amendment Procedures | Final | Process | 2025-12-05 | -| [SEP-1865](/seps/1865-mcp-apps-interactive-user-interfaces-for-mcp) | MCP Apps - Interactive User Interfaces for MCP | Final | Extensions Track | 2025-11-21 | -| [SEP-1850](/seps/1850-pr-based-sep-workflow) | PR-Based SEP Workflow | Final | Process | 2025-11-20 | -| [SEP-1730](/seps/1730-sdks-tiering-system) | SDKs Tiering System | Final | Standards Track | 2025-10-29 | -| [SEP-1699](/seps/1699-support-sse-polling-via-server-side-disconnect) | Support SSE polling via server-side disconnect | Final | Standards Track | 2025-10-22 | -| [SEP-1686](/seps/1686-tasks) | Tasks | Final | Standards Track | 2025-10-20 | -| [SEP-1613](/seps/1613-establish-json-schema-2020-12-as-default-dialect-f) | Establish JSON Schema 2020-12 as Default Dialect for MCP | Final | Standards Track | 2025-10-06 | -| [SEP-1577](/seps/1577--sampling-with-tools) | Sampling With Tools | Final | Standards Track | 2025-09-30 | -| [SEP-1330](/seps/1330-elicitation-enum-schema-improvements-and-standards) | Elicitation Enum Schema Improvements and Standards Compliance | Final | Standards Track | 2025-08-11 | -| [SEP-1319](/seps/1319-decouple-request-payload-from-rpc-methods-definiti) | Decouple Request Payload from RPC Methods Definition | Final | Standards Track | 2025-08-08 | -| [SEP-1303](/seps/1303-input-validation-errors-as-tool-execution-errors) | Input Validation Errors as Tool Execution Errors | Final | Standards Track | 2025-08-05 | -| [SEP-1302](/seps/1302-formalize-working-groups-and-interest-groups-in-mc) | Formalize Working Groups and Interest Groups in MCP Governance | Final | Standards Track | 2025-08-05 | -| [SEP-1046](/seps/1046-support-oauth-client-credentials-flow-in-authoriza) | Support OAuth client credentials flow in authorization | Final | Standards Track | 2025-07-23 | -| [SEP-1036](/seps/1036-url-mode-elicitation-for-secure-out-of-band-intera) | URL Mode Elicitation for secure out-of-band interactions | Final | Standards Track | 2025-07-22 | -| [SEP-1034](/seps/1034--support-default-values-for-all-primitive-types-in) | Support default values for all primitive types in elicitation schemas | Final | Standards Track | 2025-07-22 | -| [SEP-1024](/seps/1024-mcp-client-security-requirements-for-local-server-) | MCP Client Security Requirements for Local Server Installation | Final | Standards Track | 2025-07-22 | -| [SEP-994](/seps/994-shared-communication-practicesguidelines) | Shared Communication Practices/Guidelines | Final | Process | 2025-07-17 | -| [SEP-991](/seps/991-enable-url-based-client-registration-using-oauth-c) | Enable URL-based Client Registration using OAuth Client ID Metadata Documents | Final | Standards Track | 2025-07-07 | -| [SEP-990](/seps/990-enable-enterprise-idp-policy-controls-during-mcp-o) | Enable enterprise IdP policy controls during MCP OAuth flows | Final | Standards Track | 2025-06-04 | -| [SEP-986](/seps/986-specify-format-for-tool-names) | Specify Format for Tool Names | Final | Standards Track | 2025-07-16 | -| [SEP-985](/seps/985-align-oauth-20-protected-resource-metadata-with-rf) | Align OAuth 2.0 Protected Resource Metadata with RFC 9728 | Final | Standards Track | 2025-07-16 | -| [SEP-973](/seps/973-expose-additional-metadata-for-implementations-res) | Expose additional metadata for Implementations, Resources, Tools and Prompts | Final | Standards Track | 2025-07-15 | -| [SEP-932](/seps/932-model-context-protocol-governance) | Model Context Protocol Governance | Final | Process | 2025-07-08 | -| [SEP-414](/seps/414-request-meta) | Document OpenTelemetry Trace Context Propagation Conventions | Final | Standards Track | 2025-04-25 | +| SEP | Title | Status | Type | Created | +| ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | ----------------------------------------------- | ---------------- | ---------- | +| [SEP-2663](/seps/2663-tasks-extension) | Tasks Extension | Final | Extensions Track | 2026-04-27 | +| [SEP-2640](/seps/2640-skills-extension) | Skills Extension | Draft | Extensions Track | 2026-04-23 | +| [SEP-2596](/seps/2596-spec-feature-lifecycle-and-deprecation) | Specification Feature Lifecycle and Deprecation Policy | Final | Process | 2026-04-17 | +| [SEP-2577](/seps/2577-deprecate-roots-sampling-and-logging) | Deprecate Roots, Sampling, and Logging | Final | Standards Track | 2026-04-14 | +| [SEP-2575](/seps/2575-stateless-mcp) | Make MCP Stateless | Final | Standards Track | 2025-06-18 | +| [SEP-2567](/seps/2567-sessionless-mcp) | Sessionless MCP via Explicit State Handles | Final | Standards Track | 2026-03-11 | +| [SEP-2549](/seps/2549-TTL-for-list-results) | TTL for List Results | Final | Standards Track | 2026-04-09 | +| [SEP-2484](/seps/2484-conformance-tests-required-for-final-seps) | Require Conformance Tests for Standards Track SEPs to Reach Final Status | Final | Process | 2026-03-27 | +| [SEP-2468](/seps/2468-recommend-issuer-claim-for-auth) | Recommend Issuer (iss) Parameter in MCP Auth Responses | Final | Standards Track | 2026-03-25 | +| [SEP-2322](/seps/2322-MRTR) | Multi Round-Trip Requests | Final | Standards Track | 2026-02-03 | +| [SEP-2260](/seps/2260-Require-Server-requests-to-be-associated-with-Client-requests) | Require Server requests to be associated with a Client request. | Final | Standards Track | 2026-02-16 | +| [SEP-2243](/seps/2243-http-standardization) | HTTP Header Standardization for Streamable HTTP Transport | Final | Standards Track | 2026-02-04 | +| [SEP-2207](/seps/2207-oidc-refresh-token-guidance) | OIDC-Flavored Refresh Token Guidance | Final | Standards Track | 2026-02-04 | +| [SEP-2164](/seps/2164-resource-not-found-error) | Standardize Resource Not Found Error Code | Final | Standards Track | 2026-01-28 | +| [SEP-2149](/seps/2149-working-group-charter-template) | MCP Group Governance and Charter Template | Final | Process | 2025-01-15 | +| [SEP-2148](/seps/2148-contributor-ladder) | MCP Contributor Ladder | Final | Process | 2026-01-15 | +| [SEP-2133](/seps/2133-extensions) | Extensions | Final | Standards Track | 2025-01-21 | +| [SEP-2106](/seps/2106-json-schema-2020-12) | Tools `inputSchema` & `outputSchema` Conform to JSON Schema 2020-12 | Final | Standards Track | 2026-01-06 | +| [SEP-2085](/seps/2085-governance-succession-and-amendment) | Governance Succession and Amendment Procedures | Final | Process | 2025-12-05 | +| [SEP-1865](/seps/1865-mcp-apps-interactive-user-interfaces-for-mcp) | MCP Apps - Interactive User Interfaces for MCP | Final | Extensions Track | 2025-11-21 | +| [SEP-1850](/seps/1850-pr-based-sep-workflow) | PR-Based SEP Workflow | Final | Process | 2025-11-20 | +| [SEP-1730](/seps/1730-sdks-tiering-system) | SDKs Tiering System | Final | Standards Track | 2025-10-29 | +| [SEP-1699](/seps/1699-support-sse-polling-via-server-side-disconnect) | Support SSE polling via server-side disconnect | Final | Standards Track | 2025-10-22 | +| [SEP-1686](/seps/1686-tasks) | Tasks | Final | Standards Track | 2025-10-20 | +| [SEP-1613](/seps/1613-establish-json-schema-2020-12-as-default-dialect-f) | Establish JSON Schema 2020-12 as Default Dialect for MCP | Final | Standards Track | 2025-10-06 | +| [SEP-1577](/seps/1577--sampling-with-tools) | Sampling With Tools | Final | Standards Track | 2025-09-30 | +| [SEP-1330](/seps/1330-elicitation-enum-schema-improvements-and-standards) | Elicitation Enum Schema Improvements and Standards Compliance | Final | Standards Track | 2025-08-11 | +| [SEP-1319](/seps/1319-decouple-request-payload-from-rpc-methods-definiti) | Decouple Request Payload from RPC Methods Definition | Final | Standards Track | 2025-08-08 | +| [SEP-1303](/seps/1303-input-validation-errors-as-tool-execution-errors) | Input Validation Errors as Tool Execution Errors | Final | Standards Track | 2025-08-05 | +| [SEP-1302](/seps/1302-formalize-working-groups-and-interest-groups-in-mc) | Formalize Working Groups and Interest Groups in MCP Governance | Final | Standards Track | 2025-08-05 | +| [SEP-1046](/seps/1046-support-oauth-client-credentials-flow-in-authoriza) | Support OAuth client credentials flow in authorization | Final | Standards Track | 2025-07-23 | +| [SEP-1036](/seps/1036-url-mode-elicitation-for-secure-out-of-band-intera) | URL Mode Elicitation for secure out-of-band interactions | Final | Standards Track | 2025-07-22 | +| [SEP-1034](/seps/1034--support-default-values-for-all-primitive-types-in) | Support default values for all primitive types in elicitation schemas | Final | Standards Track | 2025-07-22 | +| [SEP-1024](/seps/1024-mcp-client-security-requirements-for-local-server-) | MCP Client Security Requirements for Local Server Installation | Final | Standards Track | 2025-07-22 | +| [SEP-994](/seps/994-shared-communication-practicesguidelines) | Shared Communication Practices/Guidelines | Final | Process | 2025-07-17 | +| [SEP-991](/seps/991-enable-url-based-client-registration-using-oauth-c) | Enable URL-based Client Registration using OAuth Client ID Metadata Documents | Final | Standards Track | 2025-07-07 | +| [SEP-990](/seps/990-enable-enterprise-idp-policy-controls-during-mcp-o) | Enable enterprise IdP policy controls during MCP OAuth flows | Final | Standards Track | 2025-06-04 | +| [SEP-986](/seps/986-specify-format-for-tool-names) | Specify Format for Tool Names | Final | Standards Track | 2025-07-16 | +| [SEP-985](/seps/985-align-oauth-20-protected-resource-metadata-with-rf) | Align OAuth 2.0 Protected Resource Metadata with RFC 9728 | Final | Standards Track | 2025-07-16 | +| [SEP-973](/seps/973-expose-additional-metadata-for-implementations-res) | Expose additional metadata for Implementations, Resources, Tools and Prompts | Final | Standards Track | 2025-07-15 | +| [SEP-932](/seps/932-model-context-protocol-governance) | Model Context Protocol Governance | Final | Process | 2025-07-08 | +| [SEP-414](/seps/414-request-meta) | Document OpenTelemetry Trace Context Propagation Conventions | Final | Standards Track | 2025-04-25 | ## SEP Status Definitions From 2e04c48da90224000e750ffd54a3611f2824fbc0 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Tue, 9 Jun 2026 10:42:32 +0100 Subject: [PATCH 15/25] Add resources/directory/read for scoped directory listing Skills often point agents at a directory of files (templates, scripts) without naming one; resources/list cannot answer this scoped question. The new method lists the direct children of a resource with mimeType inode/directory, with resources/list-style cursor pagination. Support is opt-in via a directoryRead setting in the extension capability. --- docs/seps/2640-skills-extension.mdx | 90 +++++++++++++++++++++++++++-- seps/2640-skills-extension.md | 90 +++++++++++++++++++++++++++-- 2 files changed, 172 insertions(+), 8 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index 62eb1da9b..ab6524426 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -30,7 +30,7 @@ description: "Skills Extension" 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 well-known `skill://index.json` resource enumerates the skills a server serves, but is not required — accommodating servers whose skill catalogs are large, generated, or otherwise unenumerable. 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. -Because the extension adds no new protocol methods or capabilities, 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. +The extension defines one optional protocol method — `resources/directory/read`, which 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 @@ -231,13 +231,21 @@ Per [SEP-2133] extension negotiation, servers declare support for this extension { "capabilities": { "extensions": { - "io.modelcontextprotocol/skills": {} + "io.modelcontextprotocol/skills": { + "directoryRead": true + } } } } ``` -No extension-specific settings are currently defined; an empty object indicates support. +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. Clients MUST NOT call `resources/directory/read` against a server that has not declared `directoryRead: true`. ### Reading @@ -245,6 +253,66 @@ Skill files are read via the standard `resources/read` method. No skill-specific 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 `skill://` scheme root. +### 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 Is Enumeration Optional?](#why-is-enumeration-optional)) — 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": { + "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. + ## Implementation Guidelines The following are recommendations for interoperable implementations. They are not part of the normative specification. @@ -297,6 +365,8 @@ When the model calls `read_skill`, the host looks up the name in its registry an 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:///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 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. Either way the resolution rule is the same: relative references resolve against the skill's root directory, exactly as on a filesystem. +**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 enumeration is optional, 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. A `read_resource` call for an unlisted `skill://` URI is forwarded to the named server, which either serves it or returns not-found. 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. @@ -325,6 +395,8 @@ The SDK handles: reading `SKILL.md` frontmatter to populate resource metadata, s skills = await client.list_skills() # reads skill://index.json, may be empty or absent 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. @@ -371,13 +443,23 @@ An earlier draft excluded archives on the basis that supporting files are alread Attaching archives to a skill entry as alternative retrieval forms — rather than as a separate kind of skill — preserves the virtual-filesystem model: the post-unpack view is identical to individual-file distribution. The property given up is per-file `resources/subscribe` granularity, which is acceptable because subscription is not part of the skill reading model defined here. +### Why a Directory Read Method? + +The virtual-filesystem model this SEP builds on had a read operation (`resources/read`) but no readdir. That gap is harmless while skill instructions name files explicitly, but skills routinely defer the choice to the agent — "use the template in `templates/` that matches the document type." On a filesystem the agent lists the directory; over MCP the only enumeration was `resources/list`, which is global: it returns the server's entire resource space, cannot be scoped to a subtree, and is precisely what large or generative servers — the ones that motivated optional enumeration — decline to implement. A server that cannot enumerate its catalog can still trivially enumerate one directory it is already serving. + +`resources/directory/read` is the readdir analog: scoped, paginated, and composable — listings mark subdirectories with `inode/directory`, so an agent descends a skill's tree exactly as it would walk a directory locally. + +Two alternatives were considered. Extending `resources/list` with a scope parameter would change a core method's semantics from inside an extension, and a URI-prefix filter misstates the model anyway — prefixes are string matching, not structure. Embedding a file manifest in `SKILL.md` or the index would freeze the listing at authoring or indexing time and bloat context for skills with many files; a method keeps listings live and on demand. + +Although introduced by this extension, the method is deliberately general — any directory resource qualifies, under any scheme — making it a candidate for promotion into the core Resources primitive if usage warrants. + ### Why Verbatim Frontmatter in the Index? The index could instead carry a curated subset of skill metadata — `name` and `description` as dedicated top-level fields. That forces a choice every time the Agent Skills frontmatter grows a field: amend this SEP to mirror it, or leave index consumers to fetch and parse every `SKILL.md` for it. Copying the frontmatter verbatim removes the choice. The index carries exactly what the skill author wrote; the fields hosts need for a registry (`name`, `description`) are guaranteed present because the Agent Skills specification requires them; and new frontmatter fields flow through with no change to this extension. A host builds its complete skill registry from a single `resources/read` of the index. ## Backward Compatibility -This extension introduces no new protocol 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. +This extension introduces one optional protocol method, `resources/directory/read`, gated behind an explicit capability setting — a server that does not declare `directoryRead` never receives the call, and a client that predates it never issues one. It 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. diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index 1563000ae..9da1f597f 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -19,7 +19,7 @@ Design history, experimental findings, and reference implementations are maintai 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 well-known `skill://index.json` resource enumerates the skills a server serves, but is not required — accommodating servers whose skill catalogs are large, generated, or otherwise unenumerable. 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. -Because the extension adds no new protocol methods or capabilities, 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. +The extension defines one optional protocol method — `resources/directory/read`, which 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 @@ -220,13 +220,21 @@ Per [SEP-2133] extension negotiation, servers declare support for this extension { "capabilities": { "extensions": { - "io.modelcontextprotocol/skills": {} + "io.modelcontextprotocol/skills": { + "directoryRead": true + } } } } ``` -No extension-specific settings are currently defined; an empty object indicates support. +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. Clients MUST NOT call `resources/directory/read` against a server that has not declared `directoryRead: true`. ### Reading @@ -234,6 +242,66 @@ Skill files are read via the standard `resources/read` method. No skill-specific 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 `skill://` scheme root. +### 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 Is Enumeration Optional?](#why-is-enumeration-optional)) — 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": { + "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. + ## Implementation Guidelines The following are recommendations for interoperable implementations. They are not part of the normative specification. @@ -286,6 +354,8 @@ When the model calls `read_skill`, the host looks up the name in its registry an 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:///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 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. Either way the resolution rule is the same: relative references resolve against the skill's root directory, exactly as on a filesystem. +**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 enumeration is optional, 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. A `read_resource` call for an unlisted `skill://` URI is forwarded to the named server, which either serves it or returns not-found. 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. @@ -314,6 +384,8 @@ The SDK handles: reading `SKILL.md` frontmatter to populate resource metadata, s skills = await client.list_skills() # reads skill://index.json, may be empty or absent 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. @@ -360,13 +432,23 @@ An earlier draft excluded archives on the basis that supporting files are alread Attaching archives to a skill entry as alternative retrieval forms — rather than as a separate kind of skill — preserves the virtual-filesystem model: the post-unpack view is identical to individual-file distribution. The property given up is per-file `resources/subscribe` granularity, which is acceptable because subscription is not part of the skill reading model defined here. +### Why a Directory Read Method? + +The virtual-filesystem model this SEP builds on had a read operation (`resources/read`) but no readdir. That gap is harmless while skill instructions name files explicitly, but skills routinely defer the choice to the agent — "use the template in `templates/` that matches the document type." On a filesystem the agent lists the directory; over MCP the only enumeration was `resources/list`, which is global: it returns the server's entire resource space, cannot be scoped to a subtree, and is precisely what large or generative servers — the ones that motivated optional enumeration — decline to implement. A server that cannot enumerate its catalog can still trivially enumerate one directory it is already serving. + +`resources/directory/read` is the readdir analog: scoped, paginated, and composable — listings mark subdirectories with `inode/directory`, so an agent descends a skill's tree exactly as it would walk a directory locally. + +Two alternatives were considered. Extending `resources/list` with a scope parameter would change a core method's semantics from inside an extension, and a URI-prefix filter misstates the model anyway — prefixes are string matching, not structure. Embedding a file manifest in `SKILL.md` or the index would freeze the listing at authoring or indexing time and bloat context for skills with many files; a method keeps listings live and on demand. + +Although introduced by this extension, the method is deliberately general — any directory resource qualifies, under any scheme — making it a candidate for promotion into the core Resources primitive if usage warrants. + ### Why Verbatim Frontmatter in the Index? The index could instead carry a curated subset of skill metadata — `name` and `description` as dedicated top-level fields. That forces a choice every time the Agent Skills frontmatter grows a field: amend this SEP to mirror it, or leave index consumers to fetch and parse every `SKILL.md` for it. Copying the frontmatter verbatim removes the choice. The index carries exactly what the skill author wrote; the fields hosts need for a registry (`name`, `description`) are guaranteed present because the Agent Skills specification requires them; and new frontmatter fields flow through with no change to this extension. A host builds its complete skill registry from a single `resources/read` of the index. ## Backward Compatibility -This extension introduces no new protocol 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. +This extension introduces one optional protocol method, `resources/directory/read`, gated behind an explicit capability setting — a server that does not declare `directoryRead` never receives the call, and a client that predates it never issues one. It 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. From 211c2497ac801e1b913014a73054a90060e5035c Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Thu, 18 Jun 2026 13:07:01 +0100 Subject: [PATCH 16/25] Add Sambhav Kothari to SEP-2640 author list Collapse the Author(s) field onto one line so the generated SEP page includes the full author list and working-group attribution. Claude-Session: https://claude.ai/code/session_01MbcPVz67EwJdcNgeXgn7iG --- docs/seps/2640-skills-extension.mdx | 20 ++++++++++---------- seps/2640-skills-extension.md | 3 +-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index ab6524426..88ff4b708 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -13,16 +13,16 @@ description: "Skills Extension"
-| Field | Value | -| ------------- | -------------------------------------------------------------------------------------------------------------------- | -| **SEP** | 2640 | -| **Title** | Skills Extension | -| **Status** | Draft | -| **Type** | Extensions Track | -| **Created** | 2026-04-23 | -| **Author(s)** | Peter Alexander ([@pja-ant](https://github.com/pja-ant)), Ola Hungerford ([@olaservo](https://github.com/olaservo)), | -| **Sponsor** | [@pja-ant](https://github.com/pja-ant) | -| **PR** | [#2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) | +| Field | Value | +| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **SEP** | 2640 | +| **Title** | Skills Extension | +| **Status** | Draft | +| **Type** | Extensions Track | +| **Created** | 2026-04-23 | +| **Author(s)** | Peter Alexander ([@pja-ant](https://github.com/pja-ant)), Ola Hungerford ([@olaservo](https://github.com/olaservo)), Sambhav Kothari ([@sambhav](https://github.com/sambhav)), on behalf of the Skills Over MCP Working Group | +| **Sponsor** | [@pja-ant](https://github.com/pja-ant) | +| **PR** | [#2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) | --- diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index 9da1f597f..690bac6b0 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -3,8 +3,7 @@ - **Status**: Draft - **Type**: Extensions Track - **Created**: 2026-04-23 -- **Author(s)**: Peter Alexander (@pja-ant), Ola Hungerford (@olaservo), - on behalf of the Skills Over MCP Working Group +- **Author(s)**: Peter Alexander (@pja-ant), Ola Hungerford (@olaservo), Sambhav Kothari (@sambhav), 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 From 96cfc68eb040b5292a41fd4f1cc657b7cea3dc59 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Thu, 18 Jun 2026 15:05:46 +0100 Subject: [PATCH 17/25] Reserve io.modelcontextprotocol/ metadata key prefix in skill frontmatter Keys under this prefix in the frontmatter metadata object are reserved for MCP-defined semantics. No keys are defined yet; unrecognized keys under the prefix should be ignored. Claude-Session: https://claude.ai/code/session_01MbcPVz67EwJdcNgeXgn7iG --- docs/seps/2640-skills-extension.mdx | 2 ++ seps/2640-skills-extension.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index 88ff4b708..3e1013ca2 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -186,6 +186,8 @@ Clients SHOULD ignore unrecognized fields. The index carries no version marker: The `frontmatter` object MUST be identical in content to the frontmatter of the `SKILL.md` it describes. When `url` is present, the final `` segment of the 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. + ##### Direct entries When present, `url` 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). diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index 690bac6b0..2302b08d7 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -174,6 +174,8 @@ Clients SHOULD ignore unrecognized fields. The index carries no version marker: The `frontmatter` object MUST be identical in content to the frontmatter of the `SKILL.md` it describes. When `url` is present, the final `` segment of the 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. + ##### Direct entries When present, `url` 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). From 98fbbca8b6bb3d4e931ecde4fe5e0e8e472fdb14 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Tue, 23 Jun 2026 14:07:02 +0100 Subject: [PATCH 18/25] Apply suggestions from code review Co-authored-by: Den Delimarsky <53200638+localden@users.noreply.github.com> --- seps/2640-skills-extension.md | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index 2302b08d7..87ebafe5a 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -195,7 +195,18 @@ Archive contents represent the skill directory directly. When an entry carries b Compared to individual-file distribution, an archive delivers a multi-file skill atomically and can carry UNIX file metadata (executable bits, symlinks) that individual resources cannot represent — robustly for `.tar.gz`, variably for `.zip`. -Archives are an unpacking attack surface. Hosts unpacking an archive MUST reject archives containing path-traversal sequences or absolute paths, reject symlinks or hard links that resolve outside the skill directory, and enforce a limit on total unpacked size to prevent decompression bombs. +Archives are an unpacking attack surface. Hosts unpacking an archive MUST: + +1. Reject entries with path-traversal sequences or absolute paths. +2. Reject symlinks or hard links that resolve outside the skill directory. +3. Extract only regular files, directories, and symlinks, rejecting device nodes and any other special-file entry type. +4. Reject archives in which any two entry paths are equal after Unicode normalization and case folding, as a case/normalization collision can silently overwrite `SKILL.md` on case-insensitive or normalizing filesystems. +5. Clear setuid/setgid/sticky mode bits on every extracted entry and extract all entries as the host process's own user and group, ignoring uid/gid fields recorded in the archive. +6. Enforce limits on total unpacked size, total entry count, and maximum path depth, applied both per archive and as a cumulative per-server budget. Once the per-server budget is exhausted, hosts MUST refuse to unpack further archives from that server. + +Hosts SHOULD use existing hardened libraries for handling archives, specific to their language and platform rather than reimplement them. + +Digest verification ([Integrity and verification](#integrity-and-verification)) covers the archive bytes at retrieval only, not the unpacked tree thereafter. Hosts MUST unpack 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, and MUST keep the unpacked tree immutable for the cached skill's lifetime. ##### Integrity and verification @@ -203,6 +214,11 @@ Digests are SHA-256 hashes of an artifact's raw bytes, formatted as `sha256:{hex Hosts MUST verify retrieved content against the `digest` in the index. A mismatch indicates the content is corrupted or tampered with — hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. +Hosts MUST verify retrieved content against the `digest` in the index. A mismatch indicates the content is corrupted or tampered with - hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. + +After fetching and digest-verifying a `SKILL.md` whose entry appeared in the index, 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 index is what the model actually receives. + +A host that retrieves a skill via an archive and verifies the archive digest MUST thereafter serve every read of that skill's files from the locally unpacked archive for the lifetime of that verification. It MUST NOT satisfy those reads by issuing live `resources/read` calls against the server, because the archive digest is the only integrity check covering supporting files. A malicious server can return different bytes live than the verified archive contained. The `skill://index.json` resource is served via `resources/read` like any other resource, with `mimeType` of `application/json`. A server MAY also surface it in `resources/list` so clients can detect its presence, but clients MAY attempt to read it directly without prior discovery. A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY decline to expose `skill://index.json`, or MAY expose a partial index. Hosts MUST NOT treat an absent or empty index as proof that a server has no skills. @@ -458,11 +474,16 @@ Existing implementations using other `skill://` URI structures will need to adju Skill content is instructional text delivered to a model, which makes it a prompt-injection surface. The Working Group's position, recorded 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), is: - **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. -- **Skills do not introduce a new trust tier.** A user who connects a server has already extended their trust boundary to it; a malicious server can do as much harm via tools as via a skill document. Serving skills over MCP adds no risk beyond what skills already carry in any transport — but the defensive posture above applies regardless. -- **No implicit local execution.** Hosts MUST NOT honor mechanisms in skill content that would cause local code execution without explicit user opt-in. This includes, non-exhaustively: hook declarations, pre/post-invocation scripts, shell commands embedded in frontmatter, or any field that a filesystem-sourced skill might use to register executable behavior on the host. Hosts MUST either ignore such fields entirely when the skill arrives over MCP, or gate them behind an explicit per-skill user approval that states what will execute and where. Silently executing server-provided code because it appeared in a skill directory is a remote code execution vector. +- **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 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. +- **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. - **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. - **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. - **Archive safety.** Archive-distributed skills are an unpacking attack surface. Hosts MUST validate archives per the requirements in [Archives](#archives): reject path traversal and absolute paths, reject links resolving outside the skill directory, and bound total unpacked size to prevent decompression bombs. +- **Content-bound approval.** When a host persists any per-skill user approval, it MUST be bound to the `digest` of the `SKILL.md` (and the archive `digest`, if present) observed at the moment of approval. If a subsequent index advertises a different digest under the same skill name or URI, the host MUST treat the prior approval as revoked and re-prompt before loading or executing. Digest verification ([Integrity and verification](#integrity-and-verification)) defends only against transport tampering - it cannot defend against the originating server rotating content, because the server authors both the index and the body. +- **Cache isolation and durable origin.** Hosts that unpack or cache MCP-served skill content on disk MUST 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. Unpacked 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 From 13908216b02fb8e991efe0e7aa8bfc7760cd7f6d Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Thu, 25 Jun 2026 14:39:44 +0100 Subject: [PATCH 19/25] Update seps/2640-skills-extension.md Co-authored-by: Den Delimarsky <53200638+localden@users.noreply.github.com> --- seps/2640-skills-extension.md | 1 + 1 file changed, 1 insertion(+) diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index 87ebafe5a..1272793e5 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -482,6 +482,7 @@ Skill content is instructional text delivered to a model, which makes it a promp - **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. - **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. - **Archive safety.** Archive-distributed skills are an unpacking attack surface. Hosts MUST validate archives per the requirements in [Archives](#archives): reject path traversal and absolute paths, reject links resolving outside the skill directory, and bound total unpacked size to prevent decompression bombs. +- **Digests are not a security boundary.** Index digests are unsigned and come from the same server as the content. They confirm consistency between the index 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 `digest` of the `SKILL.md` (and the archive `digest`, if present) observed at the moment of approval. If a subsequent index advertises a different digest under the same skill name or URI, the host MUST treat the prior approval as revoked and re-prompt before loading or executing. Digest verification ([Integrity and verification](#integrity-and-verification)) defends only against transport tampering - it cannot defend against the originating server rotating content, because the server authors both the index and the body. - **Cache isolation and durable origin.** Hosts that unpack or cache MCP-served skill content on disk MUST 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. Unpacked 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. From 82609b1924fbd475e62da5af8a4e447e348d8072 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Thu, 25 Jun 2026 14:40:05 +0100 Subject: [PATCH 20/25] Update seps/2640-skills-extension.md Co-authored-by: Den Delimarsky <53200638+localden@users.noreply.github.com> --- seps/2640-skills-extension.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index 1272793e5..d4e6a177d 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -214,7 +214,7 @@ Digests are SHA-256 hashes of an artifact's raw bytes, formatted as `sha256:{hex Hosts MUST verify retrieved content against the `digest` in the index. A mismatch indicates the content is corrupted or tampered with — hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. -Hosts MUST verify retrieved content against the `digest` in the index. A mismatch indicates the content is corrupted or tampered with - hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. +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 index and the content together. Hosts MUST NOT treat a digest match as a security boundary. After fetching and digest-verifying a `SKILL.md` whose entry appeared in the index, 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 index is what the model actually receives. From 8d25c11f4f1f7ff313f73e916555672dbf54fda7 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Wed, 8 Jul 2026 12:23:27 +0100 Subject: [PATCH 21/25] Fix grammar and paragraph break in skills extension SEP Add the missing conjunction in the host-side surfaces bullet, and restore the blank line before the skill://index.json paragraph so it no longer renders joined to the preceding archive-read paragraph. Also regenerate docs/seps/2640-skills-extension.mdx, which had gone stale against the source markdown. No-Verification-Needed: doc-only change with no runtime surface --- docs/seps/2640-skills-extension.mdx | 29 ++++++++++++++++++++++++++--- seps/2640-skills-extension.md | 3 ++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index 3e1013ca2..143419234 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -207,7 +207,18 @@ Archive contents represent the skill directory directly. When an entry carries b Compared to individual-file distribution, an archive delivers a multi-file skill atomically and can carry UNIX file metadata (executable bits, symlinks) that individual resources cannot represent — robustly for `.tar.gz`, variably for `.zip`. -Archives are an unpacking attack surface. Hosts unpacking an archive MUST reject archives containing path-traversal sequences or absolute paths, reject symlinks or hard links that resolve outside the skill directory, and enforce a limit on total unpacked size to prevent decompression bombs. +Archives are an unpacking attack surface. Hosts unpacking an archive MUST: + +1. Reject entries with path-traversal sequences or absolute paths. +2. Reject symlinks or hard links that resolve outside the skill directory. +3. Extract only regular files, directories, and symlinks, rejecting device nodes and any other special-file entry type. +4. Reject archives in which any two entry paths are equal after Unicode normalization and case folding, as a case/normalization collision can silently overwrite `SKILL.md` on case-insensitive or normalizing filesystems. +5. Clear setuid/setgid/sticky mode bits on every extracted entry and extract all entries as the host process's own user and group, ignoring uid/gid fields recorded in the archive. +6. Enforce limits on total unpacked size, total entry count, and maximum path depth, applied both per archive and as a cumulative per-server budget. Once the per-server budget is exhausted, hosts MUST refuse to unpack further archives from that server. + +Hosts SHOULD use existing hardened libraries for handling archives, specific to their language and platform rather than reimplement them. + +Digest verification ([Integrity and verification](#integrity-and-verification)) covers the archive bytes at retrieval only, not the unpacked tree thereafter. Hosts MUST unpack 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, and MUST keep the unpacked tree immutable for the cached skill's lifetime. ##### Integrity and verification @@ -215,6 +226,12 @@ Digests are SHA-256 hashes of an artifact's raw bytes, formatted as `sha256:{hex Hosts MUST verify retrieved content against the `digest` in the index. A mismatch indicates the content is corrupted or tampered with — hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. +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 index and the content together. Hosts MUST NOT treat a digest match as a security boundary. + +After fetching and digest-verifying a `SKILL.md` whose entry appeared in the index, 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 index is what the model actually receives. + +A host that retrieves a skill via an archive and verifies the archive digest MUST thereafter serve every read of that skill's files from the locally unpacked archive for the lifetime of that verification. It MUST NOT satisfy those reads by issuing live `resources/read` calls against the server, because the archive digest is the only integrity check covering supporting files. A malicious server can return different bytes live than the verified archive contained. + The `skill://index.json` resource is served via `resources/read` like any other resource, with `mimeType` of `application/json`. A server MAY also surface it in `resources/list` so clients can detect its presence, but clients MAY attempt to read it directly without prior discovery. A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY decline to expose `skill://index.json`, or MAY expose a partial index. Hosts MUST NOT treat an absent or empty index as proof that a server has no skills. @@ -470,11 +487,17 @@ Existing implementations using other `skill://` URI structures will need to adju Skill content is instructional text delivered to a model, which makes it a prompt-injection surface. The Working Group's position, recorded 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), is: - **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. -- **Skills do not introduce a new trust tier.** A user who connects a server has already extended their trust boundary to it; a malicious server can do as much harm via tools as via a skill document. Serving skills over MCP adds no risk beyond what skills already carry in any transport — but the defensive posture above applies regardless. -- **No implicit local execution.** Hosts MUST NOT honor mechanisms in skill content that would cause local code execution without explicit user opt-in. This includes, non-exhaustively: hook declarations, pre/post-invocation scripts, shell commands embedded in frontmatter, or any field that a filesystem-sourced skill might use to register executable behavior on the host. Hosts MUST either ignore such fields entirely when the skill arrives over MCP, or gate them behind an explicit per-skill user approval that states what will execute and where. Silently executing server-provided code because it appeared in a skill directory is a remote code execution vector. +- **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. +- **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. - **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. - **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. - **Archive safety.** Archive-distributed skills are an unpacking attack surface. Hosts MUST validate archives per the requirements in [Archives](#archives): reject path traversal and absolute paths, reject links resolving outside the skill directory, and bound total unpacked size to prevent decompression bombs. +- **Digests are not a security boundary.** Index digests are unsigned and come from the same server as the content. They confirm consistency between the index 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 `digest` of the `SKILL.md` (and the archive `digest`, if present) observed at the moment of approval. If a subsequent index advertises a different digest under the same skill name or URI, the host MUST treat the prior approval as revoked and re-prompt before loading or executing. Digest verification ([Integrity and verification](#integrity-and-verification)) defends only against transport tampering - it cannot defend against the originating server rotating content, because the server authors both the index and the body. +- **Cache isolation and durable origin.** Hosts that unpack or cache MCP-served skill content on disk MUST 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. Unpacked 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 diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index d4e6a177d..bd2c2cdda 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -219,6 +219,7 @@ Digests are unsigned and supplied by the same server that supplies the content. After fetching and digest-verifying a `SKILL.md` whose entry appeared in the index, 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 index is what the model actually receives. A host that retrieves a skill via an archive and verifies the archive digest MUST thereafter serve every read of that skill's files from the locally unpacked archive for the lifetime of that verification. It MUST NOT satisfy those reads by issuing live `resources/read` calls against the server, because the archive digest is the only integrity check covering supporting files. A malicious server can return different bytes live than the verified archive contained. + The `skill://index.json` resource is served via `resources/read` like any other resource, with `mimeType` of `application/json`. A server MAY also surface it in `resources/list` so clients can detect its presence, but clients MAY attempt to read it directly without prior discovery. A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY decline to expose `skill://index.json`, or MAY expose a partial index. Hosts MUST NOT treat an absent or empty index as proof that a server has no skills. @@ -475,7 +476,7 @@ Skill content is instructional text delivered to a model, which makes it a promp - **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 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. +- **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. - **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. From af08f6fe16675351433e7803a5fee3cdd1eb1d9a Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Wed, 8 Jul 2026 12:41:38 +0100 Subject: [PATCH 22/25] Remove archive distribution from skills extension Archives allowed a skill entry to advertise pre-packed .tar.gz/.zip forms of the whole skill directory as an alternative retrieval form. Remove them, per Core Maintainer review: - Unpacking a server-supplied archive is a large host-side attack surface (decompression bombs, path traversal, link escapes, normalization collisions, setuid bits, special-file entries), and every host would have to defend it correctly. - It gave servers two ways to express the same skill, so hosts had to support both forms to interoperate, and an archive-only skill was unreadable to a host that implemented individual-file reads alone. With archives gone, `url` and `digest` become unconditionally required on an index entry, so the "url, archives, or both" footnote is dropped. Rename the "Direct entries" subsection to "Skill URLs", since it no longer contrasts with an archive form. Record the removal and its reasoning in a new "Appendix: Deferred Features" so a future proposal starts from the objections. No-Verification-Needed: doc-only change with no runtime surface --- docs/seps/2640-skills-extension.mdx | 107 ++++++++-------------------- seps/2640-skills-extension.md | 107 ++++++++-------------------- 2 files changed, 62 insertions(+), 152 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index 143419234..35aae9145 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -131,33 +131,16 @@ A server SHOULD expose a resource at the well-known URI `skill://index.json` who "name": "refunds", "description": "Process customer refund requests per company policy", "license": "Apache-2.0" - }, - "archives": [ - { - "url": "skill://acme/billing/refunds.tar.gz", - "mimeType": "application/gzip", - "digest": "sha256:c4d5e6f7..." - } - ] + } }, { + "url": "skill://pdf-processing/SKILL.md", + "digest": "sha256:d5e6f7a8...", "frontmatter": { "name": "pdf-processing", "description": "Extract, fill, and assemble PDF documents", "metadata": { "version": "2.1.0" } - }, - "archives": [ - { - "url": "skill://pdf-processing.tar.gz", - "mimeType": "application/gzip", - "digest": "sha256:d5e6f7a8..." - }, - { - "url": "skill://pdf-processing.zip", - "mimeType": "application/zip", - "digest": "sha256:e6f7a8b9..." - } - ] + } } ] } @@ -165,18 +148,12 @@ A server SHOULD expose a resource at the well-known URI `skill://index.json` who Index 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[].url` | No¹ | Resource URI of the skill's `SKILL.md`. See [Direct entries](#direct-entries). | -| `skills[].digest` | With `url` | SHA-256 digest of the `SKILL.md` file. MUST be present when `url` is present, absent otherwise. See [Integrity](#integrity-and-verification). | -| `skills[].archives` | No¹ | Array of archive forms of the skill. See [Archives](#archives). | -| `skills[].archives[].url` | Yes | Resource URI of the archive. | -| `skills[].archives[].mimeType` | Yes | The archive format's media type (e.g., `application/gzip`, `application/zip`). | -| `skills[].archives[].digest` | Yes | SHA-256 digest of the archive file. See [Integrity](#integrity-and-verification). | - -¹ Every entry MUST include `url`, a non-empty `archives`, or both. +| 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[].url` | Yes | Resource URI of the skill's `SKILL.md`. See [Skill URLs](#skill-urls). | +| `skills[].digest` | Yes | SHA-256 digest of the `SKILL.md` file. See [Integrity](#integrity-and-verification). | Clients SHOULD ignore unrecognized fields. The index carries no version marker: the format is versioned by the extension itself, and an incompatible revision would be negotiated through the [capability declaration](#capability-declaration). Tolerating unrecognized fields is what allows the format to grow additively. @@ -184,45 +161,17 @@ Clients SHOULD ignore unrecognized fields. The index carries no version marker: `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 a single read of the index, without fetching each `SKILL.md`. -The `frontmatter` object MUST be identical in content to the frontmatter of the `SKILL.md` it describes. When `url` is present, the final `` segment of the URI MUST equal `frontmatter.name`, per [Resource Mapping](#resource-mapping). +The `frontmatter` object MUST be identical in content to the frontmatter of the `SKILL.md` it describes. The final `` segment of the entry's `url` 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. -##### Direct entries - -When present, `url` 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). - -##### Archives - -`archives` lists pre-packed forms of the entire skill directory. Each archive is a single resource whose content is the complete skill directory in an archive format; reading it via `resources/read` retrieves the skill — `SKILL.md` and all supporting files — in one round trip. When multiple archives are listed, they are alternative encodings of identical content; a host picks one whose format it supports, determined from `mimeType`. - -`mimeType` MUST identify an archive format. Hosts SHOULD support at least gzip-compressed tar (`application/gzip`, conventionally `.tar.gz`) and ZIP (`application/zip`); servers MAY offer additional formats but SHOULD include at least one of these two so that any conforming host can retrieve the skill. - -Archive layout: - -- `SKILL.md` MUST be at the archive root, not nested inside a wrapper directory. -- Entry paths MUST use `/` as the separator and MUST be relative — no path-traversal sequences (`..`) and no absolute paths. - -Archive contents represent the skill directory directly. When an entry carries both `url` and `archives`, each archive MUST unpack to the same files that are individually addressable under the skill path — a host may fetch the skill either way and observes the same content. When an entry is archive-only, the skill's files are not individually addressable on the server; the host unpacks the archive locally and addresses its contents under `skill:///`, where `` is `frontmatter.name`. In both cases the archive `url` itself carries no path semantics — it is a download location, nothing more. - -Compared to individual-file distribution, an archive delivers a multi-file skill atomically and can carry UNIX file metadata (executable bits, symlinks) that individual resources cannot represent — robustly for `.tar.gz`, variably for `.zip`. - -Archives are an unpacking attack surface. Hosts unpacking an archive MUST: +##### Skill URLs -1. Reject entries with path-traversal sequences or absolute paths. -2. Reject symlinks or hard links that resolve outside the skill directory. -3. Extract only regular files, directories, and symlinks, rejecting device nodes and any other special-file entry type. -4. Reject archives in which any two entry paths are equal after Unicode normalization and case folding, as a case/normalization collision can silently overwrite `SKILL.md` on case-insensitive or normalizing filesystems. -5. Clear setuid/setgid/sticky mode bits on every extracted entry and extract all entries as the host process's own user and group, ignoring uid/gid fields recorded in the archive. -6. Enforce limits on total unpacked size, total entry count, and maximum path depth, applied both per archive and as a cumulative per-server budget. Once the per-server budget is exhausted, hosts MUST refuse to unpack further archives from that server. - -Hosts SHOULD use existing hardened libraries for handling archives, specific to their language and platform rather than reimplement them. - -Digest verification ([Integrity and verification](#integrity-and-verification)) covers the archive bytes at retrieval only, not the unpacked tree thereafter. Hosts MUST unpack 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, and MUST keep the unpacked tree immutable for the cached skill's lifetime. +`url` 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). ##### 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. For a skill entry's `digest`, the artifact is the `SKILL.md` file; for an archive's `digest`, the artifact is the archive file. +Digests are SHA-256 hashes of an artifact's raw bytes, formatted as `sha256:{hex}` where `{hex}` is 64 lowercase hexadecimal characters. A skill entry's `digest` covers its `SKILL.md` file. The index carries no digest for a skill's supporting files; they are ordinary resources, outside the integrity mechanism this extension defines. Hosts MUST verify retrieved content against the `digest` in the index. A mismatch indicates the content is corrupted or tampered with — hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. @@ -230,8 +179,6 @@ Digests are unsigned and supplied by the same server that supplies the content. After fetching and digest-verifying a `SKILL.md` whose entry appeared in the index, 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 index is what the model actually receives. -A host that retrieves a skill via an archive and verifies the archive digest MUST thereafter serve every read of that skill's files from the locally unpacked archive for the lifetime of that verification. It MUST NOT satisfy those reads by issuing live `resources/read` calls against the server, because the archive digest is the only integrity check covering supporting files. A malicious server can return different bytes live than the verified archive contained. - The `skill://index.json` resource is served via `resources/read` like any other resource, with `mimeType` of `application/json`. A server MAY also surface it in `resources/list` so clients can detect its presence, but clients MAY attempt to read it directly without prior discovery. A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY decline to expose `skill://index.json`, or MAY expose a partial index. Hosts MUST NOT treat an absent or empty index as proof that a server has no skills. @@ -456,12 +403,6 @@ The baseline is therefore direct readability — a skill URI is always a valid a The Agent Skills specification already defines YAML frontmatter fields, naming rules, directory conventions, and the progressive-disclosure model. It has its own governance, contributing process, and multi-vendor participation. Redefining any of this in an MCP SEP would create a second source of truth and a drift risk. This SEP is a transport binding; the payload format is someone else's concern. -### Why Permit Archive Distribution? - -An earlier draft excluded archives on the basis that supporting files are already individually addressable as resources. The Working Group [reversed this](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-04-19-archives-permitted-as-server-side-packaging-optimization) for three reasons: individual-file distribution cannot guarantee atomicity across a multi-file skill read; hosts that pre-materialize skills to a local filesystem pay N+1 round trips; and UNIX file metadata (executable bits, symlinks) has no representation when files are served individually. - -Attaching archives to a skill entry as alternative retrieval forms — rather than as a separate kind of skill — preserves the virtual-filesystem model: the post-unpack view is identical to individual-file distribution. The property given up is per-file `resources/subscribe` granularity, which is acceptable because subscription is not part of the skill reading model defined here. - ### Why a Directory Read Method? The virtual-filesystem model this SEP builds on had a read operation (`resources/read`) but no readdir. That gap is harmless while skill instructions name files explicitly, but skills routinely defer the choice to the agent — "use the template in `templates/` that matches the document type." On a filesystem the agent lists the directory; over MCP the only enumeration was `resources/list`, which is global: it returns the server's entire resource space, cannot be scoped to a subtree, and is precisely what large or generative servers — the ones that motivated optional enumeration — decline to implement. A server that cannot enumerate its catalog can still trivially enumerate one directory it is already serving. @@ -494,10 +435,9 @@ Skill content is instructional text delivered to a model, which makes it a promp - **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. - **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. - **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. -- **Archive safety.** Archive-distributed skills are an unpacking attack surface. Hosts MUST validate archives per the requirements in [Archives](#archives): reject path traversal and absolute paths, reject links resolving outside the skill directory, and bound total unpacked size to prevent decompression bombs. - **Digests are not a security boundary.** Index digests are unsigned and come from the same server as the content. They confirm consistency between the index 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 `digest` of the `SKILL.md` (and the archive `digest`, if present) observed at the moment of approval. If a subsequent index advertises a different digest under the same skill name or URI, the host MUST treat the prior approval as revoked and re-prompt before loading or executing. Digest verification ([Integrity and verification](#integrity-and-verification)) defends only against transport tampering - it cannot defend against the originating server rotating content, because the server authors both the index and the body. -- **Cache isolation and durable origin.** Hosts that unpack or cache MCP-served skill content on disk MUST 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. Unpacked 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. +- **Content-bound approval.** When a host persists any per-skill user approval, it MUST be bound to the `digest` of the `SKILL.md` observed at the moment of approval. If a subsequent index advertises a different digest under the same skill name or URI, the host MUST treat the prior approval as revoked and re-prompt before loading or executing. Digest verification ([Integrity and verification](#integrity-and-verification)) defends only against transport tampering - it cannot defend against the originating server rotating content, because the server authors both the index and the body. +- **Cache isolation and durable origin.** Hosts that cache MCP-served skill content on disk MUST 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 @@ -519,6 +459,21 @@ Per [SEP-2133], an Extensions Track SEP requires at least one reference implemen - 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 `url`. 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. 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. + ## References - [Agent Skills specification](https://agentskills.io/specification) diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index bd2c2cdda..8e3c83bd9 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -119,33 +119,16 @@ A server SHOULD expose a resource at the well-known URI `skill://index.json` who "name": "refunds", "description": "Process customer refund requests per company policy", "license": "Apache-2.0" - }, - "archives": [ - { - "url": "skill://acme/billing/refunds.tar.gz", - "mimeType": "application/gzip", - "digest": "sha256:c4d5e6f7..." - } - ] + } }, { + "url": "skill://pdf-processing/SKILL.md", + "digest": "sha256:d5e6f7a8...", "frontmatter": { "name": "pdf-processing", "description": "Extract, fill, and assemble PDF documents", "metadata": { "version": "2.1.0" } - }, - "archives": [ - { - "url": "skill://pdf-processing.tar.gz", - "mimeType": "application/gzip", - "digest": "sha256:d5e6f7a8..." - }, - { - "url": "skill://pdf-processing.zip", - "mimeType": "application/zip", - "digest": "sha256:e6f7a8b9..." - } - ] + } } ] } @@ -153,18 +136,12 @@ A server SHOULD expose a resource at the well-known URI `skill://index.json` who Index 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[].url` | No¹ | Resource URI of the skill's `SKILL.md`. See [Direct entries](#direct-entries). | -| `skills[].digest` | With `url` | SHA-256 digest of the `SKILL.md` file. MUST be present when `url` is present, absent otherwise. See [Integrity](#integrity-and-verification). | -| `skills[].archives` | No¹ | Array of archive forms of the skill. See [Archives](#archives). | -| `skills[].archives[].url` | Yes | Resource URI of the archive. | -| `skills[].archives[].mimeType` | Yes | The archive format's media type (e.g., `application/gzip`, `application/zip`). | -| `skills[].archives[].digest` | Yes | SHA-256 digest of the archive file. See [Integrity](#integrity-and-verification). | - -¹ Every entry MUST include `url`, a non-empty `archives`, or both. +| 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[].url` | Yes | Resource URI of the skill's `SKILL.md`. See [Skill URLs](#skill-urls). | +| `skills[].digest` | Yes | SHA-256 digest of the `SKILL.md` file. See [Integrity](#integrity-and-verification). | Clients SHOULD ignore unrecognized fields. The index carries no version marker: the format is versioned by the extension itself, and an incompatible revision would be negotiated through the [capability declaration](#capability-declaration). Tolerating unrecognized fields is what allows the format to grow additively. @@ -172,45 +149,17 @@ Clients SHOULD ignore unrecognized fields. The index carries no version marker: `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 a single read of the index, without fetching each `SKILL.md`. -The `frontmatter` object MUST be identical in content to the frontmatter of the `SKILL.md` it describes. When `url` is present, the final `` segment of the URI MUST equal `frontmatter.name`, per [Resource Mapping](#resource-mapping). +The `frontmatter` object MUST be identical in content to the frontmatter of the `SKILL.md` it describes. The final `` segment of the entry's `url` 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. -##### Direct entries - -When present, `url` 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). - -##### Archives - -`archives` lists pre-packed forms of the entire skill directory. Each archive is a single resource whose content is the complete skill directory in an archive format; reading it via `resources/read` retrieves the skill — `SKILL.md` and all supporting files — in one round trip. When multiple archives are listed, they are alternative encodings of identical content; a host picks one whose format it supports, determined from `mimeType`. - -`mimeType` MUST identify an archive format. Hosts SHOULD support at least gzip-compressed tar (`application/gzip`, conventionally `.tar.gz`) and ZIP (`application/zip`); servers MAY offer additional formats but SHOULD include at least one of these two so that any conforming host can retrieve the skill. - -Archive layout: - -- `SKILL.md` MUST be at the archive root, not nested inside a wrapper directory. -- Entry paths MUST use `/` as the separator and MUST be relative — no path-traversal sequences (`..`) and no absolute paths. - -Archive contents represent the skill directory directly. When an entry carries both `url` and `archives`, each archive MUST unpack to the same files that are individually addressable under the skill path — a host may fetch the skill either way and observes the same content. When an entry is archive-only, the skill's files are not individually addressable on the server; the host unpacks the archive locally and addresses its contents under `skill:///`, where `` is `frontmatter.name`. In both cases the archive `url` itself carries no path semantics — it is a download location, nothing more. - -Compared to individual-file distribution, an archive delivers a multi-file skill atomically and can carry UNIX file metadata (executable bits, symlinks) that individual resources cannot represent — robustly for `.tar.gz`, variably for `.zip`. - -Archives are an unpacking attack surface. Hosts unpacking an archive MUST: +##### Skill URLs -1. Reject entries with path-traversal sequences or absolute paths. -2. Reject symlinks or hard links that resolve outside the skill directory. -3. Extract only regular files, directories, and symlinks, rejecting device nodes and any other special-file entry type. -4. Reject archives in which any two entry paths are equal after Unicode normalization and case folding, as a case/normalization collision can silently overwrite `SKILL.md` on case-insensitive or normalizing filesystems. -5. Clear setuid/setgid/sticky mode bits on every extracted entry and extract all entries as the host process's own user and group, ignoring uid/gid fields recorded in the archive. -6. Enforce limits on total unpacked size, total entry count, and maximum path depth, applied both per archive and as a cumulative per-server budget. Once the per-server budget is exhausted, hosts MUST refuse to unpack further archives from that server. - -Hosts SHOULD use existing hardened libraries for handling archives, specific to their language and platform rather than reimplement them. - -Digest verification ([Integrity and verification](#integrity-and-verification)) covers the archive bytes at retrieval only, not the unpacked tree thereafter. Hosts MUST unpack 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, and MUST keep the unpacked tree immutable for the cached skill's lifetime. +`url` 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). ##### 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. For a skill entry's `digest`, the artifact is the `SKILL.md` file; for an archive's `digest`, the artifact is the archive file. +Digests are SHA-256 hashes of an artifact's raw bytes, formatted as `sha256:{hex}` where `{hex}` is 64 lowercase hexadecimal characters. A skill entry's `digest` covers its `SKILL.md` file. The index carries no digest for a skill's supporting files; they are ordinary resources, outside the integrity mechanism this extension defines. Hosts MUST verify retrieved content against the `digest` in the index. A mismatch indicates the content is corrupted or tampered with — hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. @@ -218,8 +167,6 @@ Digests are unsigned and supplied by the same server that supplies the content. After fetching and digest-verifying a `SKILL.md` whose entry appeared in the index, 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 index is what the model actually receives. -A host that retrieves a skill via an archive and verifies the archive digest MUST thereafter serve every read of that skill's files from the locally unpacked archive for the lifetime of that verification. It MUST NOT satisfy those reads by issuing live `resources/read` calls against the server, because the archive digest is the only integrity check covering supporting files. A malicious server can return different bytes live than the verified archive contained. - The `skill://index.json` resource is served via `resources/read` like any other resource, with `mimeType` of `application/json`. A server MAY also surface it in `resources/list` so clients can detect its presence, but clients MAY attempt to read it directly without prior discovery. A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY decline to expose `skill://index.json`, or MAY expose a partial index. Hosts MUST NOT treat an absent or empty index as proof that a server has no skills. @@ -444,12 +391,6 @@ The baseline is therefore direct readability — a skill URI is always a valid a The Agent Skills specification already defines YAML frontmatter fields, naming rules, directory conventions, and the progressive-disclosure model. It has its own governance, contributing process, and multi-vendor participation. Redefining any of this in an MCP SEP would create a second source of truth and a drift risk. This SEP is a transport binding; the payload format is someone else's concern. -### Why Permit Archive Distribution? - -An earlier draft excluded archives on the basis that supporting files are already individually addressable as resources. The Working Group [reversed this](https://github.com/modelcontextprotocol/experimental-ext-skills/blob/main/docs/decisions.md#2026-04-19-archives-permitted-as-server-side-packaging-optimization) for three reasons: individual-file distribution cannot guarantee atomicity across a multi-file skill read; hosts that pre-materialize skills to a local filesystem pay N+1 round trips; and UNIX file metadata (executable bits, symlinks) has no representation when files are served individually. - -Attaching archives to a skill entry as alternative retrieval forms — rather than as a separate kind of skill — preserves the virtual-filesystem model: the post-unpack view is identical to individual-file distribution. The property given up is per-file `resources/subscribe` granularity, which is acceptable because subscription is not part of the skill reading model defined here. - ### Why a Directory Read Method? The virtual-filesystem model this SEP builds on had a read operation (`resources/read`) but no readdir. That gap is harmless while skill instructions name files explicitly, but skills routinely defer the choice to the agent — "use the template in `templates/` that matches the document type." On a filesystem the agent lists the directory; over MCP the only enumeration was `resources/list`, which is global: it returns the server's entire resource space, cannot be scoped to a subtree, and is precisely what large or generative servers — the ones that motivated optional enumeration — decline to implement. A server that cannot enumerate its catalog can still trivially enumerate one directory it is already serving. @@ -482,10 +423,9 @@ Skill content is instructional text delivered to a model, which makes it a promp - **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. - **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. - **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. -- **Archive safety.** Archive-distributed skills are an unpacking attack surface. Hosts MUST validate archives per the requirements in [Archives](#archives): reject path traversal and absolute paths, reject links resolving outside the skill directory, and bound total unpacked size to prevent decompression bombs. - **Digests are not a security boundary.** Index digests are unsigned and come from the same server as the content. They confirm consistency between the index 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 `digest` of the `SKILL.md` (and the archive `digest`, if present) observed at the moment of approval. If a subsequent index advertises a different digest under the same skill name or URI, the host MUST treat the prior approval as revoked and re-prompt before loading or executing. Digest verification ([Integrity and verification](#integrity-and-verification)) defends only against transport tampering - it cannot defend against the originating server rotating content, because the server authors both the index and the body. -- **Cache isolation and durable origin.** Hosts that unpack or cache MCP-served skill content on disk MUST 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. Unpacked 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. +- **Content-bound approval.** When a host persists any per-skill user approval, it MUST be bound to the `digest` of the `SKILL.md` observed at the moment of approval. If a subsequent index advertises a different digest under the same skill name or URI, the host MUST treat the prior approval as revoked and re-prompt before loading or executing. Digest verification ([Integrity and verification](#integrity-and-verification)) defends only against transport tampering - it cannot defend against the originating server rotating content, because the server authors both the index and the body. +- **Cache isolation and durable origin.** Hosts that cache MCP-served skill content on disk MUST 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 @@ -507,6 +447,21 @@ Per [SEP-2133], an Extensions Track SEP requires at least one reference implemen - 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 `url`. 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. 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. + ## References - [Agent Skills specification](https://agentskills.io/specification) From 2971db76fe113d486a1dff4ae0b068ab53e89d94 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Wed, 8 Jul 2026 13:55:22 +0100 Subject: [PATCH 23/25] Make skill URI schemes non-privileged Skills may be served under any URI scheme. Three things in Resource Mapping contradicted that: - Serving skills under a non-skill:// scheme was conditional on listing every skill in the index, a requirement skill:// servers did not have. Drop the condition; the index is equally optional under all schemes. - Hosts were told to "recognize skills by the skill:// scheme prefix" outside the index, keying skill identity off the scheme. Replace it with the rule the rest of the document already assumes: a host learns a resource is a skill from an index entry or from an explicit reference, for every scheme alike. - The relative-reference rule spoke of "the skill:// scheme root". The index stays at the well-known skill://index.json for every scheme, since it is a discovery resource rather than a skill, and one fixed location keeps discovery uniform. The no-nesting constraint is untouched and still applies regardless of scheme; relaxing it is a separate change. No-Verification-Needed: doc-only change with no runtime surface --- docs/seps/2640-skills-extension.mdx | 8 +++++--- seps/2640-skills-extension.md | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index 35aae9145..433324b7c 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -81,7 +81,9 @@ Further constraints: Per RFC 3986, the first segment of `` 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 instead serve skills under another scheme native to its domain (e.g., `github://owner/repo/skills/refunds/SKILL.md`), provided each skill is listed in the [`skill://index.json`](#enumeration-via-skillindexjson) resource. The index is the authoritative record of which resources are skills; outside the index, hosts recognize skills by the `skill://` scheme prefix. The structural constraints above — `` ending in the skill name, `SKILL.md` explicit in the URI, no nesting — apply regardless of scheme. +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 — `` ending in the skill name, `SKILL.md` explicit in the URI, no nesting — apply regardless of scheme, and the [`skill://index.json`](#enumeration-via-skillindexjson) resource is equally optional under all of them. + +Skill identity does not depend on the scheme. A host learns that a resource is a skill in one of two ways: from an entry in [`skill://index.json`](#enumeration-via-skillindexjson), which is the authoritative record of the skills a server publishes; or from an explicit reference — the server's `instructions` field, another skill, or the user. 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 @@ -183,7 +185,7 @@ The `skill://index.json` resource is served via `resources/read` like any other A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY decline to expose `skill://index.json`, or MAY expose a partial index. Hosts MUST NOT treat an absent or empty index as proof that a server has no skills. -The URI `skill://index.json` is reserved and does not conflict with any valid ``: skill names may contain only lowercase letters, digits, and hyphens, so `index.json` cannot be a skill name. +The URI `skill://index.json` is reserved and does not conflict with any valid ``: skill names may contain only lowercase letters, digits, and hyphens, so `index.json` cannot be a skill name. A server serves its index at this URI whatever scheme its skills use — the index is a discovery resource, not a skill, so a single well-known location keeps discovery uniform across schemes. #### Pointer from Server Instructions @@ -217,7 +219,7 @@ An empty object indicates support for the extension with no optional features. C Skill files are read via the standard `resources/read` method. No skill-specific read semantics are defined. -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 `skill://` scheme root. +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. ### Directory Listing diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index 8e3c83bd9..18f8db118 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -69,7 +69,9 @@ Further constraints: Per RFC 3986, the first segment of `` 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 instead serve skills under another scheme native to its domain (e.g., `github://owner/repo/skills/refunds/SKILL.md`), provided each skill is listed in the [`skill://index.json`](#enumeration-via-skillindexjson) resource. The index is the authoritative record of which resources are skills; outside the index, hosts recognize skills by the `skill://` scheme prefix. The structural constraints above — `` ending in the skill name, `SKILL.md` explicit in the URI, no nesting — apply regardless of scheme. +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 — `` ending in the skill name, `SKILL.md` explicit in the URI, no nesting — apply regardless of scheme, and the [`skill://index.json`](#enumeration-via-skillindexjson) resource is equally optional under all of them. + +Skill identity does not depend on the scheme. A host learns that a resource is a skill in one of two ways: from an entry in [`skill://index.json`](#enumeration-via-skillindexjson), which is the authoritative record of the skills a server publishes; or from an explicit reference — the server's `instructions` field, another skill, or the user. 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 @@ -171,7 +173,7 @@ The `skill://index.json` resource is served via `resources/read` like any other A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY decline to expose `skill://index.json`, or MAY expose a partial index. Hosts MUST NOT treat an absent or empty index as proof that a server has no skills. -The URI `skill://index.json` is reserved and does not conflict with any valid ``: skill names may contain only lowercase letters, digits, and hyphens, so `index.json` cannot be a skill name. +The URI `skill://index.json` is reserved and does not conflict with any valid ``: skill names may contain only lowercase letters, digits, and hyphens, so `index.json` cannot be a skill name. A server serves its index at this URI whatever scheme its skills use — the index is a discovery resource, not a skill, so a single well-known location keeps discovery uniform across schemes. #### Pointer from Server Instructions @@ -205,7 +207,7 @@ An empty object indicates support for the extension with no optional features. C Skill files are read via the standard `resources/read` method. No skill-specific read semantics are defined. -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 `skill://` scheme root. +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. ### Directory Listing From 100c5a40011fe4591a38a3eb3f4ac6d5d424d4c2 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Wed, 8 Jul 2026 15:29:55 +0100 Subject: [PATCH 24/25] Allow nested skills, gated on fresh activation consent Replace the ban on a SKILL.md appearing in a descendant directory of a skill with defined nesting semantics, per review discussion: - Nested content is supporting content: read through the enclosing skill, a nested SKILL.md is ordinary markdown and hosts MUST NOT act on its frontmatter. - Activation requires fresh consent: approval is per skill, and activating a nested skill - through the host's skill-loading machinery or by giving effect to its frontmatter - requires explicit user consent; approval of the enclosing skill does not substitute. - Publication is flat: a nested skill is published through its own index entry or explicit reference, and nothing in the index marks nesting. Clarifications that follow: relative references in any SKILL.md resolve against its own directory regardless of how the file was reached, and the digest-verification requirement is scoped to content retrieved via its index entry. Security Implications gains a nested-skill-consent bullet, and the no-implicit-permission-grants bullet states that a nested skill's allowed-tools has no effect unless that skill is activated under its own approval. No-Verification-Needed: doc-only change with no runtime surface --- docs/seps/2640-skills-extension.mdx | 19 ++++++++++++++----- seps/2640-skills-extension.md | 19 ++++++++++++++----- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index 433324b7c..ca9c01786 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -76,12 +76,12 @@ The final segment of `` MUST equal the skill's `name` as declared in Further constraints: -- A `SKILL.md` MUST NOT appear in any descendant directory of a skill. The skill directory is the boundary; skills do not nest inside other skills. +- A `SKILL.md` MAY appear in a descendant directory of a skill — skills can nest. See [Nested skills](#nested-skills). - The final `` segment, being the skill `name`, MUST satisfy the Agent Skills specification's naming rules. Prefix segments SHOULD be valid URI path segments per [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986); no further constraints are imposed on them. Per RFC 3986, the first segment of `` 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 — `` ending in the skill name, `SKILL.md` explicit in the URI, no nesting — apply regardless of scheme, and the [`skill://index.json`](#enumeration-via-skillindexjson) resource is equally optional under all of them. +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 — `` ending in the skill name, `SKILL.md` explicit in the URI — apply regardless of scheme, and the [`skill://index.json`](#enumeration-via-skillindexjson) resource is equally optional under all of them. Skill identity does not depend on the scheme. A host learns that a resource is a skill in one of two ways: from an entry in [`skill://index.json`](#enumeration-via-skillindexjson), which is the authoritative record of the skills a server publishes; or from an explicit reference — the server's `instructions` field, another skill, or the user. 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. @@ -105,6 +105,14 @@ For each `skill:///SKILL.md` resource: 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 entry in [`skill://index.json`](#enumeration-via-skillindexjson), or by explicit reference. The index remains a flat list — an entry for a nested skill is an ordinary entry whose `url` happens to share a path prefix with the enclosing skill's, and nothing in the index 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 index, 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. @@ -175,7 +183,7 @@ Within the frontmatter `metadata` object, keys prefixed with `io.modelcontextpro Digests are SHA-256 hashes of an artifact's raw bytes, formatted as `sha256:{hex}` where `{hex}` is 64 lowercase hexadecimal characters. A skill entry's `digest` covers its `SKILL.md` file. The index carries no digest for a skill's supporting files; they are ordinary resources, outside the integrity mechanism this extension defines. -Hosts MUST verify retrieved content against the `digest` in the index. A mismatch indicates the content is corrupted or tampered with — hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. +When a host retrieves a `SKILL.md` via its index entry, it MUST verify the content against the entry's `digest`. A mismatch indicates the content is corrupted or tampered with — hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. 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 index and the content together. Hosts MUST NOT treat a digest match as a security boundary. @@ -219,7 +227,7 @@ An empty object indicates support for the extension with no optional features. C Skill files are read via the standard `resources/read` method. No skill-specific read semantics are defined. -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. +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 @@ -434,8 +442,9 @@ Skill content is instructional text delivered to a model, which makes it a promp - **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. -- **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. +- **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.** Index digests are unsigned and come from the same server as the content. They confirm consistency between the index 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 `digest` of the `SKILL.md` observed at the moment of approval. If a subsequent index advertises a different digest under the same skill name or URI, the host MUST treat the prior approval as revoked and re-prompt before loading or executing. Digest verification ([Integrity and verification](#integrity-and-verification)) defends only against transport tampering - it cannot defend against the originating server rotating content, because the server authors both the index and the body. diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index 18f8db118..bfb708bcd 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -64,12 +64,12 @@ The final segment of `` MUST equal the skill's `name` as declared in Further constraints: -- A `SKILL.md` MUST NOT appear in any descendant directory of a skill. The skill directory is the boundary; skills do not nest inside other skills. +- A `SKILL.md` MAY appear in a descendant directory of a skill — skills can nest. See [Nested skills](#nested-skills). - The final `` segment, being the skill `name`, MUST satisfy the Agent Skills specification's naming rules. Prefix segments SHOULD be valid URI path segments per [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986); no further constraints are imposed on them. Per RFC 3986, the first segment of `` 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 — `` ending in the skill name, `SKILL.md` explicit in the URI, no nesting — apply regardless of scheme, and the [`skill://index.json`](#enumeration-via-skillindexjson) resource is equally optional under all of them. +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 — `` ending in the skill name, `SKILL.md` explicit in the URI — apply regardless of scheme, and the [`skill://index.json`](#enumeration-via-skillindexjson) resource is equally optional under all of them. Skill identity does not depend on the scheme. A host learns that a resource is a skill in one of two ways: from an entry in [`skill://index.json`](#enumeration-via-skillindexjson), which is the authoritative record of the skills a server publishes; or from an explicit reference — the server's `instructions` field, another skill, or the user. 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. @@ -93,6 +93,14 @@ For each `skill:///SKILL.md` resource: 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 entry in [`skill://index.json`](#enumeration-via-skillindexjson), or by explicit reference. The index remains a flat list — an entry for a nested skill is an ordinary entry whose `url` happens to share a path prefix with the enclosing skill's, and nothing in the index 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 index, 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. @@ -163,7 +171,7 @@ Within the frontmatter `metadata` object, keys prefixed with `io.modelcontextpro Digests are SHA-256 hashes of an artifact's raw bytes, formatted as `sha256:{hex}` where `{hex}` is 64 lowercase hexadecimal characters. A skill entry's `digest` covers its `SKILL.md` file. The index carries no digest for a skill's supporting files; they are ordinary resources, outside the integrity mechanism this extension defines. -Hosts MUST verify retrieved content against the `digest` in the index. A mismatch indicates the content is corrupted or tampered with — hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. +When a host retrieves a `SKILL.md` via its index entry, it MUST verify the content against the entry's `digest`. A mismatch indicates the content is corrupted or tampered with — hosts MUST NOT use unverified content. Digests also enable efficient caching: compare a skill's `digest` against a locally cached value to determine whether the artifact has changed without re-reading it. 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 index and the content together. Hosts MUST NOT treat a digest match as a security boundary. @@ -207,7 +215,7 @@ An empty object indicates support for the extension with no optional features. C Skill files are read via the standard `resources/read` method. No skill-specific read semantics are defined. -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. +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 @@ -422,8 +430,9 @@ Skill content is instructional text delivered to a model, which makes it a promp - **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. -- **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. +- **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.** Index digests are unsigned and come from the same server as the content. They confirm consistency between the index 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 `digest` of the `SKILL.md` observed at the moment of approval. If a subsequent index advertises a different digest under the same skill name or URI, the host MUST treat the prior approval as revoked and re-prompt before loading or executing. Digest verification ([Integrity and verification](#integrity-and-verification)) defends only against transport tampering - it cannot defend against the originating server rotating content, because the server authors both the index and the body. From 8b97c28d25c09a0f5acb585d607243ec26d36c37 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Wed, 8 Jul 2026 15:49:45 +0100 Subject: [PATCH 25/25] Specify skill name collision handling PR review surfaced that duplicate skill names are legal but unaddressed: two skills at different paths can share a name, nesting makes that likelier, existing harnesses resolve same-name collisions five different ways, and published-skill scans show name-squatting as a live impersonation pattern. Add a Names subsection to the index section: a skill's name is a label, not an identifier - a skill is identified by its url. Names SHOULD be unique within an index; hosts MUST NOT assume they are. Intra-index collisions are disambiguated rather than silently dropped, and names resolve within per-origin namespaces, so an MCP-served skill can never silently shadow a same-named skill from another server or from the host's own filesystem. A matching Security Implications bullet records the impersonation risk, requires host-assigned server labels rather than the self-reported serverInfo.name, and notes that intermediaries may attach provenance annotations via _meta under their own prefix, with no semantics assigned by this extension. Align the host integration guidelines: the illustrative registry keys by origin plus name and qualifies collisions, and the section no longer describes MCP-served skills as indistinguishable from filesystem skills, which contradicted the origin-visibility requirement in Security Implications. No-Verification-Needed: doc-only change with no runtime surface --- docs/seps/2640-skills-extension.mdx | 13 +++++++++---- seps/2640-skills-extension.md | 13 +++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/docs/seps/2640-skills-extension.mdx b/docs/seps/2640-skills-extension.mdx index ca9c01786..3bcd3b6a8 100644 --- a/docs/seps/2640-skills-extension.mdx +++ b/docs/seps/2640-skills-extension.mdx @@ -167,6 +167,10 @@ Index fields: Clients SHOULD ignore unrecognized fields. The index carries no version marker: the format is versioned by the extension itself, and an incompatible revision would be negotiated through the [capability declaration](#capability-declaration). Tolerating unrecognized fields is what allows the format to grow additively. +##### Names + +A skill's `name` is a label, not an identifier — a skill is identified by its `url`. Within an index, 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 index 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 a single read of the index, without fetching each `SKILL.md`. @@ -295,9 +299,9 @@ The following are recommendations for interoperable implementations. They are no ### 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, from the model's perspective, an MCP-served skill is indistinguishable from a filesystem skill. +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 `skill://index.json` from each connected MCP server that declares the `io.modelcontextprotocol/skills` extension. Each registry entry records the skill's `name` and `description` (from the index 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. Origin is host bookkeeping; it is not exposed to the model. +**Registry.** At startup and on connection change, the host assembles a single internal skill registry from every origin it supports: filesystem skill directories, and `skill://index.json` from each connected MCP server that declares the `io.modelcontextprotocol/skills` extension. Each registry entry records the skill's `name` and `description` (from the index 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. 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. @@ -317,7 +321,7 @@ This section sketches one way a host might wire MCP-served skills into an existi } ``` -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. The model neither knows nor cares which path was taken. Hosts that already expose a name-keyed skill-loading tool for filesystem skills extend it rather than introducing a parallel one. +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. 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: @@ -401,7 +405,7 @@ Earlier drafts required `` to be a single segment equal to the front A subsequent draft went further and fully decoupled the path from the name. That was too loose: a URI like `skill://a/b/c/SKILL.md` tells you nothing about what the skill is called until you fetch and parse frontmatter. Clients listing skills, hosts displaying them in a picker, and models reasoning over URIs all want the name visible without a round trip. -Constraining the final segment to match the frontmatter `name` gets both properties. The prefix carries the server's organizational structure; the final segment carries the skill's identity; and the two together form a locator from which the name can be read directly. +Constraining the final segment to match the frontmatter `name` gets both properties. The prefix carries the server's organizational structure; the final segment carries the skill's name; and the two together form a locator from which the name can be read directly. ### Why Is Enumeration Optional? @@ -442,6 +446,7 @@ Skill content is instructional text delivered to a model, which makes it a promp - **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. diff --git a/seps/2640-skills-extension.md b/seps/2640-skills-extension.md index bfb708bcd..9912a3b0a 100644 --- a/seps/2640-skills-extension.md +++ b/seps/2640-skills-extension.md @@ -155,6 +155,10 @@ Index fields: Clients SHOULD ignore unrecognized fields. The index carries no version marker: the format is versioned by the extension itself, and an incompatible revision would be negotiated through the [capability declaration](#capability-declaration). Tolerating unrecognized fields is what allows the format to grow additively. +##### Names + +A skill's `name` is a label, not an identifier — a skill is identified by its `url`. Within an index, 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 index 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 a single read of the index, without fetching each `SKILL.md`. @@ -283,9 +287,9 @@ The following are recommendations for interoperable implementations. They are no ### 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, from the model's perspective, an MCP-served skill is indistinguishable from a filesystem skill. +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 `skill://index.json` from each connected MCP server that declares the `io.modelcontextprotocol/skills` extension. Each registry entry records the skill's `name` and `description` (from the index 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. Origin is host bookkeeping; it is not exposed to the model. +**Registry.** At startup and on connection change, the host assembles a single internal skill registry from every origin it supports: filesystem skill directories, and `skill://index.json` from each connected MCP server that declares the `io.modelcontextprotocol/skills` extension. Each registry entry records the skill's `name` and `description` (from the index 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. 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. @@ -305,7 +309,7 @@ This section sketches one way a host might wire MCP-served skills into an existi } ``` -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. The model neither knows nor cares which path was taken. Hosts that already expose a name-keyed skill-loading tool for filesystem skills extend it rather than introducing a parallel one. +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. 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: @@ -389,7 +393,7 @@ Earlier drafts required `` to be a single segment equal to the front A subsequent draft went further and fully decoupled the path from the name. That was too loose: a URI like `skill://a/b/c/SKILL.md` tells you nothing about what the skill is called until you fetch and parse frontmatter. Clients listing skills, hosts displaying them in a picker, and models reasoning over URIs all want the name visible without a round trip. -Constraining the final segment to match the frontmatter `name` gets both properties. The prefix carries the server's organizational structure; the final segment carries the skill's identity; and the two together form a locator from which the name can be read directly. +Constraining the final segment to match the frontmatter `name` gets both properties. The prefix carries the server's organizational structure; the final segment carries the skill's name; and the two together form a locator from which the name can be read directly. ### Why Is Enumeration Optional? @@ -430,6 +434,7 @@ Skill content is instructional text delivered to a model, which makes it a promp - **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.