Skip to content

fix(StatelessHTTPServerTransport): reject colliding in-flight JSON-RPC ids (#254, #265) - #267

Open
jpurnell wants to merge 1 commit into
modelcontextprotocol:mainfrom
jpurnell:fix/stateless-transport-id-correlation
Open

fix(StatelessHTTPServerTransport): reject colliding in-flight JSON-RPC ids (#254, #265)#267
jpurnell wants to merge 1 commit into
modelcontextprotocol:mainfrom
jpurnell:fix/stateless-transport-id-correlation

Conversation

@jpurnell

Copy link
Copy Markdown

Summary

StatelessHTTPServerTransport keys per-request state — the response waiter and the
originating HTTP context — on the client-chosen JSON-RPC id. JSON-RPC ids are
client-scoped, and this transport is explicitly multi-client, so concurrent POSTs with
colliding ids are legal and common (most clients start their id sequence at 1). Two
concurrent requests sharing an id currently:

Fix

Reject a request whose id is already in flight with HTTP 409 / JSON-RPC -32600
instead of silently displacing the first request. The in-flight context map is populated
synchronously before any suspension, so it is a reliable "id in flight" marker on the
transport actor. This isolates every in-flight exchange and closes both the hang/leak and
the cross-client context disclosure.

This is the minimal, non-behavioral-change-to-ctx.id fix. Transparent support for
concurrent colliding ids (via internal id remapping) is a larger change tied to the
stateless-core semantics in SEP-2575 (#245) and is intentionally out of scope here.

Tests

Adds testCollidingInFlightRequestIDsRejected:

  • the duplicate returns promptly with 409 (no hang),
  • the first request's httpRequestContext is never overwritten (auth stays isolated),
  • the first request still completes normally with its own response,
  • only the first request is ever dispatched to the server.

All existing HTTP-server transport and handler-context tests pass unchanged; full MCP
suite green (552 tests).

Fixes #254
Fixes #265

…C ids

Per-request state (response waiter + HTTP context) was keyed by the client-chosen
JSON-RPC id. Those ids are client-scoped, and this transport is multi-client, so
concurrent POSTs with colliding ids (most clients start at 1) would:

- overwrite the first request's response waiter — the first POST hangs forever and its
  continuation leaks (modelcontextprotocol#254); and
- overwrite the first request's HTTP context — a handler processing one client's request
  could read another client's Authorization header (modelcontextprotocol#265).

Reject a request whose id is already in flight with HTTP 409 / JSON-RPC -32600 instead of
silently displacing the first. This isolates every in-flight exchange and closes both the
hang/leak and the cross-client context disclosure. Transparent support for concurrent
colliding ids is deferred to the stateless core (SEP-2575).

Adds a regression test asserting the duplicate is rejected promptly (no hang), the first
request's context is never overwritten, and only the first request reaches the server.

Fixes modelcontextprotocol#254
Fixes modelcontextprotocol#265
@jpurnell
jpurnell force-pushed the fix/stateless-transport-id-correlation branch from f7921c6 to b79c7b3 Compare July 29, 2026 15:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment