Fix streamable HTTP server-to-client MCP routing#1514
Merged
Conversation
🦋 Changeset detectedLatest commit: 9b21dd4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
9cbd152 to
df2c294
Compare
Streamable HTTP clients are not required to keep a standalone GET/SSE stream open. When a server emits a related server-to-client request or notification while handling an in-flight client request, prefer the originating POST response stream when no standalone stream is available. Track the active MCP request id during transport dispatch so SDK convenience calls such as Server.elicitInput() can still be delivered to clients that only hold the POST stream. If async context is unavailable, conservatively fall back only when there is exactly one active POST request id, and reject unroutable server-to-client requests instead of silently dropping them until the SDK timeout fires. Clean up completed POST request ids once the stream has received all final responses so fallback routing does not consider stale request state. Add regression coverage for McpAgent and SDK elicitation without standalone SSE, concurrent elicitation routing, request-scoped logging notifications over POST, context-loss fallback, completed request cleanup, and ambiguous request rejection. Co-authored-by: Cursor <cursoragent@cursor.com>
df2c294 to
9b21dd4
Compare
agents
@cloudflare/ai-chat
@cloudflare/codemode
hono-agents
@cloudflare/shell
@cloudflare/think
@cloudflare/voice
@cloudflare/worker-bundler
commit: |
Merged
threepointone
added a commit
that referenced
this pull request
May 15, 2026
Bring back the server-only MCP elicitation and raw transport examples that were lost during the full-stack example migration. Also document that focused MCP protocol examples may stay Wrangler-only, refresh the workspace lockfile for the restored mcp-server workspace, and add a patch changeset noting the PR #1514 routing revert. Co-authored-by: Cursor <cursoragent@cursor.com>
mattzcarey
pushed a commit
that referenced
this pull request
May 15, 2026
* Revert "Fix streamable HTTP server-to-client routing (#1514)" This reverts commit 0371a6f. * Restore minimal MCP server examples Bring back the server-only MCP elicitation and raw transport examples that were lost during the full-stack example migration. Also document that focused MCP protocol examples may stay Wrangler-only, refresh the workspace lockfile for the restored mcp-server workspace, and add a patch changeset noting the PR #1514 routing revert. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
Merged
cjol
pushed a commit
that referenced
this pull request
May 20, 2026
* Revert "Fix streamable HTTP server-to-client routing (#1514)" This reverts commit 0371a6f. * Restore minimal MCP server examples Bring back the server-only MCP elicitation and raw transport examples that were lost during the full-stack example migration. Also document that focused MCP protocol examples may stay Wrangler-only, refresh the workspace lockfile for the restored mcp-server workspace, and add a patch changeset noting the PR #1514 routing revert. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
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.
Summary
Fixes the Streamable HTTP routing failure described in #1510 for clients that do not keep a standalone GET/SSE stream open.
The MCP Streamable HTTP spec permits clients to rely on the originating POST response stream for related server-to-client messages. Previously,
StreamableHTTPServerTransport.send()treated server-to-client requests and notifications without an explicitrelatedRequestIdas standalone messages. If no connection had_standaloneSseset, those messages were silently dropped. For request/response flows such aselicitation/create, the caller then waited until the MCP SDK timeout and surfaced an opaque request timeout.This PR makes the transport route related server-to-client messages through POST when that is the only correct available channel.
What changed
AsyncLocalStoragewhile invoking the SDKonmessagehandler.send()is called with no explicitrelatedRequestIdand no standalone SSE stream exists:agents.Why this approach
The full semantic fix belongs upstream in
@modelcontextprotocol/sdk: SDK convenience APIs such asServer.elicitInput(),Server.createMessage(),Server.listRoots(), and notification helpers should automatically inheritrelatedRequestIdwhen called from inside a request handler.This transport-side fix is still needed so
agentsworks with spec-compliant Streamable HTTP clients today. It avoids unsafe “pick any POST stream” behavior by using the active request context when possible and falling back only when there is exactly one active POST request.Follow-up tracking issue for the SDK work: #1513.
Coverage
Added regression coverage for:
McpAgent.elicitInput()over the originating POST stream without standalone SSE.Server.elicitInput()over the originating POST stream without standalone SSE.Test plan
npm --workspace packages/agents run test:workers -- src/tests/mcp/elicitation.test.ts src/tests/mcp/transports/streamable-http.test.tsnpm --workspace packages/agents run test:workers -- src/tests/mcpBoth passed locally.
Known remaining limitations
_standaloneSsebut the outer HTTP SSE writer is already dead, the transport will still prefer the standalone connection. That lifecycle race is not fully observable fromsend().relatedRequestIdpropagation tracked in Track MCP SDK fix for implicit relatedRequestId on server-to-client requests #1513 is the correct fix for that case.Made with Cursor