MCP Hub
Back to servers

actual-mcp-server

Model Context Protocol server for AI-driven financial management with Actual Budget - 51 conversational tools, session management, transaction queries, budget planning, and bank sync support for LibreChat and MCP clients

Stars
2
Tools
51
Updated
Jan 8, 2026
Validated
Jan 9, 2026

Actual MCP Server

License: MIT Node.js Version TypeScript MCP Protocol Docker Pulls Docker Image Size GitHub Actions CI GitHub stars

A production-ready Model Context Protocol (MCP) server that bridges AI assistants with Actual Budget, enabling natural language financial management through 51 specialized tools covering 82% of the Actual Budget API, including 6 exclusive ActualQL-powered tools designed specifically for this MCP server.

🧪 Tested with Multiple AI Clients: This MCP server has been extensively tested and verified with both LibreChat and LobeChat. All 51 tools load and function correctly. Other MCP clients should work but have not been tested yet.


📋 Table of Contents


💡 Use Cases & Scenarios

What Can You Do with This Integration?

LibreChat ↔ Actual MCP Server ↔ Actual Budget

This MCP server enables conversational financial management by connecting LibreChat's AI capabilities with your Actual Budget data. Here are real-world scenarios:

🗣️ Natural Language Queries

You: "How much did I spend on groceries last month?"
AI: [Uses transactions_filter] "You spent $847.23 on groceries in October 2025"

You: "Show me all transactions over $100 this week"
AI: [Uses transactions_filter] Lists large transactions with dates and payees

You: "What's my checking account balance?"
AI: [Uses accounts_get_balance] "Your checking account has $3,247.89"

💰 Budget Management

You: "Set my restaurant budget to $300 for this month"
AI: [Uses budgets_setAmount] "Restaurant budget for November 2025 set to $300"

You: "Transfer $50 from Entertainment to Groceries"
AI: [Uses budgets_transfer] "Transferred $50 from Entertainment to Groceries"

You: "How much budget do I have left for December?"
AI: [Uses budgets_getMonth] Shows remaining budget for all categories

📝 Transaction Management

You: "Add a $45.67 transaction at Kroger from my checking account"
AI: [Uses transactions_create] "Transaction created and categorized as Groceries"

You: "Import my bank CSV file" [with file upload]
AI: [Uses transactions_import] "Imported 47 transactions, matched 12 duplicates"

You: "Find all Amazon transactions this year"
AI: [Uses transactions_filter] Lists all Amazon purchases with amounts

🏷️ Organization & Automation

You: "Create a rule to automatically categorize Uber as Transportation"
AI: [Uses rules_create] "Rule created: Uber → Transportation"

You: "Merge my duplicate Starbucks payees"
AI: [Uses payees_merge] "Merged 3 Starbucks entries into one"

You: "Create a new category called 'Pet Supplies' under Expenses"
AI: [Uses categories_create] "Pet Supplies category created"

🎯 Perfect For

  • 💬 Conversational Finance: Chat with your budget naturally
  • 📊 Quick Insights: Get financial answers without opening Actual Budget
  • 🔄 Bulk Operations: Manage multiple transactions through conversation
  • 🤖 Automation: Set up rules and recurring patterns via AI
  • 📱 Mobile-Friendly: Use LibreChat mobile app for on-the-go budget access
  • ♿ Accessibility: Voice-based financial management for users with disabilities

🔗 Connection Architecture

┌─────────────┐         ┌──────────────────┐         ┌──────────────┐
│             │  HTTPS  │                  │   API   │              │
│  LibreChat  │ ◄─────► │  Actual MCP      │ ◄─────► │   Actual     │
│   (Client)  │  MCP    │     Server       │  calls  │   Budget     │
│             │Protocol │                  │         │   Server     │
└─────────────┘         └──────────────────┘         └──────────────┘
      │                        │                           │
      │                        │                           │
   User asks              Translates                  Financial
  "How much               natural language            operations
  did I spend?"           to Actual API calls         & data storage

How It Works

  1. User chats with LibreChat in natural language
  2. LibreChat sends MCP tool requests to Actual MCP Server
  3. Actual MCP Server translates requests to Actual Budget API calls
  4. Actual Budget executes operations and returns data
  5. Actual MCP Server formats response for AI consumption
  6. LibreChat presents results in conversational format

Transport Protocols

  • HTTP/HTTPS: Production-ready with Bearer token authentication (recommended)
  • SSE (Server-Sent Events): Streaming updates (compatible with LibreChat and LobeChat)

🐳 Docker Networking Best Practices

For Docker-to-Docker communication (LibreChat, LobeChat, etc.), use internal Docker hostnames instead of host IPs:

RECOMMENDED: http://actual-mcp-server-backend:3600/http
NOT RECOMMENDED: http://192.168.x.x:3600/http

Why internal hostnames are better:

  • Direct communication: No extra network hops through host bridge
  • Better security: No external network exposure required
  • Built-in DNS: Automatic hostname resolution within Docker networks
  • Resilient: Works even if host IP changes
  • Performance: Lower latency for container-to-container communication

Example configuration for AI clients:

# LibreChat librechat.yaml
mcpServers:
  actual-mcp:
    url: "http://actual-mcp-server-backend:3600/http"  # ✅ Use container name

# LobeChat (via UI)
Server URL: http://actual-mcp-server-backend:3600/http  # ✅ Use container name

