Labsco
agigante80 logo

Actual Budget MCP

β˜… 25

from agigante80

Docker MCP server connecting Claude Desktop/LibreChat/LobeChat to Actual Budget for natural-language budgeting, transaction management, and financial insights.

πŸ”₯πŸ”₯πŸ”₯πŸ”₯βœ“ VerifiedAccount requiredAdvanced setup
<p align="center"> <img src="unraid/actual-mcp-icon.png" alt="Actual MCP Server icon" width="128" height="128"> </p> <h1 align="center">Actual MCP Server</h1>

npm version npm downloads License: MIT Node.js Version TypeScript MCP Protocol Docker Pulls Docker Image Size Unraid Community Apps GitHub Actions CI GitHub stars

Talk to your budget. Run it anywhere. Trust it in production.

Actual MCP Server is a Model Context Protocol server that connects any MCP-compatible AI assistant (such as LibreChat, LobeChat, Claude Desktop, and more) directly to your self-hosted Actual Budget instance. Ask natural language questions, create transactions, analyse spending, and manage your entire budget without ever opening the Actual Budget UI.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   MCP/HTTP    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   Actual API   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  LibreChat  β”‚ ◄───────────► β”‚  Actual MCP      β”‚ ◄───────────► β”‚   Actual     β”‚
β”‚  LobeChat   β”‚               β”‚  Server          β”‚               β”‚   Budget     β”‚
β”‚  (remote)   β”‚               β”‚  (71 tools)      β”‚               β”‚   Server     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   MCP/stdio   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   Actual API   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Claude     β”‚ ◄───────────► β”‚  Actual MCP      β”‚ ◄───────────► β”‚   Actual     β”‚
β”‚  Desktop    β”‚               β”‚  Server          β”‚               β”‚   Budget     β”‚
β”‚  (local)    β”‚               β”‚  (71 tools)      β”‚               β”‚   Server     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Why this project?

Most Actual Budget MCP implementations are simple stdio bridges designed for single-user, local use with Claude Desktop. This project goes further:

  • 71 tools, the most comprehensive coverage available. Accounts, transactions, categories, payees, tags, notes, rules, budgets, batch operations, bank sync, and more. Covers the reachable Actual Budget API with no genuine gaps.
  • HTTP and stdio transport. Runs as a real remote server for LibreChat/LobeChat (--http), or as a direct local process for Claude Desktop (--stdio). No Docker or HTTP server is needed for local use.
  • 6 exclusive ActualQL-powered tools. Search and summarise transactions by month, amount, category, or payee using Actual Budget's native query engine. Aggregated results, no raw data dumped into the AI context window.
  • Multi-budget switching at runtime. Configure multiple budget files and let the AI switch between them mid-conversation with actual_budgets_switch.
  • Multi-user ready with OIDC. Secure every session with JWKS-validated JWTs and per-user budget ACLs. No shared tokens required.
  • Production-grade reliability. Connection pooling (up to 15 concurrent sessions), automatic retry with exponential backoff, and a full test suite (unit + E2E + integration).

Verified working with LibreChat, LobeChat, and Claude Desktop. All 71 tools tested end-to-end. Any MCP-compatible client should work.


Table of Contents


Upgrading

Docker (Option A)

docker pull ghcr.io/agigante80/actual-mcp-server:latest
docker stop actual-mcp-server-backend
docker rm actual-mcp-server-backend
# Re-run the original docker run command with the same flags and volumes

Also available on Docker Hub: docker pull agigante80/actual-mcp-server:latest

Docker Compose (Option B)

docker compose pull
docker compose --profile production up -d

npm / cloned repo (Option C)

git pull
npm install
npm run build
# Then restart the server

npx / stdio (Options C & D)

If you run npx actual-mcp-server without a globally installed version, npx fetches the latest from the registry automatically. But if you previously installed it globally (npm install -g actual-mcp-server), the global install takes precedence, so you must upgrade it explicitly:

# Upgrade the global install
npm install -g actual-mcp-server

# Or force the registry version without touching your global install
npx actual-mcp-server@latest --http

For Claude Desktop (stdio), restart Claude after upgrading.


