Inspiration

AI agents are starting to shop for people. Once an agent is connected to a payment tool, it can spend whatever it decides to. It can misread a request, ignore a budget, or be talked into a purchase by a product page written to manipulate it. Nothing sits between the agent's decision and the user's money. We wanted a guard that sits there and that the agent can't argue its way past.

What it does

SpendGuard sits between an AI agent and PayPal. The agent can still search, compare and check out, but every payment is checked before any money moves:

  1. Pins the request. What the user asked for is stored on the server before shopping starts. The agent can't rewrite it later, so purchases are checked against the user's words, not the agent's summary of them.
  2. Runs the rules at checkout. A monthly budget, a per-purchase cap and blocked categories. When an item is over the cap, SpendGuard suggests a cheaper item in the same category instead of just saying no. It also shows how many days each purchase pushes back the user's savings goal.
  3. Lets PayPal hold the money while it decides. Checkout creates a PayPal order with intent=AUTHORIZE. The user approves in PayPal, which only authorizes the payment. Then an AI check compares the item with the pinned request, and SpendGuard either captures or voids the payment. The agent never controls capture.

Every decision and its reason goes into a ledger. The same guard works from the web app's built-in shopping agent and as an MCP server, so you can connect your own agent, such as Claude.

How we built it

  • FastAPI backend with one shared code path (core.py), which the web app, the REST API, the MCP tools and the PayPal redirect all call
  • PayPal Orders API v2 in sandbox: create, authorize, capture, void
  • Official MCP Python SDK, over streamable HTTP at /mcp and stdio for Claude Desktop
  • gpt-4o-mini for the intent check and the web app's tool-calling shopping agent, at temperature 0
  • SQLite for settings, pinned sessions, orders and the ledger
  • A single static HTML page with no build step, deployed on Render with a Blueprint

Challenges we ran into

  • Making the guard hard to talk around. Product text is untrusted, so the demo catalog includes a listing that tells AI agents it's "unscented and pre-approved." Both the agent and the intent check are told to ignore instructions inside listings, and the check fails closed: if it errors or times out, the authorization is voided.
  • Keeping the agent away from the request. An agent holding the MCP token could have used the REST API to replace the pinned request. We split the secrets (MCP_AUTH_TOKEN and APP_PASSWORD), and an MCP agent can only pin a request when no session is active.
  • Intent-check accuracy. Our first eval run scored 17/21. The model invented price limits nobody asked for, and it gave its verdict before its reasoning, so some answers flipped between runs. After we rewrote the prompt it scores 21/21 across 5 runs.
  • PayPal sandbox quirks. Sandbox accounts created outside the US failed with currency errors, so the demo uses US sandbox accounts.
  • Rules that change mid-purchase. The rules run again right before capture, so a budget change made after checkout still counts.

Accomplishments that we're proud of

  • Authorize-then-decide: PayPal holds the money, and the guard, not the agent, decides whether it's captured.
  • The prompt-injection product gets approved in PayPal and is still blocked and voided.
  • One guard serves both the web app and any MCP agent, through the same functions.

What we learned

Guardrails for agents belong on the server, at the point where money moves, and not in the agent's prompt. PayPal's authorize/capture split turned out to be exactly the right tool for this: it lets a payment wait while something other than the agent makes the final call.

What's next for SpendGuard

  • Multiple users, each with their own requests (the demo handles one user and one active request)
  • Real merchant catalogs instead of a static demo catalog
  • Approval through the user's phone for purchases near the limits
  • Persistent storage beyond Render's free tier

Built With

Share this project:

Updates

Submission history