The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the MCP Firefly Iii listing page.
A Model Context Protocol server that gives an AI assistant access to your own Firefly III instance — 152 operations behind 5 scoped tools, with reading, writing and deleting kept as three separate, explicitly-authorized surfaces instead of one tool that can do all three.
Türkçe: README.tr.md
Everyone runs this against their own Firefly instance with their own token — there is no hosted backend or relay in between.
Listed in the official MCP Registry as io.github.YakupEmreYerli/mcp-firefly-iii, on Glama, and in Firefly III's own third-party apps documentation. Every release is built and published by CI from a tagged commit, with npm provenance attesting that the tarball came from this repository.
https://github.com/user-attachments/assets/4866f13e-ff09-43b0-b99c-2b4789a30224
38-second demo: ask a financial question, read the answer through MCP, preview a change with dry_run, approve it, and write it back to Firefly III. Recorded against a synthetic instance — all financial data shown is fabricated.
firefly_query, firefly_mutate, firefly_destructive, plus firefly_list_operations and firefly_get_schema for discovery — a typed registry maps every Firefly endpoint onto these instead of flooding the model's tool list.dry_run on every write, returning the exact request — resolved record IDs included — without sending it.max_matches and refuse an incomplete scan before the first write; multi-split transaction groups are rejected outright rather than risk folding their amounts together.linux/amd64/linux/arm64, and a self-checking documentation pipeline that keeps the tool catalogue in sync with the code.MCP_UPDATE_CHECK=false turns it off.| Method | Transport | Best for |
|---|---|---|
npx — stdio | stdio | Claude Code, Claude Desktop, Cursor — simplest setup |
| Static token | HTTP | n8n, automation, headless callers |
| OAuth | HTTP + OAuth | Claude web, Claude mobile, ChatGPT — can't hold a static token |
| Docker | HTTP | Self-hosted, either auth mode above |
Let setup do it — it asks for your Firefly III address and token, checks that they actually work, then configures Claude Code and Claude Desktop if it finds them: npx -y @yakupemreyerli/firefly-mcp setup. For any other client it prints the configuration to paste.
By hand, Claude Code:
By hand, Claude Desktop / Cursor / other clients — add to the MCP config file:
For n8n, automation, or any caller that can't drive a browser-based OAuth flow. Set MCP_HTTP_TOKEN in .env, then run npx -y -p @yakupemreyerli/firefly-mcp firefly-mcp-http. Every request to /mcp must carry Authorization: Bearer <token> — one token, full access, no per-connection scoping.
None of these clients can hold a static token, and none of them can spawn a local process — they connect to a public HTTPS URL and expect OAuth. With MCP_AUTH_PASSWORD set, this server is the OAuth 2.1 authorization server: it handles client registration, PKCE and token exchange itself, so there is no Keycloak, no Google sign-in, and no token to copy anywhere.
Step 1 — give the server a public HTTPS address. Cloudflare Tunnel is the easiest route for a home server (no port forwarding, no certificate); Caddy or Traefik work on a VPS. compose.example.yml ships cloudflare and caddy profiles for exactly this. Say the result is https://mcp.example.com.
Step 2 — configure .env:
MCP_RESOURCE_URL is the external origin, character for character, with no path — not the internal http://firefly-mcp:3000, and not the /mcp connection URL. A mismatch fails the token audience check and the client only reports "invalid token". MCP_AUTH_STATE_DIR must sit on a persistent volume (compose.example.yml mounts one) or every restart de-authorizes every client.
Step 3 — start it and verify:
If auth says bearer instead, the password never reached the process and the client will report that the server doesn't support OAuth.
Step 4a — Claude (web, Desktop, iOS/Android). Settings → Connectors → Add custom connector, URL https://mcp.example.com/mcp. Leave the authentication choices as detected — Claude probes the server and picks the flow it supports. The connector then works on every Claude surface you're signed into, phone included.
Step 4b — ChatGPT. In the custom connector / MCP screen, enter the same https://mcp.example.com/mcp and choose OAuth as the authentication method.
Step 5 — enter the password. A Firefly login screen opens in the browser; type MCP_AUTH_PASSWORD. That one screen is the whole decision — the connection is granted all three scopes (firefly:read, firefly:write, firefly:destructive), whatever the client itself asked for. There is no second consent screen: whoever holds the password could have ticked every box on it. To hand out a connection that genuinely cannot write, give the server a read-only Firefly Personal Access Token instead.
Full TLS recipes and troubleshooting: docs/oauth.md.
Recommended for either HTTP mode above:
Swap build: . in compose.example.yml for image: ghcr.io/yakupemreyerli/mcp-firefly-iii:latest to use the prebuilt image — pin a version tag, not :latest, for anything you depend on. Single container without Compose: docker run -d --env-file .env -p 3000:3000 ghcr.io/yakupemreyerli/mcp-firefly-iii:latest. It refuses to start without one of the two auth modes above, and /mcp needs TLS in front — compose.example.yml has optional cloudflare and caddy profiles for that. /health is open, for container probes.
| Variable | Default | Purpose |
|---|---|---|
FIREFLY_API_URL | — | Required. A bare domain, or a full base URL including /api/v1. |
FIREFLY_API_TOKEN | — | Required. Personal Access Token. |
FIREFLY_DISABLE_SSL_VERIFY | false | Only for a local instance with a self-signed certificate. |
MCP_UPDATE_CHECK | true | Daily check for a newer release. The only request this server makes to anywhere but your Firefly instance, and it carries no data. |
Every variable, including HTTP and OAuth mode: docs/configuration.md.
| Tool | Answers | Risk |
|---|---|---|
firefly_query | Read anything. Its description carries the catalogue, so choosing an operation costs no extra call. | read-only |
firefly_mutate | Create or change a record. | writes |
firefly_destructive | Delete a record, or rewrite one field across many records at once. | cannot be undone |
firefly_list_operations | What can I do with this entity? | read-only |
firefly_get_schema | What parameters does this operation take? | read-only |
The split is enforced, not just advertised — a delete reached through firefly_query is refused, and a connection granted only firefly:read never even sees the two writing tools. Responses are trimmed before they reach the model: empty and null attributes are always dropped, and every execution tool takes a fields list — roughly a 90% cut on a large transaction list. Full reference: docs/api/operations.md.
This server never sends your data to a third party, but it doesn't control what the AI client or model you connect it to does with a response once it has one. Full threat model: SECURITY.md. Found a vulnerability? Report it privately there.
| Page | What it covers |
|---|---|
| Quickstart | Getting a token, wiring up your client, first things to try, troubleshooting |
| Configuration | Every environment variable, the permission policy, HTTP mode |
| Remote access with embedded OAuth | Deploying for Claude web, Claude mobile, and ChatGPT |
| MCP Integration | Claude Code, Claude Desktop, Cursor, VS Code, n8n and remote HTTP |
| Operations | All 152 operations, response trimming, the Firefly quirks that bite |
| Analysis Operations | summary.overview, search, and the eight insight endpoints |
| MCP Inspector | Poking at the server interactively while developing |
Tests are mocked and never reach the network. npm run smoke:live is a maintainer tool that walks every read operation against the instance in .env; it is read-only and not part of the published package. Bug reports and pull requests are welcome — see CONTRIBUTING.md.
MIT — see LICENSE.