Support the Skills extension (SEP-2640)

Aberta
#1,391 1 comentário 0 reações 1 responsável Ver no GitHub

Mantenedores costumam responder em até 2 dias

@DaleSeo já está trabalhando nisso.

Desde 23/10/2026.

  • #1392 de @DaleSeo — integrado
  • #1409 de @DaleSeo — aberto

Avaliação

Esta issue ainda não foi avaliada.

Descrição

enhancement P2 ready for work T-enhancement T-handler T-model T-service

Summary

Add support for the Skills extension (SEP-2640, io.modelcontextprotocol/skills) to rmcp. The extension adds three methods: skills/list, skills/get, and the optional resources/directory/read. This issue covers both the server side and the client side.

Requirements

A server built with rmcp can:

  • Declare the extension in capabilities.extensions, with directoryRead only when it serves resources/directory/read.
  • Answer skills/list and skills/get with typed results, and resources/directory/read when declared.
  • Serve the files of a skill through resources/read, so that the bytes match the size and digest in the manifest.
  • Return the error codes that the spec defines: -32601 for a method it does not serve, -32602 for an unknown skill or a non-directory URI.
  • Send ttlMs and cacheScope to peers on protocol version 2026-07-28.

A client built with rmcp can:

  • Detect the extension and its directoryRead setting from the server capabilities.
  • Call the three methods through typed Peer methods, and receive server errors unchanged.
  • Verify a fetched file against the size and digest in the manifest.

Both sides pass the sep-2640-* conformance scenarios.

The spec targets protocol revision 2026-07-28 or later. rmcp also serves the methods to peers on an older revision and removes the cache hints from those responses, the same as the TypeScript, Python, and Go SDKs do. This needs the legacy strip helper to clear ttlMs and cacheScope as well as resultType, which also closes the same gap for the existing list results.

Proposal

This is how I plan to design and implement it. The choices below are open for discussion.

PR 1: protocol surface

Everything the spec defines, wired the same way as the tasks extension and without a feature flag.

  • Model types for the skill manifest and the three methods in model/skill.rs, with the matching ClientRequest and ServerResult variants. ReadResourceDirectoryResult is a type alias for ListResourcesResult, and ServerResult reuses the existing ListResourcesResult variant for it. No new directory variant: the two schemas are identical and the untagged ServerResult cannot tell them apart (see #771).
  • Three ServerHandler methods with capability-gated dispatch. Dispatch checks what the result object alone can show: the skill URI matches the request, the name matches the last URI segment, the manifest contains SKILL.md and no duplicate URIs, and every digest has the sha256: form. A result that fails these checks returns -32603. Whether the bytes behind each URI match the manifest is checked by the catalog (PR 2) and by the host that reads them, not by dispatch.
  • enable_skills() and enable_skills_with_directory_read() on the capability builder, with the matching supports_* getters. Skills requests fail with -32603 when the server declares skills without resources, because the extension serves its files through resources/read.
  • The matching Peer methods for the client. They accept a 2026-07-28 response that omits the cache hints, the same as list_tools does today; the TypeScript and Go clients reject such a response, rmcp does not. SkillResource::verify(&[u8]) checks the size and the sha256: digest of one file. Frontmatter verification against skills/get is left to the host. sha2 becomes a regular dependency of rmcp: it is pure Rust, already in the tree for request-state, and a client must not have to enable a filesystem feature to verify a download.
  • Integration tests through Peer over a real transport: pagination across skills/list, a -32602 from skills/get arriving unchanged, read_resource_directory not sent when directoryRead is absent, the directory result arriving as ListResourcesResult, and verify failing on a wrong size and on a wrong digest.
  • Conformance fixtures with a nested skill, a subdirectory, and a "dynamic" skill, and the scenarios in conformance.yml.
PR 2: filesystem catalog

Behind a skills-fs feature. SkillCatalog::from_dir walks a directory tree and builds the manifests.

  • The catalog is a snapshot. It holds the file bytes and their digests, so resources/read serves the same bytes that the manifest describes, even if the files change on disk later.
  • reload() builds a new snapshot and swaps it in. If the rebuild fails, the old snapshot stays.
  • The walk rejects paths that leave the root, including through symlinks, and validates that each skill name matches its directory name. It skips a skill over 512 files or 16 MiB, as the spec asks servers not to serve one.
  • A nested skill appears in the listing on its own, and its files also appear in the manifest of the parent skill.
Follow-ups, not blocking
  • PR 1 adds the two new results to the hand-written lists in strip_result_type_for_legacy_peer and fill_missing_cache_hints. A non-breaking refactor right after PR 1 replaces both lists with a trait on each cacheable result and a dispatch that ts_union! generates, since the macro already enumerates every ServerResult variant.
  • A ResultEnvelope { result_type, meta, ttl_ms, cache_scope } flattened into each cacheable result, with the result structs marked #[non_exhaustive], so that the next mixin field from the spec is not a major release. Breaking, so it waits for the next major batch.
  • An Extension trait for declaring and reading extension capabilities, so that each extension adds one impl instead of four functions. Skills and tasks move onto it. Non-breaking.

Acceptance

  • The sep-2640-* conformance checks pass on the fixture server. The PR body lists the numbers.
  • Goose can list, fetch, and verify remote skills through Peer without custom requests.
  • For PR 2: a file served through resources/read always matches the manifest, a path outside the root is never served, and the files of a nested skill are listed in both manifests.
Linguagem predominante
Rust
Estrelas
4.1k
Forks
694
Merge médio
3d 14h
PRs com merge (30d)
39

Preparar o ambiente

Abrir no Codespaces

Inicia o contêiner de desenvolvimento do projeto no navegador, com a sua própria conta do GitHub.

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Mais de modelcontextprotocol/rust-sdk

Todas as issues de modelcontextprotocol/rust-sdk

Issues semelhantes

Mais issues de Rust

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.