Security

Why Token Passthrough Is a Security Hole in MCP Deployments

6 min read ToolHost Engineering

When you call a tool through an MCP gateway, the gateway receives your bearer token, determines which upstream MCP server owns that tool, and forwards the request. The question is: which credential does it use to talk to that upstream server?

The easy answer — and the one most implementations ship with — is to forward your token. The gateway takes the Authorization: Bearer <token> header it received and sends it unchanged to the upstream. This is called token passthrough, and it is a significant security error.

The attack vector

Imagine you are running a gateway that routes tool calls to three upstream MCP servers: your internal code search service, a customer-data service, and a third-party analytics tool that a vendor operates. All three receive your downstream bearer token because the gateway passes it through.

The third-party analytics server is compromised. The attacker running it now has your downstream bearer token — the same credential your users present to your gateway. That token was issued by your authorization server. It carries your users' permissions. The attacker can now make direct API calls to your own services, bypassing the gateway entirely, for as long as the token is valid.

This is not a theoretical edge case. In any multi-server MCP deployment, you are distributing trust to every upstream you forward to. A single compromised or malicious upstream collects tokens with authority over resources it was never supposed to reach.

What passthrough looks like on the wire

In a passthrough implementation, the gateway receives:

POST /mcp HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
Content-Type: application/json

{"jsonrpc":"2.0","method":"tools/call","params":{"name":"search_code","arguments":{...}}}

And then immediately makes this outbound call:

POST https://analytics.vendor.example/mcp HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
Content-Type: application/json

{"jsonrpc":"2.0","method":"tools/call","params":{"name":"analytics_query","arguments":{...}}}

The token is identical. The vendor's server has a fully valid credential for your authorization domain.

Why does this happen? Because it is easy. The gateway has the token in hand. Forwarding it requires zero additional infrastructure. Issuing a separate per-upstream credential requires a credential store, rotation logic, and scope management. Most implementations take the easy path.

What an attacker can do with it

Bearer tokens are opaque to the gateway — it validates them but passes them along unmodified. Depending on your authorization server configuration, a replayed downstream token may be accepted by your own APIs for minutes or hours.

The attacker does not need to break cryptography. They do not need to exploit your gateway. They just log incoming Authorization headers on their MCP server and replay them out-of-band. Standard HTTP infrastructure. The MCP protocol does not protect against this because it is a transport-layer problem, not an MCP problem.

Token passthrough also violates the principle that each hop in a call chain uses its own credential with its own scope. Once you forward a token, you have no way to enforce that the upstream uses it only for the intended operation.

How ToolHost handles this

ToolHost never forwards the downstream bearer token upstream. Each upstream backend is configured with its own credential. When the gateway routes a tool call to an upstream, it authenticates using that backend's configured credential — completely separate from what the downstream user presented.

The downstream token is validated at the gateway boundary and then discarded from the outbound path. The upstream receives a credential scoped specifically to what that upstream is allowed to do, issued by an authority the upstream trusts.

In the gateway configuration, each backend declares its credential independently:

{
  "name": "analytics-vendor",
  "url": "https://analytics.vendor.example/mcp",
  "credentials": {
    "type": "bearer",
    "token": "${ANALYTICS_VENDOR_API_KEY}"
  }
}

That ANALYTICS_VENDOR_API_KEY is a vendor-issued API key scoped only to the analytics service. Even if the vendor's server is compromised and logs it, the attacker gains access only to the analytics API — not to your downstream user's authorization domain.

The general principle

Each hop in an MCP call chain is a separate trust boundary. The downstream presents a credential to the gateway. The gateway presents a different credential to each upstream. These credentials are not interchangeable and are not shared.

This is not a novel security principle — it is how well-designed API gateways have worked for years. MCP deployments inherit the same requirement. The protocol being new does not change the underlying threat model.

If you are evaluating an MCP gateway and it does not have explicit documentation on per-upstream credential management, assume it is doing passthrough. Ask specifically: what credential does the gateway present to an upstream MCP server? If the answer is "the downstream user's token," that is your answer.