mcp: add documentation for new protocol version (#1019)
Add documentation for new protocol version in the `internal/docs`
This commit is contained in:
parent
01847b35b3
commit
b9e6fc5a66
9 changed files with 719 additions and 36 deletions
11
README.md
11
README.md
|
|
@ -34,11 +34,12 @@ contains feature documentation, mapping the MCP spec to the packages above.
|
|||
|
||||
The following table shows which versions of the Go SDK support which versions of the MCP specification:
|
||||
|
||||
| SDK Version | Latest MCP Spec | All Supported MCP Specs |
|
||||
|-----------------|-------------------|----------------------------------------------------|
|
||||
| v1.4.0+ | 2025-11-25\* | 2025-11-25\*, 2025-06-18, 2025-03-26, 2024-11-05 |
|
||||
| v1.2.0 - v1.3.1 | 2025-11-25\*\* | 2025-11-25\*\*, 2025-06-18, 2025-03-26, 2024-11-05 |
|
||||
| v1.0.0 - v1.1.0 | 2025-06-18 | 2025-06-18, 2025-03-26, 2024-11-05 |
|
||||
| SDK Version | Latest MCP Spec | All Supported MCP Specs |
|
||||
|-----------------|-------------------|------------------------------------------------------------------|
|
||||
| v1.7.0+ | 2026-07-28 | 2026-07-28, 2025-11-25\*, 2025-06-18, 2025-03-26, 2024-11-05 |
|
||||
| v1.4.0 - v1.6.1 | 2025-11-25\* | 2025-11-25\*, 2025-06-18, 2025-03-26, 2024-11-05 |
|
||||
| v1.2.0 - v1.3.1 | 2025-11-25\*\* | 2025-11-25\*\*, 2025-06-18, 2025-03-26, 2024-11-05 |
|
||||
| v1.0.0 - v1.1.0 | 2025-06-18 | 2025-06-18, 2025-03-26, 2024-11-05 |
|
||||
|
||||
\* Client side OAuth has experimental support.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@
|
|||
1. [Roots](#roots)
|
||||
1. [Sampling](#sampling)
|
||||
1. [Elicitation](#elicitation)
|
||||
1. [Multi Round-Trip Requests (MRTR)](#multi-round-trip-requests-(mrtr))
|
||||
1. [Capabilities](#capabilities)
|
||||
1. [Capability inference](#capability-inference)
|
||||
1. [Explicit capabilities](#explicit-capabilities)
|
||||
1. [Extensions](#extensions)
|
||||
|
||||
## Roots
|
||||
|
||||
|
|
@ -34,6 +36,10 @@ client, a call to `AddRoot` or `RemoveRoots` will result in a
|
|||
[`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).
|
||||
For protocol versions `2026-07-28` and later, `ListRoots` requests are
|
||||
delivered via the
|
||||
[Multi Round-Trip Requests](protocol.md#multi-round-trip-requests-mrtr)
|
||||
pattern.
|
||||
|
||||
```go
|
||||
func Example_roots() {
|
||||
|
|
@ -102,6 +108,11 @@ 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).
|
||||
|
||||
For protocol versions `2026-07-28` and later, sampling requests are
|
||||
delivered via the
|
||||
[Multi Round-Trip Requests](protocol.md#multi-round-trip-requests-mrtr)
|
||||
pattern.
|
||||
|
||||
```go
|
||||
func Example_sampling() {
|
||||
ctx := context.Background()
|
||||
|
|
@ -154,6 +165,11 @@ you must declare that capability explicitly (see [Capabilities](#capabilities))
|
|||
**Server-side**: To use elicitation from the server, call
|
||||
[`ServerSession.Elicit`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.Elicit).
|
||||
|
||||
For protocol versions `2026-07-28` and later, elicitation requests are
|
||||
delivered via the
|
||||
[Multi Round-Trip Requests](protocol.md#multi-round-trip-requests-mrtr)
|
||||
pattern.
|
||||
|
||||
```go
|
||||
func Example_elicitation() {
|
||||
ctx := context.Background()
|
||||
|
|
@ -191,6 +207,37 @@ func Example_elicitation() {
|
|||
}
|
||||
```
|
||||
|
||||
## Multi Round-Trip Requests (MRTR)
|
||||
|
||||
[SEP-2322](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2322)
|
||||
introduces the MRTR pattern: server-to-client requests for sampling,
|
||||
elicitation, and roots are no longer issued as fresh JSON-RPC requests but
|
||||
are carried inside the in-flight reply of a `tools/call`, `prompts/get`, or
|
||||
`resources/read`. The client must respond by retrying the original request
|
||||
with the produced responses.
|
||||
|
||||
The SDK installs `clientMultiRoundTripMiddleware` for every client by
|
||||
default. The middleware:
|
||||
|
||||
1. Inspects each `tools/call`/`prompts/get`/`resources/read` reply.
|
||||
2. If the result's `NeedsInput()` is true, fans out the `InputRequests` map
|
||||
concurrently, calling the configured handler for each (`elicit`,
|
||||
`createMessage`/`createMessageWithTools`, or `listRoots`).
|
||||
3. Threads the server-supplied opaque `RequestState` back unchanged.
|
||||
4. Retries the original request with the responses set, repeating until the
|
||||
result no longer needs input.
|
||||
|
||||
The middleware is enabled by default. To opt out, set
|
||||
[`ClientOptions.MultiRoundTrip.Disabled = true`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#MultiRoundTripOptions);
|
||||
the client will then surface `InputRequiredResult` values directly to the
|
||||
caller and your code must fulfil the requests manually.
|
||||
|
||||
For legacy (`<= 2025-11-25`) servers, the SDK transparently sends server
|
||||
requests on the legacy server-initiated channel; the MRTR machinery is a
|
||||
no-op in that direction. For legacy clients talking to MRTR-style servers,
|
||||
the server SDK applies the inverse compatibility shim — see the
|
||||
[server-side documentation](server.md#multi-round-trip-requests-mrtr).
|
||||
|
||||
## Capabilities
|
||||
|
||||
Client capabilities are advertised to servers during the initialization
|
||||
|
|
@ -247,3 +294,11 @@ client := mcp.NewClient(impl, &mcp.ClientOptions{
|
|||
})
|
||||
```
|
||||
|
||||
### Extensions
|
||||
|
||||
[SEP-2133](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133)
|
||||
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.
|
||||
|
||||
|
|
|
|||
145
docs/protocol.md
145
docs/protocol.md
|
|
@ -2,6 +2,8 @@
|
|||
# Support for the MCP base protocol
|
||||
|
||||
1. [Lifecycle](#lifecycle)
|
||||
1. [Discovery (`server/discover`)](#discovery-(server/discover))
|
||||
1. [Per-request `_meta` keys](#per-request-meta-keys)
|
||||
1. [Transports](#transports)
|
||||
1. [Stdio Transport](#stdio-transport)
|
||||
1. [Streamable Transport](#streamable-transport)
|
||||
|
|
@ -20,20 +22,27 @@
|
|||
1. [Cancellation](#cancellation)
|
||||
1. [Ping](#ping)
|
||||
1. [Progress](#progress)
|
||||
1. [Error codes](#error-codes)
|
||||
|
||||
## Lifecycle
|
||||
|
||||
The SDK provides an API for defining both MCP clients and servers, and
|
||||
connecting them over various transports. When a client and server are
|
||||
connected, it creates a logical session, which follows the MCP spec's
|
||||
[lifecycle](https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle).
|
||||
connected, it creates a logical session.
|
||||
|
||||
In this SDK, both a
|
||||
[`Client`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Client)
|
||||
and
|
||||
[`Server`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server)
|
||||
can handle multiple peers. Every time a new peer is connected, it creates a new
|
||||
session.
|
||||
The MCP specification defines two lifecycle models, and the SDK supports both
|
||||
transparently based on the negotiated protocol version:
|
||||
|
||||
- The **legacy `initialize` handshake**
|
||||
([lifecycle](https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle))
|
||||
used by protocol versions through `2025-11-25`.
|
||||
- A **stateless** model introduced in `2026-07-28` by
|
||||
[SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575),
|
||||
in which there is no `initialize`/`notifications/initialized` handshake, and
|
||||
each request carries its protocol version, client identity, and client
|
||||
capabilities in `_meta`.
|
||||
|
||||
In both models, the SDK exposes the same API:
|
||||
|
||||
- A `Client` is a logical MCP client, configured with various
|
||||
[`ClientOptions`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions).
|
||||
|
|
@ -49,11 +58,16 @@ session.
|
|||
[`Server.Connect`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.Connect),
|
||||
it creates a
|
||||
[`ServerSession`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession).
|
||||
This session is not initialized until the client sends the
|
||||
`notifications/initialized` message. Use `ServerOptions.InitializedHandler`
|
||||
to listen for this event, or just use the session through various feature
|
||||
handlers (such as a `ToolHandler`). Requests to the server are rejected until
|
||||
the client has initialized the session.
|
||||
|
||||
In the legacy model, the server session is not considered initialized until
|
||||
the client sends the `notifications/initialized` message. Use
|
||||
`ServerOptions.InitializedHandler` to listen for this event, or just use the
|
||||
session through various feature handlers (such as a `ToolHandler`). Requests
|
||||
to the server are rejected until the client has initialized the session.
|
||||
|
||||
In the stateless model (`2026-07-28`+), there is no handshake. The server
|
||||
processes the first request the moment it arrives, validates the per-request
|
||||
`_meta` fields.
|
||||
|
||||
Both `ClientSession` and `ServerSession` have a `Close` method to terminate the
|
||||
session, and a `Wait` method to await session termination by the peer. Typically,
|
||||
|
|
@ -98,6 +112,41 @@ func Example_lifecycle() {
|
|||
}
|
||||
```
|
||||
|
||||
### Discovery (`server/discover`)
|
||||
|
||||
Introduced in `2026-07-28` by
|
||||
[SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575),
|
||||
the `server/discover` RPC lets a client discover a server's supported
|
||||
protocol versions, capabilities, and identity before issuing any other
|
||||
request. Servers implementing `2026-07-28` MUST implement it.
|
||||
|
||||
- **Server**: `Server.Connect` registers the
|
||||
`server/discover` handler automatically; the response is computed from the
|
||||
server's static configuration (capabilities, server info, instructions) and
|
||||
the transport-filtered list of supported protocol versions.
|
||||
- **Client**: `Client.Connect` calls `server/discover` first and
|
||||
uses the result to negotiate a mutually supported version. If discovery
|
||||
fails or the server does not support the latest version, the client falls back to the
|
||||
legacy `initialize` handshake.
|
||||
|
||||
### Per-request `_meta` keys
|
||||
|
||||
When the negotiated protocol version is `2026-07-28` or later, every request
|
||||
carries these keys inside its `_meta` map (constants live in
|
||||
[`mcp/protocol.go`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#pkg-constants)):
|
||||
|
||||
| Constant | Wire key | Type |
|
||||
|---|---|---|
|
||||
| `MetaKeyProtocolVersion` | `io.modelcontextprotocol/protocolVersion` | `string` |
|
||||
| `MetaKeyClientInfo` | `io.modelcontextprotocol/clientInfo` | `*Implementation` |
|
||||
| `MetaKeyClientCapabilities` | `io.modelcontextprotocol/clientCapabilities` | `*ClientCapabilities` |
|
||||
| `MetaKeyLogLevel` | `io.modelcontextprotocol/logLevel` | `LoggingLevel` (deprecated by SEP-2577) |
|
||||
|
||||
The client populates these keys automatically on every outgoing request.
|
||||
Server-side handlers can read them
|
||||
through `ServerRequest[P].ProtocolVersion()`, `ServerRequest[P].ClientInfo()`,
|
||||
and `ServerRequest[P].ClientCapabilities()`.
|
||||
|
||||
## Transports
|
||||
|
||||
A
|
||||
|
|
@ -188,6 +237,39 @@ client, err := mcp.Connect(ctx, transport, &mcp.ClientOptions{...})
|
|||
The `StreamableClientTransport` handles the HTTP requests and communicates with
|
||||
the server using the streamable transport protocol.
|
||||
|
||||
#### HTTP Headers
|
||||
|
||||
[SEP-2243](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2243)
|
||||
standardises a small set of MCP-specific HTTP headers, sent and validated by
|
||||
the SDK only for sessions negotiated on protocol version `2026-07-28` or
|
||||
later.
|
||||
|
||||
| Header | Direction | Purpose |
|
||||
|---|---|---|
|
||||
| `Mcp-Protocol-Version` | request | Mirrors `_meta.io.modelcontextprotocol/protocolVersion` from the body |
|
||||
| `Mcp-Session-Id` | request/response | Logical session identifier (removed in stateless mode) |
|
||||
| `Mcp-Method` | request | Mirrors the JSON-RPC `method` field; mismatch ⇒ `-32020` |
|
||||
| `Mcp-Name` | request | Mirrors the request's principal name (`tools/call.params.name`, `prompts/get.params.name`, `resources/read.params.uri`); mismatch ⇒ `-32020` |
|
||||
| `Mcp-Param-{Header}` | request | Per-tool parameter passthrough; see below |
|
||||
|
||||
The header-name set is exhaustive: for protocol versions earlier than
|
||||
`2026-07-28`, only `Mcp-Protocol-Version` and `Mcp-Session-Id` are recognised.
|
||||
|
||||
**Body↔header mirroring.** When `Mcp-Method` or `Mcp-Name` is present but
|
||||
does not match the JSON-RPC body, the server returns
|
||||
[`CodeHeaderMismatch`](#error-codes) (`-32020`). The same applies to a
|
||||
mismatch between `Mcp-Protocol-Version` and the body's
|
||||
`_meta.io.modelcontextprotocol/protocolVersion`.
|
||||
|
||||
**`Mcp-Param-*` passthrough.** A tool may annotate properties of its
|
||||
`InputSchema` with `x-mcp-header: "<HeaderName>"`. When a client invokes that
|
||||
tool over HTTP, the SDK serializes the matching argument(s) as
|
||||
`Mcp-Param-<HeaderName>` request headers (using `=?base64?...?=` encoding for
|
||||
non-ASCII values). On the server, the headers are validated against the body
|
||||
and either accepted or rejected with `-32020`. Properties so annotated must
|
||||
be typed `string`, `integer`, or `boolean`; tools that fail validation at
|
||||
registration time are silently dropped from `tools/list`.
|
||||
|
||||
#### Resumability and Redelivery
|
||||
|
||||
By default, the streamable server does not support [resumability or
|
||||
|
|
@ -201,6 +283,13 @@ value. The SDK provides a `MemoryEventStore` for testing or simple use cases;
|
|||
for production use it is generally advisable to use a more sophisticated
|
||||
implementation.
|
||||
|
||||
> **Note**: [SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575)
|
||||
> removes SSE stream resumability (`Last-Event-ID`, SSE event IDs) for protocol
|
||||
> version `2026-07-28`. The SDK preserves the `EventStore` and `Last-Event-ID`
|
||||
> code paths for backward compatibility with `2025-11-25` and earlier; on
|
||||
> `2026-07-28` sessions, a broken response stream loses the in-flight request
|
||||
> and the client must re-issue it as a new request with a new ID.
|
||||
|
||||
#### Stateless Mode
|
||||
|
||||
The streamable server supports a _stateless mode_ by setting
|
||||
|
|
@ -219,6 +308,10 @@ to see the logical session
|
|||
> modelcontextprotocol/modelcontextprotocol#1372, or
|
||||
> modelcontextprotocol/modelcontextprotocol#1442 for potential refinements.
|
||||
|
||||
> **Required for `2026-07-28`**: the streamable HTTP transport accepts
|
||||
> requests at protocol version `2026-07-28` **only** when `Stateless = true`.
|
||||
> Requests at that version against a non-stateless handler are rejected.
|
||||
|
||||
_See [examples/server/distributed](https://github.com/modelcontextprotocol/go-sdk/blob/main/examples/server/distributed/main.go) for
|
||||
an example using stateless mode to implement a server distributed across
|
||||
multiple processes._
|
||||
|
|
@ -597,6 +690,13 @@ session if the ping fails, set
|
|||
or
|
||||
[`ServerOptions.KeepAlive`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.KeepAlive).
|
||||
|
||||
> **Note**: `ping` is removed from the protocol as of `2026-07-28` by
|
||||
> [SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575).
|
||||
> The SDK preserves the API for backward compatibility with legacy clients;
|
||||
> when both peers negotiate `2026-07-28`, the server returns
|
||||
> `MethodNotFound` (`-32601`) for `ping` and `KeepAlive` should not be
|
||||
> enabled.
|
||||
|
||||
### Progress
|
||||
|
||||
[Progress](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/progress)
|
||||
|
|
@ -658,3 +758,22 @@ func Example_progress() {
|
|||
}
|
||||
```
|
||||
|
||||
### Error codes
|
||||
|
||||
The SDK uses the standard JSON-RPC base codes (parse error `-32700`,
|
||||
invalid request `-32600`, method not found `-32601`, invalid params
|
||||
`-32602`, internal error `-32603`) plus the following MCP-specific codes:
|
||||
|
||||
| Constant | Code | Meaning |
|
||||
|---|---|---|
|
||||
| [`CodeHeaderMismatch`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CodeHeaderMismatch) | `-32020` | An MCP HTTP header (`Mcp-Method`, `Mcp-Name`, `Mcp-Protocol-Version`, or `Mcp-Param-*`) does not match the JSON-RPC body |
|
||||
| [`CodeMissingRequiredClientCapabilities`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CodeMissingRequiredClientCapabilities) | `-32021` | Server requires client capabilities the client did not declare |
|
||||
| [`CodeUnsupportedProtocolVersion`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CodeUnsupportedProtocolVersion) | `-32022` | Requested protocol version not supported. Data: `UnsupportedProtocolVersionData{Supported []string, Requested string}` |
|
||||
| [`CodeResourceNotFound`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CodeResourceNotFound) | `-32602` | Resource URI not found (variable; was `-32002` before SEP-2164) |
|
||||
|
||||
The error code allocation policy defined in `2026-07-28`
|
||||
([SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575))
|
||||
partitions the JSON-RPC server-error range:
|
||||
|
||||
- `-32000` to `-32019`: implementation-defined; existing SDK usage is grandfathered.
|
||||
- `-32020` to `-32099`: reserved for the MCP specification.
|
||||
|
|
|
|||
169
docs/server.md
169
docs/server.md
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ client, a call to `AddRoot` or `RemoveRoots` will result in a
|
|||
[`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).
|
||||
For protocol versions `2026-07-28` and later, `ListRoots` requests are
|
||||
delivered via the
|
||||
[Multi Round-Trip Requests](protocol.md#multi-round-trip-requests-mrtr)
|
||||
pattern.
|
||||
|
||||
%include ../../mcp/client_example_test.go roots -
|
||||
|
||||
|
|
@ -51,6 +55,11 @@ 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).
|
||||
|
||||
For protocol versions `2026-07-28` and later, sampling requests are
|
||||
delivered via the
|
||||
[Multi Round-Trip Requests](protocol.md#multi-round-trip-requests-mrtr)
|
||||
pattern.
|
||||
|
||||
%include ../../mcp/client_example_test.go sampling -
|
||||
|
||||
## Elicitation
|
||||
|
|
@ -68,8 +77,44 @@ you must declare that capability explicitly (see [Capabilities](#capabilities))
|
|||
**Server-side**: To use elicitation from the server, call
|
||||
[`ServerSession.Elicit`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.Elicit).
|
||||
|
||||
For protocol versions `2026-07-28` and later, elicitation requests are
|
||||
delivered via the
|
||||
[Multi Round-Trip Requests](protocol.md#multi-round-trip-requests-mrtr)
|
||||
pattern.
|
||||
|
||||
%include ../../mcp/client_example_test.go elicitation -
|
||||
|
||||
## Multi Round-Trip Requests (MRTR)
|
||||
|
||||
[SEP-2322](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2322)
|
||||
introduces the MRTR pattern: server-to-client requests for sampling,
|
||||
elicitation, and roots are no longer issued as fresh JSON-RPC requests but
|
||||
are carried inside the in-flight reply of a `tools/call`, `prompts/get`, or
|
||||
`resources/read`. The client must respond by retrying the original request
|
||||
with the produced responses.
|
||||
|
||||
The SDK installs `clientMultiRoundTripMiddleware` for every client by
|
||||
default. The middleware:
|
||||
|
||||
1. Inspects each `tools/call`/`prompts/get`/`resources/read` reply.
|
||||
2. If the result's `NeedsInput()` is true, fans out the `InputRequests` map
|
||||
concurrently, calling the configured handler for each (`elicit`,
|
||||
`createMessage`/`createMessageWithTools`, or `listRoots`).
|
||||
3. Threads the server-supplied opaque `RequestState` back unchanged.
|
||||
4. Retries the original request with the responses set, repeating until the
|
||||
result no longer needs input.
|
||||
|
||||
The middleware is enabled by default. To opt out, set
|
||||
[`ClientOptions.MultiRoundTrip.Disabled = true`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#MultiRoundTripOptions);
|
||||
the client will then surface `InputRequiredResult` values directly to the
|
||||
caller and your code must fulfil the requests manually.
|
||||
|
||||
For legacy (`<= 2025-11-25`) servers, the SDK transparently sends server
|
||||
requests on the legacy server-initiated channel; the MRTR machinery is a
|
||||
no-op in that direction. For legacy clients talking to MRTR-style servers,
|
||||
the server SDK applies the inverse compatibility shim — see the
|
||||
[server-side documentation](server.md#multi-round-trip-requests-mrtr).
|
||||
|
||||
## Capabilities
|
||||
|
||||
Client capabilities are advertised to servers during the initialization
|
||||
|
|
@ -126,3 +171,11 @@ client := mcp.NewClient(impl, &mcp.ClientOptions{
|
|||
})
|
||||
```
|
||||
|
||||
### Extensions
|
||||
|
||||
[SEP-2133](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133)
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,15 +6,21 @@
|
|||
|
||||
The SDK provides an API for defining both MCP clients and servers, and
|
||||
connecting them over various transports. When a client and server are
|
||||
connected, it creates a logical session, which follows the MCP spec's
|
||||
[lifecycle](https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle).
|
||||
connected, it creates a logical session.
|
||||
|
||||
In this SDK, both a
|
||||
[`Client`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Client)
|
||||
and
|
||||
[`Server`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server)
|
||||
can handle multiple peers. Every time a new peer is connected, it creates a new
|
||||
session.
|
||||
The MCP specification defines two lifecycle models, and the SDK supports both
|
||||
transparently based on the negotiated protocol version:
|
||||
|
||||
- The **legacy `initialize` handshake**
|
||||
([lifecycle](https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle))
|
||||
used by protocol versions through `2025-11-25`.
|
||||
- A **stateless** model introduced in `2026-07-28` by
|
||||
[SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575),
|
||||
in which there is no `initialize`/`notifications/initialized` handshake, and
|
||||
each request carries its protocol version, client identity, and client
|
||||
capabilities in `_meta`.
|
||||
|
||||
In both models, the SDK exposes the same API:
|
||||
|
||||
- A `Client` is a logical MCP client, configured with various
|
||||
[`ClientOptions`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions).
|
||||
|
|
@ -30,11 +36,16 @@ session.
|
|||
[`Server.Connect`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.Connect),
|
||||
it creates a
|
||||
[`ServerSession`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession).
|
||||
This session is not initialized until the client sends the
|
||||
`notifications/initialized` message. Use `ServerOptions.InitializedHandler`
|
||||
to listen for this event, or just use the session through various feature
|
||||
handlers (such as a `ToolHandler`). Requests to the server are rejected until
|
||||
the client has initialized the session.
|
||||
|
||||
In the legacy model, the server session is not considered initialized until
|
||||
the client sends the `notifications/initialized` message. Use
|
||||
`ServerOptions.InitializedHandler` to listen for this event, or just use the
|
||||
session through various feature handlers (such as a `ToolHandler`). Requests
|
||||
to the server are rejected until the client has initialized the session.
|
||||
|
||||
In the stateless model (`2026-07-28`+), there is no handshake. The server
|
||||
processes the first request the moment it arrives, validates the per-request
|
||||
`_meta` fields.
|
||||
|
||||
Both `ClientSession` and `ServerSession` have a `Close` method to terminate the
|
||||
session, and a `Wait` method to await session termination by the peer. Typically,
|
||||
|
|
@ -42,6 +53,41 @@ it is the client's responsibility to end the session.
|
|||
|
||||
%include ../../mcp/mcp_example_test.go lifecycle -
|
||||
|
||||
### Discovery (`server/discover`)
|
||||
|
||||
Introduced in `2026-07-28` by
|
||||
[SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575),
|
||||
the `server/discover` RPC lets a client discover a server's supported
|
||||
protocol versions, capabilities, and identity before issuing any other
|
||||
request. Servers implementing `2026-07-28` MUST implement it.
|
||||
|
||||
- **Server**: `Server.Connect` registers the
|
||||
`server/discover` handler automatically; the response is computed from the
|
||||
server's static configuration (capabilities, server info, instructions) and
|
||||
the transport-filtered list of supported protocol versions.
|
||||
- **Client**: `Client.Connect` calls `server/discover` first and
|
||||
uses the result to negotiate a mutually supported version. If discovery
|
||||
fails or the server does not support the latest version, the client falls back to the
|
||||
legacy `initialize` handshake.
|
||||
|
||||
### Per-request `_meta` keys
|
||||
|
||||
When the negotiated protocol version is `2026-07-28` or later, every request
|
||||
carries these keys inside its `_meta` map (constants live in
|
||||
[`mcp/protocol.go`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#pkg-constants)):
|
||||
|
||||
| Constant | Wire key | Type |
|
||||
|---|---|---|
|
||||
| `MetaKeyProtocolVersion` | `io.modelcontextprotocol/protocolVersion` | `string` |
|
||||
| `MetaKeyClientInfo` | `io.modelcontextprotocol/clientInfo` | `*Implementation` |
|
||||
| `MetaKeyClientCapabilities` | `io.modelcontextprotocol/clientCapabilities` | `*ClientCapabilities` |
|
||||
| `MetaKeyLogLevel` | `io.modelcontextprotocol/logLevel` | `LoggingLevel` (deprecated by SEP-2577) |
|
||||
|
||||
The client populates these keys automatically on every outgoing request.
|
||||
Server-side handlers can read them
|
||||
through `ServerRequest[P].ProtocolVersion()`, `ServerRequest[P].ClientInfo()`,
|
||||
and `ServerRequest[P].ClientCapabilities()`.
|
||||
|
||||
## Transports
|
||||
|
||||
A
|
||||
|
|
@ -112,6 +158,39 @@ client, err := mcp.Connect(ctx, transport, &mcp.ClientOptions{...})
|
|||
The `StreamableClientTransport` handles the HTTP requests and communicates with
|
||||
the server using the streamable transport protocol.
|
||||
|
||||
#### HTTP Headers
|
||||
|
||||
[SEP-2243](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2243)
|
||||
standardises a small set of MCP-specific HTTP headers, sent and validated by
|
||||
the SDK only for sessions negotiated on protocol version `2026-07-28` or
|
||||
later.
|
||||
|
||||
| Header | Direction | Purpose |
|
||||
|---|---|---|
|
||||
| `Mcp-Protocol-Version` | request | Mirrors `_meta.io.modelcontextprotocol/protocolVersion` from the body |
|
||||
| `Mcp-Session-Id` | request/response | Logical session identifier (removed in stateless mode) |
|
||||
| `Mcp-Method` | request | Mirrors the JSON-RPC `method` field; mismatch ⇒ `-32020` |
|
||||
| `Mcp-Name` | request | Mirrors the request's principal name (`tools/call.params.name`, `prompts/get.params.name`, `resources/read.params.uri`); mismatch ⇒ `-32020` |
|
||||
| `Mcp-Param-{Header}` | request | Per-tool parameter passthrough; see below |
|
||||
|
||||
The header-name set is exhaustive: for protocol versions earlier than
|
||||
`2026-07-28`, only `Mcp-Protocol-Version` and `Mcp-Session-Id` are recognised.
|
||||
|
||||
**Body↔header mirroring.** When `Mcp-Method` or `Mcp-Name` is present but
|
||||
does not match the JSON-RPC body, the server returns
|
||||
[`CodeHeaderMismatch`](#error-codes) (`-32020`). The same applies to a
|
||||
mismatch between `Mcp-Protocol-Version` and the body's
|
||||
`_meta.io.modelcontextprotocol/protocolVersion`.
|
||||
|
||||
**`Mcp-Param-*` passthrough.** A tool may annotate properties of its
|
||||
`InputSchema` with `x-mcp-header: "<HeaderName>"`. When a client invokes that
|
||||
tool over HTTP, the SDK serializes the matching argument(s) as
|
||||
`Mcp-Param-<HeaderName>` request headers (using `=?base64?...?=` encoding for
|
||||
non-ASCII values). On the server, the headers are validated against the body
|
||||
and either accepted or rejected with `-32020`. Properties so annotated must
|
||||
be typed `string`, `integer`, or `boolean`; tools that fail validation at
|
||||
registration time are silently dropped from `tools/list`.
|
||||
|
||||
#### Resumability and Redelivery
|
||||
|
||||
By default, the streamable server does not support [resumability or
|
||||
|
|
@ -125,6 +204,13 @@ value. The SDK provides a `MemoryEventStore` for testing or simple use cases;
|
|||
for production use it is generally advisable to use a more sophisticated
|
||||
implementation.
|
||||
|
||||
> **Note**: [SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575)
|
||||
> removes SSE stream resumability (`Last-Event-ID`, SSE event IDs) for protocol
|
||||
> version `2026-07-28`. The SDK preserves the `EventStore` and `Last-Event-ID`
|
||||
> code paths for backward compatibility with `2025-11-25` and earlier; on
|
||||
> `2026-07-28` sessions, a broken response stream loses the in-flight request
|
||||
> and the client must re-issue it as a new request with a new ID.
|
||||
|
||||
#### Stateless Mode
|
||||
|
||||
The streamable server supports a _stateless mode_ by setting
|
||||
|
|
@ -143,6 +229,10 @@ to see the logical session
|
|||
> modelcontextprotocol/modelcontextprotocol#1372, or
|
||||
> modelcontextprotocol/modelcontextprotocol#1442 for potential refinements.
|
||||
|
||||
> **Required for `2026-07-28`**: the streamable HTTP transport accepts
|
||||
> requests at protocol version `2026-07-28` **only** when `Stateless = true`.
|
||||
> Requests at that version against a non-stateless handler are rejected.
|
||||
|
||||
_See [examples/server/distributed](https://github.com/modelcontextprotocol/go-sdk/blob/main/examples/server/distributed/main.go) for
|
||||
an example using stateless mode to implement a server distributed across
|
||||
multiple processes._
|
||||
|
|
@ -463,6 +553,13 @@ session if the ping fails, set
|
|||
or
|
||||
[`ServerOptions.KeepAlive`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.KeepAlive).
|
||||
|
||||
> **Note**: `ping` is removed from the protocol as of `2026-07-28` by
|
||||
> [SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575).
|
||||
> The SDK preserves the API for backward compatibility with legacy clients;
|
||||
> when both peers negotiate `2026-07-28`, the server returns
|
||||
> `MethodNotFound` (`-32601`) for `ping` and `KeepAlive` should not be
|
||||
> enabled.
|
||||
|
||||
### Progress
|
||||
|
||||
[Progress](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/progress)
|
||||
|
|
@ -480,3 +577,22 @@ Issue #460 discusses some potential ergonomic improvements to this API.
|
|||
|
||||
%include ../../mcp/mcp_example_test.go progress -
|
||||
|
||||
### Error codes
|
||||
|
||||
The SDK uses the standard JSON-RPC base codes (parse error `-32700`,
|
||||
invalid request `-32600`, method not found `-32601`, invalid params
|
||||
`-32602`, internal error `-32603`) plus the following MCP-specific codes:
|
||||
|
||||
| Constant | Code | Meaning |
|
||||
|---|---|---|
|
||||
| [`CodeHeaderMismatch`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CodeHeaderMismatch) | `-32020` | An MCP HTTP header (`Mcp-Method`, `Mcp-Name`, `Mcp-Protocol-Version`, or `Mcp-Param-*`) does not match the JSON-RPC body |
|
||||
| [`CodeMissingRequiredClientCapabilities`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CodeMissingRequiredClientCapabilities) | `-32021` | Server requires client capabilities the client did not declare |
|
||||
| [`CodeUnsupportedProtocolVersion`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CodeUnsupportedProtocolVersion) | `-32022` | Requested protocol version not supported. Data: `UnsupportedProtocolVersionData{Supported []string, Requested string}` |
|
||||
| [`CodeResourceNotFound`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CodeResourceNotFound) | `-32602` | Resource URI not found (variable; was `-32002` before SEP-2164) |
|
||||
|
||||
The error code allocation policy defined in `2026-07-28`
|
||||
([SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575))
|
||||
partitions the JSON-RPC server-error range:
|
||||
|
||||
- `-32000` to `-32019`: implementation-defined; existing SDK usage is grandfathered.
|
||||
- `-32020` to `-32099`: reserved for the MCP specification.
|
||||
|
|
|
|||
|
|
@ -214,6 +214,80 @@ 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).
|
||||
|
||||
%include ../../mcp/server_example_test.go mrtr -
|
||||
|
||||
## 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
|
||||
|
|
@ -318,6 +392,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
|
||||
|
|
|
|||
|
|
@ -33,11 +33,12 @@ contains feature documentation, mapping the MCP spec to the packages above.
|
|||
|
||||
The following table shows which versions of the Go SDK support which versions of the MCP specification:
|
||||
|
||||
| SDK Version | Latest MCP Spec | All Supported MCP Specs |
|
||||
|-----------------|-------------------|----------------------------------------------------|
|
||||
| v1.4.0+ | 2025-11-25\* | 2025-11-25\*, 2025-06-18, 2025-03-26, 2024-11-05 |
|
||||
| v1.2.0 - v1.3.1 | 2025-11-25\*\* | 2025-11-25\*\*, 2025-06-18, 2025-03-26, 2024-11-05 |
|
||||
| v1.0.0 - v1.1.0 | 2025-06-18 | 2025-06-18, 2025-03-26, 2024-11-05 |
|
||||
| SDK Version | Latest MCP Spec | All Supported MCP Specs |
|
||||
|-----------------|-------------------|------------------------------------------------------------------|
|
||||
| v1.7.0+ | 2026-07-28 | 2026-07-28, 2025-11-25\*, 2025-06-18, 2025-03-26, 2024-11-05 |
|
||||
| v1.4.0 - v1.6.1 | 2025-11-25\* | 2025-11-25\*, 2025-06-18, 2025-03-26, 2024-11-05 |
|
||||
| v1.2.0 - v1.3.1 | 2025-11-25\*\* | 2025-11-25\*\*, 2025-06-18, 2025-03-26, 2024-11-05 |
|
||||
| v1.0.0 - v1.1.0 | 2025-06-18 | 2025-06-18, 2025-03-26, 2024-11-05 |
|
||||
|
||||
\* Client side OAuth has experimental support.
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
"log/slog"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
|
|
@ -220,3 +221,89 @@ func Example_resources() {
|
|||
}
|
||||
|
||||
// !-resources
|
||||
|
||||
// !+mrtr
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// !-mrtr
|
||||
|
|
|
|||
Loading…
Reference in a new issue