@agimon-ai/mcp-proxy 0.4.4 → 0.4.5

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.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const require_src = require('./src-G1hs2GLZ.cjs');
2
+ const require_src = require('./src-h-U3G1P0.cjs');
3
3
  let node_crypto = require("node:crypto");
4
4
  let node_fs_promises = require("node:fs/promises");
5
5
  let node_path = require("node:path");
@@ -113,6 +113,169 @@ const initCommand = new commander.Command("init").description("Initialize MCP co
113
113
  }
114
114
  });
115
115
 
116
+ //#endregion
117
+ //#region src/commands/prestart-http.ts
118
+ /**
119
+ * Prestart HTTP Command
120
+ *
121
+ * Starts an mcp-proxy HTTP runtime in the background, waits until it is healthy,
122
+ * and then exits so the runtime can be reused by other commands.
123
+ */
124
+ const WORKSPACE_MARKERS = [
125
+ "pnpm-workspace.yaml",
126
+ "nx.json",
127
+ ".git"
128
+ ];
129
+ const DEFAULT_HOST$1 = "localhost";
130
+ const DEFAULT_TIMEOUT_MS = 12e4;
131
+ const POLL_INTERVAL_MS = 250;
132
+ function resolveWorkspaceRoot(startPath = process.env.PROJECT_PATH || process.cwd()) {
133
+ let current = node_path.default.resolve(startPath);
134
+ while (true) {
135
+ for (const marker of WORKSPACE_MARKERS) if ((0, node_fs.existsSync)(node_path.default.join(current, marker))) return current;
136
+ const parent = node_path.default.dirname(current);
137
+ if (parent === current) return process.cwd();
138
+ current = parent;
139
+ }
140
+ }
141
+ function resolveSiblingRegistryPath(registryPath, fileName) {
142
+ if (!registryPath) return;
143
+ const resolved = node_path.default.resolve(registryPath);
144
+ if (node_path.default.extname(resolved) === ".json") return node_path.default.join(node_path.default.dirname(resolved), fileName);
145
+ return node_path.default.join(resolved, fileName);
146
+ }
147
+ function buildCliCandidates() {
148
+ const __filename$1 = (0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href);
149
+ const __dirname$1 = node_path.default.dirname(__filename$1);
150
+ const distCandidates = [
151
+ node_path.default.resolve(__dirname$1, "cli.mjs"),
152
+ node_path.default.resolve(__dirname$1, "..", "dist", "cli.mjs"),
153
+ node_path.default.resolve(__dirname$1, "..", "..", "dist", "cli.mjs")
154
+ ];
155
+ const srcCandidates = [node_path.default.resolve(__dirname$1, "..", "cli.ts"), node_path.default.resolve(__dirname$1, "..", "..", "src", "cli.ts")];
156
+ for (const candidate of distCandidates) if ((0, node_fs.existsSync)(candidate)) return {
157
+ command: process.execPath,
158
+ args: [candidate]
159
+ };
160
+ for (const candidate of srcCandidates) if ((0, node_fs.existsSync)(candidate)) return {
161
+ command: process.execPath,
162
+ args: [
163
+ "--import",
164
+ "tsx",
165
+ candidate
166
+ ]
167
+ };
168
+ throw new Error("Unable to locate mcp-proxy CLI entrypoint");
169
+ }
170
+ function parseTimeoutMs(value) {
171
+ if (!value) return DEFAULT_TIMEOUT_MS;
172
+ const parsed = Number.parseInt(value, 10);
173
+ if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`Invalid timeout value: ${value}`);
174
+ return parsed;
175
+ }
176
+ async function waitForFile(filePath, timeoutMs) {
177
+ const deadline = Date.now() + timeoutMs;
178
+ while (Date.now() < deadline) {
179
+ try {
180
+ await (0, node_fs_promises.access)(filePath);
181
+ return;
182
+ } catch {}
183
+ await new Promise((resolve$2) => setTimeout(resolve$2, POLL_INTERVAL_MS));
184
+ }
185
+ throw new Error(`Timed out waiting for runtime state file: ${filePath}`);
186
+ }
187
+ async function waitForHealthyRuntime(serverId, timeoutMs) {
188
+ const runtimeStateService = new require_src.RuntimeStateService();
189
+ const deadline = Date.now() + timeoutMs;
190
+ while (Date.now() < deadline) {
191
+ const record = await runtimeStateService.read(serverId);
192
+ if (record) {
193
+ const healthUrl = `http://${record.host}:${record.port}/health`;
194
+ try {
195
+ const response = await fetch(healthUrl);
196
+ if (response.ok) {
197
+ const payload = await response.json().catch(() => null);
198
+ if (!payload || payload.transport === "http") return {
199
+ host: record.host,
200
+ port: record.port
201
+ };
202
+ }
203
+ } catch {}
204
+ }
205
+ await new Promise((resolve$2) => setTimeout(resolve$2, POLL_INTERVAL_MS));
206
+ }
207
+ throw new Error(`Timed out waiting for HTTP runtime '${serverId}' to become healthy`);
208
+ }
209
+ function spawnBackgroundRuntime(args, env, cwd) {
210
+ const { command, args: baseArgs } = buildCliCandidates();
211
+ const child = (0, node_child_process.spawn)(command, [...baseArgs, ...args], {
212
+ detached: true,
213
+ cwd,
214
+ stdio: "ignore",
215
+ env
216
+ });
217
+ child.unref();
218
+ return child;
219
+ }
220
+ async function prestartHttpRuntime(options) {
221
+ const serverId = options.id || require_src.generateServerId();
222
+ const timeoutMs = parseTimeoutMs(options.timeoutMs);
223
+ const registryPath = options.registryPath || options.registryDir;
224
+ const workspaceRoot = resolveWorkspaceRoot();
225
+ const childEnv = {
226
+ ...process.env,
227
+ ...registryPath ? {
228
+ PORT_REGISTRY_PATH: registryPath,
229
+ PROCESS_REGISTRY_PATH: resolveSiblingRegistryPath(registryPath, "processes.json")
230
+ } : {}
231
+ };
232
+ const child = spawnBackgroundRuntime([
233
+ "mcp-serve",
234
+ "--type",
235
+ "http",
236
+ "--id",
237
+ serverId,
238
+ "--host",
239
+ options.host || DEFAULT_HOST$1,
240
+ ...options.port !== void 0 ? ["--port", String(options.port)] : [],
241
+ ...options.config ? ["--config", options.config] : [],
242
+ ...options.cache === false ? ["--no-cache"] : [],
243
+ ...options.definitionsCache ? ["--definitions-cache", options.definitionsCache] : [],
244
+ ...options.clearDefinitionsCache ? ["--clear-definitions-cache"] : [],
245
+ "--proxy-mode",
246
+ options.proxyMode
247
+ ], childEnv, workspaceRoot);
248
+ const childExit = new Promise((_, reject) => {
249
+ child.once("exit", (code, signal) => {
250
+ reject(/* @__PURE__ */ new Error(`Background runtime exited before becoming healthy (code=${code ?? "null"}, signal=${signal ?? "null"})`));
251
+ });
252
+ });
253
+ const runtimeFile = node_path.default.join(require_src.RuntimeStateService.getDefaultRuntimeDir(), `${serverId}.runtime.json`);
254
+ try {
255
+ await Promise.race([waitForFile(runtimeFile, timeoutMs), childExit]);
256
+ const { host, port } = await Promise.race([waitForHealthyRuntime(serverId, timeoutMs), childExit]);
257
+ return {
258
+ host,
259
+ port,
260
+ serverId,
261
+ workspaceRoot
262
+ };
263
+ } catch (error) {
264
+ throw new Error(`Failed to prestart HTTP runtime '${serverId}': ${error instanceof Error ? error.message : String(error)}`);
265
+ }
266
+ }
267
+ const prestartHttpCommand = new commander.Command("prestart-http").description("Start an mcp-proxy HTTP runtime in the background and wait until it is healthy").option("--id <id>", "Server identifier to assign to the runtime").option("--host <host>", "Host to bind to", DEFAULT_HOST$1).option("-p, --port <port>", "Preferred HTTP port for the runtime", (value) => Number.parseInt(value, 10)).option("-c, --config <path>", "Path to MCP server configuration file").option("--no-cache", "Disable configuration caching, always reload from config file").option("--definitions-cache <path>", "Path to prefetched tool/prompt/skill definitions cache file").option("--clear-definitions-cache", "Delete definitions cache before startup", false).option("--proxy-mode <mode>", "How mcp-proxy exposes downstream tools: meta, flat, or search", "meta").option("--registry-path <path>", "Custom registry path or directory for service discovery").option("--registry-dir <path>", "Custom registry directory for service discovery").option("--timeout-ms <ms>", "How long to wait for the runtime to become healthy", String(DEFAULT_TIMEOUT_MS)).action(async (options) => {
268
+ try {
269
+ const { host, port, serverId, workspaceRoot } = await prestartHttpRuntime(options);
270
+ process.stdout.write(`mcp-proxy HTTP runtime ready at http://${host}:${port} (${serverId})\n`);
271
+ process.stdout.write(`runtimeId=${serverId}\n`);
272
+ process.stdout.write(`runtimeUrl=http://${host}:${port}\n`);
273
+ process.stdout.write(`workspaceRoot=${workspaceRoot}\n`);
274
+ } catch (error) {
275
+ throw new Error(`Failed to prestart HTTP runtime '${options.id || "generated-server-id"}': ${error instanceof Error ? error.message : String(error)}`);
276
+ }
277
+ });
278
+
116
279
  //#endregion
