---
title: "Streaming JSON-RPC over HTTP: The Evolution of MCP Transports"
slug: "streaming-json-rpc-over-http-the-evolution-of-mcp-transports"
category: "Inference & Infrastructure"
author: "Daniel Jaffa"
date: "2026-10-29T01:34:53.583Z"
tags: ["MCP", "HTTP", "JSON-RPC", "Streaming", "Infrastructure"]
summary: "A deep architectural comparison of legacy SSE and modern Streamable HTTP MCP transports, including session management, proxy buffering, and Express implementations."
---

# Streaming JSON-RPC over HTTP: The Evolution of MCP Transports

The Model Context Protocol specification initially supported two primary transports: local standard input/output (`stdio`) for command-line tools and desktop clients, and Server-Sent Events paired with HTTP POST (`sse` + `/messages`) for networked services.

While functional, legacy SSE suffered from operational friction in distributed cloud topologies: dual-endpoint session handshake complexity, middlebox proxy timeouts, and disjointed request-response correlation. The modern **Streamable HTTP Transport (MCP 2025-03-26+ Spec)** solves these challenges by unifying bidirectional JSON-RPC streaming into a single HTTP route.

```mermaid
sequenceDiagram
    participant Client as MCP Client (Cursor / Claude / Agent)
    participant Edge as Edge Proxy (Cloudflare / Envoy)
    participant Server as MCP Streamable Server (/mcp)

    Client->>Server: POST /mcp (initialize request)
    Note over Client,Server: Accept: application/json, text/event-stream
    Server->>Client: 200 OK (mcp-session-id: uuid-8821a)
    Client->>Server: POST /mcp (callTool: search_articles)
    Note over Client,Server: Headers: mcp-session-id: uuid-8821a
    Server-->>Client: Streamed chunk 1: Tool execution started...
    Server-->>Client: Streamed chunk 2: JSON results payload
    Server->>Client: 200 Finished (Keep-Alive)
```

---

## 1. Comparing Legacy SSE vs Streamable HTTP

| Metric / Capability | Legacy SSE (`/sse` + `/messages`) | Modern Streamable HTTP (`/mcp`) |
| :--- | :--- | :--- |
| **Endpoint Topology** | Two endpoints (`/sse` for down, `/messages` for up) | Single unified endpoint (`/mcp`) |
| **HTTP Methods** | `GET` for event stream, `POST` for messages | Standard `POST`, `GET`, `DELETE` |
| **Session Identification** | URL query parameter (`?sessionId=...`) | Standard Header (`mcp-session-id: ...`) |
| **Streaming Support** | Unidirectional server-to-client push | Bidirectional chunked transfer streaming |
| **Proxy Compatibility** | Prone to buffering in CDNs | Fully compatible with standard HTTP/2 and HTTP/3 |

---

## 2. Implementing the Streamable Transport in TypeScript

Using `@modelcontextprotocol/sdk/server/streamableHttp.js`, an Express server mounts the transport with session tracking:

```typescript
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { randomUUID } from 'node:crypto';

const streamableTransports = new Map<string, StreamableHTTPServerTransport>();

app.all('/mcp', async (req, res) => {
  const sessionId = req.headers['mcp-session-id'] as string | undefined;
  let transport = sessionId ? streamableTransports.get(sessionId) : undefined;

  if (!transport) {
    transport = new StreamableHTTPServerTransport({
      sessionIdGenerator: () => randomUUID(),
      onsessioninitialized: (id) => {
        streamableTransports.set(id, transport!);
      },
      onsessionclosed: (id) => {
        streamableTransports.delete(id);
      }
    });

    await mcpServer.connect(transport);
  }

  await transport.handleRequest(req, res, req.body);
});
```

---

## 3. Operational Best Practices

1. **Always Set `mcp-session-id` on Outgoing Requests**: In stateless multi-instance clusters, configure ingress controllers (e.g. Traefik, HAProxy, Cloudflare Workers) to pin sticky sessions based on the `mcp-session-id` header.
2. **Graceful Session Invalidation**: Clients should send an explicit `DELETE /mcp` request with `mcp-session-id` upon task completion to purge cached server resources.
3. **Handle Edge Buffering**: Configure reverse proxies to disable response buffering (`X-Accel-Buffering: no` in NGINX) to enable instant tool progress streaming.