
Research
/Security News
OpenAPI React Query Codegen Compromised in Mini Shai-Hulud npm Supply Chain Attack
Ten malicious OpenAPI React Query Codegen versions were published to npm in the Mini Shai-Hulud attack, all with valid provenance.
@grantor/mcp
Advanced tools
Grantor permission broker for multi-agent frameworks — grant, delegate, check, revoke bounded capabilities over MCP. 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.
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.
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.
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. Two brokers, or two runs against the same config without a
shared state file, do not share a budget.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. |
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
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.
The npm package @grantor/mcp receives a total of 1,538 weekly downloads. As such, @grantor/mcp popularity was classified as popular.
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.

Research
/Security News
Ten malicious OpenAPI React Query Codegen versions were published to npm in the Mini Shai-Hulud attack, all with valid provenance.

Security News
Socket joins more than 100 technology, cybersecurity, and financial organizations calling for a global surge in cyber defense.

Product
Enterprise security teams can now detect malware, credential theft, suspicious network activity, and risky updates across Microsoft Edge extensions.