Both containers must be on the same Docker network. See Docker Deployment section below for network configuration.


✨ Features

Core Capabilities

Core Capabilities

  • 🤖 51 MCP Tools: Comprehensive financial operations via natural language
  • 🔄 Multiple Transports: HTTP and Server-Sent Events (SSE)
  • 🔐 Secure: Bearer token authentication + HTTPS/TLS encryption
  • 🛡️ Type-Safe: Full TypeScript implementation with runtime validation (Zod)
  • 🔁 Resilient: Automatic retry logic with exponential backoff
  • 📊 82% API Coverage: Supports majority of Actual Budget operations
  • 🚀 Production-Ready: Docker support, structured logging, health checks
  • LibreChat Verified: All 51 tools tested and working
  • Exclusive Tools: 6 ActualQL-powered tools for advanced queries and summaries

Advanced Features

  • Concurrent Control: Rate-limited API calls prevent overwhelming Actual Budget
  • Observability: Prometheus metrics, structured logging with Winston
  • Flexible Deployment: Docker, Kubernetes, bare metal, or Docker Compose
  • HTTPS Support: TLS encryption with self-signed or CA-signed certificates
  • Tested: >80% test coverage with unit, integration, and E2E tests

Financial Operations

With conversational AI, you can:

  • 💰 Manage Accounts: Create, update, close accounts; check balances
  • 💳 Track Transactions: Add, update, delete transactions; import bank data
  • 📁 Organize Categories: Create category groups and categories
  • 👥 Handle Payees: Manage payees, merge duplicates
  • 📅 Budget Planning: Set budget amounts, enable carryover, track spending
  • 🔧 Automate Rules: Create rules for automatic transaction categorization
  • 🔄 Batch Operations: Efficiently update multiple budget categories

🚀 Quick Start

🐳 Docker Images Available:

Prerequisites

  • Node.js 20+ or Docker
  • Actual Budget server (running locally or hosted)
  • Actual Budget credentials (password and sync ID)

⚠️ Critical: Zod Version Constraint

This project MUST use Zod 3.x (3.25.76). Do NOT upgrade to Zod 4.x!

Zod 4.x has breaking internal changes that cause zod-to-json-schema to produce incomplete schemas, breaking LibreChat tool validation. All 51 tools become invisible if Zod 4.x is used.

  • Reject any Dependabot/Renovate PRs suggesting Zod 4.x
  • The project includes safeguards: package.json overrides and Dockerfile post-install
  • See docs/ZOD_VERSION_CONSTRAINT.md for technical details

Install with npm

# Clone the repository
git clone https://github.com/agigante80/actual-mcp-server.git
cd actual-mcp-server

# Install dependencies
npm install

# Configure environment
cp .env.example .env
# Edit .env with your Actual Budget credentials

# Build and run
npm run build
npm run dev -- --debug

Run with Docker

Docker images are published on two registries:

Available tags:

  • latest - Latest stable release from main branch
  • latest-<sha> - Specific commit from main (e.g., latest-abc1234)
  • development - Latest development build
  • development-<sha> - Specific development commit

Both registries have identical images. Use Docker Hub for public access, or GHCR for integration with GitHub workflows.

Note: Docker images default to HTTP transport mode. To use SSE instead, set MCP_TRANSPORT_MODE=--sse.

Quick Start (HTTP)

# Pull and run from Docker Hub (HTTP is the default)
docker run -d \
  --name actual-mcp-server \
  -p 3600:3600 \
  -e ACTUAL_SERVER_URL=http://your-actual-server:5006 \
  -e ACTUAL_PASSWORD=your_password \
  -e ACTUAL_BUDGET_SYNC_ID=your_sync_id \
  -e MCP_SSE_AUTHORIZATION=$(openssl rand -hex 32) \
  -v actual-mcp-data:/data \
  agigante80/actual-mcp-server:latest

# Or from GitHub Container Registry
docker run -d \
  --name actual-mcp-server \
  -p 3600:3600 \
  -e ACTUAL_SERVER_URL=http://your-actual-server:5006 \
  -e ACTUAL_PASSWORD=your_password \
  -e ACTUAL_BUDGET_SYNC_ID=your_sync_id \
  -e MCP_SSE_AUTHORIZATION=$(openssl rand -hex 32) \
  -v actual-mcp-data:/data \
  ghcr.io/agigante80/actual-mcp-server:latest

# Check if running
curl http://localhost:3600/health

With HTTPS (Recommended)

# Generate self-signed certificate first
mkdir -p certs
openssl req -x509 -newkey rsa:4096 -nodes \
  -keyout certs/key.pem -out certs/cert.pem \
  -days 365 -subj "/CN=your-server-ip" \
  -addext "subjectAltName=IP:your-server-ip,DNS:localhost"

# Run with HTTPS enabled
docker run -d \
  --name actual-mcp-server \
  -p 3600:3600 \
  -e ACTUAL_SERVER_URL=http://your-actual-server:5006 \
  -e ACTUAL_PASSWORD=your_password \
  -e ACTUAL_BUDGET_SYNC_ID=your_sync_id \
  -e MCP_SSE_AUTHORIZATION=$(openssl rand -hex 32) \
  -e MCP_ENABLE_HTTPS=true \
  -e MCP_HTTPS_CERT=/app/certs/cert.pem \
  -e MCP_HTTPS_KEY=/app/certs/key.pem \
  -v actual-mcp-data:/data \
  -v $(pwd)/certs:/app/certs:ro \
  agigante80/actual-mcp-server:latest

