fix: enforce HTTP 400 and data.requested for per-request _meta and version errors#343
Merged
Merged
Conversation
…rsion errors The everything-server returned its -32602 missing-_meta rejection with HTTP 200; the draft spec requires 400 Bad Request. It also applied the per-request _meta validation to sessionless requests carrying a legacy session-era version header, although per-request metadata is a 2026-07-28 requirement; such requests now fall through to the session path. The server-stateless scenario gains a companion check (sep-2575-http-server-meta-invalid-400) requiring HTTP 400 on those rejections, and sep-2575-server-unsupported-version-error now also requires error.data.requested to echo the requested version. Two new negative tests pin both.
commit: |
commit: |
pcarleton
approved these changes
Jun 16, 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.
Three small, strictly spec-backed fixes around per-request
_metahandling: the everything-server fixture returned its missing-_metarejection with the wrong HTTP status and applied draft-only validation to legacy traffic, and theserver-statelessscenario was not checking two members the spec requires. No error-code expectations change in this PR (those are tied to the open renumbering discussion in modelcontextprotocol/modelcontextprotocol#2907 and stay with #336).Motivation and Context
Each change maps to a normative sentence in the current draft (2026-07-28) revision (quotes from
98c49d9):Missing required
_metafields ⇒ HTTP 400.basic/index.mdx#L313-L315:The everything-server returned this
-32602error with HTTP 200, and thesep-2575-request-meta-invalid-*checks only looked at the JSON-RPC code, so the suite could not catch the wrong status.Per-request
_metais a modern-revision requirement.basic/versioning.mdx#L34-L37:The dual-era everything-server applied the per-request
_metavalidation to any sessionless request that carried anMCP-Protocol-Versionheader — including legacy-revision values — so a legacy client that sends the header before it has a session would be rejected with "missing_meta" for metadata its revision does not define.UnsupportedProtocolVersionError.datarequiresrequested(andsupported).schema/draft/schema.ts#L387-L402definesdata: { supported: string[]; requested: string }(both non-optional), andbasic/versioning.mdx#L48-L52:sep-2575-server-unsupported-version-erroralready validateddata.supported, but never checked thatdata.requestedis present and echoes the version the request asked for.What changed
examples/servers/typescript/everything-server.ts_meta-fields rejection now returns HTTP 400 (code stays-32602)._metaand a legacy session-era version inMCP-Protocol-Version(2024-11-05 … 2025-11-25) skip the per-request validation block and fall through to the existing session/initialize handling. Requests that carry_meta, the draft version, or an unknown version are validated exactly as before.src/scenarios/server/stateless.tssep-2575-http-server-meta-invalid-400(FAILURE severity), emitted once per_metaprobe: rejections of requests missing required_metafields must use HTTP 400. The existingsep-2575-request-meta-invalid-*checks keep their code-only meaning, mirroring how the scenario already splits error-shape vs HTTP-status checks elsewhere (e.g.sep-2575-server-unsupported-version-error/sep-2575-http-server-unsupported-version-400).sep-2575-server-unsupported-version-errornow also requireserror.data.requestedto echo the requested version.src/scenarios/server/stateless.test.ts: two new negative tests — a server that returns the-32602rejection with HTTP 200 keeps the per-field checks green but FAILs the new status check, and a server that omitsdata.requestedfrom its unsupported-version error FAILs the negotiation check.Deliberately not in this PR: any change to which JSON-RPC error codes the scenario expects. The
-32602/-32001/-32004questions interact with the error-code renumbering in modelcontextprotocol/modelcontextprotocol#2907 and remain with #336.How Has This Been Tested?
npm run check,npm run build,npm test— all green (287 tests).all-scenarios.test.tsruns the draft and active suites against the modified everything-server in-process, which exercises the strengthened checks against the fixed fixture; the active (2025) scenarios are unaffected by the legacy-traffic gate because the SDK client never sends the version header before it has a session.data.requested).Breaking Changes
None for conformant servers. Servers that return the missing-
_metarejection with a non-400 status, or an unsupported-version error withoutdata.requested, will newly fail the corresponding checks — both are MUST/required-member violations of the draft revision — and may need anexpected-failuresentry until fixed.Types of changes
Checklist
Additional context
_meta/protocolVersionwhen a valid header is present, andHeaderMismatchvsUnsupportedProtocolVersionprecedence when both rules trigger) depend on the error-code allocation/renumbering in Define error code allocation policy and renumber draft error codes modelcontextprotocol#2907 and on a precedence clarification, and are untouched here.server-initializescenario's raw probe (which sends a2025-11-25header with no session and no_meta) previously hit the_metarejection on the everything-server and reported its session-id check as INFO; it now reaches the real initialize/session path, so that check reports SUCCESS against the fixture.LEGACY_SESSION_PROTOCOL_VERSIONSlist rather than importing the suite'sSTATEFUL_VERSIONSbecause the example servers intentionally don't import fromsrc/.sep-2575-http-server-meta-invalid-400is not added tosep-2575.yaml, matching the existingsep-2575-request-meta-invalid-*checks which aren't mapped there either; a follow-up could add rows for both halves of the missing-field requirement if we want them traced.