Repository navigation
fix(sdk): serve a legacy SSE message post whatever its Accept header says - #957
Closed
frontegg-david wants to merge 1 commit into
Closed
frontegg-david wants to merge 1 commit into
frontegg-david wants to merge 1 commit into
Conversation
…says The HTTP+SSE transport (MCP 2024-11-05) answers a post to its message endpoint with 202 and puts the response on the SSE stream, so it asks nothing of the post's Accept header. The router applied Streamable HTTP's Accept rule to /message too: a post that listed neither application/json nor text/event-stream, such as the Accept: */* that Node's fetch sends for the official SDK's SSEClientTransport, decided as unknown and fell through to Express's 404 Cannot POST /mcp/message. decideIntent now skips the Accept check for the legacy /message channel only. A Streamable HTTP post to the entry path keeps its Accept rules, and MCP 2026-07-28 routing does not consult them.
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Comment |
Contributor
Performance Test ResultsStatus: ✅ All tests passed Summary
Total: 101 tests across 21 projects 📊 View full report in workflow run Generated at: 2026-10-25T19:35:13.210Z |
Contributor
Author
|
Superseded by #958, which targets |
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.
What and why
Problem
The Node server serves the legacy HTTP+SSE transport (MCP 2024-11-05) by default. With
http.entryPath: '/mcp',GET /mcp/sseopens the stream and names/mcp/message?sessionId=…as the endpoint. A post to that endpoint:Accept: application/jsonorAccept: text/event-streamgets202, and the answer arrives on the stream (correct);Accept: */*orAccept: application/*gets Express's404 Cannot POST /mcp/message.Accept: */*is what Node'sfetch(andcurl) sends when the client sets no Accept header, so the official SDK'sSSEClientTransport(@modelcontextprotocol/sdk1.32.x) cannot connect: its first post,initialize, gets the 404. The 2024-11-05 transport asks nothing of the Accept header on these posts.Reproduction
Cause
decideIntent(libs/sdk/src/common/utils/decide-request-intent.utils.ts) ran Streamable HTTP's Accept check on every POST,/messageincluded: a post that listed neitherapplication/jsonnortext/event-streamwas decidedunknownbefore the legacy/messagerule was reached. Forinitialize, thehttp:requestrouter hands anunknowndecision to the next middleware, which is Express's 404.A request with no Accept header at all was already routed, because the router tolerates a missing Accept. It only looked broken because
curlandfetchfill in*/*.Fix
decideIntentskips the Accept check for the legacy/messagechannel only. A post there is answered202with its response on the SSE stream, so any Accept value, or none, is routed to the legacy SSE handler, whatevertolerateMissingAcceptsays. Legacy SSE that is turned off is still refused, and a/messagepost without a session id is still not routed.Deliberately unchanged
*/*,application/*andtext/plainare still not served. Both media types listed still routes to Streamable HTTP, and the existing JSON and missing-Accept routing is unchanged. New tests pin this at the unit level and against a real server.tools/liston the entry path is still served sessionless.The change cherry-picks cleanly onto
release/1.10.x: the touched files are the same on both branches.User-facing change
<entryPath>/message?sessionId=…) accepts a post whatever its Accept header says.Accept: */*,application/*or another value that lists neitherapplication/jsonnortext/event-streamused to get404 Cannot POST <entryPath>/message. It now gets202 Accepted, with the response on the SSE stream. The official SDK'sSSEClientTransportnow connects with its default headers.'legacy'preset already promises compatibility with all MCP clients.Tests
New:
libs/sdk/src/common/utils/__tests__/decide-request-intent.accept.spec.ts: unit tests of the routing decision./messagepost routes to legacy SSE withapplication/json,text/event-stream, both,*/*,application/*,text/plainor no Accept, whether or not a missing Accept is tolerated. It is still refused when legacy SSE is off, and still not routed without a session id.libs/sdk/src/transport/__tests__/legacy-sse.message-accept.spec.ts: a real in-process server withhttp.entryPath: '/mcp', in the style ofverified-session.legacy-sse.spec.ts./mcp/message, which answers202plus the result on the stream for every Accept value above.tools/call) posted with*/*is served.ClientwithSSEClientTransportand default headers connects, lists tools and calls one.initializewith both media types gets200and a session; with*/*orapplication/*it still gets404.tools/listis served sessionless.apps/e2e/demo-e2e-standalone/e2e/legacy-sse-sdk-client.e2e.spec.ts: the official SDK'sSSEClientTransport, with default headers, against the spawned demo server atFRONTMCP_HTTP_ENTRY_PATH=/mcp. It connects, lists tools and callsparent-hello.Before the fix, the new specs failed the way the report describes. The legacy
*/*andapplication/*posts got404, the SDK client threwError POSTing to endpoint (HTTP 404), and the e2e failed the same way. All the Streamable HTTP and 2026-07-28 cases passed on the unmodified code.Run after the fix:
yarn nx test sdkyarn nx run-many -t lint,typecheck -p sdk demo-e2e-standaloneyarn nx test demo-e2e-standaloneyarn nx test demo-e2e-protocol-20260728yarn nx test demo-e2e-testing --testPathPatterns=hooks-and-sse@frontmcp/testingSSE client)node scripts/fix-unused-imports.mjs