Transport guideChecked for MCP 2026-07-28

MCP Transports: stdio, Streamable HTTP, and Legacy SSE

Compare stdio, Streamable HTTP, and legacy SSE. Match stdio-to-HTTP and SSE proxy directions to the client, server, and protocol versions you need.

On this page

Version verifiedChecked August 25, 2026 against MCP 2026-07-28. Review method.

The short version

  • The current specification defines two standard bindings: stdio for a client-launched process and Streamable HTTP for a remote endpoint.
  • Modern Streamable HTTP uses one POST endpoint and has no GET endpoint, MCP session ID, or resumable SSE stream.
  • SSE can carry one modern HTTP response; that does not make modern Streamable HTTP the deprecated HTTP+SSE transport.
  • Choose a transport by process boundary, then add explicit legacy negotiation only when your compatibility requirements demand it.

An MCP transport answers a narrow question: how does a JSON-RPC 2.0 message travel between a client and a server S001? It does not change what tools/call means, who controls a resource, or whether an operation is allowed. The 2026-07-28 transport overview calls a transport a binding and keeps protocol semantics consistent across bindings S057.

Connecting stdio to HTTP or legacy SSE? Use the proxy direction table to match the host-facing and server-facing sides before selecting a bridge.

For the roles on either side of that binding, see the MCP server architecture overview. The MCP protocol versions guide separates wire-version compatibility from transport compatibility. For why native function calling has no equivalent cross-application transport contract, see MCP vs function calling.

The detailed logical-role versus physical-connection guide explains why one client-to-server relationship does not imply one permanent TCP connection or one server process.

Transport is only one deployment dimension. For where the code, authority, secrets, operating burden, and failure domain should live, use the local vs remote MCP server decision guide. This page stays focused on wire mechanics and transport history.

The current specification has two standard bindings: stdio and Streamable HTTP. Confusion comes from the fact that MCP has used three different HTTP shapes since launch, and all three use or can use Server-Sent Events. Treat the version as part of the transport name.

For new work, use stdio when the client launches a local server and 2026-07-28 Streamable HTTP when clients connect to a remote service. HTTP+SSE is legacy and deprecated.

The two standard transports solve different process boundaries

The two standard transports solve different process boundaries comparison table
QuestionstdioStreamable HTTP (2026-07-28)
Who starts the server?The MCP client launches a subprocessAn operator deploys an independent HTTP service
Where does it usually run?Same machine and user context as the hostLocal network, cloud, edge, or another machine
Message framingOne newline-delimited JSON-RPC message per lineOne JSON-RPC message per HTTP POST
Response deliveryShared stdout byte streamJSON body or request-scoped SSE stream
AuthenticationUsually environment/configuration outside MCP HTTP authMCP HTTP authorization or another negotiated scheme
Cancellationnotifications/cancelled with request IDClose that request’s SSE response stream
Protocol sessionNoneNone

The choice is normally architectural, not a performance contest. If the host owns the process lifecycle and needs a private local integration, stdio is the natural binding. If many clients need a URL, independent deployment, load balancing, or normal HTTP controls, use Streamable HTTP.

Custom transports are allowed, but they create an interoperability obligation. A custom binding still has to preserve MCP’s request, response, notification, metadata, cancellation, and termination rules. Most products benefit more from supporting the two standard bindings correctly than from inventing a third.

stdio is a strict JSON-RPC subprocess channel

In the current stdio binding, the client launches the server process, writes requests and notifications to its standard input, and reads responses and notifications from its standard output S057. Each UTF-8 JSON-RPC message occupies exactly one line and cannot contain an embedded newline.

Host / MCP client                 Local MCP server
       |--- stdin: request ---------->|
       |<-- stdout: response ----------|
       |<-- stderr: diagnostic log ----|

The separation between stdout and stderr is a protocol requirement with a practical consequence: printing a friendly startup banner to stdout corrupts the MCP stream. Application logs, debug output, stack traces, and progress intended for a human terminal go to stderr. Only valid MCP messages go to stdout.

All requests share one bidirectional channel, so JSON-RPC IDs correlate responses with requests. Request-scoped notifications such as progress also share it. Notifications from subscriptions/listen include io.modelcontextprotocol/subscriptionId so a client can demultiplex multiple active subscriptions.

Closing the server’s input is the portable graceful-shutdown signal. A server should exit when stdin reaches end-of-file. If it crashes, the client should restart it; in-flight work is lost, and subscriptions must be opened again. Because current MCP is stateless, a restarted process does not need to reconstruct hidden protocol-session data.

stdio is local, but it is not automatically low risk. The subprocess inherits some combination of the user’s filesystem access, environment variables, network reach, and working directory. Use absolute executable paths where appropriate, constrain environment variables, avoid placing secrets in command arguments, and do not assume a downloaded package is safe merely because installation was easy.

Modern Streamable HTTP is one POST endpoint

