Blog ·

How to secure MCP servers: a guide for the teams that build and run them

How to secure MCP servers from the builder side: the seven decisions that close the gaps attackers use, each tied to the MCP spec or OWASP guidance.

How to secure MCP servers comes down to seven decisions you make as the team that builds or runs them: pick the narrowest transport, validate every tool input, give each tool the least access it needs, run the server in a sandbox, keep secrets out of config and logs, rate limit calls, and publish releases in a way others can verify. The MCP specification makes several of these mandatory; its tools section says servers must validate inputs, enforce access controls, rate limit invocations and sanitize outputs. This guide walks through each one from the operator's side, with the requirement it comes from.

Who this guide is for

Most writing on MCP security is for the people who install servers. Our MCP security checklist covers that side: what to check before an agent loads someone else's server. This guide is for the other side, the teams who write an MCP server for their product, run one for internal tools, or publish one for others to use. The attacks are the same ones described in our overview of MCP server security risks. The difference is that here you control the code, so you can close the gap at the source instead of hoping every user catches it.

How to secure MCP servers at the transport layer

The transport decides who can talk to your server. For a server that runs on the user's machine and serves one client, use stdio. The security best practices page recommends it for local servers because only the client that started the process can reach it. If a local server must use HTTP, require an authorization token or use a Unix domain socket or other restricted IPC, since an open localhost port can be reached by other processes and by web pages through DNS rebinding.

Remote servers use HTTP, and that is where authorization comes in. The specification says HTTP-based servers that support authorization should follow its OAuth 2.1 profile, and that stdio servers should not, reading credentials from the environment instead. Our guide to MCP server authentication covers that profile in full. Whatever you choose, serve every remote endpoint over HTTPS.

Validate every input and sanitize every output

A tool's arguments are written by a language model, and that model may have just read a web page, an email or a file an attacker controls. The OWASP MCP Security Cheat Sheet says to treat all tool inputs as untrusted for exactly this reason. In practice that means strict schemas: set additionalProperties: false, add a pattern to string fields, and reject anything that does not match rather than trying to repair it.

Then guard the dangerous sinks. Never build a shell command from an argument. Resolve file paths and refuse anything outside the directory the tool is meant to use, which stops path traversal. Use parameterized queries for databases. If a tool fetches a URL the model supplied, check it against a strict allowlist, or a malicious prompt can point your server at internal addresses. On the way out, sanitize results before they reach the model, because text your tool returns becomes instructions another tool may act on. That second path is how an MCP tool poisoning attack can travel through a server that is not itself malicious.

Give each tool the least access it needs

Scope is the difference between a bug and a breach. Give the server its own credentials and never share a token across servers, as OWASP recommends. Request narrow scopes, read-only where the tool only reads. Prefer short-lived tokens over long-lived personal access tokens. Check authorization per tool at the moment it runs, not once at connection.

Split tools by risk. A server that offers both a harmless search and a write to production is one prompt injection away from the write. Put sensitive tools, such as anything touching payments, identity or personal data, in their own server with their own credentials, so that compromising the general-purpose server does not reach them. The specification's scope minimization guidance applies the same idea to OAuth: start with a minimal scope set and elevate only when a privileged tool is first called.

Run the server in a sandbox

A local MCP server runs with the user's permissions unless you do something about it. The security best practices page lists arbitrary code execution and data exfiltration among the risks and recommends running servers in a sandboxed environment with minimal default privileges. OWASP adds that a bare chroot is not a real sandbox and that stdio, while it avoids a listening port, does nothing to limit file, network or credential access on its own.

For servers you ship, publish a container image that mounts only the directories the server needs, turns network off unless a tool requires it, and runs as a non-root user. For servers you run centrally, apply the same limits in your orchestration platform. Document the permissions your server needs, so the people installing it can grant exactly those. A one-line statement such as "reads one project folder, no network" in your README does more for adoption by security-conscious teams than any badge, because it is the first thing a reviewer looks for.

Keep secrets out of config, code and logs

Store OAuth tokens in the operating system's credential store, such as Keychain or Windows Credential Manager, not in a plaintext config file, as OWASP advises. For API keys, read them from environment variables and say so in your README, so users do not paste keys into a mcp.json that later gets committed. Validate that every token you receive was issued for your server, and never forward a token you received to an upstream API; the authorization specification forbids that token passthrough outright. Finally, keep secrets out of your logs, a topic our guide to MCP server logging best practices covers in detail.

Rate limit, time out and pin what you depend on

The tools specification requires servers to rate limit tool invocations. Set limits, quotas and timeouts per user or tenant, so one runaway agent loop cannot exhaust your server or the API behind it. Then look at your own supply chain. Pin dependency versions, scan them for known vulnerabilities, and check package names carefully for typosquats. OWASP also recommends hashing full tool definitions, including schemas and annotations, so that a change to what a tool claims to do is visible instead of silent.

Publish in a way users can verify

The first malicious MCP server found in the wild, postmark-mcp, worked by publishing under a familiar brand name on npm with no connection to the brand. You cannot stop impersonators, but you can make the real thing easy to tell apart. Publish to the official MCP Registry, which requires proof that you own the namespace: a GitHub account for names like io.github.yourorg/server, or a DNS or HTTP proof for names under your own domain. On npm, publish from CI with provenance, which links each release to the source and build that produced it and lets users check it with npm audit signatures. Require two-factor authentication on every publishing account, and link your official package from your own website.

Before each release, test the server the way an attacker would: send arguments that break your schema, paths that climb out of the allowed folder, and URLs pointing at internal addresses, and confirm each one is rejected. Then read your tool descriptions as the model will, and remove anything that reads like an instruction rather than a description.

Next step: check how to secure MCP servers against what users see

Once you know how to secure MCP servers on your side, check how your server looks to the teams deciding whether to install it. Look it up on the NoFly list to see what a published risk score says about what it reaches and does, or scan every MCP server your organization runs in one pass.

Questions about securing MCP servers

How to secure MCP servers that run locally over stdio?

Run them in a sandbox or container with access only to the folders and network they need, and read credentials from the environment rather than a config file. The MCP specification says stdio servers should get credentials from the environment instead of using its OAuth flow, and OWASP notes that stdio alone does not limit file, network or credential access.

Do MCP servers have to validate tool inputs?

Yes. The MCP tools specification says servers must validate all tool inputs, implement access controls, rate limit tool invocations and sanitize tool outputs. Inputs come from a model that may have read malicious content, so treat every argument as untrusted.

Should every MCP server require authentication?

Any server reachable over HTTP should. Authorization is optional in the MCP specification, but a remote server without it is open to anyone who finds the address, and a local HTTP server without a token can be reached by other processes on the machine.

How do we stop someone impersonating our MCP server?

Publish under a namespace you can prove you own. The official MCP Registry ties server names to a verified GitHub account or domain, and npm provenance links each release to the source and build that produced it.

Sources

  1. Model Context Protocol: Tools specification, security considerations
  2. Model Context Protocol: Security best practices
  3. Model Context Protocol: Authorization (2025-11-25)
  4. OWASP Cheat Sheet Series: MCP Security
  5. Model Context Protocol: Authenticating when publishing to the MCP Registry
  6. npm Docs: Generating provenance statements
  7. The Register: postmark-mcp server code hijacked (Sept 29, 2025)

Related reading

See how your MCP servers look from the outside.