Skip to main content
Glama
frapbod

Monarch MCP

by frapbod
README.md
# Monarch MCP

A small TypeScript MCP server for Monarch Money. Its TypeScript Monarch client
is pinned to a tested fork so upstream GraphQL changes can be fixed and verified
without waiting for an npm release.

The server uses the stable MCP TypeScript SDK v2 and serves both the modern
`2026-07-28` protocol and older MCP clients over stdio. Every successful call
returns concise text plus a schema-declared `structuredContent` envelope; clients
read the envelope for the data rather than parsing the text. Paginated calls expose
`total`, `returned`, and `next_offset`; compact records always keep their IDs.

## Run

Node 22 is pinned in `.node-version` and `versions.env`.

```bash
npm ci
npm run build
MONARCH_EMAIL='you@example.com' \
MONARCH_PASSWORD='...' \
MONARCH_MFA_SECRET='BASE32_TOTP_SECRET' \
node dist/server.js
```

`MONARCH_TOKEN` can replace the three credential variables. Optional settings:

- `MONARCH_SESSION_DIR` — token-cache directory; defaults to `~/.monarch-mcp`
- `MONARCH_TIMEOUT_SECONDS` — upstream request timeout; defaults to `30`
- `MONARCH_MCP_EVENT_LOG` — optional JSONL path for privacy-safe tool outcome and latency events
- `MONARCH_CHANGE_DIR` — durable change journal; defaults to `MONARCH_SESSION_DIR/changes`

The image is built with `make image`; its stdio entrypoint is the server.

## Tool surface

### Accounts and institutions

- `get_accounts` — IDs, Monarch-reported balances, types, institutions, and sync metadata
- `get_account_holdings` — investment positions for an account
- `get_account_history` — historical balance snapshots for an account
- `get_recent_account_balances` — balance arrays across accounts from a date
- `get_net_worth_history` — aggregate balance history
- `get_account_snapshots_by_type` — monthly or yearly balances by account type
- `get_account_type_options` — valid types/subtypes for manual accounts
- `get_institutions` — linked credentials and connection state
- `get_subscription` — plan and entitlement metadata
- `get_refresh_status` — whether a requested institution sync is still running
- `refresh_accounts` — start a sync and, by default, wait and re-read the accounts
- `create_manual_account`, `update_account`, `delete_account`
- `upload_account_balance_history` — import CSV balances for a manual account

Account reads and refresh results include per-account `available_balance_supported`
and `available_balance`. The latter comes from Monarch's read-only
`displayBalancePreview(useAvailableBalance: true, invertSyncedBalance: false)`,
in the existing accounts query. Unsupported accounts and missing values return
null; current balance is never substituted. `use_available_balance` reports the
account's display preference, which this read does not change or depend on.

The shared `balance_context` identifies the source as `monarch`,
`pending_transactions_included` as `unknown`, and `bank_freshness` as `unverified`.
Version 0.6.0 removes the incorrect global `available_balance_provided: false`;
capability and values belong to each account. `get_transactions` exposes `pending` on each item;
pending debits can explain a discrepancy, but subtracting them automatically
could double-count amounts already reflected by a provider.

In 0.5.0, compact account fields are renamed: `balance` → `current_balance`,
`connection_status` → `institution_status`, and `last_updated_at` →
`monarch_last_updated_at`. These retain the original upstream values. Institution
status is institution-wide, not an account connection verdict. `detail=full`
retains upstream fields with the same `balance_context` alongside the accounts.

