MCP transports: stdio, SSE, and streamable HTTP

The reason MCP transports confuse people is that the deprecated transport is named after a technology the current transport also uses. SSE the transport is dead. Server-sent events the mechanism is alive and well inside streamable HTTP. Untangling those two is most of the work.

By the Continuum team. We build a workbench that runs Claude Code, Codex, and their peers, so the model rates quoted here are the ones our own cost analytics ship with.

The short version

MCP defines two standard transports today: stdio, where the client launches the server as a subprocess, and streamable HTTP, a single endpoint taking POST and returning either JSON or an SSE stream. The older HTTP+SSE transport, with its separate /sse and POST endpoints, was deprecated in the 2025-03-26 spec revision and formally reclassified as Deprecated on 2026-07-28 under a policy that keeps it functional for at least twelve more months. Claude Code and Cursor still support SSE explicitly; Codex never did. Point a new server at streamable HTTP and only reach for SSE when the vendor exposes nothing else.

What you need to know
  • Two current transports: stdio and streamable HTTP. SSE is the deprecated third.
  • HTTP+SSE was deprecated in 2025-03-26 and reclassified Deprecated in 2026-07-28.
  • Streamable HTTP still uses server-sent events, on the POST response. That is the naming trap.
  • Claude Code: --transport sse works and warns. Cursor: "type": "sse". Codex: no SSE at all.
  • A url in a Claude Code JSON entry with no type is silently skipped, not defaulted.
  • One /sse in a URL is usually the whole diagnosis.

The three names, and which one you want

A transport is only the pipe the JSON-RPC messages travel down. It has nothing to do with what a server can do, and picking the wrong one produces a connection failure rather than a degraded server, which is at least an honest failure mode.

MCP transports and their status in the specification.checked 19 aug 2026
TransportShapeStatus
stdioClient launches the server as a subprocess, messages over stdin and stdoutCurrent. Clients SHOULD support it whenever possible
Streamable HTTPOne HTTP endpoint taking POST, replying with JSON or an SSE streamCurrent. The remote transport
HTTP+SSEA GET /sse stream plus a separate POST endpoint the stream announcesDeprecated since 2025-03-26

stdio, and why it is still the default

The client spawns the server as a child process and speaks newline-delimited JSON-RPC over its stdin and stdout. Anything on stderr is treated as logging. The server must not write anything to stdout that is not a valid MCP message, which is the single most common way a home-grown stdio server breaks: one stray print() during startup corrupts the stream.

Every client agrees on the shape of this one.
# Claude Code
claude mcp add playwright -- npx @playwright/mcp@latest

# Codex
codex mcp add context7 -- npx -y @upstash/context7-mcp

stdio wins on latency, needs no auth layer, and keeps data on your machine. It loses when the server should be shared, when it needs to survive your editor restarting, or when the vendor would rather run it themselves. That is the entire case for the remote transports.

MCP SSE: what the SSE transport was, and why it went away

The original remote transport, introduced in protocol version 2024-11-05, used two endpoints. The client opened a long-lived GET to something like https://example.com/sse, the server replied with an endpoint event naming a second URL, and every client-to-server message was POSTed there while every server-to-client message came back down the original stream.

It worked, and it had three problems that a single-endpoint design does not.

  • It requires a long-lived connection for everything. A server that only answers requests still had to hold an open stream per client, which serverless platforms and ordinary load balancers are both bad at.
  • Two endpoints means two places to get auth, CORS, and routing wrong. Most reported "SSE MCP server" failures were the POST endpoint being unreachable while the stream itself connected fine, which reads as a mysterious hang rather than an error.
  • Resumption was ill-defined. Dropping the stream dropped the session, and there was no agreed way to pick it back up.
The deprecation, by spec revision.
RevisionWhat happened to SSE
2024-11-05HTTP+SSE introduced as the remote transport
2025-03-26Streamable HTTP replaces it. SSE marked deprecated, with a documented backwards-compatibility probe
2025-11-25Unchanged, still deprecated
2026-07-28Reclassified as Deprecated under the new feature lifecycle policy, with a minimum twelve-month window before removal

Streamable HTTP, and what the 2026-07-28 revision changed