The current Streamable HTTP binding uses a single MCP endpoint, conventionally /mcp, that accepts POST S057. Every client message is a new HTTP request. For a JSON-RPC request, the server returns either:

  • application/json with one response object; or
  • text/event-stream with notifications related to that request followed by the final response.
Client                                      https://api.example/mcp
  |-- POST tools/call ------------------------------>|
  |   Accept: application/json, text/event-stream    |
  |   MCP-Protocol-Version: 2026-07-28               |
  |   Mcp-Method: tools/call                         |
  |   Mcp-Name: search                               |
  |<-- JSON response OR request-scoped SSE stream ---|

The version appears both in the MCP-Protocol-Version header and in the request body’s _meta; those values must agree. Mcp-Method mirrors the JSON-RPC method on all requests. Mcp-Name also mirrors the tool name, prompt name, or resource URI for tools/call, prompts/get, and resources/read. A server that processes the body must reject missing or inconsistent mirrored headers. This lets gateways route, meter, or authorize using headers without creating two sources of truth.

The endpoint has no modern GET or DELETE behavior. There is no Mcp-Session-Id. The server does not send independent JSON-RPC requests on an SSE response. When it needs client-provided input, it returns an MRTR InputRequiredResult, and the client retries the original method. Long-lived change events use a POSTed subscriptions/listen request whose response remains open.

Modern streams are not resumable with Last-Event-ID. If a request-scoped stream breaks, the in-flight request is lost and any retry uses a new request ID. Durable work belongs in an explicit mechanism such as the Tasks extension, not in hidden transport replay.

SSE is a response format, not one timeless MCP transport

“Does MCP use SSE?” has two valid answers: modern Streamable HTTP may use SSE for a response, while the transport historically named HTTP+SSE is deprecated. The same technology appears in different protocol designs.

In modern MCP, an SSE stream is scoped to the POST request that created it. It can carry progress or log notifications related to that request, then its final result. A subscriptions/listen response may stay open for opted-in change notifications. Servers should disable reverse-proxy buffering and may send SSE comment lines as keep-alives, but Last-Event-ID replay is not supported.

The 2024 HTTP+SSE transport used a standalone GET stream plus a separate POST endpoint. Streamable HTTP revisions from March 2025 through November 2025 used one endpoint but could add sessions, a standalone GET stream, DELETE, and resumability. Neither is the 2026 wire model.

Calling all three designs “SSE transport” erases the exact differences that determine whether a client can connect. When documenting an endpoint, publish the protocol revision and say one of: 2024-11-05 HTTP+SSE, 2025-era Streamable HTTP, or 2026-07-28 Streamable HTTP.

MCP has passed through three HTTP eras

Timeline showing launch-era HTTP+SSE in 2024, session-based Streamable HTTP in 2025, and stateless Streamable HTTP in 2026 with request-scoped SSE and subscriptions.
MCP replaced the original HTTP+SSE transport, but current Streamable HTTP can still use SSE for request-scoped or subscribed streams. Source and protocol version are printed in the diagram.
MCP has passed through three HTTP eras comparison table
Protocol eraEndpoint shapeSession behaviorServer-to-client pathCurrent status
2024-11-05GET SSE endpoint plus a separate message POST endpointConnection-oriented lifecycle after initializeServer messages arrive on the dedicated SSE streamHTTP+SSE deprecated
2025-03-26 to 2025-11-25Single MCP endpoint supporting POST and optional GET/DELETEServer could mint Mcp-Session-Id; clients could terminate it with DELETERequest responses plus standalone GET stream; Last-Event-ID could resumeLegacy compatibility behavior
2026-07-28One POST endpointNo protocol sessionJSON or request-scoped SSE; subscriptions/listen for opted-in changesCurrent modern core

The original 2024-11-05 transport specification documents the two-endpoint design S027. Streamable HTTP arrived in the 2025-03-26 revision S038, and the 2025-11-25 specification is the last revision in that handshake-and-session era S048. The 2026-07-28 changelog removed sessions, GET, resumability, and independent server requests S055. HTTP+SSE’s deprecated status is governed by the project’s feature lifecycle policy S065.

These are protocol versions, not merely SDK generations. An SDK may support more than one era at once. Conversely, seeing a class named StreamableHTTP does not prove that its wire behavior is current. Verify which MCP versions the deployed client and server actually negotiate.

Compatibility should be deliberate and observable

A modern-only implementation can send self-contained requests and reject unsupported versions. A product that must connect to older peers needs an explicit dual-era path. The versioning specification describes negotiation and fallback S056.

For stdio, a dual-era client should probe with server/discover. A successful discovery result identifies a modern server. A recognized modern version error means “select another advertised modern version,” not “fall back.” An implementation-defined error, unknown-method behavior, or reasonable timeout can trigger the legacy initialize path.

