Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 59 additions & 26 deletions src/seps/sep-2640.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# spec_source: modelcontextprotocol/modelcontextprotocol@sep/skills-extension seps/2640-skills-extension.md
# extracted: 2026-08-28
# spec_source: modelcontextprotocol/modelcontextprotocol@main seps/2640-skills-extension.md
# extracted: 2026-09-21
#
# provenance: re-extracted against the 2026-08-21 revision, which the core
# maintainers produced after their second review round and which went back up
Expand All @@ -23,15 +23,34 @@
# archive rows) and four were reworded. Because the SEP is mid-vote, expect one
# more pass at the CM-stamped head.
#
# coverage: extracted against branch head a3e147ca2710 (2026-08-25), the commit
# the CM vote is running on. A keyword sweep of the source finds 97 sentences
# carrying an RFC 2119 term; this file declares 89 checks plus 7 excluded rows.
# The residual handful are restatements that fold into a declared row rather
# than standing alone. Of the 89 declared checks, 40 are exercised on the wire
# by the three server scenarios; the other 49 are host obligations (retrieval
# policy, approval binding, cache isolation, context handling) that a
# server-side harness cannot observe and that would need a host-side scenario
# set to test.
# final pass: that pass is this one. SEP-2640 was marked Accepted on 2026-09-03
# (d6b31a03) and Final on 2026-09-11 (8075db4a), and the source moved from the
# sep/skills-extension branch to main. Four commits landed on the text between
# a3e147ca and f56f204f, only one of them normative: the Skill Format section
# gained a paragraph binding clients to the Agent Skills specification's own
# backward-compatibility mechanisms, declared below as two excluded rows.
# The Capability Declaration section was reworded to locate the declaration in
# the `extensions` field of the server's capabilities rather than loosely in
# the `initialize` response, and the `resources`-capability requirement is now
# stated there outright instead of being derived from Dependencies; both rows
# are restated from the Final text. A copy-editing pass (582d814a) replaced
# em-dashes with commas, colons and parentheses throughout, which left twelve
# rows quoting text the SEP no longer contains, so those quotes are re-synced.
# The design-history links moved with the repository, from
# experimental-ext-skills to ext-skills/docs/archive.
#
# coverage: extracted against f56f204f6290 on main, the SEP at Final. A keyword
# sweep of the source finds 98 sentences carrying an RFC 2119 term; this file
# declares 90 checks plus 10 excluded rows. The residual handful are
# restatements that fold into a declared row rather than standing alone. Of
# the 90 declared checks, 42 are exercised on the wire by the three server
# scenarios; the other 48 are host obligations (retrieval policy, approval
# binding, cache isolation, context handling) that a server-side harness
# cannot observe and that would need a host-side scenario set to test.
#
# The three counts above were 89, 7, 40 and 49 before this pass. Two of those
# moved because rows were added; the rest were simply wrong when written, and
# nothing recomputes them, so treat them as a claim with a date.
#
# deliberately not declared here: `resultType`, which appears in all three
# result examples in this SEP, is a base-protocol field on the common `Result`
Expand Down Expand Up @@ -78,7 +97,7 @@ requirements:
# Reverses the retired sep-2640-no-nested-skills row: the June text said a
# SKILL.md MUST NOT appear in any descendant directory.
- check: sep-2640-nested-skills-permitted
text: 'A `SKILL.md` MAY appear in a descendant directory of a skill — skills can nest.'
text: 'A `SKILL.md` MAY appear in a descendant directory of a skill, so skills can nest.'
- check: sep-2640-nested-skillmd-not-acted-on
text: "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."
- check: sep-2640-nested-publication-flat
Expand All @@ -100,14 +119,15 @@ requirements:
# identifiers to per-extension settings objects" and defines no envelope; the
# SEP-2640 capability block matches it.
- check: sep-2640-capability-declaration-inline
text: 'Per SEP-2133 extension negotiation, servers declare support for this extension in their `initialize` response, mapping the extension identifier directly to its per-extension settings object: `{"capabilities": {"extensions": {"io.modelcontextprotocol/skills": {"directoryRead": true}}}}`.'
text: 'Per SEP-2133 extension negotiation, servers declare support for this extension in the `extensions` field of their capabilities, mapping the extension identifier directly to its per-extension settings object: `{"capabilities": {"extensions": {"io.modelcontextprotocol/skills": {"directoryRead": true}}}}`.'
url: https://modelcontextprotocol.io/seps/2640-skills-extension#capability-declaration
- check: sep-2640-capability-commits-to-methods
text: 'Declaring the extension itself commits the server to `skills/list` and `skills/get`.'
- check: sep-2640-capability-empty-object
text: 'An empty object indicates support for the extension with no optional features.'
- check: sep-2640-capability-requires-resources
text: "This extension depends only on the base protocol's Resources primitive. Skill files are served through `resources/read`, and the base Resources specification requires any server that supports resources to declare the `resources` capability. A server declaring this extension therefore **MUST** also declare the `resources` capability."
text: 'A server declaring this extension MUST also declare the `resources` capability.'
url: https://modelcontextprotocol.io/seps/2640-skills-extension#capability-declaration
- check: sep-2640-capability-directory-read-flag
text: 'Clients MUST NOT call `resources/directory/read` against a server that has not declared `directoryRead: true`.'

Expand All @@ -117,15 +137,15 @@ requirements:
- check: sep-2640-skills-list-pagination
text: "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."
- check: sep-2640-skills-list-entry-atomic
text: "An entry is atomic — a skill's `resources` set is never split across pages."
text: "An entry is atomic: a skill's `resources` set is never split across pages."
- check: sep-2640-skills-list-cache-attributes
text: "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."
- check: sep-2640-skills-list-may-be-partial
text: 'A server whose skill catalog is large, generated on demand, or otherwise unenumerable MAY return an empty or partial listing.'
- check: sep-2640-host-no-empty-listing-assumption
text: 'Hosts MUST NOT treat an empty or partial listing as proof that a server has no skills.'
- check: sep-2640-enumeration-scheme-uniform
text: "The method serves entries for a server's skills whatever URI scheme they use — enumeration is uniform across schemes."
text: "The method serves entries for a server's skills whatever URI scheme they use. Enumeration is uniform across schemes."

# === The skill entry (shared by skills/list and skills/get) ===
- check: sep-2640-entry-uri-required
Expand All @@ -141,21 +161,21 @@ requirements:
- check: sep-2640-names-should-be-unique
text: "Within a server's listing, names SHOULD be unique, but they are not guaranteed to be."
- check: sep-2640-host-disambiguate-listing-collision
text: '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.'
text: '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.'
- check: sep-2640-names-not-unique
text: "A skill's `name` is a label, not an identifier. Within a server's listing, names SHOULD be unique, but they are not guaranteed to be. 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."
text: "A skill's `name` is a label, not an identifier. Within a server's listing, names SHOULD be unique, but they are not guaranteed to be. 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."

