Choose your setup
API keys vs OAuth for MCP servers
A static API key in a config file trusts whoever holds it, forever, until someone remembers to change it. OAuth makes a server ask who is calling and for how long, at the cost of a flow most MCP clients still don't implement.
New to MCP? What is an MCP server? covers the basics first.
A static API key is a password that works until someone revokes it: anyone with the string can call anything it's scoped to, for as long as it's valid, and nothing in the key itself says who is actually using it right now. OAuth, as MCP's authorization spec defines it, is a handshake that gets a short-lived token tied to one person or agent, one server and a specific set of permissions, re-checked on every call. The key is simpler to set up, and the token is harder to misuse.
What each one is
A static key or a personal access token (PAT) is a string, usually created once in a vendor's dashboard, that you paste into an MCP server's environment variables or a config file. The server reads it and sends it on every call to the upstream API. There's no login step and no per-caller distinction: the server either has a valid key or it doesn't, and the API on the other end sees whoever's key that is.
OAuth 2.1, as MCP's authorization specification adopts it, is a protocol for getting a token through a flow instead of a copy-paste: a client discovers an authorization server, a person approves a specific set of scopes, and a token comes back bound to one MCP server's URL. How MCP authorization works covers that flow in detail: discovery, PKCE, audience binding and the ban on forwarding a token to a server it wasn't issued for.
Authorization is optional in the spec. Where a server reached over HTTP does it, it should follow the OAuth-based spec. A server started as a local subprocess on stdio should skip that and read a credential from its environment instead, so most local MCP setups run on API keys either way.
Scope, expiry, revocation, identity and audit
Here is where they differ:
| Static API key or PAT | OAuth 2.1 under the MCP spec | |
|---|---|---|
| Scope | Whatever the vendor's dashboard let you pick when you created it, often broad | Negotiated per request; the spec's scope minimization guidance pushes servers toward narrow, incremental scopes |
| Expiry | Usually none, or a long default (90 days, a year) that most teams never shorten | Short-lived access tokens, often minutes to an hour, with a separate refresh token |
| Revocation | Delete the key in the vendor's dashboard; every holder of that string loses access at once | Revoke one token or one client's authorization without touching anyone else's |
| Per-caller identity | None. The upstream API sees "whoever has this key," not which person or agent made the call | A token is issued to one client, for one user, and the server checks its audience on every call |
| Audit trail | Logs show the key's name, if the vendor logs key usage at all. Multiple agents sharing one key are indistinguishable | A log can name the actual caller, because the token was issued to them specifically |
The common thread is that a static key answers "is this request authorized at all," while OAuth answers "is this request authorized, for this caller, right now, for this server." That second question matters more as more people and agents share a connection.
Setup cost and support across clients and servers today
A key is one paste into a config file. OAuth needs the MCP server to implement the resource server side (protected resource metadata, audience checks) and the client to implement discovery, PKCE and token storage, none of which existed in most MCP clients and servers at launch. Many hosted MCP servers still ask for a PAT in a header instead of running the full flow, and some popular clients added OAuth support well after they shipped. The practical result in 2026 is a mix: some remote servers (Linear, Atlassian, Sentry among others that run their own hosted MCP endpoints) support sign-in through OAuth, while plenty of others, and nearly every self-hosted or local server, still expect a key pasted into the environment.
That gap is a real cost on the OAuth side: a server operator has to run or point at an authorization server, and a client has to handle a browser redirect and store refresh tokens correctly. A key has no equivalent cost, which is exactly why it's still the default for anything one person wires up quickly.
A worked example
Say a support team connects an agent to a Jira MCP server so it can read and comment on tickets. Set up with a PAT, the agent's config holds one token created in Jira's admin console, scoped to "read and write issues" because that was the closest preset available. Three other agents on the team get the same token, because creating a second one meant another trip through Jira's console. Six months later someone leaves the team, and nobody is sure whether that token needs rotating, because nobody wrote down which agents hold a copy.
Set up with OAuth, each agent authenticates on its own, gets a token scoped and bound to that one Jira MCP server, and the token expires on its own within the hour if nothing refreshes it. When the person who set up the integration leaves, revoking their authorization doesn't touch the other three agents, because each one has its own. And the log says "agent: triage-bot" instead of "PAT ending in 7f2a".
In this example Jira supports OAuth. The key won because it was faster to set up, and nobody went back to change it.
When a static key is fine, and when to require OAuth
A key behind a gateway that injects it at call time, never hands it to the agent, and is scoped narrowly and rotated on a schedule closes most of the gap with OAuth: the agent never holds the string, so leaking its own context doesn't leak the credential, and a short rotation window limits how long a stolen key stays useful. Keep credentials out of AI agents covers that pattern. A single-user local server, a key scoped to read-only, or a short-lived key minted by a secret manager and injected per call are all reasonable choices, and the spec leaves stdio servers on environment credentials anyway.
Require OAuth, or at minimum per-caller tokens issued by something that can revoke one without touching the rest, when more than one person or agent shares a connection, when the upstream data is sensitive enough that "who did this" has to be answerable from a log, or when the server is reached over the open internet rather than run as a local subprocess you control. A long-lived, unscoped, shared key in any of those situations is the setup a security review flags first, regardless of which gateway or client sits in front of it.
Where a gateway fits
Neither choice is complete on its own. A gateway between agents and MCP servers can hold either kind of credential, API key or OAuth token, and attach it to a request only after checking what that specific caller is allowed to do, which is a separate decision from how the credential was obtained. Statio works this way. Agent identity gives each agent its own short-lived token instead of a shared key, and per-tool permissions decide what that token can call, whether or not the upstream server speaks OAuth. The audit trail names the caller on every entry. The upstream server's own choice still matters, though. If it only accepts a shared API key, a gateway can limit who triggers a call through it, but it can't add an audience check the upstream API never implemented. Self-hosted MCP gateways and the MCP security checklist cover where that line sits for your own servers.
Next
- How MCP authorization works (OAuth 2.1), the flow this article compares against.
- Keep credentials out of AI agents, for running a static key safely behind a gateway.
- Give every AI agent its own identity, on why a shared credential breaks revocation and audit.
- What is an MCP gateway?, on where credential injection and permission checks happen.
- Least privilege for AI agents, on scoping whichever credential you pick.