go-sdk/mcp
Repository files (latest commit first)
Filename Latest commit message Latest commit date
José M. Requena Plens a82c86a6cd
mcp: classify the per-request protocol version by membership (#1268)
`validateRequestMeta` decided whether a request follows the 2026-07-28
protocol by comparing `_meta.protocolVersion` against `2026-07-28` with
`<`. This replaces that comparison with a membership test against
`supportedProtocolVersions`, which is the rule `mcp/streamable.go:354`
already applies to the `Mcp-Protocol-Version` header.

Nothing changes for a version the SDK supports: one predating 2026-07-28
still selects the legacy handshake, and one at 2026-07-28 or later is
still validated as the full `_meta` triple. What changes is a version
the SDK does not support at all. It now reports `usesNewProtocol`, so it
reaches the `CodeUnsupportedProtocolVersion` check in
`(*ServerSession).handle` and is answered with the error the versioning
page requires.

Driving a stdio server with `_meta.protocolVersion` set to `1900-01-01`,
the worked example on that page, before:

```json
{"jsonrpc":"2.0","id":1,"error":{"code":0,"message":"method \"tools/list\" is invalid during session initialization"}}
```

and after:

```json
{"jsonrpc":"2.0","id":1,"error":{"code":-32022,"message":"unsupported protocol version","data":{"supported":["2026-07-28","2025-11-25","2025-06-18","2025-03-26","2024-11-05"],"requested":"1900-01-01"}}}
```

There are two judgement calls in the diff, and I would rather state them
than leave them to be found.

The first is that an unsupported version returns early with
`initializeParams` carrying only that version, and the rest of the
`_meta` triple is left unvalidated. The shape of `_meta` is defined by
the revision the request names, and that is a revision this SDK does not
know, so there is nothing to validate it against; answering "missing
`io.modelcontextprotocol/clientCapabilities`" would send the client off
to fix the wrong thing while its real problem is the version. The partly
populated value cannot escape: `handle` runs the unsupported check
before anything reads the capabilities, and `Server.protocolVersions` is
always a subset of `supportedProtocolVersions`, so a version that fails
the membership test in `validateRequestMeta` always fails it in `handle`
too.

The second is that an empty `protocolVersion` still means "no version
declared" rather than "unsupported version". `mcp/streamable.go` already
reads the field that way when it decides whether a request carries a
version at all (`metaVersion != ""`), and rejecting the empty string
here would put the two layers back into disagreement, in the other
direction. The `!ok` and `== ""` cases are therefore handled together.

**Behaviour an existing user would see change.** A request whose
`_meta.protocolVersion` is a string not in `SupportedProtocolVersions()`
is now refused with `-32022`, where before a value sorting below
`2026-07-28` was treated as a legacy request: served if the session was
already initialized, refused with the session-initialization error if it
was not. No published protocol revision is in that set, so a client
speaking any released version of MCP sees no difference. A deployment
that put a sentinel or placeholder version in `_meta` and relied on it
being ignored would see those requests refused, which I believe is the
right answer but is a visible change.

This is not limited to stdio, and I want to be precise about where the
header gate does and does not stand in front of it. The gate at
`mcp/streamable.go:354` refuses an unsupported `Mcp-Protocol-Version`
only when that version **also** sorts below `2026-07-28`, so an unknown
version sorting above it (`2027-01-01`, say) passes the gate and reaches
this classification; the deprecated HTTP+SSE transport in `mcp/sse.go`
has no header check at all; and stdio, the in-memory transport and any
custom transport have none either. The window the old comparison left
open is therefore reachable on every transport, and a value sorting
above `2026-07-28` with an incomplete `_meta` triple also changes
answer, from `-32602` to `-32022`.

**The test that pins it** is
`TestServerUnknownProtocolVersion_NewProtocol` in `mcp/server_test.go`.
It drives `(*ServerSession).handle` with a `tools/list` request whose
`_meta.protocolVersion` names a version the SDK does not know, and
asserts that the returned error is a `*jsonrpc.Error` with code
`CodeUnsupportedProtocolVersion`, `Data.Requested` equal to the version
asked for, and `Data.Supported` equal to `SupportedProtocolVersions()`.
Four cases: `1900-01-01`, `2025-11-24`, `1.0`, and `1900-01-01` again
with the rest of the triple omitted, which is the case that pins the
first judgement call above. Before the change all four fail with `handle
returned method "tools/list" is invalid during session initialization,
want a *jsonrpc.Error`. `TestValidateRequestMeta` in
`mcp/shared_test.go` gains four cases at the unit level: two that fail
before the change, and two that guard the classifications which must not
move, a supported pre-2026-07-28 version and an empty one.

`go test ./...` and `gofmt -l .` are both clean.


Fixes #1260.
2026-09-14 15:49:53 +00:00
..
testdata mcp: update return meta in DiscoverResult (SEP-2575) (#1097) 2026-07-16 16:10:12 +02:00
cache.go mcp: do not enforce TTL on cached tool (#1053) 2026-07-07 13:43:10 +02:00
capabilities_test.go mcp: add 2026-07-28 to the supported protocol versions (#1020) 2026-06-24 12:02:56 +02:00
client.go fix: only subscribe when server advertises capability (#1221) 2026-09-01 14:43:23 +02:00
client_example_test.go mcp: add docs for tier 1 assessment (#1195) 2026-08-24 12:00:59 +02:00
client_list_test.go mcp: allow any value for schemas (#517) 2025-09-26 11:55:54 -04:00
client_test.go mcp: export ClientSessionOptions.ProtocolVersion (#1127) 2026-08-03 15:29:52 +02:00
cmd.go mcp: speed up TestCommandTransportDuration 2025-09-12 12:00:29 -04:00
cmd_export_test.go mcp: speed up TestCommandTransportDuration 2025-09-12 12:00:29 -04:00
cmd_test.go all: modernize code (#868) 2026-03-31 10:58:02 +02:00
conformance_test.go mcp: add 2026-07-28 to the supported protocol versions (#1020) 2026-06-24 12:02:56 +02:00
content.go mcp: Deprecate roots, sampling and logging (SEP-2577) (#1017) 2026-06-23 11:11:08 +02:00
content_nil_test.go mcp: update return meta in DiscoverResult (SEP-2575) (#1097) 2026-07-16 16:10:12 +02:00
content_test.go mcp: switch icon theme to named string type (#733) 2025-12-19 16:44:10 -05:00
elicitation_test.go mcp: add ServerSession.NotifyElicitationComplete (#1194) 2026-08-24 10:14:28 +02:00
error_test.go mcp: add ServerSession.NotifyElicitationComplete (#1194) 2026-08-24 10:14:28 +02:00
event.go mcp: add an sse event size cap (#1205) 2026-09-02 11:35:23 +02:00
event_test.go mcp: add an sse event size cap (#1205) 2026-09-02 11:35:23 +02:00
features.go mcp: minor cleanup of some TODOs (#332) 2025-08-20 12:55:19 -04:00
features_test.go mcp: change tool handler design (#325) 2025-08-20 19:36:34 -04:00
logging.go mcp: keep request log levels scoped to each request (#1168) 2026-08-17 14:00:41 +02:00
mcp.go mcp: fix inaccurate package doc (#534) 2025-09-26 14:16:10 -04:00
mcp_example_test.go mcp: add 2026-07-28 to the supported protocol versions (#1020) 2026-06-24 12:02:56 +02:00
mcp_test.go mcp: carry a cancelled notification's reason as the request's cancel cause (#1255) 2026-09-14 11:33:30 +00:00
mrtr.go mcp: label resultType on results that bypass the dispatcher (#1226) 2026-09-14 09:52:10 +02:00
mrtr_test.go mcp: don't mutate caller params on multi-round-trip retries (#1145) 2026-08-31 13:53:34 +02:00
prompt.go mcp: align PromptHandler args with other handlers (#348) 2025-08-22 10:44:05 -04:00
protocol.go mcp: label resultType on results that bypass the dispatcher (#1226) 2026-09-14 09:52:10 +02:00
protocol_test.go mcp: include resultType on new-protocol responses (#1060) 2026-07-13 11:51:19 +00:00
requests.go mcp: Implement subscriptions/listen rpc (SEP-2575) (#1007) 2026-06-22 18:57:36 +02:00
resource.go mcp: align resource not found error code with sep-2164 (#931) 2026-05-05 16:20:02 +02:00
resource_test.go mcp/Resource: Refactor URI Template Matching with uritemplate Library (#100) 2025-07-08 15:52:54 -04:00
sampling_test.go mcp: export ClientSessionOptions.ProtocolVersion (#1127) 2026-08-03 15:29:52 +02:00
schema_cache.go perf: add schema caching to avoid repeated reflection (#685) 2026-01-20 20:16:46 -05:00
schema_cache_test.go all: modernize code (#868) 2026-03-31 10:58:02 +02:00
server.go mcp: label resultType on results that bypass the dispatcher (#1226) 2026-09-14 09:52:10 +02:00
server_example_test.go mcp: add docs for tier 1 assessment (#1195) 2026-08-24 12:00:59 +02:00
server_test.go mcp: classify the per-request protocol version by membership (#1268) 2026-09-14 15:49:53 +00:00
session.go mcp: allow restricting the protocol versions a server supports (#1199) 2026-08-28 16:06:10 +02:00
shared.go mcp: classify the per-request protocol version by membership (#1268) 2026-09-14 15:49:53 +00:00
shared_test.go mcp: classify the per-request protocol version by membership (#1268) 2026-09-14 15:49:53 +00:00
sse.go refactor: remove legacy MCPGODEBUG compatibility for new protocol release (#1237) 2026-09-04 10:08:52 +02:00
sse_example_test.go mcp: add docs for tier 1 assessment (#1195) 2026-08-24 12:00:59 +02:00
sse_test.go mcp: add a max request body size of the old transport (#1224) 2026-09-01 17:28:35 +02:00
streamable.go refactor: remove legacy MCPGODEBUG compatibility for new protocol release (#1237) 2026-09-04 10:08:52 +02:00
streamable_bench_test.go all: modernize code (#868) 2026-03-31 10:58:02 +02:00
streamable_client.go mcp: do not call DELETE for sessionless & doc adjustments (#960) 2026-05-15 21:47:34 +02:00
streamable_client_test.go mcp: tolerate 404 on subscriptions/listen in stateless mode (#1193) 2026-08-25 14:27:37 +02:00
streamable_example_test.go mcp: add 2026-07-28 to the supported protocol versions (#1020) 2026-06-24 12:02:56 +02:00
streamable_headers.go mcp: base64-encode a non-header-safe Mcp-Name (#1246) 2026-09-07 10:16:16 +02:00
streamable_headers_test.go mcp: base64-encode a non-header-safe Mcp-Name (#1246) 2026-09-07 10:16:16 +02:00
streamable_server.go mcp: simplify stream state management in streamable transport 2025-12-10 13:28:25 -05:00
streamable_test.go mcp: add an sse event size cap (#1205) 2026-09-02 11:35:23 +02:00
tool.go mcp: add docs for tier 1 assessment (#1195) 2026-08-24 12:00:59 +02:00
tool_example_test.go mcp: add docs for tier 1 assessment (#1195) 2026-08-24 12:00:59 +02:00
tool_test.go mcp: tool.InputSchema and tool.OutputSchema validation (SEP-2106) (#1009) 2026-06-16 13:55:47 +02:00
transport.go mcp: carry a cancelled notification's reason as the request's cancel cause (#1255) 2026-09-14 11:33:30 +00:00
transport_example_test.go mcp: update return meta in DiscoverResult (SEP-2575) (#1097) 2026-07-16 16:10:12 +02:00
transport_test.go mcp: add an sse event size cap (#1205) 2026-09-02 11:35:23 +02:00
util.go all: use case-sensitive JSON unmarshaling (#807) 2026-02-18 16:38:36 +01:00