feat(skills): add SEP-2640 Skills extension APIs (phase 1: schemas, client ops, server handlers) - #2818
Draft
tobi-oye wants to merge 2 commits into
Draft
feat(skills): add SEP-2640 Skills extension APIs (phase 1: schemas, client ops, server handlers)#2818tobi-oye wants to merge 2 commits into
tobi-oye wants to merge 2 commits into
Conversation
…, server handlers) Adds the first phase of first-class Skills extension support behind new `/ext/skills` subpath exports. Root barrels are unchanged. - `@modelcontextprotocol/core/ext/skills` — shared, runtime-neutral schemas, inferred types, wire constants and `skillsCapabilityOf()`. - `@modelcontextprotocol/client/ext/skills` — `listSkills()`, `getSkill()` and `getSkillsCapability()`, gated on the server advertising both `io.modelcontextprotocol/skills` and `resources`. - `@modelcontextprotocol/server/ext/skills` — `installSkills()` declares the extension capability and serves `skills/list` / `skills/get` from caller-provided skill definitions. The skills result schemas deliberately carry no `resultType` member: the 2026-07-28 era codec validates that discriminator on decode and consumes it before any caller-supplied result schema runs, so a schema that re-declares it can never match (modelcontextprotocol#2789). Regression tests pin this end to end. Metadata surface only — filesystem discovery, digest-verified resource reads and `resources/directory/read` follow separately. Refs modelcontextprotocol#2798 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@modelcontextprotocol/client
@modelcontextprotocol/codemod
@modelcontextprotocol/core
@modelcontextprotocol/server
@modelcontextprotocol/server-legacy
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
`packageTopologyPins` asserts each published package's export-map keys exactly, so the new `./ext/skills` subpath on core, client and server trips it by design. Per docs/behavior-surface-pins.md the pin is updated in the same PR rather than loosened, with a note on each entry recording why the subpath is public and why it stays off the root barrel. No behavior change: this updates the expectation only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: b009106 The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds first-class support for the Skills extension (SEP-2640) to the SDK.
A "skill" is a folder of instructions that a server publishes to a client. It has a
SKILL.mdmanifest with YAML frontmatter, plus any number of supporting files. SEP-2640 defines two methods for discovering them —skills/listandskills/get— and a capability,io.modelcontextprotocol/skills, that a server uses to advertise support.Today the SDK has no typed API for any of this. Anyone serving skills has to hand-write the Zod schemas and register raw custom request handlers. This PR gives all three packages a proper typed surface for it.
The new APIs live behind
/ext/skillssubpath exports, following the layout the Tasks extension introduced in #2782. No new workspace package was added, and the root barrels of every package are untouched.What this adds
core@modelcontextprotocol/core/ext/skillsskillsCapabilityOf()client@modelcontextprotocol/client/ext/skillslistSkills(),getSkill(),getSkillsCapability()server@modelcontextprotocol/server/ext/skillsinstallSkills()On the server, one call declares the capability and registers both handlers:
On the client, the two operations are free functions that take the client as their first argument:
listSkills()fetches a single page. To walk all of them, pass the previous result'snextCursorback asparams.cursorand stop when a result comes back without one.Validation
The schemas enforce what the SEP requires, rather than accepting loosely shaped input:
sha256:followed by 64 lowercase hex characters.resourcesaccepts either the array of entries or the literal string"dynamic", for servers that generate content per request and cannot publish digests ahead of time.nameanddescription, and preserves any further keys the author wrote verbatim.Capability negotiation
installSkills()publishesio.modelcontextprotocol/skillsintoServerCapabilities.extensions. That record already exists in the core schemas, so no core schema change was needed.On the client side,
listSkills()andgetSkill()both refuse to send a request unless the server advertised both the skills extension and theresourcescapability. SEP-2640 requires servers to declareresourcesalongside the extension, because every skill entry points at resource URIs the host later reads throughresources/read. A call against a server missing either one throwsSdkErrorwithCapabilityNotSupported, rather than failing later on the wire.Runtime neutrality
core/ext/skillsimports nothing beyondzod/v4and the existing core schema modules. It pulls in no Node built-ins and no YAML dependency, so the subpath stays safe to bundle for browsers and Cloudflare Workers.Motivation and Context
Requested in #2798, which lays out a four-phase plan for bringing Skills into the SDK. This PR is phase 1 of that plan.
It is deliberately scoped to the metadata surface only — the schemas, the two discovery methods, and capability negotiation. That is the smallest piece that is useful on its own, and it is small enough to review in one sitting.
The following are explicitly not in this PR, and will follow separately:
resources/directory/readand thedirectoryRead: truecapability flag.The files a skill points at remain ordinary MCP resources in this phase. Servers register and serve them exactly as they do today.
A note on #2789, which reported that skills results always fail validation
#2789 reports that
skills/listandskills/getfail client-side validation even when the server's response is fully spec-conforming, with an error like:I traced this before writing any schemas, because the issue asks whether a fix is needed at the codec boundary.
It is not an SDK bug, and no codec change is needed. Here is what actually happens:
resultTypediscriminator on the wire.packages/core-internal/src/wire/rev2026-07-28/codec.ts).So a result schema that re-declares
resultTypecan never match. The client removes the field that the schema is about to require. This is intentional and documented inpackages/core/src/schemas.ts, where the neutralResultSchemaexplains why it models noresultTypemember.The schemas in the original prototype re-declared it, which is what produced the reported failure. The schemas in this PR extend
ResultSchemaandPaginatedResultSchema, so they inherit the correct behaviour and the problem does not arise.No validation was weakened to achieve this. The codec still hard-requires
resultTypeon the wire and still rejects a response that omits it. The change is only in where the field is modelled. A client test in this PR drives a real, spec-conformingresultType: "complete"response through the live decode path to pin the behaviour against regressions.How Has This Been Tested?
38 new tests were added: 16 in
core, 13 inserver, 9 inclient. They cover only the new public behaviour:resourcesis missing.skills/listandskills/getcalls.SKILL.mdURI. Both must return-32602, as the SEP requires.Everything below was run locally and passed:
tsgo --noEmitacrosscore,client,servereslintandprettier --checkacrosscore,client,servertsdownbuild for all three packages@modelcontextprotocol/core/ext/skillscorrectly stays an external import rather than being inlinedcoretest suiteservertest suiteclienttest suitenode scripts/smoke-dist-types.mjsThe conformance suite in modelcontextprotocol/conformance#330 is the acceptance target named in #2798.
Breaking Changes
None.
Every new API is additive and reachable only through a new subpath export. No existing export, type, or runtime behaviour was modified. The root barrels of
core,client, andserverare byte-identical tomain.Changes to existing files are limited to 50 lines across 8 configuration files:
package.jsonexports andtypesVersions,tsdown.config.tsbuild entries, andtsconfig.jsonpath mappings.Types of changes
Checklist
Additional context
Attribution
This work adapts the Apache-2.0
@olaservo/ext-skillsprototype (modelcontextprotocol/ext-skills#71), as #2798 suggested. Where the prototype and the final SEP disagree, this PR follows the SEP. Attribution notices are in the module headers of all threeext/skillsentry points.On documentation
This PR adds JSDoc on every exported symbol, usage examples on the three main entry points, and a changeset describing the full surface. It does not add a page under
docs/. That felt premature while the extension surface is still landing in phases. Happy to add one in this PR if you would prefer it now.Two open questions for maintainers
Both of these touch code owned outside this extension, so I left both alone rather than deciding unilaterally.
1. Where should
ttlMsandcacheScopecome from?SEP-2640 makes skills results
CacheableResultextenders on revision 2026-07-28, and the draft leg of the conformance suite expects those two fields onskills/list.The SDK already has machinery for this, but
CACHEABLE_RESULT_METHODSinpackages/core-internal/src/shared/resultCacheHints.tsis a deliberately closed list of six core operations. Two existing tests assert it is "closed at exactly six operations".Rather than change that invariant from an extension PR,
installSkills()emits the fields itself through an optionalcacheHintoption. This is the same approach the conformance fixture in #2797 takes.If you would rather fold skills into core's cacheable set, that is a two-line change plus updates to those two tests. Happy to do it either way — it seemed like a core-owned decision.
2. Where should the Node-specific skills code live?
#2798 names
@modelcontextprotocol/node/ext/skillsas the home for filesystem discovery and YAML parsing in a later phase.That package currently lives under
packages/middleware/, andCLAUDE.mdstates that middleware packages "should not add new MCP functionality". Filesystem skill discovery is new functionality, so it does not obviously belong there.Guidance on the right home would be useful before I open that follow-up PR.
🤖 Generated with Claude Code