ZenMoney MCP Server
MCP server for trustworthy personal-finance analytics and explicitly confirmed user-entity changes over the ZenMoney API. The project started as a fork of nnslvp/zenmoney-mcp and is maintained here as a substantially extended version. It keeps its working data local and adds financially conservative calculations, atomic sync, and a two-step write workflow.
Complete tool catalog
Both local and remote modes expose 56 tools. They share 54 tools and use two mode-specific tools for synchronization and category suggestions.
| Area | Tools |
|---|---|
| Financial analytics | get_net_worth, get_liquidity, analyze_spending, analyze_income, analyze_merchants, check_budget_health, get_upcoming_payments, analyze_trends, detect_recurring, get_account_flow, analyze_transfers, detect_anomalies, get_debts, convert_currency, get_exchange_rates, search_transactions |
| Planning analytics | get_financial_snapshot, get_cash_flow, get_spending_baseline, compare_periods, get_emergency_fund_status, get_debt_service, forecast_cash_flow |
| Decision support | plan_emergency_fund, plan_debt_payoff, compare_debt_strategies, plan_financial_goal, plan_multiple_goals, run_financial_scenario, build_financial_plan |
| Entity reads | list_accounts, get_account, list_tags, get_tag, list_merchants, get_merchant, list_reminders, get_reminder, list_reminder_markers, get_reminder_marker, list_transactions, get_transaction, list_budgets, get_budget |
| Confirmed entity changes | prepare_account_changes, prepare_tag_changes, prepare_merchant_changes, prepare_reminder_changes, prepare_reminder_marker_changes, prepare_transaction_changes, prepare_budget_changes, prepare_mixed_changes, get_change_proposal, apply_changes |
| Mode | Mode-specific tools |
|---|---|
| Local stdio | sync_data, suggest_category |
| Remote through OpenAI Secure MCP Tunnel | force_sync, get_sync_status |
Analytics
| Question | Tool |
|---|---|
| How much money do I have? | get_net_worth |
| Can I afford a purchase? | get_liquidity |
| Where does my money go? | analyze_spending |
| Where does my income come from? | analyze_income |
| Which merchants receive my money? | analyze_merchants |
| Am I within budget? | check_budget_health |
| What subscriptions do I have? | detect_recurring |
| How are income and spending changing? | analyze_trends |
| What transfers did I make? | analyze_transfers |
| Are there unusual or duplicate expenses? | detect_anomalies |
| Who owes whom? | get_debts |
| What payments are coming up? | get_upcoming_payments |
| Find matching transactions | search_transactions |
| What happened on this account? | get_account_flow |
| Convert currencies | convert_currency, get_exchange_rates |
| Overall financial position | get_financial_snapshot |
| Monthly cash flow | get_cash_flow |
| Normal spending level | get_spending_baseline |
| Compare periods | compare_periods |
| Emergency fund coverage | get_emergency_fund_status |
| Debt burden | get_debt_service |
| 30/60/90 day forecast | forecast_cash_flow |
The server also exposes paginated collection and exact resources for Account,
Tag, Merchant, Reminder, ReminderMarker, Transaction, and Budget, plus currencies,
synchronization status, and a cache-only financial snapshot at
zenmoney://financial-snapshot. Reading a resource never starts synchronization.
Confirmed user-entity changes
All writes use two separate calls. Choose the entity-specific prepare tool for
ordinary work, or prepare_mixed_changes when one proposal creates or changes
several related entity types:
prepare_account_changes prepare_tag_changes
prepare_merchant_changes prepare_reminder_changes
prepare_reminder_marker_changes
prepare_transaction_changes prepare_budget_changes
prepare_mixed_changes
get_change_proposal apply_changes
Prepare validates 1–100 operations and returns an immutable field-by-field
preview without writing to ZenMoney. After reviewing it, pass only its
proposal_id to apply_changes; get_change_proposal reports state and results.
Preparation requires a successful full sync so that untouched ZenMoney fields can be preserved. Apply rejects the whole proposal before writing if any source entity changed since preparation. Related creates are submitted in dependency layers because the live API does not safely accept every dependency in one Diff request; a failed layer is never retried or rolled back automatically.
Create and update are supported for all seven user entities. Safe delete archives
an Account, marks a Transaction or ReminderMarker deleted, or clears a Budget.
Tag, Merchant, and Reminder deletion and all physical purge operations are not
exposed. Prepared proposals expire after 24 hours. Terminal proposals are retained
for 30 days, and an uncertain write or verification result becomes needs_review.
Planning analytics are deliberately conservative:
- emergency-fund coverage requires explicit essential category IDs or a monthly essential-spending override;
- debt service reports observed balances and payments but does not infer APR, minimum payments, or amortization schedules;
- recurring-payment detection is a historical heuristic and is labeled as such;
- cash-flow forecasts are transparent scenarios, not prediction guarantees.
Financial Planning
Phase 3 adds deterministic decision support on top of the factual analytics. Every result exposes inputs, assumptions, constraints, reasons, alternatives, and measurable outcomes; it does not execute or write a financial decision.
| Question | Tool |
|---|---|
| How fast can I build a 6-month emergency fund? | plan_emergency_fund |
| Should I pay the high-interest loan first? | plan_debt_payoff, compare_debt_strategies |
| Can I afford a car in 18 months? | plan_financial_goal |
| Which of my goals conflict? | plan_multiple_goals |
| What happens if my income falls by 20%? | run_financial_scenario |
| How should I allocate my monthly free cash flow? | build_financial_plan |
Planning inputs that ZenMoney does not contain must be supplied explicitly:
{
"emergency_fund": {
"target_months": 6,
"essential_category_ids": ["category-id"]
},
"debt_accounts": {
"loan-account-id": {
"apr_pct": 19.9,
"minimum_payment": 15000
}
},
"goals": []
}
Missing APR, minimum payments, or essential-spending configuration returns
configuration_required; the server never invents those values. Calculations
use zero investment return, Decimal money arithmetic, and future calendar
month-end snapshots. Restricted deposits are excluded from emergency reserves
unless explicitly enabled, and credit capacity is always excluded.
See docs/planning-semantics.md for the priority
policy, formulas, rounding, data-quality labels, and limitations.
Runtime modes
The installed zenmoney-mcp command is the local stdio server for Codex,
ChatGPT Desktop, Claude Desktop, and Cursor. It uses the shared SDK v2
registry and hardened runtime directly; it does not preserve an upstream
server through a runtime overlay.
For a private remote deployment, zenmoney-mcp-http exposes Streamable HTTP
at /mcp only inside Docker, and the OpenAI Secure MCP Tunnel client connects
outbound to OpenAI. The remote registry excludes the local API-dependent
sync_data and suggest_category tools. Its analytical tools remain read-only.
Remote force_sync can request a cache refresh, while confirmed entity-change
proposals are queued for the separate credentialed worker. get_sync_status
and get_change_proposal report their respective progress. The MCP
container still receives no ZenMoney token and cannot write the financial
snapshot or call ZenMoney directly. See the
remote operations runbook and
threat model.
Hardening in this fork
The installed zenmoney-mcp command runs zenmoney_mcp.entrypoint against a
shared SDK v2 registry with hardened database, synchronization, and analytics
implementations:
HardenedDatabaseadds idempotent migrations and strict FX handling;HardenedSyncEnginevalidates responses and atomically replaces the live cache;- corrected analytics cover net worth, liquidity, budgets, debts, account flow, spending, transaction search, upcoming payments, and FX;
- remaining analytics receive bounded runtime validation;
- MCP discovery advertises the same limits enforced at runtime.
Key semantics:
net_worthincludes only active accounts within_balance=true;- excluded accounts are returned separately in
net_worth_all_accounts; - credit is borrowing capacity, not an asset;
- accessible savings and term deposits are not treated as equivalent liquidity;
- budget periods respect the user's configured month-start day;
- zero-budget and unbudgeted spending are surfaced explicitly;
- debt-account balances are authoritative and attribution gaps remain visible;
- account flow includes signed transfers in native and user currency;
- missing or zero exchange rates fail explicitly instead of becoming a 1:1 rate;
- full sync replaces the cache, preventing stale rows from surviving.
More detail is available in
README-HARDENING.md.
Local installation with uvx
Install uv, then run the server from PyPI:
Get a personal API token at zerro.app/token, as documented in the official ZenMoney API wiki.
export ZENMONEY_TOKEN="replace-with-your-token"
uvx --from zenmoney-mcp-server zenmoney-mcp
uvx downloads the package into an isolated cached environment; cloning the
repository or creating a virtual environment is not required.
To run the current main branch before its next PyPI release, use
uvx --from git+https://github.com/ekho/zenmoney-mcp.git zenmoney-mcp.
The first hardened start performs additive SQLite migrations. Back up
~/.cache/zenmoney-mcp/zenmoney.db before the first run when preserving an
existing cache matters. A full sync can recreate the cache from ZenMoney.
Private ChatGPT installation with OpenAI Secure MCP Tunnel
ChatGPT web cannot start the local stdio command. For private remote access, run the included Docker Compose deployment and connect it through OpenAI Secure MCP Tunnel. The MCP endpoint stays inside the Docker network; only the tunnel client makes an outbound connection to OpenAI.
This mode requires Docker Engine with Compose v2, a ZenMoney token, an OpenAI tunnel runtime API key, and a tunnel ID associated with the target ChatGPT workspace. Clone the repository and create the non-secret environment file:
git clone https://github.com/ekho/zenmoney-mcp.git ~/zenmoney-mcp
cd ~/zenmoney-mcp
cp deploy/remote-mcp/.env.example deploy/remote-mcp/.env
Set CONTROL_PLANE_TUNNEL_ID in deploy/remote-mcp/.env. Provision the
ZenMoney token and OpenAI key as separate file-backed Compose secrets using the
ownership and permission commands in the
remote operations runbook; do not put either
secret in .env. Then pull and start the deployment:
docker compose --env-file deploy/remote-mcp/.env \
-f deploy/remote-mcp/compose.yaml pull
docker compose --env-file deploy/remote-mcp/.env \
-f deploy/remote-mcp/compose.yaml up -d --no-build --pull never
docker compose --env-file deploy/remote-mcp/.env \
-f deploy/remote-mcp/compose.yaml ps
Complete the health checks and tunnel-client doctor from the runbook, then add
the MCP app in ChatGPT Developer Mode with Connection = Tunnel and scan its
tools.
ChatGPT Desktop and Codex
Add the server to ~/.codex/config.toml:
[mcp_servers.zenmoney]
command = "uvx"
args = ["--from", "git+https://github.com/ekho/zenmoney-mcp.git", "zenmoney-mcp"]
env_vars = ["ZENMONEY_TOKEN"]
tool_timeout_sec = 120
Restart the desktop client after changing MCP configuration. For ChatGPT web, use the private remote Streamable HTTP + Secure MCP Tunnel deployment in the operations runbook, not a local executable.
Claude Desktop or Cursor
{
"mcpServers": {
"zenmoney": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/ekho/zenmoney-mcp.git",
"zenmoney-mcp"
],
"env": {
"ZENMONEY_TOKEN": "replace-with-your-token"
}
}
}
}
Development
Clone the repository only when changing or testing the code locally:
git clone https://github.com/ekho/zenmoney-mcp.git ~/zenmoney-mcp
cd ~/zenmoney-mcp
uv sync --extra dev
Data flow
- Local
sync_datareads/v8/diff/directly through the local sync engine. - In the remote deployment, the periodic worker reads
/v8/diff/; remoteforce_synconly asks that credentialed worker to run immediately. - Both modes publish a SQLite cache at
~/.cache/zenmoney-mcp/zenmoney.dbor the configuredZENMONEY_DB_PATH; analytics read that cache locally. - A local confirmed proposal is written synchronously. A remote confirmed proposal is persisted on the control volume and written by the worker.
- Only the local process or credentialed worker can call ZenMoney.
Testing
uv sync --extra dev
uv run python -m compileall -q src tests
uv run python -m pytest tests/ -v --ignore=tests/test_integration.py
The live integration test requires ZENMONEY_TOKEN and is excluded from CI.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zenmoney_mcp_server-0.5.2.tar.gz.
File metadata
- Download URL: zenmoney_mcp_server-0.5.2.tar.gz
- Upload date:
- Size: 101.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
70e763ad0662e24e7b734ad80f4235047f89639f4272e0e06a1d158e4a6e11c9
|
|
| MD5 |
c1917a7c6695d6cd93f16160e7e528e5
|
|
| BLAKE2b-256 |
c380dd566fa187f2609b8d7c05c4cd7d40ae686f6233e3df624e6a4dbeb0af09
|
File details
Details for the file zenmoney_mcp_server-0.5.2-py3-none-any.whl.
File metadata
- Download URL: zenmoney_mcp_server-0.5.2-py3-none-any.whl
- Upload date:
- Size: 117.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0a84877b2a0bba0f63759c3861bb5a17eaa71c1550ec829226700724e19ded19
|
|
| MD5 |
a04629af5761a06c798c11cba2ca6217
|
|
| BLAKE2b-256 |
d801ee9e50364caef499deb99277f6ec4688cb40ab6ddea6c1b636c43d50fa81
|