@agimon-ai/mcp-proxy 0.4.1 → 0.4.3

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,13 +1,16 @@
1
1
  #!/usr/bin/env node
2
- const require_src = require('./src-6KF7hZTe.cjs');
2
+ const require_src = require('./src-G1hs2GLZ.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");
6
+ node_path = require_src.__toESM(node_path);
6
7
  let node_fs = require("node:fs");
7
8
  let liquidjs = require("liquidjs");
9
+ let node_child_process = require("node:child_process");
8
10
  let commander = require("commander");
9
11
  let __agimon_ai_foundation_process_registry = require("@agimon-ai/foundation-process-registry");
10
12
  let __agimon_ai_foundation_port_registry = require("@agimon-ai/foundation-port-registry");
13
+ let node_url = require("node:url");
11
14
 
12
15
  //#region src/utils/output.ts
13
16
  function writeLine(message = "") {
@@ -138,21 +141,16 @@ const CONFIG_FILE_NAMES = [
138
141
  "mcp-config.json"
139
142
  ];
140
143
  const MCP_ENDPOINT_PATH = "/mcp";
141
- const DEFAULT_PORT = 3e3;
142
- const DEFAULT_HOST = "localhost";
144
+ const DEFAULT_HOST$1 = "localhost";
143
145
  const TRANSPORT_TYPE_STDIO = "stdio";
144
146
  const TRANSPORT_TYPE_HTTP = "http";
145
147
  const TRANSPORT_TYPE_SSE = "sse";
146
148
  const TRANSPORT_TYPE_STDIO_HTTP = "stdio-http";
147
149
  const RUNTIME_TRANSPORT = TRANSPORT_TYPE_HTTP;
148
- const STDIO_HTTP_PROXY_STOP_LABEL = "Failed stopping stdio-http proxy";
149
- const INTERNAL_HTTP_STOP_LABEL = "Failed stopping internal HTTP transport";
150
150
  const PORT_REGISTRY_SERVICE_HTTP = "mcp-proxy-http";
151
- const PORT_REGISTRY_SERVICE_STDIO_HTTP = "mcp-proxy-stdio-http";
152
151
  const PORT_REGISTRY_SERVICE_TYPE = "service";
153
152
  const PORT_REGISTRY_REPOSITORY_PATH = process.cwd();
154
153
  const PROCESS_REGISTRY_SERVICE_HTTP = "mcp-proxy-http";
155
- const PROCESS_REGISTRY_SERVICE_STDIO_HTTP = "mcp-proxy-stdio-http";
156
154
  const PROCESS_REGISTRY_SERVICE_TYPE = "service";
157
155
  const PROCESS_REGISTRY_REPOSITORY_PATH = process.cwd();
158
156
  function toErrorMessage$9(error) {
@@ -164,13 +162,6 @@ function isValidTransportType(type) {
164
162
  function isValidProxyMode(mode) {
165
163
  return mode === "meta" || mode === "flat" || mode === "search";
166
164
  }
167
- function isAddressInUseError(error) {
168
- if (error instanceof Error && error.message.includes("EADDRINUSE")) return true;
169
- if (typeof error !== "object" || error === null) return false;
170
- if ("code" in error && error.code === "EADDRINUSE") return true;
171
- if ("message" in error && typeof error.message === "string" && error.message.includes("EADDRINUSE")) return true;
172
- return false;
173
- }
174
165
  async function pathExists(filePath) {
175
166
  try {
176
167
  await (0, node_fs_promises.access)(filePath, node_fs.constants.F_OK);
@@ -218,10 +209,11 @@ function validateProxyMode(mode) {
218
209
  if (!isValidProxyMode(mode)) throw new Error(`Unknown proxy mode: '${mode}'. Valid options: meta, flat, search`);
219
210
  }
220
211
  function createTransportConfig(options, mode) {
212
+ const envPort = process.env.MCP_PORT ? Number(process.env.MCP_PORT) : void 0;
221
213
  return {
222
214
  mode,
223
- port: options.port || Number(process.env.MCP_PORT) || DEFAULT_PORT,
224
- host: options.host || process.env.MCP_HOST || DEFAULT_HOST
215
+ port: options.port ?? (Number.isFinite(envPort) ? envPort : void 0),
216
+ host: options.host || process.env.MCP_HOST || DEFAULT_HOST$1
225
217
  };
226
218
  }
227
219
  function createServerOptions(options, resolvedConfigPath, serverId) {
@@ -237,12 +229,12 @@ function createServerOptions(options, resolvedConfigPath, serverId) {
237
229
  function formatStartError(type, host, port, error) {
238
230
  const startErrorMessage = toErrorMessage$9(error);
239
231
  if (type === TRANSPORT_TYPE_STDIO) return `Failed to start MCP server with transport '${type}': ${startErrorMessage}`;
240
- return `Failed to start MCP server with transport '${type}' on ${host}:${port}: ${startErrorMessage}`;
232
+ return `Failed to start MCP server with transport '${type}' on ${port === void 0 ? `${host} (dynamic port)` : `${host}:${port}`}: ${startErrorMessage}`;
241
233
  }
242
234
  function createRuntimeRecord(serverId, config, port, shutdownToken, configPath) {
243
235
  return {
244
236
  serverId,
245
- host: config.host ?? DEFAULT_HOST,
237
+ host: config.host ?? DEFAULT_HOST$1,
246
238
  port,
247
239
  transport: RUNTIME_TRANSPORT,
248
240
  shutdownToken,
@@ -260,7 +252,10 @@ function createProcessRegistryService() {
260
252
  function getRegistryEnvironment() {
261
253
  return process.env.NODE_ENV ?? "development";
262
254
  }
263
- async function createPortRegistryLease(serviceName, host, preferredPort, serverId, transport, configPath) {
255
+ async function createPortRegistryLease(serviceName, host, preferredPort, serverId, transport, configPath, portRange = preferredPort !== void 0 ? {
256
+ min: preferredPort,
257
+ max: preferredPort
258
+ } : __agimon_ai_foundation_port_registry.DEFAULT_PORT_RANGE) {
264
259
  const portRegistry = createPortRegistryService();
265
260
  const result = await portRegistry.reservePort({
266
261
  repositoryPath: PORT_REGISTRY_REPOSITORY_PATH,
@@ -270,10 +265,7 @@ async function createPortRegistryLease(serviceName, host, preferredPort, serverI
270
265
  pid: process.pid,
271
266
  host,
272
267
  preferredPort,
273
- portRange: {
274
- min: preferredPort,
275
- max: preferredPort
276
- },
268
+ portRange,
277
269
  force: true,
278
270
  metadata: {
279
271
  transport,
@@ -281,21 +273,27 @@ async function createPortRegistryLease(serviceName, host, preferredPort, serverI
281
273
  ...configPath ? { configPath } : {}
282
274
  }
283
275
  });
284
- if (!result.success || !result.record) throw new Error(result.error || `Failed to reserve port ${preferredPort} in port registry`);
276
+ if (!result.success || !result.record) {
277
+ const requestedPortLabel = preferredPort === void 0 ? "dynamic port" : `port ${preferredPort}`;
278
+ throw new Error(result.error || `Failed to reserve ${requestedPortLabel} in port registry`);
279
+ }
285
280
  let released = false;
286
- return { release: async () => {
287
- if (released) return;
288
- released = true;
289
- const releaseResult = await portRegistry.releasePort({
290
- repositoryPath: PORT_REGISTRY_REPOSITORY_PATH,
291
- serviceName,
292
- serviceType: PORT_REGISTRY_SERVICE_TYPE,
293
- pid: process.pid,
294
- environment: getRegistryEnvironment(),
295
- force: true
296
- });
297
- if (!releaseResult.success && releaseResult.error && !releaseResult.error.includes("No matching registry entry")) throw new Error(releaseResult.error || `Failed to release port for ${serviceName}`);
298
- } };
281
+ return {
282
+ port: result.record.port,
283
+ release: async () => {
284
+ if (released) return;
285
+ released = true;
286
+ const releaseResult = await portRegistry.releasePort({
287
+ repositoryPath: PORT_REGISTRY_REPOSITORY_PATH,
288
+ serviceName,
289
+ serviceType: PORT_REGISTRY_SERVICE_TYPE,
290
+ pid: process.pid,
291
+ environment: getRegistryEnvironment(),
292
+ force: true
293
+ });
294
+ if (!releaseResult.success && releaseResult.error && !releaseResult.error.includes("No matching registry entry")) throw new Error(releaseResult.error || `Failed to release port for ${serviceName}`);
295
+ }
296
+ };
299
297
  }
300
298
  async function createProcessRegistryLease(serviceName, host, port, serverId, transport, configPath) {
301
299
  const processRegistry = createProcessRegistryService();
@@ -387,27 +385,6 @@ async function cleanupFailedRuntimeStartup(handler, runtimeStateService, serverI
387
385
  await removeRuntimeRecord(runtimeStateService, serverId);
388
386
  }
389
387
  }
390
- async function stopTransportWithContext(label, stopOperation) {
391
- try {
392
- await stopOperation();
393
- } catch (error) {
394
- throw new Error(`${label}: ${toErrorMessage$9(error)}`);
395
- }
396
- }
397
- async function removeRuntimeRecordDuringStop(runtimeStateService, serverId) {
398
- try {
399
- await removeRuntimeRecord(runtimeStateService, serverId);
400
- } catch (error) {
401
- throw new Error(`Failed to remove runtime state during HTTP stop callback for '${serverId}': ${toErrorMessage$9(error)}`);
402
- }
403
- }
404
- function createStdioHttpInternalTransport(sharedServices, config, adminOptions) {
405
- try {
406
- return new require_src.HttpTransportHandler(() => require_src.createSessionServer(sharedServices), config, adminOptions);
407
- } catch (error) {
408
- throw new Error(`Failed to create internal HTTP transport for stdio-http proxy: ${toErrorMessage$9(error)}`);
409
- }
410
- }
411
388
  /**
412
389
  * Start MCP server with given transport handler
413
390
  * @param handler - The transport handler to start
@@ -438,13 +415,23 @@ async function createAndStartHttpRuntime(serverOptions, config, resolvedConfigPa
438
415
  const runtimeStateService = new require_src.RuntimeStateService();
439
416
  const shutdownToken = (0, node_crypto.randomUUID)();
440
417
  const runtimeServerId = serverOptions.serverId ?? require_src.generateServerId();
441
- const processLease = await createProcessRegistryLease(PROCESS_REGISTRY_SERVICE_HTTP, config.host ?? DEFAULT_HOST, config.port ?? DEFAULT_PORT, runtimeServerId, TRANSPORT_TYPE_HTTP, resolvedConfigPath);
442
- const portLease = await createPortRegistryLease(PORT_REGISTRY_SERVICE_HTTP, config.host ?? DEFAULT_HOST, config.port ?? DEFAULT_PORT, runtimeServerId, TRANSPORT_TYPE_HTTP, resolvedConfigPath);
418
+ const requestedPort = config.port;
419
+ const portRange = requestedPort !== void 0 ? {
420
+ min: requestedPort,
421
+ max: requestedPort
422
+ } : __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);
424
+ const runtimePort = portLease.port;
425
+ const runtimeConfig = {
426
+ ...config,
427
+ port: runtimePort
428
+ };
429
+ const processLease = await createProcessRegistryLease(PROCESS_REGISTRY_SERVICE_HTTP, runtimeConfig.host ?? DEFAULT_HOST$1, runtimePort, runtimeServerId, TRANSPORT_TYPE_HTTP, resolvedConfigPath);
443
430
  let releasePort = async () => {
444
- await releasePortLease(portLease);
431
+ await releasePortLease(portLease ?? null);
445
432
  releasePort = async () => void 0;
446
433
  };
447
- const runtimeRecord = createRuntimeRecord(runtimeServerId, config, config.port ?? DEFAULT_PORT, shutdownToken, resolvedConfigPath);
434
+ const runtimeRecord = createRuntimeRecord(runtimeServerId, runtimeConfig, runtimePort, shutdownToken, resolvedConfigPath);
448
435
  let handler;
449
436
  let isStopping = false;
450
437
  const stopHandler = async () => {
@@ -460,7 +447,7 @@ async function createAndStartHttpRuntime(serverOptions, config, resolvedConfigPa
460
447
  }
461
448
  };
462
449
  try {
463
- handler = new require_src.HttpTransportHandler(() => require_src.createSessionServer(sharedServices), config, createHttpAdminOptions(runtimeRecord.serverId, shutdownToken, stopHandler));
450
+ handler = new require_src.HttpTransportHandler(() => require_src.createSessionServer(sharedServices), runtimeConfig, createHttpAdminOptions(runtimeRecord.serverId, shutdownToken, stopHandler));
464
451
  } catch (error) {
465
452
  await releasePort();
466
453
  await processLease.release({
@@ -478,7 +465,7 @@ async function createAndStartHttpRuntime(serverOptions, config, resolvedConfigPa
478
465
  kill: false,
479
466
  releasePort: false
480
467
  });
481
- await removeRuntimeRecordDuringStop(runtimeStateService, runtimeRecord.serverId);
468
+ await removeRuntimeRecord(runtimeStateService, runtimeRecord.serverId);
482
469
  });
483
470
  await writeRuntimeRecord(runtimeStateService, runtimeRecord);
484
471
  } catch (error) {
@@ -493,19 +480,6 @@ async function createAndStartHttpRuntime(serverOptions, config, resolvedConfigPa
493
480
  }
494
481
  console.error(`Runtime state: http://${runtimeRecord.host}:${runtimeRecord.port} (${runtimeRecord.serverId})`);
495
482
  }
496
- async function stopInternalHttpTransport(stdioHttpHandler, httpHandler, ownsInternalHttpTransport) {
497
- try {
498
- const stopOperations = [stopTransportWithContext(STDIO_HTTP_PROXY_STOP_LABEL, async () => {
499
- await stdioHttpHandler.stop();
500
- })];
501
- if (ownsInternalHttpTransport && httpHandler) stopOperations.push(stopTransportWithContext(INTERNAL_HTTP_STOP_LABEL, async () => {
502
- await httpHandler.stop();
503
- }));
504
- await Promise.all(stopOperations);
505
- } catch (error) {
506
- throw new Error(`Failed to stop stdio-http transport: ${toErrorMessage$9(error)}`);
507
- }
508
- }
509
483
  async function startStdioTransport(serverOptions) {
510
484
  try {
511
485
  await startServer(new require_src.StdioTransportHandler(await require_src.createServer(serverOptions)));
@@ -520,109 +494,21 @@ async function startSseTransport(serverOptions, config) {
520
494
  throw new Error(`Failed to start SSE transport: ${toErrorMessage$9(error)}`);
521
495
  }
522
496
  }
523
- async function startStdioHttpTransport(serverOptions, config, resolvedConfigPath) {
524
- const shared = { services: null };
525
- let processLease;
526
- let internalPortLease = null;
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}`);
499
+ const result = await createPortRegistryService().getPort({
500
+ repositoryPath: PORT_REGISTRY_REPOSITORY_PATH,
501
+ serviceName: PORT_REGISTRY_SERVICE_HTTP,
502
+ serviceType: PORT_REGISTRY_SERVICE_TYPE,
503
+ environment: getRegistryEnvironment()
504
+ });
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}`);
507
+ }
508
+ async function startStdioHttpTransport(config) {
527
509
  try {
528
- const stdioHttpHandler = new require_src.StdioHttpTransportHandler({ endpoint: new URL(`http://${config.host}:${config.port}${MCP_ENDPOINT_PATH}`) });
529
- const runtimeStateService = new require_src.RuntimeStateService();
530
- const serverId = serverOptions.serverId ?? require_src.generateServerId();
531
- const shutdownToken = (0, node_crypto.randomUUID)();
532
- processLease = await createProcessRegistryLease(PROCESS_REGISTRY_SERVICE_STDIO_HTTP, config.host ?? DEFAULT_HOST, config.port ?? DEFAULT_PORT, serverId, TRANSPORT_TYPE_STDIO_HTTP, resolvedConfigPath);
533
- let httpHandler = null;
534
- let ownsInternalHttpTransport = false;
535
- let isStopping = false;
536
- let releaseInternalPort = async () => {
537
- await releasePortLease(internalPortLease);
538
- internalPortLease = null;
539
- releaseInternalPort = async () => void 0;
540
- };
541
- const stopOwnedRuntime = async () => {
542
- if (isStopping) return;
543
- isStopping = true;
544
- try {
545
- await Promise.all([stopInternalHttpTransport(stdioHttpHandler, httpHandler, ownsInternalHttpTransport), removeRuntimeRecord(runtimeStateService, serverId)]);
546
- await releaseInternalPort();
547
- if (shared.services) await shared.services.dispose();
548
- ownsInternalHttpTransport = false;
549
- process.exit(0);
550
- } catch (error) {
551
- console.error(`Unexpected error during admin shutdown: ${toErrorMessage$9(error)}`);
552
- process.exit(1);
553
- }
554
- };
555
- const adminOptions = createHttpAdminOptions(serverId, shutdownToken, stopOwnedRuntime);
556
- await startServer({
557
- async start() {
558
- let initialProxyConnectError;
559
- try {
560
- await stdioHttpHandler.start();
561
- return;
562
- } catch (error) {
563
- initialProxyConnectError = error;
564
- }
565
- if (!shared.services) shared.services = await require_src.initializeSharedServices(serverOptions);
566
- try {
567
- internalPortLease = await createPortRegistryLease(PORT_REGISTRY_SERVICE_STDIO_HTTP, config.host ?? DEFAULT_HOST, config.port ?? DEFAULT_PORT, serverId, TRANSPORT_TYPE_STDIO_HTTP, resolvedConfigPath);
568
- httpHandler = createStdioHttpInternalTransport(shared.services, config, adminOptions);
569
- await httpHandler.start();
570
- ownsInternalHttpTransport = true;
571
- } catch (error) {
572
- if (!isAddressInUseError(error)) {
573
- await releaseInternalPort();
574
- throw new Error(`Failed to start internal HTTP transport for stdio-http proxy: ${toErrorMessage$9(error)}`);
575
- }
576
- }
577
- try {
578
- await stdioHttpHandler.start();
579
- } catch (error) {
580
- let rollbackStopErrorMessage = "";
581
- if (ownsInternalHttpTransport && httpHandler) {
582
- try {
583
- await httpHandler.stop();
584
- } catch (stopError) {
585
- rollbackStopErrorMessage = toErrorMessage$9(stopError);
586
- }
587
- ownsInternalHttpTransport = false;
588
- }
589
- await releaseInternalPort();
590
- const retryErrorMessage = toErrorMessage$9(error);
591
- const initialErrorMessage = toErrorMessage$9(initialProxyConnectError);
592
- const rollbackMessage = rollbackStopErrorMessage ? `; rollback stop failed: ${rollbackStopErrorMessage}` : "";
593
- throw new Error(`Failed to start stdio-http proxy bridge: initial connect failed (${initialErrorMessage}); retry failed (${retryErrorMessage})${rollbackMessage}`);
594
- }
595
- if (ownsInternalHttpTransport) try {
596
- await writeRuntimeRecord(runtimeStateService, createRuntimeRecord(serverId, config, config.port ?? DEFAULT_PORT, shutdownToken, resolvedConfigPath));
597
- } catch (error) {
598
- throw new Error(`Failed to persist runtime state for stdio-http server '${serverId}': ${toErrorMessage$9(error)}`);
599
- }
600
- },
601
- async stop() {
602
- try {
603
- await Promise.all([stopInternalHttpTransport(stdioHttpHandler, httpHandler, ownsInternalHttpTransport), removeRuntimeRecord(runtimeStateService, serverId)]);
604
- await releaseInternalPort();
605
- await processLease?.release({
606
- kill: false,
607
- releasePort: false
608
- });
609
- ownsInternalHttpTransport = false;
610
- if (shared.services) await shared.services.dispose();
611
- } catch (error) {
612
- ownsInternalHttpTransport = false;
613
- throw new Error(`Failed during stdio-http shutdown for '${serverId}': ${toErrorMessage$9(error)}`);
614
- }
615
- }
616
- });
510
+ await startServer(new require_src.StdioHttpTransportHandler({ endpoint: await resolveStdioHttpEndpoint(config) }));
617
511
  } catch (error) {
618
- try {
619
- await releasePortLease(internalPortLease);
620
- await processLease?.release({
621
- kill: false,
622
- releasePort: false
623
- });
624
- } catch {}
625
- if (shared.services) await shared.services.dispose();
626
512
  throw new Error(`Failed to start stdio-http transport: ${toErrorMessage$9(error)}`);
627
513
  }
628
514
  }
@@ -640,7 +526,7 @@ async function startTransport(transportType, options, resolvedConfigPath, server
640
526
  await startSseTransport(serverOptions, createTransportConfig(options, require_src.TRANSPORT_MODE.SSE));
641
527
  return;
642
528
  }
643
- await startStdioHttpTransport(serverOptions, createTransportConfig(options, require_src.TRANSPORT_MODE.HTTP), resolvedConfigPath);
529
+ await startStdioHttpTransport(createTransportConfig(options, require_src.TRANSPORT_MODE.HTTP));
644
530
  } catch (error) {
645
531
  throw new Error(`Failed to start transport '${transportType}': ${toErrorMessage$9(error)}`);
646
532
  }
@@ -648,7 +534,7 @@ async function startTransport(transportType, options, resolvedConfigPath, server
648
534
  /**
649
535
  * MCP Serve command
650
536
  */
651
- 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/stdio-http internal HTTP)", (val) => parseInt(val, 10), DEFAULT_PORT).option("--host <host>", "Host to bind to (http/sse/stdio-http internal 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
+ 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) => {
652
538
  try {
653
539
  const transportType = validateTransportType(options.type.toLowerCase());
654
540
  validateProxyMode(options.proxyMode);
@@ -657,11 +543,163 @@ const mcpServeCommand = new commander.Command("mcp-serve").description("Start MC
657
543
  } catch (error) {
658
544
  const rawTransportType = options.type.toLowerCase();
659
545
  const transportType = isValidTransportType(rawTransportType) ? rawTransportType : TRANSPORT_TYPE_STDIO;
660
- console.error(formatStartError(transportType, options.host, options.port, error));
546
+ const envPort = process.env.MCP_PORT ? Number(process.env.MCP_PORT) : void 0;
547
+ const requestedPort = options.port ?? (Number.isFinite(envPort) ? envPort : void 0);
548
+ console.error(formatStartError(transportType, options.host, requestedPort, error));
661
549
  process.exit(1);
662
550
  }
663
551
  });
664
552
 
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
+
665
703
  //#endregion
666
704
  //#region src/commands/bootstrap.ts
667
705
  function toErrorMessage$8(error) {
@@ -1490,6 +1528,7 @@ async function main() {
1490
1528
  program.name("mcp-proxy").description("MCP proxy server package").version(require_src.version);
1491
1529
  program.addCommand(initCommand);
1492
1530
  program.addCommand(mcpServeCommand);
1531
+ program.addCommand(prestartHttpCommand);
1493
1532
  program.addCommand(searchToolsCommand);
1494
1533
  program.addCommand(describeToolsCommand);
1495
1534
  program.addCommand(useToolCommand);
package/dist/cli.mjs CHANGED
@@ -1,13 +1,15 @@
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-DQ_MagA0.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-0OJqEpGA.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { access, writeFile } from "node:fs/promises";
5
- import { join, resolve } from "node:path";
6
- import { constants } from "node:fs";
5
+ import path, { join, resolve } from "node:path";
6
+ import { constants, existsSync } from "node:fs";
7
7
  import { Liquid } from "liquidjs";
8
+ import { spawn } from "node:child_process";
8
9
  import { Command } from "commander";
9
10
  import { ProcessRegistryService } from "@agimon-ai/foundation-process-registry";
10
- import { PortRegistryService } from "@agimon-ai/foundation-port-registry";
11
+ import { DEFAULT_PORT_RANGE, PortRegistryService } from "@agimon-ai/foundation-port-registry";
12
+ import { fileURLToPath } from "node:url";
11
13
 
12
14
  //#region src/utils/output.ts
13
15
  function writeLine(message = "") {
@@ -138,21 +140,16 @@ const CONFIG_FILE_NAMES = [
138
140
  "mcp-config.json"
139
141
  ];
140
142
  const MCP_ENDPOINT_PATH = "/mcp";
141
- const DEFAULT_PORT = 3e3;
142
- const DEFAULT_HOST = "localhost";
143
+ const DEFAULT_HOST$1 = "localhost";
143
144
  const TRANSPORT_TYPE_STDIO = "stdio";
144
145
  const TRANSPORT_TYPE_HTTP = "http";
145
146
  const TRANSPORT_TYPE_SSE = "sse";
146
147
  const TRANSPORT_TYPE_STDIO_HTTP = "stdio-http";
147
148
  const RUNTIME_TRANSPORT = TRANSPORT_TYPE_HTTP;
148
- const STDIO_HTTP_PROXY_STOP_LABEL = "Failed stopping stdio-http proxy";
149
- const INTERNAL_HTTP_STOP_LABEL = "Failed stopping internal HTTP transport";
150
149
  const PORT_REGISTRY_SERVICE_HTTP = "mcp-proxy-http";
151
- const PORT_REGISTRY_SERVICE_STDIO_HTTP = "mcp-proxy-stdio-http";
152
150
  const PORT_REGISTRY_SERVICE_TYPE = "service";
153
151
  const PORT_REGISTRY_REPOSITORY_PATH = process.cwd();
154
152
  const PROCESS_REGISTRY_SERVICE_HTTP = "mcp-proxy-http";
155
- const PROCESS_REGISTRY_SERVICE_STDIO_HTTP = "mcp-proxy-stdio-http";
156
153
  const PROCESS_REGISTRY_SERVICE_TYPE = "service";
157
154
  const PROCESS_REGISTRY_REPOSITORY_PATH = process.cwd();
158
155
  function toErrorMessage$9(error) {
@@ -164,13 +161,6 @@ function isValidTransportType(type) {
164
161
  function isValidProxyMode(mode) {
165
162
  return mode === "meta" || mode === "flat" || mode === "search";
166
163
  }
167
- function isAddressInUseError(error) {
168
- if (error instanceof Error && error.message.includes("EADDRINUSE")) return true;
169
- if (typeof error !== "object" || error === null) return false;
170
- if ("code" in error && error.code === "EADDRINUSE") return true;
171
- if ("message" in error && typeof error.message === "string" && error.message.includes("EADDRINUSE")) return true;
172
- return false;
173
- }
174
164
  async function pathExists(filePath) {
175
165
  try {
176
166
  await access(filePath, constants.F_OK);
@@ -218,10 +208,11 @@ function validateProxyMode(mode) {
218
208
  if (!isValidProxyMode(mode)) throw new Error(`Unknown proxy mode: '${mode}'. Valid options: meta, flat, search`);
219
209
  }
220
210
  function createTransportConfig(options, mode) {
211
+ const envPort = process.env.MCP_PORT ? Number(process.env.MCP_PORT) : void 0;
221
212
  return {
222
213
  mode,
223
- port: options.port || Number(process.env.MCP_PORT) || DEFAULT_PORT,
224
- host: options.host || process.env.MCP_HOST || DEFAULT_HOST
214
+ port: options.port ?? (Number.isFinite(envPort) ? envPort : void 0),
215
+ host: options.host || process.env.MCP_HOST || DEFAULT_HOST$1
225
216
  };
226
217
  }
227
218
  function createServerOptions(options, resolvedConfigPath, serverId) {
@@ -237,12 +228,12 @@ function createServerOptions(options, resolvedConfigPath, serverId) {
237
228
  function formatStartError(type, host, port, error) {
238
229
  const startErrorMessage = toErrorMessage$9(error);
239
230
  if (type === TRANSPORT_TYPE_STDIO) return `Failed to start MCP server with transport '${type}': ${startErrorMessage}`;
240
- return `Failed to start MCP server with transport '${type}' on ${host}:${port}: ${startErrorMessage}`;
231
+ return `Failed to start MCP server with transport '${type}' on ${port === void 0 ? `${host} (dynamic port)` : `${host}:${port}`}: ${startErrorMessage}`;
241
232
  }
242
233
  function createRuntimeRecord(serverId, config, port, shutdownToken, configPath) {
243
234
  return {
244
235
  serverId,
245
- host: config.host ?? DEFAULT_HOST,
236
+ host: config.host ?? DEFAULT_HOST$1,
246
237
  port,
247
238
  transport: RUNTIME_TRANSPORT,
248
239
  shutdownToken,
@@ -260,7 +251,10 @@ function createProcessRegistryService() {
260
251
  function getRegistryEnvironment() {
261
252
  return process.env.NODE_ENV ?? "development";
262
253
  }
263
- async function createPortRegistryLease(serviceName, host, preferredPort, serverId, transport, configPath) {
254
+ async function createPortRegistryLease(serviceName, host, preferredPort, serverId, transport, configPath, portRange = preferredPort !== void 0 ? {
255
+ min: preferredPort,
256
+ max: preferredPort
257
+ } : DEFAULT_PORT_RANGE) {
264
258
  const portRegistry = createPortRegistryService();
265
259
  const result = await portRegistry.reservePort({
266
260
  repositoryPath: PORT_REGISTRY_REPOSITORY_PATH,
@@ -270,10 +264,7 @@ async function createPortRegistryLease(serviceName, host, preferredPort, serverI
270
264
  pid: process.pid,
271
265
  host,
272
266
  preferredPort,
273
- portRange: {
274
- min: preferredPort,
275
- max: preferredPort
276
- },
267
+ portRange,
277
268
  force: true,
278
269
  metadata: {
279
270
  transport,
@@ -281,21 +272,27 @@ async function createPortRegistryLease(serviceName, host, preferredPort, serverI
281
272
  ...configPath ? { configPath } : {}
282
273
  }
283
274
  });
284
- if (!result.success || !result.record) throw new Error(result.error || `Failed to reserve port ${preferredPort} in port registry`);
275
+ if (!result.success || !result.record) {
276
+ const requestedPortLabel = preferredPort === void 0 ? "dynamic port" : `port ${preferredPort}`;
277
+ throw new Error(result.error || `Failed to reserve ${requestedPortLabel} in port registry`);
278
+ }
285
279
  let released = false;
286
- return { release: async () => {
287
- if (released) return;
288
- released = true;
289
- const releaseResult = await portRegistry.releasePort({
290
- repositoryPath: PORT_REGISTRY_REPOSITORY_PATH,
291
- serviceName,
292
- serviceType: PORT_REGISTRY_SERVICE_TYPE,
293
- pid: process.pid,
294
- environment: getRegistryEnvironment(),
295
- force: true
296
- });
297
- if (!releaseResult.success && releaseResult.error && !releaseResult.error.includes("No matching registry entry")) throw new Error(releaseResult.error || `Failed to release port for ${serviceName}`);
298
- } };
280
+ return {
281
+ port: result.record.port,
282
+ release: async () => {
283
+ if (released) return;
284
+ released = true;
285
+ const releaseResult = await portRegistry.releasePort({
286
+ repositoryPath: PORT_REGISTRY_REPOSITORY_PATH,
287
+ serviceName,
288
+ serviceType: PORT_REGISTRY_SERVICE_TYPE,
289
+ pid: process.pid,
290
+ environment: getRegistryEnvironment(),
291
+ force: true
292
+ });
293
+ if (!releaseResult.success && releaseResult.error && !releaseResult.error.includes("No matching registry entry")) throw new Error(releaseResult.error || `Failed to release port for ${serviceName}`);
294
+ }
295
+ };
299
296
  }
300
297
  async function createProcessRegistryLease(serviceName, host, port, serverId, transport, configPath) {
301
298
  const processRegistry = createProcessRegistryService();
@@ -387,27 +384,6 @@ async function cleanupFailedRuntimeStartup(handler, runtimeStateService, serverI
387
384
  await removeRuntimeRecord(runtimeStateService, serverId);
388
385
  }
389
386
  }
390
- async function stopTransportWithContext(label, stopOperation) {
391
- try {
392
- await stopOperation();
393
- } catch (error) {
394
- throw new Error(`${label}: ${toErrorMessage$9(error)}`);
395
- }
396
- }
397
- async function removeRuntimeRecordDuringStop(runtimeStateService, serverId) {
398
- try {
399
- await removeRuntimeRecord(runtimeStateService, serverId);
400
- } catch (error) {
401
- throw new Error(`Failed to remove runtime state during HTTP stop callback for '${serverId}': ${toErrorMessage$9(error)}`);
402
- }
403
- }
404
- function createStdioHttpInternalTransport(sharedServices, config, adminOptions) {
405
- try {
406
- return new HttpTransportHandler(() => createSessionServer(sharedServices), config, adminOptions);
407
- } catch (error) {
408
- throw new Error(`Failed to create internal HTTP transport for stdio-http proxy: ${toErrorMessage$9(error)}`);
409
- }
410
- }
411
387
  /**
412
388
  * Start MCP server with given transport handler
413
389
  * @param handler - The transport handler to start
@@ -438,13 +414,23 @@ async function createAndStartHttpRuntime(serverOptions, config, resolvedConfigPa
438
414
  const runtimeStateService = new RuntimeStateService();
439
415
  const shutdownToken = randomUUID();
440
416
  const runtimeServerId = serverOptions.serverId ?? generateServerId();
441
- const processLease = await createProcessRegistryLease(PROCESS_REGISTRY_SERVICE_HTTP, config.host ?? DEFAULT_HOST, config.port ?? DEFAULT_PORT, runtimeServerId, TRANSPORT_TYPE_HTTP, resolvedConfigPath);
442
- const portLease = await createPortRegistryLease(PORT_REGISTRY_SERVICE_HTTP, config.host ?? DEFAULT_HOST, config.port ?? DEFAULT_PORT, runtimeServerId, TRANSPORT_TYPE_HTTP, resolvedConfigPath);
417
+ const requestedPort = config.port;
418
+ const portRange = requestedPort !== void 0 ? {
419
+ min: requestedPort,
420
+ max: requestedPort
421
+ } : DEFAULT_PORT_RANGE;
422
+ const portLease = await createPortRegistryLease(PORT_REGISTRY_SERVICE_HTTP, config.host ?? DEFAULT_HOST$1, requestedPort, runtimeServerId, TRANSPORT_TYPE_HTTP, resolvedConfigPath, portRange);
423
+ const runtimePort = portLease.port;
424
+ const runtimeConfig = {
425
+ ...config,
426
+ port: runtimePort
427
+ };
428
+ const processLease = await createProcessRegistryLease(PROCESS_REGISTRY_SERVICE_HTTP, runtimeConfig.host ?? DEFAULT_HOST$1, runtimePort, runtimeServerId, TRANSPORT_TYPE_HTTP, resolvedConfigPath);
443
429
  let releasePort = async () => {
444
- await releasePortLease(portLease);
430
+ await releasePortLease(portLease ?? null);
445
431
  releasePort = async () => void 0;
446
432
  };
447
- const runtimeRecord = createRuntimeRecord(runtimeServerId, config, config.port ?? DEFAULT_PORT, shutdownToken, resolvedConfigPath);
433
+ const runtimeRecord = createRuntimeRecord(runtimeServerId, runtimeConfig, runtimePort, shutdownToken, resolvedConfigPath);
448
434
  let handler;
449
435
  let isStopping = false;
450
436
  const stopHandler = async () => {
@@ -460,7 +446,7 @@ async function createAndStartHttpRuntime(serverOptions, config, resolvedConfigPa
460
446
  }
461
447
  };
462
448
  try {
463
- handler = new HttpTransportHandler(() => createSessionServer(sharedServices), config, createHttpAdminOptions(runtimeRecord.serverId, shutdownToken, stopHandler));
449
+ handler = new HttpTransportHandler(() => createSessionServer(sharedServices), runtimeConfig, createHttpAdminOptions(runtimeRecord.serverId, shutdownToken, stopHandler));
464
450
  } catch (error) {
465
451
  await releasePort();
466
452
  await processLease.release({
@@ -478,7 +464,7 @@ async function createAndStartHttpRuntime(serverOptions, config, resolvedConfigPa
478
464
  kill: false,
479
465
  releasePort: false
480
466
  });
481
- await removeRuntimeRecordDuringStop(runtimeStateService, runtimeRecord.serverId);
467
+ await removeRuntimeRecord(runtimeStateService, runtimeRecord.serverId);
482
468
  });
483
469
  await writeRuntimeRecord(runtimeStateService, runtimeRecord);
484
470
  } catch (error) {
@@ -493,19 +479,6 @@ async function createAndStartHttpRuntime(serverOptions, config, resolvedConfigPa
493
479
  }
494
480
  console.error(`Runtime state: http://${runtimeRecord.host}:${runtimeRecord.port} (${runtimeRecord.serverId})`);
495
481
  }
496
- async function stopInternalHttpTransport(stdioHttpHandler, httpHandler, ownsInternalHttpTransport) {
497
- try {
498
- const stopOperations = [stopTransportWithContext(STDIO_HTTP_PROXY_STOP_LABEL, async () => {
499
- await stdioHttpHandler.stop();
500
- })];
501
- if (ownsInternalHttpTransport && httpHandler) stopOperations.push(stopTransportWithContext(INTERNAL_HTTP_STOP_LABEL, async () => {
502
- await httpHandler.stop();
503
- }));
504
- await Promise.all(stopOperations);
505
- } catch (error) {
506
- throw new Error(`Failed to stop stdio-http transport: ${toErrorMessage$9(error)}`);
507
- }
508
- }
509
482
  async function startStdioTransport(serverOptions) {
510
483
  try {
511
484
  await startServer(new StdioTransportHandler(await createServer(serverOptions)));
@@ -520,109 +493,21 @@ async function startSseTransport(serverOptions, config) {
520
493
  throw new Error(`Failed to start SSE transport: ${toErrorMessage$9(error)}`);
521
494
  }
522
495
  }
523
- async function startStdioHttpTransport(serverOptions, config, resolvedConfigPath) {
524
- const shared = { services: null };
525
- let processLease;
526
- let internalPortLease = null;
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}`);
498
+ const result = await createPortRegistryService().getPort({
499
+ repositoryPath: PORT_REGISTRY_REPOSITORY_PATH,
500
+ serviceName: PORT_REGISTRY_SERVICE_HTTP,
501
+ serviceType: PORT_REGISTRY_SERVICE_TYPE,
502
+ environment: getRegistryEnvironment()
503
+ });
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}`);
506
+ }
507
+ async function startStdioHttpTransport(config) {
527
508
  try {
528
- const stdioHttpHandler = new StdioHttpTransportHandler({ endpoint: new URL(`http://${config.host}:${config.port}${MCP_ENDPOINT_PATH}`) });
529
- const runtimeStateService = new RuntimeStateService();
530
- const serverId = serverOptions.serverId ?? generateServerId();
531
- const shutdownToken = randomUUID();
532
- processLease = await createProcessRegistryLease(PROCESS_REGISTRY_SERVICE_STDIO_HTTP, config.host ?? DEFAULT_HOST, config.port ?? DEFAULT_PORT, serverId, TRANSPORT_TYPE_STDIO_HTTP, resolvedConfigPath);
533
- let httpHandler = null;
534
- let ownsInternalHttpTransport = false;
535
- let isStopping = false;
536
- let releaseInternalPort = async () => {
537
- await releasePortLease(internalPortLease);
538
- internalPortLease = null;
539
- releaseInternalPort = async () => void 0;
540
- };
541
- const stopOwnedRuntime = async () => {
542
- if (isStopping) return;
543
- isStopping = true;
544
- try {
545
- await Promise.all([stopInternalHttpTransport(stdioHttpHandler, httpHandler, ownsInternalHttpTransport), removeRuntimeRecord(runtimeStateService, serverId)]);
546
- await releaseInternalPort();
547
- if (shared.services) await shared.services.dispose();
548
- ownsInternalHttpTransport = false;
549
- process.exit(0);
550
- } catch (error) {
551
- console.error(`Unexpected error during admin shutdown: ${toErrorMessage$9(error)}`);
552
- process.exit(1);
553
- }
554
- };
555
- const adminOptions = createHttpAdminOptions(serverId, shutdownToken, stopOwnedRuntime);
556
- await startServer({
557
- async start() {
558
- let initialProxyConnectError;
559
- try {
560
- await stdioHttpHandler.start();
561
- return;
562
- } catch (error) {
563
- initialProxyConnectError = error;
564
- }
565
- if (!shared.services) shared.services = await initializeSharedServices(serverOptions);
566
- try {
567
- internalPortLease = await createPortRegistryLease(PORT_REGISTRY_SERVICE_STDIO_HTTP, config.host ?? DEFAULT_HOST, config.port ?? DEFAULT_PORT, serverId, TRANSPORT_TYPE_STDIO_HTTP, resolvedConfigPath);
568
- httpHandler = createStdioHttpInternalTransport(shared.services, config, adminOptions);
569
- await httpHandler.start();
570
- ownsInternalHttpTransport = true;
571
- } catch (error) {
572
- if (!isAddressInUseError(error)) {
573
- await releaseInternalPort();
574
- throw new Error(`Failed to start internal HTTP transport for stdio-http proxy: ${toErrorMessage$9(error)}`);
575
- }
576
- }
577
- try {
578
- await stdioHttpHandler.start();
579
- } catch (error) {
580
- let rollbackStopErrorMessage = "";
581
- if (ownsInternalHttpTransport && httpHandler) {
582
- try {
583
- await httpHandler.stop();
584
- } catch (stopError) {
585
- rollbackStopErrorMessage = toErrorMessage$9(stopError);
586
- }
587
- ownsInternalHttpTransport = false;
588
- }
589
- await releaseInternalPort();
590
- const retryErrorMessage = toErrorMessage$9(error);
591
- const initialErrorMessage = toErrorMessage$9(initialProxyConnectError);
592
- const rollbackMessage = rollbackStopErrorMessage ? `; rollback stop failed: ${rollbackStopErrorMessage}` : "";
593
- throw new Error(`Failed to start stdio-http proxy bridge: initial connect failed (${initialErrorMessage}); retry failed (${retryErrorMessage})${rollbackMessage}`);
594
- }
595
- if (ownsInternalHttpTransport) try {
596
- await writeRuntimeRecord(runtimeStateService, createRuntimeRecord(serverId, config, config.port ?? DEFAULT_PORT, shutdownToken, resolvedConfigPath));
597
- } catch (error) {
598
- throw new Error(`Failed to persist runtime state for stdio-http server '${serverId}': ${toErrorMessage$9(error)}`);
599
- }
600
- },
601
- async stop() {
602
- try {
603
- await Promise.all([stopInternalHttpTransport(stdioHttpHandler, httpHandler, ownsInternalHttpTransport), removeRuntimeRecord(runtimeStateService, serverId)]);
604
- await releaseInternalPort();
605
- await processLease?.release({
606
- kill: false,
607
- releasePort: false
608
- });
609
- ownsInternalHttpTransport = false;
610
- if (shared.services) await shared.services.dispose();
611
- } catch (error) {
612
- ownsInternalHttpTransport = false;
613
- throw new Error(`Failed during stdio-http shutdown for '${serverId}': ${toErrorMessage$9(error)}`);
614
- }
615
- }
616
- });
509
+ await startServer(new StdioHttpTransportHandler({ endpoint: await resolveStdioHttpEndpoint(config) }));
617
510
  } catch (error) {
618
- try {
619
- await releasePortLease(internalPortLease);
620
- await processLease?.release({
621
- kill: false,
622
- releasePort: false
623
- });
624
- } catch {}
625
- if (shared.services) await shared.services.dispose();
626
511
  throw new Error(`Failed to start stdio-http transport: ${toErrorMessage$9(error)}`);
627
512
  }
628
513
  }
@@ -640,7 +525,7 @@ async function startTransport(transportType, options, resolvedConfigPath, server
640
525
  await startSseTransport(serverOptions, createTransportConfig(options, TRANSPORT_MODE.SSE));
641
526
  return;
642
527
  }
643
- await startStdioHttpTransport(serverOptions, createTransportConfig(options, TRANSPORT_MODE.HTTP), resolvedConfigPath);
528
+ await startStdioHttpTransport(createTransportConfig(options, TRANSPORT_MODE.HTTP));
644
529
  } catch (error) {
645
530
  throw new Error(`Failed to start transport '${transportType}': ${toErrorMessage$9(error)}`);
646
531
  }
@@ -648,7 +533,7 @@ async function startTransport(transportType, options, resolvedConfigPath, server
648
533
  /**
649
534
  * MCP Serve command
650
535
  */
651
- 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/stdio-http internal HTTP)", (val) => parseInt(val, 10), DEFAULT_PORT).option("--host <host>", "Host to bind to (http/sse/stdio-http internal 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) => {
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) => {
652
537
  try {
653
538
  const transportType = validateTransportType(options.type.toLowerCase());
654
539
  validateProxyMode(options.proxyMode);
@@ -657,11 +542,163 @@ const mcpServeCommand = new Command("mcp-serve").description("Start MCP server w
657
542
  } catch (error) {
658
543
  const rawTransportType = options.type.toLowerCase();
659
544
  const transportType = isValidTransportType(rawTransportType) ? rawTransportType : TRANSPORT_TYPE_STDIO;
660
- console.error(formatStartError(transportType, options.host, options.port, error));
545
+ const envPort = process.env.MCP_PORT ? Number(process.env.MCP_PORT) : void 0;
546
+ const requestedPort = options.port ?? (Number.isFinite(envPort) ? envPort : void 0);
547
+ console.error(formatStartError(transportType, options.host, requestedPort, error));
661
548
  process.exit(1);
662
549
  }
663
550
  });
664
551
 
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
+
665
702
  //#endregion
666
703
  //#region src/commands/bootstrap.ts
667
704
  function toErrorMessage$8(error) {
@@ -1490,6 +1527,7 @@ async function main() {
1490
1527
  program.name("mcp-proxy").description("MCP proxy server package").version(version);
1491
1528
  program.addCommand(initCommand);
1492
1529
  program.addCommand(mcpServeCommand);
1530
+ program.addCommand(prestartHttpCommand);
1493
1531
  program.addCommand(searchToolsCommand);
1494
1532
  program.addCommand(describeToolsCommand);
1495
1533
  program.addCommand(useToolCommand);
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_src = require('./src-6KF7hZTe.cjs');
1
+ const require_src = require('./src-G1hs2GLZ.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-DQ_MagA0.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-0OJqEpGA.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 };
@@ -2758,9 +2758,9 @@ function isShutdownResponse(value) {
2758
2758
  * @param path - Request path to append
2759
2759
  * @returns Full runtime URL
2760
2760
  */
2761
- function buildRuntimeUrl(runtime, path) {
2761
+ function buildRuntimeUrl(runtime, path$1) {
2762
2762
  if (!ALLOWED_HOSTS.has(runtime.host)) throw new Error(`Refusing to connect to non-loopback host '${runtime.host}'. Only ${Array.from(ALLOWED_HOSTS).join(", ")} are allowed.`);
2763
- return `${HTTP_PROTOCOL}${runtime.host}${URL_PORT_SEPARATOR}${runtime.port}${path}`;
2763
+ return `${HTTP_PROTOCOL}${runtime.host}${URL_PORT_SEPARATOR}${runtime.port}${path$1}`;
2764
2764
  }
2765
2765
  function toErrorMessage$1(error) {
2766
2766
  return error instanceof Error ? error.message : String(error);
@@ -2938,13 +2938,13 @@ var SkillLoadError = class extends Error {
2938
2938
  * @returns true if path exists, false otherwise
2939
2939
  * @throws Error for unexpected filesystem errors (permission denied, etc.)
2940
2940
  */
2941
- async function pathExists(path) {
2941
+ async function pathExists(path$1) {
2942
2942
  try {
2943
- await access(path);
2943
+ await access(path$1);
2944
2944
  return true;
2945
2945
  } catch (error) {
2946
2946
  if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
2947
- throw new Error(`Failed to check path existence for "${path}": ${error instanceof Error ? error.message : "Unknown error"}`);
2947
+ throw new Error(`Failed to check path existence for "${path$1}": ${error instanceof Error ? error.message : "Unknown error"}`);
2948
2948
  }
2949
2949
  }
2950
2950
  /**
@@ -4313,7 +4313,7 @@ var StdioHttpTransportHandler = class {
4313
4313
 
4314
4314
  //#endregion
4315
4315
  //#region package.json
4316
- var version = "0.3.19";
4316
+ var version = "0.4.2";
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.3.19";
4345
+ var version = "0.4.2";
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.1",
4
+ "version": "0.4.3",
5
5
  "license": "AGPL-3.0",
6
6
  "keywords": [
7
7
  "mcp",
@@ -27,9 +27,9 @@
27
27
  "js-yaml": "^4.1.0",
28
28
  "liquidjs": "^10.21.0",
29
29
  "zod": "^3.24.1",
30
- "@agimon-ai/foundation-process-registry": "0.2.1",
31
- "@agimon-ai/foundation-port-registry": "0.2.5",
32
- "@agimon-ai/log-sink-mcp": "0.2.5"
30
+ "@agimon-ai/foundation-process-registry": "0.2.3",
31
+ "@agimon-ai/foundation-port-registry": "0.2.7",
32
+ "@agimon-ai/log-sink-mcp": "0.2.7"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/express": "^5.0.0",