How AI Agents Authenticate to Your API: Bearer, API Key, and OAuth 2.1
MCP auth has two layers that people constantly conflate. Here's how each one works — client to server, and server to your API — with the three upstream auth types explained.
Almost every MCP auth problem I've watched someone debug came down to the same confusion: they thought there was one layer of authentication when there are two. They'd get a 401, fix the wrong credential, and get the same 401 — because the failure was happening on a layer they weren't looking at.
So this post is about drawing that line clearly. When an AI agent calls your API through an MCP server, there are two separate authentication handshakes, they use different credentials, and they fail in different places. Get that model straight and MCP auth goes from confusing to boring — which is exactly what you want auth to be.
If MCP itself is still fuzzy, start here. Everyone else, let's split the layers.
The two layers
Picture the path a single call travels: AI client → MCP server → your API. There's a handshake at each hop.
Layer 1 — the client authenticates to the MCP server. This proves that whoever's connecting is allowed to use this server. It's the access token you paste into Claude Desktop or Cursor, or the OAuth sign-in a cloud client walks you through. It has nothing to do with your API's credentials.
Layer 2 — the MCP server authenticates to your API. This is the real upstream credential — the key or token your API actually checks. It lives on the server, and the server attaches it to the outbound request. The AI never sees it.
The single most useful diagnostic habit is figuring out which layer a 401 came from. A 401 that stops the tool from being called at all is Layer 1 — the client isn't authorized to the server. A 401 that shows up inside the tool's response is Layer 2 — the server reached your API but your API rejected the credential. Same status code, completely different fix.
Layer 1: getting into the server
The server-facing auth exists so your MCP server isn't an open door. Two flavors show up in practice.
A static access token. When you wrap an API into a server yourself, you get a bearer token. The client sends it in an Authorization: Bearer ... header, the server checks it, done. Simple, and fine for a server you configured and handed to your own clients.
OAuth 2.1. For servers meant to be connected by cloud clients — think a shared server that many people connect to, rather than one you hand-configured for yourself — the MCP authorization spec expects a proper OAuth flow. Concretely that means the server implements OAuth 2.1 with PKCE and dynamic client registration: the client discovers the auth endpoints, registers itself, redirects you to sign in, and gets a scoped token back. This is what makes "click connect, a browser opens, you log in" work without anyone copying a token by hand. It's more moving parts, which is exactly why you don't want to implement it yourself if you can avoid it — API2MCP runs that OAuth 2.1 authorization server for you, so servers that need it just work with Claude and other clients that speak the spec.
The thing to hold onto: Layer 1 is about the server's front door, and its credential has nothing to do with your API.
Layer 2: getting into your API
This is the layer people actually mean when they say "API authentication," and it's where the three types live. When you register a provider, you tell the platform how your API expects to be authenticated, and it applies that on every upstream request. Here are the three, with how each actually goes out on the wire.
API key
The most common, and the one with the most variation. An API key is a secret string your API checks — but where it goes differs by API:
- in a header, often
X-API-Key: <key>or a vendor-specific name - in a query parameter, like
?api_key=<key> - occasionally baked into a header with a prefix
The important detail is that you specify the location, because getting it wrong is a silent failure — the key is valid, it's just in a place your API isn't looking. When you register the provider you set the scheme (header vs query, and the exact name), the platform stores the key encrypted, and it injects it into the right spot on each call.
Bearer token
A token sent as Authorization: Bearer <token>. This covers a huge range of APIs — anything issuing a long-lived access token, a personal access token, a service token. From the server's side it's the simplest to apply: attach the header, send the request. If your API hands out a token and expects it in the Authorization header, this is your type.
OAuth 2.1 (upstream)
Note this is a different OAuth than Layer 1. Here, OAuth is how the server authenticates to your API — your API is the OAuth-protected resource, and the server has to obtain and refresh tokens to call it. The platform handles the token lifecycle: getting an access token, attaching it, refreshing it before it expires so calls don't start failing mid-conversation. You configure the flow once; the server keeps the token fresh on every request after that.
(For the SOAP holdouts: yes, some upstreams want WS-Security rather than any of the above, and that's a supported case too — the same "configure once, applied per call" idea, just a different header-signing scheme.)
Why the credential never reaches the model
Here's the design decision that ties it together: the Layer 2 credential is stored encrypted at rest and injected server-side, at call time, after the model has already decided to invoke a tool. The model produces a tool call — a name and some arguments. The server takes that, attaches the real API credential, sends the HTTP request, and returns only the response. The model's input was the tool schema; its output was the arguments; the credential was never in either.
That's not incidental — it's the reason routing through a server is safe in the first place, and it deserves its own treatment, which is why keeping keys away from the model gets a full post.
Putting it to use
Next time you set up a server and hit a 401, run the two-layer check before touching anything:
- Did the tool get called at all? If not, it's Layer 1 — fix the access token or OAuth connection between your client and the server. The Claude Desktop and Cursor guides cover this side.
- Did the tool get called and your API rejected it? That's Layer 2 — the upstream credential or its scheme is wrong. Check that the key is in the location your API expects.
Two layers, two credentials, two places to look. Once that's second nature, MCP auth stops being mysterious. If you want to see all three upstream types in the configuration flow, the free tier lets you register a provider and pick one without committing to anything.