Scope: this guide is about servers running on a machine you (or your
organization’s users) control — a laptop or workstation, typically launched
over stdio. Deploying and hardening remote or hosted servers is a different
topic with different controls.
What a Local Server Can Touch
A local MCP server is not a plugin running inside a sandbox the protocol provides. It is an ordinary operating system process, launched by your host application, with all the access that implies: Clients that offer one-click installation are required to show you the exact command before it runs (SEP-1024), but a consent dialog only helps if you know what to look for. That is what the rest of this guide is about.The stdio Trust Model
Most local servers use the stdio transport: your client spawns the server as a subprocess, and the two communicate over stdin and stdout. That design carries an important assumption: the client and a stdio server share one trust domain. They run side by side with the same privileges, and neither the protocol nor the SDKs defend one against the other — a server already has code execution on your machine by virtue of being launched, so there is no boundary between the two processes for a transport to enforce. This is by design: the SDKs assume full trust between stdio peers, and any isolation or security fencing around a local server is the responsibility of the host application and of whoever operates the machine. Two practical consequences:- Your real security decisions happen before launch: what you install (provenance) and how you run it (isolation). The stdio transport is not a sandbox.
- If you run a server at reduced privilege, that boundary is yours to enforce — a container, sandbox, or VM, as described below.
Threats This Guide Addresses
- A malicious server. Typosquatted package names, lookalike repositories, or an abandoned project taken over by a new publisher. The “server” works as advertised while also exfiltrating your environment or files.
- An overcollecting server. A legitimate server that collects more than its job requires: environment variables, files, or network traffic it has no reason to touch. Prompt injection in untrusted content can steer an honest server into the same overreach, using whatever access it was given.
- A compromised dependency. The server author is honest, but one of the packages in its dependency tree was compromised in a supply chain attack.
- A poisoned tool catalog. Tool names and descriptions are instructions your model reads. A server can hide directives in them that steer the agent — including how it uses other servers’ tools — without the poisoned tool ever being invoked (often called tool poisoning, or tool shadowing when it targets another server’s tools). Definitions are not fixed at install time either: a server can quietly change them after you approved it (a rug pull), so a one-time consent dialog and package pinning do not cover this surface.
- A vulnerable server or bridge. A legitimate, unmodified component can still be the way in. Local bridges that connect stdio clients to remote servers have had critical flaws triggered by nothing more than connecting to a malicious endpoint, and local developer tools listening on a port have been exploited directly from a web page.
Ask Whether It Needs to Run Locally
The cheapest way to shrink a blast radius is not to create one. Before you install a server as a local process, ask what it actually needs from your machine. Reading your working tree, driving a local tool, or reaching hardware are reasons to run locally. Wrapping a remote API is not: a server that only talks to a service on the internet gains nothing from running on your laptop, while you take on everything else in this guide for no benefit.- Prefer the remote version where one exists. A server offered over Streamable HTTP runs no code on your machine, uses the protocol’s authorization flow instead of a token in a local config file, and is patched in one place. The protocol threat model (linked above) applies to it instead of this one.
- Treat an unnecessary local install as a signal. When a third-party server needs no local access yet ships only as a stdio process, ask why. It may just be the author’s path of least resistance, but it is also exactly how a malicious or overcollecting server gets onto your machine.
- Keep the genuine exceptions. Some capabilities only exist as a local process. For those, the rest of this guide applies in full.
Check Provenance Before You Install
A listing on a registry is not an endorsement. Docker Hub, ghcr.io, npm, PyPI, and NuGet will host almost anything, and a package being popular or well-named guarantees neither that it is safe nor that it is current. Even curated MCP catalogs describe their own vetting as best-effort: curation reduces risk, it does not take on accountability for what you run. Before installing a server:- Identify the publisher. Can you tie the package to a source repository and a real maintainer or organization? Does the repository show recent, plausible activity?
- Verify the exact package name. Typosquatting is the cheapest attack available. Confirm you are installing the package the documentation actually refers to, not a lookalike.
- Pin the version. Install a specific version rather than
latest, so a compromised future release does not walk onto your machine through an auto-update. Pair the pin with a deliberate update habit: when a security advisory lands for something you pinned, the pin is what keeps you vulnerable. - Know what the pin covers. Pinning a package version with a launcher
such as
npxoruvxfixes only the top-level package; its transitive dependencies are typically resolved fresh at install time, which is exactly where a compromised dependency arrives. Pinning a container image by digest (image@sha256:...) freezes the entire dependency tree. - Prefer artifacts with provenance metadata. Signed artifacts, build attestations, and SBOMs (software bills of materials) let you verify who built the artifact and what is inside it — including which MCP SDK and version it uses, which matters when a vulnerability is announced in one.
Treat Tool Definitions as Untrusted Input
Installing a server is not the last trust decision. At runtime, the server sends your client its tool names and descriptions, and the model reads them as instructions — an injection surface that install-time review does not cover.- Read the tool list before you rely on a server. Most clients can show each tool’s description, and the MCP Inspector lists every tool a server exposes without a model in the loop. If a description contains instructions that have nothing to do with the tool’s job, remove the server.
- Prefer clients that pin tool definitions. Some clients record tool definitions when you first approve a server and ask again when they change. The protocol does not require this, so it is a property worth choosing a client for.
- Keep the roster small. Because all configured servers share the model’s context, every server you add extends the surface the others are exposed to.
Run Servers in an Isolated Environment
Process isolation is the single highest-leverage mitigation in this guide. It turns “this server can read my SSH keys” into “this server can read an empty directory”, and it protects you even when provenance checks fail — against all the threats above at once.- Baseline: run third-party servers inside a container (for example with Docker or Podman) with no volume mounts beyond what the server needs. Stdio transport works unchanged: the client talks to the container’s stdin and stdout exactly as it would to a bare process.
- Alternatives: a lightweight virtual machine dedicated to development work, or your operating system’s process-sandboxing facilities. Some MCP-focused runtimes and client integrations will wrap servers in containers for you; the pattern matters more than the tool.
Isolate Credentials
A locally launched server may inherit the environment of whatever launched it. If your shell profile exports cloud credentials and API tokens globally, every server you run can potentially read all of them.- Pass secrets per server, not globally. Use the per-server environment configuration in your client’s config file rather than exporting secrets in your shell profile, so each server sees only its own credentials.
- Scope tokens narrowly. Issue each server its own token with the smallest scope that works (for example a fine-grained access token limited to one repository, not your account-wide token), so a leak is contained and revocable.
- Keep long-lived secrets out of plaintext config. Client configuration files are a known, predictable location on disk. Prefer your operating system’s secret store (such as macOS Keychain, Windows Credential Manager, or the freedesktop Secret Service on Linux) where your client supports referencing it.
- Remember files count too.
~/.ssh,~/.aws, and similar directories are credentials on disk. Process isolation (above) is what actually keeps them out of a server’s reach.
Limit Filesystem Access
Many useful servers legitimately need file access. The question is how much.- Grant the narrowest root that works. Configure filesystem-oriented servers with a specific project directory, passed explicitly through the server’s configuration or tool parameters — never your home directory.
- Prefer read-only. If the server’s job is to read code or documents, mount or grant the directory read-only.
- Be suspicious of breadth. A server that asks for a broad root directory to do a narrow job is overreaching. Grant a narrower directory, or pick a different server.
Control Network Egress
Exfiltration needs an exit. A local process normally has unrestricted outbound access, which turns any overcollection or compromise into data loss.- Know where a server should be talking. A server that wraps one API has a short, predictable set of destinations. Traffic anywhere else is a signal worth investigating.
- Deny network where none is needed. Servers that operate purely on
local files or tools need no network at all — with containers this is one
flag (
--network=none). - Filter what remains. For servers that do need egress, an outbound-filtering firewall or an egress proxy with a domain allowlist restricts them to their expected destinations and logs the attempts that fall outside it.
Egress is not the only network concern. Local MCP plumbing that listens on a
port — bridges to remote servers, development tools, locally hosted HTTP
servers — is reachable by other local processes and, through your browser, by
websites. Binding to 127.0.0.1 is not an authentication boundary: such
components need real authentication and origin validation, and they need
prompt updates when advisories land.
Managing Local Servers Across an Organization
If you are an IT, security, or platform administrator, the sections above are your building blocks, but your problem is different: many machines, many users, servers installed without your involvement. Two facts shape everything:- Local MCP servers are ordinary software installed by your users. The protocol cannot inventory, patch, or contain processes on machines it does not manage. This is endpoint and software governance — your existing responsibility, with a new package type.
- You will not be asked first. Assume users install servers the same way they install editor extensions.
- Define approved sources. Publish a short allowlist of vetted servers (with pinned versions) and the criteria a new server must meet to join it: identifiable publisher, readable source, provenance metadata, an SBOM. Make the approved path easier than the unapproved one.
- Set a default runtime posture. Decide and document how servers run on managed machines — for example, “third-party servers run in containers with no home-directory mounts” — rather than leaving isolation to each user’s initiative.
- Deploy centrally where local access is not required. The question from the top of this guide applies fleet-wide: a server that only wraps a SaaS API does not need to run on every laptop. A centrally managed remote deployment gives you one instance to patch, audit, and control instead of hundreds.
- Inventory continuously. Client configuration files live in known locations on each platform. Your endpoint management tooling can collect them, giving you a live answer to “who is running what, at which version, from which source.”
- Keep an audit trail. Where your tooling supports it, log MCP activity on managed machines — which server, which tool, when — so an incident review can reconstruct what a compromised or overreaching server actually did instead of guessing from its permissions.
- Plan for the fleet incident. When a widely used local server (or the SDK inside it) turns out to be vulnerable, you need to: find every instance (the inventory above), block its egress at the proxy or firewall while you respond, rotate every credential those configs passed to it, and push the fixed, pinned version through your approved channel. SBOMs are what let you answer “which of our machines run something built on the affected SDK version” in minutes instead of days.
- Mind your regulatory context. Software supply chain regulation (for example the EU Cyber Resilience Act) increasingly expects exactly these practices — SBOMs, update processes, and vulnerability handling — so the work above often does double duty.
Checklist
For your own machine:- I know every server in my client’s configuration, and I removed the ones I don’t use.
- Servers that need nothing from my machine run remotely, not as a local process.
- Each server came from an identifiable publisher, and its version is pinned.
- Third-party servers run isolated (container, sandbox, or VM), not as bare processes.
- Each server gets only its own narrowly scoped credentials via per-server config — nothing inherited from my shell, nothing reused.
- Filesystem access is limited to specific directories, read-only where possible.
- Servers without a network need run with egress disabled; the rest have a known list of destinations.
- I have skimmed each server’s tool descriptions, and my client re-prompts when a tool definition changes.
- We maintain an allowlist of approved servers with published acceptance criteria.
- We continuously inventory MCP client configurations on managed machines.
- We have a documented default isolation posture for local servers.
- We keep audit logs of MCP activity on managed machines.
- We can block a specific server’s egress and rotate its credentials across the fleet.
Next Steps
Security Best Practices
The protocol threat model: confused deputy, token passthrough, and session
hijacking
Authorization
How OAuth-based authorization works for remote MCP servers
SEP-1024
Client security requirements for local server installation consent
Connect to local servers
Set up local servers in your client — the workflow this guide secures