@warmio/mcp 5.0.0 → 6.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/README.md CHANGED
@@ -5,10 +5,12 @@ Warmio is the one-command CLI and read-only MCP server for Warm financial data.
5
5
  ## Install
6
6
 
7
7
  ```bash
8
- npx @warmio/mcp
8
+ npx -y @warmio/mcp@latest
9
9
  ```
10
10
 
11
- The installer detects supported MCP clients, prompts for your Warm API key, validates it, and writes the local `stdio` MCP config automatically. The key is stored once in your local Warm profile instead of being duplicated into every MCP client config.
11
+ The installer detects supported MCP clients, prompts for your Warm API key, validates it, and
12
+ writes the local `stdio` MCP config automatically. The key is stored once in your local Warm
13
+ profile instead of being duplicated into every MCP client config.
12
14
 
13
15
  ## Requirements
14
16
 
@@ -25,7 +27,7 @@ Use this when a client asks you to paste an MCP JSON config:
25
27
  "mcpServers": {
26
28
  "warm": {
27
29
  "command": "npx",
28
- "args": ["-y", "@warmio/mcp", "mcp"]
30
+ "args": ["-y", "@warmio/mcp@latest", "mcp"]
29
31
  }
30
32
  }
31
33
  }
@@ -38,7 +40,7 @@ On Windows, prefer:
38
40
  "mcpServers": {
39
41
  "warm": {
40
42
  "command": "cmd",
41
- "args": ["/c", "npx", "-y", "@warmio/mcp", "mcp"]
43
+ "args": ["/c", "npx", "-y", "@warmio/mcp@latest", "mcp"]
42
44
  }
43
45
  }
44
46
  }
@@ -49,7 +51,7 @@ Optional auth overrides:
49
51
  - `WARM_API_KEY`
50
52
  - `WARM_API_KEY_FILE`
51
53
 
52
- ## Universal copy/paste setup prompt
54
+ ## Universal Copy/Paste Setup Prompt
53
55
 
54
56
  ```text
55
57
  I want to connect Warm to this AI app using MCP.
@@ -60,28 +62,40 @@ Use this MCP server config:
60
62
  "mcpServers": {
61
63
  "warm": {
62
64
  "command": "npx",
63
- "args": ["-y", "@warmio/mcp", "mcp"]
65
+ "args": ["-y", "@warmio/mcp@latest", "mcp"]
64
66
  }
65
67
  }
66
68
  }
67
69
 
68
70
  If this app supports MCP configuration, add a server named "warm" with that command.
69
71
 
70
- Do not ask me to paste my Warm API key into this chat. Help me store it locally using Warm's setup flow, then test the connection by calling Warm's verify_key tool.
72
+ Do not ask me to paste my Warm API key into this chat. Help me store it locally using Warm's setup
73
+ flow, then test the connection by calling Warm's verify_key tool.
71
74
 
72
75
  After setup, I should be able to ask:
73
- - What’s my net worth?
76
+ - What is my net worth?
74
77
  - Summarize my recent transactions.
75
78
  - What changed recently?
76
79
  - What recurring charges do I have?
77
80
  ```
78
81
 
82
+ ## CLI
83
+
84
+ ```bash
85
+ npx -y @warmio/mcp@latest context get
86
+ npx -y @warmio/mcp@latest context meta
87
+ npx -y @warmio/mcp@latest transactions get --month 2026-07
88
+ npx -y @warmio/mcp@latest transactions get --latest
89
+ ```
90
+
91
+ All CLI read commands print JSON.
92
+
79
93
  ## Optional Streamable HTTP
80
94
 
81
95
  If your MCP client supports Streamable HTTP, you can run:
82
96
 
83
97
  ```bash
84
- npx -y @warmio/mcp http --host 127.0.0.1 --port 3000 --path /mcp
98
+ npx -y @warmio/mcp@latest http --host 127.0.0.1 --port 3000 --path /mcp
85
99
  ```
86
100
 
87
101
  Relevant optional environment variables:
@@ -93,26 +107,31 @@ Relevant optional environment variables:
93
107
 
