AI & MCP

SSE and Streamable HTTP: how an MCP server talks

Streamable HTTP replaced HTTP+SSE: one endpoint takes JSON-RPC over POST and may answer with a stream. Here is what this server really does.

Key points

  • HTTP+SSE used two endpoints; Streamable HTTP uses one and answers with JSON or SSE.
  • Stdio remains the choice for a local server started by the client on the same machine.
  • The EneUI endpoint always answers application/json and never emits Mcp-Session-Id.

Streamable HTTP replaced the HTTP+SSE transport: a single endpoint takes JSON-RPC messages over POST and may answer with an SSE stream. Stdio remains the choice for a local server started by the client.

Three transports, two generations

The first revision, 2024-11-05, used two endpoints: a GET for the stream and a POST for the messages. Streamable HTTP, from the 2025-03-26 revision, folds them into one path.

TransportEndpointWhenStatus
stdiostdin and stdoutlocal processstandard
HTTP+SSEGET /sse and POST /messages2024 clientsdeprecated
Streamable HTTPPOST /mcpremote serverstandard

What the EneUI endpoint answers

curl -s -i https://ui.eneawork.it/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"ping"}'

The header comes back as application/json, not text/event-stream, and Mcp-Session-Id is absent. There is one response: {"jsonrpc":"2.0","id":1,"result":{}}.

One request, one response

The server keeps no state: every POST is independent, and a notification without an id closes with 202 and an empty body. For a read-only catalogue that is enough, and it crosses any proxy without surprises.

The price of that choice

Without an open stream the server cannot start a message: no sampling, no notice about an updated resource, no resume after a disconnect. All of them are optional features, but whoever wants them must accept SSE.

The same path for browsers and agents

An Accept: text/html drops the GET into the /mcp page; an Accept: */* gets the JSON document with name, version, transport and the 9 tools. Negotiation happens in the header, not on a separate path.

The EneUI catalogue

Answers come from the same origin as the pages: heroes, icons and a real block such as SaaS Pricing with Billing Toggle, 1 file and 4 registry dependencies, checked on 1 November 2026.

The limitation

The endpoint implements neither Mcp-Session-Id nor the streaming GET, so it cannot resume an interrupted response with Last-Event-ID. A client that demands a stream must accept JSON as the complete response.

Where to continue

The OAuth flow that sits beside this transport is in OAuth for MCP servers; the dispatcher is in how to build an MCP server; the comparison with REST APIs is in MCP vs REST. The protocol definition is in what MCP is.

Frequently asked questions

Is Streamable HTTP mandatory in MCP?

No. A server can use stdio alone. Streamable HTTP is the transport for a remote endpoint that several clients reach.

Can I still use HTTP+SSE?

Technically yes, but the transport has been deprecated since the 2025-03-26 revision. A new client is better off speaking Streamable HTTP or stdio.

Why does EneUI answer with JSON instead of SSE?

Because each request produces one response and JSON crosses every proxy. A stream would only help for server-initiated messages, which do not exist here.

What is Mcp-Session-Id?

It is the header a server uses to give the client a session identifier. The EneUI endpoint never emits it: every POST is independent.

Can a browser open /mcp?

Yes, but it receives the HTML page: with Accept text/html the GET falls into the application, while a client asking for JSON gets the discovery document.