Onchain spending mandates for AI agents

Give your agent a mandate, not a wallet.

A human funds a project with USDC. Agents receive sub-mandates and can delegate their own — never more than they hold. Every limit is enforced in the onchain program, and any branch is freezable in an instant. Agents reach their mandate over MCP and need no gas.

ChainsSolana · Base · Tempo · HyperEVM
AccessMCP · 9 tools
Agent gasnone — relayer pays

The problem

A key is all-or-nothing. A mandate is not.

Agents are starting to spend money — on data, compute, APIs, and each other. Hand an agent a funded key and one bug or prompt injection drains it. Existing agent wallets are flat (one human → one agent) and off-chain guardrails are bypassable. Real agent systems are hierarchies, so the mandate should be the primitive.

Flat agent walletsQuaestor
StructureOne human → one agentArbitrary hierarchy — orchestrator → researcher → scraper
Delegation—An agent sub-delegates, capped by what it holds
LimitsBalance onlyPer-tx + time-window + expiry, tighter-or-equal down the tree
EnforcementServer / SDKIn the onchain program
Kill switchRevoke one keyFreeze a node → the whole subtree stops
Agent needs gas?YesNo — a relayer pays the fees
Blast radius if compromisedThe whole walletOne mandate node

How it works

The mandate is the first-class object.

Compromise an agent and you leak a mandate, never a wallet. Three ideas make that true.

I

Hierarchical delegation

Each agent holds a mandate node and can sub-delegate to children — capped by what it holds, with per-tx, daily and expiry limits that only tighten down the tree.

II

Enforced in the program

Every spend is checked against the entire ancestor chain inside the onchain program — not a server that can be bypassed. Freeze one node and its whole subtree stops at once.

III

No gas for agents

A relayer pays fees and rent, so agents need no SOL or ETH. The agent’s key stays encrypted in a local keystore — the model never sees a secret.

Invariantspent + reserved_for_children ≤ allocated— checked on every transaction, at every node.

Set up the MCP

From clone to a mandated agent.

Quaestor runs locally over stdio — one MCP instance per agent. Prerequisites: Node 22 and pnpm 10; plus the Solana CLI and Anchor for Solana, or Foundry for EVM. Everything runs on devnet / testnet.

  1. 1

    Clone and build

    Build the SDK and the MCP server from the monorepo.

    terminal
    git clone https://github.com/raglibol-re/Quaestor
    cd Quaestor
    pnpm install
    pnpm --filter @quaestor/sdk build
    pnpm --filter @quaestor/mcp-server build
    anchor build            # Solana only — builds the program + IDL
                            # EVM: build the contracts with Foundry (see docs/evm.md)
  2. 2

    One-time owner setup

    As the human owner, mint test USDC, create a relayer, open a project, deposit, and create your top agent. This prints the PROJECT address you’ll paste below.

    Solana — owner CLI
    export QUAESTOR_RPC_URL=https://api.devnet.solana.com
    export QUAESTOR_OWNER_KEYPAIR=~/.config/solana/id.json
    export QUAESTOR_KEYSTORE_PASSPHRASE='choose-a-strong-passphrase'
    
    # a test USDC mint you control
    node packages/mcp-server/dist/cli.js create-mint                   # -> MINT
    node packages/mcp-server/dist/cli.js fund --mint <MINT> --amount 100
    
    # a relayer that pays fees, so agents need no SOL
    solana-keygen new -o ~/.quaestor/relayer.json
    solana airdrop 2 $(solana-keygen pubkey ~/.quaestor/relayer.json) -u "$QUAESTOR_RPC_URL"
    
    # create the project, deposit, and the top agent
    node packages/mcp-server/dist/cli.js init --mint <MINT> --project-id 1 \
      --relayer $(solana-keygen pubkey ~/.quaestor/relayer.json)       # -> PROJECT
    node packages/mcp-server/dist/cli.js deposit --project <PROJECT> --amount 10
    node packages/mcp-server/dist/cli.js agent-create orchestrator     # -> AGENT

    On Base (EVM) you can skip deployment entirely — the generator below is prefilled with the live Base Sepolia contracts. Quaestor is also live on Tempo Moderato and Hyperliquid (HyperEVM) — same contract, same MCP tools. To deploy your own, see docs/evm.md.

  3. 3

    Generate your Claude config

    Pick your chain and host, fill in the fields, and copy the result. Defaults are prefilled so the output is valid out of the box.

    claude_desktop_config.json
    {
      "mcpServers": {
        "quaestor-orchestrator": {
          "command": "node",
          "args": [
            "<ABS_PATH>/packages/mcp-server/dist/server.js"
          ],
          "env": {
            "QUAESTOR_RPC_URL": "https://api.devnet.solana.com",
            "QUAESTOR_PROJECT": "<PROJECT>",
            "QUAESTOR_AGENT": "orchestrator",
            "QUAESTOR_KEYSTORE_PASSPHRASE": "choose-a-strong-passphrase",
            "QUAESTOR_RELAYER_KEYPAIR": "~/.quaestor/relayer.json"
          }
        }
      }
    }

    The passphrase is stored in your host’s config in plain text — use a throwaway devnet passphrase, never a mainnet key. Quaestor runs on devnet / testnet; the agent’s key stays encrypted in a local keystore and the model never sees it.

  4. 4

    Paste and restart Claude

    Add the block to claude_desktop_config.json (Claude Desktop) or .mcp.json at your project root (Claude Code), then restart. The Quaestor tools — get_my_mandate, pay, and the rest — appear in the agent.

  5. 5

    Spin up subagents

    When an agent calls create_submandate, the tool returns a ready-to-paste config snippet for the child. Start a second MCP instance for it the same way — the tree grows itself.

No install

Or use it in claude.ai.

Quaestor also runs as a live remote connector — add one URL in claude.ai (web or app), no local setup. Each connection authorizes over OAuth and is bound to exactly one mandate node; the agent key is custodied server-side and the model never sees it.

claude.ai → Settings → Connectors → Add custom connector
https://quaestor-connector.vercel.app/api/mcp
I

Add the connector

In claude.ai: Settings → Connectors → Add custom connector → paste the URL above and connect.

II

Approve by wallet

The project owner signs a Sign-In-With-Solana message and picks one mandate node. A delegated sub-mandate uses a one-time invite code instead — no wallet.

III

Tools appear

The same nine tools show up in the agent, bound to that node. Limits stay enforced onchain. Set up the connector →

The tools

Nine tools, one mandate.

What an agent sees over MCP. Amounts are decimal USDC; errors are human-readable, e.g. Per-transaction limit exceeded: limit 0.50 USDC.

get_my_mandate

Available balance, allocated / spent, per-tx and daily limits, status and expiry.

pay

Send USDC to a recipient wallet — enforced onchain against every limit and ancestor.

create_submandate

Delegate a sub-mandate (limits ≤ your own) and get a ready-to-paste config snippet.

list_subagents

This agent's direct subagent mandates, with status and balances.

freeze_subagent

Freeze or unfreeze a subagent — instantly blocks it and its whole subtree.

revoke_subagent

Permanently revoke a subagent (terminal); requires owner authority.

reclaim_subagent

Pull a revoked or expired subagent's unused funds back into your balance.

get_history

Recent transactions touching this agent's mandate account.

pay_x402

Fetch a paywalled URL; on HTTP 402, pay the required USDC straight from the mandate.