Skip to content

fix: isolate stateless HTTP request exchanges - #264

Open
jstar0 wants to merge 1 commit into
modelcontextprotocol:mainfrom
jstar0:fix/stateless-response-exchange-routing
Open

jstar0 wants to merge 1 commit into
modelcontextprotocol:mainfrom
jstar0:fix/stateless-response-exchange-routing

Conversation

@jstar0

@jstar0 jstar0 commented Jul 26, 2026

Copy link
Copy Markdown

Summary

Fixes #254.

StatelessHTTPServerTransport currently keys response waiters by the client's
raw JSON-RPC id. Because ids are scoped to each client, concurrent POSTs can
legally reuse the same id; the later request then overwrites the earlier
continuation, leaving one HTTP exchange suspended indefinitely. String and
integer ids also collide after stringification.

Changes

This change assigns a private UUID to each in-flight HTTP exchange before the
request is yielded to Server. Responses route by that private id, then restore
the client's original string or integer id before returning over HTTP.

The same exchange identity now scopes HTTP request context, while typed raw-id
aliases preserve existing direct lookups. A package-only mapping keeps
Server.currentHandlerContext.id and cancellation lookup on the original client
id without changing public transport or handler APIs. Cancellation fails closed
when multiple active exchanges make a reused wire id ambiguous.

Verification

  • Added a deterministic regression that waits for two same-id requests to be in
    flight before sending either response.
  • Added string/integer id, HTTP context, handler context, disconnect cleanup, and
    cancellation compatibility coverage.
swift test -v

Result: 555 tests in 40 suites passed.

Scope

Signed-off-by: King Star <mcxin.y@gmail.com>
ianegordon added a commit to ianegordon/swift-sdk that referenced this pull request Sep 10, 2026
…s HTTP request exchanges

Brings modelcontextprotocol#264 onto the modelcontextprotocol#254 branch so its
exchange-scoped id routing can be verified against this branch's
regression tests. Sources are the PR's head e14ef60, unmodified.
@ianegordon

Copy link
Copy Markdown

I have independently verified this PR does resolve the issue I opened, #254 . I have some low priority test improvements that can land after this PR.

I recommend landing the related PRs in the following order.
PR #264 (fixes Issue #254)
PR #260 (fixes Issue #255)

ianegordon added a commit to ianegordon/swift-sdk that referenced this pull request Sep 10, 2026
ianegordon added a commit to ianegordon/swift-sdk that referenced this pull request Sep 10, 2026
…rotocol#264

Manifest entry 1a. Tests-only, stacked on pr/264 at e14ef60; offered
upstream alongside modelcontextprotocol#264.
ianegordon added a commit to ianegordon/swift-sdk that referenced this pull request Sep 10, 2026
ianegordon added a commit to ianegordon/swift-sdk that referenced this pull request Sep 10, 2026
…tream modelcontextprotocol#260, rebased onto modelcontextprotocol#264)

Manifest entry 2. Upstream refs/pull/260/head b5da0ef (branch pr/260),
rebased onto modelcontextprotocol#264 as 12b8c92 with the cancellation routed through the
exchange table. Fixes modelcontextprotocol#255.
ianegordon added a commit to ianegordon/swift-sdk that referenced this pull request Sep 10, 2026
ianegordon pushed a commit to ianegordon/swift-sdk that referenced this pull request Oct 13, 2026
…ests without a reply (upstream modelcontextprotocol#298, EhsanAzish80)

Supersedes the contributions of modelcontextprotocol#264, modelcontextprotocol#260 and modelcontextprotocol#270 (manifest entries 1-3),
which modelcontextprotocol#298 combines. Conflicts resolved in favour of modelcontextprotocol#298 in
HTTPServerTypes.swift, StatelessHTTPServerTransport.swift, Server.swift,
CancellationTests.swift and HTTPServerTransportTests.swift.

Fork changes re-applied on top:
- modelcontextprotocol#258's Server.init description/websiteUrl/icons parameters
- modelcontextprotocol#221's MockTransport receiveCallCount / finishStream
- entry 1a's collision regression tests and their makeStatelessPOSTRequest
  authorization overload

Behavior: a cancelled original POST now ends as HTTP 200 with an empty
text/event-stream and no JSON-RPC body (previously -32002 from modelcontextprotocol#260).
Follow-up on when to use -32800: fork issue #41.

Full suite: 589 tests in 43 suites passed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@varun06

varun06 commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Hi @jstar0, thank you for this contribution. We're consolidating overlapping PRs.

#298 includes the same per-exchange isolation as this PR (fixing #254), plus the cancellation fixes for #255, so we'd like to land them together in one change. Thanks for the independent verification here.

We plan to close this PR once #298 is merged, so the fix isn't lost in between. If you think something here isn't covered by #298, please comment there or here and we'll make sure it's carried over.

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.

StatelessHTTPServerTransport: concurrent requests sharing a JSON-RPC id overwrite each other's response waiter (hang + continuation leak)

3 participants