Thanks to visit codestin.com
Credit goes to github.com

Skip to content

SEP-2640: Skills Extension#2640

Open
pja-ant wants to merge 25 commits into
mainfrom
sep/skills-extension
Open

SEP-2640: Skills Extension#2640
pja-ant wants to merge 25 commits into
mainfrom
sep/skills-extension

Conversation

@pja-ant

@pja-ant pja-ant commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Extensions Track SEP defining the skill:// resource convention for serving Agent Skills over MCP. Developed by the Skills Over MCP Working Group; transports the design from experimental-ext-skills#69 with archive distribution per experimental-ext-skills#83.

Extension identifier: io.modelcontextprotocol/skills.

Reference implementations: TypeScript SDK wrappers, host prototypes in gemini-cli/fast-agent/goose/codex/Claude Code, and the GitHub MCP Server.

@pja-ant pja-ant requested a review from olaservo April 23, 2026 19:46
@pja-ant pja-ant force-pushed the sep/skills-extension branch from 1f35055 to 7b35855 Compare April 23, 2026 19:47
@pja-ant pja-ant changed the title SEP: Skills Extension SEP-2640: Skills Extension Apr 23, 2026
@pja-ant pja-ant added SEP draft SEP proposal with a sponsor. extension labels Apr 23, 2026
@pja-ant pja-ant marked this pull request as ready for review April 23, 2026 19:51
@pja-ant pja-ant requested review from a team as code owners April 23, 2026 19:51
@pja-ant pja-ant self-assigned this Apr 23, 2026
@olaservo olaservo moved this from Backlog to In review in Skills Over MCP Working Group Apr 24, 2026
Comment thread seps/2640-skills-extension.md Outdated
Comment thread seps/2640-skills-extension.md Outdated
Comment thread seps/2640-skills-extension.md Outdated
Comment thread seps/2640-skills-extension.md Outdated
Comment thread seps/2640-skills-extension.md Outdated
Comment thread seps/2640-skills-extension.md Outdated
Comment thread seps/2640-skills-extension.md Outdated
Comment thread seps/2640-skills-extension.md
Comment thread seps/2640-skills-extension.md Outdated
Comment thread seps/2640-skills-extension.md Outdated
Comment thread seps/2640-skills-extension.md
Comment thread seps/2640-skills-extension.md Outdated

Per RFC 3986, the first segment of `<skill-path>` occupies the authority component. This carries no special semantics under this convention and clients MUST NOT attempt DNS or network resolution of it.

A server MAY 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 — `<skill-path>` ending in the skill name, `SKILL.md` explicit in the URI, no nesting — apply regardless of scheme.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the skill path for the github:// example is owner/repo/skills/refunds, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

Comment on lines +199 to +218
```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"]
}
}
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is just an example - but I don't think the model typically knows about MCP servers in most client implementations (or do they in your investigations into existing read_resource impls?).

For tools, the host keeps track of which tools exposed to the model come from MCP servers; it doesn't disclose to the model which MCP servers are available or how tools map to individual MCP servers.

Similarly, I would expect hosts to potentially have something like a single read_skill progressive disclosure tool that can read skills locally on disk and from MCP servers. Especially wrt the section below "Unified Treatement of Filesystem and MCP Skills". It's not clear to me how this signature would support unified treatment, since local file-based skills wouldn't have a server input value.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@olaservo did an exploration here: #2527 (comment)

I did some digging in code and:

  • claude code, codex, and fast-agent all have a single read_resource tool with a server param.
  • gemini-cli and goose just take uri alone and seem to probe servers one-by-one until one hits... which seems pretty janky and IMO should be fixed regardless of skills.

but yes, it's non-normative and implementations are free to do what they like here.

Similarly, I would expect hosts to potentially have something like a single read_skill progressive disclosure tool that can read skills locally on disk and from MCP servers. Especially wrt the section below "Unified Treatement of Filesystem and MCP Skills". It's not clear to me how this signature would support unified treatment, since local file-based skills wouldn't have a server input value.

What I expect (but again, non-normative):

  • Client enumerate skills for all servers by reading skill://index.json
  • They create a map of skill-name to skill metadata. The skill metadata says whether it is a local file or MCP-backed skill + which server it came from (or anything else).
  • They have a single read_skill tool that keys on skill-name and when invoked uses the metadata to decide whether to do a local file read or MCP resources/read (note: the model doesn't need to use read_resource for this -- just read_skill and it is backed by a resource/read for MCP skills).
  • read_resource is mostly needed for reading related files as part of the skill, e.g. if the skill references skill://code-review/checklist.json then the model will need to read that using a read_resource tool and make sure it is disambiguated.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That matches how I'm thinking about too, I recommend adding it to the SEP

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

Comment thread docs/seps/2640-skills-extension.mdx Outdated

## 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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good callout on boundaries here 👍🏼
Probably a good idea to wait until this SEP lands, but was curious if there is an RFC on how we could leverage this in Agent Skills specs, or if there has already been a conversation. Sorry if my question had already been addressed. I'm still catching up on the topic.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm in touch with the Agent Skills folks and they are happy with this proposal since it keeps the Agent Skills format and just focuses on delivery. Once it lands can maybe chat with them about linking to it from the agentskills.io site as a reference.

Comment thread seps/2640-skills-extension.md Outdated
olaservo added a commit to olaservo/skilljack-mcp that referenced this pull request May 3, 2026
* Align resource layer with SEP-2640 (Skills Extension)

Replace legacy `skill://{name}` and `skill://{name}/` URIs with the SEP-2640
shape: `skill://<skill-path>/SKILL.md` for skill markdown, individual files
addressable as siblings, and a new `skill://index.json` discovery resource.
Declares the `io.modelcontextprotocol/skills` extension in initialize
capabilities, and adds `getSkillPath` / `buildSkillResourceUri` /
`parseSkillResourceUri` / `buildSkillIndex` helpers in skill-discovery.ts.

This is a breaking change to the URI scheme; pre-1.0, draft SEP, no shims.

SEP: modelcontextprotocol/modelcontextprotocol#2640

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* Update CLAUDE.md for SEP-2640 resource layer

Document the new URI shapes, the `extensions["io.modelcontextprotocol/skills"]`
capability, and the four SEP helpers in skill-discovery.ts. Note the explicit
`skill://index.json` update notification fired from `refreshSkills()`.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
@sambhav

sambhav commented Jun 20, 2026

Copy link
Copy Markdown
Member

I spent some time looking at how the major harnesses implement skill discovery, because it affects both:

  1. @dsp-ant's question about whether nesting should be allowed here: L67
  2. @localden's question about what happens when two same-name skills collide on unpack: L194

I think these are the same issue from two directions: is a skill name unique within the namespace a client resolves against?

What current implementations do

Harness Nesting depth Same-name collision Warns?
Claude Code depth 1 only project shadows user no
Gemini CLI depth 1 (['SKILL.md','*/SKILL.md']) workspace > user yes
Codex recursive ≤6 dedup by path; both kept, then skipped at selection no
Goose recursive, unbounded dedup by name; first-found wins no
OpenCode recursive, unbounded (**/SKILL.md) dedup by name; last-found wins yes

References:

Why this matters

An author cannot currently rely on nesting as portable behaviour.

Claude Code and Gemini only discover skills one level deep, so a nested skill may simply disappear in two of the five harnesses, with no clear error explaining why.

The three harnesses that do recurse then handle same-name collisions differently:

  • Codex keeps both by path, but later skips ambiguous name-based selection.
  • Goose deduplicates by name and keeps the first match.
  • OpenCode deduplicates by name and keeps the last match.

So if two nested skills share the same name, the result depends on which client loads them.

This is not only theoretical. OpenCode's remote-index loader resolves each skill cache location as:

path.resolve(sourceRoot, skill.name)

That means two remote skills whose frontmatter both say name: refunds land at the same local path, regardless of where they appeared on the server.

Codex has a related runtime ambiguity: select_skills_from_mentions drops any name that matches more than one skill, so /refunds resolves to nothing if there are multiple refunds skills. Its test appears to treat that as intended behaviour. Related issues: #9930, #25324, #23987.

A skillPath field could distinguish the two files in the URI, but it would not solve the common case where a user or model invokes a skill by name.

An invariant that might cut through this

The framing I keep coming back to is name uniqueness within a server. Concretely, something like:

A server SHOULD NOT publish two skills with the same name in the same index.json.

The server owns its index.json, so this could be a publish-time validation check on the author side, similar to how a package registry rejects duplicate package names in the same namespace.

The appeal for me is that it gives clients a clean namespace to consume and removes the need for each client to invent its own collision-resolution behaviour at runtime, which is what Codex, Goose, and OpenCode are each doing differently today. But I'd genuinely like to hear where this breaks down — @localden, does this interact well or badly with the unpack/skillPath direction you were thinking about?

What this could imply for nesting

If we take that invariant, I don't think the SEP would need to ban nested files, which I know was the part @dsp-ant was uncomfortable with.

Nesting could stay permitted and be treated purely as server-side file layout rather than protocol-level hierarchy. That avoids the forward-compatibility cost @dsp-ant raised: a server can organise files however it wants, while the distribution layer still exposes a flat, unambiguous index.

In that model:

  • directory depth carries no protocol meaning;
  • clients don't infer hierarchy from paths;
  • names stay unique within a server's published index.

I think this gets us most of what the nesting request was after without the discovery inconsistency, but I may be missing a case where layout genuinely needs to be load-bearing.

Across-server collisions

Across servers, names can still collide, and that seems fine and unavoidable under any design. The client can namespace by origin/server, the same way clients already disambiguate tool names when multiple servers are connected, so this reuses existing client-side machinery rather than adding a new burden to the SEP.

On skill composition and hierarchy

I do think the nesting discussion is pointing at a real need: authors want a way to express relationships between skills. I just don't think directory hierarchy is the right abstraction for it, because the composition model is usually more of a graph than a tree.

For example:

  • billing/refunds may depend on a shared auth skill;
  • support/refunds may also depend on that same auth skill;
  • both may belong to different product areas, workflows, or bundles;
  • a single skill may be reused by several higher-level skills.

A directory layout can't express that cleanly without either duplicating skills, inventing path semantics, or forcing one "primary" parent where there may not be one.

That's why, on our side, we've been keeping the distributed list of skills flat and using metadata for relationships rather than encoding them in folder structure. The properties that has been buying us:

  • the distribution namespace stays simple and unambiguous;
  • clients resolve skills by unique name;
  • authors can still group, relate, compose, or annotate skills;
  • relationship models can evolve without changing the discovery mechanism;
  • the same skill can participate in multiple workflows or bundles without moving on the filesystem.

So I wouldn't read composition as irrelevant to this SEP. I'd read it as evidence that hierarchy probably wants to be explicit metadata rather than implicit directory structure.

I also don't think this SEP needs to define that relationship model itself, since it feels more like a skill-semantics question for the agentskills spec, where there are already related proposals:

The distribution case feels like a strong argument for picking that work up, though. We have the same need internally today and currently express these links as metadata key/value pairs in frontmatter rather than through layout.

Where I'd lean (open to other framings)

Pulling it together, the direction I'd lean toward for this SEP:

  1. Treat name uniqueness within a server's published index.json as the core guarantee, ideally validated at publish time.
  2. Allow nested files, but define them as server-side layout only.
  3. Keep the distributed index flat, with no protocol semantics attached to directory depth.
  4. Be explicit that folder structure isn't the composition/hierarchy model.
  5. Use the composition use case as motivation for explicit relationship metadata in the agentskills spec, rather than solving it here.
  6. Note that across-server name collisions are resolved by client-side server/origin namespacing.

That's where I've landed, but I'm very open to being wrong on any of these, especially whether uniqueness belongs in this SEP or upstream. Curious what @dsp-ant and @localden think.

@AgentGymLeader

This comment was marked as spam.

@eeee2345

Copy link
Copy Markdown

On the same-name collision / cross-server namespacing point — this isn't only a discovery/UX question, it's a live impersonation surface.

We scanned ~96,000 published agent skills and confirmed 751 as malicious. A recurring pattern was name collision: a malicious skill published under (or near) the name of a popular legitimate one, relying on the consumer resolving the wrong artifact at unpack time. Once "two skills with the same name" can occur across sources, "which one unpacks" becomes a security decision, not just a precedence rule.

