MCP server for the Monzo banking API with OAuth, local transaction cache, and spending analysis.
This STDIO-only server implements 7 tools with generally clear naming and reasonable descriptions. Most tools follow verb_noun patterns (monzo_list_*, monzo_get_*, monzo_search_*). However, there are significant gaps: parameter descriptions in schemas are minimal/missing, output schemas are not formally documented, error handling lacks recovery guidance, and the server has no pagination support despite tools that return lists. Tool descriptions range from 80 - 250 characters (baseline p10=34, p90=392), which is acceptable but brief. Input schemas are present but descriptions within them are sparse or absent for many parameters. The lack of tool annotations (readOnlyHint, destructiveHint) is a missed opportunity for clarity. Error handling is basic, tools call require_auth and may raise AccountNotFound, but no structured error messages or recovery paths are visible.
Get current balance for a Monzo account. Args: account_type: "personal" or "joint" (default: "personal") Returns balance, spend today, and currency. Also records a balance snapshot.
List all Monzo accounts with their types and IDs. Returns account details including whether each is personal or joint, and whether it is open or closed.
List all pots (savings buckets) for a Monzo account. Args: account_type: "personal" or "joint" (default: "personal") Returns the account the pots belong to, plus pot names and balances. Also records balance snapshots.
List cached Monzo transactions with optional filters. Does not sync; use `monzo_sync_transactions` first to update the cache. Transactions are listed most recent first. Args: since: Optional ISO date ("2026-01-01") to filter from (inclusive) until: Optional ISO date ("2026-01-01") to filter until (inclusive) category: Optional category name to filter by account_type: "personal" or "joint" to filter by account type limit: Maximum number of transactions to return (default: 100)
No output schemas documented. Tools return formatted strings (via format_response), but the LLM has no visibility into what fields or structure to expect. This violates pattern:tool and forces LLMs to guess at response structure.
No pagination support on list/search tools. monzo_list_transactions and monzo_search_transactions accept 'limit' but provide no 'offset', 'cursor', or 'total' fields. Large result sets can blow context windows. No indication that results are capped or how to iterate.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 62 | 2026-07-28+ | v2 |
Search cached Monzo transactions by description or merchant name. Does not sync; use `monzo_sync_transactions` first to update the cache. Uses case-insensitive substring matching. Args: query: Search term (substring match) type: "description" or "merchant" (default: both) since: Optional ISO date ("2026-01-01") to filter from account_type: "personal" or "joint" to filter by account type limit: Maximum number of results (default: 50)
Analyse spending from cached Monzo transactions. Auto-syncs if the cache is stale (last sync before today). Every result carries `account_type`, echoing the filter applied and null when unfiltered, so a zero total says which account it measured. Args: month: Month in YYYY-MM format (default: current month) category: Filter by category, e.g. "groceries", "eating_out", "transport" account_type: "personal" or "joint" (default: all) detail: If true, return individual transactions instead of category summary
Sync transactions, balances, and pots from the Monzo API into the local cache. Fetches up to 11 months of history (within SCA window) or falls back to the last-synced timestamp / 90 days. Handles pagination and auth-hold deduplication automatically. Args: account_type: "personal", "joint", or None to sync all accounts since: Optional ISO date ("2026-01-01") or datetime ("2026-01-01T14:30:00Z") to start the backfill from, overriding last-sync resumption. Reaching beyond ~90 days only works inside the post-auth SCA window; outside it the API's 90-day fallback still applies (the value is passed straight to the Monzo API, not gated client-side).
Parameter descriptions in JSON schema are minimal. For example, monzo_spending has parameters like 'month', 'category', 'account_type', 'detail' with descriptions present but sparse. Descriptions lack examples of valid formats for 'month' (YYYY-MM) and 'since'/'until' (ISO date).
No tool annotations. None of the tools declare readOnlyHint, destructiveHint, or idempotentHint. This is a current pattern (2026-07-28 spec) and helps LLMs understand which tools are safe to retry and which modify state. monzo_sync_transactions is marked WRITE in the manifest but the tool definition has no destructiveHint.
Error handling lacks recovery guidance. require_auth decorator will fail if credentials are missing, but no actionable error message is visible. AccountNotFound exception is raised but the error response is not documented. Per pattern:recovery-guide, errors must tell the LLM what to do next.
Enum constraints are correct (account_type: [personal, joint]) but parameter descriptions reference example values like '"groceries", "eating_out", "transport"' in category parameter. Use enum constraint or validation instead.