ExpensifyAI
# ExpensifyAI
**Talk to your Splitwise. Get CRED-grade spending analytics.**
ExpensifyAI is a Model Context Protocol (MCP) server for [Splitwise](https://www.splitwise.com/)
that lets any LLM client (Claude, etc.) manage shared expenses **and** produce deterministic,
premium spending analytics — category breakdowns, monthly trends, per-member comparisons, and
minimum-transaction settlement plans — rendered as a self-contained, offline HTML dashboard.
> Built on top of the excellent [tarunn2799/splitwise-mcp](https://github.com/tarunn2799/splitwise-mcp);
> extended with a deterministic analytics engine and a category-first dashboard.

*Interactive dashboard, generated from synthetic data. Open `examples/demo-dashboard.html`
in a browser to try it live — pick a date range and every section recomputes instantly.*
## Analytics (what makes this ExpensifyAI)
- **Deterministic by construction** — every number is computed in pure Python with `Decimal`
math (no float drift, no LLM estimation). Same input → byte-identical output. Each report
carries a **reconciliation check**: per-expense shares must sum to cost, or the mismatch is flagged.
- **Category-first, à la CRED** — an expandable "where it goes" view leads every report; tap a
category to drill into its transactions.
- **Seven analytics modules** — category breakdown · monthly trend · owed-vs-paid ("mine vs split")
· per-member comparison + category×member matrix · transaction ledger · top transactions ·
**settlement optimizer** (minimum transactions to settle a group — no other Splitwise tool has this).
- **Interactive dashboard** — CRED-grade dark UI with a live date-range picker + presets
(this month / 3mo / 6mo / this year / all) that re-filter and recompute every section in the
browser. Hand-rolled inline-SVG charts, validated colorblind-safe palette, fully offline
(self-contained single file — no CDN, no server). All client math is integer paise, so the
live recompute stays exact and reconciles against the Python source of truth.
- **Two analytics tools** — `analyze_spending(target_type, target_id?, dates?, generate_dashboard?)`
and `compare_group_members(group_id, …)`. `target_type` is `me` | `group` | `friend`.
## Local mirror: delta sync + instant search
Splitwise's own search is Pro-paywalled and the API has no search endpoint. ExpensifyAI
mirrors your data into a local SQLite DB (`~/.expensifyai/splitwise.db`) and searches it offline.
- **`sync_all(full=False)`** — **delta sync**: uses the API's `updated_after` cursor so only
expenses added/edited/moved/deleted since the last sync are fetched (first run pulls everything;
a re-sync with no changes is a single call). Upserts by expense id, so it's idempotent. Groups
and friends are fully refreshed each run (small). Deletes are mirrored (soft-deleted, excluded
from search by default).
- **`search_expenses(query?, min_amount?, max_amount?, user_id?, group_id?, category?, dates?)`** —
full-text (FTS5) over description/details/category plus structured filters, against the local DB.
Instant, offline, covers your entire history across all groups. Beats the paywalled app search.
Each expense's full raw API object is stored (`raw` column) as the source of truth, so displayed
values stay faithful — the indexed REAL columns are only for filtering.
## Itemization, receipt scanning & default splits
Splitwise-Pro-parity, built deterministically:
- **Structured itemization** — `create_itemized_expense(description, group_id, items, …)` turns
line-items into ONE expense where **each item can split differently** (beers ¾ to one person,
groceries 4-way, cake between two). Each person's total `owed_share` is computed in exact
integer paise (largest-remainder rounding, so an indivisible ₹100/3 still sums back to ₹100),
and the expense is **reconciled to its total before anything is written** — a mismatch refuses
to create rather than posting a wrong split. `dry_run=True` previews the computed split.
- **Receipt scanning (LLM-vision-native)** — no OCR engine, no cloud keys, no new dependencies:
the calling agent (Claude) reads the receipt image, extracts line-items, and calls
`create_itemized_expense`. The server owns the exact math and the Splitwise write.
- **Receipt image upload** — `attach_receipt(expense_id, image_path)` uploads a local
image/PDF to an existing expense (multipart), so the receipt shows on it in the Splitwise
app. Pairs with the scan flow: extract line-items from the image, then attach the image itself.
- **Statement import** — `import_statement(transactions, default_split_name?, dedup)` proposes a
categorized, split-suggested, duplicate-flagged list from parsed statement rows;
`confirm_import(rows)` bulk-creates the approved ones via the itemization engine.
- **Gmail read-only connector (optional)** — `gmail_find_statements` / `gmail_read_statement`
fetch bank/card statement emails (scope `gmail.readonly`) to feed statement import — the
CRED-style "no manual entry" flow. Requires a one-time Google Cloud OAuth setup; install
extras with `pip install -e ".[gmail]"`. The connector only reads email text; all expense
creation still goes through the reviewed import path.
- **Save default splits** — `save_default_split(name, split)` / `list_default_splits` /
`delete_default_split`. Reuse a template by putting `"split_ref": "roomies-4way"` on an item.
Stored locally in `~/.expensifyai/splits.json`.
Pick any date range — the whole dashboard recomputes live in the browser:

Try it without an account:
```bash
python examples/generate_demo.py # writes examples/demo-dashboard.html
```
## Features (MCP)
- **Full API Access**: Manage expenses, groups, friends, and comments.
- **Natural Language Resolution**: Fuzzy matching for names ("John" -> "John Smith") and groups.
- **Dual Auth**: Supports both OAuth 2.0 (recommended) and API Keys.
- **Smart Caching**: Optimizes performance for static data like categories and currencies.
## Prerequisite: Splitwise Pro
**Since early September 2026 the Splitwise API requires the API app's registered developer to hold an
active [Splitwise Pro](https://secure.splitwise.com/pro) subscription (~$3/mo, $30/yr).** Without it
every call returns:
```
403 — base: This app can't access Splitwise right now because its developer
doesn't have an active Splitwise Pro subscription.
```
This is Splitwise's policy, not a bug here — their [API terms](https://dev.splitwise.com/) reserve it
explicitly ("Splitwise may impose conditions on the use of the Self-Serve API, including, for example,
maintaining an active Splitwise Pro subscription"). Verified: the same 403 is returned by this server,
by a bare API client with no server involved, and to keys that worked days earlier. **There is no
workaround** — the gate sits upstream of every endpoint, reads included.
So to use the 37 Splitwise tools you need Pro, then register an app at
[secure.splitwise.com/apps](https://secure.splitwise.com/apps) for your key. The 5 statement tools
(Gmail, PDF unlock, bank store) need no Splitwise account at all — 3 of them work entirely offline.
## Installation
```bash
git clone https://github.com/udaysrinu/ExpensifyAI
cd ExpensifyAI
python -m venv venv
source venv/bin/activate
pip install -e .
```
## Configuration
See [SETUP.md](SETUP.md) for detailed authentication and configuration instructions.
### Quick Config
Run the included setup script:
```
python -m splitwise_mcp_server.oauth_setup
```
Use the keys provided there, and add all three to your `mcp.json`:
```json
{
"mcpServers": {
"splitwise": {
"command": "python",
"args": ["-m", "splitwise_mcp_server"],
"env": {
"SPLITWISE_OAUTH_ACCESS_TOKEN": "your_token_here"
}
}
}
}
```
> **Get your Auth Keys**
> You can get your Consumer Key and Secret by registering an app at [https://secure.splitwise.com/apps](https://secure.splitwise.com/apps).
> **IMPORTANT:** Using a Virtual Environment?
> If you installed the package in a `venv` or Conda environment, you must use the **absolute path** to the python executable in your config.
>
> ```json
> "command": "/absolute/path/to/venv/bin/python"
> ```
> See [SETUP.md](SETUP.md#using-a-virtual-environment-venvconda) for details.
## Usage
The server enables natural language interactions with your Splitwise data.
**Examples:**
- "What's my current balance?"
- "Split a $50 dinner with Sarah."
- "Use the receipt I uploaded to split the dinner between Manav and me."
- "Show me expenses from last month."
- "Create a group called 'Ski Trip' with Mike."
## Hosted mode — use it from your phone, with your own key
The stdio server above runs as a subprocess on your machine, which the Claude mobile and web apps
cannot do. `api/index.py` is the same server over HTTP, deployable to Vercel and addable as a custom
connector.
**You bring your own key; the server stores nothing.** Add the connector with your Splitwise API key
(from [secure.splitwise.com/apps](https://secure.splitwise.com/apps)) as a bearer token:
```
URL: https://<your-deployment>.vercel.app
Header: Authorization: Bearer <your Splitwise API key>
```
Each request is served with the credential it arrives with, so one deployment works for any number of
people, and the operator never holds anyone else's key. **Do not set `SPLITWISE_API_KEY` on a shared
deployment** — it becomes the fallback for keyless requests, which turns the public URL into your own
account. See [`per_request.py`](src/splitwise_mcp_server/per_request.py).
**The hosted server has 37 of the 42 tools.** Left out: `gmail_find_statements`,
`gmail_read_statement`, `unlock_statement_pdf`, `import_statement`, `confirm_import`. Those need your
Gmail token, your statement PDFs and the SQLite store under `~/.expensifyai`. A serverless filesystem
is ephemeral so they could not work anyway — and hosting them would put your bank statements and
mailbox credentials on a server, which defeats the point of keeping that data local. Bank statement
import stays on stdio.
```bash
vercel # deploy; no environment variables to set
```
Because a key is tied to the app that issued it, bring-your-own-key means each user registers their own
app — and so each needs their own Pro (see [Prerequisite](#prerequisite-splitwise-pro)). If you would
rather carry that cost yourself so your users pay nothing, switch this to **OAuth2**: one app you
register, your Pro, and users just authorize. `OAuth2Handler` already emits the same
`Authorization: Bearer <token>` header, so `per_request.py` needs no changes — only the token's source
does. `oauth_setup.py` has the authorization-code flow.
## Tools
See [TOOLS.md](TOOLS.md) for detailed documentation.
### User Tools
- `get-current-user`: Get authenticated user information
- `get-user`: Get information about a specific user
### Expense Tools
- `create-expense`: Create a new expense with splits
- `get-expenses`: List expenses with optional filters
- `get-expense`: Get detailed expense information
- `update-expense`: Update an existing expense
- `delete-expense`: Delete an expense
### Group Tools
- `get-groups`: List all groups
- `get-group`: Get detailed group information
- `create-group`: Create a new group
- `delete-group`: Delete a group
- `add-user-to-group`: Add a user to a group
- `remove-user-from-group`: Remove a user from a group
### Friend Tools
- `get-friends`: List all friends
- `get-friend`: Get detailed friend information
### Resolution Tools
- `resolve-friend`: Fuzzy match friend names to user IDs
- `resolve-group`: Fuzzy match group names to group IDs
- `resolve-category`: Fuzzy match category names to category IDs
### Comment Tools
- `create-comment`: Add a comment to an expense
- `get-comments`: Get all comments for an expense
- `delete-comment`: Delete a comment
### Utility Tools
- `get-categories`: Get all expense categories
- `get-currencies`: Get all supported currencies
### Arithmetic Tools
- `add`: Add multiple numbers
- `subtract`: Subtract numbers
- `multiply`: Multiply numbers
- `divide`: Divide numbers
- `modulo`: Calculate remainder
## Development
```bash
# Setup
git clone https://github.com/udaysrinu/ExpensifyAI
cd ExpensifyAI
python -m venv venv
source venv/bin/activate
pip install -e ".[dev]"
# Test
pytest # full suite
pytest tests/test_analytics.py # deterministic analytics (15 tests)
# See the dashboard with no account
python examples/generate_demo.py # writes examples/demo-dashboard.html
```
## License
MIT License. See [LICENSE](LICENSE) for details.
Built on top of [tarunn2799/splitwise-mcp](https://github.com/tarunn2799/splitwise-mcp) (MIT);
the analytics engine, interactive dashboard, and tests are added by ExpensifyAI.
TDQS
Scored across 42 tools
Most tools have clear distinct purposes (expenses, groups, friends, splits, sync/search, import pipeline). Some potential confusion exists between get_expenses/search_expenses/search_notes and analyze_spending/compare_group_members, but descriptions clarify the differences.
Tool names mostly follow a consistent verb_noun pattern (create_expense, get_expense, update_expense, delete_expense, restore_expense). Minor deviations: sync_all, analyze_spending, compare_group_members, and the gmail_* / unlock_statement_pdf tools break the pattern slightly but remain readable.
42 tools is on the heavy side for a single server, but the breadth is justified by the domain (expense management, groups, friends, itemization, import pipeline, sync/search). Still, the count feels high and could overwhelm an agent.
The tool surface covers the full expense lifecycle (create, read, update, delete, restore), groups, friends, comments, categories, currencies, notifications, itemized expenses, default splits, receipt attachment, local sync/search, and a full statement import pipeline (Gmail → PDF unlock → import → confirm). No obvious dead ends.