Install
$ agentstack add mcp-trek-boldly-go-actual-budget-mcp-server ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README — it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming — see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps — measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Simple Streamable HTTP MCP Server
A minimal Model Context Protocol (MCP) HTTP server for the Actual Budget API. It exposes the Actual tools over HTTP with server-sent events for streaming responses.
What it does
- Runs an MCP server at
/mcpwith streaming (SSE) support. - Provides tool endpoints for Accounts, Transactions, Categories, Payees, Rules, reporting, and AI-style summaries.
Prerequisites
- Node 18+ (matches the SDK requirement)
- npm
- Actual credentials:
ACTUAL_SERVER_URL,ACTUAL_PASSWORD,ACTUAL_SYNC_ID(required to start the server)
Install
npm install
Run (dev)
export ACTUAL_SERVER_URL=https://your-actual
export ACTUAL_PASSWORD=...
export ACTUAL_SYNC_ID=... # Find your sync_id in your Actual Server advanced settings
npm run dev
The server listens on http://localhost:3000/mcp by default.
Build & run compiled output
npm run build
npm start
Docker
Build and run locally:
npm run docker:run
# or manually
npm run docker:build
docker run --rm -p 3000:3000 \
-e MCP_PORT=3000 \
-e ACTUAL_SERVER_URL=https://your-actual \
-e ACTUAL_PASSWORD=... \
-e ACTUAL_SYNC_ID=... \
ghcr.io/trek-boldly-go/actual-budget-mcp-server:latest
Docker Compose (MCP + Keycloak OAuth)
The repo includes a compose stack that runs the MCP server and Keycloak, with a realm preloaded from an env-templated JSON. First/last name and email are populated so the default user can authenticate without extra profile setup.
docker compose up -d
Services:
mcp(ports3000:3000): runs withMCP_AUTH_MODE=oauthand introspects tokens against Keycloak.keycloak(ports8080:8080): importsdocker/keycloak/realm-export/actual-mcp-realm.template.jsonrendered bykeycloak-realm-builder. If you changeKEYCLOAK_REALM, also override the MCP issuer envs (compose defaults do not nest).keycloak-realm-builder: renders the realm template with env vars before Keycloak starts.
Images and platforms:
mcpservice keeps the published image name and will build locally if missing (so arm64 works). Override the target platform withMCP_DOCKER_PLATFORM(defaultlinux/amd64); if you prefer native, setMCP_DOCKER_PLATFORM=linux/arm64.
Environment knobs (with defaults):
MCP_PORT(default3000),MCP_AUTH_MODE(defaultoauth),MCP_PUBLIC_URL(defaulthttp://localhost:3000/mcp)MCP_OAUTH_ISSUER_URL(defaulthttp://keycloak:8080/realms/${KEYCLOAK_REALM})KEYCLOAK_REALM(defaultactual-mcp)KEYCLOAK_PUBLIC_CLIENT_ID(defaultactual-mcp-public)KEYCLOAK_INTROSPECTION_CLIENT_ID/KEYCLOAK_INTROSPECTION_CLIENT_SECRET(defaultactual-mcp-introspection/actual-mcp-introspection-secret)KEYCLOAK_DEMO_USER/KEYCLOAK_DEMO_PASSWORD(defaultdemo-user/demo-pass)KEYCLOAK_DEMO_FIRST_NAME/KEYCLOAK_DEMO_LAST_NAME(defaultDemo/User)KEYCLOAK_ADMIN/KEYCLOAK_ADMIN_PASSWORD(defaultadmin/admin)- Actual credentials (required):
ACTUAL_SERVER_URL,ACTUAL_PASSWORD,ACTUAL_SYNC_ID
Getting a token for testing:
curl -X POST http://localhost:8080/realms/actual-mcp/protocol/openid-connect/token \
-d grant_type=password \
-d client_id=actual-mcp-public \
-d username=demo-user \
-d password=demo-pass
Then call the MCP server with Authorization: Bearer . If you prefer UI login, you can also run a standard authorization code flow via your OAuth client.
Redirect URIs for actual-mcp-public (preloaded):
http://localhost:3000/*http://localhost:4173/*
Add your MCP client’s exact callback if it differs.
Environment variables
MCP_PORT(default3000)MCP_PUBLIC_URL(defaulthttp://localhost:/mcp) used for OAuth resource metadataMCP_AUTH_MODE(bearerdefault) choosenone,bearer, oroauthMCP_BEARER_TOKENrequired whenMCP_AUTH_MODE=bearer- OAuth mode:
MCP_OAUTH_INTERNAL_ISSUER_URL(e.g.,http://keycloak:8080/realms/actual-mcp) used by the server for discovery/introspection (defaults toMCP_OAUTH_ISSUER_URL)MCP_OAUTH_ISSUER_URL(back-compat) falls back to internal issuer whenMCP_OAUTH_INTERNAL_ISSUER_URLis unset. If you changeKEYCLOAK_REALM, update these explicitly; compose does not nest defaults.MCP_OAUTH_CLIENT_ID/MCP_OAUTH_CLIENT_SECRET(used for token introspection)MCP_OAUTH_INTROSPECTION_URL(optional override; defaults to issuer metadata)MCP_OAUTH_AUDIENCE(optional audience/resource value to enforce)MCP_OAUTH_PUBLIC_ISSUER_URL(optional) public-facing issuer to advertise in metadata; server still uses the internal issuer for discovery/introspectionMCP_DANGEROUSLY_ALLOW_INSECURE_ISSUER_URL(set totruefor HTTP issuers in dev only)- Actual configuration:
ACTUAL_SERVER_URL,ACTUAL_PASSWORD,ACTUAL_SYNC_ID(required for real Actual usage) ACTUAL_DATA_DIR(default/app/.actual-datain Docker)ACTUAL_ENCRYPTION_PASS(optional)- Amounts: all tools and rules use the currency’s smallest unit (e.g., cents for USD).
Authentication
The server now supports three modes via MCP_AUTH_MODE (default bearer):
bearer(default): RequireAuthorization: Beareron every/mcprequest. SetMCP_BEARER_TOKENto a long random value.oauth: Use OAuth 2.1/2.0 Bearer tokens from an external Authorization Server (e.g., Keycloak). The server introspects tokens withMCP_OAUTH_CLIENT_ID/MCP_OAUTH_CLIENT_SECRET, exposes OAuth protected resource metadata at/.well-known/oauth-protected-resource/mcp, and enforcesMCP_OAUTH_AUDIENCEif provided.none: No authentication (only for trusted networks).
Quick bearer setup: 1) Set MCP_AUTH_MODE=bearer and MCP_BEARER_TOKEN="". 2) Restart the server. 3) Configure your MCP client to send Authorization: Bearer on /mcp.
Quick OAuth (Keycloak) flow: 1) Set MCP_AUTH_MODE=oauth, MCP_OAUTH_ISSUER_URL, MCP_OAUTH_CLIENT_ID, MCP_OAUTH_CLIENT_SECRET, and optionally MCP_OAUTH_AUDIENCE. 2) Ensure your Authorization Server supports the RFC 7662 introspection endpoint; the server will auto-discover it from the issuer metadata. 3) Clients can discover the resource metadata at /.well-known/oauth-protected-resource/mcp and follow the authorization_servers entry to your IdP.
Development notes
- Lint:
npm run lint(standard-with-typescript, semicolons enforced) - Build:
npm run build - CI publishes Docker images to GHCR on
mainpushes (.github/workflows/ci.yml).
Where to wire in the real Actual API
src/actual/client.tsconnects directly to your Actual server. All reads and writes are live; provide required env vars before starting.
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: trek-boldly-go
- Source: trek-boldly-go/actual-budget-mcp-server
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.