Summary
WebStandardStreamableHTTPServerTransport validates the Accept header with raw substring checks. As a result, invalid media types such as application/jsonx and text/event-stream-bogus satisfy the transport requirements and are accepted as though the client had listed application/json and text/event-stream.
This affects both POST negotiation and the GET SSE check.
Reproduction
On current main (24be4040), send an initialize request with:
Accept: application/jsonx, text/event-stream-bogus
Content-Type: application/json
Equivalent unit-level reproduction:
const request = new Request('http://localhost/mcp', {
method: 'POST',
headers: {
Accept: 'application/jsonx, text/event-stream-bogus',
'Content-Type': 'application/json',
},
body: JSON.stringify(initializeRequest),
});
const response = await transport.handleRequest(request);
console.log(response.status); // currently 200; expected 406
For GET, Accept: text/event-stream-bogus likewise passes the current check instead of returning 406.
Root cause
packages/server/src/server/streamableHttp.ts currently uses:
acceptHeader?.includes('text/event-stream')
acceptHeader?.includes('application/json')
Those checks match substrings inside other type/subtype tokens and parameters; they do not parse the comma-separated media ranges.
This is the same class of bug recently fixed for Content-Type in #2441 / #2444, where the server switched from substring matching to parsed media-type comparison. The Accept checks still use the older substring behavior.
Expected behavior
The transport should require the exact concrete media types named by the MCP Streamable HTTP requirements:
- GET:
text/event-stream
- POST: both
application/json and text/event-stream
Parameters and case variants should remain valid, for example:
Accept: Application/JSON; q=0.9, text/event-stream; charset=utf-8
Values that only contain the required strings as substrings should return 406.
Suggested fix
Parse the comma-separated Accept values and compare each media-type essence (type/subtype, case-insensitive, without parameters) to the required concrete values. Add regression coverage for POST and GET substring false positives plus valid parameterized media types.
Summary
WebStandardStreamableHTTPServerTransportvalidates theAcceptheader with raw substring checks. As a result, invalid media types such asapplication/jsonxandtext/event-stream-bogussatisfy the transport requirements and are accepted as though the client had listedapplication/jsonandtext/event-stream.This affects both POST negotiation and the GET SSE check.
Reproduction
On current
main(24be4040), send an initialize request with:Equivalent unit-level reproduction:
For GET,
Accept: text/event-stream-boguslikewise passes the current check instead of returning 406.Root cause
packages/server/src/server/streamableHttp.tscurrently uses:Those checks match substrings inside other type/subtype tokens and parameters; they do not parse the comma-separated media ranges.
This is the same class of bug recently fixed for
Content-Typein #2441 / #2444, where the server switched from substring matching to parsed media-type comparison. TheAcceptchecks still use the older substring behavior.Expected behavior
The transport should require the exact concrete media types named by the MCP Streamable HTTP requirements:
text/event-streamapplication/jsonandtext/event-streamParameters and case variants should remain valid, for example:
Accept: Application/JSON; q=0.9, text/event-stream; charset=utf-8Values that only contain the required strings as substrings should return 406.
Suggested fix
Parse the comma-separated
Acceptvalues and compare each media-type essence (type/subtype, case-insensitive, without parameters) to the required concrete values. Add regression coverage for POST and GET substring false positives plus valid parameterized media types.