For HTTP, a client may attempt a modern request first. Before falling back, it must inspect a 400 response body because modern servers use structured errors for unsupported versions, missing capabilities, and header mismatches. An empty or unrecognized response can indicate a handshake-era server. A separate fallback from Streamable HTTP to the oldest HTTP+SSE endpoint shape should occur only after the modern POST fails with the conditions defined by the binding.

Make the selected era visible in logs and diagnostics. Useful facts include the requested version, server-supported versions, endpoint URL, status code, content type, and whether a fallback occurred. “Connection failed” is not enough when the likely defect is an era mismatch.

Bridge recipe: choose one direction, then prove the boundary

A bridge is useful when the host and server support different connection shapes. It is not a third MCP role and should not silently change authorization or protocol semantics. Start by writing the direction in one sentence.

Which proxy direction do you need?

Names such as “stdio to SSE” are ambiguous about which end is the client. Identify what the host accepts and what the server exposes before selecting an adapter.

Which proxy direction do you need? comparison table
Your actual connectionWhat the adapter must doCompatibility check
Remote HTTP client → local stdio serverAccept HTTP MCP requests and launch or connect to the stdio process.Confirm the HTTP revision the client uses; “HTTP” alone does not distinguish legacy SSE from Streamable HTTP.
Legacy HTTP+SSE client → local stdio serverProvide the legacy SSE connection and message endpoint while speaking stdio downstream.Require explicit legacy HTTP+SSE support. A modern Streamable HTTP endpoint is not the same endpoint shape.
Stdio-only host → remote HTTP+SSE serverAppear as a stdio server to the host and act as a legacy HTTP+SSE client downstream.Require support for the remote server’s legacy lifecycle, endpoints, and authentication.
Stdio-only host → remote Streamable HTTP serverAppear as a stdio server locally and send HTTP MCP requests downstream.Match the supported protocol revision on both sides, including any required lifecycle translation.

Changing message framing does not by itself translate protocol revisions. If the two ends use different eras, require documented version translation as well as transport support. The transport overview separates bindings from semantics; the HTTP-era table above explains why “SSE support” is insufficient.

If connection succeeds but calls fail: record both endpoint versions, then test tool listing and one harmless call. A missing legacy endpoint suggests the wrong HTTP shape; non-JSON output on stdio suggests logs on stdout; an authorization failure needs an identity or permission check. Do not fix these by disabling authentication or silently swallowing protocol errors.

Expose a local stdio server to remote clients

The bridge acts as an MCP client toward the local subprocess and as an MCP server toward remote clients:

Remote host → HTTPS /mcp → bridge → stdin/stdout → local MCP server

Use this sequence:

  1. Launch the local server with a pinned executable and a minimal environment.
  2. Expose one authenticated HTTPS MCP endpoint; validate Origin when present and bind the local side narrowly.
  3. Forward JSON-RPC IDs, errors, cancellation, progress, and content types without rewriting successful results into a different contract.
  4. Keep remote user identity separate from any credential the bridge uses for the local capability.
  5. Stop accepting new work before restarting the child process, and make in-flight failure explicit.

Let a stdio-only host reach a remote MCP server

Here the adapter appears to the host as a local stdio server and acts as an HTTP MCP client downstream:

Local host → stdin/stdout → adapter → HTTPS /mcp → remote MCP server

The adapter must keep diagnostics on stderr, authenticate the downstream request, preserve version errors, and map a broken request-scoped stream to a visible failed call rather than inventing a result.

Minimum bridge test

Before calling either pattern working, record the exact host, bridge, server, and protocol versions and verify:

  • discovery or the intended pinned-version path;
  • one successful tool list and one successful read-only tool call;
  • malformed arguments and an unauthorized request;
  • cancellation, timeout, child-process crash, and remote disconnect;
  • no secrets on stdout, in URLs, or in ordinary logs; and
  • clean shutdown and repeatable restart.

This is a directional implementation recipe and acceptance test, not a product endorsement. A named bridge should be recommended only after that exact matrix has been executed. Use the local versus remote guide to decide whether bridging is justified before adding the extra failure and trust boundary.

Secure the boundary that each transport creates

For Streamable HTTP, the current specification requires servers to validate the Origin header when present and return 403 for an invalid origin. A local HTTP server should bind to 127.0.0.1, not 0.0.0.0, unless network exposure is intentional. Remote endpoints should authenticate requests, authorize them independently, use TLS, apply request limits, and validate that mirrored headers match the body.

For stdio, process launch is the boundary. Treat the command, arguments, package source, executable path, environment, current directory, and inherited permissions as security configuration. Keep diagnostics off stdout and avoid exposing secrets through process listings.

Neither transport changes the larger trust model described in how MCP works: servers and their output remain untrusted, while the host owns consent and policy. Choose the binding that fits the deployment boundary, then implement the exact version rather than a transport name remembered from an older tutorial.

Sources8 cited primary or authoritative sources
Last reviewedAugust 25, 2026
How we research