117
280
  //#region src/commands/mcp-serve.ts
118
281
  /**
@@ -141,7 +304,7 @@ const CONFIG_FILE_NAMES = [
141
304
  "mcp-config.json"
142
305
  ];
143
306
  const MCP_ENDPOINT_PATH = "/mcp";
144
- const DEFAULT_HOST$1 = "localhost";
307
+ const DEFAULT_HOST = "localhost";
145
308
  const TRANSPORT_TYPE_STDIO = "stdio";
146
309
  const TRANSPORT_TYPE_HTTP = "http";
147
310
  const TRANSPORT_TYPE_SSE = "sse";
@@ -149,10 +312,14 @@ const TRANSPORT_TYPE_STDIO_HTTP = "stdio-http";
149
312
  const RUNTIME_TRANSPORT = TRANSPORT_TYPE_HTTP;
150
313
  const PORT_REGISTRY_SERVICE_HTTP = "mcp-proxy-http";
151
314
  const PORT_REGISTRY_SERVICE_TYPE = "service";
152
- const PORT_REGISTRY_REPOSITORY_PATH = process.cwd();
315
+ function getWorkspaceRoot() {
316
+ return resolveWorkspaceRoot();
317
+ }
153
318
  const PROCESS_REGISTRY_SERVICE_HTTP = "mcp-proxy-http";
154
319
  const PROCESS_REGISTRY_SERVICE_TYPE = "service";
155
- const PROCESS_REGISTRY_REPOSITORY_PATH = process.cwd();
320
+ function getRegistryRepositoryPath() {
321
+ return getWorkspaceRoot();
322
+ }
156
323
  function toErrorMessage$9(error) {
157
324
  return error instanceof Error ? error.message : String(error);
158
325
  }
@@ -213,7 +380,7 @@ function createTransportConfig(options, mode) {
213
380
  return {
214
381
  mode,
215
382
  port: options.port ?? (Number.isFinite(envPort) ? envPort : void 0),
216
- host: options.host || process.env.MCP_HOST || DEFAULT_HOST$1
383
+ host: options.host || process.env.MCP_HOST || DEFAULT_HOST
217
384
  };
218
385
  }
219
386
  function createServerOptions(options, resolvedConfigPath, serverId) {
@@ -234,7 +401,7 @@ function formatStartError(type, host, port, error) {
234
401
  function createRuntimeRecord(serverId, config, port, shutdownToken, configPath) {
235
402
  return {
236
403
  serverId,
237
- host: config.host ?? DEFAULT_HOST$1,
404
+ host: config.host ?? DEFAULT_HOST,
238
405
  port,
239
406
  transport: RUNTIME_TRANSPORT,
240
407
  shutdownToken,
@@ -258,7 +425,7 @@ async function createPortRegistryLease(serviceName, host, preferredPort, serverI
258
425
  } : __agimon_ai_foundation_port_registry.DEFAULT_PORT_RANGE) {
259
426
  const portRegistry = createPortRegistryService();
260
427
  const result = await portRegistry.reservePort({
261
- repositoryPath: PORT_REGISTRY_REPOSITORY_PATH,
428
+ repositoryPath: getRegistryRepositoryPath(),
262
429
  serviceName,
263
430
  serviceType: PORT_REGISTRY_SERVICE_TYPE,
264
431
  environment: getRegistryEnvironment(),
@@ -284,7 +451,7 @@ async function createPortRegistryLease(serviceName, host, preferredPort, serverI
284
451
  if (released) return;
285
452
  released = true;
286
453
  const releaseResult = await portRegistry.releasePort({
287
- repositoryPath: PORT_REGISTRY_REPOSITORY_PATH,
454
+ repositoryPath: getRegistryRepositoryPath(),
288
455
  serviceName,
289
456
  serviceType: PORT_REGISTRY_SERVICE_TYPE,
290
457
  pid: process.pid,
@@ -298,7 +465,7 @@ async function createPortRegistryLease(serviceName, host, preferredPort, serverI
298
465
  async function createProcessRegistryLease(serviceName, host, port, serverId, transport, configPath) {
299
466
  const processRegistry = createProcessRegistryService();
300
467
  const result = await processRegistry.registerProcess({
301
- repositoryPath: PROCESS_REGISTRY_REPOSITORY_PATH,
468
+ repositoryPath: getRegistryRepositoryPath(),
302
469
  serviceName,
303
470
  serviceType: PROCESS_REGISTRY_SERVICE_TYPE,
304
471
  environment: getRegistryEnvironment(),
@@ -319,7 +486,7 @@ async function createProcessRegistryLease(serviceName, host, port, serverId, tra
319
486
  if (released) return;
320
487
  released = true;
321
488
  const releaseResult = await processRegistry.releaseProcess({
322
- repositoryPath: PROCESS_REGISTRY_REPOSITORY_PATH,
489
+ repositoryPath: getRegistryRepositoryPath(),
323
490
  serviceName,
324
491
  serviceType: PROCESS_REGISTRY_SERVICE_TYPE,
325
492
  pid: process.pid,
@@ -420,13 +587,13 @@ async function createAndStartHttpRuntime(serverOptions, config, resolvedConfigPa
420
587
  min: requestedPort,
421
588
  max: requestedPort
422
589
  } : __agimon_ai_foundation_port_registry.DEFAULT_PORT_RANGE;
423
- const portLease = await createPortRegistryLease(PORT_REGISTRY_SERVICE_HTTP, config.host ?? DEFAULT_HOST$1, requestedPort, runtimeServerId, TRANSPORT_TYPE_HTTP, resolvedConfigPath, portRange);
590
+ const portLease = await createPortRegistryLease(PORT_REGISTRY_SERVICE_HTTP, config.host ?? DEFAULT_HOST, requestedPort, runtimeServerId, TRANSPORT_TYPE_HTTP, resolvedConfigPath, portRange);
424
591
  const runtimePort = portLease.port;
425
592
  const runtimeConfig = {
426
593
  ...config,
427
594
  port: runtimePort
428
595
  };
429
- const processLease = await createProcessRegistryLease(PROCESS_REGISTRY_SERVICE_HTTP, runtimeConfig.host ?? DEFAULT_HOST$1, runtimePort, runtimeServerId, TRANSPORT_TYPE_HTTP, resolvedConfigPath);
596
+ const processLease = await createProcessRegistryLease(PROCESS_REGISTRY_SERVICE_HTTP, runtimeConfig.host ?? DEFAULT_HOST, runtimePort, runtimeServerId, TRANSPORT_TYPE_HTTP, resolvedConfigPath);
430
597
  let releasePort = async () => {
431
598
  await releasePortLease(portLease ?? null);
432
599
  releasePort = async () => void 0;
@@ -494,20 +661,29 @@ async function startSseTransport(serverOptions, config) {
494
661
  throw new Error(`Failed to start SSE transport: ${toErrorMessage$9(error)}`);
495
662
  }
496
663
  }
497
- async function resolveStdioHttpEndpoint(config) {
498
- if (config.port !== void 0) return new URL(`http://${config.host ?? DEFAULT_HOST$1}:${config.port}${MCP_ENDPOINT_PATH}`);
664
+ async function resolveStdioHttpEndpoint(config, options, resolvedConfigPath) {
665
+ const repositoryPath = getRegistryRepositoryPath();
666
+ if (config.port !== void 0) return new URL(`http://${config.host ?? DEFAULT_HOST}:${config.port}${MCP_ENDPOINT_PATH}`);
499
667
  const result = await createPortRegistryService().getPort({
500
- repositoryPath: PORT_REGISTRY_REPOSITORY_PATH,
668
+ repositoryPath,
501
669
  serviceName: PORT_REGISTRY_SERVICE_HTTP,
502
670
  serviceType: PORT_REGISTRY_SERVICE_TYPE,
503
671
  environment: getRegistryEnvironment()
504
672
  });
505
- if (!result.success || !result.record) throw new Error(result.error || "No prestarted HTTP backend found for stdio-http transport");
506
- return new URL(`http://${config.host ?? result.record.host}:${result.record.port}${MCP_ENDPOINT_PATH}`);
673
+ if (result.success && result.record) return new URL(`http://${config.host ?? result.record.host}:${result.record.port}${MCP_ENDPOINT_PATH}`);
674
+ const runtime = await prestartHttpRuntime({
675
+ host: config.host ?? DEFAULT_HOST,
676
+ config: options.config || resolvedConfigPath,
677
+ cache: options.cache,
678
+ definitionsCache: options.definitionsCache,
679
+ clearDefinitionsCache: options.clearDefinitionsCache,
680
+ proxyMode: options.proxyMode
681
+ });
682
+ return new URL(`http://${runtime.host}:${runtime.port}${MCP_ENDPOINT_PATH}`);
507
683
  }
508
- async function startStdioHttpTransport(config) {
684
+ async function startStdioHttpTransport(config, options, resolvedConfigPath) {
509
685
  try {
510
- await startServer(new require_src.StdioHttpTransportHandler({ endpoint: await resolveStdioHttpEndpoint(config) }));
686
+ await startServer(new require_src.StdioHttpTransportHandler({ endpoint: await resolveStdioHttpEndpoint(config, options, resolvedConfigPath) }));
511
687
  } catch (error) {
512
688
  throw new Error(`Failed to start stdio-http transport: ${toErrorMessage$9(error)}`);
513
689
  }
@@ -526,7 +702,7 @@ async function startTransport(transportType, options, resolvedConfigPath, server
526
702
  await startSseTransport(serverOptions, createTransportConfig(options, require_src.TRANSPORT_MODE.SSE));
527
703
  return;
528
704
  }
529
- await startStdioHttpTransport(createTransportConfig(options, require_src.TRANSPORT_MODE.HTTP));
705
+ await startStdioHttpTransport(createTransportConfig(options, require_src.TRANSPORT_MODE.HTTP), options, resolvedConfigPath);
530
706
  } catch (error) {
531
707
  throw new Error(`Failed to start transport '${transportType}': ${toErrorMessage$9(error)}`);
532
708
  }
@@ -534,7 +710,7 @@ async function startTransport(transportType, options, resolvedConfigPath, server
534
710
  /**
535
711
  * MCP Serve command
536
712
  */
