[Schema][Server] Add Skills extension (io.modelcontextprotocol/skills) support by wachterjohannes · Pull Request #372 · modelcontextprotocol/php-sdk · GitHub
Skip to content

[Schema][Server] Add Skills extension (io.modelcontextprotocol/skills) support - #372

Draft
wachterjohannes wants to merge 3 commits into
modelcontextprotocol:mainfrom
wachterjohannes:feature/skills-extension
Draft

wachterjohannes wants to merge 3 commits into
modelcontextprotocol:mainfrom
wachterjohannes:feature/skills-extension

Conversation

@wachterjohannes

@wachterjohannes wachterjohannes commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Skills extension (io.modelcontextprotocol/skills, SEP-2640)

Implements the Skills extension. SEP-2640 reached Final status and merged into the specification on 2026-09-13. A skill is a directory of files, at minimum a SKILL.md with YAML frontmatter, served through the existing Resources primitive under skill://<skill-path>/<file-path>. Builder::addSkillsFromDirectory() walks a directory and registers every skill's files as resources, and enables the extension's two mandatory methods: skills/list, which enumerates the skills a server serves with a complete digest-and-size manifest per skill, and skills/get, which returns a single skill's entry by URI, including skills absent from the listing.

The manifest lets a host build its registry, present a skill for approval, and verify every later read without a follow-up call. skills/list results carry the base protocol's ttlMs/cacheScope caching attributes on protocol version 2026-07-28 and later, per the base list-caching mechanism (SEP-2549). Nested skills, a SKILL.md inside another skill's directory, are supported per the spec: a nested skill gets its own top-level entry under its own frontmatter, and still appears in the enclosing skill's resource manifest.

