Fix stdio legacy-fallback rule and add a compatibility matrix#2844
Merged
Conversation
The stdio backward-compatibility probe told clients to fall back to the legacy initialize handshake only on Method not found (-32601). Legacy protocol revisions never specified how a server responds to an unknown request received before initialize, and implementations vary: some return -32601, others reject the request as invalid (-32602), and some do not respond at all. A fallback keyed to -32601 therefore fails against real legacy servers. - Rewrite the stdio fallback rule: any error that is not a recognized modern error, or no response within a timeout, indicates a legacy server. Forbid keying the fallback to a single error code. - Add guidance to cache the era determination per server. - Recommend probing on stdio even for modern-only clients, since legacy servers may process era-ambiguous methods under legacy semantics. - Add a compatibility matrix covering modern/dual-era/legacy client and server combinations, including the previously unspecified cases (legacy client vs modern-only server, dual-era server semantics). - Align the duplicated text in transports.mdx and discover.mdx.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
* Restructure draft transport documentation
The transports page presented MRTR, subscription streams, and the
request-metadata model as Streamable HTTP features, while the stdio
section was silent about them. The split mirrored the old transport
model, where each transport had genuinely different capabilities. In the
current draft, protocol semantics are identical on every transport; only
the binding differs.
- Split basic/transports.mdx into a transports/ section:
- index.mdx: transport-independent overview — the client-initiated
message flow, MRTR and subscriptions/listen as the only
server-to-client mechanisms on any transport, the per-request
metadata model, and what a binding must define.
- stdio.mdx and streamable-http.mdx: per-transport bindings.
- Move MRTR from basic/utilities/ to basic/transports/, since it is part
of the transport message flow rather than an optional utility. Add a
redirect for the old URL.
- Remove leftovers from the bidirectional era: clients no longer send
JSON-RPC responses on either transport, and the stdio page now states
explicitly that servers must not write JSON-RPC requests to stdout.
- Update navigation and all draft-internal links.
* Fix typos in MRTR security requirements
* Note that stdio framing generalizes to any byte stream
The stdio wire format (newline-delimited JSON-RPC over a reliable
bidirectional byte stream) is not specific to standard streams; only the
process-lifecycle rules are. State this on the stdio page and recommend
that custom stream-based transports (Unix domain sockets, TCP) reuse the
stdio framing instead of defining new framing.
* State the transport message flow declaratively
* Trim request metadata wording in transport overview
* Replace em-dashes with plain punctuation in new spec text
* Make message-direction rules prescriptive in transport bindings
* Clarify stdio receiving-messages section
* Deduplicate backward-compatibility text across lifecycle and transports
The era-detection mechanics were spelled out three times: in lifecycle,
in the transport binding pages, and partially in server/discover. The
copies had already drifted once.
- Binding pages are now canonical for detection mechanics. The stdio
page absorbs the detail previously only in lifecycle (preferred
version in _meta, three probe outcomes, supportedVersions selection).
- Lifecycle keeps the transport-independent content: the era model,
caching guidance, and the compatibility matrix. It links to the
binding pages for mechanics.
- server/discover refers to the stdio binding page instead of restating
the fallback rules.
* Reorder lifecycle page: scope first, transition material last
- Open with a scope statement and a short overview instead of normative
rules.
- Add a terminology section defining modern, legacy, and dual-era before
first use (previously defined inside the compatibility-matrix intro).
- Group the statelessness requirements under their own heading; split
compound bullets and demote the connection-identity rationale to a
note.
- Move extension negotiation before backward compatibility, so core
protocol material precedes transition material.
- Promote backward compatibility to a top-level section (anchor
unchanged).
* Reorder transport overview: scope first, transition material last
- Open with a scope statement instead of the UTF-8 encoding rule; the
encoding requirement moves to the top of Message Flow.
- Move Custom Transports before Backward Compatibility, so requirements
for new bindings precede transition material.
* Add message patterns page; keep MRTR under utilities
Review feedback on the transport restructure: MRTR describes message
flow and contents, like subscriptions and progress, which live under
utilities. Moving only MRTR under transports was inconsistent. The
underlying gap is that no page defined the core message patterns every
transport carries.
- Add basic/patterns.mdx (Message Patterns), after Lifecycle in the nav.
It defines the client-initiated model and the three core patterns
(request/response, multi round-trip requests, subscribe and notify),
each with a sequence diagram and a link to its detailed page, and
states that new patterns are added there without transport changes.
- Move MRTR back to basic/utilities/ and drop the redirect.
- Slim the transports overview to binding concerns: message delivery
requirements, metadata carriage, cancellation, custom transports, and
backward compatibility. It links to the patterns page for flow
semantics.
* update basic/index.mdx to reference message patterns page
* add message patterns
* Align pattern names between overview and patterns page
Use the same pattern names and order in the base-protocol overview as on
the patterns page, and link each pattern to its section. Rename the
Request/Response heading to Request and Response so its anchor does not
contain an encoded slash.
* Title the transports index page Overview, matching other group index pages
* Rename Lifecycle to Versioning and Compatibility
The protocol is stateless: there is no initialize/operate/shutdown
sequence left to describe. Initialization became per-request metadata,
and shutdown is a transport-binding concern documented on the binding
pages. The remaining content of the lifecycle page has one theme: how
two implementations agree on what they are speaking.
- Rename basic/lifecycle.mdx to basic/versioning.mdx, titled
"Versioning and Compatibility", covering version negotiation,
extension negotiation, and backward compatibility. Add a redirect.
- Move the statelessness section to the base-protocol overview, where
the execution model belongs.
- Update navigation and all draft-internal links.
---------
Co-authored-by: Caitie McCaffrey <caitiem20@github.com>
CaitieM20
approved these changes
Jun 3, 2026
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.
Motivation and Context
The draft spec removed the
initializehandshake (SEP-2575). For stdio, the backward-compatibility text told clients to fall back to the legacyinitializehandshake only when aserver/discoverprobe returnsMethod not found(-32601).Legacy protocol revisions never specified how a server responds to an unknown request received before
initialize, and implementations differ: the TypeScript SDK returns-32601, the Python SDK rejects the request as invalid (-32602, the unknown method fails request validation before dispatch), and some servers do not respond at all. A fallback keyed to-32601therefore fails against real legacy servers.Changes
server/discoveron stdio even for modern-only clients. Some legacy servers do not validate that requests arrive afterinitializeand would process era-ambiguous methods (such astools/call) under legacy semantics, silently ignoring the declared protocol version; probing turns this into a deterministic failure.server/discover.The branch also carries two small pre-existing doc fixes (deprecated-feature mentions, architecture links).
How was this tested?
npm run prep(schema generation, format check, link check, SEP check) passes.