Give an AI agent a wallet it can spend from on its own, but only within rules you set. Spend limits, an allowlist, an approval threshold for large payments, and a full audit trail, on Circle's Arc (where USDC is the native gas token). Exposed as MCP tools, so a Claude Code agent gets safe payment ability instantly.
AI agents are starting to pay for things on their own: API access, compute, services, small invoices. Stablecoins are the natural rails for that, and Arc is built for it (USDC as gas, sub-second finality, a Circle and Claude Code partnership).
But to let an agent pay, someone has to give it a private key. An agent can be prompt injected, can hallucinate, can be tricked into sending funds to the wrong address. Hand it the raw key and it can drain the wallet. So agent payments mostly stay demos: everyone wants them, nobody wants to take off the safety.
arc-agent-guard is the safety. The key lives behind the guard. The agent asks to pay,
and the guard decides.
The agent calls guard_pay(to, amount). Before anything is signed, the guard evaluates
the payment against your policy and does one of three things:
- executes it, if it is within every limit,
- queues it for your approval, if it is at or above the approval threshold,
- denies it, if it breaks any rule (bad recipient, over a limit), and nothing is sent.
A hard denial always wins over "needs approval", so a rule-breaking payment is never merely queued. Only executed payments count toward the daily and total limits, so a denied or pending payment never eats your budget. Approving a queued payment re-checks the limits at that moment, so an old approval cannot push you past the daily cap.
| Rule | Meaning |
|---|---|
per_tx_max_usdc |
max USDC per single payment |
daily_cap_usdc |
max total USDC per UTC day |
total_budget_usdc |
max total USDC ever |
approval_threshold_usdc |
payments at or above this need human approval |
allowlist |
recipient addresses the agent may pay |
allow_any_recipient |
if false (default), only the allowlist is payable (deny by default) |
enabled |
master switch; if false, every payment is denied |
The default policy is deny-by-default: with no allowlist and allow_any_recipient off,
nothing goes out until you set rules.
| Tool | What it does |
|---|---|
guard_set_policy |
set or update limits (only the fields you pass change) |
guard_allowlist_add / guard_allowlist_remove |
manage payable recipients |
guard_status |
policy, remaining daily and total budget, pending count, wallet |
guard_check |
dry run a payment, no send |
guard_pay |
the gated payment (execute / queue / deny) |
guard_list_pending |
payments waiting for approval |
guard_approve / guard_reject |
sign off or refuse a queued payment |
guard_audit_log |
recent attempts with decision, reason, tx hash |
guard_wallet_balance |
the guarded wallet's USDC balance |
cp .env.example .env
# set ARC_PRIVATE_KEY to a throwaway testnet key, funded at https://faucet.circle.com
uv run -m guard.serverRegister it with an MCP client (Claude Code / Claude Desktop):
{
"mcpServers": {
"arc-agent-guard": {
"command": "uv",
"args": ["--directory", "/path/to/arc-agent-guard", "run", "-m", "guard.server"],
"env": { "ARC_PRIVATE_KEY": "0x..." }
}
}
}Then, for example: set a policy of 10 USDC per tx, 50 a day, allow one supplier address, and require approval above 20. The agent can now pay that supplier small amounts on its own, and anything bigger waits for you.
- The private key is read from the environment and stays inside the sender. It is never a tool argument, never returned, never logged.
- The agent can never raise its own limits in a way that bypasses the guard: every send
goes through
evaluatefirst. - Built for testnet. Use a throwaway key. The guard reduces risk, it does not remove it: treat the wallet as money you can afford to lose.
The safety-critical logic (policy evaluation, spend accounting, the deny-beats-approval precedence, the approval re-check) is covered by a deterministic test suite that runs without a chain:
uv run -m pytestMIT, see LICENSE. Built on arc-testnet-mcp.