Local vs Remote MCP Servers: stdio and Streamable HTTP
By SRIFT maintainers · Published
Reviewed by SRIFT maintainers · last reviewed against the SRIFT 4.3.2 source · third-party facts checked 8 October 2026.
Short answer
A local MCP server runs on your machine, either launched by the client as a subprocess over stdio or listening on the loopback address over HTTP, and it can reach anything your user account can: files, local processes, credentials in the environment. A remote server is reached over Streamable HTTP at an HTTPS URL, sees only what the host sends it in requests, and is normally protected with OAuth.
Choose local when the tools must touch local files or programs. Choose remote when they only need data the service already holds, when several users share one deployment, or when the client runs in the cloud and cannot start processes or reach 127.0.0.1.
- stdio
- Client launches the server as a subprocess; newline-delimited JSON-RPC
- Streamable HTTP
- One endpoint; every client message is an HTTP POST
- Remote auth
- Optional in the spec; OAuth 2.1 resource server when used
- Local HTTP must
- Validate Origin, bind to 127.0.0.1, ideally require a token
Three ways to run an MCP server
“Local vs remote” really covers three deployment shapes, and the middle one is where most mistakes happen.
- Local over stdio. The host starts the server as a child process and talks to it through stdin and stdout. No network socket exists; only the parent process can talk to it.
- Local over HTTP. The server is a long-running process on the same machine, listening on a loopback port. Several clients can share it, but so can every other program on the machine, and, unless it defends itself, web pages open in the user's browser.
- Remote over HTTPS. The server runs on someone else's infrastructure and is reached at a URL such as
https://example.com/mcp. It has no access to the user's machine at all.
The protocol messages are identical in all three; the MCP overview covers them. What differs is framing, who can connect, how callers are authenticated and what the server can reach.
How stdio works
In the stdio transport the client launches the server and writes JSON-RPC requests to its stdin, one message per line; the server writes responses and notifications to stdout. Messages must not contain embedded newlines. The server may log anything to stderr, but a single stray console.log to stdout corrupts the stream, which is the most common reason a stdio server “connects and then does nothing”. To shut down, the client closes stdin and waits; servers should exit as soon as stdin reaches end-of-file. Cancelling a request means sending a notifications/cancelled message, because there is no per-request stream to close.
stdio servers do not use MCP's OAuth flow. The authorization specification says they should take credentials from the environment instead, which is why client configurations for stdio servers often include an env block with an API key. The process runs with the full privileges of the user who started the host: if it can open ~/.ssh/id_rsa, so can any tool it implements.
How Streamable HTTP works
Streamable HTTP, introduced in 2025-03-26 to replace the older HTTP+SSE transport, uses a single endpoint. Each client message is a separate POST with an Accept header listing both application/json and text/event-stream. The server answers a request with one JSON object, or with a Server-Sent Events stream that belongs to that request alone: progress notifications first, the final response last. Closing that stream cancels the request.
Under the 2026-07-28 revision every POST also carries headers that mirror the body: MCP-Protocol-Version, Mcp-Method and, for tool calls, resource reads and prompt fetches, Mcp-Name. A gateway can rate-limit or route by tool name without parsing JSON, and the server must reject a request whose headers disagree with its body.
Sessions: Mcp-Session-Id then and now
Revisions 2025-03-26 to 2025-11-25 let a server assign an Mcp-Session-Id in its reply to initialize. The client echoed it on every later request and ended the session with an HTTP DELETE; clients could also hold a GET stream open for server-initiated messages and resume broken streams with Last-Event-ID. Revision 2026-07-28 removed all of that. Every request now carries its own version and capabilities, servers that need state hand out explicit handles as tool arguments, and a server that only speaks the new revision ignores Mcp-Session-Id and answers GET with 405. The practical gain is horizontal scaling: any instance behind a load balancer can answer any request without sticky sessions. Servers and clients that must work with the older revisions implement both behaviours.
What each can reach, and how callers are authenticated
Remote servers: OAuth 2.1
A remote server sees only what arrives in requests. If it needs to know who is calling, the specification's authorization flow applies: the server acts as an OAuth 2.1 resource server, answers an unauthenticated request with 401 and a pointer to its Protected Resource Metadata (RFC 9728), and the client discovers the authorization server from there, runs an authorization-code flow with PKCE, and sends Authorization: Bearer on every request. Clients must include the resource parameter (RFC 8707) so that the token is bound to that one server, and servers must reject tokens issued for anyone else and never pass a client's token through to an upstream API. The spec also asks servers to challenge for the smallest scope an operation needs rather than requesting everything up front.
Local HTTP servers: DNS rebinding and drive-by requests
A server on 127.0.0.1 feels private, but a browser on the same machine can reach it. In a DNS rebinding attack, a page on attacker.example waits for its DNS record to expire, re-points the name at 127.0.0.1, and then sends requests that the browser considers same-origin. Even without rebinding, a page can send cross-origin requests to a loopback port, and if the server answers with permissive CORS headers, the page can read the replies as well. The Streamable HTTP specification therefore requires servers to validate the Origin header (and return 403 for an invalid one), recommends binding to 127.0.0.1 rather than 0.0.0.0, and recommends authentication on every connection. Checking the Host header blocks rebinding but not a page that addresses 127.0.0.1 directly; only an Origin allowlist or a per-install token stops that. The security best-practices page goes further for local servers: prefer stdio, or restrict HTTP access with a token or an IPC mechanism such as a Unix domain socket.
Side by side
| Local, stdio | Local, HTTP on loopback | Remote, HTTPS | |
|---|---|---|---|
| Started by | The client, per host | The user or a service manager | The operator |
| Who can connect | Only the parent process | Any local process; browsers too unless Origin is checked | Anyone who can reach the URL and pass auth |
| Local files and processes | Everything the user can access | Everything the user can access | None |
| Authentication | Environment credentials; none needed for the pipe | Should require a token | OAuth 2.1 (optional in the spec, expected in practice) |
| Shared by several clients | No; one process per client | Yes | Yes, across users |
| Per-call latency | A local pipe, no network | Loopback TCP | A network round trip plus TLS, more if the server asks for input |
| Updates | Whatever version the launch command resolves to | Restart the process | Operator deploys; every user gets it at once |
| Works for cloud-hosted agents | No | No: 127.0.0.1 is the cloud machine | Yes |
Latency rarely decides the choice. Tool calls are dominated by the model's own generation time and by the work the tool does. What does decide it is reach: a remote server cannot read a file on your disk, and a local server cannot be used by an agent that runs in someone else's cloud.
Worked example: one file-transfer server, three deployments
SRIFT, an open-source encrypted transfer tool, ships all three shapes, which makes the trade-offs concrete. The details below are from version 4.3.2.
Local over stdio: all 15 tools
srift mcp is a stdio server. It can read local files, so it offers the full set of 15 tools, including srift_quick_share, which turns a local path into a download link. A Claude Desktop-style configuration that needs no global install:
{
"mcpServers": {
"srift": {
"command": "npx",
"args": ["-y", "srift-transfer", "mcp"]
}
}
}Behind the stdio process sits a background daemon. In sandboxes that forbid local servers or background processes, the MCP process runs the daemon inside itself instead (“embedded mode”): no local port is opened and only outbound HTTPS and WebSocket traffic on port 443 is needed. srift mcp --foreground forces that mode.
Local over HTTP: the daemon's endpoint
The same 15 tools are available at http://127.0.0.1:3822/mcp for clients that prefer HTTP. The daemon listens on 127.0.0.1 only and rejects any request whose Host header is not a loopback name for its port, which defeats DNS rebinding.
Remote: https://srift.app/mcp, 9 tools
Cloud agents cannot reach anyone's 127.0.0.1, so SRIFT also hosts a Streamable HTTP endpoint. It has no filesystem access and holds no encryption keys, so it offers only the 9 tools that make sense without them: start, join, inspect and close a session; approve, reject and kick participants; list transfers; and network diagnosis. The files themselves move between the real participants (a person in a browser, or a CLI). The endpoint answers both protocol eras; its room tools still tie calls together through the Mcp-Session-Id returned by initialize, and that link is dropped after 30 minutes idle.
{ "mcpServers": { "srift": { "type": "streamable-http", "url": "https://srift.app/mcp" } } }
# Claude Code
claude mcp add --transport http srift https://srift.app/mcpBecause the endpoint is public and unauthenticated, abuse is handled with limits rather than identity: 5 requests per second per IP with a burst of 40, a stricter 0.5 per second (burst 15) for creating or joining sessions, and a 200 kB body limit. Its Origin check rejects malformed values such as null or file:// origins; it is not an allowlist, which matters less for a public endpoint that cannot touch anyone's files. Setup for specific clients is on the MCP file transfer page.
Choosing between them
- The tool must read or write local files, or drive local programs: local, and stdio unless several clients really need to share one process.
- The tool wraps a SaaS API the user logs into: remote with OAuth. The vendor holds the data anyway, and users get updates without reinstalling.
- The client runs in the cloud (a hosted chat assistant, a workflow tool): remote is the only option. Tunnelling a local HTTP server to the internet to make it reachable exposes it to everyone who finds the URL, so add authentication first.
- You need a local HTTP server: bind to 127.0.0.1, validate
Originagainst an allowlist, require a per-install token, and stop the process when it is not in use.
Whichever you pick, a local server inherits all of your privileges. The MCP server security guide covers how to limit what a file-handling tool can do when the model is told to misuse it.
Frequently asked questions
Can a cloud AI assistant use an MCP server running on my laptop?
Not directly. A cloud-hosted client resolves 127.0.0.1 to its own machine, not yours. It needs a server reachable over the internet; exposing a local server through a tunnel works but makes it reachable by anyone with the URL, so it must have authentication.
Is stdio more secure than HTTP for a local MCP server?
For who can connect, yes: only the parent process can write to a stdio server, while a loopback HTTP port is reachable by every local program and possibly by web pages. For what the server can do once called, there is no difference; both run with the user's privileges.
Do I still need to handle Mcp-Session-Id?
Only to support clients on protocol revisions 2025-03-26 to 2025-11-25. The 2026-07-28 revision removed protocol sessions; servers that need state across calls return explicit handles as tool arguments.
References
- Transports: stdio (Model Context Protocol)
- Transports: Streamable HTTP (Model Context Protocol)
- Authorization (Model Context Protocol)
- Security Best Practices (Model Context Protocol)
- RFC 9728: OAuth 2.0 Protected Resource Metadata (IETF)
- RFC 8707: Resource Indicators for OAuth 2.0 (IETF)
Give your agent a file-transfer tool
Local MCP over stdio (15 tools) or hosted at https://srift.app/mcp (9 session tools, no file access).
{ "mcpServers": { "srift": { "command": "npx", "args": ["-y", "srift-transfer", "mcp"] } } }Related resources
- What Is MCP? The Model Context Protocol ExplainedThe Model Context Protocol in plain terms: hosts, clients and servers, tools vs resources vs prompts, stdio vs Streamable HTTP, and where the risks are.Explainer
- MCP Server Security for File-Handling ToolsThreats to MCP servers that read or send files: prompt injection, path traversal and exfiltration, and the controls a file-transfer server needs.Explainer
- MCP Config Files for VS Code, Windsurf, Cline, Codex and Other ClientsWhere each MCP client keeps its server config, and the exact SRIFT entry for VS Code, Windsurf, Cline, Codex, Zed, Gemini CLI, Continue and more.How-to
- MCP File Transfer Server for AI AgentsThe core SRIFT MCP server (15 tools, local) and the hosted endpoint (9 tools).SRIFT product