`refresh_accounts` and `get_refresh_status` identify `completion_scope` as
`monarch_sync`. Even `complete: true` does not verify newer bank data: Monarch
may finish a sync with unchanged balances. Neither Monarch's displayed update
time nor the MCP's `meta.retrieved_at` proves bank freshness. See Monarch's
[refresh explanation](https://help.monarch.com/hc/en-us/articles/360054839131-Refreshing-Your-Accounts)
and [pending-transaction guidance](https://help.monarch.com/hc/en-us/articles/360048393352-Connection-issues).

Refreshes honor Monarch's `canBeForceRefreshed` flag; ineligible accounts are not
requested again. `requested_account_ids` distinguishes new requests from
accounts whose existing sync is being checked. Status polling uses the read
client independently of the mutation, so reauthentication cannot resubmit a
refresh or reset the remaining polling budget.

### Transactions

- `get_transactions` — complete filters, including review state, and explicit offset pagination
- `get_transactions_summary` — aggregate totals, averages, counts, and date bounds
- `get_transaction`, `get_transaction_splits`
- `get_transaction_categories`, `get_transaction_category_groups`
- `get_transaction_tags`
- `create_transaction`, `update_transaction`, `bulk_update_transactions`, `delete_transaction`
- `set_transaction_splits`, `set_transaction_tags`
- `create_transaction_tag`, `delete_transaction_tag`
- `create_transaction_category`, `delete_transaction_category`
- `get_transaction_rules`, `preview_transaction_rule`
- `create_transaction_rule`, `update_transaction_rule`, `delete_transaction_rule`

### Planning

- `get_budgets`, `get_goals`, `set_budget_amount`
- `get_cashflow`, `get_cashflow_summary`
- `get_recurring_transactions`, `update_recurring_merchant`

### Change history

- `get_change_history` — inspect change IDs, affected counts, status, and saved inverse operations
- `undo_change` — idempotently reverse a journaled change
- `redo_change` — reapply an undone stable-ID update

Transaction edits, bulk review work, recurring corrections, and rule changes are
journaled before the upstream write as atomic mode-0600 records. The local
`chg_<UUID>` identifies that private record; it is not a Monarch object ID.
Completed results include their `change_id` and affected count, and ambiguous
responses retain an `uncertain` journal entry. Before undo, state guards detect
newer edits and require `force=true` rather than silently overwriting them.
Undo verifies the restored state and records a second guard before redo becomes
available. Redo then refuses to overwrite any edit made after undo. Stable-ID
account, transaction, split, tag, budget, recurring, and non-retroactive rule
updates are redoable; identity-changing creates and deletes are not presented as
the same Monarch object after recreation.
Retroactive rule application snapshots matching transactions before the rule
runs, including their tags and splits. If Monarch's reported applied count
differs from the previewed set, the change is explicitly marked as requiring
manual review rather than claiming an unsafe automatic undo.

`get_transactions` provides the client's paged and all-transactions behavior
without an unbounded MCP result. `delete_transaction_category` provides the
client's single and batch-helper behavior one exact result at a time.
Authentication, session, and timeout methods remain server internals.

Mutation tools are not hidden behind a server-specific flag. Their MCP
annotations accurately distinguish reads, updates, creates, and deletes so the
host can apply its normal interaction policy without making the toolset less
useful.

Account refreshes, bulk transaction updates, retroactive rules, and undo emit
standard MCP progress notifications. Standard cancellation stops work before a
write when possible, bounds already-started transaction writes to the existing
concurrency limit, and records the exact attempted set. If Monarch has already
accepted a retroactive rule, the server finishes its recovery journal before
stopping so a disconnected caller cannot leave an untracked change.

## Development

```bash
make check
```

The check runs formatting, lint, strict type checking, exact handler-contract
tests for every tool, and a production build. A separate self-cleaning live
suite validates the same surface against Monarch because CI has no account
credentials.

## Status

Monarch does not currently provide a supported public API for this integration.
The MCP is therefore an intentionally replaceable adapter around Monarch's web
API. It is not affiliated with or endorsed by Monarch Money.

## License

MIT

TDQS

B3.4/5.0

Scored across 33 tools

Disambiguation4/5

Tool purposes are largely distinct with clear separation between account, transaction, and budget resources. However, there is a cluster of balance-history tools (get_recent_account_balances, get_account_history, get_account_snapshots_by_type, get_net_worth_history) that could trip up an agent seeking general balance data despite detailed descriptions.

Naming Consistency5/5

All tools use snake_case and consistently follow a verb_noun pattern (get/refresh/create/update/delete/set). Even qualifiers are placed predictably, making the set easy to navigate.

Tool Count2/5

33 tools is well above the 25 threshold for an MCP surface, making selection and discovery cumbersome. The server wraps a full finance API, but for agent use it would benefit from consolidation (e.g., generic balance retrieval) and removing metadata endpoints like get_subscription or get_account_type_options as separate tools.

Completeness4/5

The lifecycle coverage is strong for accounts and transactions, including splits and tags, plus budgets and cashflow reporting. Minor gaps exist: tags can only be created and assigned, with no delete/update operation, and category updates are missing, creating some dead ends.

Maintenance

ActivityMaintained
ResponsivenessNo issues