How to Implement MCP Authentication

MCP authentication controls which clients can connect to an MCP server and which actions they can perform. For local stdio servers running on your own machine, authentication is not required — only local processes can communicate with the server through OS-level pipes. For remote SSE servers accessible over a network, authentication is essential. MCP’s authentication specification uses OAuth 2.0 as the standard mechanism, but simpler approaches are appropriate for internal team servers. This guide covers when authentication is needed, which approach fits each deployment scenario, and what the MCP OAuth flow looks like in practice.

When Authentication Is Needed

Authentication requirements depend entirely on who can reach the server. A stdio server launched by the client as a local process is inherently local — no authentication needed, because the pipe connection is already OS-isolated. An SSE server listening on localhost (127.0.0.1) is accessible only from the same machine, so authentication adds little security value for personal use. An SSE server listening on a LAN or public IP address needs authentication because any machine that can reach that IP can attempt to connect. The three scenarios in order of increasing auth requirement: local stdio (no auth), localhost SSE for personal use (optional, low priority), LAN or public SSE (required).

OAuth 2.0: The MCP Standard

The MCP specification defines OAuth 2.0 as the authentication mechanism for remote servers. The flow is the standard OAuth Authorization Code flow: the client redirects the user to the server’s authorization endpoint, the user authenticates and grants permission, the server issues an authorization code, the client exchanges the code for an access token, and the client includes the access token in subsequent requests. MCP clients that implement the OAuth flow handle this automatically — the user is prompted to log in the first time they connect to a new server, and the client stores and refreshes the token thereafter. For server implementers, adding OAuth support means implementing the required OAuth endpoints: authorization, token exchange, and token refresh. The MCP Python and TypeScript SDKs provide helper implementations for these endpoints to reduce boilerplate.

Simpler Authentication for Internal Servers

For internal team servers where OAuth setup overhead is not justified, simpler authentication approaches work well. Static API key: the server requires a key in a request header (e.g. X-API-Key: your-key-here), and clients include this key in their configuration. This is straightforward to implement, appropriate for servers on a private network or VPN, and sufficient when the threat model is “prevent accidental connections” rather than “prevent determined attackers with network access.” Bearer token: similar to API key but uses the standard Authorization: Bearer token header, which is slightly more aligned with web conventions. mTLS (mutual TLS): both client and server present certificates for mutual authentication — more complex to set up but provides strong cryptographic guarantees appropriate for high-security internal deployments. For most internal team MCP servers, a static API key passed via the client configuration is the pragmatic starting point.

Figure 1 — MCP authentication: approach by deployment type

Deployment Recommended auth Complexity Local stdioNone neededZero Localhost SSE (personal)Optional API keyLow LAN / VPN team serverStatic API key / bearerLow Public / cloud serverOAuth 2.0 requiredMedium

The MCP OAuth Flow Step by Step

For a public or multi-tenant MCP server implementing the full OAuth flow: the client first calls the server’s discovery endpoint (/.well-known/oauth-authorization-server) to learn the server’s OAuth endpoints and capabilities. The client then constructs an authorization URL pointing to the server’s authorization endpoint and opens it in the user’s browser. The user authenticates (with username/password, SSO, or any method the server supports) and grants the requested scopes. The server redirects back to the client with an authorization code. The client exchanges the code for an access token via a POST to the token endpoint. The client stores the token and includes it as a Bearer token in the Authorization header of all subsequent MCP requests. The server validates the token on each request. When the token expires, the client uses the refresh token to obtain a new access token without re-prompting the user. The MCP TypeScript SDK’s auth package implements the client side of this flow, and FastMCP (a popular Python server framework) provides server-side OAuth helpers.

Scopes and Per-Tool Permissions

OAuth scopes allow fine-grained access control: a client can be granted permission to use only specific tools or access only specific resources, rather than the full server capability. A database MCP server might define scopes like read:customers, write:orders, and admin:schema. A client that only needs to query customer records would request only read:customers, and the server would reject tool calls outside that scope even if the client attempts them. Implementing scoped access control adds complexity but enables the principle of least privilege at the authentication layer — clients get exactly the access they need and no more. For internal team servers, role-based access (different API keys for different access levels) achieves a similar outcome with less implementation overhead than full OAuth scopes.

Configuring Authentication in Claude Desktop

For remote SSE servers in Claude Desktop, authentication credentials are passed in the server configuration in claude_desktop_config.json. For API key authentication, include the key as a header in the server URL configuration using the headers field if the client supports it, or configure it via the server’s expected method (some servers accept the key as a query parameter or in the connection URL). For OAuth-enabled servers, Claude Desktop implements the OAuth flow automatically — when you add a new remote server, it opens the browser for authentication, completes the flow, and stores the resulting token. Token storage uses the system keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service) rather than a plain text file, which is important for credential security on shared machines.

Session Management

Remote MCP servers need session management to handle multiple concurrent clients without leaking context between them. Each client connection should have an isolated session — tool calls from one client should not be visible to or affect another client’s session. For stateless servers (where each tool call is independent and the server holds no session state), this is automatic. For stateful servers (where the server maintains context across calls within a session, such as a server with a persistent database transaction or a multi-step workflow), session isolation requires the server to maintain per-connection state keyed by a session identifier. The MCP SSE transport includes session management primitives — clients receive a session ID on connection and include it in subsequent requests — which the server uses to route requests to the correct session context. Getting session isolation right is critical for production multi-client servers; leaking state between sessions can expose one user’s data to another.

Testing Authentication

Testing authentication in MCP servers requires verifying both that authenticated requests succeed and that unauthenticated or incorrectly authenticated requests are rejected. The MCP Inspector tool supports adding auth headers to connections, making it practical for manual testing of API key and bearer token authentication. For OAuth flows, a test client that implements the OAuth flow against a local development server confirms the full authentication path before deploying. Automated tests should cover: successful authentication with valid credentials, rejection of requests with missing credentials, rejection of requests with invalid or expired credentials, and rejection of requests for scopes not granted to the client. These tests should run in CI alongside the server’s functional tests to catch authentication regressions alongside capability regressions.

MCP authentication is straightforward for the most common cases: local servers need none, internal team servers need a shared secret, and public servers need OAuth 2.0. The MCP SDK implementations reduce the implementation work significantly for OAuth. Match the authentication approach to the threat model — OAuth for public servers, API keys for internal ones, nothing for local — rather than over-engineering authentication for deployments where simpler approaches are appropriate. The goal is preventing unauthorised access proportionate to the actual risk, not achieving the maximum possible authentication complexity.

One practical note for teams migrating from API-key to OAuth authentication: run both in parallel during the transition. Accept both API keys (for existing clients and automated scripts that cannot easily do the OAuth flow) and OAuth tokens (for interactive clients) simultaneously, with a deprecation date for API keys communicated to users. This migration path avoids forcing all clients to upgrade simultaneously, which is rarely practical in a real team environment where not everyone updates tools at the same time.

Leave a Comment