DraftExtensions Track
Abstract
This SEP defines a convention for serving Agent Skills over MCP using the existing Resources primitive. A skill is a directory of files (minimally aSKILL.md) that provides structured workflow instructions to an agent. This extension specifies that each file in a skill directory is exposed as an MCP resource, conventionally under the skill:// URI scheme. Skills are addressed by URI and may be read directly; a skills/list method enumerates the skills a server serves — servers whose skill catalogs are large, generated, or otherwise unenumerable MAY return an empty or partial listing — and a skills/get method returns any single skill’s entry by URI. The skill format itself — directory structure, YAML frontmatter, naming rules, and the progressive disclosure model that governs how hosts stage content into context — is delegated entirely to the Agent Skills specification; this SEP defines only the transport binding.
The extension defines three protocol methods. Every server declaring the extension implements skills/list, which enumerates the skills a server serves, and skills/get, which returns the entry for a single skill by URI — including skills absent from the listing. The optional resources/directory/read lists the direct children of a directory resource, giving agents scoped navigation of a skill’s supporting files. Everything else rides on existing protocol surface, so hosts that already treat MCP resources as a virtual filesystem can consume MCP-served skills identically to local filesystem skills. The specification is accompanied by implementation guidelines for host-provided resource-reading tools and SDK-level convenience wrappers.
Motivation
Native skills support in host applications demonstrates strong demand for rich, progressively disclosed workflow instructions. MCP does not currently offer a conventional way to ship this content alongside the tools it describes, which leads to:- Fragmented distribution. A server and the skill that teaches an agent to use it are versioned, discovered, and installed separately. Users installing a server from a registry have no signal that a companion skill exists. (problem statement)
- Instruction size limits. Server instructions are delivered as the
instructionsfield of theserver/discoverresult and are practically bounded in size. Complex workflows — such as the 875-line mcpGraph skill — do not fit this model. (experimental findings) - Inconsistent ad-hoc solutions. Absent a convention, several independent implementations have each invented their own
skill://URI structure, with diverging semantics for authority, path, and sub-resource addressing.
Specification
Dependencies
This extension has no dependencies beyond the base MCP Resources primitive. In protocol versions 2026-07-28 and later,skills/list results additionally carry the base protocol’s list-caching attributes (SEP-2549).
Skill Format
A skill served over MCP MUST conform to the Agent Skills specification. In particular:- A skill is a directory. Its skill name is the value of the
namefield in itsSKILL.mdfrontmatter. - Every skill MUST contain a
SKILL.mdfile at its root. SKILL.mdMUST begin with YAML frontmatter containing at minimum thenameanddescriptionfields as defined by the Agent Skills specification.- A skill MAY contain additional files and subdirectories (references, scripts, examples, assets).
Resource Mapping
Each file within a skill directory is exposed as an MCP resource. Servers SHOULD use theskill:// URI scheme, under which the resource URI has the form:
<skill-path>is a/-separated path of one or more segments locating the skill directory within the server’s skill namespace. It MAY be a single segment (git-workflow) or nested to arbitrary depth (acme/billing/refunds).<file-path>is the file’s path relative to the skill directory root, using/as the separator.
SKILL.md is therefore always addressable as skill://<skill-path>/SKILL.md, and the skill’s root directory is skill://<skill-path> (the /SKILL.md suffix removed, no trailing slash), matching Directory Listing.
The final segment of <skill-path> MUST equal the skill’s name as declared in its SKILL.md frontmatter. This mirrors the Agent Skills specification’s requirement that name match the parent directory name. 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.mdMAY appear in a descendant directory of a skill — skills can nest. See Nested skills. - The final
<skill-path>segment, being the skillname, MUST satisfy the Agent Skills specification’s naming rules. The first<skill-path>segment occupies the authority component and SHOULD be a validreg-nameper RFC 3986; any other prefix segments SHOULD be valid URI path segments; no further constraints are imposed on them.
<skill-path> occupies the authority component. This carries no special semantics under this convention and clients MUST NOT attempt DNS or network resolution of it.
A server MAY serve skills under another scheme native to its domain (e.g., github://owner/repo/skills/refunds/SKILL.md). No scheme is privileged: the structural constraints above — <skill-path> ending in the skill name, SKILL.md explicit in the URI — apply regardless of scheme, and skills/list enumerates a server’s skills whatever scheme they use.
Skill identity does not depend on the scheme. A host learns that a resource is a skill in one of two ways: from a skills/list entry, the authoritative record of the skills a server publishes; or from an explicit reference — the server’s instructions field, another skill, or the user — which skills/get confirms, the server answering for a skill it serves and erroring otherwise. This holds for every scheme, skill:// included. A host MUST NOT conclude that a resource is a skill merely because its URI carries a particular scheme.
Examples
Resource Metadata
For eachskill://<skill-path>/SKILL.md resource:
mimeTypeSHOULD betext/markdown.nameSHOULD be set from thenamefield of theSKILL.mdYAML frontmatter. By the path constraint above, this will equal the final segment of<skill-path>.descriptionSHOULD be set from thedescriptionfield of theSKILL.mdYAML frontmatter.
_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 itsname, 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.mdread this way is ordinary markdown: hosts MUST NOT act on its frontmatter. - Activation requires fresh consent. Approval is per skill: approving a skill approves that skill alone and says nothing about skills nested within it. Activating a nested skill — loading it as a skill in its own right, whether through the host’s skill-loading machinery or by giving effect to its frontmatter — requires fresh, explicit user consent; approval of the enclosing skill does not substitute for it. Once activated, a nested skill is an ordinary skill: its frontmatter takes effect under the same rules as any other MCP-served skill, including the approval gate on
allowed-tools. - Publication is flat. A nested skill is published like any other: through its own
skills/listentry, or by explicit reference. The listing remains flat — an entry for a nested skill is an ordinary entry whoseurihappens to share a path prefix with the enclosing skill’s, and nothing in the listing marks nesting.
Discovery
A server is not required to make its skills enumerable. A skill’s URI is directly readable viaresources/read whether or not it appears in any listing, and hosts MUST support loading a skill given only its URI (see Hosts: End-to-End Integration). This is the baseline: if a model has the URI — from server instructions, from another skill, from the user — it can read the skill.
On top of that baseline, three mechanisms are defined. Two are discovery: enumeration via skills/list, which every server declaring this extension implements, and an optional pointer from server instructions. The third is retrieval: however a host arrives at a skill’s URI, skills/get returns that skill’s entry — its metadata and digests — including for skills no listing mentions.
Enumeration via skills/list
A server declaring the io.modelcontextprotocol/skills extension MUST implement the skills/list method, which returns the skills it serves. The result MAY be empty.
The request carries an optional pagination cursor:
¹ MAY be omitted only for dynamically generated skills whose content cannot be pre-digested. Hosts MAY decline to load skills without
resources.
Pagination mirrors the base protocol’s list methods: the request accepts an optional cursor, and when the result includes nextCursor the client passes it back to retrieve the next page. An entry is atomic — a skill’s resources set is never split across pages.
In protocol versions 2026-07-28 and later, the result also carries the base protocol’s list-caching attributes — ttlMs and cacheScope, as defined for tools/list and resources/list (SEP-2549) — with the same semantics: a freshness hint for the listing and a cache-scope marker, not an integrity property. Integrity and verification governs content regardless of how fresh a cached listing is.
A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY return an empty or partial listing. Hosts MUST NOT treat an empty or partial listing as proof that a server has no skills. The method serves entries for a server’s skills whatever URI scheme they use — enumeration is uniform across schemes.
Names
A skill’sname is a label, not an identifier — a skill is identified by its uri. Within a server’s listing, names SHOULD be unique, but they are not guaranteed to be: two skills at different paths may share a final segment (acme/billing/refunds and acme/support/refunds are both named refunds), and a nested skill may share its name with a top-level one. Hosts MUST NOT assume name uniqueness. When two entries in one listing collide on name, hosts MUST disambiguate them — for example by their distinguishing path segments — rather than silently discarding or preferring one. When skills from different origins collide on name, hosts MUST resolve the name within a per-origin namespace, identifying servers by a host-assigned label; an MCP-served skill MUST NOT silently shadow, or be silently substituted for, a same-named skill from any other origin — another server’s, or the host’s own filesystem skills. See Security Implications.
Frontmatter
frontmatter is the skill’s SKILL.md YAML frontmatter rendered verbatim as a JSON object — every field the author wrote, not a curated subset. Because the Agent Skills specification requires name and description, those fields are always present; everything else (license, metadata, fields added by future revisions of the Agent Skills specification) passes through unchanged. A host can therefore build its skill registry — names, descriptions, and whatever other metadata it understands — from the listing alone, without fetching each SKILL.md.
The frontmatter object MUST be identical in content to the frontmatter of the SKILL.md it describes. The final <skill-path> segment of the entry’s uri MUST equal frontmatter.name, per Resource Mapping.
Within the frontmatter metadata object, keys prefixed with io.modelcontextprotocol/ are reserved for metadata defined by MCP extensions. This extension currently defines no such keys. Implementations SHOULD ignore keys under this prefix that they do not recognize.
Skill URIs
uri is the full resource URI of the skill’s SKILL.md, readable via resources/read. Supporting files are individually addressable as sibling resources under the same skill path, per Resource Mapping. A skill is always retrieved as individually addressable resources; this extension defines no packed or bundled retrieval form. See Appendix: Deferred Features.
Resources
resources enumerates the skill’s files — SKILL.md and every supporting file — as {uri, digest} pairs. It is the unit of content that a host verifies and that a user’s approval binds to:
- When present,
resourcesMUST be complete: it lists every file of the skill, each exactly once, including an entry matching the skill’s top-leveluri— that entry carries the digest ofSKILL.mditself. - Each
uriMUST be the skill’sSKILL.mdor a file within the skill’s directory. - Completeness extends to nested skills: from the enclosing skill’s perspective their files are supporting files (Nested skills), so the enclosing skill’s
resourceslists them too, and the same file may appear in both the enclosing and the nested skill’s entries. A change to a nested skill is therefore a change to the enclosing skill’s set. resourcesMAY be omitted only when a skill’s content is generated dynamically, such that stable digests cannot be published. A skill withoutresourcesoffers no content integrity and cannot be content-bound (Security Implications). Hosts MAY decline to load such skills, and server authors SHOULD expect that some hosts will.
Integrity and verification
Digests are SHA-256 hashes of an artifact’s raw bytes, formatted assha256:{hex} where {hex} is 64 lowercase hexadecimal characters. Each entry in a skill’s resources carries the digest of the file at its uri.
When a host retrieves a file listed in a skill’s resources, it MUST verify the content against that entry’s digest. A mismatch means the content is not what the listing promised — corrupted, tampered with, or simply stale because the skill was updated after the listing was fetched. Whatever the cause, hosts MUST NOT use the unverified content; to recover from staleness, call skills/get for that skill — or skills/list to refresh the catalog — and proceed from the current resources set, which, being different, revokes any content-bound approval (Security Implications). Because resources is complete, an unlisted file is a change to the skill: while acting on a skill for which the host holds an entry, a host MUST resolve reads of the skill’s files only to URIs listed in that entry’s resources, and MUST treat a read of an unlisted file within the skill as a verification failure equivalent to a digest mismatch. Digests also enable efficient caching: compare digests against locally cached values to determine whether a file has changed without re-reading it, or fetch, verify, and cache the entire set at approval time and serve every subsequent read from the verified copy.
Digests are unsigned and supplied by the same server that supplies the content. A match proves the two are consistent, not that either is trustworthy. Any intermediary on the path, such as a gateway, can rewrite both the listing and the content together. Hosts MUST NOT treat a digest match as a security boundary.
After fetching a SKILL.md for which the host holds an entry, from either skills/list or skills/get — digest-verified when the entry carries resources, and unverifiable when it does not — 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 identity requirement on the host side, so that what a user approves from the listing is what the model actually receives.
Retrieval via skills/get
A server declaring the io.modelcontextprotocol/skills extension MUST also implement the skills/get method, which returns the entry for a single skill named by its URI:
params.uri MUST be the URI of a skill’s SKILL.md. The skill object is a skill entry, identical in shape and meaning to an entry of skills/list — the same uri, frontmatter, and resources fields, under the same rules.
Semantics:
- If the URI does not identify a skill the server serves, the server MUST return error
-32602(Invalid params) — the same coderesources/readuses for unknown resources. - A server MUST answer for every skill it serves, whether or not that skill appears in its
skills/listresult. A skill absent from a partial listing is still retrievable by URI. - The result is a point-in-time snapshot, exactly as a listing entry is. Re-calling the method is how a host refreshes one skill’s digests without re-enumerating the catalog.
- A skill whose content is generated dynamically omits
resources, per Resources, whether it is reached throughskills/listorskills/get. - The result carries no pagination cursor: a single entry is not a list. The entry is a snapshot of the skill as the server holds it at that moment; whether the result should also carry the base protocol’s caching attributes (
ttlMsandcacheScope, per SEP-2549), asresources/readresults do, is left open.
skills/get turns that same URI into the skill’s metadata and digests, so a skill that never appeared in a listing can still be verified and content-bound (Security Implications).
Pointer from Server Instructions
A server MAY direct the agent to specific skill URIs from itsinstructions 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 theirinitialize response:
An empty object indicates support for the extension with no optional features. Declaring the extension itself commits the server to
skills/list and skills/get; clients MUST NOT call resources/directory/read against a server that has not declared directoryRead: true.
Reading
Skill files are read via the standardresources/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. When skills nest, each SKILL.md’s references resolve against its own directory: a relative reference in a nested skill’s SKILL.md resolves against the nested skill’s root, regardless of how the file was reached.
Directory Listing
A skill’s instructions frequently reference a directory rather than a file: “pick the appropriate template fromtemplates/”, “run the matching script in scripts/”. To act on this, the agent must learn what the directory contains. resources/list cannot answer that scoped question: it enumerates the server’s entire resource space, not a subtree, and the servers this SEP most wants to accommodate — large, generated, or unenumerable catalogs (see Why May the Listing Be Empty or Partial?) — 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.
Directory resources
A directory resource is a resource whosemimeType 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.
- 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 coderesources/readuses 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
resourcesarray. - Pagination mirrors
resources/list: when the result includesnextCursor, the client passes it back ascursorto retrieve the next page.
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.Hosts: End-to-End Integration
This section sketches one way a host might wire MCP-served skills into an existing skills implementation. It is illustrative, not prescriptive — hosts are free to structure tools, naming, and routing however suits their architecture. The goal is that an MCP-served skill flows through the same loading and reading mechanics as a filesystem skill — while remaining origin-tagged, per Security Implications. Registry. At startup and on connection change, the host assembles a single internal skill registry from every origin it supports: filesystem skill directories, andskills/list results from each connected MCP server that declares the io.modelcontextprotocol/skills extension. Each registry entry records the skill’s name and description (from the entry’s frontmatter) and its origin — for a filesystem skill, the local directory; for an MCP skill, the server identity and the SKILL.md resource URI. Because names collide within and across origins (Names), the registry keys entries by origin and name together, qualifying colliding names for display and invocation rather than dropping either entry.
Context. The host surfaces the name and description of each enabled registry entry in the model’s context — the same list the model already sees for filesystem skills, now with MCP-served entries mixed in. The host’s UI presents the same merged list for user inspection and per-skill enable/disable, with provenance shown so users can see which server a skill came from.
Loading. The host exposes a single skill-loading tool to the model, keyed by skill name:
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), 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:
SKILL.md, which server it came from and what its base URI is — for example by stating both in the read_skill tool result — so the model can resolve references/GUIDE.md to skill://<skill-path>/references/GUIDE.md and issue read_resource against the right server. A host may instead fold this into its file-read tool by mounting each server’s skill:// namespace into a virtual path 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. When the skill’s entry carries resources, the host verifies each such read against it, per Integrity and verification.
Directory navigation. Skill instructions may point the model at a directory rather than a file (“choose the right template from templates/”). When the originating server declares directoryRead, the host SHOULD surface this capability to the model: a read_resource call whose target is a directory resource can be routed to resources/directory/read and return the child listing, and the virtual-mount approach maps it onto the host’s existing directory-listing tool — an ls of a mounted path becomes a resources/directory/read call.
Unenumerated skills. Because a listing may be empty or partial, a host should also accept skill URIs it has never seen listed — handed to the model by the user, by server instructions, or by another skill. Calling skills/get on such a URI yields the same entry a listing would have carried, so an unlisted skill enters the registry, gets verified, and is approved on the same terms as a listed one; a server that does not serve the URI as a skill answers with an error. A host may additionally let read_skill accept a full URI for this case.
The read_resource signature above includes server because two connected servers may both serve skill://refunds/SKILL.md. That is one disambiguation strategy; a host may instead rewrite URIs with a per-server prefix, scope by session, or anything else appropriate to its architecture. The tool is general-purpose — it reads any MCP resource — and is useful beyond skills.
SDKs: Convenience Wrappers
SDK maintainers SHOULD provide affordances that wrap the underlying resource operations in skill-specific terms. For example: Server-side — declare a skill from a directory, at a given path:SKILL.md frontmatter to populate resource metadata, serving file content on resources/read, and answering skills/get — and, where the server’s skill set is bounded, skills/list — computing entry digests from the registered files.
Client-side — enumerate and fetch skills:
Rationale
Why Resources Instead of a New Primitive?
The Working Group’s decision log records this as settled. Skills are files; Resources exist to expose files. Reusing Resources inherits URI addressability,resources/read, resource subscriptions (the resourceSubscriptions filter on subscriptions/listen), 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 and other MCP 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.
Why skill://<path>/<file> With an Explicit SKILL.md?
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.mdbeing 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.
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<skill-path> 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 name; and the two together form a locator from which the name can be read directly.
Why May the Listing Be Empty or Partial?
Requiring every server to implement a completeskills/list 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 method is universal but exhaustiveness is not: a server that cannot enumerate returns what it can, or nothing. 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 skills/get Alongside skills/list?
Enumeration alone leaves two gaps, and both are integrity gaps rather than conveniences.
The first is the unlisted skill. Listings may be empty or partial by design, and the baseline hands hosts skill URIs that never appeared in one — from server instructions, another skill, or the user. Before skills/get, such a skill could be read but not verified: its digests existed nowhere, so it could not be content-bound — and a catalog too large to enumerate, or fronted by a gateway, serves static content whose digests were perfectly publishable, just unreachable. skills/get makes an entry reachable from a URI alone, so verification no longer depends on how the host happened to find the skill. Skills generated at read time remain unverifiable by construction, listed or not: they omit resources either way.
The second is the cost of picking up a change. A host does not need to poll for one: the approved resources set is what its reads are verified against, so a skill whose content has moved announces itself as a verification failure. What the host then needs is the skill’s current entry — and before skills/get, the only way to get it was to re-enumerate the whole catalog, thousands of entries to learn the new digests of one skill. The same applies to a skill the user asks to update. Fetching one entry keeps the cost proportional to what changed.
Both are single-entry reads of a shape the listing already defines, so skills/get adds a method but no new schema: the skill object is a listing entry.
Why a Method Instead of a Well-Known Index Resource?
An earlier revision served enumeration as a reserved resource atskill://index.json, read like any other resource. Replacing it with skills/list — same entry schema, different carrier — buys four things. Pagination: a resource is one monolithic document, while a method pages large catalogs with the same cursor/nextCursor contract as every base list method. Caching: list methods inherit the base protocol’s caching attributes (SEP-2549) uniformly, instead of this extension inventing a parallel freshness signal for one resource. Discoverability: support is implied by the extension declaration itself, rather than probed by reading a URI and interpreting the error. And uniformity: clients reuse the machinery they already have for tools/list and resources/list, and the skill:// namespace no longer needs a reserved-URI carve-out for a document that was never a skill.
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 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 whose partial listings this SEP accommodates — 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 listing was rejected as the navigation mechanism: it would freeze the listing at authoring or publication time and bloat context for skills with many files, while a method keeps listings live and on demand. The resources enumeration (Resources) is not that manifest — it is an integrity commitment consumed by the host at verification and approval time, with no need to occupy model context, and the dynamically generated skills that need live listings most are exactly the ones that omit it.
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 Listing?
The listing 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 listing consumers to fetch and parse every SKILL.md for it. Copying the frontmatter verbatim removes the choice. The listing 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 the listing alone.
Why Per-File Digests Instead of a Single Skill Digest?
An earlier revision carried one digest per entry, covering onlySKILL.md. That left every supporting file unbound: a server could obtain approval for a benign skill, then rotate references/GUIDE.md — instructional content the model follows as readily as the skill body — while the approved digest stayed valid. Persisted approval covered the one file a user is least likely to be attacked through.
Enumerating every file closes rotation and addition in one move: the set is complete, so a new file is as detectable as a changed one. It also gives hosts a pinning primitive — fetch, verify, and cache the whole set at approval time — recovering the atomic-snapshot property that archive distribution (Appendix: Deferred Features) offered, without the unpacking surface. The cost falls on dynamically generated skills, which cannot publish stable digests; they omit resources and accept that hosts may decline them.
Backward Compatibility
This extension introduces three protocol methods.skills/list and skills/get are implemented by every server declaring the extension, so a client only issues those calls after seeing the declaration, and a client that predates the extension never issues them. resources/directory/read is additionally gated behind the directoryRead capability setting — a server that does not declare it never receives the call. The extension introduces no other methods, message types, or schema changes. A server that does not implement this extension simply exposes no skill:// resources; existing clients are unaffected. A client that does not implement this extension sees skill:// resources as ordinary resources, which they are.
Existing implementations using other skill:// URI structures will need to adjust to conform — see the Working Group’s related-work survey for a catalog. Notably, FastMCP’s widely-used SkillsProvider diverges on URI structure, discovery (per-skill _manifest vs. central index), and metadata mapping; coordinating that migration is a near-term Working Group priority. These are mechanical changes, not semantic ones.
Security Implications
Skill content is instructional text delivered to a model, which makes it a prompt-injection surface (background in open-questions.md §10). This extension imposes the following requirements:- Skill content is untrusted input. Hosts MUST treat MCP-served skill content as untrusted model input, subject to the same prompt-injection defenses applied to any server-provided text. A server being connected does not make its skill content authoritative.
- Origin MUST be visible to the model. Hosts MUST tag MCP-served skill content with its originating server identity at the point it enters model context and MUST NOT present an MCP-served skill to the model as indistinguishable from a local filesystem skill. The model, not the host, decides whether to follow a skill’s instructions. Withholding origin from it makes the untrusted-input requirement above unenforceable at the layer that acts on it.
- Skills introduce host-side surfaces that tools do not. Unlike a remote tool call, an MCP-served skill can place server-authored bytes on the host filesystem and direct the model to execute them with host-side tools. Hosts MUST treat MCP-served skills as a higher-risk surface than remote tool invocation.
- No implicit local execution. Hosts MUST NOT allow MCP-served skill content to cause host-side code execution without explicit per-skill user approval. This covers (a) declarative fields the host parses (hooks, frontmatter scripts) and (b) instructions in the skill body that direct the model to invoke any host code-execution tool, whether to run a script bundled in the skill or to run an arbitrary command the skill specifies. Hosts MUST ignore or approval-gate (a), and MUST apply the same approval gate to code-execution tool calls issued while the model is acting on an MCP-served skill.
- Origin-scoped resource reads. A model-callable resource-read surface (such as the
read_resourcepattern in 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 aresources/readagainst server B. Hosts MUST identify servers by a host-assigned label, not the server’s self-reportedserverInfo.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_metaunder their own reverse-domain prefix — not theio.modelcontextprotocol.skills/prefix reserved for this extension (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-toolsfield, 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 populatingallowed-toolsis 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 otherSKILL.mdwithin its file space: a nested skill’sallowed-toolshas no effect unless that nested skill is itself activated under its own approval (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.mdrequires fresh, explicit user consent, per Nested skills. Silently promoting a file of an approved skill to an active skill would let a server ride new instructions and permission requests in on a prior approval. - Provenance and inspection. Hosts SHOULD indicate which server a skill originates from when presenting it, SHOULD let users inspect a skill’s content before it is loaded into model context, and MAY gate loading behind per-skill or per-server user approval.
- Digests are not a security boundary. Listing digests are unsigned and come from the same server as the content. They confirm consistency between the listing and what was fetched, as described in Integrity and verification, but they cannot establish trust in the content, defend against the server itself, or detect an intermediary that rewrites both together.
- Content-bound approval. When a host persists any per-skill user approval, it MUST be bound to the entry’s
resourcesset — everyurianddigest— observed at the moment of approval. If a subsequent entry for that skill — fromskills/listorskills/get— advertises a different set, whether a file was rotated, added, or removed, the host MUST treat the prior approval as revoked and re-prompt before loading or executing. A host need not poll for changes. While it holds the approved entry, content that has moved fails verification when read; and if it does fetch a fresh entry, the rule above revokes the approval. Neither path lets moved content through under the old approval. A skill published withoutresourcescannot be content-bound: hosts MAY decline to load it, and MUST NOT treat a persisted approval as covering whatever content the server currently serves. Digest verification (Integrity and verification) defends the approval after it is granted - it cannot establish that the content was trustworthy when the user approved it, because the server authors both the listing and the body. - 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
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
skill:// resources, surfacing skills alongside filesystem skills):
- gemini-cli: olaservo/gemini-cli#1
- fast-agent: olaservo/fast-agent#1
- goose: olaservo/goose#1
- codex: olaservo/codex#1
- Claude Code: prototyped internally at Anthropic; not yet public
- GitHub MCP Server: github/github-mcp-server#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’suri. 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.
resources (Resources).
References
- Agent Skills specification
- SEP-2549: TTL for list results
- SEP-2076: Agent Skills as first-class primitive (alternative approach)
- Skills Over MCP Working Group charter
- Decision Log — Working Group decisions and rationale
- Experimental Findings — results from implementations (WIP)
- Related Work — survey of existing skill-serving implementations
- Skill
_metaKeys —_metakey conventions for skill resources - RFC 3986: URIs