Agent Wranglers

AGT-38Required

Accept only tokens issued for your MCP server and never pass them on

Check every access token's signature, issuer, expiry and audience before an MCP request runs. Refuse a token whose aud does not name this server with 401. Never forward the client's token to another API.

Spec version

MCP 2026-07-28

Review by

2027-04-30

How to do it

A token issued for another service, even by your own authorization server, must not open yours. Otherwise any service that sees your users' tokens can replay them at you. When a tool calls an upstream API, use a separate token for that API, from the person's own grant with it or from token exchange. A passed-on token makes the upstream API trust a token it never meant to accept, and hides who acted from its logs. Answer an invalid or expired token with 401 and a missing scope with 403. Exchanged tokens never widen rights, as Never widen an agent's rights when you exchange its token says. The attack procedure is in Run the AI feature checks.

Examples

JavaScript example

import { createRemoteJWKSet, jwtVerify } from 'jose';
const ISSUER = 'https://auth.example.com';
const RESOURCE = 'https://mcp.example.com/mcp';
const keys = createRemoteJWKSet(new URL(`${ISSUER}/jwks.json`));

export async function verifiedClaims(bearer) {
  try {
    const { payload } = await jwtVerify(bearer, keys, { issuer: ISSUER, audience: RESOURCE });
    return payload;
  } catch {
    return null;
  }
}

Python example

import jwt

ISSUER = "https://auth.example.com"
RESOURCE = "https://mcp.example.com/mcp"
keys = jwt.PyJWKClient(f"{ISSUER}/jwks.json")

def verified_claims(bearer: str) -> dict | None:
    try:
        key = keys.get_signing_key_from_jwt(bearer).key
        return jwt.decode(bearer, key, algorithms=["RS256", "ES256"], issuer=ISSUER, audience=RESOURCE)
    except jwt.PyJWTError:
        return None

How to check

Send a valid token from your own authorization server issued for https://other.example.com. The server answers 401 and runs no tool. A traced test tool that calls an upstream API sends it a different token from the one the client sent.

Sources

For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/agents-as-users/delegated-access-and-consent/mcp-token-audience-no-passthrough">Agent Wranglers agents as users playbook</a>. In code you write, add a comment next to it with the same link.