MCP 2026-07-28 (2/11): stateless protocol on stdio — server/discover, per-request _meta, legacy fallback - #225
Open
simonx1 wants to merge 4 commits into
Open
Conversation
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
simonx1
force-pushed
the
mcp-2026/stateless-stdio
branch
from
September 1, 2026 18:40
7065c9f to
cb0f7d6
Compare
Probe with server/discover before any other request. A DiscoverResult or a recognized modern error (UnsupportedProtocolVersionError, retried with an advertised version) marks the server modern; any other error or a timeout falls back to the initialize handshake (protocol: :auto, the default). protocol: :modern refuses to fall back, :legacy skips the probe. Modern requests carry protocolVersion, clientInfo and clientCapabilities (plus declared extensions) in _meta; host _meta keys such as progressToken and OpenTelemetry trace context are preserved and the reserved keys cannot be overridden. Client.new(request_meta:) merges default metadata into every request. An inline UnsupportedProtocolVersionError switches version and re-sends once. Removed-method mapping for modern servers: ping -> server/discover, log_level= -> per-request _meta logLevel, no roots/list_changed. Every stdio method now goes through rpc_request. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MoErzDypnq7hhuFBtueML7
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MoErzDypnq7hhuFBtueML7
Codex + grok findings on the stateless stdio PR: - Any recognized modern error (-32020/-32021, or -32022 with no usable version) or a malformed DiscoverResult now surfaces instead of falling back to initialize, and clears the tentative protocol version. - notifications/roots/list_changed (and notifications/initialized) are dropped inside rpc_notify once a server is known to be modern, so a roots= call before the first request cannot leak the removed method. - Every server/discover result is applied, not only the probe's. - The probe waits the full read_timeout by default: a slow-starting modern server must not be misclassified as legacy. - ensure_initialized is serialized on a reentrant lock so concurrent first requests spawn and probe once. - Modern requests declare no roots/sampling/elicitation capability until the multi round-trip pattern exists; an unfulfillable input_required result raises InputRequiredError rather than posing as a tool result. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MoErzDypnq7hhuFBtueML7
simonx1
force-pushed
the
mcp-2026/stateless-stdio
branch
from
September 1, 2026 19:02
cb0f7d6 to
8582b6f
Compare
2 tasks
…swer; drop server requests Third review round (codex + grok) on the stateless stdio PR: - A well-formed UnsupportedProtocolVersionError settles the server as modern: a failure of the retried probe surfaces instead of falling back. - A probe answered with something that is not a DiscoverResult (a scalar, null, or an object without supportedVersions) is a legacy answer and falls back to initialize (refused under protocol: :modern). - Every server/discover answer, including the ping heartbeat, is validated and applied. - Inline version renegotiation compares against the version the rejected request was actually sent with, so concurrent requests cannot make each other give up. - On a modern session server-initiated requests (ping, roots/list, elicitation, sampling) are dropped instead of answered: the client MUST NOT write JSON-RPC responses on 2026-07-28 stdio. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MoErzDypnq7hhuFBtueML7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Second PR of the MCP 2026-07-28 series (stacked on #224). Implements the stateless, handshake-free protocol for the stdio transport and the shared per-request
_metamachinery every transport will use.server/discoverprobe + era detection (basic/transports/stdio "Backward Compatibility", basic/versioning) — before any other request the client sendsserver/discoverwith its preferred modern version.DiscoverResult⇒ modern (version chosen fromsupportedVersions, capabilities / instructions /_meta.serverInforecorded, noinitializeever sent).UnsupportedProtocolVersionError⇒ still modern: retry the probe with an advertised version, never fall back. Any other error or timeout ⇒ legacy: runinitializeexactly as before. Era is cached per process.protocol: :auto | :modern | :legacyanddiscover_timeout:onstdio_config/ServerStdio._meta(basic/index "_meta") —protocolVersion,clientInfo(unlesssend_client_info = false),clientCapabilities(withextensionsviadeclare_extension) on every modern request. Host_meta(progressToken, OTeltraceparent/tracestate/baggage) is preserved; reserved keys cannot be overridden.Client.new(request_meta:)merges defaults per request. Legacy traffic is unchanged.UnsupportedProtocolVersionErroron any request switches to a mutually supported version and re-sends once with a new id.ping→server/discover(answered from the fresh probe),log_level=→_meta["io.modelcontextprotocol/logLevel"](nologging/setLevel), andnotifications/roots/list_changedis not sent (modernrootscapability is{}).rpc_request, so meta injection, typed errors and version retry apply uniformly.HTTP transports keep their legacy behaviour in this PR; the Streamable HTTP modern mode is PR 3.
Test plan
bundle exec rspec— 1747 examples, 0 failures (43 new instateless_stdio_2026_spec.rb)bundle exec rubocop— clean🤖 Generated with Claude Code
https://claude.ai/code/session_01MoErzDypnq7hhuFBtueML7