@warmio/mcp 4.2.1 → 4.3.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 +20 -1
- package/dist/config-paths.d.ts +1 -0
- package/dist/config-paths.js +9 -0
- package/dist/http.d.ts +5 -6
- package/dist/http.js +12 -10
- package/dist/index.d.ts +1 -1
- package/dist/index.js +56 -5
- package/dist/load-env.d.ts +1 -0
- package/dist/load-env.js +36 -0
- package/dist/schemas.d.ts +242 -191
- package/dist/schemas.js +140 -163
- package/dist/server.d.ts +15 -8
- package/dist/server.js +73 -12
- package/dist/stdio.d.ts +29 -0
- package/dist/stdio.js +200 -0
- package/dist/types.d.ts +2 -158
- package/dist/warm-api-client.d.ts +184 -0
- package/dist/warm-api-client.js +299 -0
- package/dist/warm-server.d.ts +2 -14
- package/dist/warm-server.js +2 -245
- package/package.json +15 -6
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Read-only MCP server for Warm financial data.
|
|
4
4
|
|
|
5
|
-
This package
|
|
5
|
+
This package starts in local `stdio` mode by default and also supports optional Streamable HTTP.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -14,6 +14,10 @@ The installer detects supported MCP clients, prompts for your Warm API key, and
|
|
|
14
14
|
`stdio` server config automatically. The key is stored once in your local Warm profile instead of
|
|
15
15
|
being duplicated into every MCP client config.
|
|
16
16
|
|
|
17
|
+
When launched without arguments in a non-interactive context, the binary automatically starts the
|
|
18
|
+
`stdio` server instead of the installer. This keeps MCP clients that invoke the package directly
|
|
19
|
+
from falling into the interactive setup flow.
|
|
20
|
+
|
|
17
21
|
## Requirements
|
|
18
22
|
|
|
19
23
|
- Warm Pro
|
|
@@ -54,6 +58,21 @@ On Windows, prefer:
|
|
|
54
58
|
}
|
|
55
59
|
```
|
|
56
60
|
|
|
61
|
+
## Optional Streamable HTTP
|
|
62
|
+
|
|
63
|
+
If your MCP client supports Streamable HTTP, you can run:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx @warmio/mcp http --host 127.0.0.1 --port 3000 --path /mcp
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Relevant optional environment variables:
|
|
70
|
+
|
|
71
|
+
- `WARM_MCP_HTTP_HOST`
|
|
72
|
+
- `WARM_MCP_HTTP_PORT`
|
|
73
|
+
- `WARM_MCP_HTTP_PATH`
|
|
74
|
+
- `WARM_MCP_ALLOWED_HOSTS`
|
|
75
|
+
|
|
57
76
|
## Core Tools
|
|
58
77
|
|
|
59
78
|
Warm's published/documented MCP surface is the following four-tool core:
|
package/dist/config-paths.d.ts
CHANGED
package/dist/config-paths.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
1
2
|
import * as os from 'node:os';
|
|
2
3
|
import * as path from 'node:path';
|
|
3
4
|
export function getWarmConfigDir() {
|
|
@@ -21,3 +22,11 @@ export function getWarmApiKeyPath() {
|
|
|
21
22
|
}
|
|
22
23
|
return path.join(getWarmConfigDir(), 'api_key');
|
|
23
24
|
}
|
|
25
|
+
export function readConfigFile(configPath) {
|
|
26
|
+
try {
|
|
27
|
+
return fs.readFileSync(configPath, 'utf-8').trim() || null;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
package/dist/http.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export interface HttpServerOptions {
|
|
2
|
+
allowedHosts?: string[];
|
|
3
3
|
host?: string;
|
|
4
|
-
port?: number;
|
|
5
4
|
path?: string;
|
|
6
|
-
|
|
5
|
+
port?: number;
|
|
7
6
|
}
|
|
8
|
-
export declare function resolveHttpServerOptions(overrides?:
|
|
9
|
-
export declare function startHttpServer(options?:
|
|
7
|
+
export declare function resolveHttpServerOptions(overrides?: HttpServerOptions): Required<HttpServerOptions>;
|
|
8
|
+
export declare function startHttpServer(options?: HttpServerOptions): Promise<any>;
|
package/dist/http.js
CHANGED
|
@@ -25,36 +25,38 @@ function parseAllowedHosts(value) {
|
|
|
25
25
|
}
|
|
26
26
|
function jsonRpcError(message) {
|
|
27
27
|
return {
|
|
28
|
-
jsonrpc: '2.0',
|
|
29
28
|
error: {
|
|
30
29
|
code: -32000,
|
|
31
30
|
message,
|
|
32
31
|
},
|
|
33
32
|
id: null,
|
|
33
|
+
jsonrpc: '2.0',
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
-
function sendMethodNotAllowed(res) {
|
|
37
|
-
res.status(405).set('Allow', 'POST').json(jsonRpcError('Method not allowed.'));
|
|
38
|
-
}
|
|
39
36
|
export function resolveHttpServerOptions(overrides = {}) {
|
|
40
37
|
return {
|
|
38
|
+
allowedHosts: overrides.allowedHosts ??
|
|
39
|
+
parseAllowedHosts(process.env.WARM_MCP_ALLOWED_HOSTS || process.env.MCP_ALLOWED_HOSTS),
|
|
41
40
|
host: overrides.host ||
|
|
42
41
|
process.env.WARM_MCP_HTTP_HOST ||
|
|
43
42
|
process.env.MCP_HOST ||
|
|
44
43
|
process.env.HOST ||
|
|
45
44
|
DEFAULT_HTTP_HOST,
|
|
45
|
+
path: normalizePath(overrides.path || process.env.WARM_MCP_HTTP_PATH || process.env.MCP_PATH || DEFAULT_HTTP_PATH),
|
|
46
46
|
port: overrides.port ??
|
|
47
47
|
parsePort(process.env.WARM_MCP_HTTP_PORT || process.env.MCP_PORT || process.env.PORT, DEFAULT_HTTP_PORT),
|
|
48
|
-
path: normalizePath(overrides.path || process.env.WARM_MCP_HTTP_PATH || process.env.MCP_PATH || DEFAULT_HTTP_PATH),
|
|
49
|
-
allowedHosts: overrides.allowedHosts ??
|
|
50
|
-
parseAllowedHosts(process.env.WARM_MCP_ALLOWED_HOSTS || process.env.MCP_ALLOWED_HOSTS),
|
|
51
48
|
};
|
|
52
49
|
}
|
|
50
|
+
function sendJsonMethodNotAllowed(res) {
|
|
51
|
+
res.status(405);
|
|
52
|
+
res.set('Allow', 'POST');
|
|
53
|
+
res.json(jsonRpcError('Method not allowed.'));
|
|
54
|
+
}
|
|
53
55
|
export async function startHttpServer(options = {}) {
|
|
54
56
|
const resolved = resolveHttpServerOptions(options);
|
|
55
57
|
const app = createMcpExpressApp({
|
|
56
|
-
host: resolved.host,
|
|
57
58
|
...(resolved.allowedHosts.length > 0 ? { allowedHosts: resolved.allowedHosts } : {}),
|
|
59
|
+
host: resolved.host,
|
|
58
60
|
});
|
|
59
61
|
app.post(resolved.path, async (req, res) => {
|
|
60
62
|
const server = createWarmServer();
|
|
@@ -85,10 +87,10 @@ export async function startHttpServer(options = {}) {
|
|
|
85
87
|
}
|
|
86
88
|
});
|
|
87
89
|
app.get(resolved.path, (_req, res) => {
|
|
88
|
-
|
|
90
|
+
sendJsonMethodNotAllowed(res);
|
|
89
91
|
});
|
|
90
92
|
app.delete(resolved.path, (_req, res) => {
|
|
91
|
-
|
|
93
|
+
sendJsonMethodNotAllowed(res);
|
|
92
94
|
});
|
|
93
95
|
const listener = await new Promise((resolve, reject) => {
|
|
94
96
|
const httpServer = app.listen(resolved.port, resolved.host, () => {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export
|
|
2
|
+
export declare function startStdioServer(): Promise<import("@modelcontextprotocol/sdk/server/mcp").McpServer>;
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { install } from './install.js';
|
|
3
|
-
import {
|
|
3
|
+
import { startHttpServer } from './http.js';
|
|
4
|
+
import { loadDotEnv } from './load-env.js';
|
|
5
|
+
import { createWarmServer } from './server.js';
|
|
6
|
+
import { AdaptiveStdioTransport } from './stdio.js';
|
|
7
|
+
function isInteractiveSession() {
|
|
8
|
+
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
9
|
+
}
|
|
4
10
|
function printUsage() {
|
|
5
11
|
console.log('');
|
|
6
12
|
console.log(' Warm MCP');
|
|
7
13
|
console.log(' --------');
|
|
8
14
|
console.log('');
|
|
9
|
-
console.log(' warm-mcp
|
|
15
|
+
console.log(' warm-mcp');
|
|
16
|
+
console.log(' Interactive terminal: runs installer');
|
|
17
|
+
console.log(' Non-interactive launch: starts stdio server');
|
|
18
|
+
console.log('');
|
|
19
|
+
console.log(' warm-mcp install [--force] [--no-validate]');
|
|
10
20
|
console.log(' warm-mcp stdio');
|
|
21
|
+
console.log(' warm-mcp http [--host 127.0.0.1] [--port 3000] [--path /mcp]');
|
|
11
22
|
console.log('');
|
|
12
23
|
console.log(' Aliases:');
|
|
13
24
|
console.log(' --server, --stdio Start stdio mode');
|
|
@@ -15,7 +26,7 @@ function printUsage() {
|
|
|
15
26
|
}
|
|
16
27
|
function parseCliArgs(args) {
|
|
17
28
|
const options = {
|
|
18
|
-
command: 'install',
|
|
29
|
+
command: isInteractiveSession() ? 'install' : 'stdio',
|
|
19
30
|
force: false,
|
|
20
31
|
validateApiKey: true,
|
|
21
32
|
};
|
|
@@ -33,6 +44,33 @@ function parseCliArgs(args) {
|
|
|
33
44
|
options.command = 'stdio';
|
|
34
45
|
continue;
|
|
35
46
|
}
|
|
47
|
+
if (arg === 'http') {
|
|
48
|
+
options.command = 'http';
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (arg === '--host') {
|
|
52
|
+
options.httpHost = args[index + 1];
|
|
53
|
+
index += 1;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (arg === '--path') {
|
|
57
|
+
options.httpPath = args[index + 1];
|
|
58
|
+
index += 1;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (arg === '--port') {
|
|
62
|
+
const nextArg = args[index + 1];
|
|
63
|
+
if (!nextArg) {
|
|
64
|
+
throw new Error('Missing value for --port');
|
|
65
|
+
}
|
|
66
|
+
const port = Number.parseInt(nextArg, 10);
|
|
67
|
+
if (!Number.isInteger(port) || port <= 0) {
|
|
68
|
+
throw new Error(`Invalid value for --port: ${nextArg}`);
|
|
69
|
+
}
|
|
70
|
+
options.httpPort = port;
|
|
71
|
+
index += 1;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
36
74
|
if (arg === '--force') {
|
|
37
75
|
options.force = true;
|
|
38
76
|
continue;
|
|
@@ -45,7 +83,14 @@ function parseCliArgs(args) {
|
|
|
45
83
|
}
|
|
46
84
|
return options;
|
|
47
85
|
}
|
|
86
|
+
export async function startStdioServer() {
|
|
87
|
+
const server = createWarmServer();
|
|
88
|
+
const transport = new AdaptiveStdioTransport();
|
|
89
|
+
await server.connect(transport);
|
|
90
|
+
return server;
|
|
91
|
+
}
|
|
48
92
|
async function main() {
|
|
93
|
+
loadDotEnv();
|
|
49
94
|
const options = parseCliArgs(process.argv.slice(2));
|
|
50
95
|
switch (options.command) {
|
|
51
96
|
case 'help':
|
|
@@ -54,6 +99,13 @@ async function main() {
|
|
|
54
99
|
case 'stdio':
|
|
55
100
|
await startStdioServer();
|
|
56
101
|
return;
|
|
102
|
+
case 'http':
|
|
103
|
+
await startHttpServer({
|
|
104
|
+
host: options.httpHost,
|
|
105
|
+
path: options.httpPath,
|
|
106
|
+
port: options.httpPort,
|
|
107
|
+
});
|
|
108
|
+
return;
|
|
57
109
|
case 'install':
|
|
58
110
|
await install({
|
|
59
111
|
force: options.force,
|
|
@@ -66,8 +118,7 @@ try {
|
|
|
66
118
|
await main();
|
|
67
119
|
}
|
|
68
120
|
catch (error) {
|
|
69
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
121
|
+
const message = error instanceof Error ? error.stack || error.message : String(error);
|
|
70
122
|
console.error(message);
|
|
71
|
-
console.error('Run "warm-mcp --help" for usage.');
|
|
72
123
|
process.exit(1);
|
|
73
124
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function loadDotEnv(): void;
|
package/dist/load-env.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { config as loadEnvFile } from 'dotenv';
|
|
5
|
+
let loaded = false;
|
|
6
|
+
export function loadDotEnv() {
|
|
7
|
+
if (loaded) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
loaded = true;
|
|
11
|
+
const candidates = new Set();
|
|
12
|
+
let currentDir = process.cwd();
|
|
13
|
+
while (true) {
|
|
14
|
+
candidates.add(path.join(currentDir, '.env'));
|
|
15
|
+
const parentDir = path.dirname(currentDir);
|
|
16
|
+
if (parentDir === currentDir) {
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
currentDir = parentDir;
|
|
20
|
+
}
|
|
21
|
+
let moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
while (true) {
|
|
23
|
+
candidates.add(path.join(moduleDir, '.env'));
|
|
24
|
+
const parentDir = path.dirname(moduleDir);
|
|
25
|
+
if (parentDir === moduleDir) {
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
moduleDir = parentDir;
|
|
29
|
+
}
|
|
30
|
+
for (const candidate of candidates) {
|
|
31
|
+
if (fs.existsSync(candidate)) {
|
|
32
|
+
loadEnvFile({ path: candidate, override: false });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|