test-monarch-mcp — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited test-monarch-mcp (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
You are the orchestrator of a comprehensive test suite for the Monarch Money MCP server.
You do not call Monarch tools yourself. Instead you dispatch a subagent per phase (via the Task tool); each subagent runs that phase's tests, asserts on shape/key fields only, and returns a compact PASS/FAIL summary. You aggregate the summaries, track created resources, drive cleanup, and print the final report. This keeps large tool payloads (transaction lists, rule lists, full objects) inside the subagents and out of your context.
Run tests across 14 phases, track results, and clean up after yourself.
Scope of this skill: it validates that the agent uses the MCP tools correctly — right tool, right params, correct interpretation of responses (happy paths, agent-judgment cases, and one representative graceful-error case per tool family). It does not stress-test tool robustness. Adversarial/edge inputs and live-API error paths are covered by the deterministic e2e suite intests/integration/(run withMONARCH_LIVE_TESTS=1 pytest tests/integration -m integration).
This test suite supports two modes, auto-detected at startup:
skipped. No data is created, modified, or deleted.
--enable-write): Tests all 44 tools (110 tests). Creates, modifies, anddeletes data on your live Monarch account. Self-cleaning.
subagent**.
mcp-test-state.json; merge the created-resource IDs each subagent reports.Use the Task tool (subagent type: general-purpose). The prompt you pass must contain:
read-only / read-write) and, in read-only mode, the exact subset of tests to runfor this phase (from the Phase Map).
{placeholder} tokens (see Placeholder Reference).references/transaction-crud.md).values for any {placeholder} tokens.
mcp__monarch-mcp__<tool> (e.g. mcp__monarch-mcp__get_transactions).If a tool is not directly callable, first load it with ToolSearch using the query select:mcp__monarch-mcp__<tool>, then call it.
id", "date starts with2025-01", "error string contains 'both'". Read only what you need from each response and discard the rest. Never copy a full tool response into your output.
delete-test did not already remove — transactions via delete_transaction, tags via delete_transaction_tag, categories via delete_transaction_category, accounts via delete_account. Do not revert the shared {test_transaction_id} — the orchestrator owns its original values and reverts it during cleanup.
{
"phase": 6,
"phase_name": "Transaction CRUD",
"summary": { "passed": 14, "failed": 1, "skipped": 0 },
"results": [
{ "test": "6.1", "status": "PASS" },
{ "test": "6.7", "status": "FAIL",
"detail": "expected amount -99.99; got error '<one-line msg>'; params: amount=-99.99" }
],
"created_resources": { "transactions": [], "tags": [], "categories": [], "accounts": [] },
"self_cleanup": { "deleted": ["txn:abc"], "failed": [] }
}results: one row per test. PASS rows carry only test + status. FAIL/SKIP rows add a one-linedetail (expected vs actual + the params used, or the skip reason). No payloads.
created_resources: resources the subagent created but did not delete itself — the orchestratorbackstop for cleanup.
self_cleanup: IDs the subagent deleted itself (transparency); failed lists deletions that errored.calls in one message) — they are independent and perform no writes. Aggregate as they return. No cleanup phase.
(Phases 6 and 7 both mutate the shared test transaction, so serializing avoids races and keeps cleanup deterministic). After each subagent returns, merge its created_resources into the state file and record its results before dispatching the next. Then run the cleanup subagent.
| Phase | Name | Reference file | Read-only subset | Mutates data |
|---|---|---|---|---|
| 0 | Discovery | (inline subagent — see Phase 0) | runs | no |
| 1 | Auth Tools | references/auth-tools.md | all (3) | no |
| 2 | Accounts & Holdings | references/accounts-and-holdings.md | all (5) | no |
| 3 | Transaction Reads | references/transactions-read.md | all (9) | no |
| 4 | Budgets, Cashflow & Budget Amounts | references/budgets-and-cashflow.md | 4.1–4.8 | yes (4.9–4.10) |
| 5 | Tag CRUD | references/tag-crud.md | 5.1 | yes |
| 6 | Transaction CRUD | references/transaction-crud.md | — (skip) | yes |
| 7 | Transaction Tagging | references/transaction-tagging.md | — (skip) | yes |
| 8 | Categories | references/categories.md | 8.1–8.3 | yes |
| 9 | Details & Splits | references/transaction-details-and-splits.md | 9.1–9.4 | yes (9.5–9.7) |
| 10 | Read-Only Tools | references/read-only-tools.md | all (12) | no |
| 11 | Account Management | references/account-management.md | 11.1, 11.6-alt, 11.7–11.10 | yes |
| 12 | Analytics | references/analytics-tools.md | all (5) | no |
| 13 | Transaction Rules | references/transaction-rules.md | 13.6 | yes |
| 14 | Recurring Merchant Management | references/recurring-merchant.md | 14.1 | yes |
Phase 11 read-only note: run 11.6-alt instead of 11.6 — it callsget_account_historywith{checking_account_id}because no account is created in read-only mode.
The state file is mcp-test-state.json in the project root.
mcp-test-state.json.original_values, then delete the state file.
delete state file and start fresh.
{
"status": "in_progress | completed | cleanup_needed",
"mode": "read-only | read-write",
"started_at": "<ISO timestamp>",
"last_updated": "<ISO timestamp>",
"last_completed_phase": 0,
"discovery": {
"checking_account_id": "",
"investment_account_id": "",
"test_transaction_id": "",
"valid_category_id": "",
"original_values": {
"merchant": "",
"notes": "",
"amount": 0,
"date": "",
"category_id": "",
"hide_from_reports": false,
"needs_review": false
}
},
"created_resources": {
"transactions": [],
"tags": [],
"categories": [],
"accounts": []
},
"results": {},
"summary": {
"total": 110,
"passed": 0,
"failed": 0,
"skipped": 0
}
}In read-only mode, summary.total is 58 and original_values is omitted (no mutations happen). In write-enabled mode, summary.total is 110.
original_values are now logged; status: "in_progress", last_completed_phase: 0).
created_resources into the state file,append its results, bump last_completed_phase, and update summary counts.
status: "completed" after the cleanup subagent succeeds, then delete the state file.The state file's created_resources is the orchestrator backstop. Subagents self-clean their own creations, but the merged list plus the cleanup subagent's name-prefix sweep ensures nothing is left behind even if a subagent crashes without reporting.proceed. Write the state file with status: "cleanup_needed" and stop.
FAIL per test and keeps going. If awhole subagent errors or returns a malformed object, record the phase as failed and continue — then ensure cleanup still runs.
subagent.
prefixed with MCP-Test- for easy identification and for the cleanup name-prefix sweep.
Before starting any test work (including Phase 0), detect the server mode and get user confirmation.
Check which MCP tools are available. If write tools like create_transaction, create_transaction_tag, delete_transaction, update_recurring_merchant, etc. are available, the server is in read-write mode. If they are absent, the server is in read-only mode.
Based on the detected mode, display the appropriate message and STOP and wait for explicit user approval:
#### If read-only mode detected:
Server is running in read-only mode (27 tools).
I'll run 58 read-only tests and skip 52 write tests. No data will be created, modified, or deleted on your Monarch account.
To test all 110 tests, disable monarch-money-read-only and enable monarch-money in .mcp.json, then restart.
Proceed with read-only tests?
#### If read-write mode detected:
WARNING: Server is running in read-write mode (all 44 tools).
I'll run all 110 tests. This will create, modify, and delete data on your live Monarch Money account:
error, timeout, context limit), cleanup may be incomplete and unwanted changes could remain in your account.
MCP-Test- for easy manual identification.it will detect the saved progress and offer to continue.
To test read-only mode only, disable monarch-money and enable monarch-money-read-only in .mcp.json, then restart.
Do you want to continue?
Do NOT proceed until the user explicitly confirms. If the user declines, stop immediately.
This confirmation applies to fresh runs only. When resuming an in-progress run or performing cleanup-only, skip this confirmation (the user already accepted the risk).
Dispatch a discovery subagent first (so even discovery payloads stay out of your context). Give it the mode and these instructions; it returns only the compact ID set below.
The discovery subagent must:
mcp__monarch-mcp__get_accounts().checking_account_id: the first account whose type contains "checking" (case-insensitive).Fallback: the first account in the list.
investment_account_id: the first account whose type contains "investment" or "brokerage"(case-insensitive). May be null if none found.
mcp__monarch-mcp__get_transactions(limit=5).test_transaction_id: the first transaction's ID.valid_category_id: the first transaction's category ID.mcp__monarch-mcp__get_transaction_details(transaction_id={test_transaction_id})and record original_values: merchant (or merchant_name), notes, amount, date, category_id, hide_from_reports, needs_review. These are used to revert the transaction during cleanup.
{
"checking_account_id": "...",
"investment_account_id": "..." ,
"test_transaction_id": "...",
"valid_category_id": "...",
"original_values": { "...": "... (write mode only)" }
}On return, the orchestrator:
created_resources = { transactions: [], tags: [], categories: [], accounts: [] }.mode and writes the state file with status: "in_progress", last_completed_phase: 0. === Phase 0: Discovery ===
Mode: {read-only | read-write}
Checking account: {id}
Investment account: {id} or "None found"
Test transaction: {id}
Valid category: {id}If any required value (checking_account_id, test_transaction_id, valid_category_id) is missing, HALT.
For each phase, dispatch a subagent per the Execution Model (read-only → concurrent; write → sequential). Pass the mode, the read-only subset (from the Phase Map), the discovery values, the reference file path, and the subagent rules + return contract.
After each subagent returns: merge its created_resources into the state file, append its results, bump last_completed_phase, and update summary.
Skip cleanup entirely — no resources were created and no mutations were made. Set status: "completed" in the state file, then delete mcp-test-state.json.
This phase ALWAYS runs, even if earlier phases failed or were skipped. Dispatch a cleanup subagent, passing it test_transaction_id, original_values, and the merged created_resources lists. The cleanup subagent must perform all steps below and return a compact summary.
#### Step 1: Revert the test transaction
update_transaction(
transaction_id = {test_transaction_id},
merchant_name = {original_values.merchant},
notes = {original_values.notes},
amount = {original_values.amount},
date = {original_values.date},
category_id = {original_values.category_id},
hide_from_reports = {original_values.hide_from_reports},
needs_review = {original_values.needs_review}
)Clearing notes: iforiginal_values.noteswas null/empty, omitnotesfrom the call above and instead passclear_notes=true—update_transactiontreatsnullas "leave unchanged", andclear_notes=trueclears the field reliably (some clients cannot transmit the empty string that the legacynotes=""path requires). (Same for an unlinked goal: passclear_goal=true.)
#### Step 2: Remove tags from the test transaction
set_transaction_tags(transaction_id = {test_transaction_id}, tag_ids = [])#### Step 3: Delete tracked resources
For each ID in the merged created_resources, delete it (continue on failure, log each): delete_transaction, delete_transaction_tag, delete_transaction_category, delete_account.
#### Step 4: Name-prefix straggler sweep (backstop)
Catch anything a subagent created but failed to report or delete. The distinctive MCP-Test- prefix (Monarch lowercases rule criteria, so rules appear as mcp-test) makes this safe:
get_transaction_tags() → delete every tag whose name starts with MCP-Test-.get_transaction_rules() → delete every rule whose criteria value contains mcp-test(case-insensitive). Rule IDs aren't returned by create_transaction_rule, so they are discovered here.
get_transactions(search="MCP-Test") → delete every returned transaction except{test_transaction_id} (a pre-existing transaction — never delete it; Step 1 already reverted its merchant. This guard protects the user's real transaction if Step 1 ever failed).
get_transaction_categories() → delete every category whose name starts with MCP-Test-.get_accounts() → delete every account whose name starts with MCP-Test-.Continue on failure; log each deletion.
#### Step 5: Verify
Re-query get_transaction_tags() and get_transaction_rules(); confirm no MCP-Test- / mcp-test residue. Report any remaining items so the orchestrator can warn the user.
#### Step 6: Return
The cleanup subagent returns a compact summary:
{ "reverted": true, "deleted": { "transactions": 3, "tags": 4, "categories": 2, "accounts": 1, "rules": 5 }, "residual": [] }On return, the orchestrator sets status: "completed" and deletes mcp-test-state.json. If residual is non-empty, warn the user.
After cleanup (or after skipping cleanup in read-only mode), print a final summary by aggregating the subagent returns.
╔══════════════════════════════════════════════════╗
║ MCP Tool Test Results Summary (read-only mode) ║
╠══════════════════════════════════════════════════╣
║ Phase 1 — Auth Tools: 3/3 PASS ║
║ Phase 2 — Accounts: 5/5 PASS ║
║ Phase 3 — Transaction Reads: 9/9 PASS ║
║ Phase 4 — Budgets/Cashflow: 8/8 PASS ║
║ Phase 5 — Tag CRUD: 1/1 PASS ║
║ Phase 6 — Transaction CRUD: SKIPPED (write) ║
║ Phase 7 — Tagging: SKIPPED (write) ║
║ Phase 8 — Categories: 3/3 PASS ║
║ Phase 9 — Details/Splits: 4/4 PASS ║
║ Phase 10 — Read-Only Tools: 12/12 PASS ║
║ Phase 11 — Account Mgmt: 6/6 PASS ║
║ Phase 12 — Analytics: 5/5 PASS ║
║ Phase 13 — Rules: 1/1 PASS ║
║ Phase 14 — Recurring Merch: 1/1 PASS ║
╠══════════════════════════════════════════════════╣
║ TOTAL: 58 passed, 0 failed, 0 skipped ║
║ Write tests skipped: 52 (server in read-only) ║
╚══════════════════════════════════════════════════╝╔══════════════════════════════════════════════════╗
║ MCP Tool Test Results Summary (read-write mode) ║
╠══════════════════════════════════════════════════╣
║ Phase 1 — Auth Tools: 3/3 PASS ║
║ Phase 2 — Accounts: 5/5 PASS ║
║ Phase 3 — Transaction Reads: 9/9 PASS ║
║ Phase 4 — Budgets/Cashflow: 10/10 PASS ║
║ Phase 5 — Tag CRUD: 5/5 PASS ║
║ Phase 6 — Transaction CRUD: 16/16 PASS ║
║ Phase 7 — Tagging: 4/4 PASS ║
║ Phase 8 — Categories: 9/9 PASS ║
║ Phase 9 — Details/Splits: 7/7 PASS ║
║ Phase 10 — Read-Only Tools: 12/12 PASS ║
║ Phase 11 — Account Mgmt: 10/10 PASS ║
║ Phase 12 — Analytics: 5/5 PASS ║
║ Phase 13 — Transaction Rules: 10/10 PASS ║
║ Phase 14 — Recurring Merch: 5/5 PASS ║
╠══════════════════════════════════════════════════╣
║ TOTAL: 110 passed, 0 failed, 0 skipped ║
╚══════════════════════════════════════════════════╝If any tests failed, list each failure with:
(All of this comes straight from the FAIL rows' detail fields — you never need the raw payloads.)
You never run individual tests — you parse subagent returns. For each phase:
=== Phase {n}: {name} === (dispatching subagent).Phase {n}: {passed}/{total} PASS (or list FAIL/SKIP rows).created_resources into the state file.results and update summary counts.FAIL and continue (cleanupstill runs in write mode).
The orchestrator substitutes these into each subagent's prompt (the subagent then substitutes them into the reference-file test bodies):
| Placeholder | Source |
|---|---|
{checking_account_id} | discovery.checking_account_id |
{investment_account_id} | discovery.investment_account_id |
{test_transaction_id} | discovery.test_transaction_id |
{valid_category_id} | discovery.valid_category_id |
{created_tag_id} | ID returned from the phase's most recent create_transaction_tag call (tracked inside the subagent) |
{created_txn_id} | ID returned from the phase's most recent create_transaction call (tracked inside the subagent) |
{recurring_merchant_id} | merchant_id returned by find_merchant_id_by_name in Phase 14.1 (tracked inside the subagent) |
{recurring_merchant_name} | merchant_name returned by find_merchant_id_by_name in Phase 14.1 (tracked inside the subagent) |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.