# === resources ===
- check: sep-2640-entry-resources-required
text: '`resources` is REQUIRED on every skill entry and takes one of two forms: an array enumerating the skill''s files — `SKILL.md` and every supporting file — as `{uri, digest, size}` triples, or the string `"dynamic"`.'
text: '`resources` is REQUIRED on every skill entry and takes one of two forms: an array enumerating the skill''s files (`SKILL.md` and every supporting file) as `{uri, digest, size}` triples, or the string `"dynamic"`.'
- check: sep-2640-resources-complete
text: "When present, `resources` MUST be complete: it lists every file of the skill, each exactly once, including an entry matching the skill's top-level `uri` — that entry carries the digest and size of `SKILL.md` itself."
text: "When present, `resources` MUST be complete: it lists every file of the skill, each exactly once, including an entry matching the skill's top-level `uri`. That entry carries the digest and size of `SKILL.md` itself."
- check: sep-2640-resources-uri-within-skill
text: "Each `uri` MUST be the skill's `SKILL.md` or a file within the skill's directory."
- check: sep-2640-resources-digest-format
text: "Digests are SHA-256 hashes of an artifact's raw bytes, formatted as `sha256:{hex}` where `{hex}` is 64 lowercase hexadecimal characters. Each entry in a skill's `resources` carries the digest of the file at its `uri`."
- check: sep-2640-resources-size-required
text: "Each entry MUST carry `size`: the length in bytes of the file's raw content — the same bytes the `digest` covers."
text: "Each entry MUST carry `size`: the length in bytes of the file's raw content (the same bytes the `digest` covers)."
- check: sep-2640-resources-dynamic-marker
text: 'When a skill''s content is generated dynamically, such that stable digests cannot be published, the server MUST set `"resources": "dynamic"` instead of an array.'
- check: sep-2640-resources-invalid-entry
Expand All @@ -175,9 +195,9 @@ requirements:
- check: sep-2640-skills-get-implemented
text: '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.'
- check: sep-2640-skills-get-entry-shape
text: '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.'
text: 'The `skill` object is a skill entry, identical in shape and meaning to an entry of `skills/list`, with the same `uri`, `frontmatter`, and `resources` fields under the same rules.'
- check: sep-2640-skills-get-unknown-uri-invalid-params
text: 'If the URI does not identify a skill the server serves, the server MUST return error `-32602` (Invalid params) — the same code `resources/read` uses for unknown resources.'
text: 'If the URI does not identify a skill the server serves, the server MUST return error `-32602` (Invalid params), the same code `resources/read` uses for unknown resources.'
- check: sep-2640-skills-get-answers-unlisted
text: 'A server MUST answer for every skill it serves, whether or not that skill appears in its `skills/list` result. A skill absent from a partial listing is still retrievable by URI.'
- check: sep-2640-skills-get-cache-attributes
Expand All @@ -195,7 +215,7 @@ requirements:
- check: sep-2640-host-size-mismatch-failure
text: "A read whose byte length differs from the entry's `size` is a verification failure equivalent to a digest mismatch, whether or not the host goes on to compute the digest."
- check: sep-2640-host-no-prefetch
text: "Hosts MUST NOT retrieve a skill's files ahead of need — not on connection, not on listing, and not at approval. A `SKILL.md` is fetched when the skill is loaded, and a supporting file when it is read."
text: "Hosts MUST NOT retrieve a skill's files ahead of need, whether on connection, on listing, or at approval. A `SKILL.md` is fetched when the skill is loaded, and a supporting file when it is read."
- check: sep-2640-host-frontmatter-comparison
text: "After fetching a `SKILL.md` for which the host holds an entry, 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."
- check: sep-2640-host-digest-not-security-boundary
Expand Down Expand Up @@ -280,7 +300,7 @@ requirements:
- check: sep-2640-directory-read-result-resources-shape
text: '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.'
- check: sep-2640-directory-read-invalid-params
text: '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.'
text: '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.'
- check: sep-2640-directory-read-pagination
text: 'Pagination mirrors `resources/list`: when the result includes `nextCursor`, the client passes it back as `cursor` to retrieve the next page.'
- check: sep-2640-directory-read-empty-dir
Expand All @@ -289,6 +309,19 @@ requirements:
text: 'Hosts MUST NOT treat the directory result as extending the manifest. While acting on the skill under the held entry, the host MUST NOT read a newly listed child — an unlisted file is a verification failure, exactly as a digest mismatch is — and MUST NOT surface it to the model as a file of the skill.'

# === Excluded: not observable on the MCP wire ===
# Added by the 2026-09-13 Skill Format pass, after this file's first
# extraction. Both halves condition on a future incompatible revision of an
# external specification, so neither is reachable by a harness today: there is
# no such revision to break against, and a client that honors a compatibility
# mechanism which has never been exercised is indistinguishable from one that
# would ignore it. They are declared so the requirement is tracked rather than
# lost, and they become testable the day Agent Skills ships a breaking change.
- text: 'In the event that the Agent Skills specification changes in a backwards incompatible way, clients MUST honor any backwards compatibility mechanisms provided by the Agent Skills specification.'
excluded: 'Conditioned on a breaking revision of the Agent Skills specification that does not yet exist; there is nothing for a harness to present and nothing for the client to honor.'
url: https://modelcontextprotocol.io/seps/2640-skills-extension#skill-format
- text: 'Clients SHOULD continue to support the Agent Skills specification as it existed prior to any incompatible change.'
excluded: 'Same condition. Supporting a prior revision is observable only against content written to that revision, which requires the incompatible change to have happened first.'
url: https://modelcontextprotocol.io/seps/2640-skills-extension#skill-format
- text: '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.'
excluded: 'DNS and network resolution sit below the MCP wire layer; the harness cannot observe whether the client performed name lookups on URI authority components.'
- text: "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"
Expand All @@ -304,5 +337,5 @@ requirements:
excluded: 'User-facing messaging; not protocol-observable.'
- text: 'For a skill whose `resources` is `"dynamic"`, the entry offers nothing to count. A host that chooses to load such a skill applies the total-size limit to what it actually retrieves and MAY stop loading the skill once that limit is reached.'
excluded: 'A host stopping mid-load is indistinguishable on the wire from a host that needed no further files.'
- text: 'Hosts SHOULD expect this sequence and present it as such — a skill that has changed and needs re-approval — rather than as a read error.'
- text: 'Hosts SHOULD expect this sequence and present it as a skill that has changed and needs re-approval, rather than as a read error.'
excluded: 'User-facing presentation of the stale-entry recovery path; not protocol-observable.'
Loading