# Verify HTTPS is working
curl -k https://localhost:3600/health

Pull Specific Version

# Latest stable from Docker Hub
docker pull agigante80/actual-mcp-server:latest

# Latest stable from GHCR
docker pull ghcr.io/agigante80/actual-mcp-server:latest

# Development version
docker pull agigante80/actual-mcp-server:development

# Specific commit
docker pull agigante80/actual-mcp-server:latest-abc1234

Using docker-compose.prod.yml

For production deployments, use the provided docker-compose.prod.yml:

# Copy and configure environment
cp .env.example .env
# Edit .env with your Actual Budget server details

# Start using GHCR image
docker-compose -f docker-compose.prod.yml up -d

# View logs
docker-compose -f docker-compose.prod.yml logs -f

# Stop
docker-compose -f docker-compose.prod.yml down

Run with Docker Compose

# Full stack (Actual Budget + MCP Server)
docker compose --profile fullstack --profile dev up

# Access Actual Budget: http://localhost:5006
# Access MCP Server: http://localhost:3000

Next Steps: Connect your AI assistant (LibreChat, etc.) to http://localhost:3000


🛠️ Available Tools

The MCP server exposes 51 tools organized into 10 categories. All tools follow the naming convention actual_<category>_<action>.

⚡ Exclusive ActualQL Tools: This MCP server includes 6 specialized tools powered by ActualQL that are unique to this implementation and not available in standard Actual Budget integrations. These tools provide advanced querying, aggregation, and analysis capabilities.

Accounts (7 tools)

ToolDescriptionParameters
actual_accounts_listList all accounts-
actual_accounts_createCreate new accountname, type, offbudget, closed
actual_accounts_updateUpdate account detailsid, name?, type?, offbudget?, closed?
actual_accounts_deletePermanently delete accountid
actual_accounts_closeClose account (soft delete)id
actual_accounts_reopenReopen closed accountid
actual_accounts_get_balanceGet account balance at dateid, cutoff?

Transactions (12 tools)

Basic Operations (6 tools)

ToolDescriptionParameters
actual_transactions_getGet transactions for accountaccountId, startDate?, endDate?
actual_transactions_filterFilter transactions with advanced criteriaaccountId?, startDate?, endDate?, minAmount?, maxAmount?, categoryId?, payeeId?, notes?, cleared?, reconciled?
actual_transactions_createCreate new transaction(s)accountId, date, amount, payee?, category?, notes?
actual_transactions_importImport and reconcile transactionsaccountId, transactions[]
actual_transactions_updateUpdate transactionid, amount?, payee?, category?, notes?, date?
actual_transactions_deleteDelete transactionid

⚡ Exclusive ActualQL-Powered Tools (6 tools) - Only available in this MCP server

ToolDescriptionParameters
actual_transactions_search_by_monthSearch transactions by month using $month transformmonth? (YYYY-MM, defaults to current), accountId?, categoryName?, payeeName?, minAmount?, maxAmount?
actual_transactions_search_by_amountFind transactions by amount rangeminAmount?, maxAmount?, startDate?, endDate?, accountId?, categoryName?, limit?
actual_transactions_search_by_categorySearch transactions by category namecategoryName?, startDate?, endDate?, accountId?, minAmount?, maxAmount?, limit?
actual_transactions_search_by_payeeFind transactions by payee/vendorpayeeName?, startDate?, endDate?, accountId?, categoryName?, minAmount?, maxAmount?, limit?
actual_transactions_summary_by_categoryGet spending summary grouped by category with aggregationstartDate? (defaults to month start), endDate? (defaults to today), accountId?, includeIncome?
actual_transactions_summary_by_payeeAnalyze top vendors/merchants with totals and countsstartDate? (defaults to month start), endDate? (defaults to today), accountId?, limit?

These exclusive tools use ActualQL's advanced features like $transform, groupBy, $sum, and $count for efficient queries and aggregations that go beyond standard API capabilities.

Categories (4 tools)

ToolDescriptionParameters
actual_categories_getList all categories-
actual_categories_createCreate new categoryname, group_id, is_income?
actual_categories_updateUpdate categoryid, name?, group_id?, is_income?
actual_categories_deleteDelete categoryid

Category Groups (4 tools)

ToolDescriptionParameters
actual_category_groups_getList all category groups-
actual_category_groups_createCreate category groupname, is_income?
actual_category_groups_updateUpdate groupid, name?, is_income?
actual_category_groups_deleteDelete groupid

Payees (6 tools)

ToolDescriptionParameters
actual_payees_getList all payees-
actual_payees_createCreate new payeename
actual_payees_updateUpdate payeeid, name?
actual_payees_deleteDelete payeeid
actual_payees_mergeMerge duplicate payeestargetId, mergeIds[]
actual_payee_rules_getGet rules for a payeepayeeId

Budgets (8 tools)

ToolDescriptionParameters
actual_budgets_get_allList all available budget files-
actual_budgets_getMonthsList available budget months-
actual_budgets_getMonthGet budget for specific monthmonth
actual_budgets_setAmountSet category budget amountmonth, categoryId, amount
actual_budgets_transferTransfer amount between categoriesmonth, fromCategoryId, toCategoryId, amount
actual_budgets_setCarryoverEnable/disable carryovermonth, categoryId, flag
actual_budgets_holdForNextMonthHold funds for next monthmonth, categoryId
actual_budgets_resetHoldReset hold statusmonth, categoryId

