Conversation
…esence of _meta
`_meta` belongs to the base request shape in every revision — progress
tokens, trace context, whatever a client attaches — so the fixture cannot
read its presence as "2026-07-28 traffic". It did, and session-era requests
that carry it were rejected instead of served:
initialize (2025-11-25) + `_meta: {}`, no header -> 400 -32020 Missing MCP-Protocol-Version header
initialize (2025-11-25) + `_meta: {}`, era header -> 400 -32602 Invalid params: missing _meta or required fields
MCP Python SDK 2.x sends `_meta: {}` on `initialize`, which is what makes
the fixture unusable as its upstream server (modelcontextprotocol#506).
Two markers identify the request-meta wire: a `MCP-Protocol-Version` header
naming a post-session revision, and the per-request metadata envelope,
whose own marker is `io.modelcontextprotocol/protocolVersion` inside
`_meta`. Classify on those. Session-era traffic keeps the session path
whether or not it carries `_meta`, and the rejections the envelope exists to
trigger are untouched: a request-meta-era request that omits the header
still answers -32020, and an envelope missing its protocol version still
answers -32602.
Every scenario probe reaches the modern path through `buildStandardHeaders`,
which always sets `MCP-Protocol-Version`, so the 55 server scenarios are
unaffected — verified, plus a vitest case pinning both halves.
Author
|
Closing in favour of #507 — @lucarlig opened it three days before this, as the reporter, and it fixes the same thing. It routes a session-era |
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.
Fixes #506.
What's wrong
The fixture decides which wire a request is on by asking whether
params._metais present:But
_metais part of the base request shape in every revision — progress tokens, trace context, anything a client attaches — so a session-era request that carries it is routed to the 2026-07-28 path and rejected for metadata it never owed. Measured on7169291:The second and third are the same root cause as the reported one, reached through the other branch. MCP Python SDK 2.x sends
_meta: {}oninitialize, which is what makes this fixture unusable as its upstream server.What this changes
Two markers identify the request-meta wire, and neither is "there is a
_metakey":MCP-Protocol-Versionheader naming a post-session revision, andio.modelcontextprotocol/protocolVersioninside_meta.Classification uses those. Session-era traffic keeps the session path whether or not it carries
_meta.What did NOT change
The rejections the envelope exists to trigger still fire — a request-meta-era request that omits the header still answers
-32020, and an envelope missing its protocol version still answers-32602:Nothing in the suite depended on the old rule: every scenario probe builds its request through
buildStandardHeaders, which always setsMCP-Protocol-Version(defaultDRAFT_PROTOCOL_VERSION), so the_meta-integrity probes instateless.tsreach the modern path by header and keep failing as they should.Verification
all-scenarios.test.ts: 55/55 before and after.npm test: 627/627.npm run check: clean.everything-server-request-classification.test.ts) pins both halves — the two session-era requests that were rejected, and the three request-meta-era outcomes that must not move._metafails 2 of the 5; dropping the envelope signal fails the-32020case; treating a session-era header as modern fails the progress-token case.