Carried forward from #34, which closes without it having a home.
The question
In rmcp 3.1.4, is_legacy_request (tower.rs) routes a POST to the
handshake-free stateless path when the request's revision is >= 2026-07-28 or
when a non-initialize request carries both io.modelcontextprotocol/protocolVersion
and …/clientCapabilities.
That second arm means a revision rmcp does support and that predates 2026-07-28
— 2025-11-25, say — reaches serve_negotiated_request_directly and gets a peer
synthesized with client_info: Implementation::default(), i.e. rmcp's own build
identity, purely because the request carried both keys.
Is that intended, or is the both-keys shape meant to imply the 2026-07-28
lifecycle? If intended, the discover-shape check is doing double duty. If not,
it is a routing hole that hands a server a placeholder identity for a client that
never asked for the handshake-free lifecycle.
Why bugwarden cares, and why it is not urgent for us
bugwarden is unaffected: lifecycle_of decides on the request's own _meta
revision and refuses anything below 2026-07-28 as out of contract, so that shape
gets mixed_lifecycle_refused rather than a placeholder-identity record. The
question is whether the SDK should be closing it, and whether our refusal is
defending against a bug or against a design.
The answer changes one thing for us: if upstream calls it a bug and fixes it, the
contains(&v) guard in lifecycle_of and part of the DESIGN.md trap prose become
belt-and-braces rather than load-bearing, and should be re-labelled as such rather
than left implying a hazard that no longer exists.
Suggested direction
Ask on modelcontextprotocol/rust-sdk — an issue, not a PR; this is a question
about intent, and the answer determines whether a patch is even wanted.
Filing it is outward-facing and needs Martin's go-ahead, which is why this
issue exists on our side rather than there.
Include the concrete trace: routing predicate, the synthesized-peer call site, and
the observation that validate_request_protocol_version_meta does not close it
because it only enforces header/_meta agreement once a _meta version is
present.
Re-verify against whatever rmcp version is pinned when this is picked up — the
routing description in this repo was wrong once already (bd674e7), and the
pinned version may have moved.
Carried forward from #34, which closes without it having a home.
The question
In rmcp 3.1.4,
is_legacy_request(tower.rs) routes a POST to thehandshake-free stateless path when the request's revision is >= 2026-07-28 or
when a non-
initializerequest carries bothio.modelcontextprotocol/protocolVersionand
…/clientCapabilities.That second arm means a revision rmcp does support and that predates 2026-07-28
— 2025-11-25, say — reaches
serve_negotiated_request_directlyand gets a peersynthesized with
client_info: Implementation::default(), i.e. rmcp's own buildidentity, purely because the request carried both keys.
Is that intended, or is the both-keys shape meant to imply the 2026-07-28
lifecycle? If intended, the discover-shape check is doing double duty. If not,
it is a routing hole that hands a server a placeholder identity for a client that
never asked for the handshake-free lifecycle.
Why bugwarden cares, and why it is not urgent for us
bugwarden is unaffected:
lifecycle_ofdecides on the request's own_metarevision and refuses anything below 2026-07-28 as out of contract, so that shape
gets
mixed_lifecycle_refusedrather than a placeholder-identity record. Thequestion is whether the SDK should be closing it, and whether our refusal is
defending against a bug or against a design.
The answer changes one thing for us: if upstream calls it a bug and fixes it, the
contains(&v)guard inlifecycle_ofand part of the DESIGN.md trap prose becomebelt-and-braces rather than load-bearing, and should be re-labelled as such rather
than left implying a hazard that no longer exists.
Suggested direction
Ask on
modelcontextprotocol/rust-sdk— an issue, not a PR; this is a questionabout intent, and the answer determines whether a patch is even wanted.
Filing it is outward-facing and needs Martin's go-ahead, which is why this
issue exists on our side rather than there.
Include the concrete trace: routing predicate, the synthesized-peer call site, and
the observation that
validate_request_protocol_version_metadoes not close itbecause it only enforces header/
_metaagreement once a_metaversion ispresent.
Re-verify against whatever rmcp version is pinned when this is picked up — the
routing description in this repo was wrong once already (
bd674e7), and thepinned version may have moved.