Skip to content

Implement MCP server/discover and UnsupportedProtocolVersionError (-32022) - #1340

Open
psp65 wants to merge 4 commits into
smithy-lang:mainfrom
psp65:mcp-server-discover-32022
Open

Implement MCP server/discover and UnsupportedProtocolVersionError (-32022)#1340
psp65 wants to merge 4 commits into
smithy-lang:mainfrom
psp65:mcp-server-discover-32022

Conversation

@psp65

@psp65 psp65 commented Aug 29, 2026

Copy link
Copy Markdown

Note: stacked on #1338 and the initialize-version PR — the first two commits here are theirs; this PR's own change is the third commit. Kept as draft until its predecessors land.

What behavior changes?

Two additions from MCP protocol revision 2026-07-28, implemented so that a handshake-era server negotiates deterministically with modern clients:

  1. server/discover is implemented (spec: servers MUST implement it), answering a wire-complete DiscoverResult:
{"result":{"resultType":"complete",
  "supportedVersions":["2025-11-25","2025-06-18","2025-03-26","2024-11-05"],
  "capabilities":{...},"ttlMs":0,"cacheScope":"private",
  "_meta":{"io.modelcontextprotocol/serverInfo":{"name":"...","version":"..."}}}}
  1. Requests that name an unsupported protocol version in params._meta["io.modelcontextprotocol/protocolVersion"] get UnsupportedProtocolVersionError:
{"error":{"code":-32022,"message":"Unsupported protocol version",
  "data":{"supported":["2025-11-25",...],"requested":"2026-07-28"}}}

initialize and server/discover themselves are exempt (they are the negotiation bootstraps), requests without a _meta version are served exactly as before — zero change for existing handshake traffic — and unsupported-version notifications are dropped (JSON-RPC forbids responding to notifications).

Why is this change needed?

Step 3 of #1337. Advertising only handshake-era versions from server/discover is the documented "explicit legacy advertisement": the Python, TypeScript, and Go dual-era clients all respond to a no-modern-version supportedVersions list by falling back to the initialize handshake cleanly — so this is safe and useful before full 2026-07-28 result support lands (at which point the registry-derived list simply grows). And clients that pin a modern version now fail fast with one actionable error instead of cryptic model-validation failures on mis-shaped results.

How was this validated?

  • New McpServerTest cases: discover result fields (including _meta serverInfo and registry-derived newest-first version order), discover answered with and without the _meta envelope, -32022 with data.supported/data.requested for version-tagged requests (version rung before method rung, matching the reference SDK server's ladder), supported-version requests served normally, unsupported-version notifications dropped; ProtocolVersionTest.supportedIdentifiersAreNewestFirst. Module test/check green including the integration suite.
  • End-to-end with Python SDK 2.0.0 against our internal gateway: mode=auto on the wire shows server/discoverDiscoverResult → clean initialize fallback (the explicit-legacy branch, no error-based inference); pinned mode="2026-07-28" aborts with the actionable -32022; SDK 1.28.1 and mode=legacy byte-identical to before.

What should reviewers focus on?

  • New Smithy shapes DiscoverResult and UnsupportedProtocolVersionErrorData in mcp-schemas (the JSON-RPC error shape's existing data: Document member carries the payload; _meta uses @jsonName).
  • Two deliberate leniency divergences from the Python reference server, both flagged in MCP server: support protocol revision 2026-07-28 (stateless / modern era) #1337: discover is answered regardless of the requested _meta version (for a handshake-only server, version-gating the bootstrap would make it unimplementable — the reference server gates it, but it is modern), and a non-string _meta version value is ignored rather than rejected with -32602.

Additional Links


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Param Parikh added 4 commits August 27, 2026 23:06
Per JSON-RPC 2.0, a request carrying an id must receive a response; the
server previously returned null for unknown methods, which surfaced as
an HTTP 200 with an empty body through HTTP transports. Notifications
(requests without an id) are still silently dropped.

This also makes MCP 2026-07-28 clients' auto mode fall back to the
legacy handshake via a clean, deterministic -32601 on the
server/discover probe instead of relying on a synthesized parse error
from the empty body.

https://issues.amazon.com/issues/APPDEV-2256
… versions

Per the MCP spec, a server that does not support the requested protocol
version must respond with the latest version it supports. Previously
handleInitialize left protocolVersion unset for unknown versions, so
the Smithy model default (2024-11-05, the oldest version) leaked into
InitializeResult.

Add ProtocolVersion.latestVersion(), derived from the supported-version
registry so it cannot drift as versions are added. The registry moves
into an initialization-on-demand holder: computing the latest during
class init of the sealed base class NPEs when a subclass INSTANCE is
the first member of the hierarchy touched (base clinit reads a
still-null INSTANCE; caught by McpServerIntegrationTest).

Known versions are still echoed as-is; a missing protocolVersion param
keeps its existing behavior.

https://issues.amazon.com/issues/APPDEV-2257
…rsionError

MCP 2026-07-28 requires servers to implement server/discover, which
advertises supported protocol versions, capabilities, and identity, and
to reject requests naming an unsupported protocol version (via the
io.modelcontextprotocol/protocolVersion key of params._meta) with
UnsupportedProtocolVersionError (-32022) listing the versions the
server does support.

server/discover answers a wire-complete DiscoverResult advertising the
handshake-era versions from the ProtocolVersion registry, newest first.
Advertising no modern version is an explicit legacy advertisement that
dual-era clients (Python/TypeScript/Go SDKs) answer by falling back to
the initialize handshake deterministically, instead of inferring
legacy-ness from an error. As the negotiation bootstrap, discover is
answered regardless of the protocol version the request carries;
initialize likewise stays exempt from the version guard. Requests
without a _meta protocol version are served unchanged, so existing
handshake-era traffic is unaffected.

New Smithy shapes DiscoverResult and UnsupportedProtocolVersionErrorData
model the 2026-07-28 wire contract; supported version identifiers come
from a single registry-derived list so future versions update discover,
the error data, and version lookup together.

https://issues.amazon.com/issues/APPDEV-2258
@psp65
psp65 marked this pull request as ready for review August 29, 2026 00:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant