toolcall() ← all concepts

// concept · MCP

Your MCP server is taking the wrong tokens

Your server accepts the token the client sends and forwards it to the API behind it. It works, nothing errors, and the specification says you must not do it. The reason it slips past review is that the token is never invalid.

// the anti-pattern

The MCP Security Best Practices page names it directly:

"Token passthrough" is an anti-pattern where an MCP server accepts tokens from an MCP client without validating that the tokens were properly issued to the MCP server and passes them through to the downstream API.

And the mitigation is written in RFC 2119 language, not as advice:

# MCP Security Best Practices — Token Passthrough MCP servers MUST NOT accept any tokens that were not explicitly issued for the MCP server.

Forwarding the credential the client already holds is the shortest path to a working server, and it looks like delegation. That is exactly why it gets shipped.

The token isn't broken

This is the part that matters, and the part most summaries skip. A passthrough vulnerability does not involve a forged, expired or malformed token. It involves a completely genuine credential that was issued for a different service.

The piece that makes this click is the one most explanations skip: your server does not issue its own logins. It trusts an authorization server, and that same authorization server mints tokens for many resource servers, not just yours. Every one of them is properly signed and unexpired. Each carries a claim naming the single service it was issued for.

So picture a row of venues that all sell through one ticketing app. Someone shows up at your door holding a ticket for the cinema. It is a real ticket, from the app you trust, and it has not expired. Your doorman checks that it is genuine, and it is, so in they go.

The ticket never stopped being for the cinema. Your door just never asked. That is the whole bug, and note what it is not: the ticket has no special power, and nothing about it is forged. Your door simply had no standard beyond "is this real". Which means your check is not may this caller use me — it is does this caller have a token from anywhere. Anyone who has ever signed in to anything on that issuer can call you, and it will all look fine.

Every check you probably did write — is it well-formed, is the signature good, has it expired — passes. The check that was missing is the one asking who the token was made for:

# the claim that answers it aud: https://your-mcp-server.example ← is this you?

The spec points at exactly this: when a server "doesn't verify that tokens were specifically intended for it (for example, via the audience claim, as mentioned in RFC9068), it may accept tokens originally issued for other services". That, it says, "breaks a fundamental OAuth security boundary, allowing attackers to reuse legitimate tokens across different services than intended".

What it costs once you forward it

Accepting the wrong token is one problem. Passing it downstream is a second, larger one — and the reason is the opposite of what people usually assume. Forwarding the token does not stamp your name on the request. It stamps the token's.

The record does not point back at you. In the spec's words, the downstream resource server's logs "may show requests that appear to come from a different source with a different identity, rather than the MCP server that is actually forwarding the tokens". The far side reads the credential it was handed, so your server never appears in it. Your own server, meanwhile, "will be unable to identify or distinguish between MCP Clients" when they arrive holding opaque upstream tokens. Neither end has a true account of who did what.

You become the exfiltration path. The spec is blunt: a malicious actor in possession of a stolen token "can use the server as a proxy for data exfiltration". That is what makes this worth caring about. You are not the name on the record — you are the route, and the route is the part nobody logged.

# what the downstream API records caller: whoever the token names ← not you # nothing says the request came through your server
Related but not the same. Token passthrough can cause the confused deputy problem, but the spec's confused-deputy section describes a distinct OAuth proxy attack with its own preconditions — static client IDs, dynamic client registration and consent cookies. Fixing audience validation does not by itself address that one.

The rule

Validate the audience. Reject anything not issued for you. Your MCP server is a resource server in its own right, not a pipe — it needs its own tokens, and it needs to check that the ones it receives were minted for it.

The spec adds a practical reason to do this even if you are "just a proxy" today: "Even if an MCP Server starts as a 'pure proxy' today, it might need to add security controls later. Starting with proper token audience separation makes it easier to evolve the security model."

Related: prompt injection is the other half of the MCP threat picture and is a genuinely different problem — that one is untrusted content steering the model, where this one is your server trusting a credential. And what the model actually sees of your tool covers the design side of the same server.

One concept a week. Free.

The deeper, copy-paste version of each ToolCall short — in your inbox.

// total: 0.00 · spam: void · unsubscribe: one click