← Writing

Giving Claude access to our household finances

The permission model behind an MCP server with real money behind it: off by default, writes behind a second switch, and rules the database enforces.

· 2 min read· Guard

We built a private finance app for our own household. It tracks net worth across accounts and currencies, reads our bank alert emails, and keeps budgets and shared trip expenses. A few months ago I connected it to Claude over MCP, so I could ask "what did we spend on food this month compared to last?" in a chat instead of building another chart.

That meant deciding, concretely, what an AI model should be allowed to do with our money data. These are the decisions and the reasoning behind them.

Off unless someone turns it on

MCP access is off by default for each household. Write access is a second, separate switch, also off. Reading your net worth and adding a transaction are different levels of trust, and I didn't want one switch to grant both.

Today the server exposes read tools (net worth and its history, holdings, cashflow, budgets, upcoming vesting) and a handful of write tools (add a budget transaction, add a split expense, record a settlement). The write tools are deliberately the boring, easily reversible ones.

The model acts as a specific person

The connector uses OAuth 2.1, so Claude acts as the signed-in person, with their role, rather than through a shared system token. There is no password involved, tokens are scoped and short-lived, and they refresh automatically. If one leaks, the damage is bounded by both time and that one person's permissions.

This sounds obvious written down. It's also the part I got wrong first: the initial connector checked that you belonged to the household but not your role, so a viewer could write through Claude when writes were on. It's fixed, and I wrote about how I found it in a separate post.

Rules the database enforces, not the app

Authorization lives in Postgres row-level security, keyed to the household and the person's role. Application code still checks, but if some path forgets, the database refuses. For an MCP server this matters more than usual: the tool layer is the newest code and the most likely to have a gap.

I'll admit I first thought multi-household row-level security was over-engineering for a personal app. It turned out to be the thing that made connecting an AI model feel reasonable.

What I deliberately didn't do

  • No autonomous actions. Claude can add a transaction when I ask it to. Nothing runs on a schedule with write access.
  • No hidden context. The model sees what the tools return, not raw tables.
  • No broad "query anything" tool. Every tool is narrow and named for what it does. A generic SQL tool would have been faster to build and impossible to reason about.

What I don't know yet

I know what Claude is allowed to do. I don't yet know how well it picks the right tool, with the right arguments, for a given question. That's the next thing I'm building an eval for, in the same style as the classifier ladder: a fixed set of questions, the tool calls I'd expect, and a score that tells me whether a change made things better or worse.

Permissions tell you the worst case. Evals tell you the usual case. You need both before you trust an agent with anything that matters.