This PR was reworked from an earlier version built against a draft of the spec that used a discovery-index resource and packed archive distribution. The Core Maintainers removed both during review in favor of a complete resources manifest on every skill entry (see the SEP's Deferred Features appendix for the rationale); this implementation matches the Final spec text as merged.

Not implemented: the optional resources/directory/read method and its directoryRead capability flag, which this PR does not declare. Adding it later is backward compatible.

Adds symfony/yaml for frontmatter parsing. Docs are in docs/advanced/extensions.md, and a runnable example server is at examples/server/skills/. phpstan, php-cs-fixer, and the full test suite (1822 tests) are green, including an inspector stdio snapshot test and a dispatch-level test that sends real JSON-RPC requests through the server and asserts the caching envelope.

@wachterjohannes

Copy link
Copy Markdown
Contributor Author

@wachterjohannes

Copy link
Copy Markdown
Contributor Author

Updated to track the recent SEP-2640 redesign and to round out the index entries:

  • Index format realigned (2fb9722): entries now mirror the SKILL.md frontmatter verbatim and carry a SHA-256 digest; dropped the $schema field and the type/SkillType enum (the mcp-resource-template type was removed from the index in the redesign).
  • Archives (634f81e): skills can now also be served as one-shot packed resources via addSkillsFromDirectory(..., archiveFormats: ['application/gzip']) — a deterministic .tar.gz per skill, listed under the entry's archives, with the index digest matching the served bytes.

All green: phpstan (level 6), php-cs-fixer, 812 unit tests, inspector snapshot. Description updated accordingly.

Remaining SEP surface, as a follow-up: the optional resources/directory/read method (+ directoryRead capability), and ZIP archives.

chr-hertel added a commit to symfony/ai that referenced this pull request Jul 21, 2026
…or coding agents (wachterjohannes)

This PR was squashed before being merged into the main branch.

Discussion
----------

[Mate] Add skills:install to install extension skills for coding agents

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| Docs?         | yes
| Issues        | -
| License       | MIT

Agent Skills (`SKILL.md`) shipped by Mate extensions are now installed onto the
filesystem where coding agents read them — a polyfill until skills can be served
over MCP directly.

### Background — MCP Skills (SEP-2640)

This is the **consumer/polyfill side** of the MCP Skills effort:

- **SEP-2640** proposes serving Agent Skills over MCP's Resources primitive as
  `skill://` resources — modelcontextprotocol/modelcontextprotocol#2640.
- **#2132** already added skill *content* to the Mate Symfony bridge, anticipating
  the extension (agentskills.io + SEP-2640 layout) — #2132.
- **modelcontextprotocol/php-sdk#372** implements the *serving* half in the PHP
  SDK (`skill://` resources + discovery index).

The missing piece is **getting those skills onto the filesystem**: Claude Code and
other agents do not consume MCP-served skills yet (June 2026) — they only read
skills from disk. This PR bridges that gap and can be retired once agents consume
MCP-served skills end-to-end.

### How it works

Extensions declare one or more skills directories via a new `extra.ai-mate.skills` key:

```json
{
    "extra": {
        "ai-mate": {
            "scan-dirs": ["src"],
            "skills": ["skills"]
        }
    }
}
```

Each immediate subdirectory holding a `SKILL.md` is one skill. Mate **symlinks** each one
— under a `mate-` prefixed directory name (e.g. `mate-system-information`) to avoid
clashing with skills you maintain from other sources — into `.agents/skills/` (read by
Codex, OpenCode and GitHub Copilot) and mirrors it into `.claude/skills/` for Claude Code,
which only reads its own directory. The links point into the gitignored `vendor/`, so
skills auto-update on `composer update`.

### Usage

Skills install automatically as part of `discover`, which already runs after
`composer require`, so there's usually nothing to run:

```terminal
$ vendor/bin/mate discover
```

For an explicit re-sync:

```terminal
$ vendor/bin/mate skills:install
```

The core package ships a built-in `system-information` skill to demonstrate the
format and dogfood the mechanism.

### Notes

- Documentation added under `docs/components/mate.rst` and
  `docs/components/mate/creating-extensions.rst`; `CHANGELOG.md` updated.
- Covered by tests (installer, command, discovery, and the `discover` auto-sync);
  PHPStan clean.

Commits
-------

6a88faf [Mate] Add skills:install to install extension skills for coding agents
@chr-hertel

Copy link
Copy Markdown
Member

Even without mate needing this, this could become interesting - keeping this open! I guess it needs a heavier rebase nowadays tho, since the extension part changed with v0.8.0

@wachterjohannes

Copy link
Copy Markdown
Contributor Author

The ext-skills spec moved past PR modelcontextprotocol#372 before it was published as
specification/stable/skills.mdx: discovery is no longer a skill://index.json
resource but two mandatory RPC methods, skills/list and skills/get, each
returning a complete {uri, digest, size} manifest of every file in the skill.
This rewrites the extension to match, built directly against current main.

McpSkills is now a full ExtensionInterface implementation contributing
ListSkillsRequest/Result and GetSkillRequest/Result plus their handlers,
backed by a SkillRegistry that SkillProvider populates while it registers
skill:// resources. The old discovery-index and archive layer is gone along
with SkillDiscoveryIndex/Entry and SkillArchive/Archiver, since no listing
ever pointed at them once the index disappeared from the spec.

SkillMetadata now requires description, matching the spec's Skill.frontmatter
shape. The SKILL.md resource's name comes directly from the frontmatter name
instead of a generated slug, since names don't need to be registry-unique.
Extra frontmatter fields land under _meta as one io.modelcontextprotocol.skills/
prefixed key each, not one key holding the whole map. SkillProvider also
enforces the extension's fixed per-skill limits (512 resources, 16 MiB) and
records each file's byte size on its resource definition.

Current main already carries the ServerCapabilities empty-extension-payload
fix this PR originally introduced, independently.
…atch coverage, deterministic nested-skill registration

skills/list and skills/get were only tested by calling the handlers directly, so nothing exercised the actual MessageFactory/StatelessProtocol/Rev2026Codec dispatch path or asserted the mandatory resultType/ttlMs/cacheScope envelope. Added a dispatch test that builds a real server and sends JSON-RPC requests over StatelessProtocol, covering both methods, the -32602 case, and the capability advertisement.

SkillProvider also registered a nested skill's own SKILL.md twice as a servable resource: once as an anonymous supporting file of the enclosing skill (name from basename, no description) and once under its own frontmatter as its own top-level skill. Since the registry keeps the last registration for a given URI, which metadata won depended on filesystem sort order between the two directory names. The nested manifest is now registered exactly once, by its own entry, while still being listed in the enclosing skill's resources manifest as the spec requires. Added a nested-skill fixture pinning this.

Clarified the McpSkills docblock: addSkillsFromDirectory() and manually enabling the extension are alternatives, not composable — combining them throws because the convenience method always tries to enable its own instance.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants