Spawn a sub-agent in any of today's multi-agent frameworks and look at what it inherits: your API keys. All of them, at full scope, with no expiry and no way to take them back short of rotating the key everywhere at once. The research assistant you spun up to summarize three PDFs holds the same authority as the orchestrator that spawned it. Nobody designed this; it's just what falls out when "give the child a credential" means "copy the parent's."
grantor-mcp is the missing layer, shaped as five MCP tools any MCP-speaking framework can call:
npx -y @grantor/mcp serve
grant(tools, max_uses, ttl_secs)— mint a bounded capability for a child identity the broker holds: these named tools, this many uses, this long, nothing else.delegate(parent, …)— hand a narrowed slice onward. Asking for a tool the parent doesn't hold is refused at signing; asking for more uses or a longer expiry silently clamps to the parent's bound. A child can only ever attenuate — never widen.check(child_id, tool)— the enforcement point, one call per action: a real on-chain read (is the root key still enrolled? was the cohort revoked? is the tenant's billing live?) plus a local use meter. Returnsallowor a named denial.revoke(child_id)— in your own tenant, a real on-chain revocation epoch bump. Every capability in that cohort dies at the next check, cryptographically, not by convention.status()— live standing, straight from the chain.
The part that usually takes a paragraph of disclaimers: there is no authorization server. Authority is anchored to a public smart-contract registry on Base — the delegation chain is signed locally, verified locally, and the only network dependency is an eth_call any RPC can serve. Nothing to run, nothing to trust, nothing to subpoena.
Try the whole story in two minutes#
The bundled sandbox points at a live shared tenant on the production registry, so the first run needs no wallet, no account, no config:
grant {tools: ["search","fetch"], max_uses: 3} → child_id
check {child_id, tool: "search"} → allow, 2 uses left
check {child_id, tool: "write"} → DENIED (CapabilityDenied)
delegate {parent, tools: ["search"], max_uses: 1} → narrower child
revoke {child_id} → done
The denial in the middle is the product: an escalation attempt refused by the capability grammar, not by a prompt asking the model nicely.
Honesty notes, because this page is read by people who check: the sandbox's signing key is published on purpose — it controls nothing outside the shared demo tenant. The broker self-issues its anti-replay challenge in sandbox mode (it is holder and verifier in one process). Use metering is broker-local, because deed verification is a chain read and reads are unobservable — the chain anchors identity, enrollment, revocation, and billing. And the whole stack is an unaudited developer preview; the cryptography it composes is the boring, published kind, but treat it accordingly.
Production use is one tenant away: a contract call and USDC on Base, no signup, no server — the guide walks the whole path, and your agents can read the machine manifest themselves.
MCP handles tools. A2A handles agents talking to each other. Grantor handles what they're allowed to do.