Monzo Claude Connector
by silkyrich
README.md
# Monzo → Claude Connector

A remote [MCP](https://modelcontextprotocol.io) server, running on a Cloudflare
Worker, that gives Claude **read-only** access to your Monzo account over OAuth.
Once connected in claude.ai, you can ask things like *"what's my Monzo balance?"*,
*"list my recent transactions"*, or *"how much is in my savings pots?"* in any
chat — web, desktop, or phone. Authenticate once; it follows you everywhere.
No laptop, no local server, nothing to keep running.
> **Not affiliated with Monzo.** This is a personal-use tool built on Monzo's
> public [developer API](https://docs.monzo.com). Use at your own risk, and read
> [Security model](#security-model) before deploying.
## What it exposes
| Tool | Description |
|------|-------------|
| `get_accounts` | Open accounts (personal, joint, Flex, rewards) with ids/sort codes |
| `get_balance` | Balance, total incl. pots, and spent-today — one account or all |
| `list_transactions` | Recent transactions (last 90 days), filterable by date |
| `get_transaction` | Full detail on a single transaction |
| `list_pots` | Savings pots and their balances |
**Read-only by design**: no payments, transfers, or pot movements — the tools
simply don't exist, so no amount of prompt injection can move money.
## Architecture
```
Claude (claude.ai) ──OAuth──> This Worker ──OAuth──> Monzo API
│
workers-oauth-provider (KV) + McpAgent (Durable Object)
```
- [`@cloudflare/workers-oauth-provider`](https://github.com/cloudflare/workers-oauth-provider)
is the OAuth server Claude authenticates to — it handles dynamic client
registration, `/authorize`, `/token`, and grant storage in KV.
- [`src/monzo-handler.ts`](src/monzo-handler.ts) bridges that to Monzo's OAuth
(authorize → magic-link email → in-app approval → callback).
- [`src/mcp.ts`](src/mcp.ts) is the `McpAgent` — one Durable Object per
connected user, holding that user's Monzo tokens and refreshing them
automatically via the refresh token.
- [`src/monzo.ts`](src/monzo.ts) is the thin Monzo API client.
## Security model
Worth understanding before you deploy — the design isolates users by
construction:
- **No standing bank credential in the Worker.** The only Worker-level secrets
are the Monzo OAuth `client_id`/`client_secret`, which can exchange
authorization codes but cannot read any account data by themselves.
- **Tokens live per-grant, not per-Worker.** When a user completes the OAuth
dance, their Monzo tokens are stored encrypted inside *their* grant
(`completeAuthorization({ props: ...tokens })`). Every MCP request is resolved
through the caller's bearer token to *their* grant, *their* Durable Object,
*their* Monzo account. There is no request that reaches another user's data.
- **Unauthenticated requests get 401.** The `/mcp` endpoint only answers to
bearer tokens the Worker itself issued.
- **Monzo adds its own gates.** Developer-portal OAuth clients only authorize
the client owner (plus explicitly added collaborators), and every login
requires approval in the Monzo app on the account holder's phone (SCA).
- **Scopes are read-only** and the payment/transfer endpoints are never called.
Residual risks are the usual ones for any connector: protect the deployed
secrets, and treat your claude.ai session like the credential it is.
## Setup
1. **Register a Monzo OAuth client** at <https://developers.monzo.com> —
**Confidentiality: Confidential** (required for refresh tokens).
Redirect URL: `https://<your-worker>.workers.dev/callback`.
2. **Fill secrets** in `.dev.vars` (gitignored) for local dev:
```
MONZO_CLIENT_ID=oauth2client_...
MONZO_CLIENT_SECRET=mnzconf...
COOKIE_ENCRYPTION_KEY=<openssl rand -hex 32>
```
3. **Create the KV namespace** and paste the returned id into
`wrangler.jsonc` (replacing `<YOUR_OAUTH_KV_NAMESPACE_ID>`):
```
npx wrangler kv namespace create OAUTH_KV
```
4. **Deploy and push secrets:**
```
npx wrangler deploy
npx wrangler secret put MONZO_CLIENT_ID
npx wrangler secret put MONZO_CLIENT_SECRET
npx wrangler secret put COOKIE_ENCRYPTION_KEY
```
5. **Add the connector in claude.ai** → Settings → Connectors → Add custom
connector → `https://<your-worker>.workers.dev/mcp` → authorize. You'll be
bounced through Monzo's login (magic-link email + approve in the Monzo app),
then Claude shows the five tools.
Works in Claude Code too:
```
claude mcp add --transport http monzo https://<your-worker>.workers.dev/mcp
```
## Notes & limitations
- Monzo only exposes the **last 90 days** of transactions unless you
re-authenticate immediately after approving access; this connector accepts
that rolling window.
- Monzo developer clients are **single-user** unless Monzo approves your app
for production — fine for the personal-use case this is built for.
- Secrets live in `.dev.vars` (local) and Wrangler secrets (prod) — never in
git. The `.gitignore` enforces this.
## License
[MIT](LICENSE) — do what you like, no warranty. Monzo, Claude, and Cloudflare
names and logos belong to their respective owners.
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues