Skip to content

skills: add filesystem provider helper - #1240

Draft
sambhav wants to merge 3 commits into
modelcontextprotocol:mainfrom
sambhav:skills-sep-2640-fs-helper
Draft

skills: add filesystem provider helper#1240
sambhav wants to merge 3 commits into
modelcontextprotocol:mainfrom
sambhav:skills-sep-2640-fs-helper

Conversation

@sambhav

@sambhav sambhav commented Sep 4, 2026

Copy link
Copy Markdown
Member

Summary

Add an optional filesystem provider for SEP-2640, covering discovery, metadata, manifests, resource reads, directory browsing, pagination, and refresh policy.

The latest revision adds:

  • Live resources/list enumeration alongside ordinary registered resources.
  • Consistent SKILL.md name, description, and MIME type in resource and directory listings.
  • Retries after failed invalidated-cache rebuilds, preventing an old catalog from remaining cached indefinitely.

These changes exercise both previously untestable metadata SHOULD checks in conformance #330.

Review status

This is PR 2 of 2, depends on #1238, and remains a draft.

The helper-only diff is available at sambhav/go-sdk#1. The upstream diff includes the generic protocol layer until #1238 merges; this branch can then be rebased onto main and marked ready.

Usage

server := mcp.NewServer(&mcp.Implementation{
    Name: "skill-server", Version: "v1",
}, nil)

if err := skills.AddDirectory(server, "./skills", nil); err != nil {
    log.Fatal(err)
}

Use skills.AddFS(server, fsys, options) for embed.FS, fstest.MapFS, or another fs.FS. Mutable filesystem implementations synchronize themselves.

By default, requests discover additions, changes, and removals:

  • skills/list and skills/get return complete manifests, including nested skills.
  • resources/list includes skill files alongside ordinary registered resources.
  • resources/read reads file bytes on demand through resource-template dispatch.
  • resources/directory/read returns paginated direct children, including empty subdirectories marked inode/directory.
  • Resource and directory listings expose frontmatter name/description and text/markdown for SKILL.md.

A changing catalog can still return complete static manifests. DynamicResources() means a complete manifest with stable digests is unavailable.

Refresh and caching

Configuration Catalog behavior
Cache: nil (default) Rebuild on each request
Cache: &DirectoryCacheOptions{} Lazy load, then cache indefinitely
Cache.Preload: true Build and validate during construction
Cache.MaxAge or Cache.Invalidate Rebuild on the next request after expiry or invalidation
provider.Refresh(ctx) Rebuild immediately; requires caching

For explicit refresh, construct a provider with NewDirectoryProvider or NewFSProvider, then call provider.AddTo(server).

Successful rebuilds replace the cached catalog atomically. Failed rebuilds retain the previous catalog but leave it stale: subsequent requests retry and return scan errors until rebuilding succeeds.

Resource bytes remain on demand even with cached metadata, so clients still need manifest verification after edits.

Resource and directory enumeration walk metadata and parse skill frontmatter without hashing supporting files. Skills enumeration and cache rebuilds hash complete manifests; each physical supporting file is hashed once per rebuild, including across nested skills.

The application owns filesystem watchers, timers, cancellation, and shutdown. Clients re-list to observe changes; this helper starts no watcher and emits no filesystem-change notifications. SEP-2640 defines no skills/list_changed notification.

Resource composition

The helper uses existing receiving middleware and resource-template routing, adding no further core MCP APIs beyond #1238. DirectoryProvider.ListResources is also available directly.

For resources/list, the middleware:

  • Traverses all pages of the underlying resource list.
  • Merges entries by URI, with explicit registrations taking precedence.
  • Paginates the combined list using DirectoryOptions.PageSize.
  • Preserves caller parameters and upstream results, propagates errors, and rejects repeated cursors.

The tradeoff is a full traversal of the underlying resource list per request. Combined lists use zero TTL and private cache scope so cached ordinary-resource listings cannot hide filesystem changes. Cache fields are omitted on older protocol versions.

Register one filesystem provider per server. Multiple roots can use an overlay filesystem or generic aggregation handlers.

Symlinks and non-regular files are rejected. SEP and Agent Skills validation, including the default 512-resource and 16 MiB limits, remains enabled with custom validators and explicit unsafe overrides.

Interoperability

TypeScript supports request-time resource-template list callbacks, and Python’s low-level SDK supports request-time resource-list handlers. Go already supports runtime registry changes and receiving middleware; this provider uses those existing extension points.

Inspector #2251 consumes skills/list and skills/get directly, including the modern list envelope. Directory browsing is tracked separately in #2248, with detail-pane improvements in #2265.

Conformance and validation

Validated on 2026-09-06 using conformance #330 at fa8aaec3611e387249da6adc826b02dd77372a3e, against the combined protocol/helper stack at a33c199eead5817160581ac834fb0e11e7e0a390.

The fixture includes multiple and nested skills, supporting directories, an ordinary registered resource, and page size 1.

Protocol / transport Enumeration Manifest Directory Total
2025-11-25 / stateful 29 pass, 1 warning 6 pass 7 pass 42 pass, 0 failures, 1 warning
2026-07-28 / stateless 30 pass 6 pass 7 pass 43 pass, 0 failures, 0 warnings

Totals include each scenario’s wire-schema-valid check. Both previously untestable resource metadata SHOULD checks now pass.

Older-version cache-field compatibility is already handled by #1238: skills/list omits ttlMs and cacheScope before 2026-07-28.

The sole legacy warning is a conformance-harness false positive. Its cache-attributes check warns about missing fields without checking the effective protocol version. The harness should report this requirement as SKIPPED on older versions and enforce it on 2026-07-28 and later.

All upstream workflows passed on the tested commit: Test, Docs Check, Conformance Tests, and CodeQL. Local skills race tests, vet, and build also passed.

For legacy conformance runs, use --spec-version 2025-11-25 --force. The modern fixture uses StreamableHTTPOptions{Stateless: true}.

@sambhav
sambhav force-pushed the skills-sep-2640-fs-helper branch from bd1e1fa to e6d64a1 Compare September 4, 2026 22:48
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.

1 participant