Skip to content

test(conformance): bump referee to 0.2.0-alpha.9; arm SEP-2575 diagnostic fixtures; fix post-dispatch -32021 HTTP status#2399

Merged
felixweinberger merged 3 commits into
mainfrom
fweinberger/conformance-alpha8-bump
Jul 2, 2026
Merged

test(conformance): bump referee to 0.2.0-alpha.9; arm SEP-2575 diagnostic fixtures; fix post-dispatch -32021 HTTP status#2399
felixweinberger merged 3 commits into
mainfrom
fweinberger/conformance-alpha8-bump

Conversation

@felixweinberger

@felixweinberger felixweinberger commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Adopt conformance 0.2.0-alpha.9 and reconcile the expected-failures baselines.

Motivation and Context

The alpha.8 line carries conformance#372: checks whose prerequisite is missing now fail with Not testable: instead of silently skipping. Adopting it surfaced two things here:

  1. Missing diagnostic fixtures. The conformance everythingServer never registered the three tools the server-stateless scenario hardcodes — test_missing_capability, test_streaming_elicitation, test_logging_tool — so four checks silently SKIPPED at alpha.7. The fixtures are now armed and three of the four pass.

  2. A real gap, reachable only once the fixture existed. A MissingRequiredClientCapabilityError (-32021) produced after dispatch — the input_required capability gate — surfaced in-band on HTTP 200, while the spec mandates 400 for this error with no origin condition ("For HTTP, the response status code MUST be 400 Bad Request"). The in-band status policy (httpStatusForErrorCode + the per-request transport) now carries that one code-keyed exception, applied only while the response is uncommitted: an exchange that already streamed, or one hosted with responseMode: 'sse' (which opens its stream at dispatch end), keeps its committed 200. A handler relaying a downstream peer's -32020/-32022 is not this server's spec error and deliberately keeps the origin-keyed in-band 200.

The fourth check originally failed on a referee-side assertion (requiredCapabilities asserted as an array where the schema defines a ClientCapabilities object). That fix is conformance#376, contained in 0.2.0-alpha.9 — so server-stateless passes outright (30/30) and is not baselined. The baselines now carry only the tasks-* scenarios (the server SDK does not implement the SEP-2663 tasks extension), and the 2026-07-28 baseline is empty in both directions.

How Has This Been Tested?

All six conformance legs exit 0 against the new pin, zero stale entries:

leg alpha.7 this PR (alpha.9)
client:all / client:2026 / server 438/0 · 374/0 · 42/0 unchanged
server:draft 81/0 85 / 0
server:extensions 136 / 27 expected 140 / 30 expected (all tasks-*)
server:2026 110/0 114 / 0

(+4 newly passing checks per affected leg; the only remaining expected failures are the tasks-* scenarios.)

Full suite: typecheck:all, lint:all, pnpm -r test (e2e 2633 passed / 155 expected-fail, integration 348/348). The -32021 fix verified on the wire: tools/call test_missing_capability with empty clientCapabilities answers HTTP/1.1 400 carrying data.requiredCapabilities: { "sampling": {} }; with sampling declared → 200; an unknown tool (-32602) → 200 (origin-keyed rule untouched). New transport tests pin the 400, the relay 200s, and the already-streamed case (the terminal error frame is asserted on the stream).

Breaking Changes

