@cardor/heimdall-mcp 0.1.0 → 0.1.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/dist/cli.js CHANGED
@@ -5,38 +5,79 @@ import {
5
5
  // src/cli.ts
6
6
  import { Command } from "commander";
7
7
  import pc from "picocolors";
8
+ function log(msg, ...args) {
9
+ process.stderr.write(`[heimdall-mcp] ${msg} ${args.map((a) => JSON.stringify(a)).join(" ")}
10
+ `);
11
+ }
8
12
  var program = new Command();
9
13
  program.name("heimdall-mcp").description("Transparent MCP proxy with tracing and configurable storage").version("0.1.0");
10
- program.command("start", { isDefault: true }).description("Start the proxy (default command)").option("--store <url>", "Storage connection string (sqlite://, postgres://, mysql://)").option("--target <url>", "Target server URL for http/sse outbound").option("--in <transport>", "Inbound transport: stdio | http | sse", "stdio").option("--in-port <port>", "Port for inbound http/sse", parseInt).option("--out <transport>", "Outbound transport: stdio | http | sse", "stdio").option("--out-port <port>", "Port for outbound http/sse", parseInt).allowUnknownOption(true).action(async (opts, cmd) => {
11
- const rawArgs = cmd.args;
14
+ program.command("start", { isDefault: true }).description("Start the proxy (default command)").option("--store <url>", "Storage connection string (sqlite://, postgres://, mysql://)").option("--target <url>", "Target server URL for http/sse outbound").option("--in <transport>", "Inbound transport: stdio | http | sse", "stdio").option("--in-port <port>", "Port for inbound http/sse", parseInt).option("--out <transport>", "Outbound transport: stdio | http | sse", "stdio").option("--out-port <port>", "Port for outbound http/sse", parseInt).option("--debug", "Write verbose logs to stderr").allowUnknownOption(true).action(async (opts) => {
15
+ const debug = Boolean(opts.debug);
16
+ const dashDashIdx = process.argv.indexOf("--");
17
+ const subArgs = dashDashIdx >= 0 ? process.argv.slice(dashDashIdx + 1) : [];
18
+ const [subCommand, ...subCommandArgs] = subArgs;
19
+ if (debug) {
20
+ log("starting with config", {
21
+ store: opts.store,
22
+ inTransport: opts.in,
23
+ outTransport: opts.out,
24
+ target: opts.target ?? null,
25
+ subCommand: subCommand ?? null,
26
+ subCommandArgs
27
+ });
28
+ }
12
29
  if (!opts.store) {
13
- console.error(pc.red("Error: --store is required"));
30
+ process.stderr.write(pc.red("Error: --store is required\n"));
14
31
  process.exit(1);
15
32
  }
16
33
  const inTransport = opts.in;
17
34
  const outTransport = opts.out;
18
- const separatorIdx = rawArgs.indexOf("--");
19
- const subArgs = separatorIdx >= 0 ? rawArgs.slice(separatorIdx + 1) : [];
20
- const [subCommand, ...subCommandArgs] = subArgs;
21
35
  const builder = ProxyBuilder.create().inbound({ transport: inTransport, port: opts.inPort }).store(opts.store);
22
36
  if (outTransport === "stdio") {
23
37
  if (!subCommand) {
24
- console.error(pc.red("Error: stdio outbound requires a command after --"));
38
+ process.stderr.write(pc.red("Error: stdio outbound requires a command after --\n"));
39
+ process.stderr.write(` Received process.argv: ${JSON.stringify(process.argv)}
40
+ `);
25
41
  process.exit(1);
26
42
  }
27
43
  builder.outbound({ transport: "stdio", command: subCommand, args: subCommandArgs });
28
44
  } else {
29
45
  if (!opts.target) {
30
- console.error(pc.red("Error: http/sse outbound requires --target <url>"));
46
+ process.stderr.write(pc.red("Error: http/sse outbound requires --target <url>\n"));
31
47
  process.exit(1);
32
48
  }
33
49
  builder.outbound({ transport: outTransport, url: opts.target });
34
50
  }
35
- const proxy = await builder.build();
36
- process.on("SIGINT", () => proxy.stop().then(() => process.exit(0)));
37
- process.on("SIGTERM", () => proxy.stop().then(() => process.exit(0)));
38
- proxy.on("error", (err) => console.error(pc.red("[proxy error]"), err));
51
+ let proxy;
52
+ try {
53
+ proxy = await builder.build();
54
+ } catch (err) {
55
+ process.stderr.write(pc.red(`Error building proxy: ${err}
56
+ `));
57
+ if (debug && err instanceof Error) process.stderr.write(err.stack + "\n");
58
+ process.exit(1);
59
+ }
60
+ if (debug) log("proxy built, starting...");
61
+ process.on("SIGINT", () => {
62
+ if (debug) log("SIGINT received, stopping");
63
+ proxy.stop().then(() => process.exit(0));
64
+ });
65
+ process.on("SIGTERM", () => {
66
+ if (debug) log("SIGTERM received, stopping");
67
+ proxy.stop().then(() => process.exit(0));
68
+ });
69
+ process.on("uncaughtException", (err) => {
70
+ process.stderr.write(pc.red(`[heimdall-mcp] uncaught: ${err}
71
+ `));
72
+ if (debug) process.stderr.write(err.stack + "\n");
73
+ });
74
+ proxy.on("error", (err) => {
75
+ process.stderr.write(pc.red(`[heimdall-mcp] proxy error: ${err}
76
+ `));
77
+ if (debug && err instanceof Error) process.stderr.write(err.stack + "\n");
78
+ });
39
79
  await proxy.start();
80
+ if (debug) log("proxy running");
40
81
  });
41
82
  program.parse();
42
83
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { Command } from 'commander'\nimport pc from 'picocolors'\n\nimport { ProxyBuilder } from '@/proxy/ProxyBuilder'\n\nconst program = new Command()\n\nprogram\n .name('heimdall-mcp')\n .description('Transparent MCP proxy with tracing and configurable storage')\n .version('0.1.0')\n\nprogram\n .command('start', { isDefault: true })\n .description('Start the proxy (default command)')\n .option('--store <url>', 'Storage connection string (sqlite://, postgres://, mysql://)')\n .option('--target <url>', 'Target server URL for http/sse outbound')\n .option('--in <transport>', 'Inbound transport: stdio | http | sse', 'stdio')\n .option('--in-port <port>', 'Port for inbound http/sse', parseInt)\n .option('--out <transport>', 'Outbound transport: stdio | http | sse', 'stdio')\n .option('--out-port <port>', 'Port for outbound http/sse', parseInt)\n .allowUnknownOption(true)\n .action(async (opts, cmd) => {\n const rawArgs = cmd.args\n\n if (!opts.store) {\n console.error(pc.red('Error: --store is required'))\n process.exit(1)\n }\n\n const inTransport = opts.in as 'stdio' | 'http' | 'sse'\n const outTransport = opts.out as 'stdio' | 'http' | 'sse'\n\n // remaining args after '--' are the subprocess command\n const separatorIdx = rawArgs.indexOf('--')\n const subArgs = separatorIdx >= 0 ? rawArgs.slice(separatorIdx + 1) : []\n const [subCommand, ...subCommandArgs] = subArgs\n\n const builder = ProxyBuilder.create()\n .inbound({ transport: inTransport, port: opts.inPort })\n .store(opts.store)\n\n if (outTransport === 'stdio') {\n if (!subCommand) {\n console.error(pc.red('Error: stdio outbound requires a command after --'))\n process.exit(1)\n }\n builder.outbound({ transport: 'stdio', command: subCommand, args: subCommandArgs })\n } else {\n if (!opts.target) {\n console.error(pc.red('Error: http/sse outbound requires --target <url>'))\n process.exit(1)\n }\n builder.outbound({ transport: outTransport, url: opts.target })\n }\n\n const proxy = await builder.build()\n\n process.on('SIGINT', () => proxy.stop().then(() => process.exit(0)))\n process.on('SIGTERM', () => proxy.stop().then(() => process.exit(0)))\n proxy.on('error', (err) => console.error(pc.red('[proxy error]'), err))\n\n await proxy.start()\n })\n\nprogram.parse()\n"],"mappings":";;;;;AAAA,SAAS,eAAe;AACxB,OAAO,QAAQ;AAIf,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,cAAc,EACnB,YAAY,6DAA6D,EACzE,QAAQ,OAAO;AAElB,QACG,QAAQ,SAAS,EAAE,WAAW,KAAK,CAAC,EACpC,YAAY,mCAAmC,EAC/C,OAAO,iBAAiB,8DAA8D,EACtF,OAAO,kBAAkB,yCAAyC,EAClE,OAAO,oBAAoB,yCAAyC,OAAO,EAC3E,OAAO,oBAAoB,6BAA6B,QAAQ,EAChE,OAAO,qBAAqB,0CAA0C,OAAO,EAC7E,OAAO,qBAAqB,8BAA8B,QAAQ,EAClE,mBAAmB,IAAI,EACvB,OAAO,OAAO,MAAM,QAAQ;AAC3B,QAAM,UAAU,IAAI;AAEpB,MAAI,CAAC,KAAK,OAAO;AACf,YAAQ,MAAM,GAAG,IAAI,4BAA4B,CAAC;AAClD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,cAAc,KAAK;AACzB,QAAM,eAAe,KAAK;AAG1B,QAAM,eAAe,QAAQ,QAAQ,IAAI;AACzC,QAAM,UAAU,gBAAgB,IAAI,QAAQ,MAAM,eAAe,CAAC,IAAI,CAAC;AACvE,QAAM,CAAC,YAAY,GAAG,cAAc,IAAI;AAExC,QAAM,UAAU,aAAa,OAAO,EACjC,QAAQ,EAAE,WAAW,aAAa,MAAM,KAAK,OAAO,CAAC,EACrD,MAAM,KAAK,KAAK;AAEnB,MAAI,iBAAiB,SAAS;AAC5B,QAAI,CAAC,YAAY;AACf,cAAQ,MAAM,GAAG,IAAI,mDAAmD,CAAC;AACzE,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,SAAS,EAAE,WAAW,SAAS,SAAS,YAAY,MAAM,eAAe,CAAC;AAAA,EACpF,OAAO;AACL,QAAI,CAAC,KAAK,QAAQ;AAChB,cAAQ,MAAM,GAAG,IAAI,kDAAkD,CAAC;AACxE,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,SAAS,EAAE,WAAW,cAAc,KAAK,KAAK,OAAO,CAAC;AAAA,EAChE;AAEA,QAAM,QAAQ,MAAM,QAAQ,MAAM;AAElC,UAAQ,GAAG,UAAW,MAAM,MAAM,KAAK,EAAE,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC,CAAC;AACpE,UAAQ,GAAG,WAAW,MAAM,MAAM,KAAK,EAAE,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC,CAAC;AACpE,QAAM,GAAG,SAAS,CAAC,QAAQ,QAAQ,MAAM,GAAG,IAAI,eAAe,GAAG,GAAG,CAAC;AAEtE,QAAM,MAAM,MAAM;AACpB,CAAC;AAEH,QAAQ,MAAM;","names":[]}
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { Command } from 'commander'\nimport pc from 'picocolors'\n\nimport { ProxyBuilder } from '@/proxy/ProxyBuilder'\n\nfunction log(msg: string, ...args: unknown[]) {\n process.stderr.write(`[heimdall-mcp] ${msg} ${args.map((a) => JSON.stringify(a)).join(' ')}\\n`)\n}\n\nconst program = new Command()\n\nprogram\n .name('heimdall-mcp')\n .description('Transparent MCP proxy with tracing and configurable storage')\n .version('0.1.0')\n\nprogram\n .command('start', { isDefault: true })\n .description('Start the proxy (default command)')\n .option('--store <url>', 'Storage connection string (sqlite://, postgres://, mysql://)')\n .option('--target <url>', 'Target server URL for http/sse outbound')\n .option('--in <transport>', 'Inbound transport: stdio | http | sse', 'stdio')\n .option('--in-port <port>', 'Port for inbound http/sse', parseInt)\n .option('--out <transport>', 'Outbound transport: stdio | http | sse', 'stdio')\n .option('--out-port <port>', 'Port for outbound http/sse', parseInt)\n .option('--debug', 'Write verbose logs to stderr')\n .allowUnknownOption(true)\n .action(async (opts) => {\n const debug = Boolean(opts.debug)\n\n // Commander may strip '--' from cmd.args — parse process.argv directly to be safe\n const dashDashIdx = process.argv.indexOf('--')\n const subArgs = dashDashIdx >= 0 ? process.argv.slice(dashDashIdx + 1) : []\n const [subCommand, ...subCommandArgs] = subArgs\n\n if (debug) {\n log('starting with config', {\n store: opts.store,\n inTransport: opts.in,\n outTransport: opts.out,\n target: opts.target ?? null,\n subCommand: subCommand ?? null,\n subCommandArgs,\n })\n }\n\n if (!opts.store) {\n process.stderr.write(pc.red('Error: --store is required\\n'))\n process.exit(1)\n }\n\n const inTransport = opts.in as 'stdio' | 'http' | 'sse'\n const outTransport = opts.out as 'stdio' | 'http' | 'sse'\n\n const builder = ProxyBuilder.create()\n .inbound({ transport: inTransport, port: opts.inPort })\n .store(opts.store)\n\n if (outTransport === 'stdio') {\n if (!subCommand) {\n process.stderr.write(pc.red('Error: stdio outbound requires a command after --\\n'))\n process.stderr.write(` Received process.argv: ${JSON.stringify(process.argv)}\\n`)\n process.exit(1)\n }\n builder.outbound({ transport: 'stdio', command: subCommand, args: subCommandArgs })\n } else {\n if (!opts.target) {\n process.stderr.write(pc.red('Error: http/sse outbound requires --target <url>\\n'))\n process.exit(1)\n }\n builder.outbound({ transport: outTransport, url: opts.target })\n }\n\n let proxy\n try {\n proxy = await builder.build()\n } catch (err) {\n process.stderr.write(pc.red(`Error building proxy: ${err}\\n`))\n if (debug && err instanceof Error) process.stderr.write(err.stack + '\\n')\n process.exit(1)\n }\n\n if (debug) log('proxy built, starting...')\n\n process.on('SIGINT', () => { if (debug) log('SIGINT received, stopping'); proxy.stop().then(() => process.exit(0)) })\n process.on('SIGTERM', () => { if (debug) log('SIGTERM received, stopping'); proxy.stop().then(() => process.exit(0)) })\n\n process.on('uncaughtException', (err) => {\n process.stderr.write(pc.red(`[heimdall-mcp] uncaught: ${err}\\n`))\n if (debug) process.stderr.write(err.stack + '\\n')\n })\n\n proxy.on('error', (err) => {\n process.stderr.write(pc.red(`[heimdall-mcp] proxy error: ${err}\\n`))\n if (debug && err instanceof Error) process.stderr.write(err.stack + '\\n')\n })\n\n await proxy.start()\n if (debug) log('proxy running')\n })\n\nprogram.parse()\n"],"mappings":";;;;;AAAA,SAAS,eAAe;AACxB,OAAO,QAAQ;AAIf,SAAS,IAAI,QAAgB,MAAiB;AAC5C,UAAQ,OAAO,MAAM,kBAAkB,GAAG,IAAI,KAAK,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC;AAAA,CAAI;AAChG;AAEA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,cAAc,EACnB,YAAY,6DAA6D,EACzE,QAAQ,OAAO;AAElB,QACG,QAAQ,SAAS,EAAE,WAAW,KAAK,CAAC,EACpC,YAAY,mCAAmC,EAC/C,OAAO,iBAAsB,8DAA8D,EAC3F,OAAO,kBAAsB,yCAAyC,EACtE,OAAO,oBAAsB,yCAAyC,OAAO,EAC7E,OAAO,oBAAsB,6BAA6B,QAAQ,EAClE,OAAO,qBAAsB,0CAA0C,OAAO,EAC9E,OAAO,qBAAsB,8BAA8B,QAAQ,EACnE,OAAO,WAAsB,8BAA8B,EAC3D,mBAAmB,IAAI,EACvB,OAAO,OAAO,SAAS;AACtB,QAAM,QAAQ,QAAQ,KAAK,KAAK;AAGhC,QAAM,cAAc,QAAQ,KAAK,QAAQ,IAAI;AAC7C,QAAM,UAAU,eAAe,IAAI,QAAQ,KAAK,MAAM,cAAc,CAAC,IAAI,CAAC;AAC1E,QAAM,CAAC,YAAY,GAAG,cAAc,IAAI;AAExC,MAAI,OAAO;AACT,QAAI,wBAAwB;AAAA,MAC1B,OAAc,KAAK;AAAA,MACnB,aAAc,KAAK;AAAA,MACnB,cAAc,KAAK;AAAA,MACnB,QAAc,KAAK,UAAU;AAAA,MAC7B,YAAc,cAAc;AAAA,MAC5B;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,CAAC,KAAK,OAAO;AACf,YAAQ,OAAO,MAAM,GAAG,IAAI,8BAA8B,CAAC;AAC3D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,cAAe,KAAK;AAC1B,QAAM,eAAe,KAAK;AAE1B,QAAM,UAAU,aAAa,OAAO,EACjC,QAAQ,EAAE,WAAW,aAAa,MAAM,KAAK,OAAO,CAAC,EACrD,MAAM,KAAK,KAAK;AAEnB,MAAI,iBAAiB,SAAS;AAC5B,QAAI,CAAC,YAAY;AACf,cAAQ,OAAO,MAAM,GAAG,IAAI,qDAAqD,CAAC;AAClF,cAAQ,OAAO,MAAM,4BAA4B,KAAK,UAAU,QAAQ,IAAI,CAAC;AAAA,CAAI;AACjF,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,SAAS,EAAE,WAAW,SAAS,SAAS,YAAY,MAAM,eAAe,CAAC;AAAA,EACpF,OAAO;AACL,QAAI,CAAC,KAAK,QAAQ;AAChB,cAAQ,OAAO,MAAM,GAAG,IAAI,oDAAoD,CAAC;AACjF,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,SAAS,EAAE,WAAW,cAAc,KAAK,KAAK,OAAO,CAAC;AAAA,EAChE;AAEA,MAAI;AACJ,MAAI;AACF,YAAQ,MAAM,QAAQ,MAAM;AAAA,EAC9B,SAAS,KAAK;AACZ,YAAQ,OAAO,MAAM,GAAG,IAAI,yBAAyB,GAAG;AAAA,CAAI,CAAC;AAC7D,QAAI,SAAS,eAAe,MAAO,SAAQ,OAAO,MAAM,IAAI,QAAQ,IAAI;AACxE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAO,KAAI,0BAA0B;AAEzC,UAAQ,GAAG,UAAW,MAAM;AAAE,QAAI,MAAO,KAAI,2BAA2B;AAAG,UAAM,KAAK,EAAE,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC;AAAA,EAAE,CAAC;AACrH,UAAQ,GAAG,WAAW,MAAM;AAAE,QAAI,MAAO,KAAI,4BAA4B;AAAG,UAAM,KAAK,EAAE,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC;AAAA,EAAE,CAAC;AAEtH,UAAQ,GAAG,qBAAqB,CAAC,QAAQ;AACvC,YAAQ,OAAO,MAAM,GAAG,IAAI,4BAA4B,GAAG;AAAA,CAAI,CAAC;AAChE,QAAI,MAAO,SAAQ,OAAO,MAAM,IAAI,QAAQ,IAAI;AAAA,EAClD,CAAC;AAED,QAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,YAAQ,OAAO,MAAM,GAAG,IAAI,+BAA+B,GAAG;AAAA,CAAI,CAAC;AACnE,QAAI,SAAS,eAAe,MAAO,SAAQ,OAAO,MAAM,IAAI,QAAQ,IAAI;AAAA,EAC1E,CAAC;AAED,QAAM,MAAM,MAAM;AAClB,MAAI,MAAO,KAAI,eAAe;AAChC,CAAC;AAEH,QAAQ,MAAM;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardor/heimdall-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Transparent MCP proxy with OpenTelemetry tracing and configurable storage",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -14,7 +14,10 @@
14
14
  "bin": {
15
15
  "heimdall-mcp": "./bin/heimdall-mcp.js"
16
16
  },
17
- "files": ["bin", "dist"],
17
+ "files": [
18
+ "bin",
19
+ "dist"
20
+ ],
18
21
  "scripts": {
19
22
  "build": "tsup",
20
23
  "dev": "tsup --watch",
@@ -23,7 +26,13 @@
23
26
  "prepublishOnly": "npm run build && npm test",
24
27
  "prepare": "husky"
25
28
  },
26
- "keywords": ["mcp", "proxy", "opentelemetry", "tracing", "observability"],
29
+ "keywords": [
30
+ "mcp",
31
+ "proxy",
32
+ "opentelemetry",
33
+ "tracing",
34
+ "observability"
35
+ ],
27
36
  "author": "",
28
37
  "license": "MIT",
29
38
  "dependencies": {
@@ -50,4 +59,4 @@
50
59
  "engines": {
51
60
  "node": ">=22.5.0"
52
61
  }
53
- }
62
+ }