Rules (4 tools)

ToolDescriptionParameters
actual_rules_getList all rules-
actual_rules_createCreate transaction ruleconditions, actions
actual_rules_updateUpdate ruleid, conditions?, actions?
actual_rules_deleteDelete ruleid

Advanced Query & Sync (2 tools)

ToolDescriptionParameters
actual_query_runExecute custom ActualQL queryquery
actual_bank_syncTrigger bank sync (GoCardless/SimpleFIN)accountId?

Batch Operations (1 tool)

ToolDescriptionParameters
actual_budget_updates_batchBatch multiple budget updatesupdates (function)

Server Information (1 tool)

ToolDescriptionParameters
actual_server_infoGet Actual Budget server version and build info-

Session Management (2 tools)

ToolDescriptionParameters
actual_session_listList all active MCP sessions-
actual_session_closeClose a specific MCP sessionsessionId

Total: 51 tools across 11 categories (including 6 exclusive ActualQL-powered tools)


🚧 Missing Tools (Not Yet Implemented)

The following Actual Budget API features are not yet exposed as MCP tools:

Schedules (4 API methods available)

  • getSchedules() - List all scheduled transactions
  • createSchedule() - Create recurring transaction schedules
  • updateSchedule() - Modify schedule parameters
  • deleteSchedule() - Remove schedules

These methods require access to Actual's internal API and are not directly exported from the standard methods module.

Additional Multi-Budget Tools

  • loadBudget() - Switch between budgets
  • downloadBudget() - Download budget from server
  • runImport() - Create and populate new budgets

Lookup Helper

  • getIDByName() - Look up IDs by name for accounts/payees/categories/schedules

Note: Most core financial operations (accounts, transactions, budgets, categories, payees, rules) are fully implemented with 51 tools. The missing features represent specialized workflows requiring deeper API integration. Contributions welcome!

📦 Installation

Method 1: npm (Development)

# Clone repository
git clone https://github.com/agigante80/actual-mcp-server.git
cd actual-mcp-server

# Install dependencies
npm install

# Configure environment
cp .env.example .env
# Edit .env with your credentials

# Build TypeScript
npm run build

# Run server
npm run dev -- --debug --http

Method 2: Docker (Recommended for Production)

# Pull image
docker pull ghcr.io/agigante80/actual-mcp-server:latest

# Create secrets directory
mkdir -p secrets
echo "your_password" > secrets/actual_password.txt
chmod 600 secrets/actual_password.txt

# Run container
docker run -d \
  --name actual-mcp-server \
  -p 3000:3000 \
  -e ACTUAL_SERVER_URL=http://your-actual-server:5006 \
  -e ACTUAL_PASSWORD_FILE=/run/secrets/actual_password \
  -e ACTUAL_BUDGET_SYNC_ID=your_sync_id \
  -v $(pwd)/secrets/actual_password.txt:/run/secrets/actual_password:ro \
  -v actual-mcp-data:/data \
  ghcr.io/agigante80/actual-mcp-server:latest

Method 3: Docker Compose (Full Stack)

# Clone repository
git clone https://github.com/agigante80/actual-mcp-server.git
cd actual-mcp-server

# Configure environment
cp .env.example .env
# Edit .env

# Start full stack (Actual Budget + MCP Server)
docker compose --profile fullstack --profile dev up -d

# Or production mode
mkdir -p secrets
echo "your_password" > secrets/actual_password.txt
docker compose --profile production up -d

💬 AI Client Integration (LibreChat & LobeChat)

This MCP server has been tested and verified with both LibreChat and LobeChat. All 51 tools work correctly with both clients.

Quick Setup (Docker Environment)

⚠️ Important for Docker Deployments: Always use internal Docker hostnames (not host IPs) for container-to-container communication. This provides better performance, security, and reliability.

CORRECT: http://actual-mcp-server-backend:3600/http
AVOID: http://192.168.x.x:3600/http

1. Start the MCP Server

# Using Docker Compose (recommended)
docker compose up -d mcp-server

# Or standalone Docker
docker run -d --name actual-mcp-server-backend \
  --network your-ai-network \
  -p 3600:3600 \
  -e ACTUAL_SERVER_URL=http://your-actual-server:5006 \
  -e ACTUAL_PASSWORD=your_password \
  -e ACTUAL_BUDGET_SYNC_ID=your_sync_id \
  -e MCP_SSE_AUTHORIZATION=$(openssl rand -hex 32) \
  -v actual-mcp-data:/data \
  ghcr.io/agigante80/actual-mcp-server:latest

2a. Configure LibreChat

Edit your librechat.yaml:

# librechat.yaml
mcpServers:
  actual-mcp:
    type: "streamable-http"
    # ✅ Use container name (not IP) if on same Docker network
    url: "http://actual-mcp-server-backend:3600/http"
    headers:
      Authorization: "Bearer YOUR_TOKEN_HERE"
    serverInstructions: true
    timeout: 600000  # 10 minutes

Then restart LibreChat:

docker restart ai-librechat

2b. Configure LobeChat

