@agiflowai/one-mcp 0.3.11 → 0.3.13

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.
@@ -789,14 +789,22 @@ var ConfigFetcherService = class {
789
789
  */
790
790
  mergeConfigurations(localConfig, remoteConfig, mergeStrategy) {
791
791
  switch (mergeStrategy) {
792
- case "local-priority": return { mcpServers: {
793
- ...remoteConfig.mcpServers,
794
- ...localConfig.mcpServers
795
- } };
796
- case "remote-priority": return { mcpServers: {
797
- ...localConfig.mcpServers,
798
- ...remoteConfig.mcpServers
799
- } };
792
+ case "local-priority": return {
793
+ id: localConfig.id ?? remoteConfig.id,
794
+ mcpServers: {
795
+ ...remoteConfig.mcpServers,
796
+ ...localConfig.mcpServers
797
+ },
798
+ skills: localConfig.skills ?? remoteConfig.skills
799
+ };
800
+ case "remote-priority": return {
801
+ id: remoteConfig.id ?? localConfig.id,
802
+ mcpServers: {
803
+ ...localConfig.mcpServers,
804
+ ...remoteConfig.mcpServers
805
+ },
806
+ skills: remoteConfig.skills ?? localConfig.skills
807
+ };
800
808
  case "merge-deep": {
801
809
  const merged = { ...remoteConfig.mcpServers };
802
810
  for (const [serverName, localServerConfig] of Object.entries(localConfig.mcpServers)) if (merged[serverName]) {
@@ -823,7 +831,11 @@ var ConfigFetcherService = class {
823
831
  config: mergedConfig
824
832
  };
825
833
  } else merged[serverName] = localServerConfig;
826
- return { mcpServers: merged };
834
+ return {
835
+ id: localConfig.id ?? remoteConfig.id,
836
+ mcpServers: merged,
837
+ skills: localConfig.skills ?? remoteConfig.skills
838
+ };
827
839
  }
828
840
  default: throw new Error(`Unknown merge strategy: ${mergeStrategy}`);
829
841
  }
@@ -844,213 +856,6 @@ var ConfigFetcherService = class {
844
856
  }
845
857
  };
846
858
 
847
- //#endregion
848
- //#region src/services/McpClientManagerService.ts
849
- /** Default connection timeout in milliseconds (30 seconds) */
850
- const DEFAULT_CONNECTION_TIMEOUT_MS = 3e4;
851
- /**
852
- * MCP Client wrapper for managing individual server connections
853
- * This is an internal class used by McpClientManagerService
854
- */
855
- var McpClient = class {
856
- serverName;
857
- serverInstruction;
858
- toolBlacklist;
859
- omitToolDescription;
860
- prompts;
861
- transport;
862
- client;
863
- childProcess;
864
- connected = false;
865
- constructor(serverName, transport, client, config) {
866
- this.serverName = serverName;
867
- this.serverInstruction = config.instruction;
868
- this.toolBlacklist = config.toolBlacklist;
869
- this.omitToolDescription = config.omitToolDescription;
870
- this.prompts = config.prompts;
871
- this.transport = transport;
872
- this.client = client;
873
- }
874
- setChildProcess(process$1) {
875
- this.childProcess = process$1;
876
- }
877
- setConnected(connected) {
878
- this.connected = connected;
879
- }
880
- async listTools() {
881
- if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
882
- return (await this.client.listTools()).tools;
883
- }
884
- async listResources() {
885
- if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
886
- return (await this.client.listResources()).resources;
887
- }
888
- async listPrompts() {
889
- if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
890
- return (await this.client.listPrompts()).prompts;
891
- }
892
- async callTool(name, args) {
893
- if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
894
- return await this.client.callTool({
895
- name,
896
- arguments: args
897
- });
898
- }
899
- async readResource(uri) {
900
- if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
901
- return await this.client.readResource({ uri });
902
- }
903
- async getPrompt(name, args) {
904
- if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
905
- return await this.client.getPrompt({
906
- name,
907
- arguments: args
908
- });
909
- }
910
- async close() {
911
- if (this.childProcess) this.childProcess.kill();
912
- await this.client.close();
913
- this.connected = false;
914
- }
915
- };
916
- /**
917
- * Service for managing MCP client connections to remote servers
918
- */
919
- var McpClientManagerService = class {
920
- clients = /* @__PURE__ */ new Map();
921
- constructor() {
922
- process.on("exit", () => {
923
- this.cleanupOnExit();
924
- });
925
- process.on("SIGINT", () => {
926
- this.cleanupOnExit();
927
- process.exit(0);
928
- });
929
- process.on("SIGTERM", () => {
930
- this.cleanupOnExit();
931
- process.exit(0);
932
- });
933
- }
934
- /**
935
- * Cleanup all resources on exit (child processes)
936
- */
937
- cleanupOnExit() {
938
- for (const [serverName, client] of this.clients) try {
939
- const childProcess = client["childProcess"];
940
- if (childProcess && !childProcess.killed) {
941
- console.error(`Killing stdio MCP server: ${serverName} (PID: ${childProcess.pid})`);
942
- childProcess.kill("SIGTERM");
943
- setTimeout(() => {
944
- if (!childProcess.killed) {
945
- console.error(`Force killing stdio MCP server: ${serverName} (PID: ${childProcess.pid})`);
946
- childProcess.kill("SIGKILL");
947
- }
948
- }, 1e3);
949
- }
950
- } catch (error) {
951
- console.error(`Failed to kill child process for ${serverName}:`, error);
952
- }
953
- }
954
- /**
955
- * Connect to an MCP server based on its configuration with timeout
956
- * Uses the timeout from server config, falling back to default (30s)
957
- */
958
- async connectToServer(serverName, config) {
959
- const timeoutMs = config.timeout ?? DEFAULT_CONNECTION_TIMEOUT_MS;
960
- if (this.clients.has(serverName)) throw new Error(`Client for ${serverName} is already connected`);
961
- const client = new __modelcontextprotocol_sdk_client_index_js.Client({
962
- name: `@agiflowai/one-mcp-client`,
963
- version: "0.1.0"
964
- }, { capabilities: {} });
965
- const mcpClient = new McpClient(serverName, config.transport, client, {
966
- instruction: config.instruction,
967
- toolBlacklist: config.toolBlacklist,
968
- omitToolDescription: config.omitToolDescription,
969
- prompts: config.prompts
970
- });
971
- try {
972
- await Promise.race([this.performConnection(mcpClient, config), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`Connection timeout after ${timeoutMs}ms`)), timeoutMs))]);
973
- mcpClient.setConnected(true);
974
- if (!mcpClient.serverInstruction) try {
975
- const serverInstruction = mcpClient["client"].getInstructions();
976
- if (serverInstruction) mcpClient.serverInstruction = serverInstruction;
977
- } catch (error) {
978
- console.error(`Failed to get server instruction from ${serverName}:`, error);
979
- }
980
- this.clients.set(serverName, mcpClient);
981
- } catch (error) {
982
- await mcpClient.close();
983
- throw error;
984
- }
985
- }
986
- /**
987
- * Perform the actual connection to MCP server
988
- */
989
- async performConnection(mcpClient, config) {
990
- if (config.transport === "stdio") await this.connectStdioClient(mcpClient, config.config);
991
- else if (config.transport === "http") await this.connectHttpClient(mcpClient, config.config);
992
- else if (config.transport === "sse") await this.connectSseClient(mcpClient, config.config);
993
- else throw new Error(`Unsupported transport type: ${config.transport}`);
994
- }
995
- async connectStdioClient(mcpClient, config) {
996
- const transport = new __modelcontextprotocol_sdk_client_stdio_js.StdioClientTransport({
997
- command: config.command,
998
- args: config.args,
999
- env: {
1000
- ...process.env,
1001
- ...config.env ?? {}
1002
- }
1003
- });
1004
- await mcpClient["client"].connect(transport);
1005
- const childProcess = transport["_process"];
1006
- if (childProcess) mcpClient.setChildProcess(childProcess);
1007
- }
1008
- async connectHttpClient(mcpClient, config) {
1009
- const transport = new __modelcontextprotocol_sdk_client_streamableHttp_js.StreamableHTTPClientTransport(new URL(config.url), { requestInit: config.headers ? { headers: config.headers } : void 0 });
1010
- await mcpClient["client"].connect(transport);
1011
- }
1012
- async connectSseClient(mcpClient, config) {
1013
- const transport = new __modelcontextprotocol_sdk_client_sse_js.SSEClientTransport(new URL(config.url));
1014
- await mcpClient["client"].connect(transport);
1015
- }
1016
- /**
1017
- * Get a connected client by server name
1018
- */
1019
- getClient(serverName) {
1020
- return this.clients.get(serverName);
1021
- }
1022
- /**
1023
- * Get all connected clients
1024
- */
1025
- getAllClients() {
1026
- return Array.from(this.clients.values());
1027
- }
1028
- /**
1029
- * Disconnect from a specific server
1030
- */
1031
- async disconnectServer(serverName) {
1032
- const client = this.clients.get(serverName);
1033
- if (client) {
1034
- await client.close();
1035
- this.clients.delete(serverName);
1036
- }
1037
- }
1038
- /**
1039
- * Disconnect from all servers
1040
- */
1041
- async disconnectAll() {
1042
- const disconnectPromises = Array.from(this.clients.values()).map((client) => client.close());
1043
- await Promise.all(disconnectPromises);
1044
- this.clients.clear();
1045
- }
1046
- /**
1047
- * Check if a server is connected
1048
- */
1049
- isConnected(serverName) {
1050
- return this.clients.has(serverName);
1051
- }
1052
- };
1053
-
1054
859
  //#endregion
1055
860
  //#region src/utils/findConfigFile.ts
1056
861
  /**
@@ -1316,8 +1121,565 @@ function generateServerId(length = DEFAULT_ID_LENGTH) {
1316
1121
  remaining--;
1317
1122
  }
1318
1123
  }
1319
- return result;
1320
- }
1124
+ return result;
1125
+ }
1126
+
1127
+ //#endregion
1128
+ //#region src/constants/index.ts
1129
+ /**
1130
+ * Shared constants for one-mcp package
1131
+ */
1132
+ /**
1133
+ * Prefix added to skill names when they clash with MCP tool names.
1134
+ * This ensures skills can be uniquely identified even when a tool has the same name.
1135
+ */
1136
+ const SKILL_PREFIX = "skill__";
1137
+ /**
1138
+ * Log prefix for skill detection messages.
1139
+ * Used to easily filter skill detection logs in stderr output.
1140
+ */
1141
+ const LOG_PREFIX_SKILL_DETECTION = "[skill-detection]";
1142
+ /**
1143
+ * Log prefix for general MCP capability discovery messages.
1144
+ */
1145
+ const LOG_PREFIX_CAPABILITY_DISCOVERY = "[capability-discovery]";
1146
+ /**
1147
+ * Prefix for prompt-based skill locations.
1148
+ * Format: "prompt:{serverName}:{promptName}"
1149
+ */
1150
+ const PROMPT_LOCATION_PREFIX = "prompt:";
1151
+ /**
1152
+ * Default server ID used when no ID is provided via CLI or config.
1153
+ * This fallback is used when auto-generation also fails.
1154
+ */
1155
+ const DEFAULT_SERVER_ID = "unknown";
1156
+
1157
+ //#endregion
1158
+ //#region src/services/DefinitionsCacheService.ts
1159
+ /**
1160
+ * DefinitionsCacheService
1161
+ *
1162
+ * Provides shared discovery, caching, and serialization for startup-time MCP
1163
+ * capability metadata. This avoids repeated remote enumeration during
1164
+ * mcp-serve startup and describe_tools generation.
1165
+ */
1166
+ function isYamlPath(filePath) {
1167
+ return filePath.endsWith(".yaml") || filePath.endsWith(".yml");
1168
+ }
1169
+ function toErrorMessage(error) {
1170
+ return error instanceof Error ? error.message : String(error);
1171
+ }
1172
+ function sanitizeConfigPathForFilename(configFilePath) {
1173
+ const absoluteConfigPath = (0, node_path.resolve)(configFilePath);
1174
+ 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;
1175
+ let result = "";
1176
+ let previousWasUnderscore = false;
1177
+ for (const char of normalizedPath) {
1178
+ if (char >= "a" && char <= "z" || char >= "A" && char <= "Z" || char >= "0" && char <= "9" || char === "." || char === "_" || char === "-") {
1179
+ result += char;
1180
+ previousWasUnderscore = false;
1181
+ continue;
1182
+ }
1183
+ if (!previousWasUnderscore) {
1184
+ result += "_";
1185
+ previousWasUnderscore = true;
1186
+ }
1187
+ }
1188
+ let start = 0;
1189
+ let end = result.length;
1190
+ while (start < end && result[start] === "_") start += 1;
1191
+ while (end > start && result[end - 1] === "_") end -= 1;
1192
+ return result.slice(start, end);
1193
+ }
1194
+ function cloneCache(cache) {
1195
+ return {
1196
+ ...cache,
1197
+ failures: [...cache.failures ?? []],
1198
+ skills: (cache.skills ?? []).map((skill) => ({ ...skill })),
1199
+ servers: Object.fromEntries(Object.entries(cache.servers).map(([serverName, server]) => [serverName, {
1200
+ ...server,
1201
+ tools: (server.tools ?? []).map((tool) => ({ ...tool })),
1202
+ resources: (server.resources ?? []).map((resource) => ({ ...resource })),
1203
+ prompts: (server.prompts ?? []).map((prompt) => ({
1204
+ ...prompt,
1205
+ arguments: prompt.arguments?.map((arg) => ({ ...arg }))
1206
+ })),
1207
+ promptSkills: (server.promptSkills ?? []).map((promptSkill) => ({
1208
+ ...promptSkill,
1209
+ skill: { ...promptSkill.skill }
1210
+ }))
1211
+ }]))
1212
+ };
1213
+ }
1214
+ var DefinitionsCacheService = class {
1215
+ clientManager;
1216
+ skillService;
1217
+ cacheData;
1218
+ liveDefinitionsPromise = null;
1219
+ mergedDefinitionsPromise = null;
1220
+ constructor(clientManager, skillService, options) {
1221
+ this.clientManager = clientManager;
1222
+ this.skillService = skillService;
1223
+ this.cacheData = options?.cacheData;
1224
+ }
1225
+ static async readFromFile(filePath) {
1226
+ const content = await (0, node_fs_promises.readFile)(filePath, "utf-8");
1227
+ const parsed = isYamlPath(filePath) ? js_yaml.default.load(content) : JSON.parse(content);
1228
+ if (!parsed || typeof parsed !== "object") throw new Error("Definitions cache must be an object");
1229
+ const cache = parsed;
1230
+ if (cache.version !== 1 || !cache.servers) throw new Error("Definitions cache is missing required fields");
1231
+ return {
1232
+ ...cache,
1233
+ failures: Array.isArray(cache.failures) ? cache.failures : [],
1234
+ skills: Array.isArray(cache.skills) ? cache.skills : [],
1235
+ servers: Object.fromEntries(Object.entries(cache.servers).map(([serverName, server]) => [serverName, {
1236
+ ...server,
1237
+ tools: Array.isArray(server.tools) ? server.tools : [],
1238
+ resources: Array.isArray(server.resources) ? server.resources : [],
1239
+ prompts: Array.isArray(server.prompts) ? server.prompts : [],
1240
+ promptSkills: Array.isArray(server.promptSkills) ? server.promptSkills : []
1241
+ }]))
1242
+ };
1243
+ }
1244
+ static async writeToFile(filePath, cache) {
1245
+ const serialized = isYamlPath(filePath) ? js_yaml.default.dump(cache, { noRefs: true }) : JSON.stringify(cache, null, 2);
1246
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(filePath), { recursive: true });
1247
+ await (0, node_fs_promises.writeFile)(filePath, serialized, "utf-8");
1248
+ }
1249
+ static getDefaultCachePath(configFilePath) {
1250
+ const sanitizedPath = sanitizeConfigPathForFilename(configFilePath);
1251
+ return (0, node_path.join)((0, node_os.homedir)(), ".aicode-toolkit", `${sanitizedPath}.definitions-cache.json`);
1252
+ }
1253
+ static generateConfigHash(config) {
1254
+ return (0, node_crypto.createHash)("sha256").update(JSON.stringify(config)).digest("hex");
1255
+ }
1256
+ static isCacheValid(cache, options) {
1257
+ if (options.configHash && cache.configHash && cache.configHash !== options.configHash) return false;
1258
+ if (options.oneMcpVersion && cache.oneMcpVersion && cache.oneMcpVersion !== options.oneMcpVersion) return false;
1259
+ return true;
1260
+ }
1261
+ static async clearFile(filePath) {
1262
+ await (0, node_fs_promises.rm)(filePath, { force: true });
1263
+ }
1264
+ clearLiveCache() {
1265
+ this.liveDefinitionsPromise = null;
1266
+ this.mergedDefinitionsPromise = null;
1267
+ }
1268
+ setCacheData(cacheData) {
1269
+ this.cacheData = cacheData;
1270
+ this.mergedDefinitionsPromise = null;
1271
+ }
1272
+ async collectForCache(options) {
1273
+ const liveDefinitions = await this.collectLiveDefinitions(options);
1274
+ this.setCacheData(liveDefinitions);
1275
+ this.liveDefinitionsPromise = Promise.resolve(cloneCache(liveDefinitions));
1276
+ return cloneCache(liveDefinitions);
1277
+ }
1278
+ async getDefinitions() {
1279
+ if (this.mergedDefinitionsPromise) return this.mergedDefinitionsPromise;
1280
+ this.mergedDefinitionsPromise = (async () => {
1281
+ const clients = this.clientManager.getAllClients();
1282
+ if (!this.cacheData) return this.getLiveDefinitions();
1283
+ const missingServers = clients.map((client) => client.serverName).filter((serverName) => !this.cacheData?.servers[serverName]);
1284
+ if (missingServers.length === 0) return cloneCache(this.cacheData);
1285
+ const liveDefinitions = await this.getLiveDefinitions();
1286
+ const merged = cloneCache(this.cacheData);
1287
+ for (const serverName of missingServers) {
1288
+ const serverDefinition = liveDefinitions.servers[serverName];
1289
+ if (serverDefinition) merged.servers[serverName] = serverDefinition;
1290
+ }
1291
+ const failureMap = /* @__PURE__ */ new Map();
1292
+ for (const failure of [...merged.failures, ...liveDefinitions.failures]) failureMap.set(failure.serverName, failure.error);
1293
+ merged.failures = Array.from(failureMap.entries()).map(([serverName, error]) => ({
1294
+ serverName,
1295
+ error
1296
+ }));
1297
+ if (merged.skills.length === 0 && liveDefinitions.skills.length > 0) merged.skills = liveDefinitions.skills.map((skill) => ({ ...skill }));
1298
+ return merged;
1299
+ })();
1300
+ return this.mergedDefinitionsPromise;
1301
+ }
1302
+ async getServerDefinitions() {
1303
+ const definitions = await this.getDefinitions();
1304
+ const serverOrder = this.clientManager.getKnownServerNames();
1305
+ if (serverOrder.length === 0) return Object.values(definitions.servers);
1306
+ return serverOrder.map((serverName) => definitions.servers[serverName]).filter((server) => server !== void 0);
1307
+ }
1308
+ async getServersForTool(toolName) {
1309
+ return (await this.getServerDefinitions()).filter((serverDefinition) => serverDefinition.tools.some((tool) => tool.name === toolName)).map((serverDefinition) => serverDefinition.serverName);
1310
+ }
1311
+ async getServersForResource(uri) {
1312
+ return (await this.getServerDefinitions()).filter((serverDefinition) => serverDefinition.resources.some((resource) => resource.uri === uri)).map((serverDefinition) => serverDefinition.serverName);
1313
+ }
1314
+ async getPromptSkillByName(skillName) {
1315
+ const definitions = await this.getDefinitions();
1316
+ for (const [serverName, server] of Object.entries(definitions.servers)) for (const promptSkill of server.promptSkills) if (promptSkill.skill.name === skillName) return {
1317
+ serverName,
1318
+ promptName: promptSkill.promptName,
1319
+ skill: promptSkill.skill,
1320
+ autoDetected: promptSkill.autoDetected
1321
+ };
1322
+ }
1323
+ async getCachedFileSkills() {
1324
+ return (await this.getDefinitions()).skills.map((skill) => ({ ...skill }));
1325
+ }
1326
+ async getLiveDefinitions() {
1327
+ if (!this.liveDefinitionsPromise) this.liveDefinitionsPromise = this.collectLiveDefinitions();
1328
+ return this.liveDefinitionsPromise;
1329
+ }
1330
+ async collectLiveDefinitions(options) {
1331
+ const clients = this.clientManager.getAllClients();
1332
+ const failures = [];
1333
+ const servers = {};
1334
+ const serverResults = await Promise.all(clients.map(async (client) => {
1335
+ try {
1336
+ const tools = await client.listTools();
1337
+ const resources = await this.listResourcesSafe(client);
1338
+ const prompts = await this.listPromptsSafe(client);
1339
+ const blacklist = new Set(client.toolBlacklist || []);
1340
+ const filteredTools = tools.filter((tool) => !blacklist.has(tool.name));
1341
+ const promptSkills = await this.collectPromptSkillsForClient(client, prompts);
1342
+ return {
1343
+ serverName: client.serverName,
1344
+ serverInstruction: client.serverInstruction,
1345
+ omitToolDescription: client.omitToolDescription,
1346
+ toolBlacklist: client.toolBlacklist,
1347
+ tools: filteredTools.map((tool) => ({
1348
+ name: tool.name,
1349
+ description: tool.description,
1350
+ inputSchema: tool.inputSchema,
1351
+ _meta: tool._meta
1352
+ })),
1353
+ resources,
1354
+ prompts,
1355
+ promptSkills
1356
+ };
1357
+ } catch (error) {
1358
+ failures.push({
1359
+ serverName: client.serverName,
1360
+ error: toErrorMessage(error)
1361
+ });
1362
+ return null;
1363
+ }
1364
+ }));
1365
+ for (const serverDefinition of serverResults) if (serverDefinition) servers[serverDefinition.serverName] = serverDefinition;
1366
+ return {
1367
+ version: 1,
1368
+ oneMcpVersion: options?.oneMcpVersion,
1369
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1370
+ configPath: options?.configPath,
1371
+ configHash: options?.configHash,
1372
+ serverId: options?.serverId,
1373
+ servers,
1374
+ skills: await this.collectFileSkills(),
1375
+ failures
1376
+ };
1377
+ }
1378
+ async collectFileSkills() {
1379
+ if (!this.skillService) return [];
1380
+ return (await this.skillService.getSkills()).map((skill) => this.toCachedFileSkill(skill));
1381
+ }
1382
+ toCachedFileSkill(skill) {
1383
+ return {
1384
+ name: skill.name,
1385
+ description: skill.description,
1386
+ location: skill.location,
1387
+ basePath: skill.basePath
1388
+ };
1389
+ }
1390
+ async listPromptsSafe(client) {
1391
+ try {
1392
+ return (await client.listPrompts()).map((prompt) => ({
1393
+ name: prompt.name,
1394
+ description: prompt.description,
1395
+ arguments: prompt.arguments?.map((arg) => ({ ...arg }))
1396
+ }));
1397
+ } catch (error) {
1398
+ console.error(`${LOG_PREFIX_SKILL_DETECTION} Failed to list prompts from ${client.serverName}: ${toErrorMessage(error)}`);
1399
+ return [];
1400
+ }
1401
+ }
1402
+ async listResourcesSafe(client) {
1403
+ try {
1404
+ return (await client.listResources()).map((resource) => ({
1405
+ uri: resource.uri,
1406
+ name: resource.name,
1407
+ description: resource.description,
1408
+ mimeType: resource.mimeType
1409
+ }));
1410
+ } catch (error) {
1411
+ console.error(`${LOG_PREFIX_CAPABILITY_DISCOVERY} Failed to list resources from ${client.serverName}: ${toErrorMessage(error)}`);
1412
+ return [];
1413
+ }
1414
+ }
1415
+ async collectPromptSkillsForClient(client, prompts) {
1416
+ const configuredPromptNames = new Set(client.prompts ? Object.keys(client.prompts) : []);
1417
+ const promptSkills = [];
1418
+ if (client.prompts) {
1419
+ for (const [promptName, promptConfig] of Object.entries(client.prompts)) if (promptConfig.skill) promptSkills.push({
1420
+ promptName,
1421
+ skill: { ...promptConfig.skill }
1422
+ });
1423
+ }
1424
+ const autoDetectedSkills = await Promise.all(prompts.map(async (prompt) => {
1425
+ if (configuredPromptNames.has(prompt.name)) return null;
1426
+ try {
1427
+ const skillExtraction = extractSkillFrontMatter((await client.getPrompt(prompt.name)).messages?.map((message) => {
1428
+ const content = message.content;
1429
+ if (typeof content === "string") return content;
1430
+ if (content && typeof content === "object" && "text" in content) return String(content.text);
1431
+ return "";
1432
+ }).join("\n") || "");
1433
+ if (!skillExtraction) return null;
1434
+ return {
1435
+ promptName: prompt.name,
1436
+ skill: skillExtraction.skill,
1437
+ autoDetected: true
1438
+ };
1439
+ } catch (error) {
1440
+ console.error(`${LOG_PREFIX_SKILL_DETECTION} Failed to fetch prompt '${prompt.name}' from ${client.serverName}: ${toErrorMessage(error)}`);
1441
+ return null;
1442
+ }
1443
+ }));
1444
+ for (const autoDetectedSkill of autoDetectedSkills) if (autoDetectedSkill) promptSkills.push(autoDetectedSkill);
1445
+ return promptSkills;
1446
+ }
1447
+ };
1448
+
1449
+ //#endregion
1450
+ //#region src/services/McpClientManagerService.ts
1451
+ /** Default connection timeout in milliseconds (30 seconds) */
1452
+ const DEFAULT_CONNECTION_TIMEOUT_MS = 3e4;
1453
+ /**
1454
+ * MCP Client wrapper for managing individual server connections
1455
+ * This is an internal class used by McpClientManagerService
1456
+ */
1457
+ var McpClient = class {
1458
+ serverName;
1459
+ serverInstruction;
1460
+ toolBlacklist;
1461
+ omitToolDescription;
1462
+ prompts;
1463
+ transport;
1464
+ client;
1465
+ childProcess;
1466
+ connected = false;
1467
+ constructor(serverName, transport, client, config) {
1468
+ this.serverName = serverName;
1469
+ this.serverInstruction = config.instruction;
1470
+ this.toolBlacklist = config.toolBlacklist;
1471
+ this.omitToolDescription = config.omitToolDescription;
1472
+ this.prompts = config.prompts;
1473
+ this.transport = transport;
1474
+ this.client = client;
1475
+ }
1476
+ setChildProcess(process$1) {
1477
+ this.childProcess = process$1;
1478
+ }
1479
+ setConnected(connected) {
1480
+ this.connected = connected;
1481
+ }
1482
+ async listTools() {
1483
+ if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1484
+ return (await this.client.listTools()).tools;
1485
+ }
1486
+ async listResources() {
1487
+ if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1488
+ return (await this.client.listResources()).resources;
1489
+ }
1490
+ async listPrompts() {
1491
+ if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1492
+ return (await this.client.listPrompts()).prompts;
1493
+ }
1494
+ async callTool(name, args) {
1495
+ if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1496
+ return await this.client.callTool({
1497
+ name,
1498
+ arguments: args
1499
+ });
1500
+ }
1501
+ async readResource(uri) {
1502
+ if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1503
+ return await this.client.readResource({ uri });
1504
+ }
1505
+ async getPrompt(name, args) {
1506
+ if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1507
+ return await this.client.getPrompt({
1508
+ name,
1509
+ arguments: args
1510
+ });
1511
+ }
1512
+ async close() {
1513
+ if (this.childProcess) this.childProcess.kill();
1514
+ await this.client.close();
1515
+ this.connected = false;
1516
+ }
1517
+ };
1518
+ /**
1519
+ * Service for managing MCP client connections to remote servers
1520
+ */
1521
+ var McpClientManagerService = class {
1522
+ clients = /* @__PURE__ */ new Map();
1523
+ serverConfigs = /* @__PURE__ */ new Map();
1524
+ connectionPromises = /* @__PURE__ */ new Map();
1525
+ constructor() {
1526
+ process.on("exit", () => {
1527
+ this.cleanupOnExit();
1528
+ });
1529
+ process.on("SIGINT", () => {
1530
+ this.cleanupOnExit();
1531
+ process.exit(0);
1532
+ });
1533
+ process.on("SIGTERM", () => {
1534
+ this.cleanupOnExit();
1535
+ process.exit(0);
1536
+ });
1537
+ }
1538
+ /**
1539
+ * Cleanup all resources on exit (child processes)
1540
+ */
1541
+ cleanupOnExit() {
1542
+ for (const [serverName, client] of this.clients) try {
1543
+ const childProcess = client["childProcess"];
1544
+ if (childProcess && !childProcess.killed) {
1545
+ console.error(`Killing stdio MCP server: ${serverName} (PID: ${childProcess.pid})`);
1546
+ childProcess.kill("SIGTERM");
1547
+ setTimeout(() => {
1548
+ if (!childProcess.killed) {
1549
+ console.error(`Force killing stdio MCP server: ${serverName} (PID: ${childProcess.pid})`);
1550
+ childProcess.kill("SIGKILL");
1551
+ }
1552
+ }, 1e3);
1553
+ }
1554
+ } catch (error) {
1555
+ console.error(`Failed to kill child process for ${serverName}:`, error);
1556
+ }
1557
+ }
1558
+ /**
1559
+ * Connect to an MCP server based on its configuration with timeout
1560
+ * Uses the timeout from server config, falling back to default (30s)
1561
+ */
1562
+ async connectToServer(serverName, config) {
1563
+ this.serverConfigs.set(serverName, config);
1564
+ await this.ensureConnected(serverName);
1565
+ }
1566
+ registerServerConfigs(configs) {
1567
+ for (const [serverName, config] of Object.entries(configs)) this.serverConfigs.set(serverName, config);
1568
+ }
1569
+ getKnownServerNames() {
1570
+ return Array.from(this.serverConfigs.keys());
1571
+ }
1572
+ async ensureConnected(serverName) {
1573
+ const existingClient = this.clients.get(serverName);
1574
+ if (existingClient) return existingClient;
1575
+ const inflightConnection = this.connectionPromises.get(serverName);
1576
+ if (inflightConnection) return await inflightConnection;
1577
+ const config = this.serverConfigs.get(serverName);
1578
+ if (!config) throw new Error(`No configuration found for server "${serverName}"`);
1579
+ const connectionPromise = this.createConnection(serverName, config);
1580
+ this.connectionPromises.set(serverName, connectionPromise);
1581
+ try {
1582
+ return await connectionPromise;
1583
+ } finally {
1584
+ this.connectionPromises.delete(serverName);
1585
+ }
1586
+ }
1587
+ async createConnection(serverName, config) {
1588
+ const timeoutMs = config.timeout ?? DEFAULT_CONNECTION_TIMEOUT_MS;
1589
+ const client = new __modelcontextprotocol_sdk_client_index_js.Client({
1590
+ name: `@agiflowai/one-mcp-client`,
1591
+ version: "0.1.0"
1592
+ }, { capabilities: {} });
1593
+ const mcpClient = new McpClient(serverName, config.transport, client, {
1594
+ instruction: config.instruction,
1595
+ toolBlacklist: config.toolBlacklist,
1596
+ omitToolDescription: config.omitToolDescription,
1597
+ prompts: config.prompts
1598
+ });
1599
+ try {
1600
+ await Promise.race([this.performConnection(mcpClient, config), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`Connection timeout after ${timeoutMs}ms`)), timeoutMs))]);
1601
+ mcpClient.setConnected(true);
1602
+ if (!mcpClient.serverInstruction) try {
1603
+ const serverInstruction = mcpClient["client"].getInstructions();
1604
+ if (serverInstruction) mcpClient.serverInstruction = serverInstruction;
1605
+ } catch (error) {
1606
+ console.error(`Failed to get server instruction from ${serverName}:`, error);
1607
+ }
1608
+ this.clients.set(serverName, mcpClient);
1609
+ return mcpClient;
1610
+ } catch (error) {
1611
+ await mcpClient.close();
1612
+ throw error;
1613
+ }
1614
+ }
1615
+ /**
1616
+ * Perform the actual connection to MCP server
1617
+ */
1618
+ async performConnection(mcpClient, config) {
1619
+ if (config.transport === "stdio") await this.connectStdioClient(mcpClient, config.config);
1620
+ else if (config.transport === "http") await this.connectHttpClient(mcpClient, config.config);
1621
+ else if (config.transport === "sse") await this.connectSseClient(mcpClient, config.config);
1622
+ else throw new Error(`Unsupported transport type: ${config.transport}`);
1623
+ }
1624
+ async connectStdioClient(mcpClient, config) {
1625
+ const transport = new __modelcontextprotocol_sdk_client_stdio_js.StdioClientTransport({
1626
+ command: config.command,
1627
+ args: config.args,
1628
+ env: {
1629
+ ...process.env,
1630
+ ...config.env ?? {}
1631
+ }
1632
+ });
1633
+ await mcpClient["client"].connect(transport);
1634
+ const childProcess = transport["_process"];
1635
+ if (childProcess) mcpClient.setChildProcess(childProcess);
1636
+ }
1637
+ async connectHttpClient(mcpClient, config) {
1638
+ const transport = new __modelcontextprotocol_sdk_client_streamableHttp_js.StreamableHTTPClientTransport(new URL(config.url), { requestInit: config.headers ? { headers: config.headers } : void 0 });
1639
+ await mcpClient["client"].connect(transport);
1640
+ }
1641
+ async connectSseClient(mcpClient, config) {
1642
+ const transport = new __modelcontextprotocol_sdk_client_sse_js.SSEClientTransport(new URL(config.url));
1643
+ await mcpClient["client"].connect(transport);
1644
+ }
1645
+ /**
1646
+ * Get a connected client by server name
1647
+ */
1648
+ getClient(serverName) {
1649
+ return this.clients.get(serverName);
1650
+ }
1651
+ /**
1652
+ * Get all connected clients
1653
+ */
1654
+ getAllClients() {
1655
+ return Array.from(this.clients.values());
1656
+ }
1657
+ /**
1658
+ * Disconnect from a specific server
1659
+ */
1660
+ async disconnectServer(serverName) {
1661
+ const client = this.clients.get(serverName);
1662
+ if (client) {
1663
+ await client.close();
1664
+ this.clients.delete(serverName);
1665
+ }
1666
+ }
1667
+ /**
1668
+ * Disconnect from all servers
1669
+ */
1670
+ async disconnectAll() {
1671
+ const disconnectPromises = Array.from(this.clients.values()).map((client) => client.close());
1672
+ await Promise.all(disconnectPromises);
1673
+ this.clients.clear();
1674
+ this.connectionPromises.clear();
1675
+ }
1676
+ /**
1677
+ * Check if a server is connected
1678
+ */
1679
+ isConnected(serverName) {
1680
+ return this.clients.has(serverName);
1681
+ }
1682
+ };
1321
1683
 
1322
1684
  //#endregion
1323
1685
  //#region src/services/SkillService.ts
@@ -1391,6 +1753,8 @@ var SkillService = class {
1391
1753
  skillsByName = null;
1392
1754
  /** Active file watchers for skill directories */
1393
1755
  watchers = [];
1756
+ /** Polling timers used when native file watching is unavailable */
1757
+ pollingTimers = [];
1394
1758
  /** Callback invoked when cache is invalidated due to file changes */
1395
1759
  onCacheInvalidated;
1396
1760
  /**
@@ -1475,7 +1839,13 @@ var SkillService = class {
1475
1839
  const abortController = new AbortController();
1476
1840
  this.watchers.push(abortController);
1477
1841
  this.watchDirectory(skillsDir, abortController.signal).catch((error) => {
1478
- if (error?.name !== "AbortError") console.error(`[skill-watcher] Error watching ${skillsDir}: ${error instanceof Error ? error.message : "Unknown error"}`);
1842
+ if (error?.name !== "AbortError") {
1843
+ if (this.isWatchResourceLimitError(error)) {
1844
+ this.startPollingDirectory(skillsDir, abortController.signal);
1845
+ return;
1846
+ }
1847
+ console.error(`[skill-watcher] Error watching ${skillsDir}: ${error instanceof Error ? error.message : "Unknown error"}`);
1848
+ }
1479
1849
  });
1480
1850
  }
1481
1851
  }
@@ -1486,6 +1856,8 @@ var SkillService = class {
1486
1856
  stopWatching() {
1487
1857
  for (const controller of this.watchers) controller.abort();
1488
1858
  this.watchers = [];
1859
+ for (const timer of this.pollingTimers) clearInterval(timer);
1860
+ this.pollingTimers = [];
1489
1861
  }
1490
1862
  /**
1491
1863
  * Watches a directory for changes to SKILL.md files.
@@ -1497,10 +1869,66 @@ var SkillService = class {
1497
1869
  recursive: true,
1498
1870
  signal
1499
1871
  });
1500
- for await (const event of watcher) if (event.filename?.endsWith("SKILL.md")) {
1501
- this.clearCache();
1502
- this.onCacheInvalidated?.();
1872
+ for await (const event of watcher) if (event.filename?.endsWith("SKILL.md")) this.invalidateCache();
1873
+ }
1874
+ invalidateCache() {
1875
+ this.clearCache();
1876
+ this.onCacheInvalidated?.();
1877
+ }
1878
+ isWatchResourceLimitError(error) {
1879
+ return error instanceof Error && "code" in error && (error.code === "EMFILE" || error.code === "ENOSPC");
1880
+ }
1881
+ startPollingDirectory(dirPath, signal) {
1882
+ this.createSkillSnapshot(dirPath).then((initialSnapshot) => {
1883
+ let previousSnapshot = initialSnapshot;
1884
+ const timer = setInterval(() => {
1885
+ if (signal.aborted) {
1886
+ clearInterval(timer);
1887
+ return;
1888
+ }
1889
+ this.createSkillSnapshot(dirPath).then((nextSnapshot) => {
1890
+ if (!this.snapshotsEqual(previousSnapshot, nextSnapshot)) {
1891
+ previousSnapshot = nextSnapshot;
1892
+ this.invalidateCache();
1893
+ }
1894
+ }).catch((error) => {
1895
+ console.error(`[skill-watcher] Polling failed for ${dirPath}: ${error instanceof Error ? error.message : "Unknown error"}`);
1896
+ });
1897
+ }, 100);
1898
+ this.pollingTimers.push(timer);
1899
+ signal.addEventListener("abort", () => {
1900
+ clearInterval(timer);
1901
+ this.pollingTimers = this.pollingTimers.filter((activeTimer) => activeTimer !== timer);
1902
+ }, { once: true });
1903
+ });
1904
+ }
1905
+ async createSkillSnapshot(dirPath) {
1906
+ const snapshot = /* @__PURE__ */ new Map();
1907
+ await this.collectSkillSnapshots(dirPath, snapshot);
1908
+ return snapshot;
1909
+ }
1910
+ async collectSkillSnapshots(dirPath, snapshot) {
1911
+ let entries;
1912
+ try {
1913
+ entries = await (0, node_fs_promises.readdir)(dirPath);
1914
+ } catch (error) {
1915
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return;
1916
+ throw error;
1503
1917
  }
1918
+ await Promise.all(entries.map(async (entry) => {
1919
+ const entryPath = (0, node_path.join)(dirPath, entry);
1920
+ const entryStat = await (0, node_fs_promises.stat)(entryPath);
1921
+ if (entryStat.isDirectory()) {
1922
+ await this.collectSkillSnapshots(entryPath, snapshot);
1923
+ return;
1924
+ }
1925
+ if (entry === "SKILL.md") snapshot.set(entryPath, entryStat.mtimeMs);
1926
+ }));
1927
+ }
1928
+ snapshotsEqual(left, right) {
1929
+ if (left.size !== right.size) return false;
1930
+ for (const [filePath, mtimeMs] of left) if (right.get(filePath) !== mtimeMs) return false;
1931
+ return true;
1504
1932
  }
