Skip to content
Merged
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
20 changes: 11 additions & 9 deletions .github/workflows/readme-check.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
name: README Check
on:
workflow_dispatch:
workflow_dispatch:
pull_request:
paths:
- 'internal/readme/**'
- 'README.md'

- 'internal/docs/**'
- 'docs/**'

permissions:
contents: read

Expand All @@ -17,15 +19,15 @@ jobs:
uses: actions/setup-go@v5
- name: Check out code
uses: actions/checkout@v4
- name: Check README is up-to-date
- name: Check docs is up-to-date
run: |
go generate ./internal/readme
go generate ./...
if [ -n "$(git status --porcelain)" ]; then
echo "ERROR: README.md is not up-to-date!"
echo "ERROR: docs are not up-to-date!"
echo ""
echo "The README.md file differs from what would be generated by `go generate ./internal/readme`."
echo "Please update internal/readme/README.src.md instead of README.md directly,"
echo "then run `go generate ./internal/readme` to regenerate README.md."
echo "The docs differ from what would be generated by `go generate ./...`."
echo "Please update internal/**/*.src.md instead of directly editing README.md or docs/ files,"
echo "then run `go generate ./...` to regenerate docs."
echo ""
echo "Changes:"
git status --porcelain
Expand All @@ -34,4 +36,4 @@ jobs:
git diff
exit 1
fi
echo "README.md is up-to-date"
echo "Docs are up-to-date."
40 changes: 40 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!-- Autogenerated by weave; DO NOT EDIT -->
These docs are a work-in-progress.

# Features