Two implications for SEP-2640:

  1. Resolution order needs to be specified, not implementation-defined. If each harness (gemini-cli / goose / codex / Claude Code / etc.) breaks same-name ties differently, an attacker only needs the one harness that resolves toward them. The cross-server → origin/server namespacing direction in the thread is right; it just needs to be normative.

  2. Skill metadata should leave room for a provenance / detection annotation that the resolver can consult — without the spec binding to any particular scanner or trust authority. A thin, optional hook (e.g. a signature/attestation or a detection-result field a resolver MAY honor) lets an open detection layer attach without putting trust policy into the transport spec. (Disclosure: I maintain one such open ruleset, so I'm biased toward keeping that seam open — but the seam matters regardless of who fills it.)

Happy to share the collision cases from the dataset if they'd help stress-test the resolution rules.

@localden localden left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two additions clarifying that digests are not a security boundary.

Comment thread seps/2640-skills-extension.md Outdated
Comment thread seps/2640-skills-extension.md Outdated
pja-ant and others added 2 commits June 25, 2026 14:39
@eeee2345

This comment has been minimized.

@wachterjohannes

Copy link
Copy Markdown

Feedback on resources/directory/read from implementing the server side (reference implementation in modelcontextprotocol/php-sdk#372, plus the symfony/ai Mate host). I know the WG is mid-review and heads-down on the June release, so these are just three concrete things that tripped me up while wiring this method up — no rush.

1. The error code mis-cites base resources/read

In Directory Listing → Semantics:

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.

Two problems:

  1. The parenthetical is incorrect about the base protocol. resources/read returns -32002 (Resource not found) for an unknown URI, not -32602. So this method actually deviates from resources/read while claiming to match it. If the intent is symmetry, the not-found case should be -32002.
  2. It overloads one code for two genuinely different conditions — URI absent vs. URI exists but is not a directory — with no way for a client to distinguish them. A client that just walked a tree and hit a stale/renamed path wants to tell "gone" from "not a directory" without parsing the message string.

Suggestion: return -32002 for a nonexistent URI (matching resources/read), and reserve -32602 for a genuinely malformed request (e.g. missing uri). If "exists but not a directory" warrants its own signal, state it explicitly rather than folding it into Invalid params.

2. The directory-root URI is specified twice, inconsistently

This determines the exact string a client passes to resources/directory/read:

  • Resource Mapping: "the skill's root directory is the URI obtained by stripping the trailing SKILL.md" — stripping SKILL.md from skill://acme/billing/refunds/SKILL.md yields skill://acme/billing/refunds/ with a trailing slash.
  • Directory Listing → Directory resources: "Directory URIs are written without a trailing slash" — giving skill://acme/billing/refunds without it.

So a client that derives the root per Resource Mapping and then calls resources/directory/read on it passes a URI the directory section calls non-canonical — and URI equality / cache keys differ by one byte. Please pick one canonical form and make both sections agree; I'd lean no-trailing-slash to match the inode/directory resources, and reword Resource Mapping to say the root is the parent-directory URI of SKILL.md rather than a literal string strip.

3. directoryRead does not apply to archive-only skills — worth stating

Per §Archives, a host that fetched a skill via an archive MUST serve directory navigation from its local unpacked tree, so it MUST NOT call resources/directory/read even when the server advertises directoryRead; an archive-only skill exposes no server-side directory resources at all. The method is described purely in terms of "the skill namespaces it serves as individual files," which is technically correct but easy to miss — I briefly expected it to work uniformly across delivery modes. One sentence ("archive-only skills expose no directory resources; navigation happens against the host's local unpack") would prevent the double-take, and makes clear an archive-serving server gets directory navigation for free without implementing the method at all.

@wachterjohannes

Copy link
Copy Markdown

A few more things from implementing the server + host side (reference impl in modelcontextprotocol/php-sdk#372, host in symfony/ai Mate) — companion to my note on resources/directory/read above. These are about the discovery index and integrity model.

1. Frontmatter "verbatim JSON" + field-by-field re-verification isn't interoperably implementable as written

Having implemented both a server that emits this index and the host-side loader, I think the frontmatter identity + re-verification requirement is underspecified enough that (a) two good-faith implementations will reject each other's valid skills, and (b) a parser differential lets a server smuggle content past the very check meant to enforce "what the user approved is what the model receives."

The rules in tension:

  • Frontmatter: "the skill's SKILL.md YAML frontmatter rendered verbatim as a JSON object" and "MUST be identical in content to the frontmatter of the SKILL.md it describes."
  • Integrity: the host "MUST parse its YAML frontmatter and compare it field-by-field … Any discrepancy MUST be treated as a verification failure … the skill MUST NOT be loaded."

The index frontmatter is JSON; SKILL.md carries YAML. Comparing them means parsing both to a value model, and YAML→value is not canonical. Concrete cases where a conforming server and host diverge on valid frontmatter and then hard-reject the skill:

  • YAML 1.1 vs 1.2 booleans: active: yes / on / off → boolean in 1.1, string in 1.2; octal-looking 010 (8 vs 10).
  • Scalar typing: version: 2.10 is the float 2.1; version: "2.1.0" is a string — the SEP's own example ships "version": "2.1.0".
  • Null forms: empty value, ~, null, Null all mean null; a naive server may emit "".
  • Block scalars / chomping: description: | trailing-newline handling differs between emitters.
  • Key order / duplicate keys: JSON objects are unordered, yet "verbatim" implies order is preserved; on duplicate keys JSON keeps the last, YAML libraries vary or error.
  • Tags / anchors (!!str, &/*) widen the differential further.

The security teeth: because equality is undefined, a server can ship index frontmatter the host parses (and shows the user) one way while SKILL.md parses another — passing a naive field-by-field check yet not being "identical in content." The intent is right; the mechanism turns exotic-but-legal YAML into both an interop failure and a smuggling vector.

Two ways to tighten (either works):

  1. Define the comparison on parsed values, not text: both sides parse under the YAML 1.2 core schema into the JSON data model and compare by deep structural equality with object key order insignificant; mandate duplicate-key rejection (the cheapest smuggling vector). Then frontmatter is "the JSON encoding of the parsed frontmatter," not a byte-preserving copy.
  2. Or narrow the hard-fail to the fields that gate approval/registry — name and description (both spec'd as strings) — and treat the rest as advisory/preview. The SKILL.md digest already pins the authoritative bytes, so a full-tree MUST-reject over arbitrary author YAML buys little beyond fragility.

As written, I don't think two independent SDKs can be relied on to agree, and the check doesn't fully deliver the security guarantee it claims.

2. Direct entries have no integrity coverage for supporting files

The archive-vs-live-read rule in §Archives ("MUST thereafter serve every read of that skill's files from the locally unpacked archive … MUST NOT satisfy those reads by issuing live resources/read") is airtight for archive-delivered skills, but it quietly exposes an asymmetry for direct entries.

For a url+digest entry, digest covers only SKILL.md (§Integrity: "for a skill entry's digest, the artifact is the SKILL.md file"). There is no integrity coverage for supporting files — references/GUIDE.md, scripts/extract.py are each fetched by a live resources/read with nothing in the index to verify them against.

That interacts badly with Content-bound approval: a user approves a skill bound to the SKILL.md digest, but the bytes that actually drive behavior — the script the model is told to run — are unpinned and can differ on every read, including from a transport intermediary, without invalidating the approval. §Archives already admits this ("the archive digest is the only integrity check covering supporting files"), which is exactly why a direct-entry skill's guarantee is weaker than a host author would assume from "Hosts MUST verify retrieved content against the digest."

This is precisely why our server builds a deterministic archive so a single archive digest covers the whole tree. Suggest the SEP say so directly: either state that for direct entries supporting-file integrity is trust-on-read (server-level, not transport-verified), or recommend hosts prefer an archives[] form when content-bound approval is in force.

3. Non-skill:// schemes make the index a de-facto MUST, which contradicts "enumeration is optional"

Resource Mapping says a server MAY use a native scheme: "A server MAY instead serve skills under another scheme native to its domain (e.g., github://…), provided each skill is listed in the skill://index.json resource. […] outside the index, hosts recognize skills by the skill:// scheme prefix."

That "provided" clause is a hard requirement in disguise: for a non-skill:// server, an unindexed skill is unrecognizable — nothing marks it as a skill. So for these servers the index is effectively a MUST.

But Enumeration keeps it at SHOULD and explicitly permits declining: "A server … MAY decline to expose skill://index.json, or MAY expose a partial index."

These collide: a github:// server that declines the index (or ships a partial one) has skills no host can discover or recognize, yet the spec says both are allowed. Suggest making the conditional MUST explicit — e.g. "A server using a non-skill:// scheme MUST list every such skill in skill://index.json" — and carving it out of the optional-enumeration language so the two sections stop contradicting each other.

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
pja-ant added 3 commits July 8, 2026 12:41
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
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
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
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

draft SEP proposal with a sponsor. extension SEP

Projects

Status: No status
Status: In review

Development

Successfully merging this pull request may close these issues.