Inspiration
The premise: the agent is assumed compromised. Not "the agent might make a mistake" — assumed compromised, the same way you'd design a system if you knew an adversary was already inside it. Every design decision in Pocket Change follows from that one sentence: if you're going to hand an AI agent a wallet, the system has to hold up even when the agent itself is the threat, not just when it's well-behaved.
What it does
Pocket Change gives an AI agent bounded, revocable spending authority — enough to actually get things done, never enough to do real damage.
- A person authorises a mandate: a ceiling, a purpose, an expiry.
- The agent can act only within that mandate. Every payment request passes through a gateway enforcing nine deterministic checks — depth, budget floor, fan-out, per-node budget, cycle detection, conservation of spend, and a hard rail cap — none of which a model can argue its way around, including ours.
- Every decision (allowed, denied, or escalated to a human) is written to a hash-linked audit log, so the log can prove nothing was altered after the fact — not that it was never rewritten wholesale, but that no entry was quietly edited in place.
- Payments run through Razorpay in test mode — never real money — while the authority model itself is designed to be real.
For this hackathon, the console also exposes six WebMCP tools, so an
agent working through the browser can read everything about its own
authority — spend status, counterparty history, the audit trail, the shape
of an in-progress run, which safety layers are actually enforcing right now
— without ever being handed a tool that can move money, mint a token,
widen a mandate, or approve a held payment. It can look, and it can propose
(draft_requisition, which mints nothing), but the only path to actually
spending still runs through a person clicking a button in the console.
How we built it
- Gateway: a FastAPI service enforcing the nine checks server-side, so no client — including the browser console — can bypass them. Reads are public; writes require the demo token.
- Capability tokens: Biscuit tokens carry attenuated authority, so a delegated task can only ever have equal or narrower permissions than its parent, never wider.
- Decomposition/funnel model: complex requests get broken into a tree of smaller tasks, each with its own budget, depth limit, and fan-out cap — the console renders this tree live over SSE.
- Console frontend: a five-pane live view (via SSE) of an in-progress
run, plus
frontend/webmcp.js, which registers the six read/propose-only tools with the browser's WebMCP API (navigator.modelContext.registerTool), each one calling the same GET routes and/intakeendpoint the console itself uses — no separate, more-privileged backend door for agents. - Deployment: Cloud Run (
asia-south1),--min-instances=1to avoid a cold-start during judging,--no-cpu-throttling. - 424 Python tests and 20 Node tests, including a dedicated adversarial suite trying to get the gateway to approve something it shouldn't.
Challenges we ran into
- The WebMCP spec itself is still moving. Documentation (including
Chrome's own developer docs) describes the API as
document.modelContext, but the actual shipped implementation in current Chrome exposes it asnavigator.modelContextinstead — with a separatenavigator.modelContextTestingsurface (listTools(),executeTool()) for verifying registration without a live agent. This meant tools were silently failing to register — no error, just an empty tool list — until we traced it back to a single feature-detection check on the wrong global. - Deciding what not to build was harder than building what we did.
It would have been easy to add a WebMCP tool for
/delegateor/pay— more "impressive" on paper — but that would have meant putting a spendable capability inside a public page. The interesting engineering problem was making the read-only boundary airtight, not working around it.
What we learned
- Emerging browser APIs ship inconsistently even across "the same" browser
family — testing against the real, current implementation (not the docs)
caught a bug that unit tests alone never would have, since Node has no
navigatorglobal to catch the mismatch. - A safety boundary is most convincing when it's structural (no route exists) rather than behavioral (a tool declines to). No one has to trust that an agent chose not to spend; there's simply nothing on the other end of the wire that could.
What's next for Pocket Change
- Extending WebMCP coverage as the spec stabilizes — particularly once
cross-document tool discovery (
getCrossDocumentScriptToolResult, which we noticed on thenavigator.modelContextTestingprototype but didn't use) has clearer semantics.
Built With
- ai
- api
- biscuit
- build
- chrome
- cloud
- docker
- events
- fastapi
- javascript
- pytest
- python
- razorpay
- rest
- run
- server-sent
- vite
- webmcp
Log in or sign up for Devpost to join the conversation.