Designing an MCP server that can spend money · CollabPal
All posts
Engineering9 min read

We gave an AI agent the keys to a creator program

Building an MCP server that can spend money meant answering a question most integrations dodge: what should an agent be allowed to do on your behalf?

By Leo · @leojr94_

Exposing a read-only API to an agent is a pleasant afternoon. Exposing one that can move money to strangers is a different exercise. These are the decisions that took the longest, and the ones I would defend again.

The key is a person, not a service account

The first instinct with machine access is a service account with its own permission set. We went the other way. An API key resolves to a real session for the user who created it, and every query runs through the same row-level security the browser uses.

This has a property worth the effort: there is no second authorisation system to keep in sync. When we added a rule that only owners can change a plan, agents inherited it the same day, because the tool calls the same server action the dashboard calls. A permission bug would have to exist in both places at once.

Failing loudly beats guessing

Our workspace resolution originally fell back to the user's first workspace when the requested one was not found. Harmless for a stale cookie in a browser. Not harmless when the value came from a language model.

An agent that passes a workspace it cannot access should get an error, not somebody else's workspace.

Confidentiality was never at risk, since the fallback only ever picked a workspace the user already belonged to. Integrity was: delete this campaign or pay these rewards would have executed somewhere the operator did not intend. Now a wrong id fails with a message naming the problem.

Three ways to make a key smaller

A key that can do everything forever is the wrong default for an agent you are trying out. Keys can be pinned to a single workspace, marked read-only so only inspection tools run, and given an expiry so an experiment cleans up after itself.

Read-only is enforced by tool annotation rather than a hand-maintained list, so a new read tool is covered automatically and a new write tool is blocked by default. Getting that direction right matters more than it sounds; the opposite arrangement fails open.

Some things should not be tools

It is tempting to expose everything for completeness. We deliberately left out changing an account email or password, deleting an account, managing API keys, and deleting a workspace.

The reasoning is simple. Those are the controls you reach for when a key has been compromised. An agent that can rotate your email owns the account permanently, and an agent that can delete the workspace can destroy the evidence. Completeness is not worth that.

Annotate the dangerous ones

MCP lets a tool declare that it is read-only or destructive, and good clients surface that to the user before running it. Every tool that spends money, removes a creator or deletes a campaign carries the destructive hint, and the descriptions say plainly what will happen.

Tool descriptions are the actual interface here. A model decides what to call based on prose, so the prose is load-bearing in a way ordinary API documentation is not. We write them to be read by a model at three in the morning with no context.

What we would tell someone starting

Reuse your existing authorisation rather than inventing a parallel one. Make ambiguity an error instead of a guess. Give people a way to hand over less than everything. And be honest in the tool description about which calls are irreversible, because the agent will believe you.

#mcp#engineering#ai agents#security