These docs mirror the [official MCP spec](https://modelcontextprotocol.io/specification/2025-06-18).

## Base Protocol

1. [Lifecycle (Clients, Servers, and Sessions)](protocol.md#lifecycle).
1. [Transports](protocol.md#transports)
1. [Stdio transport](protocol.md#stdio-transport)
1. [Streamable transport](protocol.md#streamable-transport)
1. [Custom transports](protocol.md#stateless-mode)
1. [Authorization](protocol.md#authorization)
1. [Security](protocol.md#security)
1. [Utilities](protocol.md#utilities)
1. [Cancellation](utilities.md#cancellation)
1. [Ping](utilities.md#ping)
1. [Progress](utilities.md#progress)

## Client Features

1. [Roots](client.md#roots)
1. [Sampling](client.md#sampling)
1. [Elicitation](clients.md#elicitation)

## Server Features

1. [Prompts](server.md#prompts)
1. [Resources](server.md#resources)
1. [Tools](tools.md)
1. [Utilities](server.md#utilities)
1. [Completion](server.md#completion)
1. [Logging](server.md#logging)
1. [Pagination](server.md#pagination)

# TroubleShooting

See [troubleshooting.md](troubleshooting.md) for a troubleshooting guide.
168 changes: 168 additions & 0 deletions docs/client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
<!-- Autogenerated by weave; DO NOT EDIT -->
# Support for MCP client features

1. [Roots](#roots)
1. [Sampling](#sampling)
1. [Elicitation](#elicitation)

## Roots

MCP allows clients to specify a set of filesystem
["roots"](https://modelcontextprotocol.io/specification/2025-06-18/client/roots).
The SDK supports this as follows:

**Client-side**: The SDK client always has the `roots.listChanged` capability.
To add roots to a client, use the
[`Client.AddRoots`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#AddRoots)
and
[`Client.RemoveRoots`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Client.RemoveRoots)
methods. If any servers are already [connected](protocol.md#lifecycle) to the
client, a call to `AddRoot` or `RemoveRoots` will result in a
`notifications/roots/list_changed` notification to each connected server.

**Server-side**: To query roots from the server, use the
[`ServerSession.ListRoots`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.ListRoots)
method. To receive notifications about root changes, set
[`ServerOptions.RootsListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.RootsListChangedHandler).

```go
func Example_roots() {
ctx := context.Background()

// Create a client with a single root.
c := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, nil)
c.AddRoots(&mcp.Root{URI: "file://a"})

// Now create a server with a handler to receive notifications about roots.
rootsChanged := make(chan struct{})
handleRootsChanged := func(ctx context.Context, req *mcp.RootsListChangedRequest) {
rootList, err := req.Session.ListRoots(ctx, nil)
if err != nil {
log.Fatal(err)
}
var roots []string
for _, root := range rootList.Roots {
roots = append(roots, root.URI)
}
fmt.Println(roots)
close(rootsChanged)
}
s := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, &mcp.ServerOptions{
RootsListChangedHandler: handleRootsChanged,
})

// Connect the server and client...
t1, t2 := mcp.NewInMemoryTransports()
if _, err := s.Connect(ctx, t1, nil); err != nil {
log.Fatal(err)
}
if _, err := c.Connect(ctx, t2, nil); err != nil {
log.Fatal(err)
}

// ...and add a root. The server is notified about the change.
c.AddRoots(&mcp.Root{URI: "file://b"})
<-rootsChanged
// Output: [file://a file://b]
}
```

## Sampling

[Sampling](https://modelcontextprotocol.io/specification/2025-06-18/client/sampling)
is a way for servers to leverage the client's AI capabilities. It is
implemented in the SDK as follows:

**Client-side**: To add the `sampling` capability to a client, set
[`ClientOptions.CreateMessageHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.CreateMessageHandler).
This function is invoked whenever the server requests sampling.

**Server-side**: To use sampling from the server, call
[`ServerSession.CreateMessage`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.CreateMessage).

```go
func Example_sampling() {
ctx := context.Background()

// Create a client with a sampling handler.
c := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, &mcp.ClientOptions{
CreateMessageHandler: func(_ context.Context, req *mcp.CreateMessageRequest) (*mcp.CreateMessageResult, error) {
return &mcp.CreateMessageResult{
Content: &mcp.TextContent{
Text: "would have created a message",
},
}, nil
},
})

// Connect the server and client...
ct, st := mcp.NewInMemoryTransports()
s := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, nil)
session, err := s.Connect(ctx, st, nil)
if err != nil {
log.Fatal(err)
}
defer session.Close()

if _, err := c.Connect(ctx, ct, nil); err != nil {
log.Fatal(err)
}

msg, err := session.CreateMessage(ctx, &mcp.CreateMessageParams{})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Content.(*mcp.TextContent).Text)
// Output: would have created a message
}
```

## Elicitation

[Elicitation](https://modelcontextprotocol.io/specification/2025-06-18/client/elicitation)
allows servers to request user inputs. It is implemented in the SDK as follows:

**Client-side**: To add the `elicitation` capability to a client, set
[`ClientOptions.ElicitationHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ElicitationHandler).
The elicitation handler must return a result that matches the requested schema;
otherwise, elicitation returns an error.

**Server-side**: To use elicitation from the server, call
[`ServerSession.Elicit`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.Elicit).

```go
func Example_elicitation() {
ctx := context.Background()
ct, st := mcp.NewInMemoryTransports()

s := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, nil)
ss, err := s.Connect(ctx, st, nil)
if err != nil {
log.Fatal(err)
}
defer ss.Close()

c := mcp.NewClient(testImpl, &mcp.ClientOptions{
ElicitationHandler: func(context.Context, *mcp.ElicitRequest) (*mcp.ElicitResult, error) {
return &mcp.ElicitResult{Action: "accept", Content: map[string]any{"test": "value"}}, nil
},
})
if _, err := c.Connect(ctx, ct, nil); err != nil {
log.Fatal(err)
}
res, err := ss.Elicit(ctx, &mcp.ElicitParams{
Message: "This should fail",
RequestedSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"test": {Type: "string"},
},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Content["test"])
// Output: value
}
```
Loading
Loading