537
- const mcpServeCommand = new commander.Command("mcp-serve").description("Start MCP server with specified transport").option("-t, --type <type>", `Transport type: ${TRANSPORT_TYPE_STDIO}, ${TRANSPORT_TYPE_HTTP}, ${TRANSPORT_TYPE_SSE}, or ${TRANSPORT_TYPE_STDIO_HTTP}`, TRANSPORT_TYPE_STDIO).option("-p, --port <port>", "Port to listen on (http/sse) or backend port for stdio-http", (val) => parseInt(val, 10)).option("--host <host>", "Host to bind to (http/sse) or backend host for stdio-http", DEFAULT_HOST$1).option("-c, --config <path>", "Path to MCP server configuration file").option("--no-cache", "Disable configuration caching, always reload from config file").option("--definitions-cache <path>", "Path to prefetched tool/prompt/skill definitions cache file").option("--clear-definitions-cache", "Delete definitions cache before startup", false).option("--proxy-mode <mode>", "How mcp-proxy exposes downstream tools: meta, flat, or search", "meta").option("--id <id>", "Unique server identifier (overrides config file id, auto-generated if not provided)").action(async (options) => {
713
+ const mcpServeCommand = new commander.Command("mcp-serve").description("Start MCP server with specified transport").option("-t, --type <type>", `Transport type: ${TRANSPORT_TYPE_STDIO}, ${TRANSPORT_TYPE_HTTP}, ${TRANSPORT_TYPE_SSE}, or ${TRANSPORT_TYPE_STDIO_HTTP}`, TRANSPORT_TYPE_STDIO).option("-p, --port <port>", "Port to listen on (http/sse) or backend port for stdio-http", (val) => parseInt(val, 10)).option("--host <host>", "Host to bind to (http/sse) or backend host for stdio-http", DEFAULT_HOST).option("-c, --config <path>", "Path to MCP server configuration file").option("--no-cache", "Disable configuration caching, always reload from config file").option("--definitions-cache <path>", "Path to prefetched tool/prompt/skill definitions cache file").option("--clear-definitions-cache", "Delete definitions cache before startup", false).option("--proxy-mode <mode>", "How mcp-proxy exposes downstream tools: meta, flat, or search", "meta").option("--id <id>", "Unique server identifier (overrides config file id, auto-generated if not provided)").action(async (options) => {
538
714
  try {
539
715
  const transportType = validateTransportType(options.type.toLowerCase());
540
716
  validateProxyMode(options.proxyMode);
@@ -550,156 +726,6 @@ const mcpServeCommand = new commander.Command("mcp-serve").description("Start MC
550
726
  }
551
727
  });
552
728
 
553
- //#endregion
554
- //#region src/commands/prestart-http.ts
555
- /**
556
- * Prestart HTTP Command
557
- *
558
- * Starts an mcp-proxy HTTP runtime in the background, waits until it is healthy,
559
- * and then exits so the runtime can be reused by other commands.
560
- */
561
- const WORKSPACE_MARKERS = [
562
- "pnpm-workspace.yaml",
563
- "nx.json",
564
- ".git"
565
- ];
566
- const DEFAULT_HOST = "localhost";
567
- const DEFAULT_TIMEOUT_MS = 12e4;
568
- const POLL_INTERVAL_MS = 250;
569
- function resolveWorkspaceRoot(startPath = process.cwd()) {
570
- let current = node_path.default.resolve(startPath);
571
- while (true) {
572
- for (const marker of WORKSPACE_MARKERS) if ((0, node_fs.existsSync)(node_path.default.join(current, marker))) return current;
573
- const parent = node_path.default.dirname(current);
574
- if (parent === current) return process.cwd();
575
- current = parent;
576
- }
577
- }
578
- function resolveSiblingRegistryPath(registryPath, fileName) {
579
- if (!registryPath) return;
580
- const resolved = node_path.default.resolve(registryPath);
581
- if (node_path.default.extname(resolved) === ".json") return node_path.default.join(node_path.default.dirname(resolved), fileName);
582
- return node_path.default.join(resolved, fileName);
583
- }
584
- function buildCliCandidates() {
585
- const __filename$1 = (0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href);
586
- const __dirname$1 = node_path.default.dirname(__filename$1);
587
- const distCandidates = [
588
- node_path.default.resolve(__dirname$1, "cli.mjs"),
589
- node_path.default.resolve(__dirname$1, "..", "dist", "cli.mjs"),
590
- node_path.default.resolve(__dirname$1, "..", "..", "dist", "cli.mjs")
591
- ];
592
- const srcCandidates = [node_path.default.resolve(__dirname$1, "..", "cli.ts"), node_path.default.resolve(__dirname$1, "..", "..", "src", "cli.ts")];
593
- for (const candidate of distCandidates) if ((0, node_fs.existsSync)(candidate)) return {
594
- command: process.execPath,
595
- args: [candidate]
596
- };
597
- for (const candidate of srcCandidates) if ((0, node_fs.existsSync)(candidate)) return {
598
- command: process.execPath,
599
- args: [
600
- "--import",
601
- "tsx",
602
- candidate
603
- ]
604
- };
605
- throw new Error("Unable to locate mcp-proxy CLI entrypoint");
606
- }
607
- function parseTimeoutMs(value) {
608
- if (!value) return DEFAULT_TIMEOUT_MS;
609
- const parsed = Number.parseInt(value, 10);
610
- if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`Invalid timeout value: ${value}`);
611
- return parsed;
612
- }
613
- async function waitForFile(filePath, timeoutMs) {
614
- const deadline = Date.now() + timeoutMs;
615
- while (Date.now() < deadline) {
616
- try {
617
- await (0, node_fs_promises.access)(filePath);
618
- return;
619
- } catch {}
620
- await new Promise((resolve$2) => setTimeout(resolve$2, POLL_INTERVAL_MS));
621
- }
622
- throw new Error(`Timed out waiting for runtime state file: ${filePath}`);
623
- }
624
- async function waitForHealthyRuntime(serverId, timeoutMs) {
625
- const runtimeStateService = new require_src.RuntimeStateService();
626
- const deadline = Date.now() + timeoutMs;
627
- while (Date.now() < deadline) {
628
- const record = await runtimeStateService.read(serverId);
629
- if (record) {
630
- const healthUrl = `http://${record.host}:${record.port}/health`;
631
- try {
632
- const response = await fetch(healthUrl);
633
- if (response.ok) {
634
- const payload = await response.json().catch(() => null);
635
- if (!payload || payload.transport === "http") return {
636
- host: record.host,
637
- port: record.port
638
- };
639
- }
640
- } catch {}
641
- }
642
- await new Promise((resolve$2) => setTimeout(resolve$2, POLL_INTERVAL_MS));
643
- }
644
- throw new Error(`Timed out waiting for HTTP runtime '${serverId}' to become healthy`);
645
- }
646
- function spawnBackgroundRuntime(args, env) {
647
- const { command, args: baseArgs } = buildCliCandidates();
648
- const child = (0, node_child_process.spawn)(command, [...baseArgs, ...args], {
649
- detached: true,
650
- stdio: "ignore",
651
- env
652
- });
653
- child.unref();
654
- return child;
655
- }
656
- const prestartHttpCommand = new commander.Command("prestart-http").description("Start an mcp-proxy HTTP runtime in the background and wait until it is healthy").option("--id <id>", "Server identifier to assign to the runtime").option("--host <host>", "Host to bind to", DEFAULT_HOST).option("-p, --port <port>", "Preferred HTTP port for the runtime", (value) => Number.parseInt(value, 10)).option("-c, --config <path>", "Path to MCP server configuration file").option("--no-cache", "Disable configuration caching, always reload from config file").option("--definitions-cache <path>", "Path to prefetched tool/prompt/skill definitions cache file").option("--clear-definitions-cache", "Delete definitions cache before startup", false).option("--proxy-mode <mode>", "How mcp-proxy exposes downstream tools: meta, flat, or search", "meta").option("--registry-path <path>", "Custom registry path or directory for service discovery").option("--registry-dir <path>", "Custom registry directory for service discovery").option("--timeout-ms <ms>", "How long to wait for the runtime to become healthy", String(DEFAULT_TIMEOUT_MS)).action(async (options) => {
657
- const serverId = options.id || require_src.generateServerId();
658
- const timeoutMs = parseTimeoutMs(options.timeoutMs);
659
- const registryPath = options.registryPath || options.registryDir;
660
- new require_src.RuntimeStateService();
661
- const workspaceRoot = resolveWorkspaceRoot(process.cwd());
662
- const childEnv = {
663
- ...process.env,
664
- ...registryPath ? {
665
- PORT_REGISTRY_PATH: registryPath,
666
- PROCESS_REGISTRY_PATH: resolveSiblingRegistryPath(registryPath, "processes.json")
667
- } : {}
668
- };
669
- const child = spawnBackgroundRuntime([
670
- "mcp-serve",
671
- "--type",
672
- "http",
673
- "--id",
674
- serverId,
675
- "--host",
676
- options.host || DEFAULT_HOST,
677
- ...options.port !== void 0 ? ["--port", String(options.port)] : [],
678
- ...options.config ? ["--config", options.config] : [],
679
- ...options.cache === false ? ["--no-cache"] : [],
680
- ...options.definitionsCache ? ["--definitions-cache", options.definitionsCache] : [],
681
- ...options.clearDefinitionsCache ? ["--clear-definitions-cache"] : [],
682
- "--proxy-mode",
683
- options.proxyMode
684
- ], childEnv);
685
- const childExit = new Promise((_, reject) => {
686
- child.once("exit", (code, signal) => {
687
- reject(/* @__PURE__ */ new Error(`Background runtime exited before becoming healthy (code=${code ?? "null"}, signal=${signal ?? "null"})`));
688
- });
689
- });
690
- const runtimeFile = node_path.default.join(require_src.RuntimeStateService.getDefaultRuntimeDir(), `${serverId}.runtime.json`);
691
- try {
692
- await Promise.race([waitForFile(runtimeFile, timeoutMs), childExit]);
693
- const { host, port } = await Promise.race([waitForHealthyRuntime(serverId, timeoutMs), childExit]);
694
- process.stdout.write(`mcp-proxy HTTP runtime ready at http://${host}:${port} (${serverId})\n`);
695
- process.stdout.write(`runtimeId=${serverId}\n`);
696
- process.stdout.write(`runtimeUrl=http://${host}:${port}\n`);
697
- process.stdout.write(`workspaceRoot=${workspaceRoot}\n`);
698
- } catch (error) {
699
- throw new Error(`Failed to prestart HTTP runtime '${serverId}': ${error instanceof Error ? error.message : String(error)}`);
700
- }
701
- });
702
-
703
729
  //#endregion
704
730
  //#region src/commands/bootstrap.ts
705
731
  function toErrorMessage$8(error) {
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { D as findConfigFile, E as generateServerId, T as DefinitionsCacheService, d as version, f as StdioHttpTransportHandler, h as HttpTransportHandler, m as SseTransportHandler, n as createServer, o as createProxyIoCContainer, p as StdioTransportHandler, r as createSessionServer, t as TRANSPORT_MODE, u as initializeSharedServices, v as RuntimeStateService } from "./src-0OJqEpGA.mjs";
2
+ import { D as findConfigFile, E as generateServerId, T as DefinitionsCacheService, d as version, f as StdioHttpTransportHandler, h as HttpTransportHandler, m as SseTransportHandler, n as createServer, o as createProxyIoCContainer, p as StdioTransportHandler, r as createSessionServer, t as TRANSPORT_MODE, u as initializeSharedServices, v as RuntimeStateService } from "./src-BgRM8oW0.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { access, writeFile } from "node:fs/promises";
5
5
  import path, { join, resolve } from "node:path";
@@ -112,6 +112,169 @@ const initCommand = new Command("init").description("Initialize MCP configuratio
112
112
  }
113
113
  });
114
114
 
115
+ //#endregion
116
+ //#region src/commands/prestart-http.ts
117
+ /**
118
+ * Prestart HTTP Command
119
+ *
120
+ * Starts an mcp-proxy HTTP runtime in the background, waits until it is healthy,
121
+ * and then exits so the runtime can be reused by other commands.
122
+ */
123
+ const WORKSPACE_MARKERS = [
124
+ "pnpm-workspace.yaml",
125
+ "nx.json",
126
+ ".git"
127
+ ];
128
+ const DEFAULT_HOST$1 = "localhost";
129
+ const DEFAULT_TIMEOUT_MS = 12e4;
130
+ const POLL_INTERVAL_MS = 250;
131
+ function resolveWorkspaceRoot(startPath = process.env.PROJECT_PATH || process.cwd()) {
132
+ let current = path.resolve(startPath);
133
+ while (true) {
134
+ for (const marker of WORKSPACE_MARKERS) if (existsSync(path.join(current, marker))) return current;
135
+ const parent = path.dirname(current);
136
+ if (parent === current) return process.cwd();
137
+ current = parent;
138
+ }
139
+ }
140
+ function resolveSiblingRegistryPath(registryPath, fileName) {
141
+ if (!registryPath) return;
142
+ const resolved = path.resolve(registryPath);
143
+ if (path.extname(resolved) === ".json") return path.join(path.dirname(resolved), fileName);
144
+ return path.join(resolved, fileName);
145
+ }
146
+ function buildCliCandidates() {
147
+ const __filename = fileURLToPath(import.meta.url);
148
+ const __dirname = path.dirname(__filename);
149
+ const distCandidates = [
150
+ path.resolve(__dirname, "cli.mjs"),
151
+ path.resolve(__dirname, "..", "dist", "cli.mjs"),
152
+ path.resolve(__dirname, "..", "..", "dist", "cli.mjs")
153
+ ];
154
+ const srcCandidates = [path.resolve(__dirname, "..", "cli.ts"), path.resolve(__dirname, "..", "..", "src", "cli.ts")];
155
+ for (const candidate of distCandidates) if (existsSync(candidate)) return {
156
+ command: process.execPath,
157
+ args: [candidate]
158
+ };
159
+ for (const candidate of srcCandidates) if (existsSync(candidate)) return {
160
+ command: process.execPath,
161
+ args: [
162
+ "--import",
163
+ "tsx",
164
+ candidate
165
+ ]
166
+ };
167
+ throw new Error("Unable to locate mcp-proxy CLI entrypoint");
168
+ }
169
+ function parseTimeoutMs(value) {
170
+ if (!value) return DEFAULT_TIMEOUT_MS;
171
+ const parsed = Number.parseInt(value, 10);
172
+ if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`Invalid timeout value: ${value}`);
173
+ return parsed;
174
+ }
175
+ async function waitForFile(filePath, timeoutMs) {
176
+ const deadline = Date.now() + timeoutMs;
177
+ while (Date.now() < deadline) {
178
+ try {
179
+ await access(filePath);
180
+ return;
181
+ } catch {}
182
+ await new Promise((resolve$1) => setTimeout(resolve$1, POLL_INTERVAL_MS));
183
+ }
184
+ throw new Error(`Timed out waiting for runtime state file: ${filePath}`);
185
+ }
186
+ async function waitForHealthyRuntime(serverId, timeoutMs) {
187
+ const runtimeStateService = new RuntimeStateService();
188
+ const deadline = Date.now() + timeoutMs;
189
+ while (Date.now() < deadline) {
190
+ const record = await runtimeStateService.read(serverId);
191
+ if (record) {
192
+ const healthUrl = `http://${record.host}:${record.port}/health`;
193
+ try {
194
+ const response = await fetch(healthUrl);
195
+ if (response.ok) {
196
+ const payload = await response.json().catch(() => null);
197
+ if (!payload || payload.transport === "http") return {
198
+ host: record.host,
199
+ port: record.port
200
+ };
201
+ }
202
+ } catch {}
203
+ }
204
+ await new Promise((resolve$1) => setTimeout(resolve$1, POLL_INTERVAL_MS));
205
+ }
206
+ throw new Error(`Timed out waiting for HTTP runtime '${serverId}' to become healthy`);
207
+ }
208
+ function spawnBackgroundRuntime(args, env, cwd) {
209
+ const { command, args: baseArgs } = buildCliCandidates();
210
+ const child = spawn(command, [...baseArgs, ...args], {
211
+ detached: true,
212
+ cwd,
213
+ stdio: "ignore",
214
+ env
215
+ });
216
+ child.unref();
217
+ return child;
218
+ }
219
+ async function prestartHttpRuntime(options) {
220
+ const serverId = options.id || generateServerId();
221
+ const timeoutMs = parseTimeoutMs(options.timeoutMs);
222
+ const registryPath = options.registryPath || options.registryDir;
223
+ const workspaceRoot = resolveWorkspaceRoot();
224
+ const childEnv = {
225
+ ...process.env,
226
+ ...registryPath ? {
227
+ PORT_REGISTRY_PATH: registryPath,
228
+ PROCESS_REGISTRY_PATH: resolveSiblingRegistryPath(registryPath, "processes.json")
229
+ } : {}
230
+ };
231
+ const child = spawnBackgroundRuntime([
232
+ "mcp-serve",
233
+ "--type",
234
+ "http",
235
+ "--id",
236
+ serverId,
237
+ "--host",
238
+ options.host || DEFAULT_HOST$1,
239
+ ...options.port !== void 0 ? ["--port", String(options.port)] : [],
240
+ ...options.config ? ["--config", options.config] : [],
241
+ ...options.cache === false ? ["--no-cache"] : [],
242
+ ...options.definitionsCache ? ["--definitions-cache", options.definitionsCache] : [],
243
+ ...options.clearDefinitionsCache ? ["--clear-definitions-cache"] : [],
244
+ "--proxy-mode",
245
+ options.proxyMode
246
+ ], childEnv, workspaceRoot);
247
+ const childExit = new Promise((_, reject) => {
248
+ child.once("exit", (code, signal) => {
249
+ reject(/* @__PURE__ */ new Error(`Background runtime exited before becoming healthy (code=${code ?? "null"}, signal=${signal ?? "null"})`));
250
+ });
251
+ });
252
+ const runtimeFile = path.join(RuntimeStateService.getDefaultRuntimeDir(), `${serverId}.runtime.json`);
253
+ try {
254
+ await Promise.race([waitForFile(runtimeFile, timeoutMs), childExit]);
255
+ const { host, port } = await Promise.race([waitForHealthyRuntime(serverId, timeoutMs), childExit]);
256
+ return {
257
+ host,
258
+ port,
259
+ serverId,
260
+ workspaceRoot
261
+ };
262
+ } catch (error) {
263
+ throw new Error(`Failed to prestart HTTP runtime '${serverId}': ${error instanceof Error ? error.message : String(error)}`);
264
+ }
265
+ }
266
+ const prestartHttpCommand = new Command("prestart-http").description("Start an mcp-proxy HTTP runtime in the background and wait until it is healthy").option("--id <id>", "Server identifier to assign to the runtime").option("--host <host>", "Host to bind to", DEFAULT_HOST$1).option("-p, --port <port>", "Preferred HTTP port for the runtime", (value) => Number.parseInt(value, 10)).option("-c, --config <path>", "Path to MCP server configuration file").option("--no-cache", "Disable configuration caching, always reload from config file").option("--definitions-cache <path>", "Path to prefetched tool/prompt/skill definitions cache file").option("--clear-definitions-cache", "Delete definitions cache before startup", false).option("--proxy-mode <mode>", "How mcp-proxy exposes downstream tools: meta, flat, or search", "meta").option("--registry-path <path>", "Custom registry path or directory for service discovery").option("--registry-dir <path>", "Custom registry directory for service discovery").option("--timeout-ms <ms>", "How long to wait for the runtime to become healthy", String(DEFAULT_TIMEOUT_MS)).action(async (options) => {
267
+ try {
268
+ const { host, port, serverId, workspaceRoot } = await prestartHttpRuntime(options);
269
+ process.stdout.write(`mcp-proxy HTTP runtime ready at http://${host}:${port} (${serverId})\n`);
270
+ process.stdout.write(`runtimeId=${serverId}\n`);
271
+ process.stdout.write(`runtimeUrl=http://${host}:${port}\n`);
272
+ process.stdout.write(`workspaceRoot=${workspaceRoot}\n`);
273
+ } catch (error) {
274
+ throw new Error(`Failed to prestart HTTP runtime '${options.id || "generated-server-id"}': ${error instanceof Error ? error.message : String(error)}`);
275
+ }
276
+ });
277
+
115
278
  //#endregion
