monzo-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sam
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,351 @@
1
+ # Monzo MCP Server
2
+
3
+ An MCP (Model Context Protocol) server for the [Monzo banking API](https://docs.monzo.com). Check your balance, list transactions, manage pots, and more — all through natural language.
4
+
5
+ Works with any MCP-compatible client, including Claude Desktop, Claude Code, Cursor, Windsurf, Cline, and others.
6
+
7
+ > **Note:** The Monzo Developer API is for personal use only. You can only connect to your own account or a small number of explicitly allowed users.
8
+
9
+ ## Features
10
+
11
+ 19 tools covering the full Monzo API:
12
+
13
+ | Category | Tools | Description |
14
+ |----------|-------|-------------|
15
+ | **Accounts** | `monzo_whoami`, `monzo_list_accounts`, `monzo_get_balance` | View account info and balances |
16
+ | **Pots** | `monzo_list_pots`, `monzo_deposit_into_pot`, `monzo_withdraw_from_pot` | Manage savings pots |
17
+ | **Transactions** | `monzo_list_transactions`, `monzo_get_transaction`, `monzo_annotate_transaction` | Browse and annotate transactions |
18
+ | **Feed** | `monzo_create_feed_item` | Push custom items to the Monzo app feed |
19
+ | **Attachments** | `monzo_upload_attachment`, `monzo_register_attachment`, `monzo_deregister_attachment` | Attach images to transactions |
20
+ | **Receipts** | `monzo_create_receipt`, `monzo_get_receipt`, `monzo_delete_receipt` | Manage digital receipts |
21
+ | **Webhooks** | `monzo_register_webhook`, `monzo_list_webhooks`, `monzo_delete_webhook` | Set up real-time notifications |
22
+
23
+ ---
24
+
25
+ ## Getting Your Monzo Access Token
26
+
27
+ You need an access token to use this server.
28
+
29
+ ### Step 1: Create a Monzo Developer Account
30
+
31
+ 1. Go to [developers.monzo.com](https://developers.monzo.com)
32
+ 2. Log in with your Monzo email address
33
+ 3. You'll receive a magic link in your email — click it to sign in
34
+
35
+ ### Step 2: Get an Access Token via the API Playground
36
+
37
+ 1. Go to [developers.monzo.com/api/playground](https://developers.monzo.com/api/playground)
38
+ 2. Click **Auth** to start the authentication flow
39
+ 3. You'll receive a **push notification** on your Monzo app — tap it and verify with your PIN, fingerprint, or Face ID (Strong Customer Authentication)
40
+ 4. Once approved, the Playground will show your **Access Token**
41
+ 5. Copy this token — you'll need it in the next section
42
+
43
+ ### Important Notes About Tokens
44
+
45
+ - **Tokens expire** after approximately **6 hours**
46
+ - After authentication, you have **5 minutes** of full transaction history access. After that, only the **last 90 days** are available
47
+ - Generating a new token **invalidates** the previous one
48
+ - If your token expires, repeat Step 2
49
+
50
+ ---
51
+
52
+ ## Installation
53
+
54
+ ### Prerequisites
55
+
56
+ - [Node.js](https://nodejs.org/) 18 or later
57
+
58
+ ### Use via npx (recommended)
59
+
60
+ No installation required — just configure your MCP client to run:
61
+
62
+ ```bash
63
+ npx monzo-mcp
64
+ ```
65
+
66
+ ### Install globally
67
+
68
+ ```bash
69
+ npm install -g monzo-mcp
70
+ ```
71
+
72
+ Then run:
73
+
74
+ ```bash
75
+ monzo-mcp-server
76
+ ```
77
+
78
+ ### Build from source
79
+
80
+ ```bash
81
+ git clone https://github.com/samaxbytez/monzo-mcp-server.git
82
+ cd monzo-mcp-server
83
+ npm install
84
+ npm run build
85
+ ```
86
+
87
+ ---
88
+
89
+ ## Configuration
90
+
91
+ ### Claude Desktop
92
+
93
+ Add the following to your Claude Desktop config file:
94
+
95
+ - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
96
+ - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
97
+
98
+ ```json
99
+ {
100
+ "mcpServers": {
101
+ "monzo": {
102
+ "command": "npx",
103
+ "args": ["monzo-mcp"],
104
+ "env": {
105
+ "MONZO_ACCESS_TOKEN": "your_access_token_here"
106
+ }
107
+ }
108
+ }
109
+ }
110
+ ```
111
+
112
+ Then restart Claude Desktop.
113
+
114
+ ### Claude Code (CLI)
115
+
116
+ Add to your project's `.mcp.json`:
117
+
118
+ ```json
119
+ {
120
+ "mcpServers": {
121
+ "monzo": {
122
+ "command": "npx",
123
+ "args": ["monzo-mcp"],
124
+ "env": {
125
+ "MONZO_ACCESS_TOKEN": "your_access_token_here"
126
+ }
127
+ }
128
+ }
129
+ }
130
+ ```
131
+
132
+ Or add it directly via the CLI:
133
+
134
+ ```bash
135
+ claude mcp add monzo -- npx monzo-mcp
136
+ ```
137
+
138
+ ### Other MCP Clients (Cursor, Windsurf, Cline, etc.)
139
+
140
+ Refer to your client's documentation for how to add an MCP server. The server command is:
141
+
142
+ ```
143
+ npx monzo-mcp
144
+ ```
145
+
146
+ With the environment variable `MONZO_ACCESS_TOKEN` set to your token.
147
+
148
+ ### Environment Variables
149
+
150
+ | Variable | Required | Description |
151
+ |----------|----------|-------------|
152
+ | `MONZO_ACCESS_TOKEN` | Yes | Your Monzo API access token |
153
+
154
+ ---
155
+
156
+ ## Usage Examples
157
+
158
+ Once configured, you can ask your AI assistant things like (using Claude as an example):
159
+
160
+ - **"What's my Monzo balance?"** — calls `monzo_get_balance`
161
+ - **"Show my recent transactions"** — calls `monzo_list_transactions`
162
+ - **"List my pots"** — calls `monzo_list_pots`
163
+ - **"Move £50 into my Holiday pot"** — calls `monzo_deposit_into_pot`
164
+ - **"What did I spend at Tesco last week?"** — calls `monzo_list_transactions` with filters
165
+ - **"Send me a notification in Monzo saying 'Remember to buy milk'"** — calls `monzo_create_feed_item`
166
+
167
+ ---
168
+
169
+ ## Tool Reference
170
+
171
+ ### Accounts
172
+
173
+ #### `monzo_whoami`
174
+ Verify the authenticated user. Returns user ID, authentication type, and client ID.
175
+
176
+ #### `monzo_list_accounts`
177
+ List all accounts. Optionally filter by `account_type` (`uk_retail`, `uk_retail_joint`).
178
+
179
+ #### `monzo_get_balance`
180
+ Get the balance for a specific account. Returns balance, total balance, currency, and spend today — all in **pence** (minor units).
181
+
182
+ **Parameters:**
183
+ - `account_id` (required) — The account ID
184
+
185
+ ---
186
+
187
+ ### Pots
188
+
189
+ #### `monzo_list_pots`
190
+ List all pots for an account.
191
+
192
+ **Parameters:**
193
+ - `current_account_id` (required) — The account ID
194
+
195
+ #### `monzo_deposit_into_pot`
196
+ Move money from your account into a pot.
197
+
198
+ **Parameters:**
199
+ - `pot_id` (required) — The pot to deposit into
200
+ - `source_account_id` (required) — The account to move money from
201
+ - `amount` (required) — Amount in **pence** (e.g. `1000` = £10.00)
202
+ - `dedupe_id` (required) — A unique string to prevent duplicate deposits
203
+
204
+ #### `monzo_withdraw_from_pot`
205
+ Move money from a pot back into your account. Pots with "added security" (lock) cannot be withdrawn via API.
206
+
207
+ **Parameters:**
208
+ - `pot_id` (required) — The pot to withdraw from
209
+ - `destination_account_id` (required) — The account to move money to
210
+ - `amount` (required) — Amount in **pence**
211
+ - `dedupe_id` (required) — A unique string to prevent duplicate withdrawals
212
+
213
+ ---
214
+
215
+ ### Transactions
216
+
217
+ #### `monzo_list_transactions`
218
+ List transactions for an account. Limited to the last 90 days (after 5 minutes post-authentication).
219
+
220
+ **Parameters:**
221
+ - `account_id` (required) — The account ID
222
+ - `since` (optional) — RFC 3339 timestamp or object ID (e.g. `2024-01-01T00:00:00Z`)
223
+ - `before` (optional) — RFC 3339 timestamp
224
+ - `limit` (optional) — Results per page (default 30, max 100)
225
+
226
+ #### `monzo_get_transaction`
227
+ Get details of a single transaction.
228
+
229
+ **Parameters:**
230
+ - `transaction_id` (required) — The transaction ID
231
+ - `expand_merchant` (optional) — Set to `true` to include full merchant details
232
+
233
+ #### `monzo_annotate_transaction`
234
+ Add custom key-value metadata to a transaction.
235
+
236
+ **Parameters:**
237
+ - `transaction_id` (required) — The transaction ID
238
+ - `key` (required) — The metadata key
239
+ - `value` (required) — The metadata value (empty string to delete)
240
+
241
+ ---
242
+
243
+ ### Feed
244
+
245
+ #### `monzo_create_feed_item`
246
+ Create a custom item in the Monzo app feed.
247
+
248
+ **Parameters:**
249
+ - `account_id` (required) — The account ID
250
+ - `title` (required) — Feed item title
251
+ - `body` (required) — Feed item body text
252
+ - `image_url` (optional) — URL of an image to display
253
+ - `url` (optional) — URL to open when tapped
254
+
255
+ ---
256
+
257
+ ### Attachments
258
+
259
+ #### `monzo_upload_attachment`
260
+ Get a pre-signed upload URL for an image.
261
+
262
+ **Parameters:**
263
+ - `file_name` (required) — e.g. `receipt.png`
264
+ - `file_type` (required) — MIME type, e.g. `image/png`
265
+ - `content_length` (required) — File size in bytes
266
+
267
+ #### `monzo_register_attachment`
268
+ Attach an uploaded image to a transaction.
269
+
270
+ **Parameters:**
271
+ - `external_id` (required) — The transaction ID
272
+ - `file_url` (required) — The URL from `monzo_upload_attachment`
273
+ - `file_type` (required) — MIME type
274
+
275
+ #### `monzo_deregister_attachment`
276
+ Remove an attachment from a transaction.
277
+
278
+ **Parameters:**
279
+ - `id` (required) — The attachment ID
280
+
281
+ ---
282
+
283
+ ### Receipts
284
+
285
+ #### `monzo_create_receipt`
286
+ Create or update a digital receipt on a transaction.
287
+
288
+ **Parameters:**
289
+ - `transaction_id` (required) — The transaction ID
290
+ - `items` (required) — JSON array of receipt items (each with `description`, `amount` in pence, `currency`, `quantity`)
291
+ - `tax` (optional) — Total tax in pence
292
+
293
+ #### `monzo_get_receipt`
294
+ Get a receipt by its external ID.
295
+
296
+ **Parameters:**
297
+ - `external_id` (required) — Typically the transaction ID
298
+
299
+ #### `monzo_delete_receipt`
300
+ Delete a receipt.
301
+
302
+ **Parameters:**
303
+ - `external_id` (required) — Typically the transaction ID
304
+
305
+ ---
306
+
307
+ ### Webhooks
308
+
309
+ #### `monzo_register_webhook`
310
+ Register a URL to receive real-time transaction notifications.
311
+
312
+ **Parameters:**
313
+ - `account_id` (required) — The account ID
314
+ - `url` (required) — The webhook URL
315
+
316
+ #### `monzo_list_webhooks`
317
+ List all webhooks for an account.
318
+
319
+ **Parameters:**
320
+ - `account_id` (required) — The account ID
321
+
322
+ #### `monzo_delete_webhook`
323
+ Delete a webhook.
324
+
325
+ **Parameters:**
326
+ - `webhook_id` (required) — The webhook ID
327
+
328
+ ---
329
+
330
+ ## Troubleshooting
331
+
332
+ ### "Missing required environment variable: MONZO_ACCESS_TOKEN"
333
+ You haven't set the `MONZO_ACCESS_TOKEN` in your MCP server config. See [Getting Your Monzo Access Token](#getting-your-monzo-access-token).
334
+
335
+ ### "Monzo API error (401): unauthorized"
336
+ Your access token has expired (tokens last ~6 hours). Get a new one from the [Monzo Playground](https://developers.monzo.com/api/playground).
337
+
338
+ ### "Monzo API error (403): forbidden"
339
+ You may need to complete Strong Customer Authentication. Open the Monzo app and check for a pending approval notification.
340
+
341
+ ### "Transaction history is empty or limited"
342
+ After 5 minutes post-authentication, the API only returns the last 90 days of transactions. This is a Monzo security restriction.
343
+
344
+ ### Pots withdrawal fails
345
+ Pots with "added security" (locked pots) cannot be withdrawn from via the API. Unlock the pot in the Monzo app first.
346
+
347
+ ---
348
+
349
+ ## License
350
+
351
+ MIT
@@ -0,0 +1,16 @@
1
+ export declare class MonzoApiError extends Error {
2
+ readonly status: number;
3
+ readonly errorCode: string;
4
+ constructor(status: number, errorCode: string, message: string);
5
+ }
6
+ export declare class MonzoClient {
7
+ private accessToken;
8
+ constructor(accessToken: string);
9
+ get<T = unknown>(path: string, params?: Record<string, string>): Promise<T>;
10
+ postForm<T = unknown>(path: string, body?: Record<string, string>): Promise<T>;
11
+ putForm<T = unknown>(path: string, body?: Record<string, string>): Promise<T>;
12
+ patchForm<T = unknown>(path: string, body?: Record<string, string>): Promise<T>;
13
+ putJson<T = unknown>(path: string, body: unknown): Promise<T>;
14
+ deleteReq<T = unknown>(path: string, params?: Record<string, string>): Promise<T>;
15
+ private request;
16
+ }
@@ -0,0 +1,84 @@
1
+ const MONZO_API_URL = "https://api.monzo.com";
2
+ export class MonzoApiError extends Error {
3
+ status;
4
+ errorCode;
5
+ constructor(status, errorCode, message) {
6
+ super(`Monzo API error (${status}): ${errorCode} - ${message}`);
7
+ this.name = "MonzoApiError";
8
+ this.status = status;
9
+ this.errorCode = errorCode;
10
+ }
11
+ }
12
+ function parseApiError(status, body) {
13
+ try {
14
+ const parsed = JSON.parse(body);
15
+ const code = parsed?.error ?? "unknown";
16
+ const message = parsed?.message ?? parsed?.error_description ?? `HTTP ${status}`;
17
+ return new MonzoApiError(status, code, message);
18
+ }
19
+ catch {
20
+ return new MonzoApiError(status, "unknown", `HTTP ${status} error`);
21
+ }
22
+ }
23
+ function encodeFormBody(data) {
24
+ return new URLSearchParams(data).toString();
25
+ }
26
+ export class MonzoClient {
27
+ accessToken;
28
+ constructor(accessToken) {
29
+ this.accessToken = accessToken;
30
+ }
31
+ async get(path, params) {
32
+ return this.request("GET", path, undefined, undefined, params);
33
+ }
34
+ async postForm(path, body) {
35
+ return this.request("POST", path, body, "form");
36
+ }
37
+ async putForm(path, body) {
38
+ return this.request("PUT", path, body, "form");
39
+ }
40
+ async patchForm(path, body) {
41
+ return this.request("PATCH", path, body, "form");
42
+ }
43
+ async putJson(path, body) {
44
+ return this.request("PUT", path, body, "json");
45
+ }
46
+ async deleteReq(path, params) {
47
+ return this.request("DELETE", path, undefined, undefined, params);
48
+ }
49
+ async request(method, path, body, bodyType, params) {
50
+ const url = new URL(path, MONZO_API_URL);
51
+ if (params) {
52
+ for (const [k, v] of Object.entries(params)) {
53
+ url.searchParams.set(k, v);
54
+ }
55
+ }
56
+ const headers = {
57
+ Authorization: `Bearer ${this.accessToken}`,
58
+ };
59
+ let encodedBody;
60
+ if (body && bodyType === "json") {
61
+ headers["Content-Type"] = "application/json";
62
+ encodedBody = JSON.stringify(body);
63
+ }
64
+ else if (body && bodyType === "form") {
65
+ headers["Content-Type"] = "application/x-www-form-urlencoded";
66
+ encodedBody = encodeFormBody(body);
67
+ }
68
+ const response = await fetch(url.toString(), {
69
+ method,
70
+ headers,
71
+ body: encodedBody,
72
+ });
73
+ if (!response.ok) {
74
+ const errorBody = await response.text();
75
+ throw parseApiError(response.status, errorBody);
76
+ }
77
+ const text = await response.text();
78
+ if (!text) {
79
+ return {};
80
+ }
81
+ return JSON.parse(text);
82
+ }
83
+ }
84
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/build/index.js ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { MonzoClient } from "./client.js";
5
+ import { registerAccountTools } from "./tools/accounts.js";
6
+ import { registerPotTools } from "./tools/pots.js";
7
+ import { registerTransactionTools } from "./tools/transactions.js";
8
+ import { registerFeedTools } from "./tools/feed.js";
9
+ import { registerAttachmentTools } from "./tools/attachments.js";
10
+ import { registerReceiptTools } from "./tools/receipts.js";
11
+ import { registerWebhookTools } from "./tools/webhooks.js";
12
+ const MONZO_ACCESS_TOKEN = process.env.MONZO_ACCESS_TOKEN;
13
+ if (!MONZO_ACCESS_TOKEN) {
14
+ console.error("Missing required environment variable: MONZO_ACCESS_TOKEN");
15
+ process.exit(1);
16
+ }
17
+ const monzoClient = new MonzoClient(MONZO_ACCESS_TOKEN);
18
+ const server = new McpServer({
19
+ name: "monzo-api",
20
+ version: "1.0.0",
21
+ });
22
+ registerAccountTools(server, monzoClient);
23
+ registerPotTools(server, monzoClient);
24
+ registerTransactionTools(server, monzoClient);
25
+ registerFeedTools(server, monzoClient);
26
+ registerAttachmentTools(server, monzoClient);
27
+ registerReceiptTools(server, monzoClient);
28
+ registerWebhookTools(server, monzoClient);
29
+ async function main() {
30
+ const transport = new StdioServerTransport();
31
+ await server.connect(transport);
32
+ console.error("Monzo MCP Server running on stdio");
33
+ }
34
+ main().catch((error) => {
35
+ console.error("Fatal error:", error);
36
+ process.exit(1);
37
+ });
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE3D,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;AAE1D,IAAI,CAAC,kBAAkB,EAAE,CAAC;IACxB,OAAO,CAAC,KAAK,CACX,2DAA2D,CAC5D,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,kBAAkB,CAAC,CAAC;AAExD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,WAAW;IACjB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,oBAAoB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAC1C,gBAAgB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AACtC,wBAAwB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAC9C,iBAAiB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AACvC,uBAAuB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAC7C,oBAAoB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAC1C,oBAAoB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAE1C,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;AACrD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { MonzoClient } from "../client.js";
3
+ export declare function registerAccountTools(server: McpServer, client: MonzoClient): void;
@@ -0,0 +1,56 @@
1
+ import { z } from "zod";
2
+ import { jsonResponse, errorResponse, logToolCall, buildParams } from "../utils.js";
3
+ export function registerAccountTools(server, client) {
4
+ server.registerTool("monzo_whoami", {
5
+ title: "Who Am I",
6
+ description: "Verify the authenticated Monzo user. Returns user ID, authentication type, and client ID.",
7
+ }, async () => {
8
+ logToolCall("monzo_whoami");
9
+ try {
10
+ const result = await client.get("/ping/whoami");
11
+ return jsonResponse(result);
12
+ }
13
+ catch (error) {
14
+ return errorResponse(error);
15
+ }
16
+ });
17
+ server.registerTool("monzo_list_accounts", {
18
+ title: "List Accounts",
19
+ description: "List the authenticated user's Monzo accounts. Returns account IDs, types, and descriptions.",
20
+ inputSchema: {
21
+ account_type: z
22
+ .string()
23
+ .optional()
24
+ .describe("Filter by account type (e.g. 'uk_retail', 'uk_retail_joint')"),
25
+ },
26
+ }, async ({ account_type }) => {
27
+ logToolCall("monzo_list_accounts", { account_type });
28
+ try {
29
+ const params = buildParams({ account_type });
30
+ const result = await client.get("/accounts", params);
31
+ return jsonResponse(result);
32
+ }
33
+ catch (error) {
34
+ return errorResponse(error);
35
+ }
36
+ });
37
+ server.registerTool("monzo_get_balance", {
38
+ title: "Get Balance",
39
+ description: "Fetch the balance of a Monzo account. Returns balance, total balance, currency, and spend today (all in minor units / pence).",
40
+ inputSchema: {
41
+ account_id: z
42
+ .string()
43
+ .describe("The account ID to get the balance for"),
44
+ },
45
+ }, async ({ account_id }) => {
46
+ logToolCall("monzo_get_balance", { account_id });
47
+ try {
48
+ const result = await client.get("/balance", { account_id });
49
+ return jsonResponse(result);
50
+ }
51
+ catch (error) {
52
+ return errorResponse(error);
53
+ }
54
+ });
55
+ }
56
+ //# sourceMappingURL=accounts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/tools/accounts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEpF,MAAM,UAAU,oBAAoB,CAClC,MAAiB,EACjB,MAAmB;IAEnB,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,KAAK,EAAE,UAAU;QACjB,WAAW,EACT,2FAA2F;KAC9F,EACD,KAAK,IAAI,EAAE;QACT,WAAW,CAAC,cAAc,CAAC,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YAChD,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,qBAAqB,EACrB;QACE,KAAK,EAAE,eAAe;QACtB,WAAW,EACT,6FAA6F;QAC/F,WAAW,EAAE;YACX,YAAY,EAAE,CAAC;iBACZ,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,8DAA8D,CAAC;SAC5E;KACF,EACD,KAAK,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE;QACzB,WAAW,CAAC,qBAAqB,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC;YAC7C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACrD,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,mBAAmB,EACnB;QACE,KAAK,EAAE,aAAa;QACpB,WAAW,EACT,+HAA+H;QACjI,WAAW,EAAE;YACX,UAAU,EAAE,CAAC;iBACV,MAAM,EAAE;iBACR,QAAQ,CAAC,uCAAuC,CAAC;SACrD;KACF,EACD,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;QACvB,WAAW,CAAC,mBAAmB,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;YAC5D,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { MonzoClient } from "../client.js";
3
+ export declare function registerAttachmentTools(server: McpServer, client: MonzoClient): void;
@@ -0,0 +1,81 @@
1
+ import { z } from "zod";
2
+ import { jsonResponse, errorResponse, logToolCall } from "../utils.js";
3
+ export function registerAttachmentTools(server, client) {
4
+ server.registerTool("monzo_upload_attachment", {
5
+ title: "Upload Attachment",
6
+ description: "Get a pre-signed upload URL for attaching an image to a Monzo transaction. Returns the upload URL and file URL to use with monzo_register_attachment.",
7
+ inputSchema: {
8
+ file_name: z
9
+ .string()
10
+ .describe("The name of the file (e.g. 'receipt.png')"),
11
+ file_type: z
12
+ .string()
13
+ .describe("The MIME type of the file (e.g. 'image/png', 'image/jpeg')"),
14
+ content_length: z
15
+ .number()
16
+ .int()
17
+ .positive()
18
+ .describe("The file size in bytes"),
19
+ },
20
+ }, async ({ file_name, file_type, content_length }) => {
21
+ logToolCall("monzo_upload_attachment", { file_name, file_type });
22
+ try {
23
+ const result = await client.postForm("/attachment/upload", {
24
+ file_name,
25
+ file_type,
26
+ content_length: String(content_length),
27
+ });
28
+ return jsonResponse(result);
29
+ }
30
+ catch (error) {
31
+ return errorResponse(error);
32
+ }
33
+ });
34
+ server.registerTool("monzo_register_attachment", {
35
+ title: "Register Attachment",
36
+ description: "Register an uploaded image as an attachment on a Monzo transaction. The image must already be uploaded to the URL from monzo_upload_attachment.",
37
+ inputSchema: {
38
+ external_id: z
39
+ .string()
40
+ .describe("The transaction ID to attach the image to"),
41
+ file_url: z
42
+ .string()
43
+ .describe("The file_url returned from monzo_upload_attachment"),
44
+ file_type: z
45
+ .string()
46
+ .describe("The MIME type of the file (e.g. 'image/png')"),
47
+ },
48
+ }, async ({ external_id, file_url, file_type }) => {
49
+ logToolCall("monzo_register_attachment", { external_id });
50
+ try {
51
+ const result = await client.postForm("/attachment/register", {
52
+ external_id,
53
+ file_url,
54
+ file_type,
55
+ });
56
+ return jsonResponse(result);
57
+ }
58
+ catch (error) {
59
+ return errorResponse(error);
60
+ }
61
+ });
62
+ server.registerTool("monzo_deregister_attachment", {
63
+ title: "Deregister Attachment",
64
+ description: "Remove an attachment from a Monzo transaction.",
65
+ inputSchema: {
66
+ id: z
67
+ .string()
68
+ .describe("The attachment ID to remove"),
69
+ },
70
+ }, async ({ id }) => {
71
+ logToolCall("monzo_deregister_attachment", { id });
72
+ try {
73
+ const result = await client.postForm("/attachment/deregister", { id });
74
+ return jsonResponse(result);
75
+ }
76
+ catch (error) {
77
+ return errorResponse(error);
78
+ }
79
+ });
80
+ }
81
+ //# sourceMappingURL=attachments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../src/tools/attachments.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { MonzoClient } from "../client.js";
3
+ export declare function registerFeedTools(server: McpServer, client: MonzoClient): void;
@@ -0,0 +1,47 @@
1
+ import { z } from "zod";
2
+ import { jsonResponse, errorResponse, logToolCall } from "../utils.js";
3
+ export function registerFeedTools(server, client) {
4
+ server.registerTool("monzo_create_feed_item", {
5
+ title: "Create Feed Item",
6
+ description: "Create a feed item in the Monzo app for a given account. The item appears in the user's transaction feed with a title, body, and optional image.",
7
+ inputSchema: {
8
+ account_id: z
9
+ .string()
10
+ .describe("The account ID to create the feed item for"),
11
+ title: z
12
+ .string()
13
+ .describe("The title of the feed item"),
14
+ body: z
15
+ .string()
16
+ .describe("The body text of the feed item"),
17
+ image_url: z
18
+ .string()
19
+ .optional()
20
+ .describe("URL of an image to display with the feed item"),
21
+ url: z
22
+ .string()
23
+ .optional()
24
+ .describe("URL to open when the feed item is tapped"),
25
+ },
26
+ }, async ({ account_id, title, body, image_url, url }) => {
27
+ logToolCall("monzo_create_feed_item", { account_id, title });
28
+ try {
29
+ const formBody = {
30
+ account_id,
31
+ type: "basic",
32
+ "params[title]": title,
33
+ "params[body]": body,
34
+ };
35
+ if (image_url)
36
+ formBody["params[image_url]"] = image_url;
37
+ if (url)
38
+ formBody["url"] = url;
39
+ const result = await client.postForm("/feed", formBody);
40
+ return jsonResponse(result);
41
+ }
42
+ catch (error) {
43
+ return errorResponse(error);
44
+ }
45
+ });
46
+ }
47
+ //# sourceMappingURL=feed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feed.js","sourceRoot":"","sources":["../../src/tools/feed.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvE,MAAM,UAAU,iBAAiB,CAC/B,MAAiB,EACjB,MAAmB;IAEnB,MAAM,CAAC,YAAY,CACjB,wBAAwB,EACxB;QACE,KAAK,EAAE,kBAAkB;QACzB,WAAW,EACT,kJAAkJ;QACpJ,WAAW,EAAE;YACX,UAAU,EAAE,CAAC;iBACV,MAAM,EAAE;iBACR,QAAQ,CAAC,4CAA4C,CAAC;YACzD,KAAK,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,QAAQ,CAAC,4BAA4B,CAAC;YACzC,IAAI,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,QAAQ,CAAC,gCAAgC,CAAC;YAC7C,SAAS,EAAE,CAAC;iBACT,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,+CAA+C,CAAC;YAC5D,GAAG,EAAE,CAAC;iBACH,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,0CAA0C,CAAC;SACxD;KACF,EACD,KAAK,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE;QACpD,WAAW,CAAC,wBAAwB,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7D,IAAI,CAAC;YACH,MAAM,QAAQ,GAA2B;gBACvC,UAAU;gBACV,IAAI,EAAE,OAAO;gBACb,eAAe,EAAE,KAAK;gBACtB,cAAc,EAAE,IAAI;aACrB,CAAC;YACF,IAAI,SAAS;gBAAE,QAAQ,CAAC,mBAAmB,CAAC,GAAG,SAAS,CAAC;YACzD,IAAI,GAAG;gBAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC;YAE/B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YACxD,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { MonzoClient } from "../client.js";
3
+ export declare function registerPotTools(server: McpServer, client: MonzoClient): void;
@@ -0,0 +1,89 @@
1
+ import { z } from "zod";
2
+ import { jsonResponse, errorResponse, logToolCall } from "../utils.js";
3
+ export function registerPotTools(server, client) {
4
+ server.registerTool("monzo_list_pots", {
5
+ title: "List Pots",
6
+ description: "List all pots for a Monzo account. Returns pot IDs, names, balances, and styles.",
7
+ inputSchema: {
8
+ current_account_id: z
9
+ .string()
10
+ .describe("The account ID to list pots for"),
11
+ },
12
+ }, async ({ current_account_id }) => {
13
+ logToolCall("monzo_list_pots", { current_account_id });
14
+ try {
15
+ const result = await client.get("/pots", { current_account_id });
16
+ return jsonResponse(result);
17
+ }
18
+ catch (error) {
19
+ return errorResponse(error);
20
+ }
21
+ });
22
+ server.registerTool("monzo_deposit_into_pot", {
23
+ title: "Deposit Into Pot",
24
+ description: "Move money from a Monzo account into a pot. Amount is in pence (e.g. 1000 = £10.00). Requires a unique dedupe_id to prevent duplicate deposits.",
25
+ inputSchema: {
26
+ pot_id: z
27
+ .string()
28
+ .describe("The pot ID to deposit into"),
29
+ source_account_id: z
30
+ .string()
31
+ .describe("The account ID to move money from"),
32
+ amount: z
33
+ .number()
34
+ .int()
35
+ .positive()
36
+ .describe("Amount in pence to deposit (e.g. 1000 = £10.00)"),
37
+ dedupe_id: z
38
+ .string()
39
+ .describe("Unique string to prevent duplicate deposits"),
40
+ },
41
+ }, async ({ pot_id, source_account_id, amount, dedupe_id }) => {
42
+ logToolCall("monzo_deposit_into_pot", { pot_id, amount });
43
+ try {
44
+ const result = await client.putForm(`/pots/${pot_id}/deposit`, {
45
+ source_account_id,
46
+ amount: String(amount),
47
+ dedupe_id,
48
+ });
49
+ return jsonResponse(result);
50
+ }
51
+ catch (error) {
52
+ return errorResponse(error);
53
+ }
54
+ });
55
+ server.registerTool("monzo_withdraw_from_pot", {
56
+ title: "Withdraw From Pot",
57
+ description: "Move money from a pot back into a Monzo account. Amount is in pence (e.g. 1000 = £10.00). Requires a unique dedupe_id. Pots with added security cannot be withdrawn via API.",
58
+ inputSchema: {
59
+ pot_id: z
60
+ .string()
61
+ .describe("The pot ID to withdraw from"),
62
+ destination_account_id: z
63
+ .string()
64
+ .describe("The account ID to move money to"),
65
+ amount: z
66
+ .number()
67
+ .int()
68
+ .positive()
69
+ .describe("Amount in pence to withdraw (e.g. 1000 = £10.00)"),
70
+ dedupe_id: z
71
+ .string()
72
+ .describe("Unique string to prevent duplicate withdrawals"),
73
+ },
74
+ }, async ({ pot_id, destination_account_id, amount, dedupe_id }) => {
75
+ logToolCall("monzo_withdraw_from_pot", { pot_id, amount });
76
+ try {
77
+ const result = await client.putForm(`/pots/${pot_id}/withdraw`, {
78
+ destination_account_id,
79
+ amount: String(amount),
80
+ dedupe_id,
81
+ });
82
+ return jsonResponse(result);
83
+ }
84
+ catch (error) {
85
+ return errorResponse(error);
86
+ }
87
+ });
88
+ }
89
+ //# sourceMappingURL=pots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pots.js","sourceRoot":"","sources":["../../src/tools/pots.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { MonzoClient } from "../client.js";
3
+ export declare function registerReceiptTools(server: McpServer, client: MonzoClient): void;
@@ -0,0 +1,75 @@
1
+ import { z } from "zod";
2
+ import { jsonResponse, errorResponse, logToolCall } from "../utils.js";
3
+ export function registerReceiptTools(server, client) {
4
+ server.registerTool("monzo_create_receipt", {
5
+ title: "Create/Update Receipt",
6
+ description: "Create or update a receipt on a Monzo transaction. Provide the transaction ID and receipt items as a JSON structure.",
7
+ inputSchema: {
8
+ transaction_id: z
9
+ .string()
10
+ .describe("The transaction ID to attach the receipt to"),
11
+ items: z
12
+ .string()
13
+ .describe("JSON array of receipt items, each with: description (string), amount (integer in pence), currency (string, e.g. 'GBP'), quantity (number). Example: [{\"description\":\"Coffee\",\"amount\":350,\"currency\":\"GBP\",\"quantity\":1}]"),
14
+ tax: z
15
+ .number()
16
+ .int()
17
+ .optional()
18
+ .describe("Total tax amount in pence"),
19
+ },
20
+ }, async ({ transaction_id, items, tax }) => {
21
+ logToolCall("monzo_create_receipt", { transaction_id });
22
+ try {
23
+ const parsedItems = JSON.parse(items);
24
+ const receiptBody = {
25
+ transaction_id,
26
+ external_id: transaction_id,
27
+ items: parsedItems,
28
+ };
29
+ if (tax !== undefined)
30
+ receiptBody.tax = tax;
31
+ const result = await client.putJson("/transaction-receipts", receiptBody);
32
+ return jsonResponse(result);
33
+ }
34
+ catch (error) {
35
+ return errorResponse(error);
36
+ }
37
+ });
38
+ server.registerTool("monzo_get_receipt", {
39
+ title: "Get Receipt",
40
+ description: "Retrieve a receipt attached to a Monzo transaction.",
41
+ inputSchema: {
42
+ external_id: z
43
+ .string()
44
+ .describe("The external ID (typically the transaction ID) of the receipt"),
45
+ },
46
+ }, async ({ external_id }) => {
47
+ logToolCall("monzo_get_receipt", { external_id });
48
+ try {
49
+ const result = await client.get("/transaction-receipts", { external_id });
50
+ return jsonResponse(result);
51
+ }
52
+ catch (error) {
53
+ return errorResponse(error);
54
+ }
55
+ });
56
+ server.registerTool("monzo_delete_receipt", {
57
+ title: "Delete Receipt",
58
+ description: "Delete a receipt from a Monzo transaction.",
59
+ inputSchema: {
60
+ external_id: z
61
+ .string()
62
+ .describe("The external ID (typically the transaction ID) of the receipt to delete"),
63
+ },
64
+ }, async ({ external_id }) => {
65
+ logToolCall("monzo_delete_receipt", { external_id });
66
+ try {
67
+ const result = await client.deleteReq("/transaction-receipts", { external_id });
68
+ return jsonResponse(result);
69
+ }
70
+ catch (error) {
71
+ return errorResponse(error);
72
+ }
73
+ });
74
+ }
75
+ //# sourceMappingURL=receipts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"receipts.js","sourceRoot":"","sources":["../../src/tools/receipts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { MonzoClient } from "../client.js";
3
+ export declare function registerTransactionTools(server: McpServer, client: MonzoClient): void;
@@ -0,0 +1,91 @@
1
+ import { z } from "zod";
2
+ import { jsonResponse, errorResponse, logToolCall, buildParams } from "../utils.js";
3
+ export function registerTransactionTools(server, client) {
4
+ server.registerTool("monzo_list_transactions", {
5
+ title: "List Transactions",
6
+ description: "List transactions for a Monzo account. Returns transaction amounts, merchants, categories, and metadata. Note: transaction history is limited to 90 days for API access.",
7
+ inputSchema: {
8
+ account_id: z
9
+ .string()
10
+ .describe("The account ID to list transactions for"),
11
+ since: z
12
+ .string()
13
+ .optional()
14
+ .describe("RFC 3339 timestamp or object ID to filter transactions after (e.g. '2024-01-01T00:00:00Z')"),
15
+ before: z
16
+ .string()
17
+ .optional()
18
+ .describe("RFC 3339 timestamp to filter transactions before"),
19
+ limit: z
20
+ .number()
21
+ .int()
22
+ .positive()
23
+ .max(100)
24
+ .optional()
25
+ .describe("Number of results per page (default 30, max 100)"),
26
+ },
27
+ }, async ({ account_id, since, before, limit }) => {
28
+ logToolCall("monzo_list_transactions", { account_id, since, before, limit });
29
+ try {
30
+ const params = buildParams({ account_id, since, before, limit });
31
+ const result = await client.get("/transactions", params);
32
+ return jsonResponse(result);
33
+ }
34
+ catch (error) {
35
+ return errorResponse(error);
36
+ }
37
+ });
38
+ server.registerTool("monzo_get_transaction", {
39
+ title: "Get Transaction",
40
+ description: "Retrieve details of a single Monzo transaction by its ID. Optionally expand merchant details.",
41
+ inputSchema: {
42
+ transaction_id: z
43
+ .string()
44
+ .describe("The transaction ID to look up"),
45
+ expand_merchant: z
46
+ .boolean()
47
+ .optional()
48
+ .describe("Set to true to include full merchant details"),
49
+ },
50
+ }, async ({ transaction_id, expand_merchant }) => {
51
+ logToolCall("monzo_get_transaction", { transaction_id });
52
+ try {
53
+ const params = {};
54
+ if (expand_merchant) {
55
+ params["expand[]"] = "merchant";
56
+ }
57
+ const result = await client.get(`/transactions/${transaction_id}`, params);
58
+ return jsonResponse(result);
59
+ }
60
+ catch (error) {
61
+ return errorResponse(error);
62
+ }
63
+ });
64
+ server.registerTool("monzo_annotate_transaction", {
65
+ title: "Annotate Transaction",
66
+ description: "Add custom metadata/annotations to a Monzo transaction. You can store key-value pairs on the transaction.",
67
+ inputSchema: {
68
+ transaction_id: z
69
+ .string()
70
+ .describe("The transaction ID to annotate"),
71
+ key: z
72
+ .string()
73
+ .describe("The metadata key (will be stored as metadata[key])"),
74
+ value: z
75
+ .string()
76
+ .describe("The metadata value. Set to empty string to delete the key."),
77
+ },
78
+ }, async ({ transaction_id, key, value }) => {
79
+ logToolCall("monzo_annotate_transaction", { transaction_id, key });
80
+ try {
81
+ const result = await client.patchForm(`/transactions/${transaction_id}`, {
82
+ [`metadata[${key}]`]: value,
83
+ });
84
+ return jsonResponse(result);
85
+ }
86
+ catch (error) {
87
+ return errorResponse(error);
88
+ }
89
+ });
90
+ }
91
+ //# sourceMappingURL=transactions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transactions.js","sourceRoot":"","sources":["../../src/tools/transactions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { MonzoClient } from "../client.js";
3
+ export declare function registerWebhookTools(server: McpServer, client: MonzoClient): void;
@@ -0,0 +1,62 @@
1
+ import { z } from "zod";
2
+ import { jsonResponse, errorResponse, logToolCall } from "../utils.js";
3
+ export function registerWebhookTools(server, client) {
4
+ server.registerTool("monzo_register_webhook", {
5
+ title: "Register Webhook",
6
+ description: "Register a webhook URL to receive real-time notifications for a Monzo account. Each time a transaction is created, Monzo will POST the transaction data to the URL.",
7
+ inputSchema: {
8
+ account_id: z
9
+ .string()
10
+ .describe("The account ID to register the webhook for"),
11
+ url: z
12
+ .string()
13
+ .describe("The URL that Monzo will POST transaction events to"),
14
+ },
15
+ }, async ({ account_id, url }) => {
16
+ logToolCall("monzo_register_webhook", { account_id, url });
17
+ try {
18
+ const result = await client.postForm("/webhooks", { account_id, url });
19
+ return jsonResponse(result);
20
+ }
21
+ catch (error) {
22
+ return errorResponse(error);
23
+ }
24
+ });
25
+ server.registerTool("monzo_list_webhooks", {
26
+ title: "List Webhooks",
27
+ description: "List all registered webhooks for a Monzo account.",
28
+ inputSchema: {
29
+ account_id: z
30
+ .string()
31
+ .describe("The account ID to list webhooks for"),
32
+ },
33
+ }, async ({ account_id }) => {
34
+ logToolCall("monzo_list_webhooks", { account_id });
35
+ try {
36
+ const result = await client.get("/webhooks", { account_id });
37
+ return jsonResponse(result);
38
+ }
39
+ catch (error) {
40
+ return errorResponse(error);
41
+ }
42
+ });
43
+ server.registerTool("monzo_delete_webhook", {
44
+ title: "Delete Webhook",
45
+ description: "Delete a registered webhook by its ID.",
46
+ inputSchema: {
47
+ webhook_id: z
48
+ .string()
49
+ .describe("The webhook ID to delete"),
50
+ },
51
+ }, async ({ webhook_id }) => {
52
+ logToolCall("monzo_delete_webhook", { webhook_id });
53
+ try {
54
+ const result = await client.deleteReq(`/webhooks/${webhook_id}`);
55
+ return jsonResponse(result);
56
+ }
57
+ catch (error) {
58
+ return errorResponse(error);
59
+ }
60
+ });
61
+ }
62
+ //# sourceMappingURL=webhooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/tools/webhooks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvE,MAAM,UAAU,oBAAoB,CAClC,MAAiB,EACjB,MAAmB;IAEnB,MAAM,CAAC,YAAY,CACjB,wBAAwB,EACxB;QACE,KAAK,EAAE,kBAAkB;QACzB,WAAW,EACT,qKAAqK;QACvK,WAAW,EAAE;YACX,UAAU,EAAE,CAAC;iBACV,MAAM,EAAE;iBACR,QAAQ,CAAC,4CAA4C,CAAC;YACzD,GAAG,EAAE,CAAC;iBACH,MAAM,EAAE;iBACR,QAAQ,CAAC,oDAAoD,CAAC;SAClE;KACF,EACD,KAAK,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,EAAE;QAC5B,WAAW,CAAC,wBAAwB,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;QAC3D,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;YACvE,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,qBAAqB,EACrB;QACE,KAAK,EAAE,eAAe;QACtB,WAAW,EACT,mDAAmD;QACrD,WAAW,EAAE;YACX,UAAU,EAAE,CAAC;iBACV,MAAM,EAAE;iBACR,QAAQ,CAAC,qCAAqC,CAAC;SACnD;KACF,EACD,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;QACvB,WAAW,CAAC,qBAAqB,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACnD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;YAC7D,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,KAAK,EAAE,gBAAgB;QACvB,WAAW,EACT,wCAAwC;QAC1C,WAAW,EAAE;YACX,UAAU,EAAE,CAAC;iBACV,MAAM,EAAE;iBACR,QAAQ,CAAC,0BAA0B,CAAC;SACxC;KACF,EACD,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;QACvB,WAAW,CAAC,sBAAsB,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,aAAa,UAAU,EAAE,CAAC,CAAC;YACjE,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ export declare const TOKEN_BUFFER_MS = 60000;
2
+ export declare function jsonResponse(data: unknown): {
3
+ content: {
4
+ type: "text";
5
+ text: string;
6
+ }[];
7
+ };
8
+ export declare function errorResponse(error: unknown): {
9
+ content: {
10
+ type: "text";
11
+ text: string;
12
+ }[];
13
+ isError: boolean;
14
+ };
15
+ export declare function buildParams(obj: Record<string, unknown>): Record<string, string>;
16
+ export declare function logToolCall(tool: string, params?: Record<string, unknown>): void;
package/build/utils.js ADDED
@@ -0,0 +1,33 @@
1
+ export const TOKEN_BUFFER_MS = 60_000;
2
+ export function jsonResponse(data) {
3
+ return {
4
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
5
+ };
6
+ }
7
+ export function errorResponse(error) {
8
+ const msg = error instanceof Error ? error.message : String(error);
9
+ return {
10
+ content: [{ type: "text", text: `Error: ${msg}` }],
11
+ isError: true,
12
+ };
13
+ }
14
+ export function buildParams(obj) {
15
+ const params = {};
16
+ for (const [k, v] of Object.entries(obj)) {
17
+ if (v !== undefined && v !== null)
18
+ params[k] = String(v);
19
+ }
20
+ return params;
21
+ }
22
+ export function logToolCall(tool, params) {
23
+ const sanitized = { ts: new Date().toISOString(), tool };
24
+ if (params) {
25
+ for (const [k, v] of Object.entries(params)) {
26
+ sanitized[k] = typeof v === "string" && v.length > 100
27
+ ? v.slice(0, 20) + "..."
28
+ : v;
29
+ }
30
+ }
31
+ console.error(JSON.stringify(sanitized));
32
+ }
33
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC;AAEtC,MAAM,UAAU,YAAY,CAAC,IAAa;IACxC,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;KAC1E,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACnE,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,UAAU,GAAG,EAAE,EAAE,CAAC;QAC3D,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,GAA4B;IAE5B,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI;YAAE,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,MAAgC;IACxE,MAAM,SAAS,GAA4B,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC;IAClF,IAAI,MAAM,EAAE,CAAC;QACX,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5C,SAAS,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,GAAG;gBACpD,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK;gBACxB,CAAC,CAAC,CAAC,CAAC;QACR,CAAC;IACH,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC;AAC3C,CAAC"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "monzo-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for the Monzo banking API",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "monzo",
9
+ "mcp",
10
+ "model-context-protocol",
11
+ "banking",
12
+ "ai",
13
+ "claude",
14
+ "llm"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/samaxbytez/monzo-mcp-server.git"
19
+ },
20
+ "homepage": "https://github.com/samaxbytez/monzo-mcp-server#readme",
21
+ "bugs": {
22
+ "url": "https://github.com/samaxbytez/monzo-mcp-server/issues"
23
+ },
24
+ "bin": {
25
+ "monzo-mcp-server": "build/index.js"
26
+ },
27
+ "files": [
28
+ "build"
29
+ ],
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
33
+ "scripts": {
34
+ "build": "tsc && chmod 755 build/index.js",
35
+ "dev": "tsc --watch",
36
+ "start": "node build/index.js",
37
+ "type-check": "tsc --noEmit",
38
+ "test": "vitest run",
39
+ "test:watch": "vitest",
40
+ "prepublishOnly": "npm run build"
41
+ },
42
+ "dependencies": {
43
+ "@modelcontextprotocol/sdk": "^1.26.0",
44
+ "zod": "^3.25.0"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^22.16.0",
48
+ "typescript": "^5.8.3",
49
+ "vitest": "^4.0.18"
50
+ }
51
+ }