94
108
  ## Core Tools
95
109
 
96
- Warm's published/documented MCP surface is the following four-tool core:
110
+ Warm's v6 MCP surface is exactly three tools:
97
111
 
98
- | Tool | Description |
99
- | --------------------- | ----------------------------------------------- |
100
- | `get_accounts` | List connected accounts with current balances |
101
- | `get_transactions` | Page through transactions with an opaque cursor |
102
- | `get_financial_state` | Return the current typed financial state bundle |
103
- | `verify_key` | Validate the configured API key |
112
+ | Tool | Description |
113
+ | ----------------------- | ------------------------------------------------ |
114
+ | `get_financial_context` | Return compact `FinancialContext` JSON |
115
+ | `get_transactions` | Return one month page or the fixed latest window |
116
+ | `verify_key` | Validate the configured API key |
104
117
 
105
- ## Strict Contract
118
+ ## Parameter Contract
106
119
 
107
120
  - Every tool takes a JSON object input and returns a JSON object output.
108
- - Treat the contracts as closed and typed. Do not depend on undocumented fields.
109
- - Calendar dates use `YYYY-MM-DD`. Incremental sync timestamps use ISO 8601 datetimes.
110
- - Amounts are numbers, never formatted strings.
111
- - Transaction amounts follow the Plaid sign convention:
112
- positive = expense/debit, negative = income/credit.
113
- - Pagination cursors are opaque strings. Do not parse them or mix them with changed filters.
121
+ - `get_financial_context` takes `{}` and returns compact context JSON without transaction items.
122
+ - `get_transactions` takes either `{ "month": "YYYY-MM" }`, `{ "latest": true }`, or `{}`.
123
+ - A bare `get_transactions` call with no arguments defaults to `{ "latest": true }`.
124
+ - `month` must use `YYYY-MM` format, for example `2026-07`.
125
+ - `latest` is a fixed 10-day window anchored to the current artifact; the caller cannot configure the window.
126
+ - `month` and `latest` are mutually exclusive.
127
+ - A malformed month returns an error.
128
+ - A well-formed month outside the covered range returns an error.
129
+ - A well-formed month inside the covered range with no transactions returns `count: 0` and `items: []`.
130
+ - Transaction amounts follow the Plaid sign convention: positive = expense/debit, negative = income/credit.
131
+
132
+ ## Shapes
114
133
 
115
- ### `get_accounts`
134
+ ### `get_financial_context`
116
135
 
117
136
  Input:
118
137
 
@@ -120,168 +139,89 @@ Input:
120
139
  {}
121
140
  ```
122
141
 
123
- Returns:
142
+ Returns compact `FinancialContext`:
124
143
 
125
144
  ```json
126
145
  {
127
- "accounts": [
128
- {
129
- "name": "Primary Checking",
130
- "type": "depository",
131
- "subtype": "checking",
132
- "balance": 2450.12,
133
- "institution": "Chase",
134
- "mask": "1234"
135
- }
136
- ]
146
+ "version": "v1",
147
+ "updated_at": "2026-07-06T11:58:41.000Z",
148
+ "currency": "USD",
149
+ "status": {
150
+ "position": {
151
+ "date": "2026-07-06",
152
+ "net_worth": 84500,
153
+ "cash": 18200,
154
+ "debt": 6400,
155
+ "investments": 72700,
156
+ "other_assets": 0,
157
+ "total_assets": 90900
158
+ },
159
+ "accounts": []
160
+ },
161
+ "transactions": {
162
+ "total": 2,
163
+ "months": [{ "month": "2026-07", "count": 2 }]
164
+ },
165
+ "recurring": [],
166
+ "budgets": [],
167
+ "goals": [],
168
+ "snapshots": [],
169
+ "liabilities": [],
170
+ "holdings": [],
171
+ "health": null
137
172
  }
138
173
  ```
139
174
 
140
175
  ### `get_transactions`
141
176
 
142
- Input:
177
+ Month input:
143
178
 
144
179
  ```json
145
- {
146
- "limit": 100,
147
- "cursor": "opaque-cursor-from-a-prior-page",
148
- "last_knowledge": "2026-03-11T00:00:00.000Z",
149
- "search": "coffee"
150
- }
180
+ { "month": "2026-07" }
151
181
  ```
152
182
 
153
- Returns:
183
+ Month response:
154
184
 
155
185
  ```json
156
186
  {
157
- "generated_at": "2026-03-11T12:00:00.000Z",
158
- "next_knowledge": "2026-03-11T12:00:00.000Z",
159
- "txns": [
187
+ "month": "2026-07",
188
+ "start_date": "2026-07-01",
189
+ "end_date": "2026-07-31",
190
+ "count": 1,
191
+ "items": [
160
192
  {
161
- "id": "txn_123",
162
- "date": "2026-01-15",
163
- "amount": 12.34,
164
- "merchant": "Coffee Shop",
165
- "description": "COFFEE SHOP",
193
+ "id": "txn_1",
194
+ "account_id": "acct_1",
195
+ "date": "2026-07-05",
196
+ "amount": 42.18,
197
+ "merchant": "Whole Foods",
198
+ "name": "WHOLE FOODS",
166
199
  "category": "FOOD_AND_DRINK",
167
- "detailed_category": "FOOD_AND_DRINK_COFFEE"
200
+ "subcategory": "FOOD_AND_DRINK_GROCERIES",
201
+ "pending": false,
202
+ "currency": "USD"
168
203
  }
169
- ],
170
- "pagination": {
171
- "limit": 100,
172
- "next_cursor": "opaque-next-cursor",
173
- "has_more": true
174
- }
204
+ ]
175
205
  }
176
206
  ```
177
207
 
178
- Cursor model:
179
-
180
- 1. Omit `cursor` on the first call.
181
- 2. Keep `limit` and any filters such as `search` fixed while following a cursor chain.
182
- 3. If `pagination.next_cursor` is non-null, pass it unchanged to fetch the next page.
183
- 4. Stop when `next_cursor` is `null`.
184
- 5. Do not combine `cursor` with `last_knowledge`.
185
-
186
- ### `get_financial_state`
187
-
188
- Input:
208
+ Latest input:
189
209
 
190
210
  ```json
191
- {}
211
+ { "latest": true }
192
212
  ```
193
213
 
194
- Returns:
214
+ Latest response:
195
215
 
196
216
  ```json
197
217
  {
198
- "generated_at": "2026-03-11T12:00:00.000Z",
199
- "snapshots": [
200
- {
201
- "date": "2026-03-11",
202
- "net_worth": 125430.55,
203
- "total_assets": 168210.77,
204
- "total_liabilities": 42780.22
205
- }
206
- ],
207
- "recurring": [
208
- {
209
- "merchant": "Netflix",
210
- "amount": 15.49,
211
- "frequency": "MONTHLY",
212
- "next_date": "2026-03-18",
213
- "type": "subscription",
214
- "active": true
215
- }
216
- ],
217
- "budgets": [
218
- {
219
- "name": "Dining Out",
220
- "amount": 400,
221
- "spent": 182.55,
222
- "remaining": 217.45,
223
- "percent_used": 45.64,
224
- "period": "monthly",
225
- "status": "on_track"
226
- }
227
- ],
228
- "goals": [
229
- {
230
- "name": "Emergency Fund",
231
- "target": 10000,
232
- "current": 4200,
233
- "progress_percent": 42,
234
- "target_date": null,
235
- "status": "active",
236
- "category": "safety",
237
- "monthly_contribution_needed": 400
238
- }
239
- ],
240
- "health": {
241
- "score": 78,
242
- "label": "Good",
243
- "data_completeness": 94,
244
- "pillars": {
245
- "spend": 20,
246
- "save": 23,
247
- "borrow": 15,
248
- "build": 20
249
- },
250
- "message": null
251
- },
252
- "liabilities": [
253
- {
254
- "account_id": "acc_loan_1",
255
- "type": "student",
256
- "balance": 12450.22,
257
- "apr_percentage": 5.2,
258
- "minimum_payment": 145,
259
- "next_payment_due_date": "2026-03-22",
260
- "is_overdue": false
261
- }
262
- ],
263
- "holdings": [
264
- {
265
- "account_id": "acc_inv_1",
266
- "security_name": "Vanguard Total Stock Market ETF",
267
- "symbol": "VTI",
268
- "type": "etf",
269
- "quantity": 12.5,
270
- "value": 3541.25,
271
- "cost_basis": 3010
272
- }
273
- ],
274
- "category_spending": [
275
- {
276
- "category": "FOOD_AND_DRINK",
277
- "amount": 182.55
278
- }
279
- ]
218
+ "since": "2026-06-26",
219
+ "window_days": 10,
220
+ "count": 1,
221
+ "items": []
280
222
  }
