MCP security

MCP server security best practices

An MCP server turns a model’s decision into a real API request. This checklist covers what the specification requires of that server and what teams usually add for production.

Written by Octri · Checked 3 November 2026

Identity and tokens

A remote server is an OAuth resource server. The specification makes authorization optional for MCP as a whole, and any server that holds or reaches private data needs it. A server over stdio takes its credentials from the environment and skips the OAuth flow.

  • Publish Protected Resource Metadata (RFC 9728) so clients can find your authorization server
  • Answer a request with no token with 401 and a WWW-Authenticate challenge
  • Validate every token on every request, including its audience
  • Send access tokens in the Authorization header, and keep them out of query strings
  • Treat a state handle as data: possession of one never proves who is calling

Least privilege

A smaller tool list is a security control and a quality improvement at once. Each tool you leave out is an action no prompt can trigger, and the model has fewer options to confuse.

The specification recommends the same shape for scopes. Start from a minimal set for reads and discovery, then ask for more with a targeted WWW-Authenticate challenge when a privileged tool is first called.

  • Remove admin, billing and destructive endpoints from the tool list unless a workflow needs them
  • Avoid wildcard scopes such as * or full-access
  • Check authorization on the server for each call, whatever scopes a token claims
  • Give separate jobs separate servers or credentials

Approval for writes

The specification says there should always be a human in the loop with the ability to deny a tool call, and that clients should show tool inputs before sending them. Approval works best when the prompt shows the exact request, because a person can judge a method, a path and a body.

Tool annotations such as a read-only hint help a client decide when to ask. Clients must treat those hints as untrusted unless the server is trusted, so your server still enforces its own rules.

Limits and audit logs

Rate limiting tool calls and validating tool inputs are both requirements on the server in the specification. Agents can retry and fan out quickly, so per-tool limits and a cap on writes keep a bad loop small.

An audit log answers the question that follows any incident: who asked for what, through which client, and what happened. The guide on audit logs and rate limits lists the fields worth keeping.

Untrusted content and outbound requests

Everything a tool returns reaches the model, including text your own customers wrote. Sanitizing tool outputs is a server requirement in the specification. Credentials echoed in a response are the first thing to remove.

Servers that fetch URLs need an egress fence as well. Block private and link-local addresses, including cloud metadata endpoints, and apply the same check to each redirect.

How Octri handles this

Each tool is set to run on its own, ask first or stay off. Reads default to running and anything that changes data defaults to asking, with the client showing the exact request.

An Off tool is never listed and cannot be called, and nothing set on a local copy brings it back.

Sign-in comes in three forms: your own authorization server, Octri signing people in to your OAuth, or a key page for API-key and basic-auth APIs.

Writes per session and calls per minute per tool can be capped on every plan.

Credentials echoed in API responses are removed before the agent sees them, and the hosted server calls public addresses only.

Questions

No. The specification says stdio implementations should take credentials from the environment. A local server that listens over HTTP is a different case, and it should require a token or use a restricted channel such as a unix domain socket.

Try it on your own spec

Generate an MCP server from your OpenAPI spec on the Free plan, with every tool set to run, ask first or stay off.