Skip to main content
Glama

imprest

ci

Programmable spend limits and audit trails so AI agents can pay for things without risking the wallet.

AI agents are probabilistic — they can be prompt-injected, loop, or simply choose the wrong tool. The moment an agent can move money, one bad decision is irreversible. imprest is the guardrail layer between an agent and an Ethereum wallet: every payment the agent requests is checked against a policy it cannot override, and every attempt is logged.

Think corporate-card controls (Ramp/Brex) or Stripe Radar — but for agents. The model in one line: give your agent an allowance, not your keys.

Why "imprest"? The imprest system is the centuries-old accounting control behind petty cash: a fixed fund is entrusted to a spender, every draw is documented, and the fund is replenished only after the records are audited. That is precisely what this server implements — for AI agents.

Quick start

pip install imprest

imprest init     # the one setup ceremony:
                      #   ✓ policy.yaml — your agent's limits (edit them)
                      #   ✓ a dedicated wallet, generated locally from OS entropy
                      #   → prints the address to fund
imprest status   # balances, gas headroom, active limits, sends switch

Then point any MCP client (Claude Desktop, Cursor, a LangChain agent — see examples/) at the server, and the agent gets request_payment and a verdict — nothing else:

{"imprest": {"transport": "stdio", "command": "imprest"}}

Defaults are safe by construction: Base Sepolia testnet, sends OFF until you explicitly set ENABLE_SENDS=true. On testnets you may even skip init — a throwaway wallet auto-creates on first use. On mainnet chains imprest refuses to create a key silently: real-money wallets only come into existence when a human runs init.

Related MCP server: valta-mcp

The wallet model: a prepaid card

The agent never gets your wallet. init generates a fresh, dedicated wallet for the agent; you fund it with only what the agent may spend, and top it up like a prepaid card. That makes the maximum possible loss the card balance — a physics-level cap that holds even if every software check failed. The policy engine is the soft limit; the balance is the hard one. Your real wallet (hardware, exchange, MetaMask) never touches imprest at all.

How it works

agent: "pay 10 USDC to 0xabc… for the data API"
   │
   │  MCP tool call: request_payment(…, asset="USDC")
   ▼
┌────────────────────── imprest ──────────────────────┐
│ policy engine:  per-tx cap · hourly/daily budgets        │
│ allow/denylist · rate limit · approval threshold         │
└────────┬───────────────────┬────────────────────┬────────┘
         │                   │                    │
       ALLOW          NEEDS_APPROVAL            DENY
         ▼                   ▼                    ▼
 preflight, sign,     queue for operator:   block + log;
 broadcast, wait      approve → re-check    agent gets the
 for confirmation     limits → execute      exact reason
         │
         ▼
 tx mined + audited

Payments move stablecoins (USDC) or ETH. Stablecoins are the spending lanes; in the default policy ETH is a gas-only lane with near-zero limits — any real ETH transfer attempt looks anomalous and gets denied or escalated.

The allowance ledger — the part nothing else has. Agents can also request_approval (a guarded ERC-20 approve()): always an exact amount, never unlimited — the vector behind most token drains. Granted allowances outlive every budget window, so imprest tracks them as standing liabilities: the total live across all spenders is capped (max_outstanding_allowance), and approve(spender, 0) revokes to free the cap. Rolling budgets alone can't see this risk; the ledger closes it.

A needs_approval verdict is not a dead end: it queues for a human operator, who approves or rejects (resolve_approval) — and hard limits are re-checked at approval time, so a human "yes" can't bust a budget.

