@warmio/mcp 4.3.2 → 5.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 +38 -27
- package/dist/index.js +11 -15
- package/dist/install.js +17 -32
- package/dist/warm-api-client.js +1 -1
- package/package.json +9 -6
package/README.md
CHANGED
|
@@ -1,26 +1,14 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Warmio
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
This package starts in local `stdio` mode by default and also supports optional Streamable HTTP.
|
|
3
|
+
Warmio is the one-command CLI and read-only MCP server for Warm financial data.
|
|
6
4
|
|
|
7
5
|
## Install
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
|
-
npx
|
|
8
|
+
npx @warmio/mcp
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
The installer detects supported MCP clients, prompts for your Warm API key, and writes the local
|
|
14
|
-
`stdio` server config automatically. The key is stored once in your local Warm profile instead of
|
|
15
|
-
being duplicated into every MCP client config.
|
|
16
|
-
|
|
17
|
-
Use `@latest` when you want the newest published package. Bare `npx @warmio/mcp` can reuse a
|
|
18
|
-
cached or local copy and leave you on an older version. If you want a deterministic install,
|
|
19
|
-
replace `@latest` with an exact version such as `@warmio/mcp@4.3.2`.
|
|
20
|
-
|
|
21
|
-
When launched without arguments in a non-interactive context, the binary automatically starts the
|
|
22
|
-
`stdio` server instead of the installer. This keeps MCP clients that invoke the package directly
|
|
23
|
-
from falling into the interactive setup flow.
|
|
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.
|
|
24
12
|
|
|
25
13
|
## Requirements
|
|
26
14
|
|
|
@@ -28,41 +16,64 @@ from falling into the interactive setup flow.
|
|
|
28
16
|
- A Warm API key from [Settings -> API Keys](https://warm.io/settings)
|
|
29
17
|
- Node.js 18+
|
|
30
18
|
|
|
31
|
-
## Manual
|
|
19
|
+
## Manual MCP Config
|
|
32
20
|
|
|
33
|
-
|
|
34
|
-
stay secret-free:
|
|
21
|
+
Use this when a client asks you to paste an MCP JSON config:
|
|
35
22
|
|
|
36
23
|
```json
|
|
37
24
|
{
|
|
38
25
|
"mcpServers": {
|
|
39
26
|
"warm": {
|
|
40
27
|
"command": "npx",
|
|
41
|
-
"args": ["-y", "@warmio/mcp
|
|
28
|
+
"args": ["-y", "@warmio/mcp", "mcp"]
|
|
42
29
|
}
|
|
43
30
|
}
|
|
44
31
|
}
|
|
45
32
|
```
|
|
46
33
|
|
|
47
|
-
|
|
48
|
-
|
|
34
|
+
On Windows, prefer:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"mcpServers": {
|
|
39
|
+
"warm": {
|
|
40
|
+
"command": "cmd",
|
|
41
|
+
"args": ["/c", "npx", "-y", "@warmio/mcp", "mcp"]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
49
46
|
|
|
50
47
|
Optional auth overrides:
|
|
51
48
|
|
|
52
49
|
- `WARM_API_KEY`
|
|
53
50
|
- `WARM_API_KEY_FILE`
|
|
54
51
|
|
|
55
|
-
|
|
52
|
+
## Universal copy/paste setup prompt
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
I want to connect Warm to this AI app using MCP.
|
|
56
|
+
|
|
57
|
+
Use this MCP server config:
|
|
56
58
|
|
|
57
|
-
```json
|
|
58
59
|
{
|
|
59
60
|
"mcpServers": {
|
|
60
61
|
"warm": {
|
|
61
|
-
"command": "
|
|
62
|
-
"args": ["
|
|
62
|
+
"command": "npx",
|
|
63
|
+
"args": ["-y", "@warmio/mcp", "mcp"]
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
}
|
|
67
|
+
|
|
68
|
+
If this app supports MCP configuration, add a server named "warm" with that command.
|
|
69
|
+
|
|
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.
|
|
71
|
+
|
|
72
|
+
After setup, I should be able to ask:
|
|
73
|
+
- What’s my net worth?
|
|
74
|
+
- Summarize my recent transactions.
|
|
75
|
+
- What changed recently?
|
|
76
|
+
- What recurring charges do I have?
|
|
66
77
|
```
|
|
67
78
|
|
|
68
79
|
## Optional Streamable HTTP
|
|
@@ -70,7 +81,7 @@ On Windows, prefer:
|
|
|
70
81
|
If your MCP client supports Streamable HTTP, you can run:
|
|
71
82
|
|
|
72
83
|
```bash
|
|
73
|
-
npx -y @warmio/mcp
|
|
84
|
+
npx -y @warmio/mcp http --host 127.0.0.1 --port 3000 --path /mcp
|
|
74
85
|
```
|
|
75
86
|
|
|
76
87
|
Relevant optional environment variables:
|
package/dist/index.js
CHANGED
|
@@ -9,24 +9,20 @@ function isInteractiveSession() {
|
|
|
9
9
|
}
|
|
10
10
|
function printUsage() {
|
|
11
11
|
console.log('');
|
|
12
|
-
console.log('
|
|
13
|
-
console.log('
|
|
12
|
+
console.log(' Warmio');
|
|
13
|
+
console.log(' ------');
|
|
14
14
|
console.log('');
|
|
15
|
-
console.log('
|
|
16
|
-
console.log('
|
|
17
|
-
console.log(' Non-interactive launch: starts stdio server');
|
|
15
|
+
console.log(' warmio');
|
|
16
|
+
console.log(' Run the interactive installer');
|
|
18
17
|
console.log('');
|
|
19
|
-
console.log('
|
|
20
|
-
console.log('
|
|
21
|
-
console.log('
|
|
22
|
-
console.log('');
|
|
23
|
-
console.log(' Aliases:');
|
|
24
|
-
console.log(' --server, --stdio Start stdio mode');
|
|
18
|
+
console.log(' warmio install [--force] [--no-validate]');
|
|
19
|
+
console.log(' warmio mcp');
|
|
20
|
+
console.log(' warmio http [--host 127.0.0.1] [--port 3000] [--path /mcp]');
|
|
25
21
|
console.log('');
|
|
26
22
|
}
|
|
27
23
|
function parseCliArgs(args) {
|
|
28
24
|
const options = {
|
|
29
|
-
command: isInteractiveSession() ? 'install' : '
|
|
25
|
+
command: isInteractiveSession() ? 'install' : 'mcp',
|
|
30
26
|
force: false,
|
|
31
27
|
validateApiKey: true,
|
|
32
28
|
};
|
|
@@ -40,8 +36,8 @@ function parseCliArgs(args) {
|
|
|
40
36
|
options.command = 'install';
|
|
41
37
|
continue;
|
|
42
38
|
}
|
|
43
|
-
if (arg === '
|
|
44
|
-
options.command = '
|
|
39
|
+
if (arg === 'mcp') {
|
|
40
|
+
options.command = 'mcp';
|
|
45
41
|
continue;
|
|
46
42
|
}
|
|
47
43
|
if (arg === 'http') {
|
|
@@ -96,7 +92,7 @@ async function main() {
|
|
|
96
92
|
case 'help':
|
|
97
93
|
printUsage();
|
|
98
94
|
return;
|
|
99
|
-
case '
|
|
95
|
+
case 'mcp':
|
|
100
96
|
await startStdioServer();
|
|
101
97
|
return;
|
|
102
98
|
case 'http':
|
package/dist/install.js
CHANGED
|
@@ -46,10 +46,10 @@ 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';
|
|
50
50
|
const MCP_CONFIG = platform() === 'win32'
|
|
51
|
-
? { command: 'cmd', args: ['/c', 'npx', '-y', MCP_PACKAGE_SPEC, '
|
|
52
|
-
: { command: 'npx', args: ['-y', MCP_PACKAGE_SPEC, '
|
|
51
|
+
? { command: 'cmd', args: ['/c', 'npx', '-y', MCP_PACKAGE_SPEC, 'mcp'] }
|
|
52
|
+
: { command: 'npx', args: ['-y', MCP_PACKAGE_SPEC, 'mcp'] };
|
|
53
53
|
const WARM_API_KEY_PATH = getWarmApiKeyPath();
|
|
54
54
|
const WARM_TOML_BLOCK_PATTERN = /\n?\[mcp_servers\.warm\][\s\S]*?(?=\n\[[^\n]+\]|\s*$)/g;
|
|
55
55
|
const WARM_TOML_ENV_BLOCK_PATTERN = /\n?\[mcp_servers\.warm\.env\][\s\S]*?(?=\n\[[^\n]+\]|\s*$)/g;
|
|
@@ -81,24 +81,13 @@ function getStringArray(value) {
|
|
|
81
81
|
return value.filter((entry) => typeof entry === 'string');
|
|
82
82
|
}
|
|
83
83
|
function isWarmPackageSpecifier(value) {
|
|
84
|
-
return /^@warmio\/
|
|
85
|
-
}
|
|
86
|
-
function isLegacyWarmPackageSpecifier(value) {
|
|
87
|
-
return /^@anthropic\/warm-mcp-server(?:@.+)?$/.test(value);
|
|
88
|
-
}
|
|
89
|
-
function normalizePathLikeValue(value) {
|
|
90
|
-
return value.replace(/\\/g, '/').toLowerCase();
|
|
91
|
-
}
|
|
92
|
-
function isLocalWarmMcpPath(value) {
|
|
93
|
-
const normalized = normalizePathLikeValue(value);
|
|
94
|
-
return normalized.includes('/warm-mcp/dist/index.js') || normalized.endsWith('/warm-mcp');
|
|
84
|
+
return /^@warmio\/warmio(?:@.+)?$/.test(value);
|
|
95
85
|
}
|
|
96
86
|
function isSupportedWarmInvocation(command, args) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return [command, ...args].some((value) => typeof value === 'string' && isLegacyWarmPackageSpecifier(value));
|
|
87
|
+
if (command !== 'npx' && command !== 'cmd') {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
return args.some((value) => isWarmPackageSpecifier(value)) && args.includes('mcp');
|
|
102
91
|
}
|
|
103
92
|
function getJsonEnvStatus(server) {
|
|
104
93
|
const env = isRecord(server?.env) ? server.env : undefined;
|
|
@@ -121,13 +110,10 @@ function getJsonStatus(client, content) {
|
|
|
121
110
|
}
|
|
122
111
|
const command = typeof server.command === 'string' ? server.command : undefined;
|
|
123
112
|
const args = getStringArray(server.args);
|
|
124
|
-
if (client.isProjectLevel && command && !isLegacyWarmInvocation(command, args)) {
|
|
125
|
-
return { client, state: 'configured', ...envStatus };
|
|
126
|
-
}
|
|
127
113
|
if (isSupportedWarmInvocation(command, args)) {
|
|
128
114
|
return { client, state: 'configured', ...envStatus };
|
|
129
115
|
}
|
|
130
|
-
return { client, state: '
|
|
116
|
+
return { client, state: 'needs_setup', ...envStatus };
|
|
131
117
|
}
|
|
132
118
|
catch {
|
|
133
119
|
return { client, state: 'missing', hasApiKeyFileOverride: false, inlineApiKey: null };
|
|
@@ -190,7 +176,7 @@ function getTomlStatus(client, content) {
|
|
|
190
176
|
if (isSupportedWarmInvocation(command, args)) {
|
|
191
177
|
return { client, state: 'configured', ...envStatus };
|
|
192
178
|
}
|
|
193
|
-
return { client, state: '
|
|
179
|
+
return { client, state: 'needs_setup', ...envStatus };
|
|
194
180
|
}
|
|
195
181
|
function getClientStatus(client) {
|
|
196
182
|
if (!existsSync(client.configPath)) {
|
|
@@ -223,8 +209,7 @@ function configureJson(client) {
|
|
|
223
209
|
const existingArgs = getStringArray(existing?.args);
|
|
224
210
|
const preserveProjectCommand = client.isProjectLevel &&
|
|
225
211
|
!!existingCommand &&
|
|
226
|
-
|
|
227
|
-
!isWarmPackageSpecifier(existingCommand);
|
|
212
|
+
isSupportedWarmInvocation(existingCommand, existingArgs);
|
|
228
213
|
delete nextEnv.WARM_API_KEY;
|
|
229
214
|
if (preserveProjectCommand) {
|
|
230
215
|
servers.warm = {
|
|
@@ -252,8 +237,8 @@ function configureToml(client) {
|
|
|
252
237
|
}
|
|
253
238
|
const tomlCommand = platform() === 'win32' ? 'cmd' : 'npx';
|
|
254
239
|
const tomlArgs = platform() === 'win32'
|
|
255
|
-
? `["/c", "npx", "-y", "${MCP_PACKAGE_SPEC}", "
|
|
256
|
-
: `["-y", "${MCP_PACKAGE_SPEC}", "
|
|
240
|
+
? `["/c", "npx", "-y", "${MCP_PACKAGE_SPEC}", "mcp"]`
|
|
241
|
+
: `["-y", "${MCP_PACKAGE_SPEC}", "mcp"]`;
|
|
257
242
|
const warmBlock = `[mcp_servers.warm]\ncommand = "${tomlCommand}"\nargs = ${tomlArgs}\n`;
|
|
258
243
|
const preservedEnvLines = getTomlEnvStatus(content).envLines.filter((line) => !/^\s*WARM_API_KEY\s*=/.test(line));
|
|
259
244
|
let nextContent = content
|
|
@@ -332,8 +317,8 @@ function getStatusLabel(status, force) {
|
|
|
332
317
|
if (status === 'configured') {
|
|
333
318
|
return 'configured';
|
|
334
319
|
}
|
|
335
|
-
if (status === '
|
|
336
|
-
return 'needs
|
|
320
|
+
if (status === 'needs_setup') {
|
|
321
|
+
return 'needs setup';
|
|
337
322
|
}
|
|
338
323
|
return 'not configured';
|
|
339
324
|
}
|
|
@@ -341,8 +326,8 @@ export async function install(options = {}) {
|
|
|
341
326
|
const force = options.force ?? false;
|
|
342
327
|
const shouldValidateApiKey = options.validateApiKey ?? true;
|
|
343
328
|
console.log('');
|
|
344
|
-
console.log('
|
|
345
|
-
console.log('
|
|
329
|
+
console.log(' Warmio Installer');
|
|
330
|
+
console.log(' ----------------');
|
|
346
331
|
console.log('');
|
|
347
332
|
const globalClients = GLOBAL_CLIENTS.filter(isDetected);
|
|
348
333
|
const projectClients = detectProjectClients();
|
package/dist/warm-api-client.js
CHANGED
|
@@ -163,7 +163,7 @@ export async function apiRequest(endpoint, params = {}, options = {}) {
|
|
|
163
163
|
const requestOptions = createWarmApiClientConfig(options);
|
|
164
164
|
const apiKey = requestOptions.apiKeyResolver?.() ?? getConfiguredApiKey();
|
|
165
165
|
if (!apiKey) {
|
|
166
|
-
throw new Error('WARM_API_KEY not set. Run "npx
|
|
166
|
+
throw new Error('WARM_API_KEY not set. Run "npx @warmio/mcp" to configure or set WARM_API_KEY.');
|
|
167
167
|
}
|
|
168
168
|
const url = new URL(endpoint, requestOptions.apiUrl);
|
|
169
169
|
Object.entries(params).forEach(([key, value]) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@warmio/mcp",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Warm MCP server for
|
|
3
|
+
"version": "5.0.0",
|
|
4
|
+
"description": "Warm CLI and MCP server for personal financial data",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -27,24 +27,27 @@
|
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
"bin": {
|
|
30
|
-
"
|
|
30
|
+
"warmio": "dist/index.js"
|
|
31
31
|
},
|
|
32
32
|
"files": [
|
|
33
33
|
"dist"
|
|
34
34
|
],
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "tsc",
|
|
37
|
-
"start": "node dist/index.js
|
|
37
|
+
"start": "node dist/index.js mcp",
|
|
38
38
|
"start:http": "node dist/index.js http",
|
|
39
39
|
"start:install": "node dist/index.js install",
|
|
40
|
-
"prepublishOnly": "npm run build"
|
|
40
|
+
"prepublishOnly": "npm run build",
|
|
41
|
+
"start:mcp": "node dist/index.js mcp"
|
|
41
42
|
},
|
|
42
43
|
"keywords": [
|
|
43
44
|
"mcp",
|
|
44
45
|
"warm",
|
|
46
|
+
"warmio",
|
|
45
47
|
"financial",
|
|
46
48
|
"api",
|
|
47
|
-
"claude"
|
|
49
|
+
"claude",
|
|
50
|
+
"chatgpt"
|
|
48
51
|
],
|
|
49
52
|
"license": "MIT",
|
|
50
53
|
"dependencies": {
|