116
279
  //#region src/commands/mcp-serve.ts
117
280
  /**
@@ -140,7 +303,7 @@ const CONFIG_FILE_NAMES = [
140
303
  "mcp-config.json"
141
304
  ];
142
305
  const MCP_ENDPOINT_PATH = "/mcp";
143
- const DEFAULT_HOST$1 = "localhost";
306
+ const DEFAULT_HOST = "localhost";
144
307
  const TRANSPORT_TYPE_STDIO = "stdio";
145
308
  const TRANSPORT_TYPE_HTTP = "http";
146
309
  const TRANSPORT_TYPE_SSE = "sse";
@@ -148,10 +311,14 @@ const TRANSPORT_TYPE_STDIO_HTTP = "stdio-http";
148
311
  const RUNTIME_TRANSPORT = TRANSPORT_TYPE_HTTP;
149
312
  const PORT_REGISTRY_SERVICE_HTTP = "mcp-proxy-http";
150
313
  const PORT_REGISTRY_SERVICE_TYPE = "service";
151
- const PORT_REGISTRY_REPOSITORY_PATH = process.cwd();
314
+ function getWorkspaceRoot() {
315
+ return resolveWorkspaceRoot();
316
+ }
152
317
  const PROCESS_REGISTRY_SERVICE_HTTP = "mcp-proxy-http";
153
318
  const PROCESS_REGISTRY_SERVICE_TYPE = "service";
154
- const PROCESS_REGISTRY_REPOSITORY_PATH = process.cwd();
319
+ function getRegistryRepositoryPath() {
320
+ return getWorkspaceRoot();
321
+ }
155
322
  function toErrorMessage$9(error) {
156
323
  return error instanceof Error ? error.message : String(error);
157
324
  }
@@ -212,7 +379,7 @@ function createTransportConfig(options, mode) {
212
379
  return {
213
380
  mode,
214
381
  port: options.port ?? (Number.isFinite(envPort) ? envPort : void 0),
215
- host: options.host || process.env.MCP_HOST || DEFAULT_HOST$1
382
+ host: options.host || process.env.MCP_HOST || DEFAULT_HOST
216
383
  };
217
384
  }
218
385
  function createServerOptions(options, resolvedConfigPath, serverId) {
@@ -233,7 +400,7 @@ function formatStartError(type, host, port, error) {
233
400
  function createRuntimeRecord(serverId, config, port, shutdownToken, configPath) {
234
401
  return {
235
402
  serverId,
236
- host: config.host ?? DEFAULT_HOST$1,
403
+ host: config.host ?? DEFAULT_HOST,
237
404
  port,
238
405
  transport: RUNTIME_TRANSPORT,
239
406
  shutdownToken,
@@ -257,7 +424,7 @@ async function createPortRegistryLease(serviceName, host, preferredPort, serverI
257
424
  } : DEFAULT_PORT_RANGE) {
258
425
  const portRegistry = createPortRegistryService();
259
426
  const result = await portRegistry.reservePort({
260
- repositoryPath: PORT_REGISTRY_REPOSITORY_PATH,
427
+ repositoryPath: getRegistryRepositoryPath(),
261
428
  serviceName,
262
429
  serviceType: PORT_REGISTRY_SERVICE_TYPE,
263
430
  environment: getRegistryEnvironment(),
@@ -283,7 +450,7 @@ async function createPortRegistryLease(serviceName, host, preferredPort, serverI
283
450
  if (released) return;
284
451
  released = true;
285
452
  const releaseResult = await portRegistry.releasePort({
286
- repositoryPath: PORT_REGISTRY_REPOSITORY_PATH,
453
+ repositoryPath: getRegistryRepositoryPath(),
287
454
  serviceName,
288
455
  serviceType: PORT_REGISTRY_SERVICE_TYPE,
289
456
  pid: process.pid,
@@ -297,7 +464,7 @@ async function createPortRegistryLease(serviceName, host, preferredPort, serverI
297
464
  async function createProcessRegistryLease(serviceName, host, port, serverId, transport, configPath) {
298
465
  const processRegistry = createProcessRegistryService();
299
466
  const result = await processRegistry.registerProcess({
300
- repositoryPath: PROCESS_REGISTRY_REPOSITORY_PATH,
467
+ repositoryPath: getRegistryRepositoryPath(),
301
468
  serviceName,
302
469
  serviceType: PROCESS_REGISTRY_SERVICE_TYPE,
303
470
  environment: getRegistryEnvironment(),
@@ -318,7 +485,7 @@ async function createProcessRegistryLease(serviceName, host, port, serverId, tra
318
485
  if (released) return;
319
486
  released = true;
320
487
  const releaseResult = await processRegistry.releaseProcess({
321
- repositoryPath: PROCESS_REGISTRY_REPOSITORY_PATH,
488
+ repositoryPath: getRegistryRepositoryPath(),
322
489
  serviceName,
323
490
  serviceType: PROCESS_REGISTRY_SERVICE_TYPE,
324
491
  pid: process.pid,
@@ -419,13 +586,13 @@ async function createAndStartHttpRuntime(serverOptions, config, resolvedConfigPa
419
586
  min: requestedPort,
420
587
  max: requestedPort
421
588
  } : DEFAULT_PORT_RANGE;
422
- const portLease = await createPortRegistryLease(PORT_REGISTRY_SERVICE_HTTP, config.host ?? DEFAULT_HOST$1, requestedPort, runtimeServerId, TRANSPORT_TYPE_HTTP, resolvedConfigPath, portRange);
589
+ const portLease = await createPortRegistryLease(PORT_REGISTRY_SERVICE_HTTP, config.host ?? DEFAULT_HOST, requestedPort, runtimeServerId, TRANSPORT_TYPE_HTTP, resolvedConfigPath, portRange);
423
590
  const runtimePort = portLease.port;
424
591
  const runtimeConfig = {
425
592
  ...config,
426
593
  port: runtimePort
427
594
  };
428
- const processLease = await createProcessRegistryLease(PROCESS_REGISTRY_SERVICE_HTTP, runtimeConfig.host ?? DEFAULT_HOST$1, runtimePort, runtimeServerId, TRANSPORT_TYPE_HTTP, resolvedConfigPath);
595
+ const processLease = await createProcessRegistryLease(PROCESS_REGISTRY_SERVICE_HTTP, runtimeConfig.host ?? DEFAULT_HOST, runtimePort, runtimeServerId, TRANSPORT_TYPE_HTTP, resolvedConfigPath);
429
596
  let releasePort = async () => {
430
597
  await releasePortLease(portLease ?? null);
431
598
  releasePort = async () => void 0;
@@ -493,20 +660,29 @@ async function startSseTransport(serverOptions, config) {
493
660
  throw new Error(`Failed to start SSE transport: ${toErrorMessage$9(error)}`);
494
661
  }
495
662
  }
496
- async function resolveStdioHttpEndpoint(config) {
497
- if (config.port !== void 0) return new URL(`http://${config.host ?? DEFAULT_HOST$1}:${config.port}${MCP_ENDPOINT_PATH}`);
663
+ async function resolveStdioHttpEndpoint(config, options, resolvedConfigPath) {
664
+ const repositoryPath = getRegistryRepositoryPath();
665
+ if (config.port !== void 0) return new URL(`http://${config.host ?? DEFAULT_HOST}:${config.port}${MCP_ENDPOINT_PATH}`);
498
666
  const result = await createPortRegistryService().getPort({
499
- repositoryPath: PORT_REGISTRY_REPOSITORY_PATH,
667
+ repositoryPath,
500
668
  serviceName: PORT_REGISTRY_SERVICE_HTTP,
501
669
  serviceType: PORT_REGISTRY_SERVICE_TYPE,
502
670
  environment: getRegistryEnvironment()
503
671
  });
504
- if (!result.success || !result.record) throw new Error(result.error || "No prestarted HTTP backend found for stdio-http transport");
505
- return new URL(`http://${config.host ?? result.record.host}:${result.record.port}${MCP_ENDPOINT_PATH}`);
672
+ if (result.success && result.record) return new URL(`http://${config.host ?? result.record.host}:${result.record.port}${MCP_ENDPOINT_PATH}`);
673
+ const runtime = await prestartHttpRuntime({
674
+ host: config.host ?? DEFAULT_HOST,
675
+ config: options.config || resolvedConfigPath,
676
+ cache: options.cache,
677
+ definitionsCache: options.definitionsCache,
678
+ clearDefinitionsCache: options.clearDefinitionsCache,
679
+ proxyMode: options.proxyMode
680
+ });
681
+ return new URL(`http://${runtime.host}:${runtime.port}${MCP_ENDPOINT_PATH}`);
506
682
  }
507
- async function startStdioHttpTransport(config) {
683
+ async function startStdioHttpTransport(config, options, resolvedConfigPath) {
508
684
  try {
509
- await startServer(new StdioHttpTransportHandler({ endpoint: await resolveStdioHttpEndpoint(config) }));
685
+ await startServer(new StdioHttpTransportHandler({ endpoint: await resolveStdioHttpEndpoint(config, options, resolvedConfigPath) }));
510
686
  } catch (error) {
511
687
  throw new Error(`Failed to start stdio-http transport: ${toErrorMessage$9(error)}`);
512
688
  }
@@ -525,7 +701,7 @@ async function startTransport(transportType, options, resolvedConfigPath, server
525
701
  await startSseTransport(serverOptions, createTransportConfig(options, TRANSPORT_MODE.SSE));
526
702
  return;
527
703
  }
528
- await startStdioHttpTransport(createTransportConfig(options, TRANSPORT_MODE.HTTP));
704
+ await startStdioHttpTransport(createTransportConfig(options, TRANSPORT_MODE.HTTP), options, resolvedConfigPath);
529
705
  } catch (error) {
530
706
  throw new Error(`Failed to start transport '${transportType}': ${toErrorMessage$9(error)}`);
531
707
  }
@@ -533,7 +709,7 @@ async function startTransport(transportType, options, resolvedConfigPath, server
533
709
  /**
534
710
  * MCP Serve command
535
711
  */
