@agimon-ai/mcp-proxy 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,10 @@
1
+ import { coerceArgs, formatZodError, jsonSchemaToZod } from "@agimon-ai/foundation-validator";
1
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
3
  import { CallToolRequestSchema, ElicitRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
4
+ import { z } from "zod";
3
5
  import { existsSync } from "node:fs";
4
6
  import { access, mkdir, readFile, readdir, rm, stat, unlink, watch, writeFile } from "node:fs/promises";
5
7
  import yaml from "js-yaml";
6
- import { z } from "zod";
7
8
  import { createHash, randomBytes, randomUUID } from "node:crypto";
8
9
  import { homedir, tmpdir } from "node:os";
9
10
  import { dirname, isAbsolute, join, resolve } from "node:path";
@@ -22,10 +23,8 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
22
23
  import { Hono } from "hono";
23
24
  import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
24
25
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
25
-
26
26
  //#region package.json
27
- var version = "0.7.1";
28
-
27
+ var version = "0.7.3";
29
28
  //#endregion
30
29
  //#region src/utils/mcpConfigSchema.ts
31
30
  /**
@@ -420,7 +419,6 @@ function parseMcpConfig(rawConfig) {
420
419
  const internalConfig = transformClaudeCodeConfig(ClaudeCodeMcpConfigSchema.parse(rawConfig));
421
420
  return InternalMcpConfigSchema.parse(internalConfig);
422
421
  }
423
-
424
422
  //#endregion
425
423
  //#region src/services/RemoteConfigCacheService.ts
426
424
  /**
@@ -647,7 +645,6 @@ var RemoteConfigCacheService = class {
647
645
  this.writeEnabled = enabled;
648
646
  }
649
647
  };
650
-
651
648
  //#endregion
652
649
  //#region src/services/ConfigFetcherService.ts
653
650
  /**
@@ -874,7 +871,6 @@ var ConfigFetcherService = class {
874
871
  return this.cachedConfig !== null && now - this.lastFetchTime < this.cacheTtlMs;
875
872
  }
876
873
  };
877
-
878
874
  //#endregion
879
875
  //#region src/constants/index.ts
880
876
  /**
@@ -894,17 +890,6 @@ const LOG_PREFIX_SKILL_DETECTION = "[skill-detection]";
894
890
  * Log prefix for general MCP capability discovery messages.
895
891
  */
896
892
  const LOG_PREFIX_CAPABILITY_DISCOVERY = "[capability-discovery]";
897
- /**
898
- * Prefix for prompt-based skill locations.
899
- * Format: "prompt:{serverName}:{promptName}"
900
- */
901
- const PROMPT_LOCATION_PREFIX = "prompt:";
902
- /**
903
- * Default server ID used when no ID is provided via CLI or config.
904
- * This fallback is used when auto-generation also fails.
905
- */
906
- const DEFAULT_SERVER_ID = "unknown";
907
-
908
893
  //#endregion
909
894
  //#region src/utils/findConfigFile.ts
910
895
  /**
@@ -957,7 +942,6 @@ function findConfigFile() {
957
942
  }
958
943
  return null;
959
944
  }
960
-
961
945
  //#endregion
962
946
  //#region src/utils/parseToolName.ts
963
947
  /**
@@ -979,7 +963,6 @@ function parseToolName(toolName) {
979
963
  };
980
964
  return { actualToolName: toolName };
981
965
  }
982
-
983
966
  //#endregion
984
967
  //#region src/utils/parseFrontMatter.ts
985
968
  /**
@@ -1113,7 +1096,6 @@ function extractSkillFrontMatter(content) {
1113
1096
  };
1114
1097
  return null;
1115
1098
  }
1116
-
1117
1099
  //#endregion
1118
1100
  //#region src/utils/generateServerId.ts
1119
1101
  /**
@@ -1178,7 +1160,6 @@ function generateServerId(length = DEFAULT_ID_LENGTH) {
1178
1160
  }
1179
1161
  return result;
1180
1162
  }
1181
-
1182
1163
  //#endregion
1183
1164
  //#region src/services/DefinitionsCacheService.ts
1184
1165
  /**
@@ -1337,6 +1318,9 @@ var DefinitionsCacheService = class {
1337
1318
  async getServersForTool(toolName) {
1338
1319
  return (await this.getServerDefinitions()).filter((serverDefinition) => serverDefinition.tools.some((tool) => tool.name === toolName)).map((serverDefinition) => serverDefinition.serverName);
1339
1320
  }
1321
+ async getToolSchema(serverName, toolName) {
1322
+ return (await this.getDefinitions()).servers[serverName]?.tools.find((t) => t.name === toolName)?.inputSchema;
1323
+ }
1340
1324
  async getServersForResource(uri) {
1341
1325
  return (await this.getServerDefinitions()).filter((serverDefinition) => serverDefinition.resources.some((resource) => resource.uri === uri)).map((serverDefinition) => serverDefinition.serverName);
1342
1326
  }
@@ -1474,7 +1458,6 @@ var DefinitionsCacheService = class {
1474
1458
  return promptSkills;
1475
1459
  }
1476
1460
  };
1477
-
1478
1461
  //#endregion
1479
1462
  //#region src/services/logger.ts
1480
1463
  function isRecord(value) {
@@ -1528,7 +1511,6 @@ async function createProxyLogger(options = {}) {
1528
1511
  maxFileCount: options.maxFileCount
1529
1512
  }));
1530
1513
  }
1531
-
1532
1514
  //#endregion
1533
1515
  //#region src/services/McpClientManagerService.ts
1534
1516
  /** Default connection timeout in milliseconds (30 seconds) */
@@ -1600,8 +1582,8 @@ var McpClient = class {
1600
1582
  this.logger = logger;
1601
1583
  this.client = client;
1602
1584
  }
1603
- setChildProcess(process$1) {
1604
- this.childProcess = process$1;
1585
+ setChildProcess(process) {
1586
+ this.childProcess = process;
1605
1587
  }
1606
1588
  setConnected(connected) {
1607
1589
  this.connected = connected;
@@ -1730,7 +1712,7 @@ var McpClientManagerService = class {
1730
1712
  if (childProcess && !childProcess.killed) {
1731
1713
  this.logger.info(`Killing stdio MCP server: ${serverName} (PID: ${childProcess.pid})`);
1732
1714
  childProcess.kill("SIGTERM");
1733
- killPromises.push(new Promise((resolve$1) => {
1715
+ killPromises.push(new Promise((resolve) => {
1734
1716
  setTimeout(() => {
1735
1717
  try {
1736
1718
  if (!childProcess.killed) {
@@ -1738,7 +1720,7 @@ var McpClientManagerService = class {
1738
1720
  childProcess.kill("SIGKILL");
1739
1721
  }
1740
1722
  } catch {}
1741
- resolve$1();
1723
+ resolve();
1742
1724
  }, 1e3);
1743
1725
  }));
1744
1726
  }
@@ -1782,7 +1764,7 @@ var McpClientManagerService = class {
1782
1764
  async createConnection(serverName, config) {
1783
1765
  const timeoutMs = config.timeout ?? DEFAULT_CONNECTION_TIMEOUT_MS;
1784
1766
  const client = new Client({
1785
- name: `@agimon-ai/mcp-proxy-client`,
1767
+ name: "@agimon-ai/mcp-proxy-client",
1786
1768
  version: "0.1.0"
1787
1769
  }, { capabilities: {} });
1788
1770
  const mcpClient = new McpClient(serverName, config.transport, client, this.logger, {
@@ -1889,7 +1871,6 @@ var McpClientManagerService = class {
1889
1871
  return this.clients.has(serverName);
1890
1872
  }
1891
1873
  };
1892
-
1893
1874
  //#endregion
1894
1875
  //#region src/services/RuntimeStateService.ts
1895
1876
  /**
@@ -1991,24 +1972,10 @@ var RuntimeStateService = class RuntimeStateService {
1991
1972
  await rm(this.getRecordPath(serverId), { force: true });
1992
1973
  }
1993
1974
  };
1994
-
1995
- //#endregion
1996
- //#region src/services/StopServerService/constants.ts
1997
- /**
1998
- * StopServerService constants.
1999
- */
2000
- /** Maximum time in milliseconds to wait for a shutdown to complete. */
2001
- const DEFAULT_STOP_TIMEOUT_MS = 5e3;
2002
- /** Minimum timeout in milliseconds for individual health check requests. */
2003
- const HEALTH_REQUEST_TIMEOUT_FLOOR_MS = 250;
2004
- /** Delay in milliseconds between shutdown polling attempts. */
2005
- const SHUTDOWN_POLL_INTERVAL_MS = 200;
2006
1975
  /** Path for the runtime health check endpoint. */
2007
1976
  const HEALTH_CHECK_PATH = "/health";
2008
1977
  /** Path for the authenticated admin shutdown endpoint. */
2009
1978
  const ADMIN_SHUTDOWN_PATH = "/admin/shutdown";
2010
- /** HTTP GET method identifier. */
2011
- const HTTP_METHOD_GET = "GET";
2012
1979
  /** HTTP POST method identifier. */
2013
1980
  const HTTP_METHOD_POST = "POST";
2014
1981
  /** HTTP header name for bearer token authorization. */
@@ -2017,35 +1984,12 @@ const AUTHORIZATION_HEADER_NAME = "Authorization";
2017
1984
  const BEARER_TOKEN_PREFIX = "Bearer ";
2018
1985
  /** HTTP protocol scheme prefix for URL construction. */
2019
1986
  const HTTP_PROTOCOL = "http://";
2020
- /** Separator between host and port in URL construction. */
2021
- const URL_PORT_SEPARATOR = ":";
2022
- /** Loopback hostname. */
2023
- const LOOPBACK_HOST_LOCALHOST = "localhost";
2024
- /** IPv4 loopback address. */
2025
- const LOOPBACK_HOST_IPV4 = "127.0.0.1";
2026
- /** IPv6 loopback address. */
2027
- const LOOPBACK_HOST_IPV6 = "::1";
2028
1987
  /** Hosts that are safe to send admin requests to (loopback only). */
2029
1988
  const ALLOWED_HOSTS = new Set([
2030
- LOOPBACK_HOST_LOCALHOST,
2031
- LOOPBACK_HOST_IPV4,
2032
- LOOPBACK_HOST_IPV6
1989
+ "localhost",
1990
+ "127.0.0.1",
1991
+ "::1"
2033
1992
  ]);
2034
- /** Expected status value in a healthy runtime response. */
2035
- const HEALTH_STATUS_OK = "ok";
2036
- /** Expected transport value in a healthy runtime response. */
2037
- const HEALTH_TRANSPORT_HTTP = "http";
2038
- /** Property key for status field in health responses. */
2039
- const KEY_STATUS = "status";
2040
- /** Property key for transport field in health responses. */
2041
- const KEY_TRANSPORT = "transport";
2042
- /** Property key for serverId field in runtime responses. */
2043
- const KEY_SERVER_ID = "serverId";
2044
- /** Property key for ok field in shutdown responses. */
2045
- const KEY_OK = "ok";
2046
- /** Property key for message field in shutdown responses. */
2047
- const KEY_MESSAGE = "message";
2048
-
2049
1993
  //#endregion
2050
1994
  //#region src/services/StopServerService/types.ts
2051
1995
  /**
@@ -2064,7 +2008,7 @@ function toRecord(value) {
2064
2008
  function isHealthResponse(value) {
2065
2009
  if (typeof value !== "object" || value === null) return false;
2066
2010
  const record = toRecord(value);
2067
- return KEY_STATUS in record && record[KEY_STATUS] === HEALTH_STATUS_OK && KEY_TRANSPORT in record && record[KEY_TRANSPORT] === HEALTH_TRANSPORT_HTTP && (!(KEY_SERVER_ID in record) || record[KEY_SERVER_ID] === void 0 || typeof record[KEY_SERVER_ID] === "string");
2011
+ return "status" in record && record["status"] === "ok" && "transport" in record && record["transport"] === "http" && (!("serverId" in record) || record["serverId"] === void 0 || typeof record["serverId"] === "string");
2068
2012
  }
2069
2013
  /**
2070
2014
  * Type guard for shutdown responses.
@@ -2074,9 +2018,8 @@ function isHealthResponse(value) {
2074
2018
  function isShutdownResponse(value) {
2075
2019
  if (typeof value !== "object" || value === null) return false;
2076
2020
  const record = toRecord(value);
2077
- return KEY_OK in record && typeof record[KEY_OK] === "boolean" && KEY_MESSAGE in record && typeof record[KEY_MESSAGE] === "string" && (!(KEY_SERVER_ID in record) || record[KEY_SERVER_ID] === void 0 || typeof record[KEY_SERVER_ID] === "string");
2021
+ return "ok" in record && typeof record["ok"] === "boolean" && "message" in record && typeof record["message"] === "string" && (!("serverId" in record) || record["serverId"] === void 0 || typeof record["serverId"] === "string");
2078
2022
  }
2079
-
2080
2023
  //#endregion
2081
2024
  //#region src/services/StopServerService/StopServerService.ts
2082
2025
  /**
@@ -2086,16 +2029,16 @@ function isShutdownResponse(value) {
2086
2029
  * @param path - Request path to append
2087
2030
  * @returns Full runtime URL
2088
2031
  */
2089
- function buildRuntimeUrl(runtime, path$1) {
2032
+ function buildRuntimeUrl(runtime, path) {
2090
2033
  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.`);
2091
- return `${HTTP_PROTOCOL}${runtime.host}${URL_PORT_SEPARATOR}${runtime.port}${path$1}`;
2034
+ return `${HTTP_PROTOCOL}${runtime.host}:${runtime.port}${path}`;
2092
2035
  }
2093
2036
  function toErrorMessage$1(error) {
2094
2037
  return error instanceof Error ? error.message : String(error);
2095
2038
  }
2096
2039
  function sleep(delayMs) {
2097
- return new Promise((resolve$1) => {
2098
- setTimeout(resolve$1, delayMs);
2040
+ return new Promise((resolve) => {
2041
+ setTimeout(resolve, delayMs);
2099
2042
  });
2100
2043
  }
2101
2044
  /**
@@ -2114,7 +2057,7 @@ var StopServerService = class {
2114
2057
  * @returns Stop result payload
2115
2058
  */
2116
2059
  async stop(request) {
2117
- const timeoutMs = request.timeoutMs ?? DEFAULT_STOP_TIMEOUT_MS;
2060
+ const timeoutMs = request.timeoutMs ?? 5e3;
2118
2061
  const runtime = await this.resolveRuntime(request);
2119
2062
  const health = await this.fetchHealth(runtime, timeoutMs);
2120
2063
  if (!health.reachable) {
@@ -2160,7 +2103,7 @@ var StopServerService = class {
2160
2103
  */
2161
2104
  async fetchHealth(runtime, timeoutMs) {
2162
2105
  try {
2163
- const response = await this.fetchWithTimeout(buildRuntimeUrl(runtime, HEALTH_CHECK_PATH), { method: HTTP_METHOD_GET }, timeoutMs);
2106
+ const response = await this.fetchWithTimeout(buildRuntimeUrl(runtime, HEALTH_CHECK_PATH), { method: "GET" }, timeoutMs);
2164
2107
  if (!response.ok) return { reachable: false };
2165
2108
  const payload = await response.json();
2166
2109
  if (!isHealthResponse(payload)) throw new Error("Received invalid health response payload.");
@@ -2199,8 +2142,8 @@ var StopServerService = class {
2199
2142
  async waitForShutdown(runtime, timeoutMs) {
2200
2143
  const deadline = Date.now() + timeoutMs;
2201
2144
  while (Date.now() < deadline) {
2202
- if (!(await this.fetchHealth(runtime, Math.max(HEALTH_REQUEST_TIMEOUT_FLOOR_MS, deadline - Date.now()))).reachable) return;
2203
- await sleep(SHUTDOWN_POLL_INTERVAL_MS);
2145
+ if (!(await this.fetchHealth(runtime, Math.max(250, deadline - Date.now()))).reachable) return;
2146
+ await sleep(200);
2204
2147
  }
2205
2148
  throw new Error(`Timed out waiting for runtime '${runtime.serverId}' to stop at http://${runtime.host}:${runtime.port}.`);
2206
2149
  }
@@ -2228,7 +2171,6 @@ var StopServerService = class {
2228
2171
  }
2229
2172
  }
2230
2173
  };
2231
-
2232
2174
  //#endregion
2233
2175
  //#region src/services/SkillService.ts
2234
2176
  /**
@@ -2266,13 +2208,13 @@ var SkillLoadError = class extends Error {
2266
2208
  * @returns true if path exists, false otherwise
2267
2209
  * @throws Error for unexpected filesystem errors (permission denied, etc.)
2268
2210
  */
2269
- async function pathExists(path$1) {
2211
+ async function pathExists(path) {
2270
2212
  try {
2271
- await access(path$1);
2213
+ await access(path);
2272
2214
  return true;
2273
2215
  } catch (error) {
2274
2216
  if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
2275
- throw new Error(`Failed to check path existence for "${path$1}": ${error instanceof Error ? error.message : "Unknown error"}`);
2217
+ throw new Error(`Failed to check path existence for "${path}": ${error instanceof Error ? error.message : "Unknown error"}`);
2276
2218
  }
2277
2219
  }
2278
2220
  /**
@@ -2585,54 +2527,14 @@ var SkillService = class {
2585
2527
  };
2586
2528
  }
2587
2529
  };
2588
-
2589
- //#endregion
2590
- //#region src/services/PrefetchService/constants.ts
2591
- /**
2592
- * PrefetchService Constants
2593
- *
2594
- * Constants for package manager commands and process configuration.
2595
- */
2596
- /** Transport type for stdio-based MCP servers */
2597
- const TRANSPORT_STDIO = "stdio";
2598
- /** npx command name */
2599
- const COMMAND_NPX = "npx";
2600
- /** npm command name */
2601
- const COMMAND_NPM = "npm";
2602
2530
  /** pnpx command name (pnpm's npx equivalent) */
2603
2531
  const COMMAND_PNPX = "pnpx";
2604
2532
  /** pnpm command name */
2605
2533
  const COMMAND_PNPM = "pnpm";
2606
- /** uvx command name */
2607
- const COMMAND_UVX = "uvx";
2608
- /** uv command name */
2609
- const COMMAND_UV = "uv";
2610
- /** Path suffix for npx command */
2611
- const COMMAND_NPX_SUFFIX = "/npx";
2612
- /** Path suffix for pnpx command */
2613
- const COMMAND_PNPX_SUFFIX = "/pnpx";
2614
- /** Path suffix for uvx command */
2615
- const COMMAND_UVX_SUFFIX = "/uvx";
2616
- /** Path suffix for uv command */
2617
- const COMMAND_UV_SUFFIX = "/uv";
2618
- /** Run subcommand for uv */
2619
- const ARG_RUN = "run";
2620
2534
  /** Tool subcommand for uv */
2621
2535
  const ARG_TOOL = "tool";
2622
2536
  /** Install subcommand for uv tool and npm/pnpm */
2623
2537
  const ARG_INSTALL = "install";
2624
- /** Add subcommand for pnpm */
2625
- const ARG_ADD = "add";
2626
- /** Global flag for npm/pnpm install */
2627
- const ARG_GLOBAL = "-g";
2628
- /** Flag prefix for command arguments */
2629
- const FLAG_PREFIX = "-";
2630
- /** npx --package flag (long form) */
2631
- const FLAG_PACKAGE_LONG = "--package";
2632
- /** npx -p flag (short form) */
2633
- const FLAG_PACKAGE_SHORT = "-p";
2634
- /** Equals delimiter used in flag=value patterns */
2635
- const EQUALS_DELIMITER = "=";
2636
2538
  /**
2637
2539
  * Regex pattern for valid package names (npm, pnpm, uvx, uv)
2638
2540
  * Allows: @scope/package-name@version, package-name, package_name
@@ -2644,13 +2546,10 @@ const EQUALS_DELIMITER = "=";
2644
2546
  const VALID_PACKAGE_NAME_PATTERN = /^(@[a-zA-Z0-9_-]+\/)?[a-zA-Z0-9._-]+(@[a-zA-Z0-9._-]+)?$/;
2645
2547
  /** Windows platform identifier */
2646
2548
  const PLATFORM_WIN32 = "win32";
2647
- /** Success exit code */
2648
- const EXIT_CODE_SUCCESS = 0;
2649
2549
  /** Stdio option to ignore stream */
2650
2550
  const STDIO_IGNORE = "ignore";
2651
2551
  /** Stdio option to pipe stream */
2652
2552
  const STDIO_PIPE = "pipe";
2653
-
2654
2553
  //#endregion
2655
2554
  //#region src/services/PrefetchService/PrefetchService.ts
2656
2555
  /**
@@ -2710,7 +2609,7 @@ var PrefetchService = class {
2710
2609
  const { mcpConfig, filter } = this.config;
2711
2610
  for (const [serverName, serverConfig] of Object.entries(mcpConfig.mcpServers)) {
2712
2611
  if (serverConfig.disabled) continue;
2713
- if (serverConfig.transport !== TRANSPORT_STDIO) continue;
2612
+ if (serverConfig.transport !== "stdio") continue;
2714
2613
  if (!isMcpStdioConfig(serverConfig.config)) continue;
2715
2614
  const packageInfo = this.extractPackageInfo(serverName, serverConfig.config);
2716
2615
  if (packageInfo) {
@@ -2799,21 +2698,21 @@ var PrefetchService = class {
2799
2698
  extractPackageInfo(serverName, config) {
2800
2699
  const command = config.command.toLowerCase();
2801
2700
  const args = config.args || [];
2802
- if (command === COMMAND_NPX || command.endsWith(COMMAND_NPX_SUFFIX)) {
2701
+ if (command === "npx" || command.endsWith("/npx")) {
2803
2702
  const packageName = this.extractNpxPackage(args);
2804
2703
  if (packageName && this.isValidPackageName(packageName)) return {
2805
2704
  serverName,
2806
- packageManager: COMMAND_NPX,
2705
+ packageManager: "npx",
2807
2706
  packageName,
2808
2707
  fullCommand: [
2809
- COMMAND_NPM,
2708
+ "npm",
2810
2709
  ARG_INSTALL,
2811
- ARG_GLOBAL,
2710
+ "-g",
2812
2711
  packageName
2813
2712
  ]
2814
2713
  };
2815
2714
  }
2816
- if (command === COMMAND_PNPX || command.endsWith(COMMAND_PNPX_SUFFIX)) {
2715
+ if (command === "pnpx" || command.endsWith("/pnpx")) {
2817
2716
  const packageName = this.extractNpxPackage(args);
2818
2717
  if (packageName && this.isValidPackageName(packageName)) return {
2819
2718
  serverName,
@@ -2821,29 +2720,29 @@ var PrefetchService = class {
2821
2720
  packageName,
2822
2721
  fullCommand: [
2823
2722
  COMMAND_PNPM,
2824
- ARG_ADD,
2825
- ARG_GLOBAL,
2723
+ "add",
2724
+ "-g",
2826
2725
  packageName
2827
2726
  ]
2828
2727
  };
2829
2728
  }
2830
- if (command === COMMAND_UVX || command.endsWith(COMMAND_UVX_SUFFIX)) {
2729
+ if (command === "uvx" || command.endsWith("/uvx")) {
2831
2730
  const packageName = this.extractUvxPackage(args);
2832
2731
  if (packageName && this.isValidPackageName(packageName)) return {
2833
2732
  serverName,
2834
- packageManager: COMMAND_UVX,
2733
+ packageManager: "uvx",
2835
2734
  packageName,
2836
- fullCommand: [COMMAND_UVX, packageName]
2735
+ fullCommand: ["uvx", packageName]
2837
2736
  };
2838
2737
  }
2839
- if ((command === COMMAND_UV || command.endsWith(COMMAND_UV_SUFFIX)) && args.includes(ARG_RUN)) {
2738
+ if ((command === "uv" || command.endsWith("/uv")) && args.includes("run")) {
2840
2739
  const packageName = this.extractUvRunPackage(args);
2841
2740
  if (packageName && this.isValidPackageName(packageName)) return {
2842
2741
  serverName,
2843
- packageManager: COMMAND_UV,
2742
+ packageManager: "uv",
2844
2743
  packageName,
2845
2744
  fullCommand: [
2846
- COMMAND_UV,
2745
+ "uv",
2847
2746
  ARG_TOOL,
2848
2747
  ARG_INSTALL,
2849
2748
  packageName
@@ -2870,18 +2769,18 @@ var PrefetchService = class {
2870
2769
  extractNpxPackage(args) {
2871
2770
  for (let i = 0; i < args.length; i++) {
2872
2771
  const arg = args[i];
2873
- if (arg.startsWith(FLAG_PACKAGE_LONG + EQUALS_DELIMITER)) return arg.slice(FLAG_PACKAGE_LONG.length + EQUALS_DELIMITER.length) || null;
2874
- if (arg === FLAG_PACKAGE_LONG && i + 1 < args.length) {
2772
+ if (arg.startsWith("--package=")) return arg.slice(10) || null;
2773
+ if (arg === "--package" && i + 1 < args.length) {
2875
2774
  const nextArg = args[i + 1];
2876
- if (!nextArg.startsWith(FLAG_PREFIX)) return nextArg;
2775
+ if (!nextArg.startsWith("-")) return nextArg;
2877
2776
  }
2878
- if (arg === FLAG_PACKAGE_SHORT && i + 1 < args.length) {
2777
+ if (arg === "-p" && i + 1 < args.length) {
2879
2778
  const nextArg = args[i + 1];
2880
- if (!nextArg.startsWith(FLAG_PREFIX)) return nextArg;
2779
+ if (!nextArg.startsWith("-")) return nextArg;
2881
2780
  }
2882
2781
  }
2883
2782
  for (const arg of args) {
2884
- if (arg.startsWith(FLAG_PREFIX)) continue;
2783
+ if (arg.startsWith("-")) continue;
2885
2784
  return arg;
2886
2785
  }
2887
2786
  return null;
@@ -2898,7 +2797,7 @@ var PrefetchService = class {
2898
2797
  */
2899
2798
  extractUvxPackage(args) {
2900
2799
  for (const arg of args) {
2901
- if (arg.startsWith(FLAG_PREFIX)) continue;
2800
+ if (arg.startsWith("-")) continue;
2902
2801
  return arg;
2903
2802
  }
2904
2803
  return null;
@@ -2915,11 +2814,11 @@ var PrefetchService = class {
2915
2814
  * extractUvRunPackage(['install', 'pkg']) // returns null (no 'run')
2916
2815
  */
2917
2816
  extractUvRunPackage(args) {
2918
- const runIndex = args.indexOf(ARG_RUN);
2817
+ const runIndex = args.indexOf("run");
2919
2818
  if (runIndex === -1) return null;
2920
2819
  for (let i = runIndex + 1; i < args.length; i++) {
2921
2820
  const arg = args[i];
2922
- if (arg.startsWith(FLAG_PREFIX)) continue;
2821
+ if (arg.startsWith("-")) continue;
2923
2822
  return arg;
2924
2823
  }
2925
2824
  return null;
@@ -2931,7 +2830,7 @@ var PrefetchService = class {
2931
2830
  * @returns Promise with success status and output
2932
2831
  */
2933
2832
  runCommand(command, args) {
2934
- return new Promise((resolve$1) => {
2833
+ return new Promise((resolve) => {
2935
2834
  const proc = spawn(command, args, {
2936
2835
  stdio: [
2937
2836
  STDIO_IGNORE,
@@ -2949,13 +2848,13 @@ var PrefetchService = class {
2949
2848
  stderr += data.toString();
2950
2849
  });
2951
2850
  proc.on("close", (code) => {
2952
- resolve$1({
2953
- success: code === EXIT_CODE_SUCCESS,
2851
+ resolve({
2852
+ success: code === 0,
2954
2853
  output: stdout || stderr
2955
2854
  });
2956
2855
  });
2957
2856
  proc.on("error", (error) => {
2958
- resolve$1({
2857
+ resolve({
2959
2858
  success: false,
2960
2859
  output: error.message
2961
2860
  });
@@ -2963,11 +2862,9 @@ var PrefetchService = class {
2963
2862
  });
2964
2863
  }
2965
2864
  };
2966
-
2967
2865
  //#endregion
2968
2866
  //#region src/templates/toolkit-description.liquid?raw
2969
- var toolkit_description_default = "<toolkit id=\"{{ serverId }}\">\n<instruction>\nBefore you use any capabilities below, you MUST call this tool with a list of names to learn how to use them properly; this includes:\n- For tools: Arguments schema needed to pass to use_tool\n- For skills: Detailed instructions that will expand when invoked (Prefer to be explored first when relevant)\n\nThis tool is optimized for batch queries - you can request multiple capabilities at once for better performance.\n\nHow to invoke:\n- For MCP tools: Use use_tool with toolName and toolArgs based on the schema\n- For skills: Use this tool with the skill name to get expanded instructions\n</instruction>\n\n<available_capabilities>\n{% for server in servers -%}\n<group name=\"{{ server.name }}\">\n{% if server.instruction -%}\n<group_instruction>{{ server.instruction }}</group_instruction>\n{% endif -%}\n{% if server.omitToolDescription -%}\n{% for toolName in server.toolNames -%}\n<item name=\"{{ toolName }}\"></item>\n{% endfor -%}\n{% else -%}\n{% for tool in server.tools -%}\n<item name=\"{{ tool.displayName }}\"><description>{{ tool.description | default: \"No description\" }}</description></item>\n{% endfor -%}\n{% endif -%}\n</group>\n{% endfor -%}\n{% if skills.size > 0 -%}\n<group name=\"skills\">\n{% for skill in skills -%}\n<item name=\"{{ skill.displayName }}\"><description>{{ skill.description }}</description></item>\n{% endfor -%}\n</group>\n{% endif -%}\n</available_capabilities>\n</toolkit>\n";
2970
-
2867
+ var toolkit_description_default = "<toolkit id=\"{{ serverId }}\">\n<instruction>\nBefore you use any capabilities below, you MUST call this tool with a list of names to learn how to use them properly; this includes:\n- For tools: Arguments schema needed to pass to use_tool\n- For skills: Detailed instructions that will expand when invoked (Prefer to be explored first when relevant)\n\nThis tool is optimized for batch queries - you can request multiple capabilities at once for better performance.\n\nHow to invoke:\n- For MCP tools: Use use_tool with toolName and toolArgs based on the schema\n- For skills: Use this tool with the skill name to get expanded instructions\n</instruction>\n\n<available_capabilities>\n{% for server in servers -%}\n<group original-mcp-server=\"{{ server.name }}\">\n{% if server.instruction -%}\n<group_instruction>{{ server.instruction }}</group_instruction>\n{% endif -%}\n{% if server.omitToolDescription -%}\n{% for toolName in server.toolNames -%}\n<item name=\"{{ toolName }}\"></item>\n{% endfor -%}\n{% else -%}\n{% for tool in server.tools -%}\n<item name=\"{{ tool.displayName }}\"><description>{{ tool.description | default: \"No description\" }}</description></item>\n{% endfor -%}\n{% endif -%}\n</group>\n{% endfor -%}\n{% if skills.size > 0 -%}\n<group name=\"skills\">\n{% for skill in skills -%}\n<item name=\"{{ skill.displayName }}\"><description>{{ skill.description }}</description></item>\n{% endfor -%}\n</group>\n{% endif -%}\n</available_capabilities>\n</toolkit>\n";
2971
2868
  //#endregion
2972
2869
  //#region src/tools/DescribeToolsTool.ts
2973
2870
  /**
@@ -2982,6 +2879,11 @@ function formatSkillInstructions(name, instructions) {
2982
2879
  return `<command-message>The "${name}" skill is loading</command-message>\n${instructions}`;
2983
2880
  }
2984
2881
  /**
2882
+ * Input schema for the DescribeToolsTool
2883
+ * @property toolNames - Array of tool names to get detailed information about
2884
+ */
2885
+ const DescribeToolsToolInputSchema = z.object({ toolNames: z.array(z.string().min(1)).min(1).describe("List of tool names to get detailed information about") });
2886
+ /**
2985
2887
  * DescribeToolsTool provides progressive disclosure of MCP tools and skills.
2986
2888
  *
2987
2889
  * This tool lists available tools from all connected MCP servers and skills
@@ -3017,7 +2919,7 @@ var DescribeToolsTool = class DescribeToolsTool {
3017
2919
  constructor(clientManager, skillService, serverId, definitionsCacheService) {
3018
2920
  this.clientManager = clientManager;
3019
2921
  this.skillService = skillService;
3020
- this.serverId = serverId || DEFAULT_SERVER_ID;
2922
+ this.serverId = serverId || "unknown";
3021
2923
  this.definitionsCacheService = definitionsCacheService || new DefinitionsCacheService(clientManager, skillService);
3022
2924
  }
3023
2925
  /**
@@ -3133,7 +3035,7 @@ var DescribeToolsTool = class DescribeToolsTool {
3133
3035
  }).join("\n") || "";
3134
3036
  return {
3135
3037
  name: promptSkill.skill.name,
3136
- location: promptSkill.skill.folder || `${PROMPT_LOCATION_PREFIX}${promptSkill.serverName}/${promptSkill.promptName}`,
3038
+ location: promptSkill.skill.folder || `prompt:${promptSkill.serverName}/${promptSkill.promptName}`,
3137
3039
  instructions: formatSkillInstructions(promptSkill.skill.name, rawInstructions)
3138
3040
  };
3139
3041
  } catch (error) {
@@ -3238,25 +3140,124 @@ var DescribeToolsTool = class DescribeToolsTool {
3238
3140
  *
3239
3141
  * @returns Tool definition with description and input schema
3240
3142
  */
3143
+ getInputSchema() {
3144
+ return DescribeToolsToolInputSchema;
3145
+ }
3241
3146
  async getDefinition() {
3242
3147
  const { content } = await this.buildToolkitDescription();
3243
3148
  return {
3244
3149
  name: DescribeToolsTool.TOOL_NAME,
3245
3150
  description: content,
3246
- inputSchema: {
3247
- type: "object",
3248
- properties: { toolNames: {
3249
- type: "array",
3250
- items: {
3251
- type: "string",
3252
- minLength: 1
3253
- },
3254
- description: "List of tool names to get detailed information about",
3255
- minItems: 1
3256
- } },
3257
- required: ["toolNames"],
3258
- additionalProperties: false
3151
+ inputSchema: z.toJSONSchema(DescribeToolsToolInputSchema, { reused: "inline" })
3152
+ };
3153
+ }
3154
+ async runLookups(toolNames, serverDefinitions) {
3155
+ const serverToolsMap = /* @__PURE__ */ new Map();
3156
+ const toolToServers = /* @__PURE__ */ new Map();
3157
+ for (const serverDefinition of serverDefinitions) {
3158
+ const typedTools = serverDefinition.tools.map((tool) => ({
3159
+ name: tool.name,
3160
+ description: tool.description,
3161
+ inputSchema: tool.inputSchema
3162
+ }));
3163
+ serverToolsMap.set(serverDefinition.serverName, typedTools);
3164
+ for (const tool of typedTools) {
3165
+ if (!toolToServers.has(tool.name)) toolToServers.set(tool.name, []);
3166
+ toolToServers.get(tool.name)?.push(serverDefinition.serverName);
3167
+ }
3168
+ }
3169
+ const lookupResults = await Promise.all(toolNames.map(async (requestedName) => {
3170
+ const result = {
3171
+ tools: [],
3172
+ skills: [],
3173
+ notFound: null
3174
+ };
3175
+ if (requestedName.startsWith("skill__")) {
3176
+ const skillName = requestedName.slice(7);
3177
+ if (this.skillService) {
3178
+ const skill = await this.skillService.getSkill(skillName);
3179
+ if (skill) {
3180
+ result.skills.push({
3181
+ name: skill.name,
3182
+ location: skill.basePath,
3183
+ instructions: formatSkillInstructions(skill.name, skill.content)
3184
+ });
3185
+ return result;
3186
+ }
3187
+ }
3188
+ const promptSkillContent = await this.getPromptSkillContent(skillName);
3189
+ if (promptSkillContent) {
3190
+ result.skills.push(promptSkillContent);
3191
+ return result;
3192
+ }
3193
+ result.notFound = requestedName;
3194
+ return result;
3195
+ }
3196
+ const { serverName, actualToolName } = parseToolName(requestedName);
3197
+ if (serverName) {
3198
+ const serverTools = serverToolsMap.get(serverName);
3199
+ if (!serverTools) {
3200
+ result.notFound = requestedName;
3201
+ return result;
3202
+ }
3203
+ const tool = serverTools.find((t) => t.name === actualToolName);
3204
+ if (tool) result.tools.push({
3205
+ server: serverName,
3206
+ tool: {
3207
+ name: tool.name,
3208
+ description: tool.description,
3209
+ inputSchema: tool.inputSchema
3210
+ }
3211
+ });
3212
+ else result.notFound = requestedName;
3213
+ return result;
3214
+ }
3215
+ const servers = toolToServers.get(actualToolName);
3216
+ if (!servers || servers.length === 0) {
3217
+ if (this.skillService) {
3218
+ const skill = await this.skillService.getSkill(actualToolName);
3219
+ if (skill) {
3220
+ result.skills.push({
3221
+ name: skill.name,
3222
+ location: skill.basePath,
3223
+ instructions: formatSkillInstructions(skill.name, skill.content)
3224
+ });
3225
+ return result;
3226
+ }
3227
+ }
3228
+ const promptSkillContent = await this.getPromptSkillContent(actualToolName);
3229
+ if (promptSkillContent) {
3230
+ result.skills.push(promptSkillContent);
3231
+ return result;
3232
+ }
3233
+ result.notFound = requestedName;
3234
+ return result;
3259
3235
  }
3236
+ for (const server of servers) {
3237
+ const tool = serverToolsMap.get(server).find((t) => t.name === actualToolName);
3238
+ result.tools.push({
3239
+ server,
3240
+ tool: {
3241
+ name: tool.name,
3242
+ description: tool.description,
3243
+ inputSchema: tool.inputSchema
3244
+ }
3245
+ });
3246
+ }
3247
+ return result;
3248
+ }));
3249
+ const foundTools = [];
3250
+ const foundSkills = [];
3251
+ const notFoundItems = [];
3252
+ for (const result of lookupResults) {
3253
+ foundTools.push(...result.tools);
3254
+ foundSkills.push(...result.skills);
3255
+ if (result.notFound) notFoundItems.push(result.notFound);
3256
+ }
3257
+ return {
3258
+ foundTools,
3259
+ foundSkills,
3260
+ notFoundItems
3260
3261
  };
3261
3262
  }
3262
3263
  /**
@@ -3270,10 +3271,9 @@ var DescribeToolsTool = class DescribeToolsTool {
3270
3271
  * @param input - Object containing toolNames array
3271
3272
  * @returns CallToolResult with tool/skill descriptions or error
3272
3273
  */
3273
- async execute(input) {
3274
+ async execute(rawInput) {
3274
3275
  try {
3275
- const { toolNames } = input;
3276
- const serverDefinitions = await this.definitionsCacheService.getServerDefinitions();
3276
+ const { toolNames } = DescribeToolsToolInputSchema.parse(rawInput);
3277
3277
  if (!toolNames || toolNames.length === 0) return {
3278
3278
  content: [{
3279
3279
  type: "text",
@@ -3281,119 +3281,13 @@ var DescribeToolsTool = class DescribeToolsTool {
3281
3281
  }],
3282
3282
  isError: true
3283
3283
  };
3284
- const serverToolsMap = /* @__PURE__ */ new Map();
3285
- const toolToServers = /* @__PURE__ */ new Map();
3286
- for (const serverDefinition of serverDefinitions) {
3287
- const typedTools = serverDefinition.tools.map((tool) => ({
3288
- name: tool.name,
3289
- description: tool.description,
3290
- inputSchema: tool.inputSchema
3291
- }));
3292
- serverToolsMap.set(serverDefinition.serverName, typedTools);
3293
- for (const tool of typedTools) {
3294
- if (!toolToServers.has(tool.name)) toolToServers.set(tool.name, []);
3295
- toolToServers.get(tool.name)?.push(serverDefinition.serverName);
3296
- }
3297
- }
3298
- const lookupResults = await Promise.all(toolNames.map(async (requestedName) => {
3299
- const result$1 = {
3300
- tools: [],
3301
- skills: [],
3302
- notFound: null
3303
- };
3304
- if (requestedName.startsWith(SKILL_PREFIX)) {
3305
- const skillName = requestedName.slice(SKILL_PREFIX.length);
3306
- if (this.skillService) {
3307
- const skill = await this.skillService.getSkill(skillName);
3308
- if (skill) {
3309
- result$1.skills.push({
3310
- name: skill.name,
3311
- location: skill.basePath,
3312
- instructions: formatSkillInstructions(skill.name, skill.content)
3313
- });
3314
- return result$1;
3315
- }
3316
- }
3317
- const promptSkillContent = await this.getPromptSkillContent(skillName);
3318
- if (promptSkillContent) {
3319
- result$1.skills.push(promptSkillContent);
3320
- return result$1;
3321
- }
3322
- result$1.notFound = requestedName;
3323
- return result$1;
3324
- }
3325
- const { serverName, actualToolName } = parseToolName(requestedName);
3326
- if (serverName) {
3327
- const serverTools = serverToolsMap.get(serverName);
3328
- if (!serverTools) {
3329
- result$1.notFound = requestedName;
3330
- return result$1;
3331
- }
3332
- const tool = serverTools.find((t) => t.name === actualToolName);
3333
- if (tool) result$1.tools.push({
3334
- server: serverName,
3335
- tool: {
3336
- name: tool.name,
3337
- description: tool.description,
3338
- inputSchema: tool.inputSchema
3339
- }
3340
- });
3341
- else result$1.notFound = requestedName;
3342
- return result$1;
3343
- }
3344
- const servers = toolToServers.get(actualToolName);
3345
- if (!servers || servers.length === 0) {
3346
- if (this.skillService) {
3347
- const skill = await this.skillService.getSkill(actualToolName);
3348
- if (skill) {
3349
- result$1.skills.push({
3350
- name: skill.name,
3351
- location: skill.basePath,
3352
- instructions: formatSkillInstructions(skill.name, skill.content)
3353
- });
3354
- return result$1;
3355
- }
3356
- }
3357
- const promptSkillContent = await this.getPromptSkillContent(actualToolName);
3358
- if (promptSkillContent) {
3359
- result$1.skills.push(promptSkillContent);
3360
- return result$1;
3361
- }
3362
- result$1.notFound = requestedName;
3363
- return result$1;
3364
- }
3365
- if (servers.length === 1) {
3366
- const server = servers[0];
3367
- const tool = serverToolsMap.get(server).find((t) => t.name === actualToolName);
3368
- result$1.tools.push({
3369
- server,
3370
- tool: {
3371
- name: tool.name,
3372
- description: tool.description,
3373
- inputSchema: tool.inputSchema
3374
- }
3375
- });
3376
- } else for (const server of servers) {
3377
- const tool = serverToolsMap.get(server).find((t) => t.name === actualToolName);
3378
- result$1.tools.push({
3379
- server,
3380
- tool: {
3381
- name: tool.name,
3382
- description: tool.description,
3383
- inputSchema: tool.inputSchema
3384
- }
3385
- });
3386
- }
3387
- return result$1;
3388
- }));
3389
- const foundTools = [];
3390
- const foundSkills = [];
3391
- const notFoundItems = [];
3392
- for (const result$1 of lookupResults) {
3393
- foundTools.push(...result$1.tools);
3394
- foundSkills.push(...result$1.skills);
3395
- if (result$1.notFound) notFoundItems.push(result$1.notFound);
3284
+ let lookup = await this.runLookups(toolNames, await this.definitionsCacheService.getServerDefinitions());
3285
+ if (lookup.notFoundItems.length > 0) {
3286
+ this.definitionsCacheService.clearLiveCache();
3287
+ await this.definitionsCacheService.collectForCache();
3288
+ lookup = await this.runLookups(toolNames, await this.definitionsCacheService.getServerDefinitions());
3396
3289
  }
3290
+ const { foundTools, foundSkills, notFoundItems } = lookup;
3397
3291
  if (foundTools.length === 0 && foundSkills.length === 0) return {
3398
3292
  content: [{
3399
3293
  type: "text",
@@ -3431,7 +3325,6 @@ var DescribeToolsTool = class DescribeToolsTool {
3431
3325
  }
3432
3326
  }
3433
3327
  };
3434
-
3435
3328
  //#endregion
3436
3329
  //#region src/utils/toolCapabilities.ts
3437
3330
  const TOOL_CAPABILITIES_META_KEY = "agiflowai/capabilities";
@@ -3443,9 +3336,12 @@ function getToolCapabilities(tool) {
3443
3336
  function getUniqueSortedCapabilities(tools) {
3444
3337
  return Array.from(new Set(tools.flatMap((tool) => getToolCapabilities(tool)))).sort();
3445
3338
  }
3446
-
3447
3339
  //#endregion
3448
3340
  //#region src/tools/SearchListToolsTool.ts
3341
+ const SearchListToolsToolInputSchema = z.object({
3342
+ capability: z.string().optional().describe("Optional capability filter. Matches explicit capability tags first, then server summaries, server names, tool names, and tool descriptions."),
3343
+ serverName: z.string().optional().describe("Optional server name filter.")
3344
+ });
3449
3345
  var SearchListToolsTool = class SearchListToolsTool {
3450
3346
  static TOOL_NAME = "list_tools";
3451
3347
  constructor(_clientManager, definitionsCacheService) {
@@ -3455,6 +3351,9 @@ var SearchListToolsTool = class SearchListToolsTool {
3455
3351
  formatToolName(toolName, serverName, toolToServers) {
3456
3352
  return (toolToServers.get(toolName) || []).length > 1 ? `${serverName}__${toolName}` : toolName;
3457
3353
  }
3354
+ getInputSchema() {
3355
+ return SearchListToolsToolInputSchema;
3356
+ }
3458
3357
  async getDefinition() {
3459
3358
  const serverDefinitions = await this.definitionsCacheService.getServerDefinitions();
3460
3359
  const capabilitySummary = serverDefinitions.length > 0 ? serverDefinitions.map((server) => {
@@ -3465,23 +3364,11 @@ var SearchListToolsTool = class SearchListToolsTool {
3465
3364
  return {
3466
3365
  name: SearchListToolsTool.TOOL_NAME,
3467
3366
  description: `Search proxied MCP tools by server capability summary.\n\nAvailable capabilities:\n${capabilitySummary}`,
3468
- inputSchema: {
3469
- type: "object",
3470
- properties: {
3471
- capability: {
3472
- type: "string",
3473
- description: "Optional capability filter. Matches explicit capability tags first, then server summaries, server names, tool names, and tool descriptions."
3474
- },
3475
- serverName: {
3476
- type: "string",
3477
- description: "Optional server name filter."
3478
- }
3479
- },
3480
- additionalProperties: false
3481
- }
3367
+ inputSchema: z.toJSONSchema(SearchListToolsToolInputSchema, { reused: "inline" })
3482
3368
  };
3483
3369
  }
3484
- async execute(input) {
3370
+ async execute(rawInput) {
3371
+ const input = SearchListToolsToolInputSchema.parse(rawInput);
3485
3372
  const serverDefinitions = await this.definitionsCacheService.getServerDefinitions();
3486
3373
  const capabilityFilter = input.capability?.trim().toLowerCase();
3487
3374
  const serverNameFilter = input.serverName?.trim().toLowerCase();
@@ -3510,20 +3397,51 @@ var SearchListToolsTool = class SearchListToolsTool {
3510
3397
  capabilities: getToolCapabilities(tool)
3511
3398
  }))
3512
3399
  })).filter((server) => server.tools.length > 0);
3513
- const result = { servers: filteredServers };
3514
3400
  return {
3515
3401
  content: [{
3516
3402
  type: "text",
3517
- text: JSON.stringify(result, null, 2)
3403
+ text: JSON.stringify({ servers: filteredServers }, null, 2)
3518
3404
  }],
3519
3405
  isError: filteredServers.length === 0 ? true : void 0
3520
3406
  };
3521
3407
  }
3522
3408
  };
3523
-
3524
3409
  //#endregion
3525
3410
  //#region src/tools/UseToolTool.ts
3526
3411
  /**
3412
+ * UseToolTool - Progressive disclosure tool for calling MCP tools and skills
3413
+ *
3414
+ * DESIGN PATTERNS:
3415
+ * - Tool pattern with getDefinition() and execute() methods
3416
+ * - Dependency injection for client manager and skill service
3417
+ * - Progressive disclosure pattern
3418
+ * - Proxy pattern for forwarding tool calls
3419
+ *
3420
+ * CODING STANDARDS:
3421
+ * - Implement Tool interface from ../types
3422
+ * - Use TOOL_NAME constant with snake_case
3423
+ * - Return CallToolResult with content array
3424
+ * - Handle errors with isError flag
3425
+ *
3426
+ * AVOID:
3427
+ * - Complex business logic in execute method
3428
+ * - Unhandled promise rejections
3429
+ * - Missing error handling
3430
+ *
3431
+ * NAMING CONVENTIONS:
3432
+ * - Tools from MCP servers use serverName__toolName format when clashing
3433
+ * - Skills use skill__skillName format (skill__ prefix)
3434
+ */
3435
+ /**
3436
+ * Input schema for UseToolTool
3437
+ * @property toolName - Name of the tool or skill to execute
3438
+ * @property toolArgs - Arguments to pass to the tool (from describe_tools schema)
3439
+ */
3440
+ const UseToolToolInputSchema = z.object({
3441
+ toolName: z.string().min(1).describe("Name of the tool to execute"),
3442
+ toolArgs: z.record(z.string(), z.unknown()).optional().describe("Arguments to pass to the tool, as discovered from describe_tools")
3443
+ });
3444
+ /**
3527
3445
  * UseToolTool executes MCP tools and skills with proper error handling.
3528
3446
  *
3529
3447
  * This tool supports three invocation patterns:
@@ -3553,7 +3471,7 @@ var UseToolTool = class UseToolTool {
3553
3471
  this.clientManager = clientManager;
3554
3472
  this.skillService = skillService;
3555
3473
  this.definitionsCacheService = definitionsCacheService || new DefinitionsCacheService(clientManager, skillService);
3556
- this.serverId = serverId || DEFAULT_SERVER_ID;
3474
+ this.serverId = serverId || "unknown";
3557
3475
  }
3558
3476
  /**
3559
3477
  * Returns the MCP tool definition with name, description, and input schema.
@@ -3563,6 +3481,9 @@ var UseToolTool = class UseToolTool {
3563
3481
  *
3564
3482
  * @returns The tool definition conforming to MCP spec
3565
3483
  */
3484
+ getInputSchema() {
3485
+ return UseToolToolInputSchema;
3486
+ }
3566
3487
  getDefinition() {
3567
3488
  return {
3568
3489
  name: UseToolTool.TOOL_NAME,
@@ -3572,22 +3493,7 @@ var UseToolTool = class UseToolTool {
3572
3493
 
3573
3494
  IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverId}".
3574
3495
  `,
3575
- inputSchema: {
3576
- type: "object",
3577
- properties: {
3578
- toolName: {
3579
- type: "string",
3580
- description: "Name of the tool to execute",
3581
- minLength: 1
3582
- },
3583
- toolArgs: {
3584
- type: "object",
3585
- description: "Arguments to pass to the tool, as discovered from describe_tools"
3586
- }
3587
- },
3588
- required: ["toolName"],
3589
- additionalProperties: false
3590
- }
3496
+ inputSchema: z.toJSONSchema(UseToolToolInputSchema, { reused: "inline" })
3591
3497
  };
3592
3498
  }
3593
3499
  /**
@@ -3600,6 +3506,16 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
3600
3506
  * @param skill - The skill that was requested
3601
3507
  * @returns CallToolResult with guidance message
3602
3508
  */
3509
+ /**
3510
+ * Coerce toolArgs using the downstream tool's cached JSON Schema.
3511
+ * Converts the schema to Zod and runs coerceArgs so that string-encoded
3512
+ * objects/arrays are parsed before being forwarded to the downstream server.
3513
+ */
3514
+ async coerceToolArgs(serverName, toolName, toolArgs) {
3515
+ const jsonSchema = await this.definitionsCacheService.getToolSchema(serverName, toolName);
3516
+ if (!jsonSchema) return toolArgs;
3517
+ return coerceArgs(toolArgs, jsonSchemaToZod(jsonSchema));
3518
+ }
3603
3519
  executeSkill(skill) {
3604
3520
  return { content: [{
3605
3521
  type: "text",
@@ -3645,11 +3561,11 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
3645
3561
  * @param input - The tool/skill name and optional arguments
3646
3562
  * @returns CallToolResult with execution output or error
3647
3563
  */
3648
- async execute(input) {
3564
+ async execute(rawInput) {
3649
3565
  try {
3650
- const { toolName: inputToolName, toolArgs = {} } = input;
3651
- if (inputToolName.startsWith(SKILL_PREFIX)) {
3652
- const skillName = inputToolName.slice(SKILL_PREFIX.length);
3566
+ const { toolName: inputToolName, toolArgs = {} } = UseToolToolInputSchema.parse(rawInput);
3567
+ if (inputToolName.startsWith("skill__")) {
3568
+ const skillName = inputToolName.slice(7);
3653
3569
  if (this.skillService) {
3654
3570
  const skill = await this.skillService.getSkill(skillName);
3655
3571
  if (skill) return this.executeSkill(skill);
@@ -3676,7 +3592,8 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
3676
3592
  isError: true
3677
3593
  };
3678
3594
  const reqTimeout = this.clientManager.getServerRequestTimeout(serverName);
3679
- return await client.callTool(actualToolName, toolArgs, reqTimeout ? { timeout: reqTimeout } : void 0);
3595
+ const coercedArgs = await this.coerceToolArgs(serverName, actualToolName, toolArgs);
3596
+ return await client.callTool(actualToolName, coercedArgs, reqTimeout ? { timeout: reqTimeout } : void 0);
3680
3597
  } catch (error) {
3681
3598
  return {
3682
3599
  content: [{
@@ -3686,7 +3603,12 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
3686
3603
  isError: true
3687
3604
  };
3688
3605
  }
3689
- const matchingServers = await this.definitionsCacheService.getServersForTool(actualToolName);
3606
+ let matchingServers = await this.definitionsCacheService.getServersForTool(actualToolName);
3607
+ if (matchingServers.length === 0) {
3608
+ this.definitionsCacheService.clearLiveCache();
3609
+ await this.definitionsCacheService.collectForCache();
3610
+ matchingServers = await this.definitionsCacheService.getServersForTool(actualToolName);
3611
+ }
3690
3612
  if (matchingServers.length === 0) {
3691
3613
  if (this.skillService) {
3692
3614
  const skill = await this.skillService.getSkill(actualToolName);
@@ -3713,7 +3635,8 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
3713
3635
  const targetServerName = matchingServers[0];
3714
3636
  const client = await this.clientManager.ensureConnected(targetServerName);
3715
3637
  const targetReqTimeout = this.clientManager.getServerRequestTimeout(targetServerName);
3716
- return await client.callTool(actualToolName, toolArgs, targetReqTimeout ? { timeout: targetReqTimeout } : void 0);
3638
+ const coercedArgs = await this.coerceToolArgs(targetServerName, actualToolName, toolArgs);
3639
+ return await client.callTool(actualToolName, coercedArgs, targetReqTimeout ? { timeout: targetReqTimeout } : void 0);
3717
3640
  } catch (error) {
3718
3641
  return {
3719
3642
  content: [{
@@ -3734,7 +3657,6 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
3734
3657
  }
3735
3658
  }
3736
3659
  };
3737
-
3738
3660
  //#endregion
3739
3661
  //#region src/transports/http.ts
3740
3662
  /**
@@ -3876,13 +3798,13 @@ var AdminRateLimiter = class {
3876
3798
  * Parse JSON body from IncomingMessage
3877
3799
  */
3878
3800
  async function parseJsonBody$1(req) {
3879
- return new Promise((resolve$1, reject) => {
3801
+ return new Promise((resolve, reject) => {
3880
3802
  const chunks = [];
3881
3803
  req.on("data", (chunk) => chunks.push(chunk));
3882
3804
  req.on("end", () => {
3883
3805
  try {
3884
3806
  const body = Buffer.concat(chunks).toString("utf-8");
3885
- resolve$1(body ? JSON.parse(body) : void 0);
3807
+ resolve(body ? JSON.parse(body) : void 0);
3886
3808
  } catch (error) {
3887
3809
  reject(/* @__PURE__ */ new Error(`Failed to parse JSON body: ${toErrorMessage(error)}`));
3888
3810
  }
@@ -3970,24 +3892,24 @@ var HttpTransportHandler = class {
3970
3892
  }
3971
3893
  async handleAdminShutdownRequest(c) {
3972
3894
  if (!this.adminOptions?.onShutdownRequested) {
3973
- const payload$1 = {
3895
+ const payload = {
3974
3896
  ok: false,
3975
3897
  message: "Shutdown endpoint is not enabled for this server instance.",
3976
3898
  serverId: this.adminOptions?.serverId
3977
3899
  };
3978
- return c.json(payload$1, 404);
3900
+ return c.json(payload, 404);
3979
3901
  }
3980
3902
  const headers = {
3981
3903
  authorization: c.req.header("authorization"),
3982
3904
  "x-mcp-proxy-shutdown-token": c.req.header("x-mcp-proxy-shutdown-token")
3983
3905
  };
3984
3906
  if (!this.isAuthorizedShutdownRequest(headers)) {
3985
- const payload$1 = {
3907
+ const payload = {
3986
3908
  ok: false,
3987
3909
  message: "Unauthorized shutdown request: invalid or missing shutdown token.",
3988
3910
  serverId: this.adminOptions?.serverId
3989
3911
  };
3990
- return c.json(payload$1, 401);
3912
+ return c.json(payload, 401);
3991
3913
  }
3992
3914
  const payload = {
3993
3915
  ok: true,
@@ -4143,7 +4065,6 @@ var HttpTransportHandler = class {
4143
4065
  return this.config.host;
4144
4066
  }
4145
4067
  };
4146
-
4147
4068
  //#endregion
4148
4069
  //#region src/transports/sse.ts
4149
4070
  /**
@@ -4213,13 +4134,13 @@ var SseSessionManager = class {
4213
4134
  * Parse JSON body from IncomingMessage
4214
4135
  */
4215
4136
  async function parseJsonBody(req) {
4216
- return new Promise((resolve$1, reject) => {
4137
+ return new Promise((resolve, reject) => {
4217
4138
  const chunks = [];
4218
4139
  req.on("data", (chunk) => chunks.push(chunk));
4219
4140
  req.on("end", () => {
4220
4141
  try {
4221
4142
  const body = Buffer.concat(chunks).toString("utf-8");
4222
- resolve$1(body ? JSON.parse(body) : void 0);
4143
+ resolve(body ? JSON.parse(body) : void 0);
4223
4144
  } catch (error) {
4224
4145
  reject(/* @__PURE__ */ new Error(`Failed to parse JSON body: ${error instanceof Error ? error.message : String(error)}`));
4225
4146
  }
@@ -4303,7 +4224,7 @@ var SseTransportHandler = class {
4303
4224
  }
4304
4225
  }
4305
4226
  async start() {
4306
- return new Promise((resolve$1, reject) => {
4227
+ return new Promise((resolve, reject) => {
4307
4228
  try {
4308
4229
  const honoHandler = getRequestListener(this.honoApp.fetch);
4309
4230
  const server = createServer(async (req, res) => {
@@ -4318,7 +4239,7 @@ var SseTransportHandler = class {
4318
4239
  this.logger.info(`SSE endpoint: http://${this.config.host}:${this.config.port}/sse`);
4319
4240
  this.logger.info(`Messages endpoint: http://${this.config.host}:${this.config.port}/messages`);
4320
4241
  this.logger.info(`Health check: http://${this.config.host}:${this.config.port}/health`);
4321
- resolve$1();
4242
+ resolve();
4322
4243
  });
4323
4244
  server.on("error", (error) => {
4324
4245
  reject(error);
@@ -4330,7 +4251,7 @@ var SseTransportHandler = class {
4330
4251
  });
4331
4252
  }
4332
4253
  async stop() {
4333
- return new Promise((resolve$1, reject) => {
4254
+ return new Promise((resolve, reject) => {
4334
4255
  if (this.server) {
4335
4256
  const server = this.server;
4336
4257
  (async () => {
@@ -4340,14 +4261,14 @@ var SseTransportHandler = class {
4340
4261
  if (err) reject(err);
4341
4262
  else {
4342
4263
  this.server = null;
4343
- resolve$1();
4264
+ resolve();
4344
4265
  }
4345
4266
  });
4346
4267
  } catch (error) {
4347
4268
  reject(error);
4348
4269
  }
4349
4270
  })();
4350
- } else resolve$1();
4271
+ } else resolve();
4351
4272
  });
4352
4273
  }
4353
4274
  getPort() {
@@ -4357,7 +4278,6 @@ var SseTransportHandler = class {
4357
4278
  return this.config.host;
4358
4279
  }
4359
4280
  };
4360
-
4361
4281
  //#endregion
4362
4282
  //#region src/transports/stdio.ts
4363
4283
  /**
@@ -4384,7 +4304,6 @@ var StdioTransportHandler = class {
4384
4304
  }
4385
4305
  }
4386
4306
  };
4387
-
4388
4307
  //#endregion
4389
4308
  //#region src/transports/stdio-http.ts
4390
4309
  /**
@@ -4506,7 +4425,7 @@ var StdioHttpTransportHandler = class {
4506
4425
  async reconnectWithBackoff() {
4507
4426
  for (let attempt = 0; attempt < this.MAX_RECONNECT_ATTEMPTS; attempt++) {
4508
4427
  const delay = Math.min(this.RECONNECT_BASE_MS * 2 ** attempt, this.RECONNECT_MAX_MS);
4509
- await new Promise((resolve$1) => setTimeout(resolve$1, delay));
4428
+ await new Promise((resolve) => setTimeout(resolve, delay));
4510
4429
  try {
4511
4430
  await this.httpClient?.close().catch(() => void 0);
4512
4431
  const client = await this.createAndConnectClient();
@@ -4592,7 +4511,6 @@ var StdioHttpTransportHandler = class {
4592
4511
  return proxyServer;
4593
4512
  }
4594
4513
  };
4595
-
4596
4514
  //#endregion
4597
4515
  //#region src/container/index.ts
4598
4516
  /**
@@ -4624,13 +4542,13 @@ function createProxyIoCContainer(logger = console) {
4624
4542
  createDescribeToolsTool: (clientManager, skillService, serverId, definitionsCacheService) => new DescribeToolsTool(clientManager, skillService, serverId, definitionsCacheService),
4625
4543
  createUseToolTool: (clientManager, skillService, serverId, definitionsCacheService) => new UseToolTool(clientManager, skillService, serverId, definitionsCacheService),
4626
4544
  createSearchListToolsTool: (clientManager, definitionsCacheService) => new SearchListToolsTool(clientManager, definitionsCacheService),
4627
- createStdioTransportHandler: async (createServer$2) => {
4628
- return new StdioTransportHandler(await createServer$2(), logger);
4545
+ createStdioTransportHandler: async (createServer) => {
4546
+ return new StdioTransportHandler(await createServer(), logger);
4629
4547
  },
4630
- createSseTransportHandler: async (createServer$2, config) => {
4631
- return new SseTransportHandler(await createServer$2(), config, logger);
4548
+ createSseTransportHandler: async (createServer, config) => {
4549
+ return new SseTransportHandler(await createServer(), config, logger);
4632
4550
  },
4633
- createHttpTransportHandler: (createServer$2, config, adminOptions) => new HttpTransportHandler(createServer$2, config, adminOptions, logger),
4551
+ createHttpTransportHandler: (createServer, config, adminOptions) => new HttpTransportHandler(createServer, config, adminOptions, logger),
4634
4552
  createStdioHttpTransportHandler: (endpoint) => new StdioHttpTransportHandler({ endpoint }, logger)
4635
4553
  };
4636
4554
  }
@@ -4767,22 +4685,22 @@ async function createProxyContainer(options) {
4767
4685
  /**
4768
4686
  * Create a sessionless stdio transport handler from the shared server factory.
4769
4687
  */
4770
- async function createStdioTransportHandler(createServer$2) {
4771
- const server = await createServer$2();
4688
+ async function createStdioTransportHandler(createServer) {
4689
+ const server = await createServer();
4772
4690
  return createProxyIoCContainer().createStdioTransportHandler(() => Promise.resolve(server));
4773
4691
  }
4774
4692
  /**
4775
4693
  * Create an SSE transport handler from the shared server factory.
4776
4694
  */
4777
- async function createSseTransportHandler(createServer$2, config) {
4778
- const server = await createServer$2();
4695
+ async function createSseTransportHandler(createServer, config) {
4696
+ const server = await createServer();
4779
4697
  return createProxyIoCContainer().createSseTransportHandler(() => Promise.resolve(server), config);
4780
4698
  }
4781
4699
  /**
4782
4700
  * Create an HTTP transport handler from shared services.
4783
4701
  */
4784
- function createHttpTransportHandler(createServer$2, config, adminOptions) {
4785
- return createProxyIoCContainer().createHttpTransportHandler(createServer$2, config, adminOptions);
4702
+ function createHttpTransportHandler(createServer, config, adminOptions) {
4703
+ return createProxyIoCContainer().createHttpTransportHandler(createServer, config, adminOptions);
4786
4704
  }
4787
4705
  /**
4788
4706
  * Create a stdio-http transport handler from an endpoint URL.
@@ -4796,7 +4714,6 @@ function createStdioHttpTransportHandler(endpoint) {
4796
4714
  async function initializeSharedServices(options) {
4797
4715
  return createProxyContainer(options);
4798
4716
  }
4799
-
4800
4717
  //#endregion
4801
4718
  //#region src/server/index.ts
4802
4719
  /**
@@ -4947,21 +4864,27 @@ async function createSessionServer(shared) {
4947
4864
  })() : [await describeTools.getDefinition(), useToolWithCache.getDefinition()] }));
4948
4865
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
4949
4866
  const { name, arguments: args } = request.params;
4950
- if (name === DescribeToolsTool.TOOL_NAME) try {
4951
- return await describeTools.execute(args);
4952
- } catch (error) {
4953
- throw new Error(`Failed to execute ${name}: ${error instanceof Error ? error.message : String(error)}`);
4954
- }
4955
- if (name === UseToolTool.TOOL_NAME) try {
4956
- return await useToolWithCache.execute(args);
4957
- } catch (error) {
4958
- throw new Error(`Failed to execute ${name}: ${error instanceof Error ? error.message : String(error)}`);
4959
- }
4960
- if (name === SearchListToolsTool.TOOL_NAME && proxyMode === "search") try {
4961
- return await searchListTools.execute(args);
4962
- } catch (error) {
4963
- throw new Error(`Failed to execute ${name}: ${error instanceof Error ? error.message : String(error)}`);
4964
- }
4867
+ const executeWithCoercion = async (tool, toolName) => {
4868
+ const coerced = coerceArgs(args ?? {}, tool.getInputSchema());
4869
+ try {
4870
+ return await tool.execute(coerced);
4871
+ } catch (error) {
4872
+ if (error instanceof z.ZodError) return {
4873
+ content: [{
4874
+ type: "text",
4875
+ text: formatZodError(error, {
4876
+ schemaName: toolName,
4877
+ schema: tool.getInputSchema()
4878
+ })
4879
+ }],
4880
+ isError: true
4881
+ };
4882
+ throw new Error(`Failed to execute ${toolName}: ${error instanceof Error ? error.message : String(error)}`);
4883
+ }
4884
+ };
4885
+ if (name === DescribeToolsTool.TOOL_NAME) return await executeWithCoercion(describeTools, name);
4886
+ if (name === UseToolTool.TOOL_NAME) return await executeWithCoercion(useToolWithCache, name);
4887
+ if (name === SearchListToolsTool.TOOL_NAME && proxyMode === "search") return await executeWithCoercion(searchListTools, name);
4965
4888
  if (proxyMode === "flat") return await useToolWithCache.execute({
4966
4889
  toolName: name,
4967
4890
  toolArgs: args || {}
@@ -5041,7 +4964,6 @@ async function createSessionServer(shared) {
5041
4964
  async function createServer$1(options) {
5042
4965
  return createSessionServer(await createProxyContainer(options));
5043
4966
  }
5044
-
5045
4967
  //#endregion
5046
4968
  //#region src/types/index.ts
5047
4969
  /**
@@ -5052,6 +4974,5 @@ const TRANSPORT_MODE = {
5052
4974
  HTTP: "http",
5053
4975
  SSE: "sse"
5054
4976
  };
5055
-
5056
4977
  //#endregion
5057
- export { DefinitionsCacheService as C, version as D, ConfigFetcherService as E, createProxyLogger as S, findConfigFile as T, DescribeToolsTool as _, createProxyContainer as a, RuntimeStateService as b, createStdioHttpTransportHandler as c, StdioHttpTransportHandler as d, StdioTransportHandler as f, SearchListToolsTool as g, UseToolTool as h, createHttpTransportHandler as i, createStdioTransportHandler as l, HttpTransportHandler as m, createServer$1 as n, createProxyIoCContainer as o, SseTransportHandler as p, createSessionServer as r, createSseTransportHandler as s, TRANSPORT_MODE as t, initializeSharedServices as u, SkillService as v, generateServerId as w, McpClientManagerService as x, StopServerService as y };
4978
+ export { DefinitionsCacheService as C, version as D, ConfigFetcherService as E, createProxyLogger as S, findConfigFile as T, DescribeToolsTool as _, createProxyContainer as a, RuntimeStateService as b, createStdioHttpTransportHandler as c, StdioHttpTransportHandler as d, StdioTransportHandler as f, SearchListToolsTool as g, UseToolTool as h, createHttpTransportHandler as i, createStdioTransportHandler as l, HttpTransportHandler as m, createServer$1 as n, createProxyIoCContainer as o, SseTransportHandler as p, createSessionServer as r, createSseTransportHandler as s, TRANSPORT_MODE as t, initializeSharedServices as u, SkillService as v, generateServerId as w, McpClientManagerService as x, StopServerService as y };