mcp: add documentation for new protocol version (#1019)

Add documentation for new protocol version in the `internal/docs`
This commit is contained in:
Guglielmo Colombo 2026-06-23 17:07:38 +02:00 • committed by GitHub
commit b9e6fc5a66
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -4,12 +4,17 @@
1. [Prompts](#prompts)
1. [Resources](#resources)
1. [Tools](#tools)
1. [Multi Round-Trip Requests (MRTR)](#multi-round-trip-requests-(mrtr))
1. [Talking to legacy clients](#talking-to-legacy-clients)
1. [Example](#example)
1. [Cacheable list results](#cacheable-list-results)
1. [Utilities](#utilities)
1. [Completion](#completion)
1. [Logging](#logging)
1. [Capabilities](#capabilities)
1. [Capability inference](#capability-inference)
1. [Explicit capabilities](#explicit-capabilities)
1. [Extensions](#extensions)
1. [Pagination](#pagination)
## Prompts
@ -424,6 +429,162 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
}
```
## Multi Round-Trip Requests (MRTR)
[SEP-2322](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2322)
defines a new pattern for server-to-client requests (sampling, elicitation,
roots). Instead of issuing a fresh JSON-RPC request mid-flight, the server
returns an `InputRequiredResult` from its in-flight handler — the
`InputRequests` field of `CallToolResult`, `GetPromptResult`, or
`ReadResourceResult` carries the requests for additional information. The
client responds by retrying the original call with `InputResponses` set.
The SDK supports this pattern from both sides without requiring callers to
choose:
- **Returning input requests from a handler.** Set `InputRequests` on the
result you return.
Leave `Content` / `StructuredContent` empty — returning
both at once is a server bug and the SDK returns `-32603 InternalError`.
- **Calling the legacy APIs.** `ServerSession.Elicit`,
`ServerSession.CreateMessage(WithTools)`, and `ServerSession.ListRoots`
remain available and work for both new and legacy clients.
### Talking to legacy clients
The server installs `serverMultiRoundTripMiddleware` automatically. For
clients on a protocol version earlier than `2026-07-28`, the middleware
intercepts any `InputRequiredResult` your handler returns, fulfils each
input request itself by calling the legacy server-initiated APIs
(`Elicit`, `CreateMessage`, `ListRoots`), and re-invokes your handler
exactly once with the responses already populated. This means a handler
written in the MRTR style works against both old and new clients without
code changes.
### Example
The following example shows a "greet" tool whose handler asks the user
for their name via elicitation before producing the final greeting. The
handler runs twice — once to issue the elicitation, once to consume the
response — but the client call site sees a single `CallTool` returning the
final result, because the SDK's MRTR middleware handles the round trip on
either side of the wire (depending on the negotiated protocol version).
```go
// Example_mrtr demonstrates the [Multi Round-Trip Requests] pattern
// (SEP-2322). A tool handler signals "I need more information from the user"
// by returning a [mcp.CallToolResult] whose `InputRequests` field carries the
// requests for the additional information. Each request can be an
// [mcp.ElicitParams] (ask the user a question),
// [mcp.CreateMessageParams] (sample from the client's LLM), or
// [mcp.ListRootsParams] (list the client's roots).
//
// On protocol version `2026-07-28` and later, the client's
// `clientMultiRoundTripMiddleware` fulfils each input request from the
// configured handler and retries the original call transparently. On earlier
// protocol versions, the server's `serverMultiRoundTripMiddleware` performs
// the equivalent dance from the server side by calling the legacy
// server-to-client API (`ServerSession.Elicit`, `CreateMessage`, or
// `ListRoots`) and re-invoking the handler with the response. Either way,
// the user-facing client call site sees only the final result.
//
// [Multi Round-Trip Requests]: https://modelcontextprotocol.io/specification/draft/basic/patterns#multi-round-trip-requests
func Example_mrtr() {
ctx := context.Background()
// Server: a "greet" tool that asks the user for their name before
// returning a greeting. The handler runs twice per logical call:
// once to issue the elicitation, once to consume the response.
s := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, nil)
mcp.AddTool(s, &mcp.Tool{Name: "greet"}, func(_ context.Context, req *mcp.CallToolRequest, _ struct{}) (*mcp.CallToolResult, any, error) {
if len(req.Params.InputResponses) == 0 {
// First call: ask the user for their name. The map key
// ("user_name") is a label the handler chooses; the client must
// echo it back in InputResponses. The wire-level method name
// ("elicitation/create") is derived from the value's Go type.
return &mcp.CallToolResult{
InputRequests: mcp.InputRequestMap{
"user_name": &mcp.ElicitParams{
Message: "What's your name?",
RequestedSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"name": {Type: "string"},
},
},
},
},
// RequestState is an opaque token the client echoes back on
// the retry, letting the handler resume its work without
// per-session storage.
RequestState: "step=1",
}, nil, nil
}
// Retry: read the elicitation response and produce the final greeting.
name := req.Params.InputResponses["user_name"].(*mcp.ElicitResult).Content["name"].(string)
return &mcp.CallToolResult{
Content: []mcp.Content{&mcp.TextContent{Text: "Hello " + name}},
}, nil, nil
})
// Client: declares an elicitation handler. The SDK's MRTR middleware
// uses it to fulfil any input request the tool handler returns.
c := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, &mcp.ClientOptions{
ElicitationHandler: func(_ context.Context, _ *mcp.ElicitRequest) (*mcp.ElicitResult, error) {
return &mcp.ElicitResult{Action: "accept", Content: map[string]any{"name": "Ada"}}, nil
},
})
ct, st := mcp.NewInMemoryTransports()
if _, err := s.Connect(ctx, st, nil); err != nil {
log.Fatal(err)
}
cs, err := c.Connect(ctx, ct, nil)
if err != nil {
log.Fatal(err)
}
defer cs.Close()
// Single call site. The MRTR round trip is invisible to the caller.
res, err := cs.CallTool(ctx, &mcp.CallToolParams{Name: "greet"})
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Content[0].(*mcp.TextContent).Text)
}
```
## Cacheable list results
[SEP-2549](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2549)
adds `ttlMs` and `cacheScope` fields to the results of `tools/list`,
`prompts/list`, `resources/list`, `resources/templates/list`, and
`resources/read`. Both fields complement existing list
results: clients use `ttlMs` as a freshness hint to reduce polling,
and `cacheScope` (`"public"` or `"private"`) controls whether shared
intermediaries may cache the response.
**Server-side defaults.** After paginating, the SDK sets `CacheScope = "public"`
and leaves `TTLMs = 0` (which the client cache treats as "immediately
stale"). A handler that wants its responses cached must set `TTLMs`
explicitly on the returned result.
```go
mcp.AddTool(server, &mcp.Tool{Name: "expensive"}, func(...) (*mcp.CallToolResult, any, error) {
// ...
})
// Override the default for tools/list:
server.AddSendingMiddleware(func(next mcp.MethodHandler) mcp.MethodHandler {
return func(ctx context.Context, method string, req mcp.Request) (mcp.Result, error) {
res, err := next(ctx, method, req)
if lr, ok := res.(*mcp.ListToolsResult); ok {
lr.TTLMs = 30_000 // 30s cache freshness hint
}
return res, err
}
})
```
## Utilities
### Completion
@ -613,6 +774,14 @@ server := mcp.NewServer(impl, &mcp.ServerOptions{
**Deprecated**: The `HasPrompts`, `HasResources`, and `HasTools` fields on
`ServerOptions` are deprecated. Use `Capabilities` instead.
### Extensions
[SEP-2133](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133)
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.
### Pagination
Server-side feature lists may be