Authdog

Authorizing tool calls for MCP

Last updated Oct 21, 2026
View as Markdown

token_verifier proves the bearer. The sample's whoami tool then returns token.subject. That 200 means userinfo accepted the caller. It does not mean the caller may invoke every tool on the server.

What the sample enforces

@server.tool()
def whoami() -> str:
    """Return the Authdog subject of the calling agent."""
    token = get_access_token()
    if token is None or not token.subject:
        raise PermissionError("Unauthorized")
    return token.subject

get_access_token() is the context the verifier filled in. The tool raises if that context is missing. It does not check an allow list. The verifier records scope agent:invoke on the AccessToken. AuthSettings.required_scopes lists the same scope. A finer permission is a separate check.

Allowed tools

When the caller is an agent, the trust-store row can name allowed tools:

Control Effect
Allowed scopes and tools Globs on the trust-store row. * allows every tool.
Trust-store enforce The resource server applies that row. This Python sample does not.
AuthZEN MCP_TOOL A per-tool allow or deny after the token is valid.
Revoke New tokens fail when the subject is the OIDC client id. Outstanding JWTs last until expires_in unless you enforce the trust store.

Those checks ship in the TypeScript runtime guard described in Build an MCP server. Turn trust store and authorization to enforce after you have watched them. A denied call should show up as MCP_TOOL_DENIED, not as a success with an empty body.

If the tool acts for a person, authorize sub and record act. The agent name in the prompt is not the decision.

Read To learn how to
Custom channel auth for MCP Fill get_access_token()
Enriching instructions for MCP Tell the model to return the subject, not invent one
MCP SDK Add trust store, AuthZEN, and telemetry
Agents Edit allowed tools and revoke the identity

Learn more