@warmio/mcp 5.0.0 → 5.0.1

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
  }
@@ -60,14 +62,15 @@ 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
76
  - What’s my net worth?
@@ -81,7 +84,7 @@ After setup, I should be able to ask:
81
84
  If your MCP client supports Streamable HTTP, you can run:
82
85
 
83
86
  ```bash
84
- npx -y @warmio/mcp http --host 127.0.0.1 --port 3000 --path /mcp
87
+ npx -y @warmio/mcp@latest http --host 127.0.0.1 --port 3000 --path /mcp
85
88
  ```
86
89
 
87
90
  Relevant optional environment variables:
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') {
@@ -4,7 +4,6 @@ const DEFAULT_REQUEST_TIMEOUT_MS = (() => {
4
4
  const raw = Number(process.env.WARM_API_TIMEOUT_MS || 10_000);
5
5
  return Number.isFinite(raw) && raw > 0 ? raw : 10_000;
6
6
  })();
7
- const DEFAULT_SPENDING_MONTHS = 6;
8
7
  let cachedApiKey;
9
8
  function roundMoney(value) {
10
9
  return Math.round(value * 100) / 100;
@@ -56,6 +55,8 @@ function normalizeAccounts(accounts) {
56
55
  };
57
56
  }
58
57
  function normalizeFinancialState(input) {
58
+ const latestSnapshot = input.snapshots?.[0];
59
+ const categorySpending = latestSnapshot?.spending_by_category ?? [];
59
60
  return {
60
61
  budgets: (input.budgets || []).map((budget) => ({
61
62
  amount: budget.amount ?? 0,
@@ -66,8 +67,8 @@ function normalizeFinancialState(input) {
66
67
  spent: budget.spent ?? 0,
67
68
  status: budget.status ?? null,
68
69
  })),
69
- category_spending: (input.categorySpending || []).map((item) => ({
70
- amount: roundMoney(item?.total ?? 0),
70
+ category_spending: categorySpending.map((item) => ({
71
+ amount: roundMoney(item?.amount ?? item?.total ?? 0),
71
72
  category: item?.category || 'Uncategorized',
72
73
  })),
73
74
  generated_at: input.generatedAt,
@@ -193,7 +194,7 @@ export async function apiRequest(endpoint, params = {}, options = {}) {
193
194
  if (!response.ok) {
194
195
  const errorMessages = {
195
196
  401: 'Invalid or expired API key. Regenerate at https://warm.io/settings',
196
- 403: 'Pro subscription required. Upgrade at https://warm.io/settings',
197
+ 403: 'API access is available on paid plans only. Upgrade at https://warm.io/settings',
197
198
  429: 'Rate limit exceeded. Try again in a few minutes.',
198
199
  };
199
200
  if (errorMessages[response.status]) {
@@ -219,7 +220,7 @@ export function createWarmApiClient(options = {}) {
219
220
  return normalizeAccounts(response.accounts || []);
220
221
  };
221
222
  const getFinancialState = async () => {
222
- const [snapshotsResponse, recurringResponse, budgetsResponse, goalsResponse, healthResponse, liabilitiesResponse, holdingsResponse, spendingResponse,] = await Promise.all([
223
+ const [snapshotsResponse, recurringResponse, budgetsResponse, goalsResponse, healthResponse, liabilitiesResponse, holdingsResponse,] = await Promise.all([
223
224
  apiRequest('/api/export', { dataset: 'snapshots' }, options),
224
225
  apiRequest('/api/export', { dataset: 'recurring' }, options),
225
226
  apiRequest('/api/export', { dataset: 'budgets' }, options),
@@ -227,10 +228,8 @@ export function createWarmApiClient(options = {}) {
227
228
  apiRequest('/api/export', { dataset: 'health' }, options),
228
229
  apiRequest('/api/export', { dataset: 'liabilities' }, options),
229
230
  apiRequest('/api/export', { dataset: 'holdings' }, options),
230
- apiRequest('/api/spending', { months: String(DEFAULT_SPENDING_MONTHS) }, options),
231
231
  ]);
232
- const generatedAt = spendingResponse.generated_at ||
233
- holdingsResponse.generated_at ||
232
+ const generatedAt = holdingsResponse.generated_at ||
234
233
  liabilitiesResponse.generated_at ||
235
234
  healthResponse.generated_at ||
236
235
  goalsResponse.generated_at ||
@@ -240,7 +239,6 @@ export function createWarmApiClient(options = {}) {
240
239
  new Date().toISOString();
241
240
  return normalizeFinancialState({
242
241
  budgets: budgetsResponse.budgets,
243
- categorySpending: spendingResponse.spending,
244
242
  generatedAt,
245
243
  goals: goalsResponse.goals,
246
244
  health: healthResponse,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warmio/mcp",
3
- "version": "5.0.0",
3
+ "version": "5.0.1",
4
4
  "description": "Warm CLI and MCP server for personal financial data",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -34,6 +34,7 @@
34
34
  ],
35
35
  "scripts": {
36
36
  "build": "tsc",
37
+ "test": "npm run build && node --test test/*.mjs",
37
38
  "start": "node dist/index.js mcp",
38
39
  "start:http": "node dist/index.js http",
39
40
  "start:install": "node dist/index.js install",