Lightweight CLI and Python SDK for accessing Monarch Money financial data.
$ monarch accounts
ACCOUNTS (5)
+--------------------------------+--------------------+----------------+
| Account | Institution | Balance |
+--------------------------------+--------------------+----------------+
| [Checking] | | $8,434.56 |
| Primary Checking | First National | $5,234.56 |
| Joint Checking | First National | $3,200.00 |
| [Credit Card] | | -$3,148.06 |
| Rewards Card | Premium Credit | -$2,345.67 |
| Store Card | Target | -$802.39 |
| [Savings] | | $12,500.00 |
| Emergency Fund | First National | $12,500.00 |
+--------------------------------+--------------------+----------------+
$ monarch transactions list --start 2025-01-01 --limit 5
TRANSACTIONS (5)
+------------+--------------------------+----------------------+--------------+
| Date | Merchant | Category | Amount |
+------------+--------------------------+----------------------+--------------+
| 2025-01-15 | Amazon | Shopping | -$127.43 |
| 2025-01-14 | Whole Foods | Groceries | -$89.23 |
| 2025-01-13 | Shell | Gas | -$45.00 |
| 2025-01-12 | Netflix | Entertainment | -$15.99 |
| 2025-01-10 | Employer Payroll | Salary | $3,500.00 |
+------------+--------------------------+----------------------+--------------+
Total: $3,222.35
pipx install git+https://github.com/krisrowe/monarch-access.gitThis installs three commands:
monarch- The CLI tool for direct command-line usemonarch-mcp- The MCP server for AI assistant integration (see MCP Server)monarch-admin- User management for MCP server (local and cloud)
- Python 3.10+
- A Monarch Money account
Monarch doesn't have a public API, so you need to grab your session token from the browser:
- Go to https://app.monarch.com/ and log in
- Open DevTools (F12) → Console tab
- Paste and run:
JSON.parse(JSON.parse(localStorage.getItem("persist:root")).user).token
- Copy the token string
- Register it:
monarch-admin connect local monarch-admin users add local --token $MONARCH_SESSION_TOKEN
The token is stored in the local user store and used by both the CLI and MCP server. Tokens typically last several months — rotate yours with:
monarch-admin users update-profile local token $MONARCH_SESSION_TOKENAll commands default to text format with ASCII tables. Use --format json or --format csv for machine-readable output.
# Transactions since a date
monarch transactions list --start 2025-12-01
# Date range (both inclusive)
monarch transactions list --start 2025-01-01 --end 2025-12-31
# Filter by account (supports wildcards)
monarch transactions list --start 2025-01-01 --account "Chase*"
# Filter by category (comma-separated)
monarch transactions list --start 2025-01-01 --category "Shopping,Groceries"
# Filter by merchant (supports wildcards)
monarch transactions list --start 2025-01-01 --merchant "*amazon*"
# Output as JSON or CSV
monarch transactions list --start 2025-01-01 --format json
monarch transactions list --start 2025-01-01 --format csv
# Limit results (default 1000)
monarch transactions list --start 2025-01-01 --limit 50JSON output example:
$ monarch transactions list --start 2025-01-01 --limit 1 --format json
{
"transactions": [
{
"id": "311447260750935400",
"amount": -127.43,
"pending": false,
"date": "2025-01-15",
"hideFromReports": false,
"needsReview": false,
"plaidName": "AMAZON #7491",
"notes": "",
"isRecurring": false,
"account": {
"id": "acc_004",
"displayName": "Rewards Card"
},
"merchant": {
"id": "merch_amazon",
"name": "Amazon"
},
"category": {
"id": "cat_005",
"name": "Shopping"
},
"tags": []
}
],
"count": 1,
"total": 147
}
monarch transactions get TRANSACTION_ID
monarch transactions get TRANSACTION_ID --format json# Update notes
monarch transactions update TRANSACTION_ID --notes "New note"
# Update category (by name)
monarch transactions update TRANSACTION_ID --category "Groceries"
# Update merchant
monarch transactions update TRANSACTION_ID --merchant "Amazon"
# Clear notes (use empty string)
monarch transactions update TRANSACTION_ID --notes ""monarch accounts
monarch accounts --format json
monarch accounts --format csv# Close an account — keeps its balance history in net worth, zeros it forward
monarch account close <account_id>
monarch account close <account_id> --date 2025-06-30
# Reopen a closed account
monarch account update <account_id> --reopen
# Rename
monarch account update <account_id> --name "New Name"
# Exclude from / include in net worth (retroactive — removes balance from history)
monarch account update <account_id> --exclude-net-worth
monarch account update <account_id> --include-net-worth
# Hide / unhide from the accounts list
monarch account update <account_id> --hide
monarch account update <account_id> --unhideClose vs. exclude. Closing (account close) keeps the account's
historical balance snapshots in net worth and reads $0 from the close date
forward — the right way to retire a manual placeholder once a real account
links, so net worth neither double-counts nor drops retroactively. Excluding
(--exclude-net-worth) removes the balance from net worth across all of
history. Closing is reversible with --reopen.
# Download an account's daily balance history as CSV (Date,Balance)
monarch balances download <account_id>
monarch balances download <account_id> -o history.csv
# Replace an account's balance history from a CSV file
monarch balances upload <account_id> history.csv
# Back up the current history before replacing, and skip the prompt
monarch balances upload <account_id> history.csv -o backup.csv --yesUpload replaces the entire balance history (there is no append mode) and sets the account's current balance to the final row. Balance history is independent of transactions — uploading creates no transactions and doesn't affect income/expense reports. Useful for correcting stale balances on accounts that stopped syncing, importing history for manual accounts, or migrating a balance curve between accounts.
Because the replace is destructive, uploads are guarded by a read-before-write interlock:
- The CLI reads the current history first, shows what will be replaced,
optionally writes it to a backup CSV (
-o), and prompts for confirmation (--yesto skip). - The SDK and MCP tool require an
expected_token— a digest number of the current history returned bydownload_balance_history. The upload re-reads the live history, recomputes the token, and refuses (changing nothing) unless it matches. This guarantees the prior history was read (so the change is reversible) and that nothing changed underneath. On success, the replaced history is returned underprevious_snapshotsfor rollback.
monarch net-worth
monarch net-worth --format json
monarch net-worth --format csvShows assets and liabilities grouped by category with totals.
# All holdings across investment accounts
monarch holdings
# Holdings in a single account
monarch holdings --account <account_id>
# Holdings as of a past date (historical snapshot)
monarch holdings --date 2025-01-15
monarch holdings --format json
monarch holdings --format csvReturns security-level positions — ticker, share quantity, closing price,
current value, cost basis, and per-acquisition tax lots. cost_basis may be
null for synced positions where the data provider did not supply basis.
import asyncio
from monarch.client import MonarchClient
from monarch import accounts, categories
from monarch.transactions import list as txn_list, get as txn_get, update as txn_update
async def main():
client = MonarchClient()
# Get all accounts
accts = await accounts.get_accounts(client)
# Get transactions
data = await txn_list.get_transactions(
client,
limit=100,
start_date="2025-01-01",
end_date="2025-12-31",
)
txns = data["results"]
# Get a single transaction
txn = await txn_get.get_transaction(client, "some-transaction-id")
# Update a transaction
updated = await txn_update.update_transaction(
client,
transaction_id="some-transaction-id",
notes="Updated via SDK",
)
# Get categories
cats = await categories.get_categories(client)
asyncio.run(main())The monarch-mcp command exposes Monarch data via the Model Context Protocol, enabling AI assistants like Claude and Gemini to access your financial data.
Register a local MCP user with your Monarch token:
monarch-admin connect local
monarch-admin users add local --token $MONARCH_SESSION_TOKENclaude mcp add --scope user monarch -- monarch-mcp stdio --user localgemini mcp add monarch -- monarch-mcp stdio --user local| Tool | Description |
|---|---|
list_accounts |
Get all accounts with balances |
update_account |
Rename, exclude from net worth, or hide an account |
close_account |
Close an account (keeps balance history in net worth) |
get_holdings |
Get investment holdings (shares, cost basis, tax lots) |
download_balance_history |
Download an account's daily balance snapshots |
upload_balance_history |
Replace an account's balance history |
list_categories |
Get all transaction categories |
list_transactions |
Query transactions with filters |
get_transaction |
Get a single transaction |
update_transaction |
Update category, notes, etc. |
mark_transactions_reviewed |
Bulk mark as reviewed |
split_transaction |
Split across categories |
create_transactions |
Create one or more manual transactions (partial success reported) |
delete_transactions |
Delete one or more transactions (partial success reported) |
list_recurring |
List recurring obligations |
update_recurring |
Update recurring stream settings |
For detailed documentation, see MCP-SERVER.md.
Deploying monarch-access as an HTTP MCP server means your Monarch session token stays on the server and is never exposed to clients — each client authenticates with a JWT issued by monarch-admin.
The MCP server reads these environment variables at startup. Any host must provide them:
| Variable | Required | Purpose |
|---|---|---|
SIGNING_KEY |
yes | JWT signing secret. Generate once with python3 -c 'import secrets; print(secrets.token_urlsafe(32))' and supply via your host's secret mechanism. |
APP_USERS_PATH |
yes for durable deploys | Directory for per-user profiles. Must be a persistent path — mount a durable volume. |
JWT_AUD |
no | Expected JWT audience claim. Leave unset unless you're running multiple apps with a shared signing key. |
TOKEN_DURATION_SECONDS |
no | Lifetime of newly issued JWTs. Defaults to ~10 years. |
The server serves the MCP endpoint at /, a liveness probe at /health, and admin REST endpoints under /admin/*.
Once the service is running, point the admin CLI at it with the same SIGNING_KEY the server is using:
monarch-admin connect https://your-service-url --signing-key "$SIGNING_KEY"connect persists the URL and signing key to ~/.config/monarch/setup.json, so subsequent monarch-admin commands don't need the flags repeated.
monarch-admin healthmonarch-admin users add user@example.com --token "$MONARCH_SESSION_TOKEN"See Authentication for how to obtain $MONARCH_SESSION_TOKEN. To rotate a user's token in place (keeps their JWT valid and their profile intact):
monarch-admin users update-profile user@example.com token "$NEW_TOKEN"To revoke a user entirely (invalidates their JWT), use monarch-admin users revoke user@example.com.
monarch-admin tokens create user@example.comCopy the returned token and register the remote server with your MCP client. Both Claude Code and Gemini CLI expand ${VAR} in MCP config, so keep the token in an env var rather than pasting it into config files:
export MONARCH_JWT="<token from tokens create>"
# Claude Code
claude mcp add --scope user --transport http monarch https://your-service-url/ \
--header "Authorization: Bearer \${MONARCH_JWT}"
# Gemini CLI
gemini mcp add --transport http monarch https://your-service-url/ \
--header "Authorization: Bearer \${MONARCH_JWT}"See CONTRIBUTING.md for development setup, testing, and architecture.
If you are a coding agent operating this repo with plugin support, the author-mcp-app and mcp-app-admin skills provide step-by-step workflows for authoring and operating apps on the mcp-app framework. They are optional — this README and CONTRIBUTING.md are self-sufficient.
MIT