Blog · · by Rafael Asor, Attenu
How much, not just who: what an MCP server can check
What MCP built well. The authorization spec (2026-07-28) is careful about the oldest problem in delegated systems, the confused deputy: resource indicators are a MUST, protected-resource metadata is a MUST, a server accepts only tokens meant for it and never passes a token through. Every hop gets its own audience-bound credential. It is the right foundation, and the recipe below stands on it rather than beside it.
The one thing left open, in their words. The roadmap post of 22 August says it plainly: "more and more of
the callers are agents running as cloud workloads with their own identity, acting on behalf of a user who isn't
present, or delegating narrower authority to sub-agents." Today a tool-call request carries name, arguments,
meta and task — nothing that says which agent is calling, on whose behalf, with how much of the original
authority. The test next to the recipe pins that shape and names the day it changes.
What a server can do today. The client puts the delegation chain — signed Delegation Tokens from the root
agent down to the calling agent — in _meta, out-of-band of the tool arguments, which is where the roadmap places
agent identity. The server loads the chain offline: signatures, parent hashes, depth, and at every hop child ⊆
parent. It checks the leaf authority against the tool's scope and the request's context, records the decision on
a hash-chained ledger, and only then runs the body. No authorization server is in the path; the verifier key is
the only thing the server needs.
The code. (≤40 lines: ChainVerifier(signer) · build_server(verifier, sink) · on the client,
wire.serialize_chain(guard, signer) and call_tool(name, args, meta={"attenu_chain": chain}).)
The denial. The exporter's chain runs the export. The reader's chain — crm.read, no egress — is denied
before the body runs. A spliced chain (the reader's chain with the exporter's leaf appended) fails the parent-hash
linkage. A forged leaf fails its signature. No chain is a deny. Retries stay denied and each attempt is on the
ledger; an argument carrying injection text changes nothing, because the decision comes from the chain, not the
text; if the ledger cannot be written, the body does not run.
The verify. AuditLog.verify on the server's ledger; on the client side the same bundle verification as
everywhere else in attenu-guard — with no service involved.
What remains MCP's. The transport, the per-hop OAuth model, and the eventual standard field for agent
identity and authority. When it lands, this server reads the chain from there instead of _meta; the verification
does not change. And what the recipe cannot do: the check lives at the MCP boundary — a direct Python call to
the function behind the tool runs, and the test says so.
Verified against mcp 1.28.1 and the roadmap post of 2026-08-22, on 2026-08-25. Recipe:
examples/integrations/mcp/server_verifier/.
Rafael Asor is the founder of Attenu and the maintainer of attenu-guard and attenu-derive, open-source Python libraries for AI agent permissions across sub-agent handoffs. He is the author of the IETF Internet-Draft draft-asor-wimse-agent-delegation-chain and is based in Tel Aviv.