536
- const mcpServeCommand = new Command("mcp-serve").description("Start MCP server with specified transport").option("-t, --type <type>", `Transport type: ${TRANSPORT_TYPE_STDIO}, ${TRANSPORT_TYPE_HTTP}, ${TRANSPORT_TYPE_SSE}, or ${TRANSPORT_TYPE_STDIO_HTTP}`, TRANSPORT_TYPE_STDIO).option("-p, --port <port>", "Port to listen on (http/sse) or backend port for stdio-http", (val) => parseInt(val, 10)).option("--host <host>", "Host to bind to (http/sse) or backend host for stdio-http", DEFAULT_HOST$1).option("-c, --config <path>", "Path to MCP server configuration file").option("--no-cache", "Disable configuration caching, always reload from config file").option("--definitions-cache <path>", "Path to prefetched tool/prompt/skill definitions cache file").option("--clear-definitions-cache", "Delete definitions cache before startup", false).option("--proxy-mode <mode>", "How mcp-proxy exposes downstream tools: meta, flat, or search", "meta").option("--id <id>", "Unique server identifier (overrides config file id, auto-generated if not provided)").action(async (options) => {
712
+ const mcpServeCommand = new Command("mcp-serve").description("Start MCP server with specified transport").option("-t, --type <type>", `Transport type: ${TRANSPORT_TYPE_STDIO}, ${TRANSPORT_TYPE_HTTP}, ${TRANSPORT_TYPE_SSE}, or ${TRANSPORT_TYPE_STDIO_HTTP}`, TRANSPORT_TYPE_STDIO).option("-p, --port <port>", "Port to listen on (http/sse) or backend port for stdio-http", (val) => parseInt(val, 10)).option("--host <host>", "Host to bind to (http/sse) or backend host for stdio-http", DEFAULT_HOST).option("-c, --config <path>", "Path to MCP server configuration file").option("--no-cache", "Disable configuration caching, always reload from config file").option("--definitions-cache <path>", "Path to prefetched tool/prompt/skill definitions cache file").option("--clear-definitions-cache", "Delete definitions cache before startup", false).option("--proxy-mode <mode>", "How mcp-proxy exposes downstream tools: meta, flat, or search", "meta").option("--id <id>", "Unique server identifier (overrides config file id, auto-generated if not provided)").action(async (options) => {
537
713
  try {
538
714
  const transportType = validateTransportType(options.type.toLowerCase());
539
715
  validateProxyMode(options.proxyMode);
@@ -549,156 +725,6 @@ const mcpServeCommand = new Command("mcp-serve").description("Start MCP server w
549
725
  }
550
726
  });
551
727
 
552
- //#endregion
553
- //#region src/commands/prestart-http.ts
554
- /**
555
- * Prestart HTTP Command
556
- *
557
- * Starts an mcp-proxy HTTP runtime in the background, waits until it is healthy,
558
- * and then exits so the runtime can be reused by other commands.
559
- */
560
- const WORKSPACE_MARKERS = [
561
- "pnpm-workspace.yaml",
562
- "nx.json",
563
- ".git"
564
- ];
565
- const DEFAULT_HOST = "localhost";
566
- const DEFAULT_TIMEOUT_MS = 12e4;
567
- const POLL_INTERVAL_MS = 250;
568
- function resolveWorkspaceRoot(startPath = process.cwd()) {
569
- let current = path.resolve(startPath);
570
- while (true) {
571
- for (const marker of WORKSPACE_MARKERS) if (existsSync(path.join(current, marker))) return current;
572
- const parent = path.dirname(current);
573
- if (parent === current) return process.cwd();
574
- current = parent;
575
- }
576
- }
577
- function resolveSiblingRegistryPath(registryPath, fileName) {
578
- if (!registryPath) return;
579
- const resolved = path.resolve(registryPath);
580
- if (path.extname(resolved) === ".json") return path.join(path.dirname(resolved), fileName);
581
- return path.join(resolved, fileName);
582
- }
583
- function buildCliCandidates() {
584
- const __filename = fileURLToPath(import.meta.url);
585
- const __dirname = path.dirname(__filename);
586
- const distCandidates = [
587
- path.resolve(__dirname, "cli.mjs"),
588
- path.resolve(__dirname, "..", "dist", "cli.mjs"),
589
- path.resolve(__dirname, "..", "..", "dist", "cli.mjs")
590
- ];
591
- const srcCandidates = [path.resolve(__dirname, "..", "cli.ts"), path.resolve(__dirname, "..", "..", "src", "cli.ts")];
592
- for (const candidate of distCandidates) if (existsSync(candidate)) return {
593
- command: process.execPath,
594
- args: [candidate]
595
- };
596
- for (const candidate of srcCandidates) if (existsSync(candidate)) return {
597
- command: process.execPath,
598
- args: [
599
- "--import",
600
- "tsx",
601
- candidate
602
- ]
603
- };
604
- throw new Error("Unable to locate mcp-proxy CLI entrypoint");
605
- }
606
- function parseTimeoutMs(value) {
607
- if (!value) return DEFAULT_TIMEOUT_MS;
608
- const parsed = Number.parseInt(value, 10);
609
- if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`Invalid timeout value: ${value}`);
610
- return parsed;
611
- }
612
- async function waitForFile(filePath, timeoutMs) {
613
- const deadline = Date.now() + timeoutMs;
614
- while (Date.now() < deadline) {
615
- try {
616
- await access(filePath);
617
- return;
618
- } catch {}
619
- await new Promise((resolve$1) => setTimeout(resolve$1, POLL_INTERVAL_MS));
620
- }
621
- throw new Error(`Timed out waiting for runtime state file: ${filePath}`);
622
- }
623
- async function waitForHealthyRuntime(serverId, timeoutMs) {
624
- const runtimeStateService = new RuntimeStateService();
625
- const deadline = Date.now() + timeoutMs;
626
- while (Date.now() < deadline) {
627
- const record = await runtimeStateService.read(serverId);
628
- if (record) {
629
- const healthUrl = `http://${record.host}:${record.port}/health`;
630
- try {
631
- const response = await fetch(healthUrl);
632
- if (response.ok) {
633
- const payload = await response.json().catch(() => null);
634
- if (!payload || payload.transport === "http") return {
635
- host: record.host,
636
- port: record.port
637
- };
638
- }
639
- } catch {}
640
- }
641
- await new Promise((resolve$1) => setTimeout(resolve$1, POLL_INTERVAL_MS));
642
- }
643
- throw new Error(`Timed out waiting for HTTP runtime '${serverId}' to become healthy`);
644
- }
645
- function spawnBackgroundRuntime(args, env) {
646
- const { command, args: baseArgs } = buildCliCandidates();
647
- const child = spawn(command, [...baseArgs, ...args], {
648
- detached: true,
649
- stdio: "ignore",
650
- env
651
- });
652
- child.unref();
653
- return child;
654
- }
655
- const prestartHttpCommand = new Command("prestart-http").description("Start an mcp-proxy HTTP runtime in the background and wait until it is healthy").option("--id <id>", "Server identifier to assign to the runtime").option("--host <host>", "Host to bind to", DEFAULT_HOST).option("-p, --port <port>", "Preferred HTTP port for the runtime", (value) => Number.parseInt(value, 10)).option("-c, --config <path>", "Path to MCP server configuration file").option("--no-cache", "Disable configuration caching, always reload from config file").option("--definitions-cache <path>", "Path to prefetched tool/prompt/skill definitions cache file").option("--clear-definitions-cache", "Delete definitions cache before startup", false).option("--proxy-mode <mode>", "How mcp-proxy exposes downstream tools: meta, flat, or search", "meta").option("--registry-path <path>", "Custom registry path or directory for service discovery").option("--registry-dir <path>", "Custom registry directory for service discovery").option("--timeout-ms <ms>", "How long to wait for the runtime to become healthy", String(DEFAULT_TIMEOUT_MS)).action(async (options) => {
656
- const serverId = options.id || generateServerId();
657
- const timeoutMs = parseTimeoutMs(options.timeoutMs);
658
- const registryPath = options.registryPath || options.registryDir;
659
- new RuntimeStateService();
660
- const workspaceRoot = resolveWorkspaceRoot(process.cwd());
661
- const childEnv = {
662
- ...process.env,
663
- ...registryPath ? {
664
- PORT_REGISTRY_PATH: registryPath,
665
- PROCESS_REGISTRY_PATH: resolveSiblingRegistryPath(registryPath, "processes.json")
666
- } : {}
667
- };
668
- const child = spawnBackgroundRuntime([
669
- "mcp-serve",
670
- "--type",
671
- "http",
672
- "--id",
673
- serverId,
674
- "--host",
675
- options.host || DEFAULT_HOST,
676
- ...options.port !== void 0 ? ["--port", String(options.port)] : [],
677
- ...options.config ? ["--config", options.config] : [],
678
- ...options.cache === false ? ["--no-cache"] : [],
679
- ...options.definitionsCache ? ["--definitions-cache", options.definitionsCache] : [],
680
- ...options.clearDefinitionsCache ? ["--clear-definitions-cache"] : [],
681
- "--proxy-mode",
682
- options.proxyMode
683
- ], childEnv);
684
- const childExit = new Promise((_, reject) => {
685
- child.once("exit", (code, signal) => {
686
- reject(/* @__PURE__ */ new Error(`Background runtime exited before becoming healthy (code=${code ?? "null"}, signal=${signal ?? "null"})`));
687
- });
688
- });
689
- const runtimeFile = path.join(RuntimeStateService.getDefaultRuntimeDir(), `${serverId}.runtime.json`);
690
- try {
691
- await Promise.race([waitForFile(runtimeFile, timeoutMs), childExit]);
692
- const { host, port } = await Promise.race([waitForHealthyRuntime(serverId, timeoutMs), childExit]);
693
- process.stdout.write(`mcp-proxy HTTP runtime ready at http://${host}:${port} (${serverId})\n`);
694
- process.stdout.write(`runtimeId=${serverId}\n`);
695
- process.stdout.write(`runtimeUrl=http://${host}:${port}\n`);
696
- process.stdout.write(`workspaceRoot=${workspaceRoot}\n`);
697
- } catch (error) {
698
- throw new Error(`Failed to prestart HTTP runtime '${serverId}': ${error instanceof Error ? error.message : String(error)}`);
699
- }
700
- });
701
-
702
728
  //#endregion
703
729
  //#region src/commands/bootstrap.ts
704
730
  function toErrorMessage$8(error) {
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_src = require('./src-G1hs2GLZ.cjs');
1
+ const require_src = require('./src-h-U3G1P0.cjs');
2
2
 
3
3
  exports.ConfigFetcherService = require_src.ConfigFetcherService;
4
4
  exports.DefinitionsCacheService = require_src.DefinitionsCacheService;
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { C as SearchListToolsTool, D as findConfigFile, E as generateServerId, S as UseToolTool, T as DefinitionsCacheService, _ as StopServerService, a as createProxyContainer, b as createProxyLogger, c as createStdioHttpTransportHandler, f as StdioHttpTransportHandler, g as SkillService, h as HttpTransportHandler, i as createHttpTransportHandler, l as createStdioTransportHandler, m as SseTransportHandler, n as createServer, p as StdioTransportHandler, r as createSessionServer, s as createSseTransportHandler, t as TRANSPORT_MODE, u as initializeSharedServices, v as RuntimeStateService, w as DescribeToolsTool, x as ConfigFetcherService, y as McpClientManagerService } from "./src-0OJqEpGA.mjs";
1
+ import { C as SearchListToolsTool, D as findConfigFile, E as generateServerId, S as UseToolTool, T as DefinitionsCacheService, _ as StopServerService, a as createProxyContainer, b as createProxyLogger, c as createStdioHttpTransportHandler, f as StdioHttpTransportHandler, g as SkillService, h as HttpTransportHandler, i as createHttpTransportHandler, l as createStdioTransportHandler, m as SseTransportHandler, n as createServer, p as StdioTransportHandler, r as createSessionServer, s as createSseTransportHandler, t as TRANSPORT_MODE, u as initializeSharedServices, v as RuntimeStateService, w as DescribeToolsTool, x as ConfigFetcherService, y as McpClientManagerService } from "./src-BgRM8oW0.mjs";
2
2
 
3
3
  export { ConfigFetcherService, DefinitionsCacheService, DescribeToolsTool, HttpTransportHandler, McpClientManagerService, RuntimeStateService, SearchListToolsTool, SkillService, SseTransportHandler, StdioHttpTransportHandler, StdioTransportHandler, StopServerService, TRANSPORT_MODE, UseToolTool, createHttpTransportHandler, createProxyContainer, createProxyLogger, createServer, createSessionServer, createSseTransportHandler, createStdioHttpTransportHandler, createStdioTransportHandler, findConfigFile, generateServerId, initializeSharedServices };
@@ -4313,7 +4313,7 @@ var StdioHttpTransportHandler = class {
4313
4313
 
4314
4314
  //#endregion
4315
4315
  //#region package.json
4316
- var version = "0.4.2";
4316
+ var version = "0.4.4";
4317
4317
 
4318
4318
  //#endregion
4319
4319
  //#region src/container/index.ts
@@ -4342,7 +4342,7 @@ var StdioHttpTransportHandler = class {
4342
4342
 
4343
4343
  //#endregion
4344
4344
  //#region package.json
4345
- var version = "0.4.2";
4345
+ var version = "0.4.4";
4346
4346
 
4347
4347
  //#endregion
4348
4348
  //#region src/container/index.ts
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agimon-ai/mcp-proxy",
3
3
  "description": "MCP proxy server package",
4
- "version": "0.4.4",
4
+ "version": "0.4.5",
5
5
  "license": "AGPL-3.0",
6
6
  "keywords": [
7
7
  "mcp",