• Uncategorised

From SSE to Streamable HTTP: Understanding MCP Transport the Right Way

If you’ve ever looked at MCP server code and thought:

“Why does this need both GET and POST?
Why does the server reply on SSE instead of POST?
Why is SSE now deprecated but examples still exist?”

— you’re not alone. MCP transport is not obvious, and most explanations online are incomplete.

This article breaks down MCP’s evolution from SSE Transport to Streamable HTTP, explaining exactly how and why it works.


The Core Problem MCP Solves

MCP is not REST.

A tool call in MCP may:

  • take minutes
  • stream tokens
  • pause for human approval
  • emit logs anytime
  • resume later

This means:

The server must push messages whenever it wants, not only when the client asks.

Classic HTTP cannot do that.


Phase 1 – MCP over SSE (Deprecated Model)

Architecture

GET /mcp        → server → client (SSE stream)
POST /messages  → client → server (JSON-RPC)

Two physical connections.


Why GET is used

Client sends:

GET /mcp
Accept: text/event-stream

Server responds with:

Content-Type: text/event-stream

Now the connection stays open forever.

But HTTP has a hard rule:

Once GET is opened, the client can never send data on that connection again.

The connection becomes:

Client  ───▶  Server   (headers only, once)
Client  ◀───  Server   (stream forever)

Why POST is mandatory

Because the client still must say:

  • call tool
  • cancel job
  • approve human step

So MCP adds a second gate:

POST /messages?sessionId=abc123

What SSEServerTransport('/messages', res) really does

That one line:

const transport = new SSEServerTransport('/messages', res);

Immediately sends this over SSE:

event: initialize
data: { "sessionId":"abc123", "postEndpoint":"/messages" }

The client now knows:

  • its sessionId
  • where to POST commands

Where replies go

Never on POST. Always on SSE.

POST /messages → HTTP 200 OK (empty)
SSE stream     → tool results, logs, tokens, progress

POST only carries questions.
SSE only carries answers.

This pair emulates WebSocket using plain HTTP.


Why SSE Transport Was Deprecated

This model requires:

  • two open sockets per client
  • session maps in memory
  • reconnection logic
  • idle pipes consuming infra

It scales badly.


Phase 2 – Streamable HTTP (Modern MCP)

MCP kept streaming — but removed permanent SSE pipes.

Now MCP uses:

POST /mcp  ←→ streaming HTTP response

How Streamable HTTP Works

Client:

POST /mcp

Server:

HTTP/1.1 200 OK
Transfer-Encoding: chunked

Then the server keeps writing:

{"progress":10}
{"log":"fetching data"}
{"token":"hello"}
{"final":"done"}

The POST connection itself becomes the temporary stream.

No GET. No sessionId. No idle pipes.


Old vs New Model

FeatureSSE TransportStreamable HTTP
Permanent connectionYes (GET)No
Client uplinkPOST /messagesPOST /mcp
Server streamingSSE onlyPOST response
Session storeRequiredNot required
Infra friendly
Recommended❌ Deprecated✅ Default

Why SSE Examples Still Exist

Because:

  • Browsers love SSE
  • Legacy MCP clients still exist
  • It is conceptually simple
  • Good for learning MCP internals

But it is no longer the future.


Final Mental Model

Old MCP

“Open a pipe and talk whenever.”

New MCP

“Every task owns its own stream.
When the task ends, the connection ends.”

You may also like...