Expense Budget Tracker
Self-hosted open-source expense and budget tracker with balances, transfers, and multi-currency reporting on Postgres.

Live demo: expense-budget-tracker.com
Features
- Fully open-source โ all code is available, deploy on your own servers with full control over your data
- Hosted MCP server โ connect a compatible remote MCP client over Streamable HTTP and OAuth to query workspace data, with optional write access controlled by scope
- SQL Query API โ generate an API key, give it to your LLM agent, and let it query, analyze, and manage your financial data via HTTP. Minimal, flat table structure designed to be hard to misuse โ ideal for AI agents
- Budget and transaction UI โ built-in interface for budgeting, browsing transactions, and tracking balances across accounts and currencies
Quick start
git clone https://github.com/kirill-markin/expense-budget-tracker.git
cd expense-budget-tracker
open -a Docker
make up
Open http://localhost:3000.
Usage with AI agents
Connect through either the hosted MCP server or the direct Agent API. Their credentials are separate and are not interchangeable: MCP uses OAuth Bearer access, while the Agent API uses an ApiKey.
MCP connector
The hosted MCP server is available directly at https://mcp.expense-budget-tracker.com/mcp. Its canonical MCP Registry identity is com.expense-budget-tracker/expense-budget-tracker; it will be available through the Registry only after the manual publication workflow succeeds and exact-version verification completes. It exposes four workspace-scoped tools:
list_workspaces โ list workspaces available to the signed-in user
get_schema โ inspect the allowed relations, columns, constraints, and limits
sql_query โ run one restricted SELECT or WITH...SELECT
sql_execute โ run one approved INSERT, UPDATE, or DELETE
Compatible remote MCP clients authenticate with OAuth authorization code + PKCE and Dynamic Client Registration. The required expenses:read scope enables the three read tools; a client may also request expenses:write, which is required for sql_execute.
Agent API
Start at GET https://api.expense-budget-tracker.com/v1/. The discovery response tells agents to ask for the user's email first, and the same email OTP flow covers both signup and login.
- Open
GET https://api.expense-budget-tracker.com/v1/ in your agent โ it will discover the OTP onboarding flow automatically
- Complete email OTP login โ the auth service returns a long-lived
ApiKey
- Give the key to your AI agent โ Claude Code, Codex, or any agent that can call HTTP APIs
- Send the agent screenshots, CSV files, or PDF bank statements โ it parses them and inserts transactions through the single-statement SQL execute endpoint
- Open the web UI โ view actual spending by category and plan the budget for the next month
For readonly work, agents send one SELECT or WITH...SELECT statement to the primary query endpoint:
curl -X POST https://api.expense-budget-tracker.com/v1/sql/query \
-H "Authorization: ApiKey ebta_..." \
-H "X-Workspace-Id: workspace-id" \
-H "Content-Type: application/json" \
-d '{"sql": "SELECT ts, amount, currency, category FROM ledger_entries ORDER BY ts DESC LIMIT 20"}'
For an explicitly approved write, agents send one INSERT, UPDATE, or DELETE statement to the primary execute endpoint:
curl -X POST https://api.expense-budget-tracker.com/v1/sql/execute \
-H "Authorization: ApiKey ebta_..." \
-H "X-Workspace-Id: workspace-id" \
-H "Content-Type: application/json" \
-d '{"sql": "INSERT INTO ledger_entries (event_id, ts, account_id, amount, currency, kind, category, counterparty, note) VALUES ('"'"'evt-001'"'"', '"'"'2025-03-15 12:30:00+00'"'"', '"'"'chase-checking'"'"', -42.50, '"'"'USD'"'"', '"'"'spend'"'"', '"'"'groceries'"'"', '"'"'Whole Foods'"'"', '"'"'Weekly groceries'"'"')"}'
After POST /v1/workspaces/{workspaceId}/select, the API key remembers that workspace, so X-Workspace-Id becomes optional on later /v1/sql/query, /v1/sql/execute, and compatibility /v1/sql calls; send the header to override the saved selection. If the user has exactly one workspace and no saved selection yet, the API auto-saves and uses that single workspace. POST /v1/sql remains available only for compatibility when an atomic multi-statement script is required.
Documentation
Security considerations
-
For full privacy, self-host on your own AWS account. If you deploy or use the hosted service with the AWS/CDK setup described in infra/aws/README.md, the LLM chat runtime stores transcript state in Postgres. That means chat data is available to:
- the deployed service operator, because the data is stored in the service database
- OpenAI, because chat requests are sent there to power the feature
- Langfuse Cloud, because chat telemetry is exported there
If you use chat to import bank statements, screenshots, PDFs, CSVs, or other financial files, data extracted from those files can also reach OpenAI and Langfuse as part of chat processing and tracing. If you do not trust even one of these parties, do not store your financial data in this hosted deployment.
-
The code is already deployed, and the maintainer stores his own real finances there. Only maintainer Kirill Markin has access to the demo database. For partial privacy, sign up with an email that doesn't contain your real name. Try the demo โ
License
MIT