None for spec-conformant consumers. The -32021 HTTP-status change is a patch-level spec-compliance fix; changeset and a migration-guide note are included (earlier v2 alphas surfaced the post-dispatch -32021 on 200).

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Initially opened at 0.2.0-alpha.8 with server-stateless baselined for the referee-side assertion; retargeted in place to 0.2.0-alpha.9 (= alpha.8 + conformance#376) once it published, which removed that entry.

Final push also folds in review hardening: the status-policy pins consolidated into errorHttpStatusMatrix.test.ts (the redundant block in the ladder cell sheet is deleted; coverage non-decreasing), plus short comments fencing the known traps — why the transport indexes the status table rather than delegating to httpStatusForErrorCode, the handler-thrown -32021 case, and a note for consumers wiring PerRequestHTTPServerTransport into custom entries.

@felixweinberger felixweinberger requested a review from a team as a code owner June 30, 2026 20:13
@changeset-bot

changeset-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fa56d77

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@modelcontextprotocol/server Patch
@modelcontextprotocol/core-internal Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jun 30, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/@modelcontextprotocol/client@2399

@modelcontextprotocol/codemod

npm i https://pkg.pr.new/@modelcontextprotocol/codemod@2399

@modelcontextprotocol/core

npm i https://pkg.pr.new/@modelcontextprotocol/core@2399

@modelcontextprotocol/server

npm i https://pkg.pr.new/@modelcontextprotocol/server@2399

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/@modelcontextprotocol/server-legacy@2399

@modelcontextprotocol/express

npm i https://pkg.pr.new/@modelcontextprotocol/express@2399

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/@modelcontextprotocol/fastify@2399

@modelcontextprotocol/hono

npm i https://pkg.pr.new/@modelcontextprotocol/hono@2399

@modelcontextprotocol/node

npm i https://pkg.pr.new/@modelcontextprotocol/node@2399

commit: fa56d77

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No bugs found, but this changes wire behavior of the modern HTTP path (post-dispatch -32021 now answers 400) and codifies a deliberate asymmetry — code-keyed mapping for -32021 vs origin-keyed for -32020/-32022, including a relayed downstream -32021 now also getting 400 — so a maintainer should confirm that spec reading and the committed-stream carve-out before merge.

Extended reasoning...

Overview

The PR bumps the conformance referee pin to 0.2.0-alpha.8, registers three SEP-2575 diagnostic tools in the conformance everythingServer, reconciles both expected-failures baselines (excusing the whole server-stateless scenario for one referee-side check, with burn-down notes), and — the substantive part — changes HTTP status mapping so a handler-/post-dispatch-produced -32021 (MissingRequiredClientCapability) answers HTTP 400 instead of the in-band 200, but only while the response is uncommitted. Touched production files: inboundClassification.ts (httpStatusForErrorCode), perRequestTransport.ts (terminal-error status arm), and a comment update in createMcpHandler.ts. Changeset and migration-guide note are included.

Security risks

None identified. The change affects only the HTTP status code attached to an already-emitted JSON-RPC error body on the modern per-request path; no auth, parsing, or data-exposure surface changes. The new conformance fixtures are test-only.

Level of scrutiny

Moderate-to-high for the transport change: it alters observable wire behavior of the 2026-07-28 serving path and introduces the first code-keyed exception to an otherwise origin-keyed status policy. The implementation looks correct — httpStatusForErrorCode('in-band', ...) has no production callers (only tests), the per-request transport applies the mapping only when no SSE stream is open so committed/forced-SSE exchanges keep their 200 and still deliver the error in-stream, and pre-dispatch ladder behavior is unchanged — but the design choice (relayed -32021 from a downstream peer also maps to 400, while relayed -32020/-32022 deliberately stay in-band) is a spec-interpretation call a maintainer should ratify, as is excusing the entire server-stateless scenario in the baselines for a single referee-side assertion (conformance#376).

Other factors

Test coverage is good: new transport tests pin the post-dispatch 400, the relay-200 cases, and the already-streamed terminal-frame case; the core-internal status-matrix tests are updated consistently. Docs (changeset, migration note) match the implemented behavior including the committed-stream exception. The conformance description claims all six legs exit 0 against the new pin, which I cannot verify here. No prior human review exists on this PR.

@felixweinberger felixweinberger force-pushed the fweinberger/conformance-alpha8-bump branch from da2796a to 3e7f63f Compare July 1, 2026 18:17
@felixweinberger felixweinberger changed the title test(conformance): bump referee to 0.2.0-alpha.8; arm SEP-2575 diagnostic fixtures; fix post-dispatch -32021 HTTP status test(conformance): bump referee to 0.2.0-alpha.9; arm SEP-2575 diagnostic fixtures; fix post-dispatch -32021 HTTP status Jul 1, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No bugs found, but beyond the conformance pin bump this changes wire-visible HTTP status semantics in the per-request transport (post-dispatch -32021 → 400) — a spec-compliance behavior change in a critical protocol path that deserves a human look at the code-keyed exception design.

Extended reasoning...

Overview

The PR bundles three things: (1) a conformance referee bump to 0.2.0-alpha.9 with expected-failures reconciliation (now only tasks-*), (2) three new SEP-2575 diagnostic fixture tools in the conformance everythingServer, and (3) a real SDK behavior change: a MissingRequiredClientCapabilityError (-32021) produced after dispatch now answers HTTP 400 instead of in-band 200, implemented as a code-keyed exception in httpStatusForErrorCode and in PerRequestHTTPServerTransport.send(), gated on the response not yet being committed (no SSE stream open). A changeset and a migration-guide note accompany it.

Security risks

None identified. The change affects only HTTP status codes on error responses; no auth, crypto, or data-exposure surface is touched. The new fixture tools live in the conformance test server only.

Level of scrutiny

The conformance pin, baselines, and fixtures are low risk. The status-mapping change, however, alters the previously origin-keyed policy (handler errors always in-band on 200) with a single code-keyed exception, in the modern per-request HTTP transport — a critical, wire-visible protocol path this repo's review conventions specifically flag for careful status-code handling. The implementation looks correct: the exception applies only while no response is committed (the already-streamed and forced-SSE cases keep their 200 and carry the error in-stream, with tests pinning both), relayed -32020/-32022 stay in-band, and httpStatusForErrorCode's in-band branch has no other production caller (the pre-dispatch gate uses the 'ladder' origin). But whether the SDK should carve a per-code exception into the origin-keyed policy — versus, say, tagging the input_required gate's emission as ladder-originated — is a design call a maintainer should weigh in on.

Other factors

Test coverage is thorough: new transport tests pin the 400, the relay-stays-200 cases, and the already-streamed terminal-frame case; the status-matrix and ladder cell-sheet tests were updated consistently; the changeset and migration prose match the implementation. The PR description reports all six conformance legs green plus the full test suite. No prior reviewer comments exist on the thread, and the bug-hunting system found no issues.

…stic fixtures; fix post-dispatch -32021 HTTP status

conformance 0.2.0-alpha.8 fails checks whose prerequisite is missing instead
of silently skipping them (conformance#372). Adopting that line surfaced two
gaps:

- the conformance everythingServer never registered the three diagnostic
  tools the server-stateless scenario hardcodes; test_missing_capability,
  test_streaming_elicitation and test_logging_tool are now armed.
- arming test_missing_capability made a real defect reachable: a
  MissingRequiredClientCapabilityError (-32021) produced after dispatch (the
  input_required capability gate) surfaced in-band on HTTP 200, while the
  spec mandates 400 for this error with no origin condition. The in-band
  status policy now carries that one code-keyed exception
  (httpStatusForErrorCode + the per-request transport), applied only while
  the response is uncommitted: an exchange that already streamed, or one
  hosted with responseMode 'sse' (which opens its stream at dispatch end),
  keeps its committed 200. Every other handler-produced code - including a
  handler relaying a downstream peer's -32020/-32022 - keeps the
  origin-keyed in-band 200.

alpha.9 also contains conformance#376 (the server-stateless
requiredCapabilities assertion corrected from an array to the schema's
ClientCapabilities object), so server-stateless passes outright and is NOT
baselined. The expected-failures baselines now carry only the tasks-*
scenarios (the server SDK does not implement the SEP-2663 tasks extension);
the 2026-07-28 baseline is empty in both directions.

All six legs exit 0 with zero stale entries: client:all 438/0,
client:2026 374/0, server 42/0, server:draft 85/0, server:extensions
140 passed / 30 expected (all tasks-*), server:2026 114/0.
@felixweinberger felixweinberger force-pushed the fweinberger/conformance-alpha8-bump branch from 3e7f63f to e20ffcb Compare July 2, 2026 10:06

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No bugs found, but note this PR ships a production behavior change beyond the conformance bump (post-dispatch -32021 now answers HTTP 400 in the per-request transport / status policy), so it deserves a maintainer's look at the design call rather than a rubber stamp.

Extended reasoning...

Overview

The PR does three things: (1) bumps the conformance referee pin from 0.2.0-alpha.7 to 0.2.0-alpha.9 and reconciles the expected-failures baselines, (2) registers three SEP-2575 diagnostic fixture tools in the conformance everythingServer so previously-skipped checks are exercised, and (3) changes production behavior in packages/core-internal/src/shared/inboundClassification.ts and packages/server/src/server/perRequestTransport.ts: a MissingRequiredClientCapabilityError (-32021) produced after dispatch now answers HTTP 400 instead of the in-band 200, whenever the response is still uncommitted. A changeset and a migration-guide note accompany the change, and the redundant status-policy tests were consolidated into errorHttpStatusMatrix.test.ts.

Security risks

None apparent. The change only affects which HTTP status accompanies an already-emitted JSON-RPC error body; no auth, crypto, or input-handling surfaces are touched. The new fixture tools live in the private conformance test package.

Level of scrutiny

The conformance/test/fixture portions are low-risk and mechanical. The -32021 status change, however, edits the error→HTTP-status policy in the core transport layer — protocol-critical wire behavior — and introduces the first code-keyed exception to the previously purely origin-keyed mapping (handler errors stay in-band on 200). Whether a handler-thrown -32021 (e.g. a proxy relaying a downstream server's error) should be re-mapped to this server's spec-mandated 400, versus keeping the exception scoped to the SDK's own input_required gate, is a design judgment the PR argues one way (the spec mandates 400 per-error with no origin condition, and the migration note tells proxies to translate rather than rethrow) — that call is worth a maintainer's confirmation. The title reads as a test-only change, so it's easy to miss that it carries a patch-level behavior change to @modelcontextprotocol/server and core-internal.

Other factors

The implementation looks careful: the mapping only applies while the response is uncommitted (already-streamed and forced-SSE exchanges keep their committed 200 with the error in-stream, and the new test asserts the terminal frame still reaches the client), the relay behavior for -32020/-32022 is deliberately left origin-keyed and pinned in tests, and the pre-dispatch gate comment in createMcpHandler.ts was updated to match. Documentation, changeset, and conformance results (server-stateless now 30/30, tasks-* the only remaining baseline) are all consistent with the diff. No bugs were found by the bug-hunting pass and I did not find any either — the deferral is about the design/behavior-change dimension, not correctness.

@felixweinberger felixweinberger merged commit 3c7ddaf into main Jul 2, 2026
18 checks passed
@felixweinberger felixweinberger deleted the fweinberger/conformance-alpha8-bump branch July 2, 2026 12:17
@github-actions github-actions Bot mentioned this pull request Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant