@agiflowai/one-mcp 0.3.10 → 0.3.12

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,12 +1,12 @@
1
1
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
- import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListToolsRequestSchema, isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
3
- import { access, mkdir, readFile, readdir, stat, unlink, watch, writeFile } from "node:fs/promises";
2
+ import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
3
+ import { access, mkdir, readFile, readdir, rm, stat, unlink, watch, writeFile } from "node:fs/promises";
4
4
  import { existsSync } from "node:fs";
5
5
  import yaml from "js-yaml";
6
6
  import { z } from "zod";
7
7
  import { createHash, randomBytes, randomUUID } from "node:crypto";
8
8
  import { dirname, isAbsolute, join, resolve } from "node:path";
9
- import { tmpdir } from "node:os";
9
+ import { homedir, tmpdir } from "node:os";
10
10
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
11
11
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
12
12
  import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
@@ -760,14 +760,22 @@ var ConfigFetcherService = class {
760
760
  */
761
761
  mergeConfigurations(localConfig, remoteConfig, mergeStrategy) {
762
762
  switch (mergeStrategy) {
763
- case "local-priority": return { mcpServers: {
764
- ...remoteConfig.mcpServers,
765
- ...localConfig.mcpServers
766
- } };
767
- case "remote-priority": return { mcpServers: {
768
- ...localConfig.mcpServers,
769
- ...remoteConfig.mcpServers
770
- } };
763
+ case "local-priority": return {
764
+ id: localConfig.id ?? remoteConfig.id,
765
+ mcpServers: {
766
+ ...remoteConfig.mcpServers,
767
+ ...localConfig.mcpServers
768
+ },
769
+ skills: localConfig.skills ?? remoteConfig.skills
770
+ };
771
+ case "remote-priority": return {
772
+ id: remoteConfig.id ?? localConfig.id,
773
+ mcpServers: {
774
+ ...localConfig.mcpServers,
775
+ ...remoteConfig.mcpServers
776
+ },
777
+ skills: remoteConfig.skills ?? localConfig.skills
778
+ };
771
779
  case "merge-deep": {
772
780
  const merged = { ...remoteConfig.mcpServers };
773
781
  for (const [serverName, localServerConfig] of Object.entries(localConfig.mcpServers)) if (merged[serverName]) {
@@ -794,7 +802,11 @@ var ConfigFetcherService = class {
794
802
  config: mergedConfig
795
803
  };
796
804
  } else merged[serverName] = localServerConfig;
797
- return { mcpServers: merged };
805
+ return {
806
+ id: localConfig.id ?? remoteConfig.id,
807
+ mcpServers: merged,
808
+ skills: localConfig.skills ?? remoteConfig.skills
809
+ };
798
810
  }
799
811
  default: throw new Error(`Unknown merge strategy: ${mergeStrategy}`);
800
812
  }
@@ -815,213 +827,6 @@ var ConfigFetcherService = class {
815
827
  }
816
828
  };
817
829
 
818
- //#endregion
819
- //#region src/services/McpClientManagerService.ts
820
- /** Default connection timeout in milliseconds (30 seconds) */
821
- const DEFAULT_CONNECTION_TIMEOUT_MS = 3e4;
822
- /**
823
- * MCP Client wrapper for managing individual server connections
824
- * This is an internal class used by McpClientManagerService
825
- */
826
- var McpClient = class {
827
- serverName;
828
- serverInstruction;
829
- toolBlacklist;
830
- omitToolDescription;
831
- prompts;
832
- transport;
833
- client;
834
- childProcess;
835
- connected = false;
836
- constructor(serverName, transport, client, config) {
837
- this.serverName = serverName;
838
- this.serverInstruction = config.instruction;
839
- this.toolBlacklist = config.toolBlacklist;
840
- this.omitToolDescription = config.omitToolDescription;
841
- this.prompts = config.prompts;
842
- this.transport = transport;
843
- this.client = client;
844
- }
845
- setChildProcess(process$1) {
846
- this.childProcess = process$1;
847
- }
848
- setConnected(connected) {
849
- this.connected = connected;
850
- }
851
- async listTools() {
852
- if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
853
- return (await this.client.listTools()).tools;
854
- }
855
- async listResources() {
856
- if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
857
- return (await this.client.listResources()).resources;
858
- }
859
- async listPrompts() {
860
- if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
861
- return (await this.client.listPrompts()).prompts;
862
- }
863
- async callTool(name, args) {
864
- if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
865
- return await this.client.callTool({
866
- name,
867
- arguments: args
868
- });
869
- }
870
- async readResource(uri) {
871
- if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
872
- return await this.client.readResource({ uri });
873
- }
874
- async getPrompt(name, args) {
875
- if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
876
- return await this.client.getPrompt({
877
- name,
878
- arguments: args
879
- });
880
- }
881
- async close() {
882
- if (this.childProcess) this.childProcess.kill();
883
- await this.client.close();
884
- this.connected = false;
885
- }
886
- };
887
- /**
888
- * Service for managing MCP client connections to remote servers
889
- */
890
- var McpClientManagerService = class {
891
- clients = /* @__PURE__ */ new Map();
892
- constructor() {
893
- process.on("exit", () => {
894
- this.cleanupOnExit();
895
- });
896
- process.on("SIGINT", () => {
897
- this.cleanupOnExit();
898
- process.exit(0);
899
- });
900
- process.on("SIGTERM", () => {
901
- this.cleanupOnExit();
902
- process.exit(0);
903
- });
904
- }
905
- /**
906
- * Cleanup all resources on exit (child processes)
907
- */
908
- cleanupOnExit() {
909
- for (const [serverName, client] of this.clients) try {
910
- const childProcess = client["childProcess"];
911
- if (childProcess && !childProcess.killed) {
912
- console.error(`Killing stdio MCP server: ${serverName} (PID: ${childProcess.pid})`);
913
- childProcess.kill("SIGTERM");
914
- setTimeout(() => {
915
- if (!childProcess.killed) {
916
- console.error(`Force killing stdio MCP server: ${serverName} (PID: ${childProcess.pid})`);
917
- childProcess.kill("SIGKILL");
918
- }
919
- }, 1e3);
920
- }
921
- } catch (error) {
922
- console.error(`Failed to kill child process for ${serverName}:`, error);
923
- }
924
- }
925
- /**
926
- * Connect to an MCP server based on its configuration with timeout
927
- * Uses the timeout from server config, falling back to default (30s)
928
- */
929
- async connectToServer(serverName, config) {
930
- const timeoutMs = config.timeout ?? DEFAULT_CONNECTION_TIMEOUT_MS;
931
- if (this.clients.has(serverName)) throw new Error(`Client for ${serverName} is already connected`);
932
- const client = new Client({
933
- name: `@agiflowai/one-mcp-client`,
934
- version: "0.1.0"
935
- }, { capabilities: {} });
936
- const mcpClient = new McpClient(serverName, config.transport, client, {
937
- instruction: config.instruction,
938
- toolBlacklist: config.toolBlacklist,
939
- omitToolDescription: config.omitToolDescription,
940
- prompts: config.prompts
941
- });
942
- try {
943
- await Promise.race([this.performConnection(mcpClient, config), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`Connection timeout after ${timeoutMs}ms`)), timeoutMs))]);
944
- mcpClient.setConnected(true);
945
- if (!mcpClient.serverInstruction) try {
946
- const serverInstruction = mcpClient["client"].getInstructions();
947
- if (serverInstruction) mcpClient.serverInstruction = serverInstruction;
948
- } catch (error) {
949
- console.error(`Failed to get server instruction from ${serverName}:`, error);
950
- }
951
- this.clients.set(serverName, mcpClient);
952
- } catch (error) {
953
- await mcpClient.close();
954
- throw error;
955
- }
956
- }
957
- /**
958
- * Perform the actual connection to MCP server
959
- */
960
- async performConnection(mcpClient, config) {
961
- if (config.transport === "stdio") await this.connectStdioClient(mcpClient, config.config);
962
- else if (config.transport === "http") await this.connectHttpClient(mcpClient, config.config);
963
- else if (config.transport === "sse") await this.connectSseClient(mcpClient, config.config);
964
- else throw new Error(`Unsupported transport type: ${config.transport}`);
965
- }
966
- async connectStdioClient(mcpClient, config) {
967
- const transport = new StdioClientTransport({
968
- command: config.command,
969
- args: config.args,
970
- env: {
971
- ...process.env,
972
- ...config.env ?? {}
973
- }
974
- });
975
- await mcpClient["client"].connect(transport);
976
- const childProcess = transport["_process"];
977
- if (childProcess) mcpClient.setChildProcess(childProcess);
978
- }
979
- async connectHttpClient(mcpClient, config) {
980
- const transport = new StreamableHTTPClientTransport(new URL(config.url), { requestInit: config.headers ? { headers: config.headers } : void 0 });
981
- await mcpClient["client"].connect(transport);
982
- }
983
- async connectSseClient(mcpClient, config) {
984
- const transport = new SSEClientTransport(new URL(config.url));
985
- await mcpClient["client"].connect(transport);
986
- }
987
- /**
988
- * Get a connected client by server name
989
- */
990
- getClient(serverName) {
991
- return this.clients.get(serverName);
992
- }
993
- /**
994
- * Get all connected clients
995
- */
996
- getAllClients() {
997
- return Array.from(this.clients.values());
998
- }
999
- /**
1000
- * Disconnect from a specific server
1001
- */
1002
- async disconnectServer(serverName) {
1003
- const client = this.clients.get(serverName);
1004
- if (client) {
1005
- await client.close();
1006
- this.clients.delete(serverName);
1007
- }
1008
- }
1009
- /**
1010
- * Disconnect from all servers
1011
- */
1012
- async disconnectAll() {
1013
- const disconnectPromises = Array.from(this.clients.values()).map((client) => client.close());
1014
- await Promise.all(disconnectPromises);
1015
- this.clients.clear();
1016
- }
1017
- /**
1018
- * Check if a server is connected
1019
- */
1020
- isConnected(serverName) {
1021
- return this.clients.has(serverName);
1022
- }
1023
- };
1024
-
1025
830
  //#endregion
1026
831
  //#region src/utils/findConfigFile.ts
1027
832
  /**
@@ -1287,8 +1092,565 @@ function generateServerId(length = DEFAULT_ID_LENGTH) {
1287
1092
  remaining--;
1288
1093
  }
1289
1094
  }
1290
- return result;
1291
- }
1095
+ return result;
1096
+ }
1097
+
1098
+ //#endregion
1099
+ //#region src/constants/index.ts
1100
+ /**
1101
+ * Shared constants for one-mcp package
1102
+ */
1103
+ /**
1104
+ * Prefix added to skill names when they clash with MCP tool names.
1105
+ * This ensures skills can be uniquely identified even when a tool has the same name.
1106
+ */
1107
+ const SKILL_PREFIX = "skill__";
1108
+ /**
1109
+ * Log prefix for skill detection messages.
1110
+ * Used to easily filter skill detection logs in stderr output.
1111
+ */
1112
+ const LOG_PREFIX_SKILL_DETECTION = "[skill-detection]";
1113
+ /**
1114
+ * Log prefix for general MCP capability discovery messages.
1115
+ */
1116
+ const LOG_PREFIX_CAPABILITY_DISCOVERY = "[capability-discovery]";
1117
+ /**
1118
+ * Prefix for prompt-based skill locations.
1119
+ * Format: "prompt:{serverName}:{promptName}"
1120
+ */
1121
+ const PROMPT_LOCATION_PREFIX = "prompt:";
1122
+ /**
1123
+ * Default server ID used when no ID is provided via CLI or config.
1124
+ * This fallback is used when auto-generation also fails.
1125
+ */
1126
+ const DEFAULT_SERVER_ID = "unknown";
1127
+
1128
+ //#endregion
1129
+ //#region src/services/DefinitionsCacheService.ts
1130
+ /**
1131
+ * DefinitionsCacheService
1132
+ *
1133
+ * Provides shared discovery, caching, and serialization for startup-time MCP
1134
+ * capability metadata. This avoids repeated remote enumeration during
1135
+ * mcp-serve startup and describe_tools generation.
1136
+ */
1137
+ function isYamlPath(filePath) {
1138
+ return filePath.endsWith(".yaml") || filePath.endsWith(".yml");
1139
+ }
1140
+ function toErrorMessage(error) {
1141
+ return error instanceof Error ? error.message : String(error);
1142
+ }
1143
+ function sanitizeConfigPathForFilename(configFilePath) {
1144
+ const absoluteConfigPath = resolve(configFilePath);
1145
+ const normalizedPath = absoluteConfigPath.length >= 2 && absoluteConfigPath[1] === ":" && (absoluteConfigPath[0] >= "A" && absoluteConfigPath[0] <= "Z" || absoluteConfigPath[0] >= "a" && absoluteConfigPath[0] <= "z") ? `${absoluteConfigPath[0].toLowerCase()}${absoluteConfigPath.slice(1)}` : absoluteConfigPath;
1146
+ let result = "";
1147
+ let previousWasUnderscore = false;
1148
+ for (const char of normalizedPath) {
1149
+ if (char >= "a" && char <= "z" || char >= "A" && char <= "Z" || char >= "0" && char <= "9" || char === "." || char === "_" || char === "-") {
1150
+ result += char;
1151
+ previousWasUnderscore = false;
1152
+ continue;
1153
+ }
1154
+ if (!previousWasUnderscore) {
1155
+ result += "_";
1156
+ previousWasUnderscore = true;
1157
+ }
1158
+ }
1159
+ let start = 0;
1160
+ let end = result.length;
1161
+ while (start < end && result[start] === "_") start += 1;
1162
+ while (end > start && result[end - 1] === "_") end -= 1;
1163
+ return result.slice(start, end);
1164
+ }
1165
+ function cloneCache(cache) {
1166
+ return {
1167
+ ...cache,
1168
+ failures: [...cache.failures ?? []],
1169
+ skills: (cache.skills ?? []).map((skill) => ({ ...skill })),
1170
+ servers: Object.fromEntries(Object.entries(cache.servers).map(([serverName, server]) => [serverName, {
1171
+ ...server,
1172
+ tools: (server.tools ?? []).map((tool) => ({ ...tool })),
1173
+ resources: (server.resources ?? []).map((resource) => ({ ...resource })),
1174
+ prompts: (server.prompts ?? []).map((prompt) => ({
1175
+ ...prompt,
1176
+ arguments: prompt.arguments?.map((arg) => ({ ...arg }))
1177
+ })),
1178
+ promptSkills: (server.promptSkills ?? []).map((promptSkill) => ({
1179
+ ...promptSkill,
1180
+ skill: { ...promptSkill.skill }
1181
+ }))
1182
+ }]))
1183
+ };
1184
+ }
1185
+ var DefinitionsCacheService = class {
1186
+ clientManager;
1187
+ skillService;
1188
+ cacheData;
1189
+ liveDefinitionsPromise = null;
1190
+ mergedDefinitionsPromise = null;
1191
+ constructor(clientManager, skillService, options) {
1192
+ this.clientManager = clientManager;
1193
+ this.skillService = skillService;
1194
+ this.cacheData = options?.cacheData;
1195
+ }
1196
+ static async readFromFile(filePath) {
1197
+ const content = await readFile(filePath, "utf-8");
1198
+ const parsed = isYamlPath(filePath) ? yaml.load(content) : JSON.parse(content);
1199
+ if (!parsed || typeof parsed !== "object") throw new Error("Definitions cache must be an object");
1200
+ const cache = parsed;
1201
+ if (cache.version !== 1 || !cache.servers) throw new Error("Definitions cache is missing required fields");
1202
+ return {
1203
+ ...cache,
1204
+ failures: Array.isArray(cache.failures) ? cache.failures : [],
1205
+ skills: Array.isArray(cache.skills) ? cache.skills : [],
1206
+ servers: Object.fromEntries(Object.entries(cache.servers).map(([serverName, server]) => [serverName, {
1207
+ ...server,
1208
+ tools: Array.isArray(server.tools) ? server.tools : [],
1209
+ resources: Array.isArray(server.resources) ? server.resources : [],
1210
+ prompts: Array.isArray(server.prompts) ? server.prompts : [],
1211
+ promptSkills: Array.isArray(server.promptSkills) ? server.promptSkills : []
1212
+ }]))
1213
+ };
1214
+ }
1215
+ static async writeToFile(filePath, cache) {
1216
+ const serialized = isYamlPath(filePath) ? yaml.dump(cache, { noRefs: true }) : JSON.stringify(cache, null, 2);
1217
+ await mkdir(dirname(filePath), { recursive: true });
1218
+ await writeFile(filePath, serialized, "utf-8");
1219
+ }
1220
+ static getDefaultCachePath(configFilePath) {
1221
+ const sanitizedPath = sanitizeConfigPathForFilename(configFilePath);
1222
+ return join(homedir(), ".aicode-toolkit", `${sanitizedPath}.definitions-cache.json`);
1223
+ }
1224
+ static generateConfigHash(config) {
1225
+ return createHash("sha256").update(JSON.stringify(config)).digest("hex");
1226
+ }
1227
+ static isCacheValid(cache, options) {
1228
+ if (options.configHash && cache.configHash && cache.configHash !== options.configHash) return false;
1229
+ if (options.oneMcpVersion && cache.oneMcpVersion && cache.oneMcpVersion !== options.oneMcpVersion) return false;
1230
+ return true;
1231
+ }
1232
+ static async clearFile(filePath) {
1233
+ await rm(filePath, { force: true });
1234
+ }
1235
+ clearLiveCache() {
1236
+ this.liveDefinitionsPromise = null;
1237
+ this.mergedDefinitionsPromise = null;
1238
+ }
1239
+ setCacheData(cacheData) {
1240
+ this.cacheData = cacheData;
1241
+ this.mergedDefinitionsPromise = null;
1242
+ }
1243
+ async collectForCache(options) {
1244
+ const liveDefinitions = await this.collectLiveDefinitions(options);
1245
+ this.setCacheData(liveDefinitions);
1246
+ this.liveDefinitionsPromise = Promise.resolve(cloneCache(liveDefinitions));
1247
+ return cloneCache(liveDefinitions);
1248
+ }
1249
+ async getDefinitions() {
1250
+ if (this.mergedDefinitionsPromise) return this.mergedDefinitionsPromise;
1251
+ this.mergedDefinitionsPromise = (async () => {
1252
+ const clients = this.clientManager.getAllClients();
1253
+ if (!this.cacheData) return this.getLiveDefinitions();
1254
+ const missingServers = clients.map((client) => client.serverName).filter((serverName) => !this.cacheData?.servers[serverName]);
1255
+ if (missingServers.length === 0) return cloneCache(this.cacheData);
1256
+ const liveDefinitions = await this.getLiveDefinitions();
1257
+ const merged = cloneCache(this.cacheData);
1258
+ for (const serverName of missingServers) {
1259
+ const serverDefinition = liveDefinitions.servers[serverName];
1260
+ if (serverDefinition) merged.servers[serverName] = serverDefinition;
1261
+ }
1262
+ const failureMap = /* @__PURE__ */ new Map();
1263
+ for (const failure of [...merged.failures, ...liveDefinitions.failures]) failureMap.set(failure.serverName, failure.error);
1264
+ merged.failures = Array.from(failureMap.entries()).map(([serverName, error]) => ({
1265
+ serverName,
1266
+ error
1267
+ }));
1268
+ if (merged.skills.length === 0 && liveDefinitions.skills.length > 0) merged.skills = liveDefinitions.skills.map((skill) => ({ ...skill }));
1269
+ return merged;
1270
+ })();
1271
+ return this.mergedDefinitionsPromise;
1272
+ }
1273
+ async getServerDefinitions() {
1274
+ const definitions = await this.getDefinitions();
1275
+ const serverOrder = this.clientManager.getKnownServerNames();
1276
+ if (serverOrder.length === 0) return Object.values(definitions.servers);
1277
+ return serverOrder.map((serverName) => definitions.servers[serverName]).filter((server) => server !== void 0);
1278
+ }
1279
+ async getServersForTool(toolName) {
1280
+ return (await this.getServerDefinitions()).filter((serverDefinition) => serverDefinition.tools.some((tool) => tool.name === toolName)).map((serverDefinition) => serverDefinition.serverName);
1281
+ }
1282
+ async getServersForResource(uri) {
1283
+ return (await this.getServerDefinitions()).filter((serverDefinition) => serverDefinition.resources.some((resource) => resource.uri === uri)).map((serverDefinition) => serverDefinition.serverName);
1284
+ }
1285
+ async getPromptSkillByName(skillName) {
1286
+ const definitions = await this.getDefinitions();
1287
+ for (const [serverName, server] of Object.entries(definitions.servers)) for (const promptSkill of server.promptSkills) if (promptSkill.skill.name === skillName) return {
1288
+ serverName,
1289
+ promptName: promptSkill.promptName,
1290
+ skill: promptSkill.skill,
1291
+ autoDetected: promptSkill.autoDetected
1292
+ };
1293
+ }
1294
+ async getCachedFileSkills() {
1295
+ return (await this.getDefinitions()).skills.map((skill) => ({ ...skill }));
1296
+ }
1297
+ async getLiveDefinitions() {
1298
+ if (!this.liveDefinitionsPromise) this.liveDefinitionsPromise = this.collectLiveDefinitions();
1299
+ return this.liveDefinitionsPromise;
1300
+ }
1301
+ async collectLiveDefinitions(options) {
1302
+ const clients = this.clientManager.getAllClients();
1303
+ const failures = [];
1304
+ const servers = {};
1305
+ const serverResults = await Promise.all(clients.map(async (client) => {
1306
+ try {
1307
+ const tools = await client.listTools();
1308
+ const resources = await this.listResourcesSafe(client);
1309
+ const prompts = await this.listPromptsSafe(client);
1310
+ const blacklist = new Set(client.toolBlacklist || []);
1311
+ const filteredTools = tools.filter((tool) => !blacklist.has(tool.name));
1312
+ const promptSkills = await this.collectPromptSkillsForClient(client, prompts);
1313
+ return {
1314
+ serverName: client.serverName,
1315
+ serverInstruction: client.serverInstruction,
1316
+ omitToolDescription: client.omitToolDescription,
1317
+ toolBlacklist: client.toolBlacklist,
1318
+ tools: filteredTools.map((tool) => ({
1319
+ name: tool.name,
1320
+ description: tool.description,
1321
+ inputSchema: tool.inputSchema,
1322
+ _meta: tool._meta
1323
+ })),
1324
+ resources,
1325
+ prompts,
1326
+ promptSkills
1327
+ };
1328
+ } catch (error) {
1329
+ failures.push({
1330
+ serverName: client.serverName,
1331
+ error: toErrorMessage(error)
1332
+ });
1333
+ return null;
1334
+ }
1335
+ }));
1336
+ for (const serverDefinition of serverResults) if (serverDefinition) servers[serverDefinition.serverName] = serverDefinition;
1337
+ return {
1338
+ version: 1,
1339
+ oneMcpVersion: options?.oneMcpVersion,
1340
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1341
+ configPath: options?.configPath,
1342
+ configHash: options?.configHash,
1343
+ serverId: options?.serverId,
1344
+ servers,
1345
+ skills: await this.collectFileSkills(),
1346
+ failures
1347
+ };
1348
+ }
1349
+ async collectFileSkills() {
1350
+ if (!this.skillService) return [];
1351
+ return (await this.skillService.getSkills()).map((skill) => this.toCachedFileSkill(skill));
1352
+ }
1353
+ toCachedFileSkill(skill) {
1354
+ return {
1355
+ name: skill.name,
1356
+ description: skill.description,
1357
+ location: skill.location,
1358
+ basePath: skill.basePath
1359
+ };
1360
+ }
1361
+ async listPromptsSafe(client) {
1362
+ try {
1363
+ return (await client.listPrompts()).map((prompt) => ({
1364
+ name: prompt.name,
1365
+ description: prompt.description,
1366
+ arguments: prompt.arguments?.map((arg) => ({ ...arg }))
1367
+ }));
1368
+ } catch (error) {
1369
+ console.error(`${LOG_PREFIX_SKILL_DETECTION} Failed to list prompts from ${client.serverName}: ${toErrorMessage(error)}`);
1370
+ return [];
1371
+ }
1372
+ }
1373
+ async listResourcesSafe(client) {
1374
+ try {
1375
+ return (await client.listResources()).map((resource) => ({
1376
+ uri: resource.uri,
1377
+ name: resource.name,
1378
+ description: resource.description,
1379
+ mimeType: resource.mimeType
1380
+ }));
1381
+ } catch (error) {
1382
+ console.error(`${LOG_PREFIX_CAPABILITY_DISCOVERY} Failed to list resources from ${client.serverName}: ${toErrorMessage(error)}`);
1383
+ return [];
1384
+ }
1385
+ }
1386
+ async collectPromptSkillsForClient(client, prompts) {
1387
+ const configuredPromptNames = new Set(client.prompts ? Object.keys(client.prompts) : []);
1388
+ const promptSkills = [];
1389
+ if (client.prompts) {
1390
+ for (const [promptName, promptConfig] of Object.entries(client.prompts)) if (promptConfig.skill) promptSkills.push({
1391
+ promptName,
1392
+ skill: { ...promptConfig.skill }
1393
+ });
1394
+ }
1395
+ const autoDetectedSkills = await Promise.all(prompts.map(async (prompt) => {
1396
+ if (configuredPromptNames.has(prompt.name)) return null;
1397
+ try {
1398
+ const skillExtraction = extractSkillFrontMatter((await client.getPrompt(prompt.name)).messages?.map((message) => {
1399
+ const content = message.content;
1400
+ if (typeof content === "string") return content;
1401
+ if (content && typeof content === "object" && "text" in content) return String(content.text);
1402
+ return "";
1403
+ }).join("\n") || "");
1404
+ if (!skillExtraction) return null;
1405
+ return {
1406
+ promptName: prompt.name,
1407
+ skill: skillExtraction.skill,
1408
+ autoDetected: true
1409
+ };
1410
+ } catch (error) {
1411
+ console.error(`${LOG_PREFIX_SKILL_DETECTION} Failed to fetch prompt '${prompt.name}' from ${client.serverName}: ${toErrorMessage(error)}`);
1412
+ return null;
1413
+ }
1414
+ }));
1415
+ for (const autoDetectedSkill of autoDetectedSkills) if (autoDetectedSkill) promptSkills.push(autoDetectedSkill);
1416
+ return promptSkills;
1417
+ }
1418
+ };
1419
+
1420
+ //#endregion
1421
+ //#region src/services/McpClientManagerService.ts
1422
+ /** Default connection timeout in milliseconds (30 seconds) */
1423
+ const DEFAULT_CONNECTION_TIMEOUT_MS = 3e4;
1424
+ /**
1425
+ * MCP Client wrapper for managing individual server connections
1426
+ * This is an internal class used by McpClientManagerService
1427
+ */
1428
+ var McpClient = class {
1429
+ serverName;
1430
+ serverInstruction;
1431
+ toolBlacklist;
1432
+ omitToolDescription;
1433
+ prompts;
1434
+ transport;
1435
+ client;
1436
+ childProcess;
1437
+ connected = false;
1438
+ constructor(serverName, transport, client, config) {
1439
+ this.serverName = serverName;
1440
+ this.serverInstruction = config.instruction;
1441
+ this.toolBlacklist = config.toolBlacklist;
1442
+ this.omitToolDescription = config.omitToolDescription;
1443
+ this.prompts = config.prompts;
1444
+ this.transport = transport;
1445
+ this.client = client;
1446
+ }
1447
+ setChildProcess(process$1) {
1448
+ this.childProcess = process$1;
1449
+ }
1450
+ setConnected(connected) {
1451
+ this.connected = connected;
1452
+ }
1453
+ async listTools() {
1454
+ if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1455
+ return (await this.client.listTools()).tools;
1456
+ }
1457
+ async listResources() {
1458
+ if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1459
+ return (await this.client.listResources()).resources;
1460
+ }
1461
+ async listPrompts() {
1462
+ if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1463
+ return (await this.client.listPrompts()).prompts;
1464
+ }
1465
+ async callTool(name, args) {
1466
+ if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1467
+ return await this.client.callTool({
1468
+ name,
1469
+ arguments: args
1470
+ });
1471
+ }
1472
+ async readResource(uri) {
1473
+ if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1474
+ return await this.client.readResource({ uri });
1475
+ }
1476
+ async getPrompt(name, args) {
1477
+ if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1478
+ return await this.client.getPrompt({
1479
+ name,
1480
+ arguments: args
1481
+ });
1482
+ }
1483
+ async close() {
1484
+ if (this.childProcess) this.childProcess.kill();
1485
+ await this.client.close();
1486
+ this.connected = false;
1487
+ }
1488
+ };
1489
+ /**
1490
+ * Service for managing MCP client connections to remote servers
1491
+ */
1492
+ var McpClientManagerService = class {
1493
+ clients = /* @__PURE__ */ new Map();
1494
+ serverConfigs = /* @__PURE__ */ new Map();
1495
+ connectionPromises = /* @__PURE__ */ new Map();
1496
+ constructor() {
1497
+ process.on("exit", () => {
1498
+ this.cleanupOnExit();
1499
+ });
1500
+ process.on("SIGINT", () => {
1501
+ this.cleanupOnExit();
1502
+ process.exit(0);
1503
+ });
1504
+ process.on("SIGTERM", () => {
1505
+ this.cleanupOnExit();
1506
+ process.exit(0);
1507
+ });
1508
+ }
1509
+ /**
1510
+ * Cleanup all resources on exit (child processes)
1511
+ */
1512
+ cleanupOnExit() {
1513
+ for (const [serverName, client] of this.clients) try {
1514
+ const childProcess = client["childProcess"];
1515
+ if (childProcess && !childProcess.killed) {
1516
+ console.error(`Killing stdio MCP server: ${serverName} (PID: ${childProcess.pid})`);
1517
+ childProcess.kill("SIGTERM");
1518
+ setTimeout(() => {
1519
+ if (!childProcess.killed) {
1520
+ console.error(`Force killing stdio MCP server: ${serverName} (PID: ${childProcess.pid})`);
1521
+ childProcess.kill("SIGKILL");
1522
+ }
1523
+ }, 1e3);
1524
+ }
1525
+ } catch (error) {
1526
+ console.error(`Failed to kill child process for ${serverName}:`, error);
1527
+ }
1528
+ }
1529
+ /**
1530
+ * Connect to an MCP server based on its configuration with timeout
1531
+ * Uses the timeout from server config, falling back to default (30s)
1532
+ */
1533
+ async connectToServer(serverName, config) {
1534
+ this.serverConfigs.set(serverName, config);
1535
+ await this.ensureConnected(serverName);
1536
+ }
1537
+ registerServerConfigs(configs) {
1538
+ for (const [serverName, config] of Object.entries(configs)) this.serverConfigs.set(serverName, config);
1539
+ }
1540
+ getKnownServerNames() {
1541
+ return Array.from(this.serverConfigs.keys());
1542
+ }
1543
+ async ensureConnected(serverName) {
1544
+ const existingClient = this.clients.get(serverName);
1545
+ if (existingClient) return existingClient;
1546
+ const inflightConnection = this.connectionPromises.get(serverName);
1547
+ if (inflightConnection) return await inflightConnection;
1548
+ const config = this.serverConfigs.get(serverName);
1549
+ if (!config) throw new Error(`No configuration found for server "${serverName}"`);
1550
+ const connectionPromise = this.createConnection(serverName, config);
1551
+ this.connectionPromises.set(serverName, connectionPromise);
1552
+ try {
1553
+ return await connectionPromise;
1554
+ } finally {
1555
+ this.connectionPromises.delete(serverName);
1556
+ }
1557
+ }
1558
+ async createConnection(serverName, config) {
1559
+ const timeoutMs = config.timeout ?? DEFAULT_CONNECTION_TIMEOUT_MS;
1560
+ const client = new Client({
1561
+ name: `@agiflowai/one-mcp-client`,
1562
+ version: "0.1.0"
1563
+ }, { capabilities: {} });
1564
+ const mcpClient = new McpClient(serverName, config.transport, client, {
1565
+ instruction: config.instruction,
1566
+ toolBlacklist: config.toolBlacklist,
1567
+ omitToolDescription: config.omitToolDescription,
1568
+ prompts: config.prompts
1569
+ });
1570
+ try {
1571
+ await Promise.race([this.performConnection(mcpClient, config), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`Connection timeout after ${timeoutMs}ms`)), timeoutMs))]);
1572
+ mcpClient.setConnected(true);
1573
+ if (!mcpClient.serverInstruction) try {
1574
+ const serverInstruction = mcpClient["client"].getInstructions();
1575
+ if (serverInstruction) mcpClient.serverInstruction = serverInstruction;
1576
+ } catch (error) {
1577
+ console.error(`Failed to get server instruction from ${serverName}:`, error);
1578
+ }
1579
+ this.clients.set(serverName, mcpClient);
1580
+ return mcpClient;
1581
+ } catch (error) {
1582
+ await mcpClient.close();
1583
+ throw error;
1584
+ }
1585
+ }
1586
+ /**
1587
+ * Perform the actual connection to MCP server
1588
+ */
1589
+ async performConnection(mcpClient, config) {
1590
+ if (config.transport === "stdio") await this.connectStdioClient(mcpClient, config.config);
1591
+ else if (config.transport === "http") await this.connectHttpClient(mcpClient, config.config);
1592
+ else if (config.transport === "sse") await this.connectSseClient(mcpClient, config.config);
1593
+ else throw new Error(`Unsupported transport type: ${config.transport}`);
1594
+ }
1595
+ async connectStdioClient(mcpClient, config) {
1596
+ const transport = new StdioClientTransport({
1597
+ command: config.command,
1598
+ args: config.args,
1599
+ env: {
1600
+ ...process.env,
1601
+ ...config.env ?? {}
1602
+ }
1603
+ });
1604
+ await mcpClient["client"].connect(transport);
1605
+ const childProcess = transport["_process"];
1606
+ if (childProcess) mcpClient.setChildProcess(childProcess);
1607
+ }
1608
+ async connectHttpClient(mcpClient, config) {
1609
+ const transport = new StreamableHTTPClientTransport(new URL(config.url), { requestInit: config.headers ? { headers: config.headers } : void 0 });
1610
+ await mcpClient["client"].connect(transport);
1611
+ }
1612
+ async connectSseClient(mcpClient, config) {
1613
+ const transport = new SSEClientTransport(new URL(config.url));
1614
+ await mcpClient["client"].connect(transport);
1615
+ }
1616
+ /**
1617
+ * Get a connected client by server name
1618
+ */
1619
+ getClient(serverName) {
1620
+ return this.clients.get(serverName);
1621
+ }
1622
+ /**
1623
+ * Get all connected clients
1624
+ */
1625
+ getAllClients() {
1626
+ return Array.from(this.clients.values());
1627
+ }
1628
+ /**
1629
+ * Disconnect from a specific server
1630
+ */
1631
+ async disconnectServer(serverName) {
1632
+ const client = this.clients.get(serverName);
1633
+ if (client) {
1634
+ await client.close();
1635
+ this.clients.delete(serverName);
1636
+ }
1637
+ }
1638
+ /**
1639
+ * Disconnect from all servers
1640
+ */
1641
+ async disconnectAll() {
1642
+ const disconnectPromises = Array.from(this.clients.values()).map((client) => client.close());
1643
+ await Promise.all(disconnectPromises);
1644
+ this.clients.clear();
1645
+ this.connectionPromises.clear();
1646
+ }
1647
+ /**
1648
+ * Check if a server is connected
1649
+ */
1650
+ isConnected(serverName) {
1651
+ return this.clients.has(serverName);
1652
+ }
1653
+ };
1292
1654
 
1293
1655
  //#endregion
1294
1656
  //#region src/services/SkillService.ts
@@ -1362,6 +1724,8 @@ var SkillService = class {
1362
1724
  skillsByName = null;
1363
1725
  /** Active file watchers for skill directories */
1364
1726
  watchers = [];
1727
+ /** Polling timers used when native file watching is unavailable */
1728
+ pollingTimers = [];
1365
1729
  /** Callback invoked when cache is invalidated due to file changes */
1366
1730
  onCacheInvalidated;
1367
1731
  /**
@@ -1446,7 +1810,13 @@ var SkillService = class {
1446
1810
  const abortController = new AbortController();
1447
1811
  this.watchers.push(abortController);
1448
1812
  this.watchDirectory(skillsDir, abortController.signal).catch((error) => {
1449
- if (error?.name !== "AbortError") console.error(`[skill-watcher] Error watching ${skillsDir}: ${error instanceof Error ? error.message : "Unknown error"}`);
1813
+ if (error?.name !== "AbortError") {
1814
+ if (this.isWatchResourceLimitError(error)) {
1815
+ this.startPollingDirectory(skillsDir, abortController.signal);
1816
+ return;
1817
+ }
1818
+ console.error(`[skill-watcher] Error watching ${skillsDir}: ${error instanceof Error ? error.message : "Unknown error"}`);
1819
+ }
1450
1820
  });
1451
1821
  }
1452
1822
  }
@@ -1457,6 +1827,8 @@ var SkillService = class {
1457
1827
  stopWatching() {
1458
1828
  for (const controller of this.watchers) controller.abort();
1459
1829
  this.watchers = [];
1830
+ for (const timer of this.pollingTimers) clearInterval(timer);
1831
+ this.pollingTimers = [];
1460
1832
  }
1461
1833
  /**
1462
1834
  * Watches a directory for changes to SKILL.md files.
@@ -1468,10 +1840,66 @@ var SkillService = class {
1468
1840
  recursive: true,
1469
1841
  signal
1470
1842
  });
1471
- for await (const event of watcher) if (event.filename?.endsWith("SKILL.md")) {
1472
- this.clearCache();
1473
- this.onCacheInvalidated?.();
1843
+ for await (const event of watcher) if (event.filename?.endsWith("SKILL.md")) this.invalidateCache();
1844
+ }
1845
+ invalidateCache() {
1846
+ this.clearCache();
1847
+ this.onCacheInvalidated?.();
1848
+ }
1849
+ isWatchResourceLimitError(error) {
1850
+ return error instanceof Error && "code" in error && (error.code === "EMFILE" || error.code === "ENOSPC");
1851
+ }
1852
+ startPollingDirectory(dirPath, signal) {
1853
+ this.createSkillSnapshot(dirPath).then((initialSnapshot) => {
1854
+ let previousSnapshot = initialSnapshot;
1855
+ const timer = setInterval(() => {
1856
+ if (signal.aborted) {
1857
+ clearInterval(timer);
1858
+ return;
1859
+ }
1860
+ this.createSkillSnapshot(dirPath).then((nextSnapshot) => {
1861
+ if (!this.snapshotsEqual(previousSnapshot, nextSnapshot)) {
1862
+ previousSnapshot = nextSnapshot;
1863
+ this.invalidateCache();
1864
+ }
1865
+ }).catch((error) => {
1866
+ console.error(`[skill-watcher] Polling failed for ${dirPath}: ${error instanceof Error ? error.message : "Unknown error"}`);
1867
+ });
1868
+ }, 100);
1869
+ this.pollingTimers.push(timer);
1870
+ signal.addEventListener("abort", () => {
1871
+ clearInterval(timer);
1872
+ this.pollingTimers = this.pollingTimers.filter((activeTimer) => activeTimer !== timer);
1873
+ }, { once: true });
1874
+ });
1875
+ }
1876
+ async createSkillSnapshot(dirPath) {
1877
+ const snapshot = /* @__PURE__ */ new Map();
1878
+ await this.collectSkillSnapshots(dirPath, snapshot);
1879
+ return snapshot;
1880
+ }
1881
+ async collectSkillSnapshots(dirPath, snapshot) {
1882
+ let entries;
1883
+ try {
1884
+ entries = await readdir(dirPath);
1885
+ } catch (error) {
1886
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return;
1887
+ throw error;
1474
1888
  }
1889
+ await Promise.all(entries.map(async (entry) => {
1890
+ const entryPath = join(dirPath, entry);
1891
+ const entryStat = await stat(entryPath);
1892
+ if (entryStat.isDirectory()) {
1893
+ await this.collectSkillSnapshots(entryPath, snapshot);
1894
+ return;
1895
+ }
1896
+ if (entry === "SKILL.md") snapshot.set(entryPath, entryStat.mtimeMs);
1897
+ }));
1898
+ }
1899
+ snapshotsEqual(left, right) {
1900
+ if (left.size !== right.size) return false;
1901
+ for (const [filePath, mtimeMs] of left) if (right.get(filePath) !== mtimeMs) return false;
1902
+ return true;
1475
1903
  }
1476
1904
  /**
1477
1905
  * Load skills from a directory.
@@ -1579,32 +2007,6 @@ var SkillService = class {
1579
2007
  }
1580
2008
  };
1581
2009
 
1582
- //#endregion
1583
- //#region src/constants/index.ts
1584
- /**
1585
- * Shared constants for one-mcp package
1586
- */
1587
- /**
1588
- * Prefix added to skill names when they clash with MCP tool names.
1589
- * This ensures skills can be uniquely identified even when a tool has the same name.
1590
- */
1591
- const SKILL_PREFIX = "skill__";
1592
- /**
1593
- * Log prefix for skill detection messages.
1594
- * Used to easily filter skill detection logs in stderr output.
1595
- */
1596
- const LOG_PREFIX_SKILL_DETECTION = "[skill-detection]";
1597
- /**
1598
- * Prefix for prompt-based skill locations.
1599
- * Format: "prompt:{serverName}:{promptName}"
1600
- */
1601
- const PROMPT_LOCATION_PREFIX = "prompt:";
1602
- /**
1603
- * Default server ID used when no ID is provided via CLI or config.
1604
- * This fallback is used when auto-generation also fails.
1605
- */
1606
- const DEFAULT_SERVER_ID = "unknown";
1607
-
1608
2010
  //#endregion
1609
2011
  //#region src/templates/toolkit-description.liquid?raw
1610
2012
  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";
@@ -1643,6 +2045,7 @@ var DescribeToolsTool = class DescribeToolsTool {
1643
2045
  static TOOL_NAME = "describe_tools";
1644
2046
  clientManager;
1645
2047
  skillService;
2048
+ definitionsCacheService;
1646
2049
  liquid = new Liquid();
1647
2050
  /** Cache for auto-detected skills from prompt front-matter */
1648
2051
  autoDetectedSkillsCache = null;
@@ -1654,10 +2057,11 @@ var DescribeToolsTool = class DescribeToolsTool {
1654
2057
  * @param skillService - Optional skill service for loading skills
1655
2058
  * @param serverId - Unique server identifier for this one-mcp instance
1656
2059
  */
1657
- constructor(clientManager, skillService, serverId) {
2060
+ constructor(clientManager, skillService, serverId, definitionsCacheService) {
1658
2061
  this.clientManager = clientManager;
1659
2062
  this.skillService = skillService;
1660
2063
  this.serverId = serverId || DEFAULT_SERVER_ID;
2064
+ this.definitionsCacheService = definitionsCacheService || new DefinitionsCacheService(clientManager, skillService);
1661
2065
  }
1662
2066
  /**
1663
2067
  * Clears the cached auto-detected skills from prompt front-matter.
@@ -1666,6 +2070,7 @@ var DescribeToolsTool = class DescribeToolsTool {
1666
2070
  */
1667
2071
  clearAutoDetectedSkillsCache() {
1668
2072
  this.autoDetectedSkillsCache = null;
2073
+ this.definitionsCacheService.clearLiveCache();
1669
2074
  }
1670
2075
  /**
1671
2076
  * Detects and caches skills from prompt front-matter across all connected MCP servers.
@@ -1731,21 +2136,12 @@ var DescribeToolsTool = class DescribeToolsTool {
1731
2136
  * @returns Array of skill template data derived from prompts
1732
2137
  */
1733
2138
  async collectPromptSkills() {
1734
- const clients = this.clientManager.getAllClients();
1735
2139
  const promptSkills = [];
1736
- for (const client of clients) {
1737
- if (!client.prompts) continue;
1738
- for (const promptConfig of Object.values(client.prompts)) if (promptConfig.skill) promptSkills.push({
1739
- name: promptConfig.skill.name,
1740
- displayName: promptConfig.skill.name,
1741
- description: promptConfig.skill.description
1742
- });
1743
- }
1744
- const autoDetectedSkills = await this.detectSkillsFromPromptFrontMatter();
1745
- for (const autoSkill of autoDetectedSkills) promptSkills.push({
1746
- name: autoSkill.skill.name,
1747
- displayName: autoSkill.skill.name,
1748
- description: autoSkill.skill.description
2140
+ const serverDefinitions = await this.definitionsCacheService.getServerDefinitions();
2141
+ for (const serverDefinition of serverDefinitions) for (const promptSkill of serverDefinition.promptSkills) promptSkills.push({
2142
+ name: promptSkill.skill.name,
2143
+ displayName: promptSkill.skill.name,
2144
+ description: promptSkill.skill.description
1749
2145
  });
1750
2146
  return promptSkills;
1751
2147
  }
@@ -1758,22 +2154,7 @@ var DescribeToolsTool = class DescribeToolsTool {
1758
2154
  */
1759
2155
  async findPromptSkill(skillName) {
1760
2156
  if (!skillName) return void 0;
1761
- const clients = this.clientManager.getAllClients();
1762
- for (const client of clients) {
1763
- if (!client.prompts) continue;
1764
- for (const [promptName, promptConfig] of Object.entries(client.prompts)) if (promptConfig.skill && promptConfig.skill.name === skillName) return {
1765
- serverName: client.serverName,
1766
- promptName,
1767
- skill: promptConfig.skill
1768
- };
1769
- }
1770
- const autoDetectedSkills = await this.detectSkillsFromPromptFrontMatter();
1771
- for (const autoSkill of autoDetectedSkills) if (autoSkill.skill.name === skillName) return {
1772
- serverName: autoSkill.serverName,
1773
- promptName: autoSkill.promptName,
1774
- skill: autoSkill.skill,
1775
- autoDetected: true
1776
- };
2157
+ return await this.definitionsCacheService.getPromptSkillByName(skillName);
1777
2158
  }
1778
2159
  /**
1779
2160
  * Retrieves skill content from a prompt-based skill configuration.
@@ -1786,13 +2167,8 @@ var DescribeToolsTool = class DescribeToolsTool {
1786
2167
  async getPromptSkillContent(skillName) {
1787
2168
  const promptSkill = await this.findPromptSkill(skillName);
1788
2169
  if (!promptSkill) return void 0;
1789
- const client = this.clientManager.getClient(promptSkill.serverName);
1790
- if (!client) {
1791
- console.error(`Client not found for server '${promptSkill.serverName}' when fetching prompt skill '${skillName}'`);
1792
- return;
1793
- }
1794
2170
  try {
1795
- const rawInstructions = (await client.getPrompt(promptSkill.promptName)).messages?.map((m) => {
2171
+ const rawInstructions = (await (await this.clientManager.ensureConnected(promptSkill.serverName)).getPrompt(promptSkill.promptName)).messages?.map((m) => {
1796
2172
  const content = m.content;
1797
2173
  if (typeof content === "string") return content;
1798
2174
  if (content && typeof content === "object" && "text" in content) return String(content.text);
@@ -1821,24 +2197,12 @@ var DescribeToolsTool = class DescribeToolsTool {
1821
2197
  * @returns Object with rendered description and set of all tool names
1822
2198
  */
1823
2199
  async buildToolkitDescription() {
1824
- const clients = this.clientManager.getAllClients();
2200
+ const serverDefinitions = await this.definitionsCacheService.getServerDefinitions();
1825
2201
  const toolToServers = /* @__PURE__ */ new Map();
1826
- const serverToolsMap = /* @__PURE__ */ new Map();
1827
- await Promise.all(clients.map(async (client) => {
1828
- try {
1829
- const tools = await client.listTools();
1830
- const blacklist = new Set(client.toolBlacklist || []);
1831
- const filteredTools = tools.filter((t) => !blacklist.has(t.name));
1832
- serverToolsMap.set(client.serverName, filteredTools);
1833
- for (const tool of filteredTools) {
1834
- if (!toolToServers.has(tool.name)) toolToServers.set(tool.name, []);
1835
- toolToServers.get(tool.name)?.push(client.serverName);
1836
- }
1837
- } catch (error) {
1838
- console.error(`Failed to list tools from ${client.serverName}:`, error);
1839
- serverToolsMap.set(client.serverName, []);
1840
- }
1841
- }));
2202
+ for (const serverDefinition of serverDefinitions) for (const tool of serverDefinition.tools) {
2203
+ if (!toolToServers.has(tool.name)) toolToServers.set(tool.name, []);
2204
+ toolToServers.get(tool.name)?.push(serverDefinition.serverName);
2205
+ }
1842
2206
  /**
1843
2207
  * Formats tool name with server prefix if the tool exists on multiple servers
1844
2208
  */
@@ -1846,21 +2210,25 @@ var DescribeToolsTool = class DescribeToolsTool {
1846
2210
  return (toolToServers.get(toolName) || []).length > 1 ? `${serverName}__${toolName}` : toolName;
1847
2211
  };
1848
2212
  const allToolNames = /* @__PURE__ */ new Set();
1849
- const servers = clients.map((client) => {
1850
- const formattedTools = (serverToolsMap.get(client.serverName) || []).map((t) => ({
1851
- displayName: formatToolName(t.name, client.serverName),
2213
+ const servers = serverDefinitions.map((serverDefinition) => {
2214
+ const formattedTools = serverDefinition.tools.map((t) => ({
2215
+ displayName: formatToolName(t.name, serverDefinition.serverName),
1852
2216
  description: t.description
1853
2217
  }));
1854
2218
  for (const tool of formattedTools) allToolNames.add(tool.displayName);
1855
2219
  return {
1856
- name: client.serverName,
1857
- instruction: client.serverInstruction,
1858
- omitToolDescription: client.omitToolDescription || false,
2220
+ name: serverDefinition.serverName,
2221
+ instruction: serverDefinition.serverInstruction,
2222
+ omitToolDescription: serverDefinition.omitToolDescription || false,
1859
2223
  tools: formattedTools,
1860
2224
  toolNames: formattedTools.map((t) => t.displayName)
1861
2225
  };
1862
2226
  });
1863
- const [rawSkills, promptSkills] = await Promise.all([this.skillService ? this.skillService.getSkills() : Promise.resolve([]), this.collectPromptSkills()]);
2227
+ const [rawSkills, cachedSkills, promptSkills] = await Promise.all([
2228
+ this.skillService ? this.skillService.getSkills() : Promise.resolve([]),
2229
+ this.definitionsCacheService.getCachedFileSkills(),
2230
+ this.collectPromptSkills()
2231
+ ]);
1864
2232
  const seenSkillNames = /* @__PURE__ */ new Set();
1865
2233
  const allSkillsData = [];
1866
2234
  for (const skill of rawSkills) if (!seenSkillNames.has(skill.name)) {
@@ -1871,6 +2239,14 @@ var DescribeToolsTool = class DescribeToolsTool {
1871
2239
  description: skill.description
1872
2240
  });
1873
2241
  }
2242
+ for (const skill of cachedSkills) if (!seenSkillNames.has(skill.name)) {
2243
+ seenSkillNames.add(skill.name);
2244
+ allSkillsData.push({
2245
+ name: skill.name,
2246
+ displayName: skill.name,
2247
+ description: skill.description
2248
+ });
2249
+ }
1874
2250
  for (const skill of promptSkills) if (!seenSkillNames.has(skill.name)) {
1875
2251
  seenSkillNames.add(skill.name);
1876
2252
  allSkillsData.push(skill);
@@ -1940,7 +2316,7 @@ var DescribeToolsTool = class DescribeToolsTool {
1940
2316
  async execute(input) {
1941
2317
  try {
1942
2318
  const { toolNames } = input;
1943
- const clients = this.clientManager.getAllClients();
2319
+ const serverDefinitions = await this.definitionsCacheService.getServerDefinitions();
1944
2320
  if (!toolNames || toolNames.length === 0) return {
1945
2321
  content: [{
1946
2322
  type: "text",
@@ -1950,25 +2326,18 @@ var DescribeToolsTool = class DescribeToolsTool {
1950
2326
  };
1951
2327
  const serverToolsMap = /* @__PURE__ */ new Map();
1952
2328
  const toolToServers = /* @__PURE__ */ new Map();
1953
- await Promise.all(clients.map(async (client) => {
1954
- try {
1955
- const tools = await client.listTools();
1956
- const blacklist = new Set(client.toolBlacklist || []);
1957
- const typedTools = tools.filter((t) => !blacklist.has(t.name)).map((t) => ({
1958
- name: t.name,
1959
- description: t.description,
1960
- inputSchema: t.inputSchema
1961
- }));
1962
- serverToolsMap.set(client.serverName, typedTools);
1963
- for (const tool of typedTools) {
1964
- if (!toolToServers.has(tool.name)) toolToServers.set(tool.name, []);
1965
- toolToServers.get(tool.name)?.push(client.serverName);
1966
- }
1967
- } catch (error) {
1968
- console.error(`Failed to list tools from ${client.serverName}:`, error);
1969
- serverToolsMap.set(client.serverName, []);
2329
+ for (const serverDefinition of serverDefinitions) {
2330
+ const typedTools = serverDefinition.tools.map((tool) => ({
2331
+ name: tool.name,
2332
+ description: tool.description,
2333
+ inputSchema: tool.inputSchema
2334
+ }));
2335
+ serverToolsMap.set(serverDefinition.serverName, typedTools);
2336
+ for (const tool of typedTools) {
2337
+ if (!toolToServers.has(tool.name)) toolToServers.set(tool.name, []);
2338
+ toolToServers.get(tool.name)?.push(serverDefinition.serverName);
1970
2339
  }
1971
- }));
2340
+ }
1972
2341
  const lookupResults = await Promise.all(toolNames.map(async (requestedName) => {
1973
2342
  const result$1 = {
1974
2343
  tools: [],
@@ -2106,6 +2475,95 @@ var DescribeToolsTool = class DescribeToolsTool {
2106
2475
  }
2107
2476
  };
2108
2477
 
2478
+ //#endregion
2479
+ //#region src/utils/toolCapabilities.ts
2480
+ const TOOL_CAPABILITIES_META_KEY = "agiflowai/capabilities";
2481
+ function getToolCapabilities(tool) {
2482
+ const rawCapabilities = tool._meta?.[TOOL_CAPABILITIES_META_KEY];
2483
+ if (!Array.isArray(rawCapabilities)) return [];
2484
+ return rawCapabilities.filter((value) => typeof value === "string");
2485
+ }
2486
+ function getUniqueSortedCapabilities(tools) {
2487
+ return Array.from(new Set(tools.flatMap((tool) => getToolCapabilities(tool)))).sort();
2488
+ }
2489
+
2490
+ //#endregion
2491
+ //#region src/tools/SearchListToolsTool.ts
2492
+ var SearchListToolsTool = class SearchListToolsTool {
2493
+ static TOOL_NAME = "list_tools";
2494
+ constructor(_clientManager, definitionsCacheService) {
2495
+ this._clientManager = _clientManager;
2496
+ this.definitionsCacheService = definitionsCacheService;
2497
+ }
2498
+ formatToolName(toolName, serverName, toolToServers) {
2499
+ return (toolToServers.get(toolName) || []).length > 1 ? `${serverName}__${toolName}` : toolName;
2500
+ }
2501
+ async getDefinition() {
2502
+ const serverDefinitions = await this.definitionsCacheService.getServerDefinitions();
2503
+ const capabilitySummary = serverDefinitions.length > 0 ? serverDefinitions.map((server) => {
2504
+ const capabilities = getUniqueSortedCapabilities(server.tools);
2505
+ const summary = capabilities.length > 0 ? capabilities.join(", ") : server.serverInstruction || "No capability summary available";
2506
+ return `${server.serverName}: ${summary}`;
2507
+ }).join("\n") : "No proxied servers available.";
2508
+ return {
2509
+ name: SearchListToolsTool.TOOL_NAME,
2510
+ description: `Search proxied MCP tools by server capability summary.\n\nAvailable capabilities:\n${capabilitySummary}`,
2511
+ inputSchema: {
2512
+ type: "object",
2513
+ properties: {
2514
+ capability: {
2515
+ type: "string",
2516
+ description: "Optional capability filter. Matches explicit capability tags first, then server summaries, server names, tool names, and tool descriptions."
2517
+ },
2518
+ serverName: {
2519
+ type: "string",
2520
+ description: "Optional server name filter."
2521
+ }
2522
+ },
2523
+ additionalProperties: false
2524
+ }
2525
+ };
2526
+ }
2527
+ async execute(input) {
2528
+ const serverDefinitions = await this.definitionsCacheService.getServerDefinitions();
2529
+ const capabilityFilter = input.capability?.trim().toLowerCase();
2530
+ const serverNameFilter = input.serverName?.trim().toLowerCase();
2531
+ const toolToServers = /* @__PURE__ */ new Map();
2532
+ for (const serverDefinition of serverDefinitions) for (const tool of serverDefinition.tools) {
2533
+ if (!toolToServers.has(tool.name)) toolToServers.set(tool.name, []);
2534
+ toolToServers.get(tool.name)?.push(serverDefinition.serverName);
2535
+ }
2536
+ const filteredServers = serverDefinitions.filter((serverDefinition) => {
2537
+ if (serverNameFilter && serverDefinition.serverName.toLowerCase() !== serverNameFilter) return false;
2538
+ if (!capabilityFilter) return true;
2539
+ if ((serverDefinition.serverInstruction?.toLowerCase() || "").includes(capabilityFilter)) return true;
2540
+ if (getUniqueSortedCapabilities(serverDefinition.tools).some((capability) => capability.toLowerCase().includes(capabilityFilter))) return true;
2541
+ return serverDefinition.tools.some((tool) => {
2542
+ const toolName = this.formatToolName(tool.name, serverDefinition.serverName, toolToServers);
2543
+ const toolCapabilities = getToolCapabilities(tool);
2544
+ return toolName.toLowerCase().includes(capabilityFilter) || (tool.description || "").toLowerCase().includes(capabilityFilter) || toolCapabilities.some((capability) => capability.toLowerCase().includes(capabilityFilter));
2545
+ });
2546
+ }).map((serverDefinition) => ({
2547
+ server: serverDefinition.serverName,
2548
+ capabilities: getUniqueSortedCapabilities(serverDefinition.tools),
2549
+ summary: serverDefinition.serverInstruction,
2550
+ tools: serverDefinition.tools.map((tool) => ({
2551
+ name: this.formatToolName(tool.name, serverDefinition.serverName, toolToServers),
2552
+ description: serverDefinition.omitToolDescription ? void 0 : tool.description,
2553
+ capabilities: getToolCapabilities(tool)
2554
+ }))
2555
+ })).filter((server) => server.tools.length > 0);
2556
+ const result = { servers: filteredServers };
2557
+ return {
2558
+ content: [{
2559
+ type: "text",
2560
+ text: JSON.stringify(result, null, 2)
2561
+ }],
2562
+ isError: filteredServers.length === 0 ? true : void 0
2563
+ };
2564
+ }
2565
+ };
2566
+
2109
2567
  //#endregion
2110
2568
  //#region src/tools/UseToolTool.ts
2111
2569
  /**
@@ -2125,6 +2583,7 @@ var UseToolTool = class UseToolTool {
2125
2583
  static TOOL_NAME = "use_tool";
2126
2584
  clientManager;
2127
2585
  skillService;
2586
+ definitionsCacheService;
2128
2587
  /** Unique server identifier for this one-mcp instance */
2129
2588
  serverId;
2130
2589
  /**
@@ -2133,9 +2592,10 @@ var UseToolTool = class UseToolTool {
2133
2592
  * @param skillService - Optional skill service for loading and executing skills
2134
2593
  * @param serverId - Unique server identifier for this one-mcp instance
2135
2594
  */
2136
- constructor(clientManager, skillService, serverId) {
2595
+ constructor(clientManager, skillService, serverId, definitionsCacheService) {
2137
2596
  this.clientManager = clientManager;
2138
2597
  this.skillService = skillService;
2598
+ this.definitionsCacheService = definitionsCacheService || new DefinitionsCacheService(clientManager, skillService);
2139
2599
  this.serverId = serverId || DEFAULT_SERVER_ID;
2140
2600
  }
2141
2601
  /**
@@ -2195,17 +2655,9 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
2195
2655
  * @param skillName - The skill name to search for
2196
2656
  * @returns PromptSkillMatch if found, undefined otherwise
2197
2657
  */
2198
- findPromptSkill(skillName) {
2658
+ async findPromptSkill(skillName) {
2199
2659
  if (!skillName) return void 0;
2200
- const clients = this.clientManager.getAllClients();
2201
- for (const client of clients) {
2202
- if (!client.prompts) continue;
2203
- for (const [promptName, promptConfig] of Object.entries(client.prompts)) if (promptConfig.skill && promptConfig.skill.name === skillName) return {
2204
- serverName: client.serverName,
2205
- promptName,
2206
- skill: promptConfig.skill
2207
- };
2208
- }
2660
+ return await this.definitionsCacheService.getPromptSkillByName(skillName);
2209
2661
  }
2210
2662
  /**
2211
2663
  * Returns guidance message for prompt-based skill invocation.
@@ -2245,7 +2697,7 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
2245
2697
  const skill = await this.skillService.getSkill(skillName);
2246
2698
  if (skill) return this.executeSkill(skill);
2247
2699
  }
2248
- const promptSkill = this.findPromptSkill(skillName);
2700
+ const promptSkill = await this.findPromptSkill(skillName);
2249
2701
  if (promptSkill) return this.executePromptSkill(promptSkill);
2250
2702
  return {
2251
2703
  content: [{
@@ -2255,53 +2707,34 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
2255
2707
  isError: true
2256
2708
  };
2257
2709
  }
2258
- const clients = this.clientManager.getAllClients();
2710
+ const knownServerNames = this.clientManager.getKnownServerNames();
2259
2711
  const { serverName, actualToolName } = parseToolName(inputToolName);
2260
- if (serverName) {
2261
- const client$1 = this.clientManager.getClient(serverName);
2262
- if (!client$1) return {
2712
+ if (serverName) try {
2713
+ const client = await this.clientManager.ensureConnected(serverName);
2714
+ if (client.toolBlacklist?.includes(actualToolName)) return {
2263
2715
  content: [{
2264
2716
  type: "text",
2265
- text: `Server "${serverName}" not found. Available servers: ${clients.map((c) => c.serverName).join(", ")}`
2717
+ text: `Tool "${actualToolName}" is blacklisted on server "${serverName}" and cannot be executed.`
2266
2718
  }],
2267
2719
  isError: true
2268
2720
  };
2269
- if (client$1.toolBlacklist?.includes(actualToolName)) return {
2721
+ return await client.callTool(actualToolName, toolArgs);
2722
+ } catch (error) {
2723
+ return {
2270
2724
  content: [{
2271
2725
  type: "text",
2272
- text: `Tool "${actualToolName}" is blacklisted on server "${serverName}" and cannot be executed.`
2726
+ text: `Failed to call tool "${actualToolName}" on server "${serverName}". Available servers: ${knownServerNames.join(", ")}. ${error instanceof Error ? error.message : "Unknown error"}`
2273
2727
  }],
2274
2728
  isError: true
2275
2729
  };
2276
- try {
2277
- return await client$1.callTool(actualToolName, toolArgs);
2278
- } catch (error) {
2279
- return {
2280
- content: [{
2281
- type: "text",
2282
- text: `Failed to call tool "${actualToolName}" on server "${serverName}": ${error instanceof Error ? error.message : "Unknown error"}`
2283
- }],
2284
- isError: true
2285
- };
2286
- }
2287
2730
  }
2288
- const matchingServers = [];
2289
- const results = await Promise.all(clients.map(async (client$1) => {
2290
- try {
2291
- if (client$1.toolBlacklist?.includes(actualToolName)) return null;
2292
- if ((await client$1.listTools()).some((t) => t.name === actualToolName)) return client$1.serverName;
2293
- } catch (error) {
2294
- console.error(`Failed to list tools from ${client$1.serverName}:`, error);
2295
- }
2296
- return null;
2297
- }));
2298
- matchingServers.push(...results.filter((r) => r !== null));
2731
+ const matchingServers = await this.definitionsCacheService.getServersForTool(actualToolName);
2299
2732
  if (matchingServers.length === 0) {
2300
2733
  if (this.skillService) {
2301
2734
  const skill = await this.skillService.getSkill(actualToolName);
2302
2735
  if (skill) return this.executeSkill(skill);
2303
2736
  }
2304
- const promptSkill = this.findPromptSkill(actualToolName);
2737
+ const promptSkill = await this.findPromptSkill(actualToolName);
2305
2738
  if (promptSkill) return this.executePromptSkill(promptSkill);
2306
2739
  return {
2307
2740
  content: [{
@@ -2318,17 +2751,9 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
2318
2751
  }],
2319
2752
  isError: true
2320
2753
  };
2321
- const targetServerName = matchingServers[0];
2322
- const client = this.clientManager.getClient(targetServerName);
2323
- if (!client) return {
2324
- content: [{
2325
- type: "text",
2326
- text: `Internal error: Server "${targetServerName}" was found but is not connected`
2327
- }],
2328
- isError: true
2329
- };
2330
2754
  try {
2331
- return await client.callTool(actualToolName, toolArgs);
2755
+ const targetServerName = matchingServers[0];
2756
+ return await (await this.clientManager.ensureConnected(targetServerName)).callTool(actualToolName, toolArgs);
2332
2757
  } catch (error) {
2333
2758
  return {
2334
2759
  content: [{
@@ -2350,6 +2775,10 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
2350
2775
  }
2351
2776
  };
2352
2777
 
2778
+ //#endregion
2779
+ //#region package.json
2780
+ var version = "0.3.11";
2781
+
2353
2782
  //#endregion
2354
2783
  //#region src/server/index.ts
2355
2784
  /**
@@ -2365,17 +2794,110 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
2365
2794
  * - Keep server setup modular and extensible
2366
2795
  * - Import tools from ../tools/ and register them in the handlers
2367
2796
  */
2797
+ function summarizeServerTools(serverDefinition) {
2798
+ const toolNames = serverDefinition.tools.map((tool) => tool.name);
2799
+ const capabilities = getUniqueSortedCapabilities(serverDefinition.tools);
2800
+ const capabilitySummary = capabilities.length > 0 ? `; capabilities: ${capabilities.join(", ")}` : "";
2801
+ if (toolNames.length === 0) return `${serverDefinition.serverName} (no tools cached${capabilitySummary})`;
2802
+ return `${serverDefinition.serverName} (${toolNames.join(", ")})${capabilitySummary}`;
2803
+ }
2804
+ function buildFlatToolDescription(serverDefinition, tool) {
2805
+ const parts = [`Proxied from server "${serverDefinition.serverName}" as tool "${tool.name}".`];
2806
+ if (serverDefinition.serverInstruction) parts.push(`Server summary: ${serverDefinition.serverInstruction}`);
2807
+ if (tool.description && !serverDefinition.omitToolDescription) parts.push(tool.description);
2808
+ const capabilities = getToolCapabilities(tool);
2809
+ if (capabilities.length > 0) parts.push(`Capabilities: ${capabilities.join(", ")}`);
2810
+ return parts.join("\n\n");
2811
+ }
2812
+ function buildFlatToolDefinitions(serverDefinitions) {
2813
+ const toolToServers = /* @__PURE__ */ new Map();
2814
+ for (const serverDefinition of serverDefinitions) for (const tool of serverDefinition.tools) {
2815
+ if (!toolToServers.has(tool.name)) toolToServers.set(tool.name, []);
2816
+ toolToServers.get(tool.name)?.push(serverDefinition.serverName);
2817
+ }
2818
+ const definitions = [];
2819
+ for (const serverDefinition of serverDefinitions) for (const tool of serverDefinition.tools) {
2820
+ const hasClash = (toolToServers.get(tool.name) || []).length > 1;
2821
+ definitions.push({
2822
+ name: hasClash ? `${serverDefinition.serverName}__${tool.name}` : tool.name,
2823
+ description: buildFlatToolDescription(serverDefinition, tool),
2824
+ inputSchema: tool.inputSchema,
2825
+ _meta: tool._meta
2826
+ });
2827
+ }
2828
+ return definitions;
2829
+ }
2830
+ async function hasAnySkills(definitionsCacheService, skillService) {
2831
+ const [fileSkills, serverDefinitions] = await Promise.all([skillService ? skillService.getSkills() : definitionsCacheService.getCachedFileSkills(), definitionsCacheService.getServerDefinitions()]);
2832
+ return fileSkills.length > 0 || serverDefinitions.some((server) => server.promptSkills.length > 0);
2833
+ }
2834
+ function buildSkillsDescribeDefinition(serverDefinitions, serverId) {
2835
+ const proxySummary = serverDefinitions.length > 0 ? serverDefinitions.map(summarizeServerTools).join("; ") : "No proxied servers available.";
2836
+ return {
2837
+ name: DescribeToolsTool.TOOL_NAME,
2838
+ description: `Get detailed skill instructions for file-based skills and prompt-based skills proxied by one-mcp.\n\nProxy summary: ${proxySummary}\n\nUse this when you need the full instructions for a skill. For MCP tools, call the flat tool names directly. Only use skills discovered from describe_tools with id="${serverId}".`,
2839
+ inputSchema: {
2840
+ type: "object",
2841
+ properties: { toolNames: {
2842
+ type: "array",
2843
+ items: {
2844
+ type: "string",
2845
+ minLength: 1
2846
+ },
2847
+ description: "List of skill names to get detailed information about",
2848
+ minItems: 1
2849
+ } },
2850
+ required: ["toolNames"],
2851
+ additionalProperties: false
2852
+ }
2853
+ };
2854
+ }
2855
+ function buildSearchDescribeDefinition(serverDefinitions, serverId) {
2856
+ const summary = serverDefinitions.length > 0 ? serverDefinitions.map(summarizeServerTools).join("; ") : "No proxied servers available.";
2857
+ return {
2858
+ name: DescribeToolsTool.TOOL_NAME,
2859
+ description: `Get detailed schemas and skill instructions for proxied MCP capabilities.\n\nProxy summary: ${summary}\n\nUse list_tools first to search capability summaries and discover tool names. Then use describe_tools to fetch full schemas or skill instructions. Only use capabilities discovered from one-mcp id="${serverId}".`,
2860
+ inputSchema: {
2861
+ type: "object",
2862
+ properties: { toolNames: {
2863
+ type: "array",
2864
+ items: {
2865
+ type: "string",
2866
+ minLength: 1
2867
+ },
2868
+ description: "List of tool or skill names to get detailed information about",
2869
+ minItems: 1
2870
+ } },
2871
+ required: ["toolNames"],
2872
+ additionalProperties: false
2873
+ }
2874
+ };
2875
+ }
2876
+ function buildProxyInstructions(serverDefinitions, mode, includeSkillsTool) {
2877
+ const summary = serverDefinitions.length > 0 ? serverDefinitions.map(summarizeServerTools).join("; ") : "No proxied servers available.";
2878
+ if (mode === "flat") return [
2879
+ "one-mcp proxies downstream MCP servers and exposes their tools and resources directly.",
2880
+ `Proxied servers and tools: ${summary}`,
2881
+ includeSkillsTool ? "Skills are still exposed through describe_tools when file-based skills or prompt-backed skills are configured." : "No skills are currently exposed through describe_tools."
2882
+ ].join("\n\n");
2883
+ if (mode === "search") return [
2884
+ "one-mcp proxies downstream MCP servers in search mode.",
2885
+ `Proxied servers and tools: ${summary}`,
2886
+ "Use list_tools to search capability summaries and discover tool names, describe_tools to fetch schemas or skill instructions, and use_tool to execute tools."
2887
+ ].join("\n\n");
2888
+ return [
2889
+ "one-mcp proxies downstream MCP servers in meta mode.",
2890
+ `Proxied servers and tools: ${summary}`,
2891
+ "Use describe_tools to inspect capabilities and use_tool to execute them."
2892
+ ].join("\n\n");
2893
+ }
2368
2894
  async function createServer(options) {
2369
- const server = new Server({
2370
- name: "@agiflowai/one-mcp",
2371
- version: "0.1.0"
2372
- }, { capabilities: {
2373
- tools: {},
2374
- prompts: {}
2375
- } });
2376
2895
  const clientManager = new McpClientManagerService();
2377
2896
  let configSkills;
2378
2897
  let configId;
2898
+ let configHash;
2899
+ let effectiveDefinitionsCachePath;
2900
+ let shouldStartFromCache = false;
2379
2901
  if (options?.configFilePath) {
2380
2902
  let config;
2381
2903
  try {
@@ -2388,38 +2910,92 @@ async function createServer(options) {
2388
2910
  }
2389
2911
  configSkills = config.skills;
2390
2912
  configId = config.id;
2391
- const failedConnections = [];
2392
- const connectionPromises = Object.entries(config.mcpServers).map(async ([serverName, serverConfig]) => {
2393
- try {
2394
- await clientManager.connectToServer(serverName, serverConfig);
2395
- console.error(`Connected to MCP server: ${serverName}`);
2396
- } catch (error) {
2397
- const err = error instanceof Error ? error : new Error(String(error));
2398
- failedConnections.push({
2399
- serverName,
2400
- error: err
2401
- });
2402
- console.error(`Failed to connect to ${serverName}:`, error);
2403
- }
2404
- });
2405
- await Promise.all(connectionPromises);
2406
- if (failedConnections.length > 0 && failedConnections.length < Object.keys(config.mcpServers).length) console.error(`Warning: Some MCP server connections failed: ${failedConnections.map((f) => f.serverName).join(", ")}`);
2407
- if (failedConnections.length > 0 && failedConnections.length === Object.keys(config.mcpServers).length) throw new Error(`All MCP server connections failed: ${failedConnections.map((f) => `${f.serverName}: ${f.error.message}`).join(", ")}`);
2913
+ configHash = DefinitionsCacheService.generateConfigHash(config);
2914
+ effectiveDefinitionsCachePath = options.definitionsCachePath || DefinitionsCacheService.getDefaultCachePath(options.configFilePath);
2915
+ clientManager.registerServerConfigs(config.mcpServers);
2916
+ if (options.clearDefinitionsCache && effectiveDefinitionsCachePath) {
2917
+ await DefinitionsCacheService.clearFile(effectiveDefinitionsCachePath);
2918
+ console.error(`[definitions-cache] Cleared ${effectiveDefinitionsCachePath}`);
2919
+ }
2920
+ if (effectiveDefinitionsCachePath) try {
2921
+ const cacheData = await DefinitionsCacheService.readFromFile(effectiveDefinitionsCachePath);
2922
+ if (DefinitionsCacheService.isCacheValid(cacheData, {
2923
+ configHash,
2924
+ oneMcpVersion: version
2925
+ })) shouldStartFromCache = true;
2926
+ } catch {}
2927
+ if (!shouldStartFromCache) {
2928
+ const failedConnections = [];
2929
+ const connectionPromises = Object.entries(config.mcpServers).map(async ([serverName, serverConfig]) => {
2930
+ try {
2931
+ await clientManager.connectToServer(serverName, serverConfig);
2932
+ console.error(`Connected to MCP server: ${serverName}`);
2933
+ } catch (error) {
2934
+ const err = error instanceof Error ? error : new Error(String(error));
2935
+ failedConnections.push({
2936
+ serverName,
2937
+ error: err
2938
+ });
2939
+ console.error(`Failed to connect to ${serverName}:`, error);
2940
+ }
2941
+ });
2942
+ await Promise.all(connectionPromises);
2943
+ if (failedConnections.length > 0 && failedConnections.length < Object.keys(config.mcpServers).length) console.error(`Warning: Some MCP server connections failed: ${failedConnections.map((f) => f.serverName).join(", ")}`);
2944
+ if (failedConnections.length > 0 && failedConnections.length === Object.keys(config.mcpServers).length) throw new Error(`All MCP server connections failed: ${failedConnections.map((f) => `${f.serverName}: ${f.error.message}`).join(", ")}`);
2945
+ } else console.error(`[definitions-cache] Using cached definitions from ${effectiveDefinitionsCachePath}`);
2408
2946
  }
2409
2947
  const serverId = options?.serverId || configId || generateServerId();
2410
2948
  console.error(`[one-mcp] Server ID: ${serverId}`);
2411
- const skillsConfig = options?.skills || configSkills;
2949
+ const skillPaths = (options?.skills || configSkills)?.paths ?? [];
2412
2950
  const toolsRef = { describeTools: null };
2413
- const skillService = skillsConfig && skillsConfig.paths.length > 0 ? new SkillService(process.cwd(), skillsConfig.paths, { onCacheInvalidated: () => {
2951
+ const skillService = skillPaths.length > 0 ? new SkillService(process.cwd(), skillPaths, { onCacheInvalidated: () => {
2414
2952
  toolsRef.describeTools?.clearAutoDetectedSkillsCache();
2415
2953
  } }) : void 0;
2416
- const describeTools = new DescribeToolsTool(clientManager, skillService, serverId);
2417
- const useTool = new UseToolTool(clientManager, skillService, serverId);
2954
+ let definitionsCacheService;
2955
+ if (effectiveDefinitionsCachePath) try {
2956
+ const cacheData = await DefinitionsCacheService.readFromFile(effectiveDefinitionsCachePath);
2957
+ if (DefinitionsCacheService.isCacheValid(cacheData, {
2958
+ configHash,
2959
+ oneMcpVersion: version
2960
+ })) definitionsCacheService = new DefinitionsCacheService(clientManager, skillService, { cacheData });
2961
+ else definitionsCacheService = new DefinitionsCacheService(clientManager, skillService);
2962
+ } catch (error) {
2963
+ console.error(`[definitions-cache] Failed to load ${effectiveDefinitionsCachePath}, falling back to live discovery: ${error instanceof Error ? error.message : "Unknown error"}`);
2964
+ definitionsCacheService = new DefinitionsCacheService(clientManager, skillService);
2965
+ }
2966
+ else definitionsCacheService = new DefinitionsCacheService(clientManager, skillService);
2967
+ const describeTools = new DescribeToolsTool(clientManager, skillService, serverId, definitionsCacheService);
2968
+ const useToolWithCache = new UseToolTool(clientManager, skillService, serverId, definitionsCacheService);
2969
+ const searchListTools = new SearchListToolsTool(clientManager, definitionsCacheService);
2418
2970
  toolsRef.describeTools = describeTools;
2971
+ const serverDefinitions = await definitionsCacheService.getServerDefinitions();
2972
+ const includeSkillsTool = await hasAnySkills(definitionsCacheService, skillService);
2973
+ const proxyMode = options?.proxyMode || "meta";
2974
+ const server = new Server({
2975
+ name: "@agiflowai/one-mcp",
2976
+ version: "0.1.0"
2977
+ }, {
2978
+ capabilities: {
2979
+ tools: {},
2980
+ resources: {},
2981
+ prompts: {}
2982
+ },
2983
+ instructions: buildProxyInstructions(serverDefinitions, proxyMode, includeSkillsTool)
2984
+ });
2419
2985
  if (skillService) skillService.startWatching().catch((error) => {
2420
2986
  console.error(`[skill-watcher] File watcher failed (non-critical): ${error instanceof Error ? error.message : "Unknown error"}`);
2421
2987
  });
2422
- server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [await describeTools.getDefinition(), useTool.getDefinition()] }));
2988
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: proxyMode === "flat" ? await (async () => {
2989
+ const currentServerDefinitions = await definitionsCacheService.getServerDefinitions();
2990
+ const shouldIncludeSkillsTool = await hasAnySkills(definitionsCacheService, skillService);
2991
+ return [...buildFlatToolDefinitions(currentServerDefinitions), ...shouldIncludeSkillsTool ? [buildSkillsDescribeDefinition(currentServerDefinitions, serverId)] : []];
2992
+ })() : proxyMode === "search" ? await (async () => {
2993
+ return [
2994
+ buildSearchDescribeDefinition(await definitionsCacheService.getServerDefinitions(), serverId),
2995
+ await searchListTools.getDefinition(),
2996
+ useToolWithCache.getDefinition()
2997
+ ];
2998
+ })() : [await describeTools.getDefinition(), useToolWithCache.getDefinition()] }));
2423
2999
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
2424
3000
  const { name, arguments: args } = request.params;
2425
3001
  if (name === DescribeToolsTool.TOOL_NAME) try {
@@ -2428,36 +3004,65 @@ async function createServer(options) {
2428
3004
  throw new Error(`Failed to execute ${name}: ${error instanceof Error ? error.message : String(error)}`);
2429
3005
  }
2430
3006
  if (name === UseToolTool.TOOL_NAME) try {
2431
- return await useTool.execute(args);
3007
+ return await useToolWithCache.execute(args);
3008
+ } catch (error) {
3009
+ throw new Error(`Failed to execute ${name}: ${error instanceof Error ? error.message : String(error)}`);
3010
+ }
3011
+ if (name === SearchListToolsTool.TOOL_NAME && proxyMode === "search") try {
3012
+ return await searchListTools.execute(args);
2432
3013
  } catch (error) {
2433
3014
  throw new Error(`Failed to execute ${name}: ${error instanceof Error ? error.message : String(error)}`);
2434
3015
  }
3016
+ if (proxyMode === "flat") return await useToolWithCache.execute({
3017
+ toolName: name,
3018
+ toolArgs: args || {}
3019
+ });
2435
3020
  throw new Error(`Unknown tool: ${name}`);
2436
3021
  });
3022
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
3023
+ const serverDefinitions$1 = await definitionsCacheService.getServerDefinitions();
3024
+ const resourceToServers = /* @__PURE__ */ new Map();
3025
+ for (const serverDefinition of serverDefinitions$1) for (const resource of serverDefinition.resources) {
3026
+ if (!resourceToServers.has(resource.uri)) resourceToServers.set(resource.uri, []);
3027
+ resourceToServers.get(resource.uri)?.push(serverDefinition.serverName);
3028
+ }
3029
+ const resources = [];
3030
+ for (const serverDefinition of serverDefinitions$1) for (const resource of serverDefinition.resources) {
3031
+ const hasClash = (resourceToServers.get(resource.uri) || []).length > 1;
3032
+ resources.push({
3033
+ ...resource,
3034
+ uri: hasClash ? `${serverDefinition.serverName}__${resource.uri}` : resource.uri
3035
+ });
3036
+ }
3037
+ return { resources };
3038
+ });
3039
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
3040
+ const { uri } = request.params;
3041
+ const { serverName, actualToolName: actualUri } = parseToolName(uri);
3042
+ if (serverName) return await (await clientManager.ensureConnected(serverName)).readResource(actualUri);
3043
+ const matchingServers = await definitionsCacheService.getServersForResource(actualUri);
3044
+ if (matchingServers.length === 0) throw new Error(`Resource not found: ${uri}`);
3045
+ if (matchingServers.length > 1) throw new Error(`Resource "${actualUri}" exists on multiple servers: ${matchingServers.join(", ")}. Use the prefixed format (e.g., "${matchingServers[0]}__${actualUri}") to specify which server to use.`);
3046
+ return await (await clientManager.ensureConnected(matchingServers[0])).readResource(actualUri);
3047
+ });
2437
3048
  server.setRequestHandler(ListPromptsRequestSchema, async () => {
2438
- const clients = clientManager.getAllClients();
3049
+ const serverDefinitions$1 = await definitionsCacheService.getServerDefinitions();
2439
3050
  const promptToServers = /* @__PURE__ */ new Map();
2440
3051
  const serverPromptsMap = /* @__PURE__ */ new Map();
2441
- await Promise.all(clients.map(async (client) => {
2442
- try {
2443
- const prompts = await client.listPrompts();
2444
- serverPromptsMap.set(client.serverName, prompts);
2445
- for (const prompt of prompts) {
2446
- if (!promptToServers.has(prompt.name)) promptToServers.set(prompt.name, []);
2447
- promptToServers.get(prompt.name).push(client.serverName);
2448
- }
2449
- } catch (error) {
2450
- console.error(`Failed to list prompts from ${client.serverName}:`, error);
2451
- serverPromptsMap.set(client.serverName, []);
3052
+ for (const serverDefinition of serverDefinitions$1) {
3053
+ serverPromptsMap.set(serverDefinition.serverName, serverDefinition.prompts);
3054
+ for (const prompt of serverDefinition.prompts) {
3055
+ if (!promptToServers.has(prompt.name)) promptToServers.set(prompt.name, []);
3056
+ promptToServers.get(prompt.name).push(serverDefinition.serverName);
2452
3057
  }
2453
- }));
3058
+ }
2454
3059
  const aggregatedPrompts = [];
2455
- for (const client of clients) {
2456
- const prompts = serverPromptsMap.get(client.serverName) || [];
3060
+ for (const serverDefinition of serverDefinitions$1) {
3061
+ const prompts = serverPromptsMap.get(serverDefinition.serverName) || [];
2457
3062
  for (const prompt of prompts) {
2458
3063
  const hasClash = (promptToServers.get(prompt.name) || []).length > 1;
2459
3064
  aggregatedPrompts.push({
2460
- name: hasClash ? `${client.serverName}__${prompt.name}` : prompt.name,
3065
+ name: hasClash ? `${serverDefinition.serverName}__${prompt.name}` : prompt.name,
2461
3066
  description: prompt.description,
2462
3067
  arguments: prompt.arguments
2463
3068
  });
@@ -2467,27 +3072,27 @@ async function createServer(options) {
2467
3072
  });
2468
3073
  server.setRequestHandler(GetPromptRequestSchema, async (request) => {
2469
3074
  const { name, arguments: args } = request.params;
2470
- const clients = clientManager.getAllClients();
3075
+ const serverDefinitions$1 = await definitionsCacheService.getServerDefinitions();
2471
3076
  const { serverName, actualToolName: actualPromptName } = parseToolName(name);
2472
- if (serverName) {
2473
- const client$1 = clientManager.getClient(serverName);
2474
- if (!client$1) throw new Error(`Server not found: ${serverName}`);
2475
- return await client$1.getPrompt(actualPromptName, args);
2476
- }
3077
+ if (serverName) return await (await clientManager.ensureConnected(serverName)).getPrompt(actualPromptName, args);
2477
3078
  const serversWithPrompt = [];
2478
- await Promise.all(clients.map(async (client$1) => {
2479
- try {
2480
- if ((await client$1.listPrompts()).some((p) => p.name === name)) serversWithPrompt.push(client$1.serverName);
2481
- } catch (error) {
2482
- console.error(`Failed to list prompts from ${client$1.serverName}:`, error);
2483
- }
2484
- }));
3079
+ for (const serverDefinition of serverDefinitions$1) if (serverDefinition.prompts.some((prompt) => prompt.name === name)) serversWithPrompt.push(serverDefinition.serverName);
2485
3080
  if (serversWithPrompt.length === 0) throw new Error(`Prompt not found: ${name}`);
2486
3081
  if (serversWithPrompt.length > 1) throw new Error(`Prompt "${name}" exists on multiple servers: ${serversWithPrompt.join(", ")}. Use the prefixed format (e.g., "${serversWithPrompt[0]}__${name}") to specify which server to use.`);
2487
3082
  const client = clientManager.getClient(serversWithPrompt[0]);
2488
- if (!client) throw new Error(`Server not found: ${serversWithPrompt[0]}`);
3083
+ if (!client) return await (await clientManager.ensureConnected(serversWithPrompt[0])).getPrompt(name, args);
2489
3084
  return await client.getPrompt(name, args);
2490
3085
  });
3086
+ if (!shouldStartFromCache && effectiveDefinitionsCachePath && options?.configFilePath) definitionsCacheService.collectForCache({
3087
+ configPath: options.configFilePath,
3088
+ configHash,
3089
+ oneMcpVersion: version,
3090
+ serverId
3091
+ }).then((definitionsCache) => DefinitionsCacheService.writeToFile(effectiveDefinitionsCachePath, definitionsCache)).then(() => {
3092
+ console.error(`[definitions-cache] Wrote ${effectiveDefinitionsCachePath}`);
3093
+ }).catch((error) => {
3094
+ console.error(`[definitions-cache] Failed to persist ${effectiveDefinitionsCachePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
3095
+ });
2491
3096
  return server;
2492
3097
  }
2493
3098
 
@@ -2837,4 +3442,4 @@ var HttpTransportHandler = class {
2837
3442
  };
2838
3443
 
2839
3444
  //#endregion
2840
- export { UseToolTool as a, findConfigFile as c, createServer as i, McpClientManagerService as l, SseTransportHandler as n, DescribeToolsTool as o, StdioTransportHandler as r, SkillService as s, HttpTransportHandler as t, ConfigFetcherService as u };
3445
+ export { version as a, DescribeToolsTool as c, DefinitionsCacheService as d, generateServerId as f, createServer as i, SkillService as l, ConfigFetcherService as m, SseTransportHandler as n, UseToolTool as o, findConfigFile as p, StdioTransportHandler as r, SearchListToolsTool as s, HttpTransportHandler as t, McpClientManagerService as u };