Remote vs Local MCP Servers in 2026: stdio vs Streamable HTTP, No Sessions

Oct 31, 2026 · 12 min · Ajay Kumar

The first MCP server most engineers run is a local one: a JSON blob in an editor config that launches npx something and talks to it over a pipe. The first MCP server most companies ship is a remote one, behind a URL and an OAuth screen. I build infrastructure that agents call through MCP, and the questions I get are nearly always about the gap between those two: which transport, whether SSE still matters, why the session ID disappeared, and where to host the thing. This post explains remote vs local MCP servers as they stand at the end of October 2026: how stdio and Streamable HTTP actually work, what the stateless 2026-07-28 revision changed, how the main hosting platforms run MCP, and the auth, scaling and security trade-offs that should decide it. The hands-on half is a step-by-step guide to deploying a remote MCP server on Cloudflare Workers, with outputs from runs I did this morning.

What is the difference between a local and a remote MCP server?

A local MCP server is a process that the client starts on your machine and talks to over stdin and stdout. A remote MCP server is an HTTP service that many clients reach over the network.

A local server inherits your operating system user. It can read whatever files you can read and call whatever APIs your environment variables open. It serves one person, dies when the client exits, and needs no authentication protocol because the trust boundary is your laptop. A remote server inherits nothing. It needs its own identity system, it has to survive many users at once, and it is exposed to everyone who learns the URL. If MCP itself is still fuzzy, my plain-English MCP explainer covers hosts, clients and servers first.

The 2026-07-28 transport overview defines exactly two standard bindings: stdio, "newline-delimited messages over the standard streams of a client-launched subprocess", and Streamable HTTP, where "each message is an HTTP POST to a single MCP endpoint". Protocol semantics are identical on both. The transport decides framing, metadata and cancellation, and nothing else.

stdio vs Streamable HTTP: how each transport works

On stdio, the client writes one JSON-RPC message per line to the server's stdin and reads one per line from its stdout. The stdio binding says the server must not write anything to stdout that is not a valid MCP message, may log to stderr, and should exit promptly when stdin closes. A stray print() on stdout corrupts the stream, which explains most "connects then dies" bugs.

On Streamable HTTP, every client message is a new POST to one endpoint, conventionally /mcp. The server answers with a JSON object or a short SSE stream scoped to that request, and cancellation is just closing that response. Since 2026-07-28 the protocol version and client capabilities ride in the body's _meta, and the Streamable HTTP binding mirrors the method and tool name into Mcp-Method and Mcp-Name headers so a proxy can route without parsing JSON. Here is the same pair of servers seen from a client, followed by a raw request against the Workers server from my guide:

# Local: Claude Code spawns the process and speaks JSON-RPC over its stdin/stdout.
# Everything after -- is the server command; its env and files are yours.
claude mcp add --transport stdio notes-local -- node ./notes-server.js

# Remote: Claude Code POSTs to a URL and handles OAuth in the browser.
claude mcp add --transport http notes https://notes-mcp.example.workers.dev/mcp

# The wire format of a 2026-07-28 remote call (wrangler 4.156.0, agents 0.32.0, run 31 Oct 2026).
# No initialize, no session header: version and capabilities travel in _meta every time.
curl -s -X POST http://localhost:8788/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2026-07-28' \
  -H 'Mcp-Method: tools/call' -H 'Mcp-Name: get_note' \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_note",
       "arguments":{"key":"deploy"},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28",
       "io.modelcontextprotocol/clientCapabilities":{}}}}'
# -> {"result":{"content":[...],"structuredContent":{"key":"deploy","text":"run wrangler deploy"},
#     "resultType":"complete",...}}

When I sent a body naming add_note with an Mcp-Name: get_note header, the server refused it with HTTP 400 and error -32020, "the request headers and body disagree".

Is SSE deprecated in MCP?

Yes, and has been for a while. The original remote transport from 2024-11-05, usually called HTTP+SSE, used a long-lived GET stream for server messages and a separate POST endpoint for client messages. Streamable HTTP replaced it in the 2025-03-26 revision. The 2026-07-28 changelog formally reclassified HTTP+SSE as Deprecated under the new feature lifecycle policy (SEP-2596), which means it stays in the spec for at least twelve months and then becomes eligible for removal. The changelog tells new implementations not to adopt deprecated features.

Two points cause confusion. First, SSE as a response format is still alive: a Streamable HTTP server can answer a POST with a request-scoped text/event-stream. What is gone is the separate SSE endpoint and, in 2026-07-28, the standalone GET stream and Last-Event-ID resumability. Second, clients are still bridging. Claude Code's MCP docs call the SSE transport deprecated and say --transport http now falls back to SSE automatically from v2.1.265 when a server does not accept HTTP. Vercel's mcp-handler v2 removed HTTP+SSE and its Redis dependency entirely. If a tutorial tells you to expose /sse, it is from 2025.

Stateless vs stateful MCP servers after 2026-07-28

Between 2025-03-26 and 2025-11-25, Streamable HTTP servers could mint an Mcp-Session-Id during an initialize handshake, keep a GET stream open to push requests to the client, and resume broken streams. That made remote MCP servers stateful in the way load balancers hate: either every request from a session hit the same replica, or you put session state in Redis.

The 2026-07-28 revision removed all of it. Per the changelog, it removed protocol sessions and the session header (SEP-2567), removed the initialize handshake so "every request now carries its protocol version and client capabilities in _meta" (SEP-2575), added a mandatory server/discover RPC, replaced server-initiated requests with multi round-trip results where the server returns input_required and the client retries (SEP-2322), and moved change notifications to an opt-in subscriptions/listen stream. Servers that need state across calls are told to mint explicit handles and pass them as ordinary tool arguments.

"Stateless" means the protocol holds no state, not your product. A notebook still lives in a database, KV or a Durable Object, keyed by the authenticated user; you just lose the excuse to keep it in process memory.

The ecosystem is mid-transition, and you should check which era your stack speaks. Cloudflare marks its stateful McpAgent class deprecated and feature-frozen in favour of a stateless createMcpHandler. Amazon's AgentCore Runtime MCP guide still pins the Python SDK to mcp>=1.28.1,<2 (the 2025-11-25 era), recommends stateless_http=True, and says the platform adds an Mcp-Session-Id header to requests without one. The good news is that current SDKs speak both eras: my Workers server answered a 2025-11-25 initialize alongside 2026-07-28 requests.

Local (stdio) Remote, up to 2025-11-25 Remote, 2026-07-28 Who starts it Client spawns a subprocess You run a long-lived service Any instance, any request Wire JSON-RPC lines on stdin/stdout POST + GET stream, session ID One POST per request, _meta Identity Your OS user, your files OAuth 2.1 token per user OAuth 2.1, audience-bound Scaling One user, one machine Sticky sessions or shared store Plain round-robin, no store Main risk Runs anything you can run Lost sessions on redeploy Exposed to the internet
The three server shapes in use today, summarised from the 2026-07-28 transport, stdio and Streamable HTTP specification pages and the earlier 2025-11-25 transport page.

Where to host a remote MCP server: Workers, Cloud Run, Lambda, AgentCore, Vercel

Once the protocol is stateless, an MCP server is a small HTTP handler that any serverless platform can run. The differences are limits, built-in auth and which SDK era the docs assume, per each vendor's pages this week:

Platform How MCP runs Limits that matter Auth help Source
Cloudflare Workers createMcpHandler, fresh server per request, V8 isolates Paid plan: CPU 30 s default, 5 min max (free: 10 ms); 128 MB per isolate; $5/month for 10M requests Workers OAuth Provider (authorization and resource server) handler docs, limits
Google Cloud Run Your container; Streamable HTTP only, "not MCP servers with stdio transport" Request timeout 5 min default, 60 min max IAM invoker by default, gcloud run services proxy for local clients host MCP servers, timeouts
AWS Lambda Function URL or API Gateway in front of a stateless handler 15 min timeout; 6 MB sync payload, 200 MB streamed IAM (SigV4) on Function URLs; OAuth needs API Gateway or a gateway Lambda quotas, awslabs library
Bedrock AgentCore Runtime Container on 0.0.0.0:8000/mcp, stateless or stateful mode Docs pin Python mcp<2 (2025-11-25 era) CUSTOM_JWT authorizer, RFC 9728 metadata on 401 AgentCore MCP guide
Vercel mcp-handler 2.x route in Next.js, Fluid compute v2 removed HTTP+SSE and Redis withMcpAuth verifies tokens; you bring the authorization server Vercel MCP docs

My reading of that table: pick the platform where your data already lives, because a tool that crosses a cloud boundary on every call pays for it in latency and egress. Workers is the least work if the tools mostly call HTTP APIs, since CPU-time billing ignores the seconds you spend waiting on upstreams. Cloud Run is the right default for anything that needs a real filesystem, a native binary or a long call. Lambda is fine for short tools, but a Function URL alone gives you IAM or nothing, and MCP clients speak OAuth, so you end up adding a gateway. The awslabs project wraps existing stdio servers to run inside Lambda, a pragmatic bridge for local-only servers. I compared the platforms' pricing models more broadly in serverless in 2026.

How do remote MCP servers handle authentication and security?

Local servers skip authentication, which is exactly why they are risky. A stdio server runs with your privileges, so installing one is equivalent to running its author's code as you. Local HTTP servers add a second problem: a malicious web page can reach localhost through DNS rebinding. That is why the Streamable HTTP spec says servers must validate Origin, respond 403 when it is invalid, and should bind to 127.0.0.1 rather than 0.0.0.0 when local. This is not theoretical. CVE-2025-49596, scored 9.4, was remote code execution in MCP Inspector versions before 0.14.1, because its local proxy accepted unauthenticated requests to launch stdio commands. My Workers test server returned 403 Invalid Origin for a forged Origin header without me writing a line of code, which is the default you want.

Remote servers carry the full MCP authorization stack: the server is an OAuth 2.1 resource server, publishes RFC 9728 protected resource metadata, answers unauthenticated calls with 401 and a WWW-Authenticate pointer, and must accept only tokens issued for its own URL. The 2026-07-28 revision also deprecated Dynamic Client Registration in favour of Client ID Metadata Documents, and added issuer checks (RFC 9207) on authorization responses. The bridge between the two worlds has its own history: mcp-remote, which lets stdio-only clients reach remote servers, had CVE-2025-6514, a 9.6 command injection from a malicious authorization endpoint, fixed in 0.1.16 per the GitHub advisory.

The trade-off: remote servers are safer for the user's machine and more dangerous for the server's data. A local server can damage one laptop; a remote one with bad token handling can damage every tenant. I went through the ecosystem side of that in MCP security in 2026.

Scaling a remote MCP server without sticky sessions

On the 2026-07-28 protocol, a remote MCP server scales like any stateless API: run more replicas behind a round-robin load balancer and keep state in a store. A few protocol details help. List results now carry ttlMs and cacheScope, so clients can cache tools/list instead of re-fetching it; the spec also asks servers to return tools in a deterministic order, which helps model prompt caching. The Mcp-Method and Mcp-Name headers let a gateway rate limit or block a destructive tool by name. And a broken response stream now simply loses that request, which the client re-issues with a new ID, so there is no replay buffer to share between instances.

Three things still need thought. Notifications are per instance: Cloudflare's docs warn that a notification published in one isolate "does not reach a subscription stream running in another isolate", and the same is true of any multi-replica deployment without a pub/sub layer. Long-running work does not fit inside a request timeout, so it belongs in the official Tasks extension, where the server returns a handle and the client polls. And older clients still send Mcp-Session-Id, so during the transition either serve them through a stateless legacy lane (Cloudflare's default) or keep session affinity for that traffic only. Ordinary platform discipline (timeouts, idempotency, per-user quotas, structured logs) is what keeps it running.

When should you still run an MCP server locally?

Local is the right answer when the server's job is your machine: a filesystem, a local database, a browser on your desktop, a build tool in your repo. It is also right for development, since stdio needs no TLS, OAuth or deploy. Remote is the right answer when more than one person uses the server, when it holds credentials users should not see, or when it touches shared data. If a local server executes model-written code, run it in a real sandbox rather than on your host; I build PandaStack, a Firecracker microVM cloud for exactly that, so weigh my opinion accordingly.

FAQ

What is the difference between stdio and Streamable HTTP in MCP?

stdio sends newline-delimited JSON-RPC over the stdin and stdout of a process the client launches, so it only works on one machine. Streamable HTTP sends each message as an HTTP POST to one endpoint and replies with JSON or a request-scoped SSE stream, so many clients can share one server over the network.

Is the MCP SSE transport deprecated?

Yes. HTTP+SSE was superseded by Streamable HTTP in the 2025-03-26 revision and formally classified as Deprecated in 2026-07-28, with at least a twelve-month window before removal. SSE survives only as a response format inside Streamable HTTP.

Are MCP servers stateless now?

At the protocol level, yes, from the 2026-07-28 revision: no initialize handshake, no session ID, version and capabilities on every request. Application state still exists and should live in a store keyed by the authenticated user or by explicit handles passed as tool arguments.

Can I run a local stdio MCP server remotely?

Not directly over the network, because stdio needs a parent process. You can wrap it: Cloud Run and similar platforms require Streamable HTTP, while projects such as awslabs' Lambda library run stdio servers inside a function and expose them remotely.

This quarter I would do three things. Move any remote server still on /sse or McpAgent-style sessions to a stateless Streamable HTTP handler, keeping a legacy lane until old clients drain. Add a test that sends a token minted for another audience and expects 401. And list every local stdio server on the team's laptops, because each one is code running with someone's credentials, and most teams have never counted them.

Related: What Is MCP (Model Context Protocol)? A Plain-English Guide for 2026, MCP in 2026: 475M SDK Downloads a Month, 39,492 Servers in a Registry Still in Preview, and 12 Gateways Selling the Same 3 Features and How to Deploy a Secure Remote MCP Server with OAuth (2026).

I'm Ajay Kumar — I build and operate PandaStack, an open-source Firecracker microVM cloud for AI agents, and write about the infrastructure and tooling around it.

Need this kind of infrastructure work? See what I do or email hello@ajayk.sh.


Related