Background
PR #5288 adds upstream JSON-RPC response validation for the streamable-HTTP transport. SSE is explicitly out of scope because the fix architecture (buffer full body → validate → rewrite or pass through) doesn't work for a streaming event format.
The gap
When an SSE-based MCP upstream sends a structurally invalid JSON-RPC frame (data: line without jsonrpc:"2.0", wrong id type, etc.), the transparent proxy forwards it unchanged. Clients receive the malformed event and may crash, misparse it, or treat it as a legitimate response.
Why it's harder than streamable-HTTP
For streamable-HTTP, ProcessResponse buffers the entire HTTP response body before any bytes reach the client, so it can atomically reject and rewrite to 502.
For SSE:
- The
200 OK and Content-Type: text/event-stream headers are committed before any events arrive. There is no way to rewrite the status code for a malformed event mid-stream.
- A fix needs a per-event streaming interceptor that wraps the SSE body reader, parses each
data: line as it arrives, validates the JSON-RPC content, and either synthesizes an error event or closes the stream cleanly on the first invalid frame.
- "Close the stream" is the strongest guarantee available — unlike streamable-HTTP, the proxy cannot return a structured 502 for an individual bad event.
Proposed scope
A follow-up to #5288 scoped to the SSE response processor:
- Wrap the SSE body reader with a per-event interceptor
- On an invalid
data: JSON-RPC frame: write a synthetic data: {"jsonrpc":"2.0","id":null,"error":{"code":-32000,"message":"Invalid upstream JSON-RPC frame"}}\n\n event and close the stream
- Leave non-
data: SSE lines (comments, event:, retry:) untouched
- The guarantee is weaker than streamable-HTTP (client receives the error event, not a 502) — document this explicitly
Context
Found via DAST adversarial testing (checkMalformedFraming in the enterprise DAST suite). The test currently documents this as a known finding for the SSE transport.
Background
PR #5288 adds upstream JSON-RPC response validation for the streamable-HTTP transport. SSE is explicitly out of scope because the fix architecture (buffer full body → validate → rewrite or pass through) doesn't work for a streaming event format.
The gap
When an SSE-based MCP upstream sends a structurally invalid JSON-RPC frame (
data:line withoutjsonrpc:"2.0", wrongidtype, etc.), the transparent proxy forwards it unchanged. Clients receive the malformed event and may crash, misparse it, or treat it as a legitimate response.Why it's harder than streamable-HTTP
For streamable-HTTP,
ProcessResponsebuffers the entire HTTP response body before any bytes reach the client, so it can atomically reject and rewrite to 502.For SSE:
200 OKandContent-Type: text/event-streamheaders are committed before any events arrive. There is no way to rewrite the status code for a malformed event mid-stream.data:line as it arrives, validates the JSON-RPC content, and either synthesizes an error event or closes the stream cleanly on the first invalid frame.Proposed scope
A follow-up to #5288 scoped to the SSE response processor:
data:JSON-RPC frame: write a syntheticdata: {"jsonrpc":"2.0","id":null,"error":{"code":-32000,"message":"Invalid upstream JSON-RPC frame"}}\n\nevent and close the streamdata:SSE lines (comments,event:,retry:) untouchedContext
Found via DAST adversarial testing (
checkMalformedFramingin the enterprise DAST suite). The test currently documents this as a known finding for the SSE transport.