281
223
  ```
282
224
 
283
- If Warm does not have enough state data yet, nullable fields remain `null`.
284
-
285
225
  ### `verify_key`
286
226
 
287
227
  Input:
@@ -299,6 +239,17 @@ Returns:
299
239
  }
300
240
  ```
301
241
 
242
+ ## v6 Breaking Changes
243
+
244
+ v6 is a clean break with no backwards compatibility and no alias window.
245
+
246
+ - `get_accounts` is removed. Use `get_financial_context.status.accounts`.
247
+ - `get_financial_state` is removed. Use `get_financial_context`.
248
+ - `get_transactions` no longer supports `cursor`, `search`, or `last_knowledge`.
249
+ - The old `txns` transaction array is gone. Transaction responses use `items`.
250
+ - Renamed fields follow the FinancialContext contract, including `subcategory`, `used_percent`,
251
+ `status`, `due_date`, and `other_assets`.
252
+
302
253
  ## Security
303
254
 
304
255
  - Read-only: no write, delete, transfer, or mutation tools
@@ -310,6 +261,7 @@ Returns:
310
261
  ```bash
311
262
  npm install
312
263
  npm run build
264
+ npm test
313
265
  ```
314
266
 
315
267
  ## License
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import { startHttpServer } from './http.js';
4
4
  import { loadDotEnv } from './load-env.js';
5
5
  import { createWarmServer } from './server.js';
6
6
  import { AdaptiveStdioTransport } from './stdio.js';
7
+ import { createWarmApiClient } from './warm-api-client.js';
7
8
  function isInteractiveSession() {
8
9
  return process.stdin.isTTY === true && process.stdout.isTTY === true;
9
10
  }
@@ -18,6 +19,13 @@ function printUsage() {
18
19
  console.log(' warmio install [--force] [--no-validate]');
19
20
  console.log(' warmio mcp');
20
21
  console.log(' warmio http [--host 127.0.0.1] [--port 3000] [--path /mcp]');
22
+ console.log(' warmio context get');
23
+ console.log(' warmio context meta');
24
+ console.log(' warmio transactions get --month YYYY-MM');
25
+ console.log(' warmio transactions get --latest');
26
+ console.log('');
27
+ console.log(' Transactions require exactly one selector: --month YYYY-MM or --latest.');
28
+ console.log(' Month and latest are mutually exclusive. Latest is a fixed 10-day window.');
21
29
  console.log('');
22
30
  }
