MCP servers need to verify who is calling them. Two options dominate: static API keys and OAuth 2.1. The MCP specification's March 2025 revision formally adopted OAuth 2.1 as the standard for remote, multi-user servers. But static API keys still make sense for a large slice of real-world deployments. We analyzed 221 security-category servers on MCPFind alongside the spec changes to produce a clear decision framework for developers.
What Is the Difference Between API Keys and OAuth 2.1 in MCP?
API keys and OAuth 2.1 are both valid authentication methods for MCP servers, but they solve different problems. An API key is a static string the client passes in the Authorization header - simple, stateless, and effective for single-developer or internal setups. OAuth 2.1 is a delegated authorization protocol: the client authenticates with a separate authorization server, receives a short-lived access token, and presents that token to the MCP server.
The MCP specification's March 2025 revision formally adopted OAuth 2.1 as the standard for remote servers handling multi-user or public access. The November 2025 revision added Client ID Metadata Documents (CIMD) as the preferred registration method and made PKCE non-negotiable for every client. For stdio servers running locally, the spec does not require OAuth - API keys or no authentication at all remain acceptable options.
MCPFind indexes 221 servers in the security category. The majority are internal tools where API key auth remains the practical choice - OAuth 2.1 adoption is concentrated in the subset of servers designed for public, multi-user deployments.
When Should You Use API Keys for an MCP Server?
API keys remain the right choice in four scenarios: local stdio servers, single-developer integrations, server-to-server communication where no human login is involved, and internal tools protected by network controls.
For a local stdio MCP server - the kind configured in Claude Desktop's claude_desktop_config.json - the transport never leaves your machine. Authentication is the OS process boundary. Adding OAuth here adds complexity without meaningful security benefit. Similarly, when you own both the client and the server in a single-developer context, rotating a single API key is far simpler than running a compliant authorization server.
The pattern to avoid is hardcoding API keys in configuration files committed to shared repositories. Keys should live in environment variables the MCP client reads at startup. MCPFind's security and devtools categories (2,840 servers) both include servers that follow this pattern with documented .env setup instructions. Look at the top-starred servers in those categories for reference implementations of proper key handling before writing your own.
How Does OAuth 2.1 Authentication Work in the MCP Spec?
The MCP OAuth 2.1 flow separates three roles: the MCP client acts as the OAuth client, the MCP server acts as the resource server (it validates tokens but never issues them), and a separate authorization server handles user login and token issuance. When the client first connects to a remote MCP server, the server returns a 401 with a WWW-Authenticate header pointing to the authorization server's discovery endpoint.
The spec mandates SHA-256 PKCE for all clients. Plain PKCE is explicitly not permitted. The flow: the client generates a code_verifier, hashes it to produce a code_challenge, sends the challenge during the authorization request, then sends the verifier when exchanging the code for a token. The server verifies the hash matches before issuing the token.
Since November 2025, the spec also requires servers to publish a Client ID Metadata Document (CIMD) so clients can register dynamically without manual coordination.
Here is a minimal PKCE verifier and challenge generation in Python:
import secrets, hashlib, base64
code_verifier = secrets.token_urlsafe(96)
code_challenge = base64.urlsafe_b64encode(
hashlib.sha256(code_verifier.encode()).digest()
).rstrip(b'=').decode()
# Send code_challenge in the authorization request
# Send code_verifier in the token exchangeHow Do You Choose Between API Keys and OAuth for Your MCP Server?
The decision tree starts with the deployment model. If your server runs locally via stdio and serves one user, API key auth or no auth is the correct choice. If your server runs remotely and serves multiple users, OAuth 2.1 is the right architecture.
Within OAuth 2.1, the next question is whether you want to operate your own authorization server or delegate to an existing identity provider. For most developers, delegating to GitHub, Google, or a service like Auth0 is faster than building a spec-compliant authorization server from scratch. The MCP spec allows this as long as the provider supports the required endpoints: authorization, token, and JWKS.
One mistake to avoid: using long-lived OAuth tokens as a drop-in replacement for API keys. OAuth tokens should be short-lived (minutes to hours) and refreshed automatically via a refresh token. Static, non-rotating tokens defeat the purpose of OAuth and introduce the same exposure risk as hardcoded API keys. If you need a static credential, use an API key by design rather than a frozen OAuth token.
For a broader look at MCP security tradeoffs, see MCP Security Basics and MCP stdio vs HTTP Transport - both are relevant to how your transport choice interacts with your auth model. If you are newer to MCP in general, What Is MCP covers the protocol fundamentals before you dig into authentication specifics.