@hakimelek/monarchmoney 0.1.0 → 0.3.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/README.md +224 -29
- package/dist/cjs/client.js +923 -0
- package/dist/cjs/client.js.map +1 -0
- package/dist/cjs/endpoints.js +11 -0
- package/dist/cjs/endpoints.js.map +1 -0
- package/dist/cjs/errors.js +78 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/index.js +12 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/queries.js +466 -0
- package/dist/cjs/queries.js.map +1 -0
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/client.d.ts +92 -15
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +233 -23
- package/dist/client.js.map +1 -1
- package/dist/endpoints.d.ts +1 -1
- package/dist/endpoints.d.ts.map +1 -1
- package/dist/endpoints.js +1 -1
- package/dist/endpoints.js.map +1 -1
- package/dist/errors.d.ts +22 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +25 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +3 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +420 -0
- package/dist/mcp.js.map +1 -0
- package/package.json +19 -6
package/README.md
CHANGED
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Node.js/TypeScript library for accessing [Monarch Money](https://www.monarchmoney.com) data.
|
|
4
4
|
|
|
5
|
-
Port of the [Python monarchmoney library](https://github.com/hammem/monarchmoney) with full TypeScript types, JSDoc, and a clean modular architecture.
|
|
6
|
-
|
|
7
5
|
> **Disclaimer:** This project is unofficial and not affiliated with Monarch Money.
|
|
8
6
|
|
|
9
7
|
## Installation
|
|
10
8
|
|
|
11
9
|
```bash
|
|
12
|
-
npm install monarchmoney
|
|
10
|
+
npm install @hakimelek/monarchmoney
|
|
13
11
|
```
|
|
14
12
|
|
|
15
13
|
Requires **Node.js 18+** (uses native `fetch` and `AbortSignal.timeout`).
|
|
@@ -17,15 +15,23 @@ Requires **Node.js 18+** (uses native `fetch` and `AbortSignal.timeout`).
|
|
|
17
15
|
## Quick Start
|
|
18
16
|
|
|
19
17
|
```ts
|
|
20
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
MonarchMoney,
|
|
20
|
+
EmailOtpRequiredException,
|
|
21
|
+
RequireMFAException,
|
|
22
|
+
} from "@hakimelek/monarchmoney";
|
|
21
23
|
|
|
22
24
|
const mm = new MonarchMoney();
|
|
23
25
|
|
|
24
|
-
// Login
|
|
25
26
|
try {
|
|
26
27
|
await mm.login("your@email.com", "password");
|
|
27
28
|
} catch (e) {
|
|
28
|
-
if (e instanceof
|
|
29
|
+
if (e instanceof EmailOtpRequiredException) {
|
|
30
|
+
// Monarch sent a verification code to your email
|
|
31
|
+
const code = await promptUser("Enter the code from your email:");
|
|
32
|
+
await mm.submitEmailOtp("your@email.com", "password", code);
|
|
33
|
+
} else if (e instanceof RequireMFAException) {
|
|
34
|
+
// TOTP-based MFA is enabled on the account
|
|
29
35
|
await mm.multiFactorAuthenticate("your@email.com", "password", "123456");
|
|
30
36
|
}
|
|
31
37
|
}
|
|
@@ -37,13 +43,20 @@ console.log(accounts[0].displayName, accounts[0].currentBalance);
|
|
|
37
43
|
|
|
38
44
|
## Authentication
|
|
39
45
|
|
|
40
|
-
|
|
46
|
+
Monarch's API requires email verification (OTP) for new devices/sessions, even when MFA is disabled. The library handles this with distinct exception types so your app can respond appropriately.
|
|
47
|
+
|
|
48
|
+
### Login with email OTP handling
|
|
41
49
|
|
|
42
50
|
```ts
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
try {
|
|
52
|
+
await mm.login(email, password);
|
|
53
|
+
} catch (e) {
|
|
54
|
+
if (e instanceof EmailOtpRequiredException) {
|
|
55
|
+
// A code was sent to the user's email — prompt them for it
|
|
56
|
+
const code = await yourApp.promptForEmailCode();
|
|
57
|
+
await mm.submitEmailOtp(email, password, code);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
47
60
|
```
|
|
48
61
|
|
|
49
62
|
### With MFA secret key (automatic TOTP)
|
|
@@ -56,29 +69,37 @@ await mm.login("email", "password", {
|
|
|
56
69
|
|
|
57
70
|
The MFA secret is the "Two-factor text code" from **Settings > Security > Enable MFA** in Monarch Money.
|
|
58
71
|
|
|
59
|
-
###
|
|
72
|
+
### Session persistence & token reuse
|
|
73
|
+
|
|
74
|
+
After a successful login (including email OTP), you can save the token to avoid re-authenticating on every run:
|
|
60
75
|
|
|
61
76
|
```ts
|
|
62
|
-
|
|
63
|
-
|
|
77
|
+
// Save token after login
|
|
78
|
+
mm.saveSession(); // writes to .mm/mm_session.json (mode 0o600)
|
|
64
79
|
|
|
65
|
-
|
|
80
|
+
// Next time, login() loads the saved session automatically
|
|
81
|
+
await mm.login(email, password); // uses saved token, no network call
|
|
82
|
+
|
|
83
|
+
// Or pass the token directly (skip login entirely)
|
|
84
|
+
const mm = new MonarchMoney({ token: "your-saved-token" });
|
|
85
|
+
```
|
|
66
86
|
|
|
67
87
|
```ts
|
|
68
|
-
|
|
88
|
+
mm.saveSession(); // save to disk
|
|
89
|
+
mm.loadSession(); // load from disk
|
|
90
|
+
mm.deleteSession(); // remove the file
|
|
91
|
+
mm.setToken("..."); // set token programmatically
|
|
69
92
|
```
|
|
70
93
|
|
|
71
|
-
###
|
|
94
|
+
### Interactive CLI
|
|
72
95
|
|
|
73
96
|
```ts
|
|
74
|
-
mm.
|
|
75
|
-
mm.loadSession(); // loads from disk
|
|
76
|
-
mm.deleteSession(); // removes the file
|
|
97
|
+
await mm.interactiveLogin(); // prompts for email, password, email OTP or MFA code
|
|
77
98
|
```
|
|
78
99
|
|
|
79
100
|
## API
|
|
80
101
|
|
|
81
|
-
All methods return **typed responses
|
|
102
|
+
All methods return **typed responses**. Hover over any method in your editor for full JSDoc and type information.
|
|
82
103
|
|
|
83
104
|
### Read Methods
|
|
84
105
|
|
|
@@ -96,6 +117,8 @@ All methods return **typed responses** — no `Record<string, unknown>`. Hover o
|
|
|
96
117
|
| `getSubscriptionDetails()` | `GetSubscriptionDetailsResponse` | Plan status (trial, premium, etc.) |
|
|
97
118
|
| `getTransactionsSummary()` | `GetTransactionsSummaryResponse` | Aggregate summary |
|
|
98
119
|
| `getTransactions(options?)` | `GetTransactionsResponse` | Transactions with full filtering |
|
|
120
|
+
| `getAllTransactions(options?)` | `Transaction[]` | All matching transactions (auto-paginates) |
|
|
121
|
+
| `getTransactionPages(options?)` | `AsyncGenerator<Transaction[]>` | Async generator yielding pages |
|
|
99
122
|
| `getTransactionCategories()` | `GetTransactionCategoriesResponse` | All categories |
|
|
100
123
|
| `getTransactionCategoryGroups()` | `GetTransactionCategoryGroupsResponse` | Category groups |
|
|
101
124
|
| `getTransactionDetails(id)` | typed response | Single transaction detail |
|
|
@@ -131,11 +154,29 @@ All methods return **typed responses** — no `Record<string, unknown>`. Hover o
|
|
|
131
154
|
|
|
132
155
|
```ts
|
|
133
156
|
import {
|
|
134
|
-
MonarchMoneyError,
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
157
|
+
MonarchMoneyError, // base class for all errors
|
|
158
|
+
EmailOtpRequiredException, // email verification code needed — call submitEmailOtp()
|
|
159
|
+
RequireMFAException, // TOTP MFA required — call multiFactorAuthenticate()
|
|
160
|
+
LoginFailedException, // bad credentials or auth error (includes .statusCode)
|
|
161
|
+
RequestFailedException, // API/GraphQL failure (includes .statusCode, .graphQLErrors)
|
|
162
|
+
} from "@hakimelek/monarchmoney";
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
await mm.login(email, password);
|
|
166
|
+
} catch (e) {
|
|
167
|
+
if (e instanceof EmailOtpRequiredException) {
|
|
168
|
+
// e.code === "EMAIL_OTP_REQUIRED"
|
|
169
|
+
// Prompt user for the code sent to their email
|
|
170
|
+
const code = await getCodeFromUser();
|
|
171
|
+
await mm.submitEmailOtp(email, password, code);
|
|
172
|
+
} else if (e instanceof RequireMFAException) {
|
|
173
|
+
// e.code === "MFA_REQUIRED"
|
|
174
|
+
// Prompt for TOTP code or use mfaSecretKey
|
|
175
|
+
} else if (e instanceof LoginFailedException) {
|
|
176
|
+
// e.code === "LOGIN_FAILED", e.statusCode
|
|
177
|
+
console.error("Login failed:", e.message);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
139
180
|
|
|
140
181
|
try {
|
|
141
182
|
await mm.getAccounts();
|
|
@@ -153,34 +194,188 @@ try {
|
|
|
153
194
|
```ts
|
|
154
195
|
const mm = new MonarchMoney({
|
|
155
196
|
sessionFile: ".mm/mm_session.json", // session file path
|
|
156
|
-
timeout: 10,
|
|
157
|
-
token: "pre-existing-token",
|
|
197
|
+
timeout: 10, // API timeout in seconds
|
|
198
|
+
token: "pre-existing-token", // skip login
|
|
199
|
+
retry: {
|
|
200
|
+
maxRetries: 3, // retry on 429/5xx (default: 3, set 0 to disable)
|
|
201
|
+
baseDelayMs: 500, // base delay with exponential backoff + jitter
|
|
202
|
+
},
|
|
203
|
+
rateLimit: {
|
|
204
|
+
requestsPerSecond: 10, // token-bucket throttle (default: 0 = unlimited)
|
|
205
|
+
},
|
|
158
206
|
});
|
|
159
207
|
|
|
160
208
|
mm.setTimeout(30); // change timeout later
|
|
161
209
|
```
|
|
162
210
|
|
|
211
|
+
Retry automatically handles transient failures (429 Too Many Requests, 500, 502, 503, 504) with exponential backoff and jitter. The `Retry-After` header is respected on 429 responses.
|
|
212
|
+
|
|
213
|
+
## Auto-Pagination
|
|
214
|
+
|
|
215
|
+
`getTransactions()` returns a single page. For large datasets, use the auto-pagination helpers:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
// Async generator — yields one page at a time (memory-efficient)
|
|
219
|
+
for await (const page of mm.getTransactionPages({ startDate: "2025-01-01", endDate: "2025-12-31" })) {
|
|
220
|
+
for (const tx of page) {
|
|
221
|
+
console.log(tx.merchant?.name, tx.amount);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Or collect everything into a flat array
|
|
226
|
+
const all = await mm.getAllTransactions({
|
|
227
|
+
startDate: "2025-01-01",
|
|
228
|
+
endDate: "2025-12-31",
|
|
229
|
+
pageSize: 100, // transactions per page (default: 100)
|
|
230
|
+
});
|
|
231
|
+
console.log(`${all.length} total transactions`);
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Both methods accept the same filter options as `getTransactions()` (date range, category, account, tags, etc.).
|
|
235
|
+
|
|
236
|
+
## Refresh Progress
|
|
237
|
+
|
|
238
|
+
Track account refresh progress with the `onProgress` callback:
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
await mm.requestAccountsRefreshAndWait({
|
|
242
|
+
timeout: 300,
|
|
243
|
+
delay: 10,
|
|
244
|
+
onProgress: ({ completed, total, elapsedMs }) => {
|
|
245
|
+
console.log(`${completed}/${total} accounts refreshed (${(elapsedMs / 1000).toFixed(0)}s)`);
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## MCP Server (AI Agent Integration)
|
|
251
|
+
|
|
252
|
+
This package includes a built-in [Model Context Protocol](https://modelcontextprotocol.io) server with **30 tools**, making your Monarch Money data accessible to AI assistants like Claude Desktop, Cursor, and any MCP-compatible client.
|
|
253
|
+
|
|
254
|
+
### Setup
|
|
255
|
+
|
|
256
|
+
1. Get your Monarch Money auth token by logging in with the library (see [Authentication](#authentication)) and saving `mm.token`.
|
|
257
|
+
|
|
258
|
+
2. Add to your MCP client config (e.g. Claude Desktop `claude_desktop_config.json`):
|
|
259
|
+
|
|
260
|
+
```json
|
|
261
|
+
{
|
|
262
|
+
"mcpServers": {
|
|
263
|
+
"monarch-money": {
|
|
264
|
+
"command": "npx",
|
|
265
|
+
"args": ["@hakimelek/monarchmoney"],
|
|
266
|
+
"env": {
|
|
267
|
+
"MONARCH_TOKEN": "your-token-here"
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Or run it directly:
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
MONARCH_TOKEN=your-token npx @hakimelek/monarchmoney
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Available Tools
|
|
281
|
+
|
|
282
|
+
**Read (18 tools):** `get_accounts`, `get_account_holdings`, `get_account_history`, `get_account_type_options`, `get_recent_account_balances`, `get_aggregate_snapshots`, `get_institutions`, `get_budgets`, `get_subscription_details`, `get_transactions`, `get_transactions_summary`, `get_transaction_details`, `get_transaction_categories`, `get_transaction_category_groups`, `get_transaction_tags`, `get_cashflow`, `get_cashflow_summary`, `get_recurring_transactions`
|
|
283
|
+
|
|
284
|
+
**Write (12 tools):** `create_transaction`, `update_transaction`, `delete_transaction`, `create_manual_account`, `update_account`, `delete_account`, `refresh_accounts`, `is_refresh_complete`, `set_budget_amount`, `create_transaction_tag`, `set_transaction_tags`, `create_transaction_category`
|
|
285
|
+
|
|
286
|
+
Every tool has typed parameters with descriptions, so AI agents know exactly what arguments to pass.
|
|
287
|
+
|
|
163
288
|
## Project Structure
|
|
164
289
|
|
|
165
290
|
```
|
|
166
291
|
src/
|
|
167
292
|
index.ts — public exports
|
|
168
293
|
client.ts — MonarchMoney class with all API methods
|
|
294
|
+
mcp.ts — MCP server (30 tools for AI agents)
|
|
169
295
|
errors.ts — error classes (MonarchMoneyError hierarchy)
|
|
170
296
|
endpoints.ts — API URL constants
|
|
171
297
|
queries.ts — all GraphQL query/mutation strings
|
|
172
298
|
types.ts — TypeScript interfaces for all API responses
|
|
173
299
|
```
|
|
174
300
|
|
|
301
|
+
## Testing
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
npm test # run tests once
|
|
305
|
+
npm run test:watch # run tests in watch mode
|
|
306
|
+
npm run test:coverage # run with coverage report
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Tests use [Vitest](https://vitest.dev) and do not require real API credentials (fetch is mocked where needed).
|
|
310
|
+
|
|
311
|
+
**Test the API connection** (against the live api.monarch.com):
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
npm run build
|
|
315
|
+
|
|
316
|
+
# Login with email + password (will prompt for email OTP code if required)
|
|
317
|
+
MONARCH_EMAIL=your@email.com MONARCH_PASSWORD=yourpassword npm run test:connection
|
|
318
|
+
|
|
319
|
+
# Use a saved token (skips login)
|
|
320
|
+
MONARCH_TOKEN=your-token npm run test:connection -- --token
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Set these in a `.env` file for convenience (see `.env.example`).
|
|
324
|
+
|
|
175
325
|
## FAQ
|
|
176
326
|
|
|
177
327
|
**How do I use this if I login to Monarch via Google?**
|
|
178
328
|
|
|
179
329
|
Set a password on your Monarch account at [Settings > Security](https://app.monarchmoney.com/settings/security), then use that password with this library.
|
|
180
330
|
|
|
331
|
+
**Why does Monarch ask for an email code every time I login?**
|
|
332
|
+
|
|
333
|
+
Monarch requires email verification for new/unrecognized devices. After login, save the session token with `mm.saveSession()` or store `mm.token` — subsequent runs will reuse it without re-authenticating.
|
|
334
|
+
|
|
335
|
+
## How This Library Compares
|
|
336
|
+
|
|
337
|
+
There are several unofficial Monarch Money integrations. Here's how `@hakimelek/monarchmoney` stacks up.
|
|
338
|
+
|
|
339
|
+
### Landscape
|
|
340
|
+
|
|
341
|
+
| | **@hakimelek/monarchmoney** | **monarch-money-api** (pbassham) | **monarchmoney** (keithah) | **monarchmoney** (hammem) |
|
|
342
|
+
|---|---|---|---|---|
|
|
343
|
+
| **Platform** | Node.js / TypeScript | Node.js / JavaScript | Node.js / TypeScript | Python |
|
|
344
|
+
| **npm weekly downloads** | — | ~440 | ~130 | N/A (pip: ~103K/mo) |
|
|
345
|
+
| **Runtime deps** | **1** (speakeasy) | 5 | 7 | 3 |
|
|
346
|
+
| **TypeScript types** | Full (every response) | None | Yes | N/A |
|
|
347
|
+
| **Email OTP flow** | Yes | No | No | No |
|
|
348
|
+
| **MFA / TOTP** | Yes | Yes | Yes | Yes |
|
|
349
|
+
| **Session persistence** | Yes (0o600 perms) | Yes | Yes (AES-256) | Yes |
|
|
350
|
+
| **Interactive CLI login** | Yes | Yes | Yes | Yes |
|
|
351
|
+
| **HTTP client** | Native `fetch` | node-fetch | node-fetch + graphql-request | aiohttp |
|
|
352
|
+
| **Error hierarchy** | 4 typed exceptions | Generic throws | Generic throws | 1 exception |
|
|
353
|
+
| **Read methods** | 20 | 15 | ~20 | ~16 |
|
|
354
|
+
| **Write methods** | 14 | 9 | ~12 | ~10 |
|
|
355
|
+
| **Rate limiting** | Yes | No | Yes | No |
|
|
356
|
+
| **Retry with backoff** | Yes | No | Yes | No |
|
|
357
|
+
| **Auto-pagination** | Yes | No | No | No |
|
|
358
|
+
| **Dual CJS + ESM** | Yes | No | Yes | No |
|
|
359
|
+
| **Refresh progress events** | Yes | No | No | No |
|
|
360
|
+
| **Built-in MCP server** | Yes (30 tools) | No | No | No |
|
|
361
|
+
|
|
362
|
+
### Where this library wins
|
|
363
|
+
|
|
364
|
+
**Minimal footprint.** One runtime dependency vs 5-7 in the JS/TS alternatives. Native `fetch` means zero HTTP polyfills on Node 18+.
|
|
365
|
+
|
|
366
|
+
**Email OTP support.** Monarch now requires email verification for unrecognized devices, even when MFA is off. This is the only Node.js library that handles the full `EmailOtpRequiredException` → `submitEmailOtp()` flow. Without it, automated scripts break on first login from a new environment.
|
|
367
|
+
|
|
368
|
+
**Typed everything.** Every API response has a dedicated TypeScript interface — 50+ exported types covering accounts, transactions, holdings, cashflow, budgets, recurring items, and mutations. The `monarch-money-api` package has no types at all.
|
|
369
|
+
|
|
370
|
+
**Structured error handling.** Four distinct exception classes (`LoginFailedException`, `RequireMFAException`, `EmailOtpRequiredException`, `RequestFailedException`) with error codes and status codes. Competitors throw generic errors or strings.
|
|
371
|
+
|
|
372
|
+
**Broader write coverage.** Includes `updateTransaction()`, `setBudgetAmount()`, `uploadAccountBalanceHistory()`, `getCashflow()`, `getCashflowSummary()`, and `getRecurringTransactions()` — all missing from `monarch-money-api`.
|
|
373
|
+
|
|
374
|
+
**Clean, flat API.** One class, direct methods, no sub-objects or verbosity levels to learn. Import `MonarchMoney`, call methods, get typed results.
|
|
375
|
+
|
|
181
376
|
## Contributing
|
|
182
377
|
|
|
183
|
-
Contributions welcome. Please ensure TypeScript compiles cleanly (`npm run build`).
|
|
378
|
+
Contributions welcome. Please ensure TypeScript compiles cleanly (`npm run build`) and tests pass (`npm test`).
|
|
184
379
|
|
|
185
380
|
## License
|
|
186
381
|
|