Home › Learn › Remote vs local MCP servers
Remote vs local MCP servers
Updated 2026-09-23
Every MCP server you install is one of two things. A local server is a process your client spawns on your machine and talks to over stdin and stdout. A remote server is an HTTP endpoint you send requests to. The protocol semantics are identical; the operational and security consequences are not.
What "local" means in practice
With the stdio transport the client launches the server as a subprocess and exchanges newline-delimited JSON-RPC over the standard streams. That is what a configuration line like npx -y some-mcp-server or uvx some-mcp-server does: it downloads a package and runs it as you.
Consequences:
- Data stays on the machine unless the server itself makes network calls.
- The server has your privileges. The MCP security best-practices document is direct about this: local servers "may have direct access to the user's system", and a malicious package or startup command means "arbitrary code execution" with the client's permissions. The same page recommends that clients show the exact command before running it, and that local servers use stdio (rather than opening an HTTP port) to limit access to just the MCP client.
- Secrets travel as environment variables. Claude Code's
--env KEY=value, Cursor'senvandenvFile, and Codex'senvandenv_varsall pass credentials to the process. The value is visible to that process and to anything that can read its environment. - Versioning is on you. Each launch of
npx -y packagecan resolve to a newer release unless you pin a version.
A server that also listens on a local HTTP port is a hybrid: it runs on your machine but is reachable by other software. The Streamable HTTP spec says such servers should bind to 127.0.0.1, must validate the Origin header, and should authenticate connections, because otherwise a web page can reach a local MCP server through DNS rebinding.
What "remote" means in practice
A remote server exposes a single MCP endpoint over HTTPS, for example https://example.com/mcp. Each request is its own HTTP POST and the response is JSON or a request-scoped SSE stream. Nothing runs on your machine except the client.
Consequences:
- Whatever you send leaves your machine. Tool arguments, resource requests and any context the host chooses to include go to the operator of that endpoint. The spec's principle that hosts must obtain consent before exposing user data to servers applies with more force here.
- You need to authenticate. The Registry's
server.jsonformat records this on theremotesentry:headerswithisRequiredandisSecretfor API-key style auth. Otherwise the endpoint uses OAuth, which the MCP authorization specification defines for HTTP transports. - Availability is the operator's problem, and yours. A remote server can be down, rate-limited, or change behaviour without a version bump on your side.
- Multi-user by design. The spec describes a Streamable HTTP server as an independent process that can handle multiple client connections; one deployment serves a team.
How each client attaches credentials to a remote server
The three clients covered on mcpnav each let you send headers without writing the secret into a shared file.
Claude Code: claude mcp add --transport http <name> <url> --header "Authorization: Bearer your-token", or headers in .mcp.json with ${VAR} expansion. Claude Code reads certain credential variables (its own ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN, cloud-provider tokens, NPM_TOKEN) as empty in a remote server's url and headers, so a repository's config cannot forward your Claude credentials to a third party. OAuth sign-in runs through /mcp or claude mcp login <name>.
Cursor: a headers object on the url entry, with ${env:NAME} interpolation, for example "Authorization": "Bearer ${env:MY_SERVICE_TOKEN}". OAuth is supported, with an optional auth block for static client credentials.
Codex: bearer_token_env_var = "NAME" sends the named variable's value in Authorization; http_headers holds static headers, env_http_headers maps header names to variable names, and http_headers_helper runs a command that prints headers. OAuth is supported with CIMD and DCR.
In all three, the pattern is the same: the config names a variable, the shell holds the value.
Rules on the server side that affect you as a user
Two requirements from the MCP security document explain why a well-run remote server will refuse some things you might expect it to accept:
- No token passthrough. A server must not accept tokens that were not issued for it and forward them to a downstream API. So you cannot hand a remote MCP server your GitHub token and expect it to act as a proxy; it should run its own authorization.
- No sessions as authentication. Servers that implement authorization must verify every inbound request. A session identifier is not a credential.
The same document recommends scope minimisation: request a minimal scope up front and elevate only when a privileged tool is first used. When a remote server asks for broad scopes at sign-in, that is a signal to look more closely.
Choosing
Pick a local server when the data is on your machine, when you need the server to see local files or processes, or when the data is sensitive enough that you do not want it leaving the host. Then treat the package as code you are executing: pin it, read it, and give it only the keys it needs.
Pick a remote server when the capability lives in a SaaS product, or when several people need the same integration. Then treat the endpoint as a third party you are sending data to: check who operates it, use OAuth or a scoped API key, and keep the key in an environment variable.
mcpnav lists both kinds. Server pages built from Registry data show a packages block for local installs and a remotes block for endpoints, and the Connect block prints the matching command with <YOUR_VALUE> placeholders for every secret header or variable.
Sources
- stdio transport: https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/stdio
- Streamable HTTP transport, including the Security & Endpoint section: https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http
- Authorization specification: https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization
- Security Best Practices (token passthrough, session hijacking, local server compromise, scope minimization): https://modelcontextprotocol.io/docs/2026-07-28/tutorials/security/security_best_practices
- MCP Registry, "Publishing Remote Servers": https://github.com/modelcontextprotocol/registry/blob/main/docs/modelcontextprotocol-io/remote-servers.mdx
- Claude Code MCP docs: https://code.claude.com/docs/en/mcp
- Cursor MCP docs: https://cursor.com/docs/mcp
- Codex MCP docs: https://developers.openai.com/codex/mcp
All pages accessed 2026-09-23.
Sources
- https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/stdio
- https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http
- https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization
- https://modelcontextprotocol.io/docs/2026-07-28/tutorials/security/security_best_practices
- https://github.com/modelcontextprotocol/registry/blob/main/docs/modelcontextprotocol-io/remote-servers.mdx
- https://code.claude.com/docs/en/mcp
- https://cursor.com/docs/mcp
- https://developers.openai.com/codex/mcp