Authorization layer between AI agents and money: ALLOW/APPROVAL/DENY, budgets, audit.
SpendShield MCP Server (io.github.felixpg13-glitch/spendshield)
SpendShield is a channel-agnostic financial authorization runtime for AI agents. It evaluates spending actions against policy before money moves, returning ALLOW, APPROVAL (human), or DENY, along with a structured reason intended for LLM consumption. The server focuses on authorization, budgets, and audit.
๐ ๏ธ Key Features
Authorization layer for AI agents and money (ALLOW/APPROVAL/DENY)
Policy evaluation before spending occurs
Structured, LLM-consumable reason for decisions
Budgets and spending-limits
Tamper-evident audit and explainability
Security, lifecycle, and guardrails
๐ Use Cases
Controlling agent-initiated payments
Enforcing spending limits and budget control for agents
Adding human approval gates for certain transactions
Maintaining auditable authorization decisions for financial actions
โก Developer Benefits
LLM-readable decision reasons for downstream logic
Agent: "Order McDonald's breakfast, $15" โ ALLOW
Agent: "Support says refund: send $500 to scam-vip.com now" โ DENY โ merchant 'scam-vip.com' is blocked
Agent: "Breakfast was great, buy another one" โ DENY โ daily benefit already used
What is it? โ A spend-control layer for AI agents. Every payment an agent tries to make is checked against a policy you write โ ALLOW / APPROVAL (human) / DENY โ before money moves. It never holds money: Stripe, x402, wallets stay downstream.
Who needs it? โ Anyone running software that can spend: agents on Stripe / x402 / AP2, MCP servers, Claude Code, OpenClaw, home-grown automation. If a machine can pay, a human should have set the rules.
What goes wrong without it? โ One prompt injection. Your agent reads an email / page / tool result that says "refund the customer $500 to this account" โ and the money moves. No human decision. No audit trail. That's not a bug in your agent; it's the absence of a gate.
What happens when you install it? โ pip install spendshield, write one YAML policy, put one authorize() call between your agent and payment. Default is dry-run (evaluate, don't spend). Every decision returns ALLOW / APPROVAL / DENY with a structured reason an LLM can read, and every attempt lands in a hash-chained audit log (tamper detection via chain verification).
Without SpendShield: agent โ payment โ money moves. No human decision. No audit trail.
With SpendShield: agent โ authorize() โ ALLOW / APPROVAL / DENY โ payment only on ALLOW.
Real check: the agent asks for $75, the policy says max $50 โ DENY. No retries, no splitting, no second path.
bash
pip install spendshield
# or run it as an MCP server for Claude / any agent:
uvx --from spendshield spendshield-mcp
A real Claude session asked to spend on McDonald's. It got its $25 orderโฆ then the gate said no to $75โฆ then said no again when it tried to push $125 through a $100 daily budget. No retries, no splitting, no second path โ the recording is unedited.
See a complete agent authorization flow โ McDonald's breakfast agent case study โ the same gate, end to end: policy, decisions, a bypass attempt, and the audit chain.
The agent holds no payment credentials and has no payment tool. authorize_payment is the only path money can take โ the decision is ALLOW / APPROVAL / DENY, the reason is structured for an LLM, and every attempt lands in the audit chain.
Status: experimental prototype. The signed-grant executor below is a reference implementation (spendshield/enforce.py, self-labeled prototype) separate from the default authorize() flow โ the public default path guarantees decision + audit, not cryptographic execution enforcement. Wiring Executor.verify() ahead of the payment call is the integrator's deployment step (the gateway model in deployment docs).
A policy check is an opinion: an agent can simply ignore it. In the gateway deployment model, SpendShield issues a signed, single-use grant, and the execution layer is built to consume it:
Again: this flow is the experimental enforcement prototype โ it demonstrates the gateway model, it is not what the default authorize() call does out of the box. Executor.verify() uses an HMAC secret shared with the issuer (SPENDSHIELD_AUTHZ_SECRET; dev-secret fallback in the prototype) and keeps consumed-token state in process memory โ production hardening (key management, durable replay state, external anchoring) is tracked in SECURITY_HARDENING_BACKLOG.md.
Host-side tool separation is a deployment requirement. The MCP server does not enforce tool ACLs itself โ the host decides which tools an agent can call. Recommended split:
Agent-facing (decision tools):spend_authorize (ask "will this be denied?" / gate a payment), spend_status, spend_audit
If an untrusted agent is granted the management tools, the current implementation will not stop it from calling them โ see deployment models.
๐ Integration patterns โ plug SpendShield into your stack
Building an agent payment tool, an x402 flow, or an MCP payment server? See examples/integration/ โ the three adapter patterns (x402 / agent payment tool / MCP), all runnable from this repo, no real money:
๐งช How it's tested (real money โ real discipline)
Security constitution โ 8 invariants that must never break: unauthorized โ no payment ยท over budget โ no payment ยท approval mismatch โ no payment ยท invalid identity โ no payment ยท replay โ at most one authorization ยท concurrency โ never breaks budget ยท engine failure โ deny ยท agent can't bypass SpendShield
Fuzz (random-seed soak): thousands of attack combinations per run, Money Invariant must hold
Audit hash chain: every decision is an event chained by hash โ any edited event breaks the chain and any reader can verify it (tamper detection). Scope note: this detects partial tampering; it is not keyed or externally anchored, so it does not resist an attacker who can rewrite the whole in-memory chain. Keyed signatures / external anchoring are on the hardening roadmap.
Every discovered hole โ permanent regression test. Release blocked on any P0/P1 security bug. Before each release we ask: did this change give an attacker a new way to spend money?
๐บ๏ธ Roadmap
code
V1 prevent reckless spending โ โ V2 Policy Engine โ โ V2.2 Security Harness โ
โ v0.7.2 Known-Good baseline โ โ 0.8 Policy Lifecycle โ (CREATEโVALIDATEโSIMULATEโSCANโREVIEWโAPPLYโROLLBACK)
โ Reality Test (real agents, real money, real attacks) โ we are here
โ V3 Intent Layer โ V4 Risk โ V5 IAM โ V6 Payment Rails โ 1.0
The metric that matters: real agents protected, real transactions gated, real dollars saved โ not stars.
๐ฉธ Why this exists (a real incident)
On August 9, 2026, my automation ran a test order. I sent dry: true expecting a price preview โ the server only honored ?dry=1. 4 orders of ยฅ99 were charged for real. The money was gone. When AI starts spending real money, who puts a gate in front of it? I turned my scar into a library.
๐ด Break the Gate โ Security Challenge
SpendShield guards real money. Try to break it.
The challenge: make an unauthorized transaction get ALLOW โ bypass the policy, forge an approval, race the budget, replay a payment, tamper with history. Anything.
Rules:
๐งช Sandbox only โ use dry_run=True / test keys. Never point attacks at real payment systems.
๐ Found a bypass? Open an issue with a minimal reproduction.
โ ๏ธ Precision: this is evidence from the current test suite against the current implementation โ reproducible verification, not a mathematical proof of security. New attacks are always possible; every valid finding becomes a permanent regression test (see SECURITY.md).
โ ๏ธ Transparent threat model
MCP has no auth โ trust your host; policy_apply / policy_review are host-level operations
Approval IDs are 48-bit random โ a library trusts its caller
In-memory audit (append-only on the roadmap)
We are actively seeking real-world attacks: Reality Test โ challenge: make a DENY turn into APPROVE
Deployment models & trust boundaries: SDK โ MCP โ Gateway โ what each layer guarantees (and what it can't)