Back to portfolio
  • Agentic AI
  • Solo human developer: design, architecture and review, built with AI coding agents
  • Jan - Oct 2026

Finance Assistant

A local-first personal finance app where a Claude agent organizes bank statements into your own categories, and totals stay exact.

Finance Assistant

The problem

Most finance apps give you fixed categories and trust their own automatic classification. I wanted to know exactly how much a household spends on utilities, on the kids or on the odd impulse purchase, across several banks, with numbers I could trust. Each project (a family, a studio, a small business) has its own blueprint of categories, groups and rules.

What I built

A monorepo of five parts, started with one command:

  • A Next.js interface with chat, dashboard, statement import, review queue and a visual blueprint editor.

  • A FastAPI service with the database (SQLite by default, Postgres optional) and migrations that run at startup.

  • An assistant service that streams chat over SSE and runs the batch jobs.

  • A tool registry served as an MCP server, and a Claude Code plugin with commands and the domain rules.

I was the only human developer, in charge of design, architecture and review. Part of the implementation was done by AI coding agents, including GitHub Copilot's coding agent.

How the agent works

  • Locked-down agent. For each chat session a headless Claude Code process runs with all its native tools turned off: no shell, no files. It can only call the finance tools, and sessions are resumed after inactivity and dropped after a few hours.

  • Domain rules as a skill. The system prompt lives in the plugin: sign conventions, absolute dates, read before writing, confirm before deleting or bulk actions, and a table that says when to show a UI component instead of text.

  • The AI proposes, the user confirms. Every transaction records who decided its category. The agent cannot save a blueprint: it validates it, shows the impact on existing transactions in a review card, and the save is a button the user presses.

  • Exact totals. The agent is forbidden from adding numbers. For any total it calls a report that sums stored data, excludes transfers between own accounts and returns the confirmed versus suggested share.

  • Constrained classification. On import the order is saved rule, then the file's own category column, then a model restricted to the leaf categories through an enum schema, with a fallback to rules when no model is available.

Interesting decisions

  • One tool registry, two engines: the same tools are served over MCP for Claude Code and exported as function calling for OpenAI, so the UI does not know which engine answers.

  • Project scoping enforced at the database session level instead of in each endpoint.

  • Imports detect the file format from content, deduplicate across overlapping statements and are undoable as a batch.

  • Confirming a merchant creates a rule reused by future imports, even across different banks. Renaming a category never rewrites data, thanks to stable keys and aliases.

  • Local first: services listen on localhost only and data lives in a local database. The docs state plainly that statement text is sent to the model provider for classification.

What I would do differently today

  • The classification eval has 57 hand-labelled transactions, mostly well-known merchants. It is a regression check, not a measure of real accuracy, and I would add a harder set.

  • The OpenAI engine is only covered by a fake client in tests and has never been run against the real API.

  • There is no authentication: it is built to run on one machine, and would need it before any hosting.

Need something similar?

Tell me about your project: I usually reply within one working day.

Get in touch
Next projectLocal Buddy Travel