x402 — pay-per-request APIs. Agents can also buy paid HTTP resources with pay_x402(url, max_amount): imprest does the x402 handshake (402 Payment Required → price quote), runs the quoted price through the same policy pipeline — caps, budgets, allowlist, approval queue — and only on ALLOW signs an EIP-3009 authorization for exactly the quoted amount (gasless; the recipient's facilitator settles on-chain). The server's quote is held to the agent's stated max_amount, the token contract must match the registry, and if a frozen payment is approved later the terms are re-fetched — a payee/asset change or a price hike refuses instead of paying.

The MCP server is the product; agents are just clients of it.

Design principles

  • Non-custodial, blast-radius first. Dedicated per-agent wallet, funded with pocket money. Keys are generated locally, never leave the machine, and never get created as a side effect on mainnet.

  • The RPC endpoint is untrusted. Gas price is capped by a configurable ceiling (MAX_FEE_GWEI) and gas limits are fixed, never estimated — a lying RPC can neither overprice nor inflate a transaction. Worst-case gas cost is bounded at gas_limit × ceiling, always.

  • Broadcast is not success. Every send waits for the receipt; reverts and timeouts fail the audit row. "Executed" means mined with status 1.

  • The policy engine is pure logic (src/imprest/services/policy.py) — no I/O — so it is exhaustively unit-tested. The code guarding money is the code under the most tests (171 across the engine, auth, audit, ERC-20, approvals, the allowance ledger, x402, the chain rails, and the CLI).

  • Config, not code. Limits live in policy.yaml. Each asset has its own limits and its own budget — 10 USDC never eats into an ETH ceiling — and a token is payable only if the policy names it.

Deploying it

The wallet owner runs the server; agents connect as clients and set nothing.

Local (stdio) — each MCP client spawns its own server process; identity is AGENT_ID, the OS is the auth boundary:

{"imprest": {"transport": "stdio", "command": "imprest"}}

Hosted (HTTP) — one server for the whole org; developers get a URL and an API key. The server refuses to start without keys (an open endpoint would mean anyone who can reach it can spend the budget):

TRANSPORT=streamable-http \
IMPREST_API_KEYS='sk-supp-…:support-bot,sk-proc-…:procurement' imprest
# or
docker build -t imprest . && docker run -p 8000:8000 \
  -e IMPREST_API_KEYS='…' -v $(pwd)/policy.yaml:/app/policy.yaml imprest
{"imprest": {"transport": "streamable_http",
              "url": "http://payments.internal:8000/mcp",
              "headers": {"Authorization": "Bearer sk-supp-…"}}}

The API key is the agent's identity: it selects that agent's policy section in policy.yaml and attributes its audit trail. The same request can be denied for support-bot and allowed for procurement — identity decides. Unauthenticated requests get a 401 before any tool runs.

For the approval flow in hosted mode, also set IMPREST_ADMIN_KEYS='sk-admin-…:ops' — a human with an admin key can list_pending_approvals / resolve_approval; agents (regular keys) cannot, so no agent signs off its own payment. Over stdio the local operator is the admin.

Hosted-mode operational notes. Bearer keys travel in headers — terminate TLS at your ingress; never expose the plain HTTP port publicly. Keep API and admin keys disjoint (the server refuses to start otherwise). The server is single-process today: budget checks are atomic within one process, but multiple workers/replicas against one audit.db are not yet safe. Run one replica per wallet.

Going to mainnet

Base mainnet (chain 8453) is the recommended target — Circle-native USDC and sub-cent gas. The sequence:

CHAIN_ID=8453 RPC_URL=https://mainnet.base.org imprest init
  1. init prints the funding address. Send it a small USDC float and a few dollars of ETH for gas — withdraw on the Base network, not Ethereum.

  2. Edit policy.yaml down to numbers you'd let an autonomous process spend. Set the allowlist to known recipients. If your agent will legitimately discover new payees (vendors, APIs), set unknown_recipient: ask — an off-allowlist payment then freezes in the approval queue for you to rule on (one payment, one ruling; the address is not remembered) instead of being denied outright. Every other limit still applies first, so an over-cap request to a stranger dies on the cap, never reaching the queue.

  3. Check the card: imprest status.

  4. Flip ENABLE_SENDS=true last.

Treat the wallet as a hot-wallet float (see limitations below): it should never hold more than you'd load onto a gift card.

Layout

main.py                          # repo-root shim (python main.py)
src/imprest/
├── main.py                      # console entrypoint (`imprest`)
├── cli.py                       # operator CLI: init (the ceremony) + status
├── application.py               # app factory: create_application()
├── api/payments.py              # MCP tools (transport)
├── services/
│   ├── policy.py                # ⭐ the policy engine — pure, tested
│   ├── audit.py                 # append-only SQLite audit log
│   ├── auth.py                  # Bearer API-key auth + per-request identity
│   ├── chain.py                 # web3 wrapper — gas rails, nonce lock, receipts
│   ├── tokens.py                # known-token registry (symbol → address/decimals)
│   ├── x402.py                  # x402 pay-per-request: 402 parsing, EIP-3009 signing
│   └── wallet.py                # dedicated wallet: explicit create, mainnet guard
├── schemas/schemas.py           # contracts (Decimal money, dataclasses)
└── configs/base.py              # pydantic settings
tests/                           # 171 tests
examples/demo_agent.py           # a LangChain agent that uses the server
examples/agent-shop/             # complete solo-dev setup: agent + operator
                                 #   approve/reject CLI (the mainnet-test rig)

Developing

git clone https://github.com/theoddalex/imprest && cd imprest
python -m venv .venv && source .venv/bin/activate
pip install -e ".[demo,dev]"
cp .env.example .env

pytest                           # 171 tests — the policy engine and the rails
python examples/demo_agent.py    # watch an agent get allowed / blocked / gated

Status

Tested live on Base mainnet — an LLM agent running the full verdict ladder with real USDC: payment allowed and mined, payment frozen for human approval then executed, over-limit payment denied, an exact-amount allowance granted and revoked (ledger and on-chain state verified in agreement), and a non-allowlisted recipient blocked. Every attempt in the audit log with the rule that fired; total gas for the ceremony ≈ $0.01. The setup used is examples/agent-shop/.

Working v1, mainnet-hardened chain layer: pure policy engine, per-agent + per-asset policies, guarded token approve() with the allowance ledger (total live allowances capped, revoke supported), human approval flow (admin-gated, re-checked at approval time), Bearer-key auth, append-only audit log, gas-fee ceiling + fixed gas limits (untrusted RPC), pending-nonce with a per-wallet lock, receipt-confirmed sends, balance preflight, init/status operator CLI, USDC on Base + Ethereum (mainnet and testnets), x402 pay-per-request purchases (policy-guarded EIP-3009 signing), stdio + hosted HTTP transports, and a LangChain demo agent. 171 tests.

Security checks

Every push runs the app-sec pipeline (.github/workflows/ci.yml), mirrored locally by make security:

  • bandit — SAST over src/ (the code handling keys, auth, and SQL)

  • Trivy — dependency CVEs (SCA), committed-secret scan (a wallet.key or API key in a commit fails the build), Dockerfile misconfig, and the built image (base OS + installed packages)

All findings gate at HIGH/CRITICAL. imprest deploys no custom smart contracts, so the risk surface is the application itself — these checks cover it; an external review is still the gate before serious funds.

Known limitations (read before mainnet)

These are deliberate boundaries of the current design.

  • The key file is unencrypted (wallet.key, permissions 0600). This is the prepaid-card trade-off: the wallet is designed to hold a small float, not savings. Anyone with file access to the machine can take the float. An encrypted keystore is on the roadmap; the mitigation today is the funding model itself.

  • The allowance ledger is conservative and off-chain. It assumes the full last-approved amount to each spender is still live (the real liability can only be lower than the cap), and it reconstructs state from imprest's own audit log: allowances granted outside imprest are invisible to it. Start from a wallet with no pre-existing approvals, or revoke them first.

  • Rate limiting counts only allowed spends. Denied and needs_approval attempts don't count toward rate_limit_per_minute, and the pending-approval queue is unbounded — a looping agent can flood the audit log.

  • Single wallet, single process. All agents sign from one keystore. The per-wallet nonce lock serialises concurrent sends within one process, but multiple workers/replicas against one audit.db are not safe. Run one replica per wallet.

  • stdio makes the caller its own approver. The "an agent can't approve its own payment" guarantee holds over HTTP (separate admin keys); over stdio the local operator is both. Hard limits are still re-checked at approval time.

  • Address validation is hex-shape only (no EIP-55 checksum) — a mistyped but well-formed address will send. Use the allowlist for known recipients.

Roadmap

  • On-chain allowance reconciliation. Cross-check the ledger against live allowance() reads so spent-down grants free the cap, and out-of-band approvals are detected instead of invisible.

  • Encrypted keystore. Password-protected key at rest (eth_account native), unlocked via env at startup.

  • Postgres audit backend. Replaces the SQLite file — unlocks multi-replica deployment and cross-process budget atomicity, lifting the single-process limitation. One swap, both wins.

  • Abuse limits. Count all attempts toward the rate limit; bound, paginate, and expire the pending-approval queue; retain/rotate the audit log.

  • Non-custodial hosted control plane. Split verdict from signing so a hosted imprest never holds customer keys: policy + audit + dashboard in the cloud, a client-side signer executing only server-issued, single-use vouchers — and, longer term, ERC-4337 session keys / spend permissions so the chain itself enforces the limits.

Deploying agents that spend money?

If your team is putting AI agents in front of real budgets and wants policy-guarded spending — budgets, approvals, audit — I'd like to hear about your use case.

Available Tools

7 tools
get_balanceA

Get the balance of an address (read-only).

Args: address: the 0x address to check asset: "ETH" (native, default) or a token symbol such as "USDC"

ParametersJSON Schema
NameRequiredDescriptionDefault
assetNoETH
addressYes

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description bears full burden. It states that the tool is read-only, which is helpful, but it does not disclose potential error conditions, rate limits, or any side effects (though none expected). Behavioral disclosure is minimal beyond read-only.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise with two sentences plus a docstring that front-loads the purpose. It includes only necessary information with no wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the lack of an output schema, the description does not explain the return format or units of the balance. Error handling and invalid inputs are not addressed. For a simple read operation, it is adequate but lacks completeness in return value specification.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must explain parameters. It defines 'address' as a 0x address and 'asset' as 'ETH' or a token symbol, adding a default value that is not present in the schema. This adds significant meaning beyond the raw schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states the verb 'Get' and the resource 'balance of an address'. It also clarifies the operation is read-only, which distinguishes it from sibling tools like request_payment or pay_x402 that involve transactions.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies the tool is for checking balances but does not explicitly state when to use it versus alternatives. It provides no exclusions or context about prerequisites, leaving usage somewhat implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_gas_priceA

Get the current gas price in gwei (read-only).

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full burden. It adds 'read-only' which is a behavioral trait, but does not disclose other aspects like authentication needs, rate limits, or network dependency. Adequate but not rich.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One concise sentence with no waste, front-loaded with the key action. Could benefit from a second sentence for additional context, but overall efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only tool with no parameters and no output schema, the description is sufficient. It tells the agent what it does and its read-only nature, covering the essential information.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so baseline is 4. The description does not need to add parameter info, and it correctly focuses on the tool's purpose.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'get' and the resource 'current gas price in gwei', and notes it's read-only. This distinguishes it from sibling tools like request_payment or pay_x402 which involve transactions.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage when you need the current gas price, but does not explicitly state when to use this tool versus alternatives (e.g., get_balance) or provide any exclusions or prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_pending_approvalsA

(operator only) List payments/approvals waiting for a human decision.

Requires an admin identity — an agent cannot see or clear its own pending approvals. Over stdio the local operator is the admin.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description fully discloses the behavioral constraint: operator-only, agent restriction, and admin role in stdio. This is sufficient transparency for a list operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, no wasted words, front-loaded with the core purpose. Every sentence adds essential information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no parameters and no output schema, the description covers all necessary context: what it lists, who can use it, and a specific deployment detail (stdio). No gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has zero parameters, so no parameter details are needed. Baseline 4 per instructions applies, and the description adds no extraneous parameter info.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool lists pending approvals/payments, specifies operator-only access, and distinguishes it from sibling tools like request_approval and resolve_approval by focusing on listing rather than creating or resolving.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states an admin identity is required and that agents cannot see their own pending approvals, providing clear context on when the tool is appropriate. It doesn't mention alternative tools but this is not critical given the specificity.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pay_x402A

Fetch a paid HTTP resource, paying for it over the x402 protocol ("402 Payment Required"). Use this for pay-per-request APIs that quote a price in the 402 response; the spend policy decides whether the quoted price is allowed, blocked, or requires human approval.

The payment is a signed one-time authorization for EXACTLY the quoted amount — never more — and it only happens if the quote passes policy.

Args: url: the resource to fetch (https) max_amount: the most you expect this to cost, in whole token units (e.g. 0.01 for 1 cent of USDC). If the server demands more, the request is refused before policy even runs. reason: what the purchase is for (recorded in the audit log)

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYes
reasonNo
max_amountYes

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Without annotations, the description discloses key behaviors: payment is a signed one-time authorization for exactly the quoted amount, never more, and only proceeds if policy passes. It does not detail side effects or error responses, but the core behavior is well explained.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose and protocol, followed by parameter details. It is moderately concise; every sentence adds value, though some minor redundancy exists (e.g., repeating the policy check).

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool has 3 parameters and no output schema. The description covers the function and parameters adequately but omits return value details and error handling (e.g., what the response contains or failure cases). It meets basic needs but leaves some gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description fully explains each parameter: url as the resource, max_amount in whole token units with an example (0.01 for 1 cent of USDC), and reason for audit logging. This adds significant meaning beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states 'Fetch a paid HTTP resource, paying for it over the x402 protocol.' It clearly identifies the tool's purpose and distinguishes it from siblings by specifying it's for pay-per-request APIs with price quotes.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description advises using this tool for APIs that quote a price in a 402 response and notes the spend policy decision. It implies limitations (max_amount blocks before policy) but does not explicitly compare to sibling tools like request_payment or get_balance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

request_approvalA

Grant a token spender an allowance so a contract (a marketplace, subscription, or swap) can later pull funds. The policy decides whether it is allowed, blocked, or needs human approval.

imprest approves an EXACT amount only — never an unlimited allowance, the vector behind most token drains. The allowance is capped by, and counts against, the same per-asset limits as a direct payment, and the TOTAL of live allowances across all spenders is itself capped (an allowance outlives budget windows, so it is tracked as a standing liability). Approving 0 revokes the spender's allowance and frees cap.

Args: spender: the 0x address being granted the allowance amount: the allowance, in whole units of asset (e.g. 25 for 25 USDC); 0 revokes this spender's existing allowance asset: the token symbol (e.g. "USDC"); native ETH cannot be approved reason: what the approval is for (recorded in the audit log)

ParametersJSON Schema
NameRequiredDescriptionDefault
assetYes
amountYes
reasonNo
spenderYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Since no annotations are provided, the description fully bears the transparency burden. It discloses critical behaviors: exact amount capping, per-asset and total allowance limits, and the revocation effect of 0. No annotation contradiction exists.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections and a parameter list. Every sentence adds value, though a slight trimming (e.g., 'the vector behind most token drains') could improve conciseness. Overall efficient and front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite no output schema, the description covers purpose, parameters, and behavioral constraints comprehensively. However, it omits what the function returns (e.g., approval ID or status), which is a minor gap for full contextual completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must compensate. It does so thoroughly: explains spender as 0x address, amount in whole units with 0 revocation, asset as token symbol with ETH exclusion, and reason for audit logging. This adds essential meaning beyond schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: granting a token allowance so a contract can later pull funds. It uses specific verbs ('grant', 'allowance') and distinguishes from sibling tools like request_payment by focusing on future contract pulls.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit usage guidance: it is for exact amount allowances only, never unlimited; 0 revokes; and it mentions policy involvement (allowed/blocked/human approval). It implies not to use for direct payments, leaving that to siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

request_paymentA

Request to pay an address. The spend policy decides whether it is allowed, blocked, or requires human approval. Use this whenever you need to send a payment; do not attempt to move funds any other way.

Args: recipient: destination 0x address amount: amount to send, in whole units of asset (e.g. 0.05, 50) reason: what the payment is for (recorded in the audit log) asset: what to send — "ETH" (native, the default) or a token symbol such as "USDC". Each asset has its own policy limits.

ParametersJSON Schema
NameRequiredDescriptionDefault
assetNoETH
amountYes
reasonNo
recipientYes

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Discloses that spend policy may trigger approval, which is critical. Missing details on idempotency, failure modes, or return behavior. Adequate given no annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two paragraphs plus args list are efficient and front-loaded. No wasted words, but the args list is somewhat redundant with schema. Good overall.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Missing return value description (e.g., payment request ID) and lacks comparison with sibling pay_x402. Incomplete for a payment tool with no output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Adds full meaning for all 4 parameters (recipient as 0x address, amount in whole units, reason for audit log, asset with default and token symbol). Compensates for 0% schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states 'Request to pay an address' and 'send a payment,' but does not explicitly differentiate from sibling tool pay_x402. Purpose is strong but lacks sibling contrast.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Use this whenever you need to send a payment; do not attempt to move funds any other way,' providing clear context. Does not specify exclusions or compare with pay_x402.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

resolve_approvalA

(operator only) Approve or reject a pending payment and, on approval, execute it. This is the resume path for needs_approval.

A human approval overrides only the approval threshold — the hard limits (per-transaction cap, budgets, deny/allow list) are RE-CHECKED against the current ledger at approval time, so an approval that would now bust a budget is refused rather than forced through.

Args: payment_id: the audit id of the pending row (from list_pending_approvals) approve: True to approve and execute, False to reject note: optional operator note recorded on the row

ParametersJSON Schema
NameRequiredDescriptionDefault
noteNo
approveNo
payment_idYes

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden. It discloses critical behavior: re-checking of hard limits at approval time and refusal if budget is busted. It does not detail authorization beyond 'operator only' or rate limits, but the key behavioral trait is well explained.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured: a concise purpose statement, a critical caveat paragraph, and then parameter documentation. All sentences are informative, though slightly longer than strictly necessary. It is front-loaded with the main action.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema and no annotations, the description covers the main behavioral aspects (execution on approval, re-checking of limits, operator notes). It lacks details on return values or error scenarios, but the complexity is moderate and the description is comprehensive enough for agent understanding.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so description must compensate. It provides clear semantics for each parameter: payment_id is 'the audit id of the pending row (from list_pending_approvals)', approve is 'True to approve and execute, False to reject', and note is 'optional operator note recorded on the row'. This adds significant value beyond the schema types and defaults.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Approve or reject a pending payment and, on approval, execute it.' It identifies the specific verb (approve/reject) and resource (pending payment), and distinguishes itself from siblings like list_pending_approvals and request_approval by specifying it is the 'resume path for needs_approval'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description indicates it is '(operator only)' and explains the context: 'the resume path for needs_approval.' It gives clear conditions for use (when a payment is pending approval) but does not explicitly mention when not to use it compared to alternatives like pay_x402 or request_payment.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 7 tool updatesv0.1.0
    • First observedget_balance
    • First observedget_gas_price
    • First observedlist_pending_approvals
    • First observedpay_x402
    • First observedrequest_approval
    • First observedrequest_payment
    • First observedresolve_approval

TDQS

A4.2/5.0

Scored across 7 tools

Disambiguation4/5

Each tool has a distinct purpose: direct payment, token allowance, paid HTTP fetch, operator pending-action management, and read-only balance/gas. However, 'request_approval' can be misread as requesting human approval, which overlaps conceptually with list_pending_approvals/resolve_approval, though the descriptions make the distinction clear.

Naming Consistency4/5

The tool names mostly follow a consistent verb_noun pattern: request_*, list_*, resolve_*, get_*. 'pay_x402' is a slight outlier as a verb plus protocol identifier, and 'request_approval' is semantically a bit misleading, but there is no mixed casing or chaotic verb usage.

Tool Count5/5

Seven tools is well-scoped for a crypto spend-control server: three spend paths (payment, allowance, x402), two operator approval tools, and two read-only helpers. The set is neither bloated nor too thin.

Completeness4/5

The core lifecycle is covered: requesting payments, granting/revoking allowances, paying for HTTP resources, and operator approval of pending actions. Minor gaps include no audit/history listing, no way to query current allowances or policy limits, and agents cannot list their own pending requests, but these are workable around.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    An MCP server for Valta that exposes financial governance tools for AI agents, including spend authorization and audit trail via MCP-compatible clients.
    16
    39 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server that enables AI agents to request human approval before spending money, check approval status, verify signed tokens, and manage API keys.
    2 npm
    MIT