Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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.
92 changes: 90 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,95 @@ 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, get, and directory 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.

### 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
51 changes: 49 additions & 2 deletions internal/docs/client.src.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,5 +233,52 @@ 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:

%include ../../skills/example_test.go skillsclient -

`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:

%include ../../skills/example_test.go skillsverify -

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.
Loading
Loading