23
31
  function parseCliArgs(args) {
@@ -44,6 +52,53 @@ function parseCliArgs(args) {
44
52
  options.command = 'http';
45
53
  continue;
46
54
  }
55
+ if (arg === 'context') {
56
+ const subcommand = args[index + 1];
57
+ if (subcommand === 'get') {
58
+ options.command = 'context-get';
59
+ index += 1;
60
+ continue;
61
+ }
62
+ if (subcommand === 'meta') {
63
+ options.command = 'context-meta';
64
+ index += 1;
65
+ continue;
66
+ }
67
+ throw new Error('Usage: warmio context get | warmio context meta');
68
+ }
69
+ if (arg === 'transactions') {
70
+ const subcommand = args[index + 1];
71
+ if (subcommand !== 'get') {
72
+ throw new Error('Usage: warmio transactions get --month YYYY-MM | --latest');
73
+ }
74
+ options.command = 'transactions-get';
75
+ index += 1;
76
+ continue;
77
+ }
78
+ if (arg === '--format') {
79
+ const format = args[index + 1];
80
+ if (format !== 'json') {
81
+ throw new Error('Only --format json is supported.');
82
+ }
83
+ index += 1;
84
+ continue;
85
+ }
86
+ if (arg === '--month') {
87
+ const month = args[index + 1];
88
+ if (!month) {
89
+ throw new Error('Missing value for --month');
90
+ }
91
+ if (!/^\d{4}-\d{2}$/.test(month)) {
92
+ throw new Error(`Invalid value for --month: ${month}. Expected YYYY-MM.`);
93
+ }
94
+ options.month = month;
95
+ index += 1;
96
+ continue;
97
+ }
98
+ if (arg === '--latest') {
99
+ options.latest = true;
100
+ continue;
101
+ }
47
102
  if (arg === '--host') {
48
103
  options.httpHost = args[index + 1];
49
104
  index += 1;
@@ -79,6 +134,23 @@ function parseCliArgs(args) {
79
134
  }
80
135
  return options;
81
136
  }
137
+ function validateCliOptions(options) {
138
+ if (options.command !== 'transactions-get') {
139
+ if (options.month || options.latest) {
140
+ throw new Error('--month and --latest are only valid with `warmio transactions get`.');
141
+ }
142
+ return;
143
+ }
144
+ if (options.month && options.latest) {
145
+ throw new Error('--month and --latest are mutually exclusive.');
146
+ }
147
+ if (!options.month && !options.latest) {
148
+ throw new Error('Usage: warmio transactions get --month YYYY-MM | --latest');
149
+ }
150
+ }
151
+ function printJson(value) {
152
+ console.log(JSON.stringify(value, null, 2));
153
+ }
82
154
  export async function startStdioServer() {
83
155
  const server = createWarmServer();
84
156
  const transport = new AdaptiveStdioTransport();
@@ -88,7 +160,17 @@ export async function startStdioServer() {
88
160
  async function main() {
89
161
  loadDotEnv();
90
162
  const options = parseCliArgs(process.argv.slice(2));
163
+ validateCliOptions(options);
91
164
  switch (options.command) {
165
+ case 'context-get':
166
+ printJson(await createWarmApiClient().getFinancialContext());
167
+ return;
168
+ case 'context-meta':
169
+ printJson(await createWarmApiClient().getFinancialContextMeta());
170
+ return;
171
+ case 'transactions-get':
172
+ printJson(await createWarmApiClient().getTransactions(options.month ? { month: options.month } : { latest: true }));
173
+ return;
92
174
  case 'help':
93
175
  printUsage();
94
176
  return;
package/dist/install.js CHANGED
@@ -46,7 +46,7 @@ const GLOBAL_CLIENTS = [
46
46
  { name: 'Gemini CLI', configPath: join(HOME, '.gemini', 'settings.json'), format: 'json' },
47
47
  ];
48
48
  const PROJECT_CONFIGS = ['.mcp.json', '.cursor/mcp.json', '.vscode/mcp.json'];
49
- const MCP_PACKAGE_SPEC = '@warmio/mcp';
49
+ const MCP_PACKAGE_SPEC = '@warmio/mcp@latest';
50
50
  const MCP_CONFIG = platform() === 'win32'
51
51
  ? { command: 'cmd', args: ['/c', 'npx', '-y', MCP_PACKAGE_SPEC, 'mcp'] }
52
52
  : { command: 'npx', args: ['-y', MCP_PACKAGE_SPEC, 'mcp'] };
@@ -81,7 +81,7 @@ function getStringArray(value) {
81
81
  return value.filter((entry) => typeof entry === 'string');
82
82
  }
83
83
  function isWarmPackageSpecifier(value) {
84
- return /^@warmio\/warmio(?:@.+)?$/.test(value);
84
+ return /^@warmio\/mcp(?:@.+)?$/.test(value);
85
85
  }
86
86
  function isSupportedWarmInvocation(command, args) {
87
87
  if (command !== 'npx' && command !== 'cmd') {