In LobeChat UI:

  1. Navigate to SettingsLanguage ModelModel Context Protocol
  2. Click Add Plugin
  3. Fill in the configuration:
    • Name: Actual Budget MCP
    • Server Type: HTTP
    • Server URL: http://actual-mcp-server-backend:3600/http ✅ (use container name)
    • Authorization: Bearer YOUR_TOKEN_HERE
  4. Click Save

LobeChat will automatically discover all 51 tools.

Network Configuration

Both the MCP server and AI client containers must be on the same Docker network:

# docker-compose.yml
networks:
  ai-network:
    driver: bridge

services:
  librechat:  # or lobe-chat
    networks:
      - ai-network
  
  actual-mcp-server-backend:
    networks:
      - ai-network

Verify connectivity:

# From LibreChat/LobeChat container
docker exec <ai-container> wget -qO- http://actual-mcp-server-backend:3600/health
# Should return: {"status":"ok","initialized":true,...}

HTTPS Setup (Optional - For External Access)

Note: HTTPS is not required when using internal Docker networking. Use HTTPS only if:

  • Accessing MCP server from outside the Docker network
  • Exposing server to the internet
  • Compliance requirements mandate encryption

For internal Docker-to-Docker communication, HTTP is secure and simpler.

Option 1: Docker Internal Network (Recommended - No HTTPS Needed)

If both containers are on the same Docker network, use HTTP securely without TLS overhead:

# docker-compose.yml
networks:
  ai-network:
    driver: bridge

services:
  librechat:  # or lobe-chat
    networks:
      - ai-network
  
  actual-mcp-server-backend:
    networks:
      - ai-network

Configuration:

# LibreChat librechat.yaml
mcpServers:
  actual-mcp:
    url: "http://actual-mcp-server-backend:3600/http"  # ✅ No HTTPS needed
    
# LobeChat UI
Server URL: http://actual-mcp-server-backend:3600/http  # ✅ No HTTPS needed

Benefits: No certificate management, simpler configuration, same security (network isolation).

Option 2: Self-Signed Certificate (Development/External Access)

For testing HTTPS or external access:

# Generate certificate
mkdir -p certs
openssl req -x509 -newkey rsa:4096 -nodes \
  -keyout certs/key.pem -out certs/cert.pem \
  -days 365 -subj "/CN=your-server-ip" \
  -addext "subjectAltName=IP:your-server-ip,DNS:localhost"

# Run MCP server with HTTPS
docker run -d --name actual-mcp-server-backend \
  -p 3600:3600 \
  -e ACTUAL_SERVER_URL=http://your-actual-server:5006 \
  -e ACTUAL_PASSWORD=your_password \
  -e ACTUAL_BUDGET_SYNC_ID=your_sync_id \
  -e MCP_SSE_AUTHORIZATION=$(openssl rand -hex 32) \
  -e MCP_ENABLE_HTTPS=true \
  -e MCP_HTTPS_CERT=/app/certs/cert.pem \
  -e MCP_HTTPS_KEY=/app/certs/key.pem \
  -v actual-mcp-data:/data \
  -v $(pwd)/certs:/app/certs:ro \
  ghcr.io/agigante80/actual-mcp-server:latest

# Trust certificate in LibreChat (Alpine Linux)
docker cp certs/cert.pem ai-librechat:/tmp/mcp-server.crt
docker exec -u root ai-librechat sh -c "cat /tmp/mcp-server.crt >> /etc/ssl/certs/ca-certificates.crt"
docker restart ai-librechat

Option 3: CA-Signed Certificate (Production/Internet-Facing)

