MCP authentication patterns: OAuth, API keys, and headers

An MCP server is only as useful as the APIs it can reach, and every one of those APIs wants credentials. The authentication pattern you choose decides something bigger than convenience: it decides whether the secret is visible to the agent, whether it can be rotated without touching every config file, and whether a compromised server can replay it. Most MCP servers default to the pattern that leaks the most.

API keys: the default that leaks

API keys are the easiest pattern to wire up — put the key in the server config or environment, done. The cost is that the key is now readable by the model, inherited by every subprocess, and one verbose error away from the context window. Keys in config files also rot slowly: they are shared across servers, hard to scope, and almost never rotated. For a tool that sits inside the agent’s trust boundary, an unscoped, long-lived key is the worst possible credential shape.

Headers: injection at the edge

Custom headers are the standard transport for MCP authentication — Authorization, X-API-Key, tenant headers. The pattern that holds up is not storing the header value in the server’s config, but injecting it at the edge: a proxy that adds the header when the request leaves the machine. The agent and the server never see the value; they only see the effect. This keeps the credential out of context and out of tool output, and it makes rotation a change in one place instead of a sweep across every server config.

The same logic applies to query parameters for APIs that insist on them: inject at the edge, per host, so no configuration file contains the value.

OAuth: built for agents

OAuth has a reputation for being heavy, but the device flow — RFC 8628 — was designed for exactly this situation. The user authorizes on a trusted device with a code, and the agent polls for the token. No password ever reaches the agent or the server. Short-lived access tokens limit the blast radius of any single leak, and refresh tokens are rotated: each refresh consumes the previous token, so a stolen refresh token stops working the moment it is used.

For agent workloads, OAuth earns its complexity when tokens are scoped per user and per service. A token that reads one API, from one identity, is one you can rotate without a maintenance window. Patterns worth adopting: single-use refresh tokens, no retry on an invalid grant, and status tracking so an expired session surfaces as “reauth required” instead of a mysterious failure.

Choosing by threat model

Single-user local setup. An API key kept in a credential store and injected at the edge is fine. The key never enters the agent’s context, and the store holds the only copy.

Shared or multi-agent environments. OAuth with per-identity scopes. Each agent gets its own token, its own scope, and its own revocation story.

Remote MCP servers. Prefer OAuth over long-lived keys. The server never sees a secret it can replay, and sessions can be revoked independently.

The common thread

Every pattern fails the same way: when the secret is visible to something that does not need it. API keys fail because they are visible to everything. Headers fail when the value lives in a config file the agent can read. OAuth fails when refresh tokens are stored next to access tokens in the agent’s environment. The fix is structural — secrets live in one place, are injected at the edge, and are rotated on schedule.

That is the architecture behind trustless, which implements the RFC 8628 device flow for OAuth with automatic refresh and single-use refresh-token rotation, and injects per-host headers for HTTP tools. The full command reference is in the documentation.

If your MCP server’s API key appeared in a model’s context window today, how many other systems would that key reach?