Build a secure MCP server by accepting only tokens issued for it, never passing client tokens to other services, requesting the smallest scopes, never using sessions for authentication, and sandboxing anything that runs locally. The protocol's own security best practices page covers each of these.
What is the threat model?
The Model Context Protocol connects AI clients to servers that expose tools and data. A server holds credentials and does real work, so a mistake can give an attacker the rights of a user or the host. The specification's security best practices document, written for implementers of authorization flows, server operators and evaluators, reads like a checklist of mistakes to avoid. It should be read next to the authorization specification and OAuth 2.0 security best practices.
What does the specification warn about?
Confused deputy. An MCP proxy server that talks to a third party API with a static client ID, allows dynamic client registration and relies on a consent cookie can be tricked into handing an authorization code to an attacker. The mitigation: keep a per client consent registry, show a consent page that names the client, scopes and redirect address, use exact redirect URI matching, protect the state parameter, and secure consent cookies.
Token passthrough. The document calls it an anti pattern: accepting a token from a client without checking it was issued to your server, then forwarding it downstream. It breaks rate limits and audit trails, blurs identity in downstream logs and widens trust. The rule is unambiguous: MCP servers must not accept tokens that were not explicitly issued for them.
Server side request forgery. During metadata discovery a client fetches URLs a malicious server controls, which may point at internal addresses or cloud metadata endpoints. Clients should require HTTPS, block private and link local ranges, validate redirects, and consider an egress proxy. The page warns against hand written IP validation because of encoding tricks, and notes DNS rebinding.
Session hijacking. Servers that authorize must verify every inbound request and must not use sessions for authentication. Session IDs must be secure and non deterministic, and should be bound to user specific information.
Local server compromise. Local servers run with the client's privileges. A client that offers one click setup must show the exact command and require approval. Sandboxing and restricted file and network access are recommended, and stdio transport limits access to the client.
Authorization URL validation. Clients must accept only http and https, reject schemes like javascript, and must not open URLs through a shell.
Scope minimization. Do not publish every scope or use wildcard scopes. Begin with a minimal set and escalate only when a privileged operation is first attempted.
Constructed example: the file tool
A team builds an MCP server with a tool that reads files for an assistant. First version: one scope, full file system access, token forwarded to an internal API. Reviewed against the guidance, it changes. The scope splits into a read only discovery scope and a separate write scope requested on demand. The server validates that every token carries its own audience. The file tool is restricted to one directory. The local version runs in a container with no network. The assistant now does the same work, and a stolen token reaches far less.
The server is imagined for illustration.
What about the tools themselves?
Each tool is an interface that a model, which can be misled by text it reads, will call. Treat arguments as untrusted input, validate them, and apply limits on the server side such as allowlisted paths and spending caps. Log every call with the identity it was made under.
Safeguard runs its own MCP server at mcp.safeguard.sh, and MCP Guardrail is a policy layer for controlling which tools agents may call. Whatever you use, enforce the limits outside the model.
How to check or apply this
- Validate the audience and signature of every token and reject tokens issued for another service.
- Do not forward client tokens. Use a separate credential for downstream calls.
- Publish minimal scopes and escalate on demand.
- Use secure random session IDs and authorize every request without trusting sessions.
- Block private and link local ranges in any code that fetches URLs.
- Run local servers in a sandbox with narrow file and network access.
- Validate all tool arguments and log calls with the caller's identity.
Quick check
Why is token passthrough forbidden?
It lets clients bypass controls that depend on the token's audience, makes audit trails unreliable and widens the trust boundary. Servers should accept only tokens issued for them.
Is a local stdio server automatically safe?
No. It limits who can connect, but the server still runs with the user's privileges. Sandbox it and review what it does.
Sources
Last reviewed: 18 October 2026.