The Model Context Protocol (MCP) gives an agent a standard way to discover and call tools. It also creates a new place where credentials meet untrusted input: a model decides which tool to call and with what arguments, and something has to decide whether that call is allowed. If the answer is "whoever can reach the server can call anything", the tool server is an open door to every system behind it.
This post walks through where authentication and authorisation belong in a tool call, and the mistakes that are easiest to make.
The parties in a tool call
A single tool call involves more hops than it first appears.
- The MCP client. The agent runtime or host application that connects to servers on the model's behalf.
- The MCP server. Exposes tools, receives
tools/listandtools/callrequests, and runs them. - The upstream API. The real system behind a tool: a ticket tracker, a code host, a database.
- The authorisation server. Issues the tokens the client presents to the MCP server.
There are therefore two separate trust relationships. The client must prove something to the MCP server. The MCP server must prove something to the upstream API. Most security problems with tools come from treating these as one.
Local and remote servers
A server launched locally over stdio runs as a child process of the client, so there is no network request to authenticate. It gets its credentials from its environment. A remote server reached over HTTP is a network service like any other and needs real authentication on every request. The rest of this post is about remote servers.
OAuth-based authorisation for MCP
The MCP specification bases authorisation for HTTP transports on OAuth 2.1. In OAuth terms the MCP server is a resource server: it accepts access tokens in the Authorization header and validates them. It does not issue them.
Discovery works like this. A client calls the server without a token and receives a 401 that points to the server's protected resource metadata (RFC 9728):
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://tools.example.com/.well-known/oauth-protected-resource"That document names the authorisation server. The client reads the authorisation server's own metadata (RFC 8414), obtains a token, and retries with:
POST /mcp HTTP/1.1
Host: tools.example.com
Authorization: Bearer eyJhbGciOi...
Content-Type: application/json
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"tickets.create","arguments":{"title":"Rotate key"}}}How the client obtains the token depends on who is acting. An interactive client working for a person uses the authorisation code flow with PKCE. An unattended agent authenticates as itself with client credentials, preferably with a signed assertion from its own key and not a shared secret.
Token audience: the check that is easy to skip
An access token is issued for a particular recipient, recorded in its aud claim. Resource indicators (RFC 8707) let the client say which resource it wants a token for, and the MCP specification requires clients to use them.
The server's half of the bargain is to check. An MCP server must accept only tokens issued for itself. If it accepts any token signed by a trusted issuer, then a token an agent obtained for a low-value tool server works at a high-value one, and any server that receives a token can replay it elsewhere.
A minimal validation list for an MCP server:
- Verify the signature against the issuer's published keys.
- Check the issuer is the one you expect.
- Check
audnames this server. - Check the token has not expired.
- Check the token type, so an ID token or some other JWT from the same issuer is not accepted as an access token.
Do not pass tokens through
The tempting shortcut is to take the token the client sent and forward it to the upstream API. The MCP specification forbids this, and for good reasons.
- Wrong audience. The token was issued for the MCP server. If the upstream accepts it, the upstream is not checking audience, which is a second bug.
- Lost accountability. The upstream sees a user's token and cannot tell that an agent and a tool server were in the middle.
- Bypassed controls. Whatever the MCP server was meant to enforce can be skipped by anyone who obtains that token and calls the upstream directly.
- Wider blast radius. A compromised or malicious tool server now holds tokens that work on other systems.
The correct pattern is for the MCP server, or a gateway in front of it, to hold its own credential for the upstream and use it for the outbound call. Where the upstream needs to know which person the call is for, use OAuth token exchange (RFC 8693) to obtain a new token with the right audience that names both the user and the acting agent. See what is AI agent identity for how delegation is expressed.
Authenticating is not authorising
A valid token says who is calling. It does not say the call is allowed. For tools, authorisation needs to be finer than "may connect to this server".
- Per tool. An agent should see and call only the tools it was granted.
tools/listshould not advertise the rest. - Per argument. Check arguments against limits on the grant, not only the tool name.
- Deny by default. A tool nobody granted is refused.
- Sometimes, a person. Certain calls should wait for approval. See human approval for agent actions.
Because the upstream often sees one shared identity (the tool server's), the tool layer is the only place where per-agent and per-user decisions can be made. That makes it the natural home for a gateway.
How AuthFI does it
AuthFI runs an MCP gateway: one address for every tool an agent uses.
An agent calls the gateway with its AuthFI access token. The gateway verifies the token and then asks, for each call, whether this agent holds a grant for this tool. The answer is no unless a grant exists. Tools are listed under their upstream's name, so an agent sees <upstream>.<tool> and only the ones it was granted. Arguments are checked against the limit on the grant, and a call marked as sensitive returns a request for approval and waits for a person.
The tool's own credential is sealed in AuthFI's key service when the tool server is registered. The gateway injects it for each call, so it never reaches the agent, and the agent's token is not what the upstream receives.
For an agent acting for a person, the delegated token is obtained through an authorisation code flow with PKCE followed by an RFC 8693 token exchange. An agent acting for someone can do no more than that person can. Tokens name the API they are for (RFC 8707), and the audience must have been granted.
Every call is recorded with its verdict and reason: allowed, refused, or waiting for a person.
One limit to plan for: the gateway accepts agent access tokens. An MCP client that can only sign a person in through a browser needs an agent identity in front of it. The details are on the AI Auth page.
Key takeaways
- A tool call has two trust relationships: client to MCP server, and MCP server to upstream. Secure them separately.
- A remote MCP server is an OAuth resource server. It must check issuer, audience, expiry and token type on every request.
- Never forward the client's token upstream. Hold a separate upstream credential, or exchange the token for one with the right audience.
- Authorise per tool and per argument, deny by default, and record every verdict.