1505
1933
  /**
1506
1934
  * Load skills from a directory.
@@ -1608,32 +2036,6 @@ var SkillService = class {
1608
2036
  }
1609
2037
  };
1610
2038
 
1611
- //#endregion
1612
- //#region src/constants/index.ts
1613
- /**
1614
- * Shared constants for one-mcp package
1615
- */
1616
- /**
1617
- * Prefix added to skill names when they clash with MCP tool names.
1618
- * This ensures skills can be uniquely identified even when a tool has the same name.
1619
- */
1620
- const SKILL_PREFIX = "skill__";
1621
- /**
1622
- * Log prefix for skill detection messages.
1623
- * Used to easily filter skill detection logs in stderr output.
1624
- */
1625
- const LOG_PREFIX_SKILL_DETECTION = "[skill-detection]";
1626
- /**
1627
- * Prefix for prompt-based skill locations.
1628
- * Format: "prompt:{serverName}:{promptName}"
1629
- */
1630
- const PROMPT_LOCATION_PREFIX = "prompt:";
1631
- /**
1632
- * Default server ID used when no ID is provided via CLI or config.
1633
- * This fallback is used when auto-generation also fails.
1634
- */
1635
- const DEFAULT_SERVER_ID = "unknown";
1636
-
1637
2039
  //#endregion
1638
2040
  //#region src/templates/toolkit-description.liquid?raw
1639
2041
  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";
@@ -1672,6 +2074,7 @@ var DescribeToolsTool = class DescribeToolsTool {
1672
2074
  static TOOL_NAME = "describe_tools";
1673
2075
  clientManager;
1674
2076
  skillService;
2077
+ definitionsCacheService;
1675
2078
  liquid = new liquidjs.Liquid();
1676
2079
  /** Cache for auto-detected skills from prompt front-matter */
1677
2080
  autoDetectedSkillsCache = null;
@@ -1683,10 +2086,11 @@ var DescribeToolsTool = class DescribeToolsTool {
1683
2086
  * @param skillService - Optional skill service for loading skills
1684
2087
  * @param serverId - Unique server identifier for this one-mcp instance
1685
2088
  */
1686
- constructor(clientManager, skillService, serverId) {
2089
+ constructor(clientManager, skillService, serverId, definitionsCacheService) {
1687
2090
  this.clientManager = clientManager;
1688
2091
  this.skillService = skillService;
1689
2092
  this.serverId = serverId || DEFAULT_SERVER_ID;
2093
+ this.definitionsCacheService = definitionsCacheService || new DefinitionsCacheService(clientManager, skillService);
1690
2094
  }
1691
2095
  /**
1692
2096
  * Clears the cached auto-detected skills from prompt front-matter.
@@ -1695,6 +2099,7 @@ var DescribeToolsTool = class DescribeToolsTool {
1695
2099
  */
1696
2100
  clearAutoDetectedSkillsCache() {
1697
2101
  this.autoDetectedSkillsCache = null;
2102
+ this.definitionsCacheService.clearLiveCache();
1698
2103
  }
1699
2104
  /**
1700
2105
  * Detects and caches skills from prompt front-matter across all connected MCP servers.
@@ -1760,21 +2165,12 @@ var DescribeToolsTool = class DescribeToolsTool {
1760
2165
  * @returns Array of skill template data derived from prompts
1761
2166
  */
1762
2167
  async collectPromptSkills() {
1763
- const clients = this.clientManager.getAllClients();
1764
2168
  const promptSkills = [];
1765
- for (const client of clients) {
1766
- if (!client.prompts) continue;
1767
- for (const promptConfig of Object.values(client.prompts)) if (promptConfig.skill) promptSkills.push({
1768
- name: promptConfig.skill.name,
1769
- displayName: promptConfig.skill.name,
1770
- description: promptConfig.skill.description
1771
- });
1772
- }
1773
- const autoDetectedSkills = await this.detectSkillsFromPromptFrontMatter();
1774
- for (const autoSkill of autoDetectedSkills) promptSkills.push({
1775
- name: autoSkill.skill.name,
1776
- displayName: autoSkill.skill.name,
1777
- description: autoSkill.skill.description
2169
+ const serverDefinitions = await this.definitionsCacheService.getServerDefinitions();
2170
+ for (const serverDefinition of serverDefinitions) for (const promptSkill of serverDefinition.promptSkills) promptSkills.push({
2171
+ name: promptSkill.skill.name,
2172
+ displayName: promptSkill.skill.name,
2173
+ description: promptSkill.skill.description
1778
2174
  });
1779
2175
  return promptSkills;
1780
2176
  }
@@ -1787,22 +2183,7 @@ var DescribeToolsTool = class DescribeToolsTool {
1787
2183
  */
1788
2184
  async findPromptSkill(skillName) {
1789
2185
  if (!skillName) return void 0;
1790
- const clients = this.clientManager.getAllClients();
1791
- for (const client of clients) {
1792
- if (!client.prompts) continue;
1793
- for (const [promptName, promptConfig] of Object.entries(client.prompts)) if (promptConfig.skill && promptConfig.skill.name === skillName) return {
1794
- serverName: client.serverName,
1795
- promptName,
1796
- skill: promptConfig.skill
1797
- };
1798
- }
1799
- const autoDetectedSkills = await this.detectSkillsFromPromptFrontMatter();
1800
- for (const autoSkill of autoDetectedSkills) if (autoSkill.skill.name === skillName) return {
1801
- serverName: autoSkill.serverName,
1802
- promptName: autoSkill.promptName,
1803
- skill: autoSkill.skill,
1804
- autoDetected: true
1805
- };
2186
+ return await this.definitionsCacheService.getPromptSkillByName(skillName);
1806
2187
  }
1807
2188
  /**
1808
2189
  * Retrieves skill content from a prompt-based skill configuration.
@@ -1815,13 +2196,8 @@ var DescribeToolsTool = class DescribeToolsTool {
1815
2196
  async getPromptSkillContent(skillName) {
1816
2197
  const promptSkill = await this.findPromptSkill(skillName);
1817
2198
  if (!promptSkill) return void 0;
1818
- const client = this.clientManager.getClient(promptSkill.serverName);
1819
- if (!client) {
1820
- console.error(`Client not found for server '${promptSkill.serverName}' when fetching prompt skill '${skillName}'`);
1821
- return;
1822
- }
1823
2199
  try {
1824
- const rawInstructions = (await client.getPrompt(promptSkill.promptName)).messages?.map((m) => {
2200
+ const rawInstructions = (await (await this.clientManager.ensureConnected(promptSkill.serverName)).getPrompt(promptSkill.promptName)).messages?.map((m) => {
1825
2201
  const content = m.content;
1826
2202
  if (typeof content === "string") return content;
1827
2203
  if (content && typeof content === "object" && "text" in content) return String(content.text);
@@ -1850,24 +2226,12 @@ var DescribeToolsTool = class DescribeToolsTool {
1850
2226
  * @returns Object with rendered description and set of all tool names
1851
2227
  */
1852
2228
  async buildToolkitDescription() {
1853
- const clients = this.clientManager.getAllClients();
2229
+ const serverDefinitions = await this.definitionsCacheService.getServerDefinitions();
1854
2230
  const toolToServers = /* @__PURE__ */ new Map();
1855
- const serverToolsMap = /* @__PURE__ */ new Map();
1856
- await Promise.all(clients.map(async (client) => {
1857
- try {
1858
- const tools = await client.listTools();
1859
- const blacklist = new Set(client.toolBlacklist || []);
1860
- const filteredTools = tools.filter((t) => !blacklist.has(t.name));
1861
- serverToolsMap.set(client.serverName, filteredTools);
1862
- for (const tool of filteredTools) {
1863
- if (!toolToServers.has(tool.name)) toolToServers.set(tool.name, []);
1864
- toolToServers.get(tool.name)?.push(client.serverName);
1865
- }
1866
- } catch (error) {
1867
- console.error(`Failed to list tools from ${client.serverName}:`, error);
1868
- serverToolsMap.set(client.serverName, []);
1869
- }
1870
- }));
2231
+ for (const serverDefinition of serverDefinitions) for (const tool of serverDefinition.tools) {
2232
+ if (!toolToServers.has(tool.name)) toolToServers.set(tool.name, []);
2233
+ toolToServers.get(tool.name)?.push(serverDefinition.serverName);
2234
+ }
1871
2235
  /**
1872
2236
  * Formats tool name with server prefix if the tool exists on multiple servers
1873
2237
  */
@@ -1875,21 +2239,25 @@ var DescribeToolsTool = class DescribeToolsTool {
1875
2239
  return (toolToServers.get(toolName) || []).length > 1 ? `${serverName}__${toolName}` : toolName;
1876
2240
  };
1877
2241
  const allToolNames = /* @__PURE__ */ new Set();
1878
- const servers = clients.map((client) => {
1879
- const formattedTools = (serverToolsMap.get(client.serverName) || []).map((t) => ({
1880
- displayName: formatToolName(t.name, client.serverName),
2242
+ const servers = serverDefinitions.map((serverDefinition) => {
2243
+ const formattedTools = serverDefinition.tools.map((t) => ({
2244
+ displayName: formatToolName(t.name, serverDefinition.serverName),
1881
2245
  description: t.description
1882
2246
  }));
1883
2247
  for (const tool of formattedTools) allToolNames.add(tool.displayName);
1884
2248
  return {
1885
- name: client.serverName,
1886
- instruction: client.serverInstruction,
1887
- omitToolDescription: client.omitToolDescription || false,
2249
+ name: serverDefinition.serverName,
2250
+ instruction: serverDefinition.serverInstruction,
2251
+ omitToolDescription: serverDefinition.omitToolDescription || false,
1888
2252
  tools: formattedTools,
1889
2253
  toolNames: formattedTools.map((t) => t.displayName)
1890
2254
  };
1891
2255
  });
1892
- const [rawSkills, promptSkills] = await Promise.all([this.skillService ? this.skillService.getSkills() : Promise.resolve([]), this.collectPromptSkills()]);
2256
+ const [rawSkills, cachedSkills, promptSkills] = await Promise.all([
2257
+ this.skillService ? this.skillService.getSkills() : Promise.resolve([]),
2258
+ this.definitionsCacheService.getCachedFileSkills(),
2259
+ this.collectPromptSkills()
2260
+ ]);
1893
2261
  const seenSkillNames = /* @__PURE__ */ new Set();
1894
2262
  const allSkillsData = [];
1895
2263
  for (const skill of rawSkills) if (!seenSkillNames.has(skill.name)) {
@@ -1900,6 +2268,14 @@ var DescribeToolsTool = class DescribeToolsTool {
1900
2268
  description: skill.description
1901
2269
  });
1902
2270
  }
2271
+ for (const skill of cachedSkills) if (!seenSkillNames.has(skill.name)) {
2272
+ seenSkillNames.add(skill.name);
2273
+ allSkillsData.push({
2274
+ name: skill.name,
2275
+ displayName: skill.name,
2276
+ description: skill.description
2277
+ });
2278
+ }
1903
2279
  for (const skill of promptSkills) if (!seenSkillNames.has(skill.name)) {
1904
2280
  seenSkillNames.add(skill.name);
1905
2281
  allSkillsData.push(skill);
@@ -1969,7 +2345,7 @@ var DescribeToolsTool = class DescribeToolsTool {
1969
2345
  async execute(input) {
1970
2346
  try {
1971
2347
  const { toolNames } = input;
1972
- const clients = this.clientManager.getAllClients();
2348
+ const serverDefinitions = await this.definitionsCacheService.getServerDefinitions();
1973
2349
  if (!toolNames || toolNames.length === 0) return {
1974
2350
  content: [{
1975
2351
  type: "text",
@@ -1979,25 +2355,18 @@ var DescribeToolsTool = class DescribeToolsTool {
1979
2355
  };
1980
2356
  const serverToolsMap = /* @__PURE__ */ new Map();
1981
2357
  const toolToServers = /* @__PURE__ */ new Map();
1982
- await Promise.all(clients.map(async (client) => {
1983
- try {
1984
- const tools = await client.listTools();
1985
- const blacklist = new Set(client.toolBlacklist || []);
1986
- const typedTools = tools.filter((t) => !blacklist.has(t.name)).map((t) => ({
1987
- name: t.name,
1988
- description: t.description,
1989
- inputSchema: t.inputSchema
1990
- }));
1991
- serverToolsMap.set(client.serverName, typedTools);
1992
- for (const tool of typedTools) {
1993
- if (!toolToServers.has(tool.name)) toolToServers.set(tool.name, []);
1994
- toolToServers.get(tool.name)?.push(client.serverName);
1995
- }
1996
- } catch (error) {
1997
- console.error(`Failed to list tools from ${client.serverName}:`, error);
1998
- serverToolsMap.set(client.serverName, []);
2358
+ for (const serverDefinition of serverDefinitions) {
2359
+ const typedTools = serverDefinition.tools.map((tool) => ({
2360
+ name: tool.name,
2361
+ description: tool.description,
2362
+ inputSchema: tool.inputSchema
2363
+ }));
2364
+ serverToolsMap.set(serverDefinition.serverName, typedTools);
2365
+ for (const tool of typedTools) {
2366
+ if (!toolToServers.has(tool.name)) toolToServers.set(tool.name, []);
2367
+ toolToServers.get(tool.name)?.push(serverDefinition.serverName);
1999
2368
  }
2000
- }));
2369
+ }
2001
2370
  const lookupResults = await Promise.all(toolNames.map(async (requestedName) => {
2002
2371
  const result$1 = {
2003
2372
  tools: [],
@@ -2135,6 +2504,95 @@ var DescribeToolsTool = class DescribeToolsTool {
2135
2504
  }
2136
2505
  };
2137
2506
 
2507
+ //#endregion
2508
+ //#region src/utils/toolCapabilities.ts
2509
+ const TOOL_CAPABILITIES_META_KEY = "agiflowai/capabilities";
2510
+ function getToolCapabilities(tool) {
2511
+ const rawCapabilities = tool._meta?.[TOOL_CAPABILITIES_META_KEY];
2512
+ if (!Array.isArray(rawCapabilities)) return [];
2513
+ return rawCapabilities.filter((value) => typeof value === "string");
2514
+ }
2515
+ function getUniqueSortedCapabilities(tools) {
2516
+ return Array.from(new Set(tools.flatMap((tool) => getToolCapabilities(tool)))).sort();
2517
+ }
2518
+
2519
+ //#endregion
2520
+ //#region src/tools/SearchListToolsTool.ts
2521
+ var SearchListToolsTool = class SearchListToolsTool {
2522
+ static TOOL_NAME = "list_tools";
2523
+ constructor(_clientManager, definitionsCacheService) {
2524
+ this._clientManager = _clientManager;
2525
+ this.definitionsCacheService = definitionsCacheService;
2526
+ }
2527
+ formatToolName(toolName, serverName, toolToServers) {
2528
+ return (toolToServers.get(toolName) || []).length > 1 ? `${serverName}__${toolName}` : toolName;
2529
+ }
2530
+ async getDefinition() {
2531
+ const serverDefinitions = await this.definitionsCacheService.getServerDefinitions();
2532
+ const capabilitySummary = serverDefinitions.length > 0 ? serverDefinitions.map((server) => {
2533
+ const capabilities = getUniqueSortedCapabilities(server.tools);
2534
+ const summary = capabilities.length > 0 ? capabilities.join(", ") : server.serverInstruction || "No capability summary available";
2535
+ return `${server.serverName}: ${summary}`;
2536
+ }).join("\n") : "No proxied servers available.";
2537
+ return {
2538
+ name: SearchListToolsTool.TOOL_NAME,
2539
+ description: `Search proxied MCP tools by server capability summary.\n\nAvailable capabilities:\n${capabilitySummary}`,
2540
+ inputSchema: {
2541
+ type: "object",
2542
+ properties: {
2543
+ capability: {
2544
+ type: "string",
2545
+ description: "Optional capability filter. Matches explicit capability tags first, then server summaries, server names, tool names, and tool descriptions."
2546
+ },
2547
+ serverName: {
2548
+ type: "string",
2549
+ description: "Optional server name filter."
2550
+ }
2551
+ },
2552
+ additionalProperties: false
2553
+ }
2554
+ };
2555
+ }
2556
+ async execute(input) {
2557
+ const serverDefinitions = await this.definitionsCacheService.getServerDefinitions();
2558
+ const capabilityFilter = input.capability?.trim().toLowerCase();
2559
+ const serverNameFilter = input.serverName?.trim().toLowerCase();
2560
+ const toolToServers = /* @__PURE__ */ new Map();
2561
+ for (const serverDefinition of serverDefinitions) for (const tool of serverDefinition.tools) {
2562
+ if (!toolToServers.has(tool.name)) toolToServers.set(tool.name, []);
2563
+ toolToServers.get(tool.name)?.push(serverDefinition.serverName);
2564
+ }
2565
+ const filteredServers = serverDefinitions.filter((serverDefinition) => {
2566
+ if (serverNameFilter && serverDefinition.serverName.toLowerCase() !== serverNameFilter) return false;
2567
+ if (!capabilityFilter) return true;
2568
+ if ((serverDefinition.serverInstruction?.toLowerCase() || "").includes(capabilityFilter)) return true;
2569
+ if (getUniqueSortedCapabilities(serverDefinition.tools).some((capability) => capability.toLowerCase().includes(capabilityFilter))) return true;
2570
+ return serverDefinition.tools.some((tool) => {
2571
+ const toolName = this.formatToolName(tool.name, serverDefinition.serverName, toolToServers);
2572
+ const toolCapabilities = getToolCapabilities(tool);
2573
+ return toolName.toLowerCase().includes(capabilityFilter) || (tool.description || "").toLowerCase().includes(capabilityFilter) || toolCapabilities.some((capability) => capability.toLowerCase().includes(capabilityFilter));
2574
+ });
2575
+ }).map((serverDefinition) => ({
2576
+ server: serverDefinition.serverName,
2577
+ capabilities: getUniqueSortedCapabilities(serverDefinition.tools),
2578
+ summary: serverDefinition.serverInstruction,
2579
+ tools: serverDefinition.tools.map((tool) => ({
2580
+ name: this.formatToolName(tool.name, serverDefinition.serverName, toolToServers),
2581
+ description: serverDefinition.omitToolDescription ? void 0 : tool.description,
2582
+ capabilities: getToolCapabilities(tool)
2583
+ }))
2584
+ })).filter((server) => server.tools.length > 0);
2585
+ const result = { servers: filteredServers };
2586
+ return {
2587
+ content: [{
2588
+ type: "text",
2589
+ text: JSON.stringify(result, null, 2)
2590
+ }],
2591
+ isError: filteredServers.length === 0 ? true : void 0
2592
+ };
2593
+ }
2594
+ };
2595
+
2138
2596
  //#endregion
2139
2597
  //#region src/tools/UseToolTool.ts
2140
2598
  /**
@@ -2154,6 +2612,7 @@ var UseToolTool = class UseToolTool {
2154
2612
  static TOOL_NAME = "use_tool";
2155
2613
  clientManager;
2156
2614
  skillService;
2615
+ definitionsCacheService;
2157
2616
  /** Unique server identifier for this one-mcp instance */
2158
2617
  serverId;
2159
2618
  /**
@@ -2162,9 +2621,10 @@ var UseToolTool = class UseToolTool {
2162
2621
  * @param skillService - Optional skill service for loading and executing skills
2163
2622
  * @param serverId - Unique server identifier for this one-mcp instance
2164
2623
  */
2165
- constructor(clientManager, skillService, serverId) {
2624
+ constructor(clientManager, skillService, serverId, definitionsCacheService) {
2166
2625
  this.clientManager = clientManager;
2167
2626
  this.skillService = skillService;
2627
+ this.definitionsCacheService = definitionsCacheService || new DefinitionsCacheService(clientManager, skillService);
2168
2628
  this.serverId = serverId || DEFAULT_SERVER_ID;
2169
2629
  }
2170
2630
  /**
@@ -2224,17 +2684,9 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
2224
2684
  * @param skillName - The skill name to search for
2225
2685
  * @returns PromptSkillMatch if found, undefined otherwise
2226
2686
  */
2227
- findPromptSkill(skillName) {
2687
+ async findPromptSkill(skillName) {
2228
2688
  if (!skillName) return void 0;
2229
- const clients = this.clientManager.getAllClients();
2230
- for (const client of clients) {
2231
- if (!client.prompts) continue;
2232
- for (const [promptName, promptConfig] of Object.entries(client.prompts)) if (promptConfig.skill && promptConfig.skill.name === skillName) return {
2233
- serverName: client.serverName,
2234
- promptName,
2235
- skill: promptConfig.skill
2236
- };
2237
- }
2689
+ return await this.definitionsCacheService.getPromptSkillByName(skillName);
2238
2690
  }
2239
2691
  /**
2240
2692
  * Returns guidance message for prompt-based skill invocation.
@@ -2274,7 +2726,7 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
2274
2726
  const skill = await this.skillService.getSkill(skillName);
2275
2727
  if (skill) return this.executeSkill(skill);
2276
2728
  }
2277
- const promptSkill = this.findPromptSkill(skillName);
2729
+ const promptSkill = await this.findPromptSkill(skillName);
2278
2730
  if (promptSkill) return this.executePromptSkill(promptSkill);
2279
2731
  return {
2280
2732
  content: [{
@@ -2284,53 +2736,34 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
2284
2736
  isError: true
2285
2737
  };
2286
2738
  }
2287
- const clients = this.clientManager.getAllClients();
2739
+ const knownServerNames = this.clientManager.getKnownServerNames();
2288
2740
  const { serverName, actualToolName } = parseToolName(inputToolName);
2289
- if (serverName) {
2290
- const client$1 = this.clientManager.getClient(serverName);
2291
- if (!client$1) return {
2741
+ if (serverName) try {
2742
+ const client = await this.clientManager.ensureConnected(serverName);
2743
+ if (client.toolBlacklist?.includes(actualToolName)) return {
2292
2744
  content: [{
2293
2745
  type: "text",
2294
- text: `Server "${serverName}" not found. Available servers: ${clients.map((c) => c.serverName).join(", ")}`
2746
+ text: `Tool "${actualToolName}" is blacklisted on server "${serverName}" and cannot be executed.`
2295
2747
  }],
2296
2748
  isError: true
2297
2749
  };
2298
- if (client$1.toolBlacklist?.includes(actualToolName)) return {
2750
+ return await client.callTool(actualToolName, toolArgs);
2751
+ } catch (error) {
2752
+ return {
2299
2753
  content: [{
2300
2754
  type: "text",
2301
- text: `Tool "${actualToolName}" is blacklisted on server "${serverName}" and cannot be executed.`
2755
+ text: `Failed to call tool "${actualToolName}" on server "${serverName}". Available servers: ${knownServerNames.join(", ")}. ${error instanceof Error ? error.message : "Unknown error"}`
2302
2756
  }],
2303
2757
  isError: true
2304
2758
  };
2305
- try {
2306
- return await client$1.callTool(actualToolName, toolArgs);
2307
- } catch (error) {
2308
- return {
2309
- content: [{
2310
- type: "text",
2311
- text: `Failed to call tool "${actualToolName}" on server "${serverName}": ${error instanceof Error ? error.message : "Unknown error"}`
2312
- }],
2313
- isError: true
2314
- };
2315
- }
2316
2759
  }
2317
- const matchingServers = [];
2318
- const results = await Promise.all(clients.map(async (client$1) => {
2319
- try {
2320
- if (client$1.toolBlacklist?.includes(actualToolName)) return null;
2321
- if ((await client$1.listTools()).some((t) => t.name === actualToolName)) return client$1.serverName;
2322
- } catch (error) {
2323
- console.error(`Failed to list tools from ${client$1.serverName}:`, error);
2324
- }
2325
- return null;
2326
- }));
2327
- matchingServers.push(...results.filter((r) => r !== null));
2760
+ const matchingServers = await this.definitionsCacheService.getServersForTool(actualToolName);
2328
2761
  if (matchingServers.length === 0) {
2329
2762
  if (this.skillService) {
2330
2763
  const skill = await this.skillService.getSkill(actualToolName);
2331
2764
  if (skill) return this.executeSkill(skill);
2332
2765
  }
2333
- const promptSkill = this.findPromptSkill(actualToolName);
2766
+ const promptSkill = await this.findPromptSkill(actualToolName);
2334
2767
  if (promptSkill) return this.executePromptSkill(promptSkill);
2335
2768
  return {
2336
2769
  content: [{
@@ -2347,17 +2780,9 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
2347
2780
  }],
2348
2781
  isError: true
2349
2782
  };
2350
- const targetServerName = matchingServers[0];
2351
- const client = this.clientManager.getClient(targetServerName);
2352
- if (!client) return {
2353
- content: [{
2354
- type: "text",
2355
- text: `Internal error: Server "${targetServerName}" was found but is not connected`
2356
- }],
2357
- isError: true
2358
- };
2359
2783
  try {
2360
- return await client.callTool(actualToolName, toolArgs);
2784
+ const targetServerName = matchingServers[0];
2785
+ return await (await this.clientManager.ensureConnected(targetServerName)).callTool(actualToolName, toolArgs);
2361
2786
  } catch (error) {
2362
2787
  return {
2363
2788
  content: [{
@@ -2379,6 +2804,10 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
2379
2804
  }
2380
2805
  };
2381
2806
 
2807
+ //#endregion
2808
+ //#region package.json
2809
+ var version = "0.3.12";
2810
+
2382
2811
  //#endregion
2383
2812
  //#region src/server/index.ts
2384
2813
  /**
@@ -2394,17 +2823,110 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
2394
2823
  * - Keep server setup modular and extensible
2395
2824
  * - Import tools from ../tools/ and register them in the handlers
2396
2825
  */
2826
+ function summarizeServerTools(serverDefinition) {
2827
+ const toolNames = serverDefinition.tools.map((tool) => tool.name);
2828
+ const capabilities = getUniqueSortedCapabilities(serverDefinition.tools);
2829
+ const capabilitySummary = capabilities.length > 0 ? `; capabilities: ${capabilities.join(", ")}` : "";
2830
+ if (toolNames.length === 0) return `${serverDefinition.serverName} (no tools cached${capabilitySummary})`;
2831
+ return `${serverDefinition.serverName} (${toolNames.join(", ")})${capabilitySummary}`;
2832
+ }
2833
+ function buildFlatToolDescription(serverDefinition, tool) {
2834
+ const parts = [`Proxied from server "${serverDefinition.serverName}" as tool "${tool.name}".`];
2835
+ if (serverDefinition.serverInstruction) parts.push(`Server summary: ${serverDefinition.serverInstruction}`);
2836
+ if (tool.description && !serverDefinition.omitToolDescription) parts.push(tool.description);
2837
+ const capabilities = getToolCapabilities(tool);
2838
+ if (capabilities.length > 0) parts.push(`Capabilities: ${capabilities.join(", ")}`);
2839
+ return parts.join("\n\n");
2840
+ }
2841
+ function buildFlatToolDefinitions(serverDefinitions) {
2842
+ const toolToServers = /* @__PURE__ */ new Map();
2843
+ for (const serverDefinition of serverDefinitions) for (const tool of serverDefinition.tools) {
2844
+ if (!toolToServers.has(tool.name)) toolToServers.set(tool.name, []);
2845
+ toolToServers.get(tool.name)?.push(serverDefinition.serverName);
2846
+ }
2847
+ const definitions = [];
2848
+ for (const serverDefinition of serverDefinitions) for (const tool of serverDefinition.tools) {
2849
+ const hasClash = (toolToServers.get(tool.name) || []).length > 1;
2850
+ definitions.push({
2851
+ name: hasClash ? `${serverDefinition.serverName}__${tool.name}` : tool.name,
2852
+ description: buildFlatToolDescription(serverDefinition, tool),
2853
+ inputSchema: tool.inputSchema,
2854
+ _meta: tool._meta
2855
+ });
2856
+ }
2857
+ return definitions;
2858
+ }
2859
+ async function hasAnySkills(definitionsCacheService, skillService) {
2860
+ const [fileSkills, serverDefinitions] = await Promise.all([skillService ? skillService.getSkills() : definitionsCacheService.getCachedFileSkills(), definitionsCacheService.getServerDefinitions()]);
2861
+ return fileSkills.length > 0 || serverDefinitions.some((server) => server.promptSkills.length > 0);
2862
+ }
2863
+ function buildSkillsDescribeDefinition(serverDefinitions, serverId) {
2864
+ const proxySummary = serverDefinitions.length > 0 ? serverDefinitions.map(summarizeServerTools).join("; ") : "No proxied servers available.";
2865
+ return {
2866
+ name: DescribeToolsTool.TOOL_NAME,
2867
+ 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}".`,
2868
+ inputSchema: {
2869
+ type: "object",
2870
+ properties: { toolNames: {
2871
+ type: "array",
2872
+ items: {
2873
+ type: "string",
2874
+ minLength: 1
2875
+ },
2876
+ description: "List of skill names to get detailed information about",
2877
+ minItems: 1
2878
+ } },
2879
+ required: ["toolNames"],
2880
+ additionalProperties: false
2881
+ }
2882
+ };
2883
+ }
2884
+ function buildSearchDescribeDefinition(serverDefinitions, serverId) {
2885
+ const summary = serverDefinitions.length > 0 ? serverDefinitions.map(summarizeServerTools).join("; ") : "No proxied servers available.";
2886
+ return {
2887
+ name: DescribeToolsTool.TOOL_NAME,
2888
+ 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}".`,
2889
+ inputSchema: {
2890
+ type: "object",
2891
+ properties: { toolNames: {
2892
+ type: "array",
2893
+ items: {
2894
+ type: "string",
2895
+ minLength: 1
2896
+ },
2897
+ description: "List of tool or skill names to get detailed information about",
2898
+ minItems: 1
2899
+ } },
2900
+ required: ["toolNames"],
2901
+ additionalProperties: false
2902
+ }
2903
+ };
2904
+ }
2905
+ function buildProxyInstructions(serverDefinitions, mode, includeSkillsTool) {
2906
+ const summary = serverDefinitions.length > 0 ? serverDefinitions.map(summarizeServerTools).join("; ") : "No proxied servers available.";
2907
+ if (mode === "flat") return [
2908
+ "one-mcp proxies downstream MCP servers and exposes their tools and resources directly.",
2909
+ `Proxied servers and tools: ${summary}`,
2910
+ 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."
2911
+ ].join("\n\n");
2912
+ if (mode === "search") return [
2913
+ "one-mcp proxies downstream MCP servers in search mode.",
2914
+ `Proxied servers and tools: ${summary}`,
2915
+ "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."
2916
+ ].join("\n\n");
2917
+ return [
2918
+ "one-mcp proxies downstream MCP servers in meta mode.",
2919
+ `Proxied servers and tools: ${summary}`,
2920
+ "Use describe_tools to inspect capabilities and use_tool to execute them."
2921
+ ].join("\n\n");
2922
+ }
2397
2923
  async function createServer(options) {
2398
- const server = new __modelcontextprotocol_sdk_server_index_js.Server({
2399
- name: "@agiflowai/one-mcp",
2400
- version: "0.1.0"
2401
- }, { capabilities: {
2402
- tools: {},
2403
- prompts: {}
2404
- } });
2405
2924
  const clientManager = new McpClientManagerService();
2406
2925
  let configSkills;
2407
2926
  let configId;
2927
+ let configHash;
2928
+ let effectiveDefinitionsCachePath;
2929
+ let shouldStartFromCache = false;
2408
2930
  if (options?.configFilePath) {
2409
2931
  let config;
2410
2932
  try {
@@ -2417,38 +2939,92 @@ async function createServer(options) {
2417
2939
  }
2418
2940
  configSkills = config.skills;
2419
2941
  configId = config.id;
2420
- const failedConnections = [];
2421
- const connectionPromises = Object.entries(config.mcpServers).map(async ([serverName, serverConfig]) => {
2422
- try {
2423
- await clientManager.connectToServer(serverName, serverConfig);
2424
- console.error(`Connected to MCP server: ${serverName}`);
2425
- } catch (error) {
2426
- const err = error instanceof Error ? error : new Error(String(error));
2427
- failedConnections.push({
2428
- serverName,
2429
- error: err
2430
- });
2431
- console.error(`Failed to connect to ${serverName}:`, error);
2432
- }
2433
- });
2434
- await Promise.all(connectionPromises);
2435
- 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(", ")}`);
2436
- 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(", ")}`);
2942
+ configHash = DefinitionsCacheService.generateConfigHash(config);
2943
+ effectiveDefinitionsCachePath = options.definitionsCachePath || DefinitionsCacheService.getDefaultCachePath(options.configFilePath);
2944
+ clientManager.registerServerConfigs(config.mcpServers);
2945
+ if (options.clearDefinitionsCache && effectiveDefinitionsCachePath) {
2946
+ await DefinitionsCacheService.clearFile(effectiveDefinitionsCachePath);
2947
+ console.error(`[definitions-cache] Cleared ${effectiveDefinitionsCachePath}`);
2948
+ }
2949
+ if (effectiveDefinitionsCachePath) try {
2950
+ const cacheData = await DefinitionsCacheService.readFromFile(effectiveDefinitionsCachePath);
2951
+ if (DefinitionsCacheService.isCacheValid(cacheData, {
2952
+ configHash,
2953
+ oneMcpVersion: version
2954
+ })) shouldStartFromCache = true;
2955
+ } catch {}
2956
+ if (!shouldStartFromCache) {
2957
+ const failedConnections = [];
2958
+ const connectionPromises = Object.entries(config.mcpServers).map(async ([serverName, serverConfig]) => {
2959
+ try {
2960
+ await clientManager.connectToServer(serverName, serverConfig);
2961
+ console.error(`Connected to MCP server: ${serverName}`);
2962
+ } catch (error) {
2963
+ const err = error instanceof Error ? error : new Error(String(error));
2964
+ failedConnections.push({
2965
+ serverName,
2966
+ error: err
2967
+ });
2968
+ console.error(`Failed to connect to ${serverName}:`, error);
2969
+ }
2970
+ });
2971
+ await Promise.all(connectionPromises);
2972
+ 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(", ")}`);
2973
+ 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(", ")}`);
2974
+ } else console.error(`[definitions-cache] Using cached definitions from ${effectiveDefinitionsCachePath}`);
2437
2975
  }
2438
2976
  const serverId = options?.serverId || configId || generateServerId();
2439
2977
  console.error(`[one-mcp] Server ID: ${serverId}`);
2440
- const skillsConfig = options?.skills || configSkills;
2978
+ const skillPaths = (options?.skills || configSkills)?.paths ?? [];
2441
2979
  const toolsRef = { describeTools: null };
2442
- const skillService = skillsConfig && skillsConfig.paths.length > 0 ? new SkillService(process.cwd(), skillsConfig.paths, { onCacheInvalidated: () => {
2980
+ const skillService = skillPaths.length > 0 ? new SkillService(process.cwd(), skillPaths, { onCacheInvalidated: () => {
2443
2981
  toolsRef.describeTools?.clearAutoDetectedSkillsCache();
2444
2982
  } }) : void 0;
2445
- const describeTools = new DescribeToolsTool(clientManager, skillService, serverId);
2446
- const useTool = new UseToolTool(clientManager, skillService, serverId);
2983
+ let definitionsCacheService;
2984
+ if (effectiveDefinitionsCachePath) try {
2985
+ const cacheData = await DefinitionsCacheService.readFromFile(effectiveDefinitionsCachePath);
2986
+ if (DefinitionsCacheService.isCacheValid(cacheData, {
2987
+ configHash,
2988
+ oneMcpVersion: version
2989
+ })) definitionsCacheService = new DefinitionsCacheService(clientManager, skillService, { cacheData });
2990
+ else definitionsCacheService = new DefinitionsCacheService(clientManager, skillService);
2991
+ } catch (error) {
2992
+ console.error(`[definitions-cache] Failed to load ${effectiveDefinitionsCachePath}, falling back to live discovery: ${error instanceof Error ? error.message : "Unknown error"}`);
2993
+ definitionsCacheService = new DefinitionsCacheService(clientManager, skillService);
2994
+ }
2995
+ else definitionsCacheService = new DefinitionsCacheService(clientManager, skillService);
2996
+ const describeTools = new DescribeToolsTool(clientManager, skillService, serverId, definitionsCacheService);
2997
+ const useToolWithCache = new UseToolTool(clientManager, skillService, serverId, definitionsCacheService);
2998
+ const searchListTools = new SearchListToolsTool(clientManager, definitionsCacheService);
2447
2999
  toolsRef.describeTools = describeTools;
3000
+ const serverDefinitions = await definitionsCacheService.getServerDefinitions();
3001
+ const includeSkillsTool = await hasAnySkills(definitionsCacheService, skillService);
3002
+ const proxyMode = options?.proxyMode || "meta";
3003
+ const server = new __modelcontextprotocol_sdk_server_index_js.Server({
3004
+ name: "@agiflowai/one-mcp",
3005
+ version: "0.1.0"
3006
+ }, {
3007
+ capabilities: {
3008
+ tools: {},
3009
+ resources: {},
3010
+ prompts: {}
3011
+ },
3012
+ instructions: buildProxyInstructions(serverDefinitions, proxyMode, includeSkillsTool)
3013
+ });
2448
3014
  if (skillService) skillService.startWatching().catch((error) => {
2449
3015
  console.error(`[skill-watcher] File watcher failed (non-critical): ${error instanceof Error ? error.message : "Unknown error"}`);
2450
3016
  });
2451
- server.setRequestHandler(__modelcontextprotocol_sdk_types_js.ListToolsRequestSchema, async () => ({ tools: [await describeTools.getDefinition(), useTool.getDefinition()] }));
3017
+ server.setRequestHandler(__modelcontextprotocol_sdk_types_js.ListToolsRequestSchema, async () => ({ tools: proxyMode === "flat" ? await (async () => {
3018
+ const currentServerDefinitions = await definitionsCacheService.getServerDefinitions();
3019
+ const shouldIncludeSkillsTool = await hasAnySkills(definitionsCacheService, skillService);
3020
+ return [...buildFlatToolDefinitions(currentServerDefinitions), ...shouldIncludeSkillsTool ? [buildSkillsDescribeDefinition(currentServerDefinitions, serverId)] : []];
3021
+ })() : proxyMode === "search" ? await (async () => {
3022
+ return [
3023
+ buildSearchDescribeDefinition(await definitionsCacheService.getServerDefinitions(), serverId),
3024
+ await searchListTools.getDefinition(),
3025
+ useToolWithCache.getDefinition()
3026
+ ];
3027
+ })() : [await describeTools.getDefinition(), useToolWithCache.getDefinition()] }));
2452
3028
  server.setRequestHandler(__modelcontextprotocol_sdk_types_js.CallToolRequestSchema, async (request) => {
2453
3029
  const { name, arguments: args } = request.params;
2454
3030
  if (name === DescribeToolsTool.TOOL_NAME) try {
@@ -2457,36 +3033,65 @@ async function createServer(options) {
2457
3033
  throw new Error(`Failed to execute ${name}: ${error instanceof Error ? error.message : String(error)}`);
2458
3034
  }
2459
3035
  if (name === UseToolTool.TOOL_NAME) try {
2460
- return await useTool.execute(args);
3036
+ return await useToolWithCache.execute(args);
3037
+ } catch (error) {
3038
+ throw new Error(`Failed to execute ${name}: ${error instanceof Error ? error.message : String(error)}`);
3039
+ }
3040
+ if (name === SearchListToolsTool.TOOL_NAME && proxyMode === "search") try {
3041
+ return await searchListTools.execute(args);
2461
3042
  } catch (error) {
2462
3043
  throw new Error(`Failed to execute ${name}: ${error instanceof Error ? error.message : String(error)}`);
2463
3044
  }
3045
+ if (proxyMode === "flat") return await useToolWithCache.execute({
3046
+ toolName: name,
3047
+ toolArgs: args || {}
3048
+ });
2464
3049
  throw new Error(`Unknown tool: ${name}`);
2465
3050
  });
3051
+ server.setRequestHandler(__modelcontextprotocol_sdk_types_js.ListResourcesRequestSchema, async () => {
3052
+ const serverDefinitions$1 = await definitionsCacheService.getServerDefinitions();
3053
+ const resourceToServers = /* @__PURE__ */ new Map();
3054
+ for (const serverDefinition of serverDefinitions$1) for (const resource of serverDefinition.resources) {
3055
+ if (!resourceToServers.has(resource.uri)) resourceToServers.set(resource.uri, []);
3056
+ resourceToServers.get(resource.uri)?.push(serverDefinition.serverName);
3057
+ }
3058
+ const resources = [];
3059
+ for (const serverDefinition of serverDefinitions$1) for (const resource of serverDefinition.resources) {
3060
+ const hasClash = (resourceToServers.get(resource.uri) || []).length > 1;
3061
+ resources.push({
3062
+ ...resource,
3063
+ uri: hasClash ? `${serverDefinition.serverName}__${resource.uri}` : resource.uri
3064
+ });
3065
+ }
3066
+ return { resources };
3067
+ });
3068
+ server.setRequestHandler(__modelcontextprotocol_sdk_types_js.ReadResourceRequestSchema, async (request) => {
3069
+ const { uri } = request.params;
3070
+ const { serverName, actualToolName: actualUri } = parseToolName(uri);
3071
+ if (serverName) return await (await clientManager.ensureConnected(serverName)).readResource(actualUri);
3072
+ const matchingServers = await definitionsCacheService.getServersForResource(actualUri);
3073
+ if (matchingServers.length === 0) throw new Error(`Resource not found: ${uri}`);
3074
+ 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.`);
3075
+ return await (await clientManager.ensureConnected(matchingServers[0])).readResource(actualUri);
3076
+ });
2466
3077
  server.setRequestHandler(__modelcontextprotocol_sdk_types_js.ListPromptsRequestSchema, async () => {
2467
- const clients = clientManager.getAllClients();
3078
+ const serverDefinitions$1 = await definitionsCacheService.getServerDefinitions();
2468
3079
  const promptToServers = /* @__PURE__ */ new Map();
2469
3080
  const serverPromptsMap = /* @__PURE__ */ new Map();
2470
- await Promise.all(clients.map(async (client) => {
2471
- try {
2472
- const prompts = await client.listPrompts();
2473
- serverPromptsMap.set(client.serverName, prompts);
2474
- for (const prompt of prompts) {
2475
- if (!promptToServers.has(prompt.name)) promptToServers.set(prompt.name, []);
2476
- promptToServers.get(prompt.name).push(client.serverName);
2477
- }
2478
- } catch (error) {
2479
- console.error(`Failed to list prompts from ${client.serverName}:`, error);
2480
- serverPromptsMap.set(client.serverName, []);
3081
+ for (const serverDefinition of serverDefinitions$1) {
3082
+ serverPromptsMap.set(serverDefinition.serverName, serverDefinition.prompts);
3083
+ for (const prompt of serverDefinition.prompts) {
3084
+ if (!promptToServers.has(prompt.name)) promptToServers.set(prompt.name, []);
3085
+ promptToServers.get(prompt.name).push(serverDefinition.serverName);
2481
3086
  }
2482
- }));
3087
+ }
2483
3088
  const aggregatedPrompts = [];
2484
- for (const client of clients) {
2485
- const prompts = serverPromptsMap.get(client.serverName) || [];
3089
+ for (const serverDefinition of serverDefinitions$1) {
3090
+ const prompts = serverPromptsMap.get(serverDefinition.serverName) || [];
2486
3091
  for (const prompt of prompts) {
2487
3092
  const hasClash = (promptToServers.get(prompt.name) || []).length > 1;
2488
3093
  aggregatedPrompts.push({
2489
- name: hasClash ? `${client.serverName}__${prompt.name}` : prompt.name,
3094
+ name: hasClash ? `${serverDefinition.serverName}__${prompt.name}` : prompt.name,
2490
3095
  description: prompt.description,
2491
3096
  arguments: prompt.arguments
2492
3097
  });
@@ -2496,27 +3101,27 @@ async function createServer(options) {
2496
3101
  });
2497
3102
  server.setRequestHandler(__modelcontextprotocol_sdk_types_js.GetPromptRequestSchema, async (request) => {
2498
3103
  const { name, arguments: args } = request.params;
2499
- const clients = clientManager.getAllClients();
3104
+ const serverDefinitions$1 = await definitionsCacheService.getServerDefinitions();
2500
3105
  const { serverName, actualToolName: actualPromptName } = parseToolName(name);
2501
- if (serverName) {
2502
- const client$1 = clientManager.getClient(serverName);
2503
- if (!client$1) throw new Error(`Server not found: ${serverName}`);
2504
- return await client$1.getPrompt(actualPromptName, args);
2505
- }
3106
+ if (serverName) return await (await clientManager.ensureConnected(serverName)).getPrompt(actualPromptName, args);
2506
3107
  const serversWithPrompt = [];
2507
- await Promise.all(clients.map(async (client$1) => {
2508
- try {
2509
- if ((await client$1.listPrompts()).some((p) => p.name === name)) serversWithPrompt.push(client$1.serverName);
2510
- } catch (error) {
2511
- console.error(`Failed to list prompts from ${client$1.serverName}:`, error);
2512
- }
2513
- }));
3108
+ for (const serverDefinition of serverDefinitions$1) if (serverDefinition.prompts.some((prompt) => prompt.name === name)) serversWithPrompt.push(serverDefinition.serverName);
2514
3109
  if (serversWithPrompt.length === 0) throw new Error(`Prompt not found: ${name}`);
2515
3110
  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.`);
2516
3111
  const client = clientManager.getClient(serversWithPrompt[0]);
2517
- if (!client) throw new Error(`Server not found: ${serversWithPrompt[0]}`);
3112
+ if (!client) return await (await clientManager.ensureConnected(serversWithPrompt[0])).getPrompt(name, args);
2518
3113
  return await client.getPrompt(name, args);
2519
3114
  });
3115
+ if (!shouldStartFromCache && effectiveDefinitionsCachePath && options?.configFilePath) definitionsCacheService.collectForCache({
3116
+ configPath: options.configFilePath,
3117
+ configHash,
3118
+ oneMcpVersion: version,
3119
+ serverId
3120
+ }).then((definitionsCache) => DefinitionsCacheService.writeToFile(effectiveDefinitionsCachePath, definitionsCache)).then(() => {
3121
+ console.error(`[definitions-cache] Wrote ${effectiveDefinitionsCachePath}`);
3122
+ }).catch((error) => {
3123
+ console.error(`[definitions-cache] Failed to persist ${effectiveDefinitionsCachePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
3124
+ });
2520
3125
  return server;
2521
3126
  }
2522
3127
 
@@ -2648,14 +3253,14 @@ var SseTransportHandler = class {
2648
3253
  }
2649
3254
  }
2650
3255
  async start() {
2651
- return new Promise((resolve$1, reject) => {
3256
+ return new Promise((resolve$2, reject) => {
2652
3257
  try {
2653
3258
  this.server = this.app.listen(this.config.port, this.config.host, () => {
2654
3259
  console.error(`@agiflowai/one-mcp MCP server started with SSE transport on http://${this.config.host}:${this.config.port}`);
2655
3260
  console.error(`SSE endpoint: http://${this.config.host}:${this.config.port}/sse`);
2656
3261
  console.error(`Messages endpoint: http://${this.config.host}:${this.config.port}/messages`);
2657
3262
  console.error(`Health check: http://${this.config.host}:${this.config.port}/health`);
2658
- resolve$1();
3263
+ resolve$2();
2659
3264
  });
2660
3265
  this.server.on("error", (error) => {
2661
3266
  reject(error);
@@ -2666,17 +3271,17 @@ var SseTransportHandler = class {
2666
3271
  });
2667
3272
  }
2668
3273
  async stop() {
2669
- return new Promise((resolve$1, reject) => {
3274
+ return new Promise((resolve$2, reject) => {
2670
3275
  if (this.server) {
2671
3276
  this.sessionManager.clear();
2672
3277
  this.server.close((err) => {
2673
3278
  if (err) reject(err);
2674
3279
  else {
2675
3280
  this.server = null;
2676
- resolve$1();
3281
+ resolve$2();
2677
3282
  }
2678
3283
  });
2679
- } else resolve$1();
3284
+ } else resolve$2();
2680
3285
  });
2681
3286
  }
2682
3287
  getPort() {
@@ -2828,12 +3433,12 @@ var HttpTransportHandler = class {
2828
3433
  this.sessionManager.deleteSession(sessionId);
2829
3434
  }
2830
3435
  async start() {
2831
- return new Promise((resolve$1, reject) => {
3436
+ return new Promise((resolve$2, reject) => {
2832
3437
  try {
2833
3438
  this.server = this.app.listen(this.config.port, this.config.host, () => {
2834
3439
  console.error(`@agiflowai/one-mcp MCP server started on http://${this.config.host}:${this.config.port}/mcp`);
2835
3440
  console.error(`Health check: http://${this.config.host}:${this.config.port}/health`);
2836
- resolve$1();
3441
+ resolve$2();
2837
3442
  });
2838
3443
  this.server.on("error", (error) => {
2839
3444
  reject(error);
@@ -2844,17 +3449,17 @@ var HttpTransportHandler = class {
2844
3449
  });
2845
3450
  }
2846
3451
  async stop() {
2847
- return new Promise((resolve$1, reject) => {
3452
+ return new Promise((resolve$2, reject) => {
2848
3453
  if (this.server) {
2849
3454
  this.sessionManager.clear();
2850
3455
  this.server.close((err) => {
2851
3456
  if (err) reject(err);
2852
3457
  else {
2853
3458
  this.server = null;
2854
- resolve$1();
3459
+ resolve$2();
2855
3460
  }
2856
3461
  });
2857
- } else resolve$1();
3462
+ } else resolve$2();
2858
3463
  });
2859
3464
  }
2860
3465
  getPort() {
@@ -2872,6 +3477,12 @@ Object.defineProperty(exports, 'ConfigFetcherService', {
2872
3477
  return ConfigFetcherService;
2873
3478
  }
2874
3479
  });
3480
+ Object.defineProperty(exports, 'DefinitionsCacheService', {
3481
+ enumerable: true,
3482
+ get: function () {
3483
+ return DefinitionsCacheService;
3484
+ }
3485
+ });
2875
3486
  Object.defineProperty(exports, 'DescribeToolsTool', {
2876
3487
  enumerable: true,
2877
3488
  get: function () {
@@ -2890,6 +3501,12 @@ Object.defineProperty(exports, 'McpClientManagerService', {
2890
3501
  return McpClientManagerService;
2891
3502
  }
2892
3503
  });
3504
+ Object.defineProperty(exports, 'SearchListToolsTool', {
3505
+ enumerable: true,
3506
+ get: function () {
3507
+ return SearchListToolsTool;
3508
+ }
3509
+ });
2893
3510
  Object.defineProperty(exports, 'SkillService', {
2894
3511
  enumerable: true,
2895
3512
  get: function () {
@@ -2931,4 +3548,16 @@ Object.defineProperty(exports, 'findConfigFile', {
2931
3548
  get: function () {
2932
3549
  return findConfigFile;
2933
3550
  }
3551
+ });
3552
+ Object.defineProperty(exports, 'generateServerId', {
3553
+ enumerable: true,
3554
+ get: function () {
3555
+ return generateServerId;
3556
+ }
3557
+ });
3558
+ Object.defineProperty(exports, 'version', {
3559
+ enumerable: true,
3560
+ get: function () {
3561
+ return version;
3562
+ }
2934
3563
  });