Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
db8cd34
skills: add SEP-2640 protocol support
sambhav Sep 4, 2026
e6d64a1
skills: add filesystem provider helper
sambhav Sep 4, 2026
a33c199
skills: expose live resource listings and retry stale catalog refreshes
sambhav Sep 6, 2026
e8a7b97
Merge branch 'main' into skills-sep-2640
sambhav Sep 6, 2026
2931ffd
Merge branch 'main' into skills-sep-2640-fs-helper
sambhav Sep 7, 2026
9fd69b3
Merge branch 'main' into skills-sep-2640
sambhav Sep 7, 2026
e1f0246
skills: simplify validation and client configuration, align current p…
sambhav Sep 11, 2026
284092e
skills: align docs, examples, and verification semantics
sambhav Sep 11, 2026
26a90b2
skills: distinguish default and explicit manifest limits
sambhav Sep 11, 2026
458f497
skills: make manifest caps opt-in and fix protocol validation
sambhav Sep 11, 2026
64566ad
skills: keep pending conformance scenarios local
sambhav Sep 11, 2026
a7e923a
skills: keep conformance instructions in the PR description
sambhav Sep 11, 2026
32871f2
scripts: support conformance repository revisions in the server runner
sambhav Sep 11, 2026
5b4b377
skills: simplify envelope stamping, pagination, and URI parsing
sambhav Sep 11, 2026
7732974
go.mod: mark golang.org/x/sync as a direct dependency
sambhav Sep 11, 2026
fade7b3
skills: restructure the tests as tables
sambhav Sep 11, 2026
a0f0674
Merge branch 'main' into skills-sep-2640
sambhav Sep 14, 2026
98deff2
Merge branch 'main' into skills-sep-2640
guglielmo-san Sep 23, 2026
21ad3c2
skills: address review with session helpers, safe list filtering, and…
sambhav Oct 9, 2026
4738f7b
skills: update filesystem helper for reviewed protocol API
sambhav Oct 9, 2026
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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ The SDK consists of several importable packages:
- The
[`github.com/modelcontextprotocol/go-sdk/oauthex`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/oauthex)
package provides extensions to the OAuth protocol, such as ProtectedResourceMetadata.
- The
[`github.com/modelcontextprotocol/go-sdk/skills`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/skills)
package provides opt-in Skills extension support for discovery, directory
browsing, and content verification. See the [client](docs/client.md#skills-extension)
and [server](docs/server.md#skills-extension) examples.

The SDK endeavors to implement the full MCP spec. The [`docs/`](/docs/) directory
contains feature documentation, mapping the MCP spec to the packages above.
Expand Down
7 changes: 7 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ The SDK consists of several importable packages:
[`github.com/modelcontextprotocol/go-sdk/oauthex`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/oauthex)
package provides extensions to the OAuth protocol, such as ProtectedResourceMetadata.

- The
[`github.com/modelcontextprotocol/go-sdk/skills`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/skills)
package provides opt-in Skills extension support for discovery, directory
browsing, and content verification.


These docs mirror the [official MCP spec](https://modelcontextprotocol.io/specification/2026-07-28).
Use the index below to learn how the SDK implements a particular aspect of the
Expand All @@ -41,12 +46,14 @@ protocol.
1. [Roots](client.md#roots)
1. [Sampling](client.md#sampling)
1. [Elicitation](client.md#elicitation)
1. [Skills extension](client.md#skills-extension)

## Server Features

1. [Prompts](server.md#prompts)
1. [Resources](server.md#resources)
1. [Tools](server.md#tools)
1. [Skills extension](server.md#skills-extension)
1. [Utilities](server.md#utilities)
1. [Completion](server.md#completion)
1. [Logging](server.md#logging)
Expand Down
87 changes: 86 additions & 1 deletion docs/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -544,5 +544,90 @@ client := mcp.NewClient(impl, &mcp.ClientOptions{
adds an `extensions` map to `ClientCapabilities` and `ServerCapabilities` so
that optional capabilities outside the core protocol can be declared on the
wire. Keys are namespaced as `"{vendor-prefix}/{extension-name}"`; values
are per-extension settings objects.
are per-extension settings objects. Extensions require explicit opt-in.

#### Skills extension

The [`skills`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/skills)
package provides typed calls for the
[Skills extension](https://github.com/modelcontextprotocol/ext-skills/blob/main/specification/stable/skills.mdx).
Call package-level functions with the connected session. This example connects to the server from the
[server example](server.md#skills-extension) over an in-memory transport:

```go
ctx := context.Background()
client := mcp.NewClient(&mcp.Implementation{Name: "skills-client", Version: "v1.0.0"}, nil)

serverTransport, clientTransport := mcp.NewInMemoryTransports()
serverSession, err := server.Connect(ctx, serverTransport, nil)
if err != nil {
log.Fatal(err)
}
defer serverSession.Close()
session, err := client.Connect(ctx, clientTransport, nil)
if err != nil {
log.Fatal(err)
}
defer session.Close()

for skill, err := range skills.All(ctx, session, nil) {
if err != nil {
log.Fatal(err)
}
fmt.Println(skill.URI, skill.Frontmatter["description"])
}
```

`List`, `Get`, and `All` validate structure without imposing count or size caps.
They support the required baseline of 512 files and 16 MiB per skill, as well as
larger manifests. Application download budgets for dynamic content remain the
host's responsibility.

List calls reject invalid entries by default. To retain valid entries from a
malformed page, pass `skills.ListOptions{SkipInvalidSkills: true}` to `List` or
`All`. `List` reports skipped entries in `InvalidSkills`; `OnInvalidSkill` can
report them during iteration. Missing/null lists, invalid cache hints, and
invalid result types still fail. All entries with a duplicate URI are skipped,
so no duplicate silently replaces another. Page cursors are preserved.

`ReadDirectory` and `DirectoryEntries` expose optional
directory browsing when the server advertises `directoryRead: true`. Calls fail
if the required server capabilities are absent. Iterators follow cursors without
modifying request parameters and stop after the first error.

Listing does not fetch content. Read files on demand with `session.ReadResource`
and check them with `skills.VerifyResource` or `skills.VerifySkillMD` before use.
A listed entry is complete; `Get` also retrieves a skill directly by URI even
when it was not listed. For example, when the user chooses to load a known skill:

```go
result, err := skills.Get(ctx, session, &skills.GetSkillParams{URI: "skill://greeting/SKILL.md"})
if err != nil {
log.Fatal(err)
}
resource, err := session.ReadResource(ctx, &mcp.ReadResourceParams{URI: result.Skill.URI})
if err != nil {
log.Fatal(err)
}
if len(resource.Contents) != 1 || resource.Contents[0] == nil || resource.Contents[0].URI != result.Skill.URI || resource.Contents[0].Blob != nil {
log.Fatal("expected one text resource for SKILL.md")
}
if err := skills.VerifySkillMD(result.Skill, []byte(resource.Contents[0].Text)); err != nil {
log.Fatal(err)
}
fmt.Println("verified", result.Skill.URI)
```

Keep skill entries scoped to their originating session: equal URIs from different
servers are different skills. Use a host-assigned server identity when persisting
entries or approvals. Directory results are live observations; they do not expand
the files authorized by a held manifest.

`VerifyResource` checks manifest membership, byte length, and SHA-256 digest.
`VerifySkillMD` also compares every frontmatter field. JSON frontmatter numbers
are decoded as `json.Number` to preserve integer precision. For dynamic manifests,
`VerifySkillMD` still checks frontmatter and returns `skills.ErrDynamicResources`
only when it matches; malformed or mismatched frontmatter returns a different error.
Applications decide whether to accept content without integrity verification and
own skill approval and execution policy. A digest match alone does not make remote
instructions trustworthy.
182 changes: 180 additions & 2 deletions docs/server.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ The SDK ensures that a read succeeds only if the URI matches a registered resour
or matches the URI pattern of a resource template.

To list a server's resources and resource templates, use the
[`ClientSession.Resources`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Resources)
[`ClientSession.Resource`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Resource)
and
[`ClientSession.ResourceTemplates`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.ResourceTemplates)
iterators, or the lower-level `ListXXX` calls (see [pagination](#pagination)).
Expand Down Expand Up @@ -1203,7 +1203,185 @@ server := mcp.NewServer(impl, &mcp.ServerOptions{
adds an `extensions` map to `ServerCapabilities` so that optional
capabilities outside the core protocol can be declared on the wire. Keys
are namespaced as `"{vendor-prefix}/{extension-name}"`; values are
per-extension settings objects.
per-extension settings objects. Extensions require explicit opt-in.

#### Skills extension

The [`skills`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/skills)
package implements the
[Skills extension](https://github.com/modelcontextprotocol/ext-skills/blob/main/specification/stable/skills.mdx).
Use `skills.AddHandlers` for request-time
`skills/list` and `skills/get` handlers. An optional directory handler enables
`resources/directory/read` and advertises `directoryRead: true`.

Before calling `AddHandlers`, register the underlying content through `Server.AddResource` or
`Server.AddResourceTemplate`; these also advertise the required `resources`
capability. Registration fails if the effective resources capability is absent.
An entry's manifest includes every file, including `SKILL.md` and
nested skills. Use `skills.DynamicResources()` when stable digests cannot be
published, not simply because the catalog changes over time.

This example serves a complete static manifest and its content. The
[client example](client.md#skills-extension) connects to this server and verifies
the resource bytes:

```go
server := mcp.NewServer(&mcp.Implementation{Name: "skills", Version: "v1.0.0"}, nil)
const uri = "skill://greeting/SKILL.md"
const content = "---\nname: greeting\ndescription: Greet the user.\n---\n# Greeting\nSay hello to the user.\n"
entry := &skills.Skill{
URI: uri,
Frontmatter: skills.Frontmatter{
"name": "greeting", "description": "Greet the user.",
},
Resources: skills.StaticResources(&skills.Resource{
URI: uri, Digest: fmt.Sprintf("sha256:%x", sha256.Sum256([]byte(content))), Size: int64(len(content)),
}),
}

server.AddResource(&mcp.Resource{
URI: uri, Name: "greeting", Description: "Greet the user.", MIMEType: "text/markdown",
}, func(context.Context, *mcp.ReadResourceRequest) (*mcp.ReadResourceResult, error) {
return &mcp.ReadResourceResult{Contents: []*mcp.ResourceContents{{
URI: uri, MIMEType: "text/markdown", Text: content,
}}}, nil
})
err := skills.AddHandlers(server, &skills.Handlers{
List: func(_ context.Context, _ *mcp.ServerSession, params *skills.ListSkillsParams) (*skills.ListSkillsResult, error) {
page, next, err := skills.PaginateSkills([]*skills.Skill{entry}, params.Cursor, 0)
return &skills.ListSkillsResult{Skills: page, NextCursor: next}, err
},
Get: func(_ context.Context, _ *mcp.ServerSession, params *skills.GetSkillParams) (*skills.GetSkillResult, error) {
if params.URI != entry.URI {
return nil, nil
}
return &skills.GetSkillResult{Skill: entry}, nil
},
}, nil)
if err != nil {
log.Fatal(err)
}
```

Return `(nil, nil)` from the get or directory handler for an unknown URI; the SDK
returns JSON-RPC Invalid Params (`-32602`). An empty directory has a non-nil result
with an empty resource list. Explicit JSON-RPC errors retain their code and data;
other handler errors and invalid results become Internal Error (`-32603`).

Handlers own pagination. `skills.PaginateSkills` and
`skills.PaginateDirectoryResources` sort by URI and return one page without
modifying the input slice. A zero page size uses `mcp.DefaultPageSize`;
`mcp.ServerOptions.PageSize` does not configure custom Skills handlers. Each skill
entry contains its complete manifest, which is never split across pages.

`skills.ServerOptions.Limits` is a `skills.Limits` value. By default it imposes no
manifest caps. Positive fields set exact caps, zero fields are unlimited, and
negative fields are invalid. Set `Limits: skills.BaselineLimits()` to opt into
the spec's interoperability baseline of 512 resources and 16 MiB per skill.
Servers should stay within this baseline for broad compatibility; serving larger
skills is allowed. Clients accept larger structurally valid manifests; these
server caps govern publication policy only.

Structural validation always runs; put additional application policy in the
handlers themselves. Dynamic content budgets belong to the application; the SDK
does not accumulate sizes across resource reads. It copies options at registration and
prepares outgoing results without mutating handler-owned data.

On protocol `2026-07-28` and later, list and get responses carry `ttlMs` and
`cacheScope`, defaulting to zero and `public`. Handlers can supply explicit hints
through the result's `mcp.Cacheable` field. The SDK also supports the extension on
earlier protocols as a compatibility backport, omitting cache fields and
`resultType`. The extension does not prefetch files or start background work.

#### Filesystem helper

For skills stored on disk, `skills.AddDirectory` installs a filesystem-backed
provider. By default it discovers skills and files on every request, so
resources added after server startup are available without re-registering
them. This applies to `skills/list`, `skills/get`, `resources/list`,
`resources/read`, and `resources/directory/read`. Changes and removals are
visible on the next request too.

```go
if err := skills.AddDirectory(server, "./skills", &skills.DirectoryOptions{
PageSize: 100,
}); err != nil {
return err
}
```

`resources/list` includes the skill files alongside ordinary registered
resources. `SKILL.md` entries carry their frontmatter name and description and
the `text/markdown` MIME type. Directory reads return only direct children,
including subdirectories with MIME type `inode/directory`, and use the same
file metadata. Neither listing reads supporting file contents just to enumerate
them; `skills/list` and `skills/get` also hash files to build static manifests.

The helper combines all pages of the underlying resource listing with its
current catalog, deduplicates by URI, and paginates the result using
`DirectoryOptions.PageSize`. Exact resource registrations take precedence over
the template, for both listing and reading. This uses existing receiving
middleware and resource-template routing; it does not change the core SDK APIs.
The merge costs a traversal of the underlying resource list on each request.

Live discovery does not require `skills.DynamicResources()`: that marker means
the server cannot provide a complete manifest with stable digests, not that its
catalog changes over time. A filesystem provider returns a complete static
manifest for the current catalog; a later request can return a different one.

Clients must re-list to see changes. The helper starts no watcher and sends no
filesystem-change notifications; SEP-2640 defines no `skills/list_changed`
notification. The merged `resources/list` response has a zero TTL and private
cache scope so a TTL configured for ordinary resources cannot conceal changes.
These cache fields are omitted on older protocol versions by the core SDK.

Set `Cache` to `&skills.DirectoryCacheOptions{}` to load the catalog on the
first request and cache it indefinitely. Set `Preload: true` to load it while
constructing the provider, which also makes the constructor report initial scan
and validation errors. A positive `MaxAge` expires the cache after that
duration; the first request after expiry rebuilds it.

Cached providers can also be invalidated by a clock or filesystem monitor through
`DirectoryCacheOptions.Invalidate`. Signals are coalesced and consumed when a
request arrives; use a buffered channel so producers do not block. Both
`MaxAge` and `Invalidate` are lazy: they do not start a background goroutine.
A failed rebuild retains the previous catalog but leaves it stale: requests
retry rebuilding until successful, even if the invalidation signal has already
been consumed. They return the scan error instead of silently serving stale
metadata. Resource bytes are always read on demand, including in cached modes.

To rebuild before the next request, construct a provider directly and call
`Refresh` from the application's watcher goroutine. The caller owns goroutine
lifetime, cancellation, and error handling:

```go
provider, err := skills.NewDirectoryProvider("./skills", &skills.DirectoryOptions{
Cache: &skills.DirectoryCacheOptions{Preload: true},
})
if err != nil {
return err
}
if err := provider.AddTo(server); err != nil {
return err
}
go func() {
for range changed {
if err := provider.Refresh(ctx); err != nil {
logger.Error("refreshing skills", "error", err)
}
}
}()
```

Register one filesystem provider per server. Applications that need to combine
multiple filesystems can use an overlay `fs.FS` or aggregate them behind custom
`AddHandlers` handlers.

Filesystem manifests use `skills.BaselineLimits()` by default. Override publication
caps through `DirectoryOptions.ServerOptions.Limits`; an explicit zero value
removes caps. Structural validation always runs. Catalog validators can add
application checks. Options are copied at construction.


### Pagination

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require (
golang.org/x/oauth2 v0.35.0
golang.org/x/time v0.15.0
golang.org/x/tools v0.42.0
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
7 changes: 7 additions & 0 deletions internal/docs/README.src.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ The SDK consists of several importable packages:
[`github.com/modelcontextprotocol/go-sdk/oauthex`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/oauthex)
package provides extensions to the OAuth protocol, such as ProtectedResourceMetadata.

- The
[`github.com/modelcontextprotocol/go-sdk/skills`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/skills)
package provides opt-in Skills extension support for discovery, directory
browsing, and content verification.


These docs mirror the [official MCP spec](https://modelcontextprotocol.io/specification/2026-07-28).
Use the index below to learn how the SDK implements a particular aspect of the
Expand All @@ -40,12 +45,14 @@ protocol.
1. [Roots](client.md#roots)
1. [Sampling](client.md#sampling)
1. [Elicitation](client.md#elicitation)
1. [Skills extension](client.md#skills-extension)

## Server Features

1. [Prompts](server.md#prompts)
1. [Resources](server.md#resources)
1. [Tools](server.md#tools)
1. [Skills extension](server.md#skills-extension)
1. [Utilities](server.md#utilities)
1. [Completion](server.md#completion)
1. [Logging](server.md#logging)
Expand Down
Loading
Loading