
Product
Socket for ClickUp Is Now Available
Create ClickUp tasks from Socket alerts, automate ticketing with custom rules, and keep alert and task status synchronized.
@grantor/mcp
Advanced tools
Grantor permission broker for multi-agent frameworks — grant, delegate, check, revoke bounded capabilities over MCP, and wrap any stdio MCP server with enforced permissions. No authorization server anywhere.
A permission broker for multi-agent frameworks. When a parent agent
spins up a sub-agent, the easy thing to do is hand it the same credentials
the parent has — every tool, no expiry, no way to take it back short of
rotating the parent's own key. grantor-mcp is the other option: an MCP
server that sits between the parent and its sub-agents and hands out
bounded capabilities — named tools only, a use budget, an expiry —
anchored on-chain (a registered root key, a live revocation epoch, a billed
tenant), not just an in-memory promise. Widening is bounded two ways: asking
for a tool the parent doesn't hold is refused at signing time; asking
for more uses or a longer expiry than the parent holds is silently
clamped down to the parent's own bound. Either way, the chain walk
re-enforces narrowing cryptographically at verify, independent of what
the broker did at signing. No authorization server anywhere — this is a
library, not a service.
And it isn't only advisory: grantor-mcp wrap runs any stdio MCP server
behind the broker as an enforcing proxy — ungranted tools are denied
before the server ever sees the request, granted tools are metered, and an
on-chain revocation lands on the wrapped session's very next call. See
Wrap any MCP server (enforcing).
Ships with a published sandbox principal so you can try the whole arc — grant, delegate, check, escalate, revoke — before you own a tenant. See Sandbox vs. your own tenant for exactly what that key can and can't do.
npx -y @grantor/mcp demo
Wraps a bundled toy server behind a grant for one tool, lets the granted
call through, and shows delete_everything getting refused before the
server sees it — enforcement is real (broker + on-chain revocation read),
only the "agent" is scripted. Then inspect your own server and get a
ready-to-paste wrap suggestion:
npx -y @grantor/mcp tools -- <your MCP server command>
The suggestion grants read-like tool names only (a name heuristic — review
it before trusting it); everything outside --tools is denied.
Add it to Claude Code:
claude mcp add grantor-mcp -- npx -y @grantor/mcp serve
Or wire it into any MCP client's config directly:
{
"mcpServers": {
"grantor-mcp": {
"command": "npx",
"args": ["-y", "@grantor/mcp", "serve"]
}
}
}
No API key, no signup, no config file required — the first run uses the
bundled sandbox tenant on Base mainnet. Call status first; it always tells
you which mode you're in.
The shared sandbox goes live with the package's first publish — the bundled
sandbox-config.jsonis provisioned (a real tenant created and funded, per docs/deploy/mcp-sandbox.md) before0.1.0ships. If you're running from source before that happens, every tool —statusincluded — refuses outright with the same clean message ("sandbox not provisioned yet"), rather than one guarded verb failing loud and another failing on the empty placeholder key with a cryptic error. Not something you'll hit against the published package.
The five tools above are the broker — authority your agents ask about.
wrap is enforcement: run any stdio MCP server behind the proxy and
ungranted tools are denied before the server ever sees them, granted tools
are metered, and revocation lands mid-session.
# one line: bound a server to two tools, 20 uses, one hour
grantor-mcp wrap --tools search,fetch --max-uses 20 --ttl-secs 3600 -- npx some-mcp-server
# or wrap a child you granted (and can delegate/revoke) beforehand
grantor-mcp wrap --child <child_id> -- npx some-mcp-server
What the wrapped client sees: tools/list filtered to granted ∩ available;
a denied tools/call answered with an isError result carrying
{allow:false, code, reason} (the model reads why); resources/* and
prompts/* denied by default — pass --allow-resources to let them
through (unmetered; a security posture flag, off on purpose). Everything
else passes through untouched. Denies never decrement the use budget.
Every tool is described in full to the MCP client at connect time
(inputSchema + a one-line purpose string); this is the short version.
status — broker + tenant snapshot: mode, live on-chain tenant
standing, principal registration, child/use counters.
status() -> {"mode":"sandbox","tenant_status":"Active","principal_registered":true,
"children":{"total":0,"revoked":0,"uses_remaining":0},
"grantor_note":"Shared sandbox tenant. For your own limits..."}
grant — give a sub-agent a bounded capability: named tools, a use
budget, an expiry. Returns a child_id the broker holds and metering.
grant({tools:["search"], max_uses:3, ttl_secs:3600})
-> {"child_id":"a1b2...","sub":"9f3c...","grants":[...],"exp":1786020000}
check — authorize ONE action for a child: real on-chain verification
(root key, revocation epoch, tenant billing) + capability match + local
use budget. Gate every sub-agent tool call on this.
check({child_id:"a1b2...", tool:"search"}) -> {"allow":true,"remaining_uses":2}
check({child_id:"a1b2...", tool:"write"}) -> {"allow":false,"code":"CapabilityDenied","reason":"..."}
delegate — narrow an existing child's capability onward to a new
child (fewer tools, fewer uses, shorter expiry). A tool outside the
parent's grant is refused at signing; a wider max_uses/ttl_secs is
silently clamped to the parent's own bound instead of refused. Either
way, the chain walk re-checks narrowing cryptographically at verify.
delegate({parent:"a1b2...", tools:["search"], max_uses:1})
-> {"child_id":"c3d4...","sub":"7e21...","grants":[...]}
revoke — revoke a child's authority. With your own tenant + admin
key this is a real on-chain bumpEpoch; in the sandbox it revokes at the
broker (which holds the key and is the check-point).
revoke({child_id:"a1b2..."}) -> {"revoked":"onchain","tx":"0x...","epoch_label":"..."}
grant/delegate accept an optional to (an external holder's deed
sub) and return a signed link instead of a broker-held child — for
handing authority to an identity the broker does not hold the key for. See
The presented-deed caveat.
The bundled sandbox-config.json ships a real, published principal
private key, registered as an agent key on a real Base mainnet tenant, so
grant/delegate/check/revoke all work with zero setup. Before you
build anything on it, know exactly what it is:
check's self-issued challenge. In broker-held mode, the broker
mints a random challenge, signs a deed against it with the child's key,
and immediately verifies that same deed — it is both holder and verifier
in one process. That's real cryptographic + on-chain verification (the
chain reads, the revocation-epoch check, and the billing gate are all
live), just not a caller-issued challenge. See
the presented-deed caveat for the mode that
is.max_uses/remaining_uses live in this process's state
file (~/.grantor-mcp/state.json by default), not on-chain. Inside one
broker process the meter holds two invariants: check reserves the use
in the same synchronous step as the balance gate (two concurrent
last-use calls can never both pass; a denied call refunds; a crash
between authorization and the tool's side effect loses a use — it can
never double-spend one), and delegate TRANSFERS uses from the parent's
live pool (a tree of delegations can never hold more aggregate uses than
the root grant; a drained parent refuses to delegate). Across processes
the meter is NOT shared or atomic: run one broker per state file.
to-mode links (external holders) carry no broker meter at all — their
bound is the signed per-path max_uses caveat alone.None of this is a limitation you have to accept — it's what changes the moment you run your own tenant.
Point the broker at your own registered tenant instead of the shared sandbox:
{
"mcpServers": {
"grantor-mcp": {
"command": "npx",
"args": ["-y", "@grantor/mcp", "serve"],
"env": {
"GRANTOR_MCP_CONFIG": "/absolute/path/to/your-broker-config.json",
"GRANTOR_MCP_PRINCIPAL_KEY": "0x<your registered agent key>",
"GRANTOR_MCP_ADMIN_KEY": "0x<your tenant admin key, for revoke>"
}
}
}
}
Env vars, all optional except when the mode they gate needs them:
| Var | Meaning |
|---|---|
GRANTOR_MCP_CONFIG | Path to a broker config JSON ({rpcUrl, chainId, registry, tenant, ...}) — its presence is what switches the broker from sandbox mode to own mode. |
GRANTOR_MCP_PRINCIPAL_KEY | The delegation-root key grant signs with. Overrides the config file's principalKey; own-tenant mode requires one or the other. |
GRANTOR_MCP_ADMIN_KEY | The tenant admin key revoke needs for a real on-chain bumpEpoch. Without it, revoke refuses and prints the exact cast send command to run by hand. |
GRANTOR_MCP_STATE | Override the broker-held-child state file path (default ~/.grantor-mcp/state.json). |
Becoming a tenant is a handful of on-chain transactions, not a signup form:
register at chaingrantor.com/register.html
(USDC on Base, no account), or run the grantor-onboard kit's create/
enroll verbs to do the same from a script. See
docs/deploy/mcp-sandbox.md for the
operator side of standing up a shared broker deployment (what this
package's own sandbox is), or
docs/guide/mcp-broker.md for the full
product walkthrough.
check also accepts an externally-presented capability deed:
check({deed, challenge, tool}). This mode needs a caller-paired
challenge — the caller must present the exact challenge value its deed was
signed against, which this simple broker does not issue or track itself
(broker-held mode self-issues its own, as documented above). It exists so a
holder identity the broker does NOT control (minted via grant/delegate
with a to argument) can present its own deed for a one-off check — it is
not a general-purpose relying-party integration.
If you're building a real relying party — an HTTP API, an MCP server with
its own tool surface — verifying deeds presented by arbitrary callers, use
@grantor/verify's
DeedGuard/CapabilityGuard directly. It owns real challenge issuance
and tracking, origin binding, and the full deed lifecycle; this package's
check tool is a convenience for a broker managing its OWN children, not a
substitute.
check never throws — every refusal comes back as
{allow: false, code, reason}:
| Code | What it means |
|---|---|
CapabilityDenied | The deed is genuine and the caller is authenticated, but no grant covers this (tool, action) — wrong resource, or a failed caveat. |
UsesExhausted | This child's local use budget (broker-metered max_uses) is spent. |
EpochRevoked | The chain's live delegationEpoch no longer matches what this link was signed against — a bumpEpoch (real or, in the sandbox demo, pre-bumped) invalidated it. |
BadDelegation | The delegation chain itself doesn't check out — a link widened past its parent, a signature didn't recover, or the chain's root isn't a currently enrolled agent key. |
TenantInactive | The tenant is neither Active nor Grace on-chain — the billing gate. |
RevokedLocally | This specific child was revoked at this broker (a fast local short-circuit, checked before any chain read). |
UnknownChild | No broker-held child with that child_id — check the ID, or that you're pointed at the state file the grant call used. |
BadRequest | Malformed check call — neither child_id nor both deed and challenge were supplied. Every field on check's schema is optional, so this is reachable from a real MCP call, not just a hand-built one. |
Chain | An on-chain read failed (unreachable RPC, etc.) — fails closed, never allows. |
ResourceDenied | (wrap only, JSON-RPC error -32001) — resources/*/prompts/* are denied by default under wrap; relaunch with --allow-resources to pass them through. |
See docs/guide/errors.md for the full verifier-wide table these are drawn from.
Every tool is also a CLI verb, for scripting outside an MCP client:
node src/cli.js grant --tools search --max-uses 3 --ttl-secs 3600
node src/cli.js check --child <child_id> --tool search
node src/cli.js delegate --parent <child_id> --tools search --max-uses 1
node src/cli.js revoke --child <child_id>
node src/cli.js status
node src/cli.js demo # the first denial, scripted, in this terminal
node src/cli.js tools -- npx some-mcp-server # list a server's tools + suggest a wrap
SEE LICENSE IN LICENSE.
FAQs
Grantor permission broker for multi-agent frameworks — grant, delegate, check, revoke bounded capabilities over MCP, and wrap any stdio MCP server with enforced permissions. No authorization server anywhere.
We found that @grantor/mcp demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Product
Create ClickUp tasks from Socket alerts, automate ticketing with custom rules, and keep alert and task status synchronized.

Product
Create and manage Asana tasks directly from Socket alerts, with manual task creation, automated ticketing rules, and two-way sync.

Security News
Open VSX has removed three extension IDs from its malicious-extension list as the legitimate publishers they impersonated move to claim the names for themselves.