Skip to content

fix(client): validate Content-Type on GET/resume SSE responses#2520

Draft
lntutor wants to merge 1 commit into
modelcontextprotocol:mainfrom
lntutor:fix/validate-get-sse-content-type
Draft

fix(client): validate Content-Type on GET/resume SSE responses#2520
lntutor wants to merge 1 commit into
modelcontextprotocol:mainfrom
lntutor:fix/validate-get-sse-content-type

Conversation

@lntutor

@lntutor lntutor commented Jul 19, 2026

Copy link
Copy Markdown

Opened as a draft only because this repo limits non-collaborators to one open PR at a time (#2519 is my other open PR, and drafts are exempt). The change is complete, tested, and review-ready — happy to convert to ready-for-review whenever suits the maintainers.

What's broken

StreamableHTTPClientTransport's standalone-GET and resume paths (_startOrAuthSse) pipe the response body into the SSE parser after only checking response.ok and handling 405 — with no Content-Type check. The POST path already validates this via mediaTypeEssence and throws ClientHttpUnexpectedContent.

Observable failure

A proxy, captive portal, or misconfigured server answering the GET with 200 and a non-SSE body (e.g. text/html) is silently swallowed: EventSourceParserStream yields no events, onerror never fires, and the caller (initial listen, resumeStream(), or a resumed request) believes it has a live stream. Per the Streamable HTTP spec the server "MUST either return Content-Type: text/event-stream ... or else return HTTP 405".

Fix

Apply the same mediaTypeEssence check on the GET path before _handleSseStream, throwing SdkError(ClientHttpUnexpectedContent) when the media type is not text/event-stream. The throw flows through the existing catch, surfacing via onerror (and rejecting the resume promise) instead of being lost.

Tests

New test in packages/client/test/client/streamableHttp.test.ts: a GET returning 200 text/html now makes _startOrAuthSse reject with ClientHttpUnexpectedContent (before the fix it resolved silently). Full @modelcontextprotocol/client suite passes (749 tests), typecheck and lint clean. Changeset included.

🤖 Generated with Claude Code

The standalone-GET and resume paths (_startOrAuthSse) piped the response
body into the SSE parser without checking Content-Type, while the POST
path already validates it via mediaTypeEssence and throws
ClientHttpUnexpectedContent. A proxy/captive-portal/misconfigured server
answering the GET with 200 and a non-SSE body (e.g. text/html) was
therefore silently swallowed: the parser yielded no events, onerror never
fired, and the caller believed it had a live stream.

Apply the same content-type check on the GET path and throw
ClientHttpUnexpectedContent when the media type is not text/event-stream,
matching the Streamable HTTP spec (GET returns text/event-stream or 405).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N6RtoHuxrDqTUo9Mw9h4Cv
@changeset-bot

changeset-bot Bot commented Jul 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8fc12d4

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

This PR includes changesets to release 6 packages
Name Type
@modelcontextprotocol/client Patch
@modelcontextprotocol/core Patch
@modelcontextprotocol/server Patch
@modelcontextprotocol/server-legacy Patch
@modelcontextprotocol/codemod 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 Jul 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

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

@modelcontextprotocol/codemod

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

@modelcontextprotocol/core

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

@modelcontextprotocol/server

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

@modelcontextprotocol/server-legacy

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

@modelcontextprotocol/express

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

@modelcontextprotocol/fastify

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

@modelcontextprotocol/hono

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

@modelcontextprotocol/node

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

commit: 8fc12d4

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