One endpoint, for example https://example.com/mcp, accepting POST. Each JSON-RPC message is a new POST. The server answers with either application/json for a single result, or text/event-stream when it wants to stream progress notifications before the result. That is the whole design, and it is why a plain request-response server no longer needs any streaming machinery at all.

The current revision, released 28 July 2026, took the design considerably further in the same direction.

  • Protocol-level sessions are gone. The Mcp-Session-Id header was removed. Servers needing cross-call state mint an explicit handle and pass it as an ordinary tool argument.
  • The initialize handshake is gone. Every request carries its protocol version and client capabilities in _meta, and a new server/discover RPC advertises what a server supports.
  • The GET endpoint is gone. Server-to-client change notifications now ride a single long-lived subscriptions/listen POST stream that the client opts into.
  • SSE resumability is gone. Last-Event-ID and SSE event ids were removed. A broken response stream loses the in-flight request and the client must reissue it with a new request id.

Configuring each transport, per client

Claude Code

Three transports, one command.
# streamable HTTP, the default choice
claude mcp add --transport http notion https://mcp.notion.com/mcp

# SSE, for a vendor that exposes nothing else
claude mcp add --transport sse asana https://mcp.asana.com/sse

# with an auth header, either transport
claude mcp add --transport http secure https://api.example.com/mcp \
  --header "Authorization: Bearer $TOKEN"

# local stdio
claude mcp add playwright -- npx @playwright/mcp@latest

Claude Code prints a deprecation warning on --transport sse and points you at HTTP. In JSON, whether that is .mcp.json, ~/.claude.json, or claude mcp add-json, the type field takes http, streamable-http as an alias for it, sse, or ws.

Cursor

.cursor/mcp.json. The transport is the type field.
{
  "mcpServers": {
    "modern": {
      "url": "https://mcp.example.com/mcp",
      "type": "http"
    },
    "legacy": {
      "url": "https://mcp.example.com/sse",
      "type": "sse"
    },
    "local": {
      "command": "npx",
      "args": ["-y", "some-mcp-server"]
    }
  }
}

Codex

~/.codex/config.toml. A url means streamable HTTP, and there is no third option.
[mcp_servers.context7]
command = "npx"
args    = ["-y", "@upstash/context7-mcp"]

[mcp_servers.figma]
url                  = "https://mcp.figma.com/mcp"
bearer_token_env_var = "FIGMA_OAUTH_TOKEN"
Or from the CLI. Verified against codex-cli 0.144.1.
codex mcp add linear --url https://mcp.linear.app/mcp \
  --bearer-token-env-var LINEAR_TOKEN

codex mcp login linear     # for servers behind OAuth
codex mcp list

Migrating an SSE MCP server to streamable HTTP

If you run the server, the migration is smaller than it looks, because both transports can live side by side while clients catch up.

01

Add the new endpoint, keep the old one

Serve a single MCP endpoint that accepts POST, alongside the existing /sse and POST pair. The spec's own backwards-compatibility guidance is to host both, and it costs you one route.

02

Return JSON when you can

Only open an SSE stream on a POST response when you have progress notifications to send before the result. A tool that answers in 200ms should reply application/json and close, which removes a whole class of proxy and timeout problem.

03

Validate Origin and bind to localhost

The spec requires servers to validate the Origin header on incoming connections, and local servers should bind to 127.0.0.1 rather than 0.0.0.0. Without both, a web page can reach a local MCP server through DNS rebinding.

04

Stop depending on the session id

The 2026-07-28 revision removed Mcp-Session-Id entirely. If your server keeps per-connection state, move it behind an explicit handle you mint and return as a tool argument, which works under both the old and new revisions.

05

Announce it, then retire the old pair

Clients pin URLs in config files that nobody revisits. Publish the new endpoint, leave the old one up through the deprecation window, and expect the tail to be long.

If you only consume servers, migration is one line: change --transport sse to --transport http and the URL from /sse to /mcp, then check claude mcp list. If the vendor has not shipped a streamable HTTP endpoint, staying on SSE is correct for now, and the twelve-month policy window means it is not urgent.

The connection errors, and what each one means

