MCP.so
Sign In
Back to blog

MCP Security Best Practices: Threats, Fixes and a Checklist

Oct 15, 2026MCP.so TeamMCP.so Team

MCP security best practices from the official spec: prompt injection, tool poisoning, confused deputy, token passthrough, SSRF, local servers, plus a checklist.

MCP Security Best Practices in One Paragraph

MCP security best practices come down to three rules: treat everything a server sends (tool descriptions, tool results, OAuth URLs) as untrusted input, give every server and token the smallest possible privileges, and keep a human in the loop for anything that writes, sends or deletes. The official MCP Security Best Practices page and the authorization spec (current revision 2026-07-28) turn those rules into concrete MUST and SHOULD requirements. This guide maps each threat to the fix for server authors, client users and organizations, and ends with a checklist.

The Threat Map at a Glance

ThreatWhat goes wrongWho fixes it first
Prompt injection via tool outputA web page, issue or email returned by a tool carries instructions the model obeysClients and users
Tool poisoning and rug pullsHidden instructions sit in a tool description, or an approved tool changes laterClients and orgs
Confused deputyAn MCP proxy with a static OAuth client ID lets an attacker skip user consentServer authors
Token passthroughA server accepts or forwards a token that was not issued for itServer authors
Over-broad scopesOne stolen token unlocks every toolServer authors and authorization servers
Malicious local serversA one-click install runs an arbitrary command on your machineClients, users and orgs
SSRF during OAuth discoveryA malicious server points the client at internal URLsClient authors
State handle hijackingA guessed cart or workflow ID exposes another user's dataServer authors

The last six rows come straight from the spec's security page. The first two are not protocol bugs, which is exactly why they are dangerous: they exploit the model, not the wire format.

Prompt Injection via Tool Output

Every tool result ends up in the model's context. If a tool fetches a GitHub issue, a web page or an inbox, whoever wrote that content can try to steer the model, for example by asking it to read a private repo and post the contents somewhere public. Invariant Labs demonstrated exactly this pattern against an agent using the GitHub MCP integration in 2025.

The tools section of the spec is explicit about both sides:

  • Servers MUST validate all tool inputs, implement access controls, rate limit invocations and sanitize tool outputs.
  • Clients SHOULD prompt for confirmation on sensitive operations, show tool inputs to the user before calling the server, validate tool results before passing them to the model, set timeouts and log tool usage.
  • For trust and safety, there SHOULD always be a human in the loop who can deny tool invocations.

Practical mitigations:

  • Separate read and write servers. An agent that reads untrusted content (browsing, issues, email) should not hold a token that can push code or send mail in the same session.
  • Keep confirmations on for side effects. Auto-approve is fine for read-only tools; leave writes, deletes, payments and outbound messages behind a prompt.
  • Scope tokens to one repo or workspace. If the GitHub MCP Server only sees one repository, an injected instruction cannot reach the others.
  • Be wary of browser tools. A Playwright MCP Server session reads arbitrary pages, so run it in a profile without your logged-in sessions unless you need them.

Tool Poisoning, Shadowing and Rug Pulls

Tool descriptions are prompt text the model reads but the user usually does not. Invariant Labs' April 2025 disclosure described three variants:

  • Tool poisoning: a harmless-looking tool, such as an add function, hides instructions to read SSH keys or config files and smuggle them out through a parameter.
  • Shadowing: a malicious server's description changes how the model uses a trusted tool from another server, for example by silently redirecting email recipients.
  • Rug pull: a server you already approved changes its tool definitions later.

The spec's own position: clients MUST treat tool annotations (such as read-only or destructive hints) as untrusted unless they come from trusted servers. Mitigations:

  • Show the full description. Clients should display what the model sees, not a shortened label.
  • Pin versions and hash definitions. Pin the package version in your config (npx -y some-server@1.4.2, not @latest) and re-review when a tool definition's hash changes.
  • Namespace tools per server. The spec says clients that aggregate servers SHOULD disambiguate colliding tool names, for example with a server prefix.
  • Install less. Every connected server adds text to the model's context. Disconnect servers you are not using.

Confused Deputy (MCP Proxy Servers)

This applies if you build an MCP server that fronts a third-party API with one static OAuth client ID while letting MCP clients register dynamically. An attacker registers a client with their own redirect_uri, sends the victim a link, the third-party authorization server sees an existing consent cookie and skips the consent screen, and the authorization code lands on the attacker's server.

The spec requires MCP proxy servers to implement per-client consent before forwarding to the third-party authorization server:

  • Keep a per-user registry of approved client_id values and check it first.
  • Show an MCP-owned consent page naming the client, the third-party scopes and the exact redirect_uri, with CSRF protection and frame-ancestors or X-Frame-Options: DENY.
  • Consent cookies MUST use the __Host- prefix, Secure, HttpOnly, SameSite=Lax, be signed, and be bound to a specific client_id.
  • Validate redirect_uri with exact string matching.
  • Generate a single-use, short-lived state value and store it only after consent is approved.

Token Passthrough and Audience Validation

Token passthrough means a server accepts a token from the client without checking it was issued for that server, then forwards it downstream. The spec forbids it outright: MCP servers MUST NOT accept any tokens that were not explicitly issued for them. It breaks rate limiting and audit trails, and turns one stolen token into access across services.

For server authors:

  • Validate the audience of every token (the aud claim or introspection result) against your server's canonical URI.
  • If you call an upstream API, obtain a separate upstream token. Never forward the client's token.
  • Pin to one issuer or tenant unless you are explicitly multi-tenant.

