LABSCO SUMMARY
An MCP key is a full-account key — issue a separate one.
The README is direct about this: MCP tools can read and manage the key owner's vendors, categories and transactions, so connecting an agent is equivalent to handing over the account. Create a dedicated key for the MCP client and revoke it when you are done, rather than reusing the one your scripts already have. Also worth knowing before you commit: there is no bank sync, budget planning or net-worth tracking, and the project describes itself as early.
What it is
A self-hostable personal finance app — dashboards, categories, vendors, reports, searchable transaction history — that also exposes an MCP endpoint. The MCP surface reads and manages the same data the web app does, using the API key of whoever created it.
What you get
- Tools covering the current user, vendors, categories, transactions, dashboard summaries and statistics
- Transactions carry categories, notes, dates, vendors and currencies; multiple currencies convert to your default through Frankfurter
- Reversals are first class —
effect: 'reversal'records a refund or chargeback as a positive amount that reports subtract from the category - Summaries across daily, weekly, monthly, quarterly and yearly windows, with category split
- The same data is reachable over a REST API with the same key, or through a typed Node client
- Optional weekly and monthly email summaries with generated spending insights
- Vendor candidate search and enrichment, which calls the configured enrichment provider when one is set
Requirements
You run it yourself: Node.js 22, npm 11, and Docker with Docker Compose v2. Published as xpenser version 0.1.0. Setup is npm install, cp .env.example .env, build the three shared workspaces, docker compose up -d postgres, then npm run dev — the web app on port 3000 and the API on port 4000, with migrations run at API startup. The MCP endpoint is Streamable HTTP at /api/mcp and authenticates with an API key created in Settings → Preferences → API keys, sent as a bearer token. Single-user deployments can set XPENSER_SINGLE_USER_MODE=1 to skip account login in the web app; the API and MCP endpoint still require a key.
Setup effort
Run a container — build the image from the repository, then point your client at it
USE CASES
01
Logging a spend without opening the app
Add the transaction with its category, vendor and currency straight from the conversation.
02
Asking where the money went last month
Pull the monthly summary with its category split instead of reading a dashboard.
03
Recording a refund correctly
Enter it as a reversal so reports subtract it from the category rather than counting it as income.
04
Keeping finance data on your own machine
Self-host the whole stack and point the assistant at your instance rather than a third-party service.
BEFORE YOU INSTALL
What to know first
Requires
- Docker with Docker Compose v2, to run your own instance
- A client that can reach a remote MCP server over HTTP
- XPENSER_API_KEY: an API key created in your own xpenser instance
INSTALLATION
Connect your client
Build the image from the repository, then point your client at it.
Server setup
TERMINAL
git clone https://github.com/cleverbrush/xpenser.git
cd xpenser
cp .env.example .env
docker compose up --buildThat starts the web app, API and PostgreSQL. The web app answers on http://localhost:3000 and exposes the API under /api, so the MCP endpoint is http://localhost:3000/api/mcp. For a public deployment, put a reverse proxy in front of the web app and set APP_URL to its public origin.
Sign in to xpenser and create an API key under Settings → Preferences → API keys. Use a dedicated key for MCP clients: MCP tools can read and manage the key owner's vendors, categories and transactions.
Add the Any MCP client block below to your client; <YOUR_XPENSER_URL> is your instance's address, and ${XPENSER_API_KEY} is that key (written out, or as an environment variable your client expands). The client must support Streamable HTTP with custom headers.
MCP Clients
Any MCP client
ANY MCP CLIENT · JSON
{
"mcpServers": {
"xpenser": {
"type": "streamable-http",
"url": "<YOUR_XPENSER_URL>/api/mcp",
"headers": {
"Authorization": "Bearer ${XPENSER_API_KEY}"
}
}
}
}Values written as ${…} are filled in only by clients that expand variables.
Clients spell the "type" value differently: Claude Code and VS Code use "http", Cline uses "streamableHttp", and Roo Code uses "streamable-http". Claude Desktop does not accept a URL entry in its config file; remote servers go under Settings → Connectors.