SymptomCauseFix
405 Method Not Allowed on a GETThe server offers no SSE stream at that endpointExpected on streamable HTTP. Not an error
Connects, then hangs with no toolsSSE stream opened but its POST endpoint is unreachableCheck the second URL the endpoint event named
400 Bad Request on every call after the firstA pre-2026 server wants Mcp-Session-Id echoed backClient is too old or too new for the server
404 mid-session on an HTTP serverThe session expiredThe client must reinitialise. Older revisions only
Server silently missing from the listA JSON entry has url but no typeAdd "type": "http" or "sse"
initialize POST fails, 404 or 405An /sse URL added with --transport httpMatch the flag to the endpoint
401 on first useOAuth never completedRun /mcp and finish the browser sign-in
Codex skips a remote server entirelyCodex has no SSE transportBridge the SSE endpoint to stdio locally

Two operational details worth knowing before you debug anything deeper. Claude Code reconnects a dropped HTTP or SSE server automatically with exponential backoff, up to five attempts, so an intermittent server shows as pending rather than failed. And remote servers carry a per-request timer covering the time to first response byte, defaulting to 60 seconds, which is separate from the per-tool-call wall clock. A server that is merely slow to start streaming will trip the first timer while looking healthy by the second.

Questions people ask

Is MCP SSE deprecated?

Yes. The HTTP+SSE transport was deprecated in the 2025-03-26 spec revision when streamable HTTP replaced it, and the 2026-07-28 revision reclassified it as Deprecated under a formal lifecycle policy with a minimum twelve-month window before removal. It still works today in clients that support it.

What is the difference between MCP SSE and streamable HTTP?

SSE used two endpoints: a long-lived GET stream that announced a separate POST URL for client messages. Streamable HTTP uses one endpoint taking POST, which replies with either JSON or an SSE stream. Streamable HTTP still uses server-sent events, it just no longer needs a standing connection for servers that only answer requests.

How do I add an SSE MCP server to Claude Code?

claude mcp add --transport sse <name> <url>, for example claude mcp add --transport sse asana https://mcp.asana.com/sse. Add credentials with --header. Claude Code warns that the transport is deprecated and suggests HTTP where the vendor offers it.

Does Codex support SSE MCP servers?

No. Codex accepts command for a stdio server or url for a streamable HTTP server, and nothing else. Native SSE support has been an open request rather than a shipped feature. To use an SSE-only server with Codex, run a local bridge that presents it as stdio.

Why does my MCP server return 405 on GET?

That is correct behaviour for a streamable HTTP server that does not offer a server-initiated stream at its endpoint. The spec allows a server to answer the optional GET with 405 Method Not Allowed. Nothing is wrong.

What transport should a new MCP server use?

stdio if it runs on the user machine, streamable HTTP if it is remote. Do not build a new HTTP+SSE server. If you must serve older clients, host both and let them probe, which is the migration path the specification documents.

What happened to Mcp-Session-Id?

The 2026-07-28 revision removed protocol-level sessions and the Mcp-Session-Id header from streamable HTTP. Servers needing state across calls now mint an explicit handle and take it back as an ordinary tool argument. Older servers still using the header are not broken, they are on an earlier revision.

Do I need to change my MCP config because of the deprecation?

Not urgently. If your vendor publishes a streamable HTTP endpoint, switching is one flag and one URL. If SSE is all they offer, the twelve-month lifecycle window means you have notice before anything stops working.

Sources

Every figure above was read from these pages on August 2026. Vendors reprice without notice; if you find a stale number, tell us.

  1. MCP specification: transports read 19 August 2026
  2. MCP 2026-07-28: key changes session removal, SSE reclassification, resumability removal
  3. MCP feature lifecycle and deprecation policy the minimum twelve-month deprecation window
  4. MCP 2025-03-26 transports the revision that deprecated HTTP+SSE
  5. Claude Code MCP documentation
  6. Cursor documentation: MCP
  7. Codex documentation: MCP the mcp_servers schema, stdio and streamable HTTP only
Try it

A server that
never connected.

Continuum shows the token split per session across Claude Code, Codex and Cursor, so a misconfigured server stops looking like a bad answer.

free app · your subscriptions · local-first