# Using Let's Encrypt (requires domain name)
sudo certbot certonly --standalone -d your-domain.com
sudo cp /etc/letsencrypt/live/your-domain.com/fullchain.pem certs/cert.pem
sudo cp /etc/letsencrypt/live/your-domain.com/privkey.pem certs/key.pem
sudo chown $USER:$USER certs/*.pem

Update AI client to use domain:

# LibreChat librechat.yaml
mcpServers:
  actual-mcp:
    url: "https://your-domain.com:3600/http"  # Use domain name
    
# LobeChat UI
Server URL: https://your-domain.com:3600/http

Verification

Test the connection: ACTUAL_PASSWORD=your_password ACTUAL_BUDGET_SYNC_ID=your_sync_id

Security (generate with: openssl rand -hex 32)

MCP_SSE_AUTHORIZATION=your_bearer_token_here

Transport mode (Docker only - defaults to --http)

Options: --http (recommended), --sse, --ws

MCP_TRANSPORT_MODE=--http

HTTPS (optional but recommended)

MCP_ENABLE_HTTPS=true MCP_HTTPS_CERT=/app/certs/cert.pem MCP_HTTPS_KEY=/app/certs/key.pem

Optional

MCP_BRIDGE_PORT=3600 MCP_BRIDGE_DATA_DIR=/data


### Verification

Test the connection:
```bash
# Health check (adjust URL based on your setup)
curl http://localhost:3600/health
# Or with HTTPS
curl -k https://localhost:3600/health

# Should return: {"status":"ok","initialized":true,...}

In your AI client, you should see:

  • 51 tools loaded in the MCP servers list
  • ✅ All tools available with actual_ prefix
  • ✅ Natural language queries working

Note: Tool count updated from 49 to 51 to reflect current implementation.


💡 Usage Examples

Example 1: Check Account Balances

# Using curl with HTTP transport
curl -X POST http://localhost:3000/http \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "actual_accounts_list",
      "arguments": {}
    }
  }'

Example 2: Add Transaction

curl -X POST http://localhost:3000/http \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "actual_transactions_create",
      "arguments": {
        "accountId": "your-account-id",
        "date": "2025-11-09",
        "amount": -5000,
        "payee": "Amazon",
        "notes": "Office supplies"
      }
    }
  }'

Note: Amounts are in cents (integer). $50.00 = 5000, expenses are negative.

Example 3: Set Monthly Budget

curl -X POST http://localhost:3000/http \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "actual_budgets_setAmount",
      "arguments": {
        "month": "2025-11",
        "categoryId": "your-category-id",
        "amount": 50000
      }
    }
  }'

Example 4: Using with AI Clients (LibreChat/LobeChat)

  1. Start the MCP server: npm run dev -- --http (or use Docker)
  2. Configure your AI client to connect:
    • LibreChat: Edit librechat.yaml with MCP server URL
    • LobeChat: Add plugin via Settings → Model Context Protocol
  3. Ask natural language questions: "What's my checking account balance?"
  4. AI client calls appropriate tools (actual_accounts_list, actual_accounts_get_balance)
  5. You get conversational responses: "Your checking account balance is $1,234.56"

Both LibreChat and LobeChat work identically - all 51 tools are available for conversational financial management.


⚙️ Configuration

Environment Variables

All configuration is managed via environment variables. See .env.example for a complete reference with detailed descriptions.

Complete Environment Variables Reference

VariableDefaultRequiredDescription
Actual Budget Connection
ACTUAL_SERVER_URLhttp://localhost:5006✅ YesURL of your Actual Budget server
ACTUAL_PASSWORD-✅ YesPassword for Actual Budget server
ACTUAL_BUDGET_SYNC_ID-✅ YesBudget Sync ID from Actual (Settings → Sync ID)
ACTUAL_BUDGET_PASSWORD-❌ NoOptional encryption password for encrypted budgets
MCP Server Settings
MCP_BRIDGE_PORT3000 (dev)
3600 (Docker)
❌ NoPort for MCP server to listen on
MCP_BRIDGE_BIND_HOST0.0.0.0❌ NoHost address to bind server to (0.0.0.0 = all interfaces)
MCP_BRIDGE_DATA_DIR./actual-data❌ NoDirectory to store Actual Budget local data (SQLite)
MCP_BRIDGE_PUBLIC_HOSTauto-detected❌ NoPublic hostname/IP for server (shown in logs)
MCP_BRIDGE_PUBLIC_SCHEMEauto-detected❌ NoPublic scheme (http or https)
MCP_BRIDGE_USE_TLSfalse❌ NoLegacy TLS flag (use MCP_ENABLE_HTTPS instead)
MCP_BRIDGE_ADVERTISED_URL-❌ NoHuman-friendly URL displayed to users
Transport Configuration
MCP_TRANSPORT_MODE--http❌ NoTransport mode (--http or --sse) - Docker only
MCP_SSE_PATH/sse❌ NoSSE endpoint path
MCP_HTTP_PATH/http❌ NoHTTP endpoint path
Session Management
USE_CONNECTION_POOLtrue❌ NoEnable session-based connection pooling
MAX_CONCURRENT_SESSIONS15❌ NoMaximum concurrent MCP sessions allowed
SESSION_IDLE_TIMEOUT_MINUTES5 (pool)
2 (HTTP)
❌ NoMinutes before idle session cleanup
Security & Authentication
MCP_SSE_AUTHORIZATION-❌ NoBearer token for authentication (highly recommended)
MCP_ENABLE_HTTPSfalse❌ NoEnable HTTPS/TLS encryption
MCP_HTTPS_CERT-⚠️ If HTTPSPath to TLS certificate file (PEM format)
MCP_HTTPS_KEY-⚠️ If HTTPSPath to TLS private key file (PEM format)
ACTUAL_PASSWORD_FILE-❌ NoPath to file containing password (Docker secrets)
Logging Configuration
MCP_BRIDGE_STORE_LOGSfalse❌ NoEnable file logging (vs console only)
MCP_BRIDGE_LOG_DIR./logs❌ NoDirectory for log files (if STORE_LOGS=true)
MCP_BRIDGE_LOG_LEVELinfo❌ NoLog level: error, warn, info, debug
LOG_LEVELinfo❌ NoAlternative log level variable (overrides default)
MCP_BRIDGE_DEBUG_TRANSPORTfalse❌ NoEnable transport-level debug logging
Log Rotation (when MCP_BRIDGE_STORE_LOGS=true)
MCP_BRIDGE_MAX_FILES14d❌ NoKeep rotated logs for N days (e.g., 14d, 30d)
MCP_BRIDGE_MAX_LOG_SIZE20m❌ NoRotate when file reaches size (e.g., 20m, 100m)
MCP_BRIDGE_COMPRESS_AFTER_HOURS24❌ NoCompress logs older than N hours
MCP_BRIDGE_ROTATE_DATEPATTERNYYYY-MM-DD❌ NoDate pattern for rotated log filenames
Development & Testing
DEBUGfalse❌ NoEnable debug mode (verbose logging)
SKIP_BUDGET_DOWNLOADfalse❌ NoSkip budget sync on startup (testing)
TEST_ACTUAL_CONNECTIONfalse❌ NoTest connection only and exit
TEST_ACTUAL_TOOLSfalse❌ NoRun all tools test and exit
USE_TEST_DATAfalse❌ NoUse mock data instead of real Actual server
Advanced/Internal
ACTUAL_API_CONCURRENCY5❌ NoMax concurrent Actual API operations
VERSIONauto-detected❌ NoServer version (auto-set by build/Docker)
ENABLE_METRICSfalse❌ NoEnable Prometheus metrics endpoint
METRICS_PORT9090❌ NoPort for metrics endpoint
TZUTC❌ NoTimezone for timestamps (e.g., America/New_York)

Quick Start Configuration

Minimum required variables to get started:

# .env file
ACTUAL_SERVER_URL=http://localhost:5006
ACTUAL_PASSWORD=your_password
ACTUAL_BUDGET_SYNC_ID=abc123

Recommended production configuration:

# Required
ACTUAL_SERVER_URL=https://actual.yourdomain.com
ACTUAL_PASSWORD=your_password
ACTUAL_BUDGET_SYNC_ID=abc123

# Security (generate token: openssl rand -hex 32)
MCP_SSE_AUTHORIZATION=your_secure_random_token

# HTTPS
MCP_ENABLE_HTTPS=true
MCP_HTTPS_CERT=/path/to/cert.pem
MCP_HTTPS_KEY=/path/to/key.pem

# Logging
MCP_BRIDGE_STORE_LOGS=true
MCP_BRIDGE_LOG_LEVEL=info

# Session Management
MAX_CONCURRENT_SESSIONS=15
SESSION_IDLE_TIMEOUT_MINUTES=5

Docker-specific settings:

Docker images use these defaults (can be overridden):

  • MCP_BRIDGE_PORT=3600 (instead of 3000)
  • MCP_TRANSPORT_MODE=--http (set in CMD)
  • MCP_BRIDGE_DATA_DIR=/data (recommended for volume mount)

🔌 Transports & Authentication

The MCP server supports two transport protocols with optional Bearer token authentication.

Docker Note: When running in Docker, HTTP transport is the default. Override with MCP_TRANSPORT_MODE environment variable (--http or --sse). For local development, specify the transport mode using command-line flags.

Available Transports

TransportTypeLibreChat SupportAuthenticationRecommended
HTTPstreamable-http✅ Full support✅ Working✅ RECOMMENDED
SSEsse✅ Supported⚠️ Headers not sent by clientUse without auth

1. HTTP Transport (Recommended)

Best for: Production deployments with LibreChat

# Start server with HTTP
npm run dev -- --http

# Or with authentication
MCP_SSE_AUTHORIZATION=your_token npm run dev -- --http

LibreChat Configuration:

# librechat.yaml
mcpServers:
  actual-mcp:
    type: "streamable-http"
    url: "http://192.168.8.245:3600/http"
    headers:
      Authorization: "Bearer your_token_here"
    serverInstructions: true

Features:

  • ✅ Full MCP protocol support via @modelcontextprotocol/sdk
  • ✅ Bearer token authentication via headers
  • ✅ All 51 tools load successfully in LibreChat
  • ✅ Session management with MCP-Session-Id headers
  • ✅ Production-ready and tested

2. SSE Transport (Alternative)

Best for: Development or non-authenticated environments

# Start server with SSE
npm run dev -- --sse

LibreChat Configuration:

# librechat.yaml (without authentication)
mcpServers:
  actual-mcp:
    type: "sse"
    url: "http://192.168.8.245:3600/sse"
    serverInstructions: true

Features:

  • ✅ Full MCP protocol support
  • ✅ Server-side authentication implemented
  • ⚠️ LibreChat SSE client doesn't send custom headers
  • ✅ Works perfectly without authentication

Known Limitation: While the server supports Bearer token authentication for SSE, LibreChat's SSE client implementation does not send custom headers specified in the configuration. Use HTTP transport for authenticated deployments.

Authentication Configuration

Enable Authentication

Set the MCP_SSE_AUTHORIZATION environment variable:

# Generate a secure token
openssl rand -hex 32

# Add to .env
MCP_SSE_AUTHORIZATION=your_generated_token_here

# Restart server
npm run build
npm run dev -- --http

Authentication Behavior

When MCP_SSE_AUTHORIZATION is set:

  • ✅ Server validates Bearer tokens on all requests
  • ✅ Returns 401 Unauthorized for missing/invalid tokens
  • ✅ Logs all authentication attempts
  • ✅ Optional - if not set, authentication is disabled

Security Best Practices

# ✅ DO: Use strong random tokens (32+ characters)
MCP_SSE_AUTHORIZATION=$(openssl rand -hex 32)

# ✅ DO: Use HTTPS in production
ACTUAL_SERVER_URL=https://actual.yourdomain.com

# ✅ DO: Rotate tokens regularly
# ✅ DO: Use environment variables, never hardcode
# ✅ DO: Monitor authentication failures in logs

# ❌ DON'T: Use weak or predictable tokens
# ❌ DON'T: Commit tokens to version control
# ❌ DON'T: Share tokens between environments

Testing Results

Comprehensive testing completed with LibreChat:

Test CaseResultTools Loaded
HTTP without auth✅ Success51 tools
HTTP with auth✅ Success51 tools
SSE without auth✅ Success51 tools
SSE with auth⚠️ Client limitation0 tools (headers not sent)

Conclusion: Use HTTP transport with Bearer token authentication for secure production LibreChat deployments.

See docs/transport-testing-final-report.md for detailed test results.


🐳 Docker Deployment

Docker Hub

Pull from GitHub Container Registry:

docker pull ghcr.io/agigante80/actual-mcp-server:latest

Production Deployment

# 1. Create secrets
mkdir -p secrets
echo "your_password" > secrets/actual_password.txt
chmod 600 secrets/actual_password.txt

# 2. Create environment file
cat > .env << EOF
ACTUAL_SERVER_URL=https://actual.yourdomain.com
ACTUAL_BUDGET_SYNC_ID=abc123
MCP_SSE_AUTHORIZATION=$(openssl rand -hex 32)
EOF

# 3. Run with Docker Compose
docker compose --profile production up -d

# 4. Check logs
docker compose logs -f mcp-server-prod

# 5. Verify health
curl http://localhost:3000/health

Kubernetes

See docs/deployment.md for Kubernetes manifests with:

  • Deployments with health checks
  • Services and Ingress
  • Secret management
  • Resource limits
  • Horizontal scaling

🏗️ Architecture

┌──────────────┐         ┌──────────────┐         ┌─────────────┐
│   LibreChat  │   MCP   │  MCP Server  │   REST  │   Actual    │
│  (AI Client) │◄────────┤  (51 Tools)  │◄────────┤   Budget    │
│              │         │  +6 Exclusive│         │             │
└──────────────┘         └──────────────┘         └─────────────┘
                                │
                                ▼
                         ┌──────────────┐
                         │   SQLite DB  │
                         │  (Cached)    │
                         └──────────────┘

Tech Stack

  • Runtime: Node.js 20 (Alpine Linux in Docker)
  • Language: TypeScript 5.9 (ES2020 + ESM modules)
  • MCP SDK: @modelcontextprotocol/sdk ^1.18.2
  • API Client: @actual-app/api ^25.9.0
  • Validation: Zod (runtime type checking + JSON Schema)
  • Transport: Express (HTTP), EventEmitter (SSE)
  • Logging: Winston with daily rotation
  • Testing: Playwright, unit tests, integration tests

Key Design Patterns

  • Singleton: Tool manager, logger, config
  • Adapter: Wraps Actual API with retry/concurrency
  • Strategy: Multiple transport implementations
  • Factory: Dynamic tool loading and registration

See docs/architecture.md for detailed architecture documentation.


📊 API Coverage

Coverage by Category

CategoryCoverageToolsStatus
Accounts100%7/7✅ Complete
Transactions100%12/12✅ Complete + 6 exclusive ActualQL tools
Categories100%4/4✅ Complete
Category Groups100%4/4✅ Complete
Payees100%6/6✅ Complete
Budgets100%8/8✅ Complete
Rules100%4/4✅ Complete
Query & Sync100%2/2✅ Complete
Batch100%1/1✅ Complete
Server Info100%1/1✅ Complete

Overall: 82% API Coverage (51 tools covering all major Actual Budget operations)

⚡ Exclusive Features: This MCP server includes 6 ActualQL-powered tools that provide advanced querying and aggregation capabilities not available in other Actual Budget integrations.

Not Yet Implemented

  • ❌ Scheduled transactions (recurring payments)
  • ❌ Budget templates
  • ❌ Transaction notes/attachments

Note: The actual_query_run tool provides direct ActualQL access for advanced custom queries beyond the 51 pre-built tools.

  • ❌ Custom reports

See docs/api-coverage.md for complete API documentation with examples.


🧪 Testing

Quick Test Commands

# Run all tests (adapter + unit + Docker E2E)
npm run test:all

# Run protocol tests only (fast)
npm run test:e2e

# Run Docker integration tests (full stack)
npm run test:e2e:docker

Test Types

CommandWhat It TestsSpeedRequires Docker
test:adapterAdapter layer, retry logic, concurrency⚡ 30sNo
test:unit-jsUnit tests (transactions)⚡ 5sNo
test:e2eMCP protocol compliance⚡ 10sNo
test:e2e:dockerFull stack integration🐢 60sYes
test:allAll of the above🐢 90sYes

Docker E2E Tests

The Docker E2E tests provide comprehensive full-stack validation:

# Run Docker-based integration tests
npm run test:e2e:docker

# Advanced options
./tests/e2e/run-docker-e2e.sh --no-cleanup   # Leave containers for debugging
./tests/e2e/run-docker-e2e.sh --verbose      # Show detailed output

What's tested:

  • ✅ Docker build correctness
  • ✅ Container networking
  • ✅ Real Actual Budget integration (all 51 tools)
  • ✅ Session management
  • ✅ Production deployment scenario

See docs/DOCKER_E2E_TESTING.md for complete documentation.


🤝 Contributing

We welcome contributions! Please see our Contributing Guide for:

  • Development setup
  • Code standards
  • Testing requirements
  • Pull request process
  • Auto-merge workflow

Quick Contribution Guide

  1. Fork the repository
  2. Create feature branch: git checkout -b feature/my-feature
  3. Make changes and add tests
  4. Commit: git commit -m "feat: add new feature"
  5. Push: git push origin feature/my-feature
  6. Open Pull Request

📚 Documentation


📜 License

This project is licensed under the MIT License - see the LICENSE file for details.


🙏 Acknowledgments


📞 Support & Community


Made with ❤️ by the Actual MCP Server community

Reviews

No reviews yet

Sign in to write a review