Available Tools

71 tools across all categories. All tools use the actual_<category>_<action> naming convention.

Accounts (7)

ToolDescription
actual_accounts_listList all accounts
actual_accounts_createCreate new account
actual_accounts_updateUpdate account details
actual_accounts_deletePermanently delete account
actual_accounts_closeClose account (soft delete)
actual_accounts_reopenReopen closed account
actual_accounts_get_balanceGet account balance at a date

Transactions (13)

Standard (6)

ToolDescription
actual_transactions_getGet transactions for an account
actual_transactions_filterFilter with advanced criteria
actual_transactions_createCreate new transaction(s)
actual_transactions_importImport and reconcile transactions
actual_transactions_updateUpdate a transaction
actual_transactions_deleteDelete a transaction

Utility (1)

ToolDescription
actual_transactions_uncategorizedSummary of uncategorized transactions (totalCount, totalAmount, per-account breakdown); pass includeTransactions:true for paginated rows

Exclusive ActualQL-powered (6), unique to this MCP server

ToolDescription
actual_transactions_search_by_monthSearch by month using $month transform
actual_transactions_search_by_amountFind by amount range
actual_transactions_search_by_categorySearch by category name
actual_transactions_search_by_payeeFind by payee/vendor
actual_transactions_summary_by_categorySpending summary grouped by category
actual_transactions_summary_by_payeeTop vendors with totals and counts

Transfers (1)

ToolDescription
actual_transfers_createCreate a paired transfer between two accounts (debit + credit linked by transfer_id, identical to UI "Make Transfer")

Note: Use actual_transfers_create for any account-to-account movement, not actual_transactions_create. The dedicated tool creates both sides (debit and credit) atomically so the books stay balanced. Limitations: both accounts must exist and be open, and from_account must differ from to_account.

Categories (4)

actual_categories_get Β· actual_categories_create Β· actual_categories_update Β· actual_categories_delete

Category Groups (4)

actual_category_groups_get Β· actual_category_groups_create Β· actual_category_groups_update Β· actual_category_groups_delete

Payees (7)

actual_payees_get Β· actual_payees_common_list Β· actual_payees_create Β· actual_payees_update Β· actual_payees_delete Β· actual_payees_merge Β· actual_payee_rules_get

Tags (4)

actual_tags_list Β· actual_tags_create Β· actual_tags_update Β· actual_tags_delete

ToolDescription
actual_tags_listList all tags (id, tag word, optional color and description)
actual_tags_createCreate or upsert a tag by name; returns the tag UUID
actual_tags_updateUpdate tag name, color, or description by UUID
actual_tags_deleteSoft-delete a tag by UUID

Notes (2)

actual_notes_get Β· actual_notes_update

ToolDescription
actual_notes_getGet the note for any entity (account/category/category-group/payee UUID, or budget-YYYY-MM)
actual_notes_updateSet or clear the note for any entity; validates entity exists or matches budget-YYYY-MM pattern

Budgets (10)

ToolDescription
actual_budgets_list_availableList all configured budget files
actual_budgets_switchSwitch active budget (multi-budget)
actual_budgets_get_allList available budget files
actual_budgets_getMonthsList budget months
actual_budgets_getMonthGet budget for a specific month
actual_budgets_setAmountSet category budget amount
actual_budgets_transferTransfer amount between categories
actual_budgets_setCarryoverEnable/disable carryover
actual_budgets_holdForNextMonthHold funds for next month
actual_budgets_resetHoldReset hold status

Rules (4)

actual_rules_get Β· actual_rules_create Β· actual_rules_update Β· actual_rules_delete

Advanced Query & Sync (2)

ToolDescription
actual_query_runExecute custom ActualQL query
actual_bank_syncTrigger bank sync (GoCardless/SimpleFIN)

Batch Operations (1)

actual_budget_updates_batch: batch multiple budget updates in one call

Server Information & Lookup (4)

ToolDescription
actual_server_infoServer status, version, build info
actual_server_get_versionActual Budget server version
actual_get_id_by_nameResolve an exact name β†’ UUID for accounts, categories, payees
actual_entities_searchFind accounts/categories/payees by a name pattern (contains/startsWith/endsWith/exact/fuzzy). Fixes "payee not found" from a partial or mistyped name

Session Management (2)

actual_session_list Β· actual_session_close

Not Yet Implemented

  • Scheduled/recurring transactions (getSchedules, createSchedule, updateSchedule, deleteSchedule)

Multi-Budget Switching

Configure multiple Actual Budget files so the AI can switch between them at runtime using actual_budgets_list_available and actual_budgets_switch.

BUDGET_N_SERVER_URL and BUDGET_N_PASSWORD fall back to ACTUAL_SERVER_URL / ACTUAL_PASSWORD when omitted.

VariableRequiredFallback
BUDGET_DEFAULT_NAMENo"Default"
BUDGET_N_NAMEYes (enables group)(none)
BUDGET_N_SYNC_IDYes(none)
BUDGET_N_SERVER_URLNoACTUAL_SERVER_URL
BUDGET_N_PASSWORDNoACTUAL_PASSWORD
BUDGET_N_ENCRYPTION_PASSWORDNo(none)
# Default budget
ACTUAL_SERVER_URL=http://actual:5006
ACTUAL_PASSWORD=my-password
ACTUAL_BUDGET_SYNC_ID=aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
BUDGET_DEFAULT_NAME=Personal

# Budget 1 (same server, same password)
BUDGET_1_NAME=Family
BUDGET_1_SYNC_ID=bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb

# Budget 2 (different server)
BUDGET_2_NAME=Business
BUDGET_2_SERVER_URL=https://actual-office.example.com
BUDGET_2_PASSWORD=office-password
BUDGET_2_SYNC_ID=cccccccc-cccc-cccc-cccc-cccccccccccc

Transport & Authentication

The server supports two transport modes:

ModeFlagUse caseAuth
HTTP--httpLibreChat, LobeChat, Docker, multi-user deploymentsBearer token or OIDC
stdio--stdioClaude Desktop, Cursor, local single-user useNone (OS process isolation)

The two modes are mutually exclusive. Pass exactly one flag when starting the server.

stdio transport

stdio is the simplest way to connect Claude Desktop directly to Actual Budget. The MCP server runs as a child process; Claude Desktop spawns it, communicates over stdin/stdout using NDJSON (the MCP wire format), and the process exits cleanly when Claude Desktop closes.

Key properties of stdio mode:

  • No network port. The transport is a pipe, not a socket.
  • No auth token. Process ownership is the security boundary.
  • All logs go to stderr so they never corrupt the JSON-RPC framing on stdout
  • The process exits when stdin closes (Claude Desktop shutting down)
  • All 71 tools are available, identical to HTTP mode

Start manually to verify:

cd /path/to/actual-mcp-server
ACTUAL_SERVER_URL=http://localhost:5006 \
ACTUAL_PASSWORD=your_password \
ACTUAL_BUDGET_SYNC_ID=your-sync-id \
node dist/src/index.js --stdio

Send a test request (keep stdin open with sleep):

{ echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'; sleep 5; } \
| ACTUAL_SERVER_URL=http://localhost:5006 ACTUAL_PASSWORD=your_password ACTUAL_BUDGET_SYNC_ID=your-sync-id \
  node dist/src/index.js --stdio 2>/dev/null

Claude Desktop config (claude_desktop_config.json):

{
  "mcpServers": {
    "actual-budget": {
      "command": "node",
      "args": ["/absolute/path/to/actual-mcp-server/dist/src/index.js", "--stdio"],
      "env": {
        "ACTUAL_SERVER_URL": "http://localhost:5006",
        "ACTUAL_PASSWORD": "your_actual_password",
        "ACTUAL_BUDGET_SYNC_ID": "your-sync-id-here",
        "MCP_BRIDGE_DATA_DIR": "/absolute/path/to/actual-mcp-server/actual-data"
      }
    }
  }
}

Path must be absolute. Claude Desktop does not inherit shell PATH, so node must also be absolute if you use NVM or a non-standard install: /home/youruser/.nvm/versions/node/v22.x.x/bin/node.

See docs/guides/MCP_CLIENTS_SETUP.md for all connection options (stdio native, mcp-remote via HTTP/HTTPS), other clients (Cursor, VS Code, Gemini CLI, Claude Code), Linux path fixes, and troubleshooting.

HTTP transport

HTTP transport uses the /http endpoint (StreamableHTTP) with optional Bearer token or OIDC authentication.

Static Bearer token (single-user)

# Generate a token
openssl rand -hex 32

# Add to .env
MCP_SSE_AUTHORIZATION=your_token_here

Clients send: Authorization: Bearer your_token_here

OIDC (multi-user)

AUTH_PROVIDER=oidc
OIDC_ISSUER=https://sso.yourdomain.com
OIDC_RESOURCE=your-client-id    # must match 'aud' JWT claim
OIDC_SCOPES=                    # leave empty for Casdoor

See AI Client Setup, OIDC for AUTH_BUDGET_ACL format and Casdoor notes.


Testing

CommandWhat It TestsRequires Live Server
npm run buildTypeScript compilationNo
npm run test:unit-js71-tool smoke, schema validation, auth ACLNo
npm run test:adapterAdapter, retry logic, concurrencyNo
npm run test:e2eMCP protocol compliance (Playwright)No
npm run test:e2e:docker:fullFull stack integrationYes (Docker)
npm run test:integrationLive server sanity checksYes
npm run test:integration:fullFull live integration suiteYes

Integration test levels (tests/manual/): sanity β†’ smoke β†’ normal β†’ extended β†’ full β†’ cleanup

See tests/manual/README.md and tests/e2e/README.md for details.


Documentation

DocumentContents
docs/guides/MCP_CLIENTS_SETUP.mdStart here to connect Claude Desktop, Cursor, VS Code (Copilot), Gemini CLI, or Claude Code
docs/guides/AI_CLIENT_SETUP.mdLibreChat & LobeChat setup, Docker networking, HTTPS/TLS proxy, OIDC
docs/guides/DEPLOYMENT.mdDocker, Docker Compose profiles, production config, Kubernetes
docs/ARCHITECTURE.mdComponent layers, data flow, transport protocols
docs/SECURITY_AND_PRIVACY.mdAuth models, threat model, hardening
docs/TESTING_AND_RELIABILITY.mdTest strategy, coverage, reliability patterns
docs/NEW_TOOL_CHECKLIST.mdStep-by-step guide for adding a new MCP tool
CONTRIBUTING.mdDevelopment setup, code standards, PR process
.env.exampleFully annotated environment variable reference

Contributing

Contributions are welcome! See CONTRIBUTING.md for development setup, code standards, and the PR process.

Quick flow:

  1. Fork β†’ git checkout -b feature/my-feature
  2. Make changes + add tests
  3. npm run build && npm run test:unit-js must pass
  4. Open a Pull Request

Architecture

  • Runtime: Node.js 22 (Alpine Linux in Docker)
  • Language: TypeScript (ESM, NodeNext module resolution)
  • MCP SDK: @modelcontextprotocol/sdk
  • Actual API: @actual-app/api
  • Validation: Zod (runtime types + JSON Schema for tool inputs)
  • Transports: Express + StreamableHTTP (--http) Β· StdioServerTransport (--stdio)
  • Logging: Winston with daily rotation (all output routed to stderr in stdio mode)

Every Actual API call goes through the withActualApi() wrapper in src/lib/actual-adapter.ts, which handles init/shutdown lifecycle, retry (3 attempts, exponential backoff), and concurrency limiting. See docs/ARCHITECTURE.md for full design documentation.


License

MIT. See LICENSE for details.


Acknowledgments


Disclaimer

This project started as a personal learning exercise to explore the Model Context Protocol technology. It is an independent open-source project, not affiliated with, endorsed by, or supported by Actual Budget or any other organisation.

The software is provided as-is, without warranty of any kind. The author accepts no responsibility for how it is used, for any data loss, financial errors, or other consequences arising from its use. If you connect it to real financial data, you do so entirely at your own risk.


Support


Version: 0.8.1 | Tool Count: 71 (verified LibreChat-compatible)