Choosing between stdio and Streamable HTTP for an MCP server is a security decision, not a packaging detail, because the two transports put the trust boundary in completely different places. With stdio the client launches the server as a child process on the same machine, so the risk is local code execution and a poisoned software supply chain. With Streamable HTTP the server is an independent network service, so the risk is authentication, token handling, DNS rebinding and everything that comes with exposing an endpoint. This guide compares threat models, controls and decision criteria for each. It is for security architects, platform engineers and MCP server owners. Protocol details reflect MCP specification revision 2026-07-28, checked on 14 November 2026; the move off the older HTTP+SSE transport is covered in the MCP SSE to Streamable HTTP Migration guide and protocol threats in general in the MCP Security Guide.
Key takeaways
- Short answer: use stdio for a server that only one user's client needs, that works on local resources, and that you can pin and sandbox on the endpoint; use Streamable HTTP when several users or agents share the server, when it fronts a shared system of record, or when you need central authentication and audit.
- stdio has no authentication layer, by design. The authorisation specification says stdio implementations should not follow the OAuth flow and should take credentials from the environment, so the security of a stdio server is the security of the process, its package source and the secrets it inherits.
- Streamable HTTP inherits every web risk. Servers must validate the
Originheader and return 403 on an invalid one, should bind to 127.0.0.1 when local, should authenticate every connection, and must only accept tokens issued for themselves. - A local HTTP server is the worst of both. It carries stdio's supply chain exposure plus a listening port that any local process or, through DNS rebinding, any web page can reach. The security best practices advise local servers to use stdio, or to restrict an HTTP transport with an authorisation token or an access-controlled IPC channel.
- The transport does not change what a tool can do. Tool poisoning, prompt injection and excessive scopes apply on either; transport decides where you enforce controls, not whether you need them.
The two transports in plain terms
stdio: the MCP client launches the server as a subprocess. The server reads newline-delimited JSON-RPC messages from stdin and writes them to stdout; it may write UTF-8 logging to stderr, and it must not write anything to stdout that is not a valid MCP message. All metadata travels in the message body under _meta; there is no header layer. The server runs with whatever user, filesystem, network and environment the client process has, unless something else restricts it.
Streamable HTTP: the server runs as an independent process that can handle many clients. It exposes one MCP endpoint, for example https://example.com/mcp, and every client message is its own HTTP POST. In revision 2026-07-28 the transport is stateless: there are no protocol sessions, the old GET stream and Mcp-Session-Id header are gone, and a server should answer GET or DELETE with 405. Each POST carries MCP-Protocol-Version, Mcp-Method and, for tools/call, resources/read and prompts/get, an Mcp-Name header mirrored from the body. Authorisation follows the MCP authorisation specification, built on OAuth 2.1 with RFC 9728 Protected Resource Metadata and RFC 8707 resource indicators.
Protocol semantics are identical on both. What differs is who can reach the server, how the caller is identified and where an intermediary can see the traffic.
Side-by-side comparison
| Dimension | stdio | Streamable HTTP |
|---|---|---|
| Who can reach the server | Only the parent process holding its standard streams | Anything that can reach the endpoint over the network, subject to binding, Origin checks and authentication |
| Caller identity | Implicit: whoever launched the process | Explicit: bearer access token in the Authorization header on every request |
| Where server credentials come from | Environment variables, files or OS keychain on the host | The server's own secret store; per-user tokens issued by an authorisation server |
| Main threat class | Malicious launch commands, malicious or compromised packages, inherited secrets | Broken authentication, token passthrough and theft, DNS rebinding, SSRF during OAuth discovery |
| Visibility to a network gateway | None; traffic never leaves the host | Full; gateways can route on mirrored headers after body validation |
| Audit evidence | Client logs, optional stderr capture, endpoint telemetry | HTTP access logs, token claims, server-side tool call logs |
| Multi-user use | One process per client; no sharing | Designed for many clients; tenancy and per-user authorisation become the server's job |
Threat model: stdio
A stdio server is code that the client executes. The security best practices describe local servers as binaries downloaded and executed on the same machine as the client, and list three attack routes: a malicious startup command placed in client configuration, a malicious payload inside the server itself, and an insecure local server left running on localhost and reached through DNS rebinding. The first two are stdio problems; the third applies only when a "local" server listens on a port.
- Configuration as an execution path: an MCP configuration entry is a command line. Whoever can write that file chooses what runs. In the Amazon Q MCP Config Vulnerability 2026, the IDE extension loaded server definitions from a repository's
.amazonq/mcp.jsonand ran their commands without asking, and the processes inherited the developer's environment, including cloud access keys and session tokens. The fix added an untrusted server consent prompt. - Package supply chain: most stdio servers are launched through a package runner, so the version that runs is whatever the registry serves at launch time unless it is pinned. The postmark-mcp Malicious MCP Server 2025 worked normally for fifteen versions before version 1.0.16 added one line that blind-copied every outgoing email to an attacker address. This maps to ATT&CK T1195.002 Compromise Software Supply Chain.
- Inherited secrets: because stdio servers take credentials from the environment, a server often receives far more than it needs: the whole shell environment, cloud CLI profiles, SSH agent sockets and tokens in dotfiles (T1552.001 Credentials In Files).
- Same privileges as the client: the security best practices tell clients to warn that MCP servers run with the same privileges as the client. Without a sandbox, a stdio server can read the home directory, open outbound connections and spawn further processes (T1059 Command and Scripting Interpreter).
- Proxies that spawn stdio servers: the specification is explicit that stdio itself is not inherently vulnerable, but a local proxy service that spawns servers on request creates an escalation path. A client-side flaw such as cross-site scripting can steal the proxy token and ask the proxy to launch arbitrary commands. The MCP Inspector advisory GHSA-7f8r-222p-6f5g (CVE-2025-49596, fixed in 0.14.1) describes this class: missing authentication between the Inspector client and its proxy let unauthenticated requests launch MCP commands over stdio.
What stdio does well is reachability: no other machine and no web page can send it a message, and there is no token in transit. That is why the specification recommends it for servers intended to run locally.
Threat model: Streamable HTTP
A Streamable HTTP server is a web service that acts as an OAuth resource server. Its main assets are the access tokens presented to it, the downstream credentials it holds, and the data behind its tools.
- Missing or weak authentication: authorisation is optional in MCP, and the transport says only that servers should implement proper authentication for all connections. An unauthenticated remote server is an open API to whatever its tools touch. The LiteLLM MCP Auth Bypass 2026 shows how a validation path that falls back to an empty authentication object instead of rejecting the request turns any bearer token into a full MCP session.
- DNS rebinding: a web page the user visits re-points its own hostname to 127.0.0.1 or an internal address, and the browser then sends requests to the MCP endpoint as if same-origin. The transport requires servers to validate the
Originheader on all incoming connections and to respond 403 when it is present and invalid, and says local servers should bind to 127.0.0.1 rather than 0.0.0.0. Without these, the specification warns, attackers could use DNS rebinding to interact with local MCP servers from remote websites. - Token passthrough and audience confusion: servers must validate that access tokens were issued specifically for them, must not accept or transit any other tokens, and must not pass the client's token to upstream APIs. Forwarding loses the audit trail and lets one compromised service replay a token at another (T1550.001 Application Access Token).
- Token theft: tokens cached or logged on the server, or stored by the client, can be replayed (T1528 Steal Application Access Token). The specification requires secure token storage, forbids tokens in the query string and says authorisation servers should issue short-lived access tokens.
- SSRF during discovery: a client fetches the
resource_metadataURL, authorisation server URLs and endpoint URLs supplied by the server. A malicious server can point them at internal ranges or169.254.169.254. Clients should require HTTPS, block private, loopback and link-local ranges, validate redirect targets and, when deployed server-side, route discovery through an egress proxy. - Header and body disagreement: intermediaries route on
Mcp-Method,Mcp-NameandMcp-Param-*headers, but the server executes the body. Servers that process the body must reject mismatches with HTTP 400 and JSON-RPC error-32020(HeaderMismatch), and intermediaries enforcing policy on headers should reject protocol versions that do not require that validation.
What Streamable HTTP does well is control: one deployment can be patched, logged, put behind a gateway and given its own least-privilege identity.
The local HTTP server problem
Many single-user servers still listen on a localhost port. This combines stdio's package and privilege risks with an endpoint that every local process can reach and that a browser can be tricked into reaching. The security best practices are direct: servers intended to run locally should use stdio to limit access to the MCP client, and if they use HTTP they should require an authorisation token or use Unix domain sockets or another IPC mechanism with restricted access. Because stdio framing works unchanged over Unix domain sockets, a socket restricted to one user keeps stdio's reachability properties without a TCP port.
If a local HTTP server cannot be avoided, treat it as a remote server that happens to run on a laptop: bind to 127.0.0.1 only, validate Origin against an explicit allowlist, require a per-launch random token, and never expose a "spawn command" endpoint without authentication. See the Local MCP Server Security Guide for workstation settings.
Controls per transport
| Control objective | stdio | Streamable HTTP |
|---|---|---|
| Decide what may run or connect | Managed client allowlist of server commands; consent dialog showing the exact command, untruncated, before one-click install | Allowlist of server URLs in client policy; gateway or egress rules for remote endpoints |
| Integrity of the code | Pin exact package versions or digests; install from the publisher's official source; review diffs before upgrade | Signed images, controlled deployment pipeline, vendor security updates applied centrally |
| Caller authentication | Not applicable at protocol level; rely on OS user and process ancestry | OAuth 2.1 bearer token on every request, audience bound with RFC 8707 resource; 401 on invalid or expired tokens |
| Credential scope | Pass only the named variables the server needs; prefer short-lived tokens from a broker over static keys in the environment | Minimal scopes_supported, step-up through insufficient_scope challenges, separate upstream tokens per API |
| Containment | Sandbox or container with restricted filesystem and network; explicit grants for extra directories | Container or workload isolation, its own non-human identity, no access to instance metadata unless needed |
| Browser and network exposure | None by default; keep it that way by avoiding HTTP wrappers | Origin validation with 403, 127.0.0.1 binding when local, TLS for remote, header and body validation |
| Logging | Client-side tool call logs, stderr capture, endpoint process telemetry | Server-side call logs with token subject, client ID, method and tool name; gateway logs |
For stdio, the endpoint is the control plane: see the How to audit MCP servers on developer laptops guide and the AI Agent Sandboxing and Code Execution Guide. For Streamable HTTP, the server and the authorisation server are the control plane: see the Remote MCP Server Hardening Guide and the MCP Authorisation Specification Guide.
Where identity fits
The transport choice decides which identity the server acts as. A stdio server acts as the user: it borrows the user's operating system account and whatever credentials sit in the user's environment, so its activity in downstream logs is indistinguishable from the user's own. A remote server has two identities: the caller's, in an audience-bound token, and its own workload identity for upstream systems. That second identity is a non-human identity needing an owner, rotation and least privilege. Static API keys are the common shortcut on both transports and the common finding in audits; see API Keys vs OAuth for MCP Servers and the MCP Static Token Audit.
Decision criteria
No standard sets the line between the two. These criteria are practitioner judgement built on the specification's recommendations.
| Factor | Points to stdio | Points to Streamable HTTP |
|---|---|---|
| Who uses it | One user, one client, on their own machine | Many users, agents or pipelines |
| What it touches | Local files, local tools, the user's own repository | A shared system of record, SaaS tenant or database |
| Credentials | The user's own, already present on the device | Service credentials that should never sit on endpoints |
| Audit requirement | Endpoint telemetry is acceptable | Central, per-call evidence is required |
| Where the agent runs | Desktop or IDE on a managed device | Hosted agent platform, CI runner or cloud service |
| Endpoint control | Managed devices with sandboxing and client policy | Unmanaged or contractor devices you cannot sandbox |
In short: if the server needs data or credentials that should not live on laptops, or more than one caller will use it, host it on Streamable HTTP with per-user authorisation. If it is a single-user tool over local resources, use stdio, pinned and sandboxed. Remember that stdio traffic never crosses a gateway, so compliance needs for central evidence point to hosting.
Small teams usually do best with pinned, allowlisted stdio servers; larger estates host shared servers and keep stdio for personal tooling. See MCP Gateway vs Direct MCP Connections for the routing decision that follows.
Worked scenario
A software company runs three MCP servers: filesystem and git for IDE assistants, ticketing against its SaaS tracker, and a customer database for a support agent.
Filesystem and git: stdio. The platform team publishes it through the managed client configuration with an exact package version, launches it in a sandbox limited to the workspace directory with no network access, and passes no environment variables.
Ticketing: Streamable HTTP, hosted centrally. Each developer authorises once; the client obtains a token bound to https://mcp.example.internal/tickets through the resource parameter, and the server exchanges it for its own token to the tracker rather than passing the user token through. The initial scope is read only; write access is requested through an insufficient_scope challenge when a write tool is first used. Moving off a stdio version that used personal API keys removed long-lived keys from laptops.
Database: Streamable HTTP behind the company's gateway, with its own workload identity that has read access to a reporting replica only. The gateway routes on Mcp-Name and rejects older protocol versions; the server validates header and body agreement and logs token subject, client ID and tool name per call.
How to implement
- Inventory: list every server with transport, launch command or URL, version and credentials used. See the MCP Server Inventory Template.
- Fix local HTTP first: find servers listening on localhost ports; move them to stdio or add binding, Origin validation and a launch token.
- Lock down stdio: enforce a client allowlist, pin versions, turn off repository-supplied configuration, and pilot sandboxing on one team first. Roll back by restoring the previous configuration.
- Host shared servers: move servers holding shared credentials to Streamable HTTP with OAuth and audience binding. Run old and new side by side for a fixed window, then remove the stdio entry and revoke the personal keys it used.
- Test: for HTTP servers, send a request with a forged
Origin(expect 403), a token for another audience (expect 401), and a header and body mismatch (expect 400 with-32020). For stdio, confirm the sandbox blocks reads outside the workspace and outbound network calls. See the MCP Security Testing Guide.
Detection and measurement
- What to log for stdio: process creation where the parent is an MCP client or IDE, with full command line and package version; outbound network connections from those child processes; file reads outside the workspace.
- What to log for HTTP: 401, 403 and 400
-32020responses by client and source, token audience failures, requests with unexpectedOriginvalues, and scope step-up events.
Metrics:
- Percentage of stdio servers launched from a pinned version in managed configuration.
- Number of MCP servers listening on non-loopback interfaces (target zero) and on loopback without a token.
- Number of long-lived API keys in endpoint environments used by MCP servers, trending down.
- Percentage of remote servers rejecting foreign-audience tokens in testing.
- Time from a published server security update to the pinned version changing on all endpoints.
Common failure patterns
- Treating stdio as "safe because local": the absence of a network endpoint does nothing against a malicious package or a poisoned configuration file, as the postmark-mcp and Amazon Q cases show.
- Passing the whole shell environment: the commonest source of unnecessary credential exposure in stdio deployments.
- Forwarding the user's token to upstream APIs: explicitly forbidden by the specification and the quickest way to lose audit attribution.
- Moving to HTTP without per-user authorisation: a shared server with one service credential and no caller identity turns every user into the most privileged user.
Framework mapping
| Framework | Reference | How it applies |
|---|---|---|
| NIST CSF 2.0 | PR.PS-05 | Preventing unauthorised software: stdio server allowlists and pinning |
| NIST CSF 2.0 | PR.DS-02, PR.AA-05 | Data in transit for HTTP; least-privilege access for both transports |
| NIST SP 800-53 Rev 5 | CM-7, CM-11, SR-11 | Least functionality, user-installed software and component authenticity for stdio packages |
| NIST SP 800-53 Rev 5 | SC-7, SC-8, SC-23, IA-9 | Boundary protection, transport integrity, request authenticity and service authentication for HTTP |
| NIST SP 800-53 Rev 5 | AC-6, AU-12 | Least privilege for server identities and credentials; audit record generation on both |
| MITRE ATT&CK | T1195.002, T1552.001, T1059 | Supply chain, credentials in files and command execution through stdio servers |
| MITRE ATT&CK | T1528, T1550.001 | Token theft and replay against HTTP servers |
| MITRE ATLAS | AML.T0053, AML.T0098 | AI Agent Tool Invocation and AI Agent Tool Credential Harvesting, on either transport |
Practitioner checklist
- Every MCP server has a recorded transport, owner, version and credential source.
- No server meant for local use listens on a TCP port without a token, Origin validation and 127.0.0.1 binding.
- stdio servers run from exact pinned versions delivered through managed client configuration.
- Repository-supplied MCP configuration is disabled or gated by a consent prompt showing the full command.
- stdio servers receive only named environment variables, not the full shell environment.
- stdio servers run sandboxed, with filesystem and network limited to what the tool needs.
- Remote servers require an audience-bound bearer token on every request and reject all others.
- Remote servers use their own upstream credentials and never pass client tokens through.
- Servers validate header and body agreement, and gateways reject protocol versions without that validation.
Standards and references
- Model Context Protocol: Transports (revision 2026-07-28)
- Model Context Protocol: stdio transport
- Model Context Protocol: Streamable HTTP transport
- Model Context Protocol: Authorization
- Model Context Protocol: Authorization Security Considerations
- Model Context Protocol: Security Best Practices
- GitHub Security Advisory: MCP Inspector proxy server vulnerabilities (CVE-2025-49596)
Related NHI Mgmt Group resources: MCP Security Guide · Local MCP Server Security Guide · Remote MCP Server Hardening Guide · MCP SSE to Streamable HTTP Migration · Structured Outputs vs Free Text for Agent Actions