SSE or Streamable HTTP transport implemented correctly if offered
Why it matters
Servers offering HTTP/SSE transport must produce strictly formatted SSE events with data: prefixes and proper Content-Type: text/event-stream. Omitting the data: prefix causes clients to receive unparseable chunks. Missing CORS headers (CWE-942) block browser-based MCP clients entirely via same-origin policy. Broken session ID management means concurrent clients corrupt each other's message streams. These are protocol-level failures that make the HTTP transport path non-functional for any client not on localhost.
Severity rationale
Medium because SSE format errors and missing CORS headers break HTTP transport for specific client types without affecting stdio-connected clients.
Remediation
Use the SDK's SSEServerTransport or StreamableHTTPServerTransport rather than implementing SSE manually.
// src/transport/http.ts
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'
import { randomUUID } from 'crypto'
app.post('/mcp', async (req, res) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
})
await server.connect(transport)
await transport.handleRequest(req, res)
})
If you need custom CORS for browser clients, add Access-Control-Allow-Origin before the SSE response headers. Never implement the data: prefix manually — use the SDK transport.
Detection
-
ID:
http-transport -
Severity:
medium -
What to look for: Count all HTTP/SSE transport implementations. Enumerate whether the server supports Streamable HTTP with proper SSE event formatting. If the server offers HTTP-based transport (SSE or Streamable HTTP), check that the SSE endpoint sends proper
event: messageanddata:fields. Check that the HTTP endpoint handles POST requests for client-to-server messages and GET/SSE for server-to-client messages. Check CORS headers if the server is meant to be accessed from browser-based clients. Check that the session ID mechanism works correctly for multi-client scenarios. -
Pass criteria: SSE events follow the standard format. HTTP endpoints correctly handle the request/response flow. CORS is configured if needed. Session management works for concurrent clients. HTTP transport must support SSE with proper
Content-Type: text/event-streamheaders and handle at least 10 concurrent connections. -
Fail criteria: SSE format is incorrect (missing event type or data prefix), HTTP endpoint doesn't handle POST correctly, missing CORS headers for browser clients, or session handling breaks with multiple clients.
-
Skip (N/A) when: The server only uses stdio transport (no HTTP/SSE). All checks skip when no MCP server is detected.
-
Cross-reference: For stdio transport, see
stdio-transport. -
Detail on fail:
"SSE endpoint sends raw JSON without 'data: ' prefix — clients cannot parse the stream"or"HTTP transport has no CORS headers — browser-based MCP clients will be blocked by same-origin policy" -
Remediation: If offering HTTP transport, follow the SSE and Streamable HTTP specs:
// src/transport/http.ts — Streamable HTTP transport import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"// TypeScript SDK — SSE transport import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js' app.get('/sse', async (req, res) => { const transport = new SSEServerTransport('/message', res) await server.connect(transport) }) app.post('/message', async (req, res) => { await transport.handlePostMessage(req, res) }) // Streamable HTTP transport import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js' app.post('/mcp', async (req, res) => { const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID() }) await server.connect(transport) await transport.handleRequest(req, res) })
External references
- cwe · CWE-942 — Permissive Cross-domain Policy with Untrusted Domains (missing CORS headers)
- external · mcp-spec-http-transport — MCP Specification — Streamable HTTP and SSE transport
Taxons
History
- 2026-04-18·v1.0.0·Initial import from mcp-server·automated