Stdio vs Streamable HTTP: Choosing a Transport

12 min read Module 3 of 10 Topic 7 of 30

What you'll learn

  • Explain when stdio transport is appropriate versus when a networked transport is required
  • Describe why Streamable HTTP replaced the original HTTP+SSE transport in the MCP spec
  • Configure a FastMCP server to run over Streamable HTTP for remote, multi-client access
  • Reason about session resumability when a Streamable HTTP connection drops mid-stream
Building this at your company? For enterprise and company teams taking this to production: book a 30-minute session with our AI engineers for architecture guidance, code review, and a rollout plan for your use case.
Book a Team Session

MCP defines two transport families: stdio, where the client launches the server as a local subprocess and communicates over its standard input and output streams, and networked transports, where the server runs as an independent, addressable service. The 2025 spec revision consolidated the networked story around Streamable HTTP, replacing the earlier two-endpoint HTTP+SSE design, and added resumability semantics that matter once you are running long-lived, streamed tool calls in production.

Stdio: Local, Single-Client, Zero Infrastructure

Stdio is the right choice for local development and for clients that launch a server as a subprocess on demand, Claude Desktop and Claude Code both work this way for locally-installed servers. There is no network exposure, no authentication layer required, and no infrastructure to run, the tradeoff is that it only serves one client per process and cannot be shared across a network.

if __name__ == "__main__":
    mcp.run(transport="stdio")  # Client spawns this as a subprocess

Stdio’s lifecycle is tied directly to the parent process: when the client exits (or kills the subprocess), the server terminates immediately with it, there is no separate deployment or health-check story to reason about, which is exactly why it fits local development and single-user desktop tools so well and exactly why it does not fit a service meant to outlive any single client connection.

Streamable HTTP: Networked, Multi-Client, Production-Ready

For any server that needs to be reachable by remote agents, or by more than one client at a time, Streamable HTTP is the correct transport. It runs as a standard HTTP server: a single endpoint accepts JSON-RPC requests via POST, and the server can optionally upgrade the response to a stream (for long-running tool calls or server-initiated notifications) using standard HTTP streaming rather than a separate SSE connection.

# Serve the same FastMCP server over Streamable HTTP instead of stdio
app = mcp.streamable_http_app()

# Run with any ASGI server: uvicorn server:app --host 0.0.0.0 --port 8080
flowchart LR
    subgraph stdio["Stdio Transport"]
        C1["Client process"] <-->|"stdin/stdout\n(subprocess pipe)"| S1["MCP Server\n(child process)"]
    end

    subgraph http["Streamable HTTP Transport"]
        C2["Client A"] -->|"POST /mcp"| S2["MCP Server\n(HTTP service)"]
        C3["Client B"] -->|"POST /mcp"| S2
        C4["Client C"] -->|"POST /mcp"| S2
        S2 -.->|"streamed response\n(optional)"| C2
    end

    style C1 fill:#EEF0F7,stroke:#6366F1,color:#0F172A
    style S1 fill:#f0fdf9,stroke:#0D9488,color:#0F172A
    style C2 fill:#EEF0F7,stroke:#6366F1,color:#0F172A
    style C3 fill:#EEF0F7,stroke:#6366F1,color:#0F172A
    style C4 fill:#EEF0F7,stroke:#6366F1,color:#0F172A
    style S2 fill:#f0fdf9,stroke:#0D9488,color:#0F172A

Why Streamable HTTP Replaced HTTP+SSE

The original networked transport required the client to open a POST connection for outgoing requests and a separate, long-lived SSE (Server-Sent Events) connection to receive server-to-client messages. Two connections per client complicates load balancer configuration (both connections must reach the same backend instance for stateful servers), makes horizontal scaling harder, and breaks more easily with corporate proxies that mishandle long-lived SSE streams. Streamable HTTP consolidates both directions onto a single HTTP endpoint per request, with streaming available as an optional response mode, so it behaves like any other HTTP API from the infrastructure’s point of view and drops in behind standard load balancers, API gateways, and reverse proxies without special casing.

# A production Streamable HTTP server behind a reverse proxy needs no special
# session-affinity configuration when state lives in shared storage (Lesson 10),
# unlike the old two-connection SSE model which often required sticky sessions.
app = mcp.streamable_http_app()

Resumability: Recovering from a Dropped Connection

A practical concern that only surfaces once tool calls genuinely stream (large search results arriving incrementally, or the progress notifications built in Lesson 27) is what happens when the underlying HTTP connection drops partway through, a mobile client losing signal, a load balancer terminating an idle connection, a brief network blip. Streamable HTTP’s design accounts for this directly: the server tags streamed messages with an identifier, and a reconnecting client can present the last identifier it successfully processed, letting the server replay only what was missed rather than forcing the entire tool call to restart from the beginning.

sequenceDiagram
    participant Client
    participant Server as MCP Server

    Client->>Server: POST /mcp (tools/call, streaming response begins)
    Server-->>Client: event id=1: partial result chunk
    Server-->>Client: event id=2: partial result chunk
    Note over Client,Server: Connection drops before event id=3 arrives
    Client->>Server: Reconnect: POST /mcp with Last-Event-ID: 2
    Server-->>Client: event id=3: partial result chunk (replayed, not restarted)
    Server-->>Client: event id=4: final result

    style Client fill:#EEF0F7,stroke:#6366F1,color:#0F172A
    style Server fill:#f0fdf9,stroke:#0D9488,color:#0F172A

Implementing resumability correctly requires the server to buffer recently-sent events for some retention window (rather than discarding them the instant they are sent), a tradeoff between memory/storage cost and how long a client can be disconnected before a reconnect attempt fails and the operation must genuinely restart. For most enterprise deployments, a short retention window (tens of seconds, matched to realistic transient network blips rather than extended outages) is the right default, longer retention mainly matters for tools whose execution is itself expensive enough that restarting from zero is meaningfully worse than the storage cost of a longer buffer.

The next lesson takes this Streamable HTTP server and containerizes it for real deployment: Docker images, health checks, and running it on Kubernetes.

Knowledge Check

3 questions to test your understanding

1 A team wants to expose their internal knowledge-base MCP server to agents running in a shared Kubernetes cluster, potentially hundreds of concurrent agent sessions. Which transport is appropriate?

2 Why did the MCP spec replace the original HTTP+SSE transport with Streamable HTTP?

3 A client's Streamable HTTP connection to an MCP server drops mid-way through a long streaming tool response. What does the transport provide to avoid the client losing everything that had already streamed?

Go further with expert guidance

Ready to build production AI?
Talk to our R&D team.

These courses give you the foundation. Our embedded AI teams take you from prototype to production in 30–90 days, with your team, your codebase, your goals. Book a free strategy call to see how we can accelerate your AI initiative.

30 minutes · No obligation · Expert AI engineers, not sales reps

AI Architecture Review

Audit your current stack and identify high-impact improvements

Project Review

Get expert feedback on your AI implementation and codebase

Team Mentoring

Upskill your engineers with hands-on AI coaching sessions

AI Strategy

Define your AI roadmap, prioritization, and implementation plan