Common Authentication Patterns for Remote MCP Servers
Why Remote MCP Authentication Is Different
The Model Context Protocol was designed first for local, stdio-based servers running on a developer's machine, where the client and server share a process boundary and trust is implicit. As soon as you expose an MCP server over HTTP or SSE so that multiple agents, teams, or third-party clients can reach it, that implicit trust disappears. You now have a network service that any AI client could attempt to call, often carrying credentials or acting on behalf of a specific user, so you need the same rigor you'd apply to a public API.
The complication is that MCP clients are frequently AI agents rather than browsers or scripts written by your own team. That means the authentication flow has to work inside a tool-calling loop, sometimes with no human present to click through a login screen, and it has to fail predictably when tokens expire mid-session. Picking the right pattern early avoids a rewrite later.
Static API Keys and Bearer Tokens
The simplest and still most common pattern is a static API key sent as a bearer token in the Authorization header of every request. The client configuration (in Claude Desktop, an IDE plugin, or a custom agent runtime) stores the key and attaches it to each JSON-RPC call over HTTP. This works well for single-tenant or developer-to-developer scenarios: you issue a key per customer or per integration, hash it server-side, and validate it on every request before routing to a tool handler.
The tradeoffs are familiar from any API-key scheme: keys are long-lived, easy to leak into logs or client configs, and hard to scope narrowly without building your own permissions layer on top. If you go this route, treat it like any production secret — support rotation, expiry, and per-key rate limits, and log which key called which tool so you can audit usage after the fact.
OAuth 2.1 and Delegated Authorization
For servers that act on behalf of an end user rather than a service account, the MCP specification has converged on OAuth 2.1 as the recommended flow for remote servers. The client redirects the user (or, for headless agents, uses a device-code equivalent) to an authorization server, receives a short-lived access token and a refresh token, and presents the access token on each MCP call. The MCP server validates the token against the issuer, checks scopes, and only then executes the requested tool.
This pattern is more work to implement — you need an authorization server or a provider like Auth0, WorkOS, or your own OIDC-compliant service — but it buys you real advantages: tokens expire automatically, scopes let you restrict an agent to read-only tools like summarize_document while denying anything that mutates data, and you get a standard revocation path if a client is compromised. If your MCP server needs to act with a specific user's permissions (for example, only summarizing documents that user already has access to), OAuth is close to a requirement rather than a nice-to-have.
Mutual TLS and Network-Level Controls
In server-to-server deployments — say, an internal agent platform calling an internal MCP server — mutual TLS is worth considering alongside or instead of application-layer tokens. Both sides present certificates, so authentication happens at the transport layer before any MCP message is parsed. This is particularly attractive when the set of clients is small and known in advance, such as a handful of internal services, because certificate issuance and rotation can be automated through your existing PKI or service mesh.
mTLS doesn't replace user-level authorization, though. It tells you which service is calling, not which human or which permission set that call should be constrained to. Most production setups pair mTLS for transport-level trust with a lighter application-layer check — a scoped token or claim embedded in the certificate — for fine-grained tool access.
Scoping Tokens to Tools, Not Just Endpoints
Whichever pattern you choose, the detail that matters most for MCP specifically is scoping at the tool level rather than the endpoint level. Because MCP exposes a single JSON-RPC endpoint through which any registered tool can be invoked, a token that's merely "valid" isn't enough — you want to know whether this token is allowed to call find_red_flags, or only summarize_document, or neither. Encode that as scopes or claims in the token, and enforce it inside your tool dispatch logic, not just at the network gateway.
This also matters for auditability. When an agent misbehaves or a customer disputes a bill, being able to say exactly which token called which tool, with which arguments, at what time, turns a guessing game into a five-minute log query. Structured, per-tool authorization logging is cheap to add up front and expensive to bolt on later.
Choosing a Pattern in Practice
For a quick internal tool or a proof of concept, a static API key with proper hashing and rotation is fine. For anything customer-facing where the server acts on a specific user's data, invest in OAuth 2.1 from the start — retrofitting delegated auth onto an API-key system is disruptive for every existing integration. For internal, high-trust service meshes, mTLS plus lightweight scoped claims gives you strong guarantees with less operational overhead than a full OAuth stack.
If you'd rather skip building and maintaining this infrastructure yourself, it's worth noting that hosted options exist: Document Analysis MCP, for instance, is a subscription-based MCP server exposing summarize_document and find_red_flags tools that any MCP-compatible client can call directly, with authentication already handled server-side. Whatever you choose, the underlying principle is the same — authenticate the caller, authorize the specific tool call, and log enough to reconstruct what happened after the fact.