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.
Medium because SSE format errors and missing CORS headers break HTTP transport for specific client types without affecting stdio-connected clients.
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.
ID: mcp-server.transport-protocol.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: message and data: 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-stream headers 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)
})