Skip to main content
Glama
Xerrion

firefly-iii-mcp-server

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