The full flow, including resource indicators that bind tokens to one server, is covered in our MCP authentication and OAuth guide.

Over-Broad Scopes

A token granted files:*, db:* and admin:* up front is a large blast radius when it leaks from a log. The spec's scope minimization guidance:

  • Start with a minimal scope set, such as mcp:tools-basic, covering low-risk read operations.
  • Ask for more with a targeted WWW-Authenticate challenge (403 with error="insufficient_scope") when a privileged tool is first used.
  • Avoid wildcard or omnibus scopes like *, all or full-access, and do not publish every possible scope in scopes_supported.
  • Check permissions server-side per tool; a scope claim alone is not authorization logic.

For users, the same idea applies to API keys: create a key per server with read-only permission when that is all the workflow needs.

Malicious or Compromised Local Servers

A local (stdio) server is a program running with your user's privileges. The spec's examples of a malicious startup command include piping ~/.ssh/id_rsa to a remote URL. If a client supports one-click local server installs, it MUST show the exact, untruncated command, flag it as code execution, and require explicit approval. Clients SHOULD highlight risky patterns (sudo, rm -rf, network calls) and run servers sandboxed with minimal default privileges.

What you can do today:

  • Read the command before you approve it. Prefer servers from a known publisher and check the repository behind any listing, including entries on mcp.so.
  • Limit filesystem reach. The Filesystem MCP Server takes allowed directories as arguments; pass a project folder, never your home directory.
  • Containerize. Running servers in containers, for example through the Docker MCP Gateway, limits what a compromised server can touch.
  • Server authors: prefer stdio for local servers. If you must listen on HTTP locally, require an authorization token or use a Unix domain socket, since DNS rebinding can reach unprotected localhost ports.

SSRF and Malicious OAuth URLs

During OAuth discovery the client fetches URLs that the server controls: the resource_metadata URL, the authorization_servers list and the endpoints in authorization server metadata. A malicious server can point those at 169.254.169.254 (cloud metadata), localhost:6379 or private ranges. Client authors SHOULD require HTTPS (allowing http only for loopback in development), block private and link-local IP ranges, validate every redirect hop, and consider an egress proxy. The spec warns against hand-rolled IP parsing because of octal, hex and IPv4-mapped IPv6 tricks.

The authorization URL itself is also input. Clients MUST allow only http and https schemes, reject javascript:, data: and file:, and MUST NOT open URLs through a shell such as cmd.exe or sh.

State Handles and Mix-Up Attacks

The 2026-07-28 revision is stateless: there is no protocol session, so servers that keep state mint a handle (a cart ID, a workflow ID) and receive it as a tool argument. Servers MUST NOT treat possession of a handle as authentication. Generate handles with a secure random generator, expire them, and bind them to the user ID taken from the verified token. Clients talking to many authorization servers MUST record the expected issuer and validate the iss parameter in the authorization response (RFC 9207) before redeeming a code, which defeats mix-up attacks that PKCE alone does not stop.

The MCP Security Checklist

Server authors

  • Validate and sanitize all tool inputs and outputs; rate limit tool calls.
  • Implement OAuth per the spec for remote HTTP servers; never accept or forward tokens not issued for you.
  • Design minimal scopes and step-up challenges; enforce authorization per tool.
  • Proxies: per-client consent, exact redirect_uri matching, single-use state.
  • Use unguessable, user-bound state handles; never authenticate by handle.
  • Keep tool descriptions short, honest and free of instructions to the model; version changes visibly.

Client users

  • Install servers from known publishers, pin versions, read the launch command.
  • Scope API keys and OAuth grants to the minimum; one key per server.
  • Keep confirmations on for writes, deletes and outbound messages.
  • Do not mix untrusted-content tools and high-privilege tools in one session when you can avoid it.
  • Disconnect servers you no longer use; rotate keys you pasted into configs.

Organizations

  • Maintain an allowlist of approved servers and versions; review changes to tool definitions.
  • Run local servers in containers or sandboxes with restricted filesystem and network access.
  • Store credentials in a secret manager and inject them as environment variables, not committed config files.
  • Log tool invocations centrally and alert on unusual volume or destinations.
  • Test servers with the MCP Inspector before rollout, and check which clients your teams use support OAuth.

Next Steps

Start with the servers you already run: confirm where each one's credentials live, what scopes they carry, and whether writes need confirmation. Then browse the MCP server directory for official, publisher-maintained alternatives, and read how MCP authentication works if you are building a remote server.

Frequently asked questions

Is MCP secure?

The protocol itself defines security requirements such as OAuth 2.1 for remote servers, token audience validation and user consent, but safety depends on implementation. Most real risks come from untrusted servers, prompt injection through tool output and over-privileged tokens, so vet servers and keep a human approving sensitive actions.

What is MCP tool poisoning?

Tool poisoning is when a malicious MCP server hides instructions in a tool description that the model reads but the user usually does not see. A related rug pull happens when an approved server later changes its tool definitions, so pin versions and re-review tool definitions when they change.

What is token passthrough in MCP?

Token passthrough is an anti-pattern where an MCP server accepts a token that was not issued for it and forwards it to a downstream API. The MCP spec forbids it: servers must validate that tokens were issued for them and must obtain separate tokens for upstream APIs.

Are local stdio MCP servers safe to run?

A local stdio server runs with your user's privileges, so it is as safe as the code and command you approve. Install servers from known publishers, read the exact launch command, pin versions and run them in a container or sandbox with limited file and network access.