firefly-iii-mcp-server
by Xerrion
README.md
# firefly-iii-mcp-server
An MCP (Model Context Protocol) server that exposes a **curated, task-shaped surface of 41 tools** over the [Firefly III](https://www.firefly-iii.org/) personal-finance API. Designed for an AI agent doing personal accounting on behalf of a human: record, review, budget, save, summarise.
Rather than mirroring Firefly's ~230 REST operations 1:1, this server is a **facade** with a thin anti-corruption layer that:
- strips the JSON:API envelope (`data: { type, id, attributes: {...} }`) on every response,
- flattens Firefly's currency-keyed maps into `{ currency_code, ... }` arrays,
- collapses per-account/per-bill/per-budget/per-category transaction list variants into a single `list_transactions` with filters,
- normalises errors into one envelope with stable codes,
- keeps amounts, IDs, and dates as strings (no float drift, no silent coercion).
The 41-tool catalogue and the capability groups deliberately not exposed in v1 are documented below.
## Install
Requires [Bun](https://bun.sh/) ≥ 1.3.
```bash
bun install
```
No build step — Bun runs TypeScript directly from `src/index.ts`.
## Configuration
Two environment variables, both required. The server **fails fast at startup** if either is missing.
| Variable | Description |
|---|---|
| `FIREFLY_III_URL` | Base URL of your Firefly III instance, no trailing slash. Example: `https://demo.firefly-iii.org`. |
| `FIREFLY_III_TOKEN` | Personal Access Token. Get one in Firefly III: *Options → Profile → OAuth → Personal Access Tokens → Create New Token*. |
A `.env.example` is provided. Copy it to `.env` for local development (never commit `.env`).
## Run
```bash
# stdio transport (the only supported transport in v1)
FIREFLY_III_URL=https://demo.firefly-iii.org \
FIREFLY_III_TOKEN=your-pat-here \
bun start
```
On success the server logs `firefly-iii-mcp-server: ready on stdio` to stderr (stdout is reserved for the MCP protocol).
## MCP client configuration
### Claude Desktop
Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
```json
{
"mcpServers": {
"firefly-iii": {
"command": "bun",
"args": ["run", "/absolute/path/to/firefly-iii-mcp/src/index.ts"],
"env": {
"FIREFLY_III_URL": "https://demo.firefly-iii.org",
"FIREFLY_III_TOKEN": "your-pat-here"
}
}
}
}
```
### opencode
Add to `~/.config/opencode/opencode.json` (or your project-local `opencode.json`):
```json
{
"mcp": {
"firefly-iii": {
"type": "local",
"command": ["bun", "run", "/absolute/path/to/firefly-iii-mcp/src/index.ts"],
"environment": {
"FIREFLY_III_URL": "https://demo.firefly-iii.org",
"FIREFLY_III_TOKEN": "your-pat-here"
},
"enabled": true
}
}
}
```
## Tools
All 41 tools are listed below. Their schemas are exposed to MCP clients through the server's tool catalogue.
**Transactions (6)**
- `list_transactions` — browse recent transactions, filter by date / type / account
- `get_transaction` — fetch one transaction with all its splits
- `search_transactions` — free-text and operator search (`amount_min:`, `category:`, `tag:`, …)
- `create_transaction` — record a withdrawal, deposit, or transfer (single or multi-split)
- `update_transaction` — edit fields on an existing transaction
- `delete_transaction` — delete a transaction by ID
`create_transaction` and `update_transaction` may return an `overridden_fields` envelope alongside the projected transaction. It lists fields the agent sent that Firefly's rule engine then overwrote (e.g. you sent `category: "Food"` and a rule rewrote it to `"Groceries"`). The envelope is absent when no diff is detected. v1.1 limitation: only the first split is diffed.
**Accounts (5)**
- `list_accounts` — list accounts, filter by type or `active`
- `get_account` — single account with current balance (historic balance via `date`)
- `create_account` — create asset / expense / revenue / liability / cash account
- `update_account` — update account metadata
- `delete_account` — delete an account by ID
**Categories (3)**
- `list_categories` — list categories
- `upsert_category` — create or update a category
- `delete_category` — delete a category
**Tags (3)**
- `list_tags` — list tags
- `upsert_tag` — create or update a tag
- `delete_tag` — delete a tag by ID
**Budgets (3)**
- `list_budgets` — list budgets enriched with limit / spent / remaining for a period
- `upsert_budget` — create or update a budget; optionally set its limit in the same call
- `delete_budget` — delete a budget by ID (Firefly clears its budget-limit rows; transactions are not deleted)
**Bills (3)**
- `list_bills` — recurring expected expenses with `next_expected_match` and `paid_dates`
- `upsert_bill` — create or update a bill (subscription, etc.)
- `delete_bill` — delete a bill by ID (matched transactions are detached, not deleted)
**Piggy banks (5)**
The piggy-bank cluster uses an explicit `create_*` + `update_*` rather than a single `upsert_*` because Firefly's create-side schema requires four fields (`name`, `accounts`, `target_amount`, `start_date`) while the update-side schema requires none — the same asymmetric-required-fields pattern as rules CRUD.
- `list_piggy_banks` — savings goals with progress (`current_amount`, `target_amount`, `percentage`)
- `create_piggy_bank` — define a new savings goal
- `update_piggy_bank` — rename, retarget, or re-attach accounts; partial update. **Does NOT accept `current_amount`** — balance changes go through `contribute_piggy_bank`, which is the single auditable path for moving money into or out of a piggy bank.
- `delete_piggy_bank` — delete a savings goal by ID (linked accounts and transactions are untouched)
- `contribute_piggy_bank` — add to (positive) or withdraw from (negative) a piggy bank
**Rules (5)**
- `list_rules` — list rules; pass `rule_group_id` to scope to one group, else lists all rules
- `get_rule` — fetch one rule with its triggers and actions
- `create_rule` — create a rule in a group; triggers and actions are flat arrays (order derived from array index)
- `update_rule` — update a rule; passing `triggers: []` or `actions: []` clears the list
- `delete_rule` — delete a rule by ID
**Rule groups (5)**
- `list_rule_groups` — list rule groups
- `get_rule_group` — fetch one rule group
- `create_rule_group` — create a rule group
- `update_rule_group` — update a rule group's metadata
- `delete_rule_group` — delete a rule group (cascades: all rules in the group are deleted)
**Summary & insights (2)**
- `get_financial_summary` — net worth, income, expense, balance for a period; flattened across currencies
- `get_spending_insights` — breakdown by `category` / `budget` / `tag` × `expense` / `income`
**System (1)**
- `get_system_info` — Firefly version, primary currency, authenticated user
### Shared conventions
- **Pagination** on every `list_*` / `search_*` tool: `{ items, page, total_pages, total_items, has_more }`. `page` is 1-indexed. `limit` defaults to 25 and is capped at 100 (over-limit requests are clamped, not rejected; the response includes `limit_clamped_to: 100`).
- **Amounts, IDs, dates are strings.** Amounts are decimal strings (`"-1012.12"`). IDs are numeric strings (`"42"`). Dates are `YYYY-MM-DD`.
- **Currency by code, not ID.** `currency_code: "EUR"`, never `currency_id: 1`.
- **Errors** are returned as a uniform envelope (see below); the tool result is marked `isError: true` so clients can detect failure.
### Error envelope
```json
{
"ok": false,
"error": {
"code": "validation_error",
"message": "The amount is required.",
"field_errors": { "transactions.0.amount": ["The amount is required."] },
"http_status": 422,
"trace_id": "f54f1c80-7d9b-4a3e-9b71-1c0c7b3d3a1b"
}
}
```
`code` is one of: `validation_error`, `not_found`, `unauthenticated`, `forbidden`, `rate_limited`, `upstream_error`, `network_error`, `internal_error`. HTML error pages from upstream are never leaked — they are converted into a safe synthetic `upstream_error`.
## Example tool calls
### List recent withdrawals in March 2026
Input to `list_transactions`:
```json
{ "start": "2026-03-01", "end": "2026-03-31", "type": "withdrawal", "limit": 3 }
```
Output (`structuredContent`):
```json
{
"items": [
{
"id": "1042",
"type": "withdrawal",
"date": "2026-03-15",
"amount": "42.50",
"currency_code": "EUR",
"description": "Groceries",
"category": "Food",
"source_name": "Checking",
"destination_name": "Supermarket"
}
],
"page": 1,
"total_pages": 4,
"total_items": 12,
"has_more": true
}
```
### Record a withdrawal
Input to `create_transaction`:
```json
{
"type": "withdrawal",
"date": "2026-03-20",
"amount": "12.99",
"description": "Coffee",
"source": "Checking",
"destination": "Daily Cafe",
"category": "Food",
"tags": ["weekly"]
}
```
`source` / `destination` accept either a numeric account ID (`"7"`) or a free-form name; the server routes to Firefly's `*_id` vs `*_name` automatically.
### Set a monthly budget for groceries with a limit
Input to `upsert_budget`:
```json
{
"name": "Groceries",
"active": true,
"limit": {
"start": "2026-03-01",
"end": "2026-03-31",
"amount": "400.00",
"currency_code": "EUR"
}
}
```
Output (`structuredContent`):
```json
{
"id": "5",
"name": "Groceries",
"active": true,
"limit": {
"start": "2026-03-01",
"end": "2026-03-31",
"amount": "400.00",
"currency_code": "EUR"
}
}
```
## Not exposed in v1
The following capability groups are deliberately omitted from v1 to keep the tool catalogue small and the agent's planning surface tractable: user / group admin, server configuration, currency admin, destructive admin (`destroyData`/`purgeData`), chart endpoints, autocomplete (`*AC`), webhooks, attachments (binary I/O over MCP is a separate design problem), per-currency / per-parent transaction list duplicates, exports, bulk update, currency exchange rates, recurrences, transaction links, object groups, available budgets, preferences, per-resource event/attachment sub-lists, and narrow insight slices.
## Development
```bash
bun install # install dependencies
bun run typecheck # tsc --noEmit (TypeScript kept as a dev dep purely for typechecking)
bun test # bun's built-in test runner (shape helpers, error mapping, tool catalogue, decimal, integration)
bun run dev # bun --watch run src/index.ts
```
The catalogue test (`tests/tool-catalogue.test.ts`) asserts that exactly the 41 tools named in `EXPECTED_TOOL_NAMES` in `src/tools.ts` are registered. Update the catalogue, its expected names, and this README together when adding or removing a tool.
## License
MIT
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues