@agiflowai/one-mcp 0.2.7 → 0.2.8

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.
@@ -231,6 +231,7 @@ const ClaudeCodeStdioServerSchema = zod.z.object({
231
231
  env: zod.z.record(zod.z.string(), zod.z.string()).optional(),
232
232
  disabled: zod.z.boolean().optional(),
233
233
  instruction: zod.z.string().optional(),
234
+ timeout: zod.z.number().positive().optional(),
234
235
  config: AdditionalConfigSchema
235
236
  });
236
237
  const ClaudeCodeHttpServerSchema = zod.z.object({
@@ -239,6 +240,7 @@ const ClaudeCodeHttpServerSchema = zod.z.object({
239
240
  type: zod.z.enum(["http", "sse"]).optional(),
240
241
  disabled: zod.z.boolean().optional(),
241
242
  instruction: zod.z.string().optional(),
243
+ timeout: zod.z.number().positive().optional(),
242
244
  config: AdditionalConfigSchema
243
245
  });
244
246
  const ClaudeCodeServerConfigSchema = zod.z.union([ClaudeCodeStdioServerSchema, ClaudeCodeHttpServerSchema]);
@@ -269,6 +271,7 @@ const SkillsConfigSchema = zod.z.object({ paths: zod.z.array(zod.z.string()) });
269
271
  * Full Claude Code MCP configuration schema
270
272
  */
271
273
  const ClaudeCodeMcpConfigSchema = zod.z.object({
274
+ id: zod.z.string().optional(),
272
275
  mcpServers: zod.z.record(zod.z.string(), ClaudeCodeServerConfigSchema),
273
276
  remoteConfigs: zod.z.array(RemoteConfigSourceSchema).optional(),
274
277
  skills: SkillsConfigSchema.optional()
@@ -309,6 +312,7 @@ const McpServerConfigSchema = zod.z.discriminatedUnion("transport", [
309
312
  toolBlacklist: zod.z.array(zod.z.string()).optional(),
310
313
  omitToolDescription: zod.z.boolean().optional(),
311
314
  prompts: zod.z.record(zod.z.string(), InternalPromptConfigSchema).optional(),
315
+ timeout: zod.z.number().positive().optional(),
312
316
  transport: zod.z.literal("stdio"),
313
317
  config: McpStdioConfigSchema
314
318
  }),
@@ -318,6 +322,7 @@ const McpServerConfigSchema = zod.z.discriminatedUnion("transport", [
318
322
  toolBlacklist: zod.z.array(zod.z.string()).optional(),
319
323
  omitToolDescription: zod.z.boolean().optional(),
320
324
  prompts: zod.z.record(zod.z.string(), InternalPromptConfigSchema).optional(),
325
+ timeout: zod.z.number().positive().optional(),
321
326
  transport: zod.z.literal("http"),
322
327
  config: McpHttpConfigSchema
323
328
  }),
@@ -327,6 +332,7 @@ const McpServerConfigSchema = zod.z.discriminatedUnion("transport", [
327
332
  toolBlacklist: zod.z.array(zod.z.string()).optional(),
328
333
  omitToolDescription: zod.z.boolean().optional(),
329
334
  prompts: zod.z.record(zod.z.string(), InternalPromptConfigSchema).optional(),
335
+ timeout: zod.z.number().positive().optional(),
330
336
  transport: zod.z.literal("sse"),
331
337
  config: McpSseConfigSchema
332
338
  })
@@ -335,6 +341,7 @@ const McpServerConfigSchema = zod.z.discriminatedUnion("transport", [
335
341
  * Full internal MCP configuration schema
336
342
  */
337
343
  const InternalMcpConfigSchema = zod.z.object({
344
+ id: zod.z.string().optional(),
338
345
  mcpServers: zod.z.record(zod.z.string(), McpServerConfigSchema),
339
346
  skills: SkillsConfigSchema.optional()
340
347
  });
@@ -360,6 +367,7 @@ function transformClaudeCodeConfig(claudeConfig) {
360
367
  toolBlacklist: stdioConfig.config?.toolBlacklist,
361
368
  omitToolDescription: stdioConfig.config?.omitToolDescription,
362
369
  prompts: stdioConfig.config?.prompts,
370
+ timeout: stdioConfig.timeout,
363
371
  transport: "stdio",
364
372
  config: {
365
373
  command: interpolatedCommand,
@@ -378,6 +386,7 @@ function transformClaudeCodeConfig(claudeConfig) {
378
386
  toolBlacklist: httpConfig.config?.toolBlacklist,
379
387
  omitToolDescription: httpConfig.config?.omitToolDescription,
380
388
  prompts: httpConfig.config?.prompts,
389
+ timeout: httpConfig.timeout,
381
390
  transport,
382
391
  config: {
383
392
  url: interpolatedUrl,
@@ -387,6 +396,7 @@ function transformClaudeCodeConfig(claudeConfig) {
387
396
  }
388
397
  }
389
398
  return {
399
+ id: claudeConfig.id,
390
400
  mcpServers: transformedServers,
391
401
  skills: claudeConfig.skills
392
402
  };
@@ -547,22 +557,21 @@ var RemoteConfigCacheService = class {
547
557
  try {
548
558
  if (!(0, node_fs.existsSync)(this.cacheDir)) return;
549
559
  const now = Date.now();
550
- const files = await (0, node_fs_promises.readdir)(this.cacheDir);
551
- let expiredCount = 0;
552
- for (const file of files) {
553
- if (!file.endsWith(".json")) continue;
560
+ const jsonFiles = (await (0, node_fs_promises.readdir)(this.cacheDir)).filter((file) => file.endsWith(".json"));
561
+ const expiredCount = (await Promise.all(jsonFiles.map(async (file) => {
554
562
  const filePath = (0, node_path.join)(this.cacheDir, file);
555
563
  try {
556
564
  const content = await (0, node_fs_promises.readFile)(filePath, "utf-8");
557
565
  if (now > JSON.parse(content).expiresAt) {
558
566
  await (0, node_fs_promises.unlink)(filePath);
559
- expiredCount++;
567
+ return true;
560
568
  }
569
+ return false;
561
570
  } catch (error) {
562
571
  await (0, node_fs_promises.unlink)(filePath).catch(() => {});
563
- expiredCount++;
572
+ return true;
564
573
  }
565
- }
574
+ }))).filter(Boolean).length;
566
575
  if (expiredCount > 0) console.error(`Cleaned up ${expiredCount} expired remote config cache entries`);
567
576
  } catch (error) {
568
577
  console.error("Failed to clean expired remote config cache:", error);
@@ -578,14 +587,15 @@ var RemoteConfigCacheService = class {
578
587
  totalSize: 0
579
588
  };
580
589
  const jsonFiles = (await (0, node_fs_promises.readdir)(this.cacheDir)).filter((file) => file.endsWith(".json"));
581
- let totalSize = 0;
582
- for (const file of jsonFiles) {
590
+ const totalSize = (await Promise.all(jsonFiles.map(async (file) => {
583
591
  const filePath = (0, node_path.join)(this.cacheDir, file);
584
592
  try {
585
593
  const content = await (0, node_fs_promises.readFile)(filePath, "utf-8");
586
- totalSize += Buffer.byteLength(content, "utf-8");
587
- } catch {}
588
- }
594
+ return Buffer.byteLength(content, "utf-8");
595
+ } catch {
596
+ return 0;
597
+ }
598
+ }))).reduce((sum, size) => sum + size, 0);
589
599
  return {
590
600
  totalEntries: jsonFiles.length,
591
601
  totalSize
@@ -836,6 +846,8 @@ var ConfigFetcherService = class {
836
846
 
837
847
  //#endregion
838
848
  //#region src/services/McpClientManagerService.ts
849
+ /** Default connection timeout in milliseconds (30 seconds) */
850
+ const DEFAULT_CONNECTION_TIMEOUT_MS = 3e4;
839
851
  /**
840
852
  * MCP Client wrapper for managing individual server connections
841
853
  * This is an internal class used by McpClientManagerService
@@ -941,8 +953,10 @@ var McpClientManagerService = class {
941
953
  }
942
954
  /**
943
955
  * Connect to an MCP server based on its configuration with timeout
956
+ * Uses the timeout from server config, falling back to default (30s)
944
957
  */
945
- async connectToServer(serverName, config, timeoutMs = 1e4) {
958
+ async connectToServer(serverName, config) {
959
+ const timeoutMs = config.timeout ?? DEFAULT_CONNECTION_TIMEOUT_MS;
946
960
  if (this.clients.has(serverName)) throw new Error(`Client for ${serverName} is already connected`);
947
961
  const client = new __modelcontextprotocol_sdk_client_index_js.Client({
948
962
  name: `@agiflowai/one-mcp-client`,
@@ -1237,6 +1251,71 @@ function extractSkillFrontMatter(content) {
1237
1251
  return null;
1238
1252
  }
1239
1253
 
1254
+ //#endregion
1255
+ //#region src/utils/generateServerId.ts
1256
+ /**
1257
+ * generateServerId Utilities
1258
+ *
1259
+ * DESIGN PATTERNS:
1260
+ * - Pure functions with no side effects
1261
+ * - Single responsibility per function
1262
+ * - Functional programming approach
1263
+ *
1264
+ * CODING STANDARDS:
1265
+ * - Export individual functions, not classes
1266
+ * - Use descriptive function names with verbs
1267
+ * - Add JSDoc comments for complex logic
1268
+ * - Keep functions small and focused
1269
+ *
1270
+ * AVOID:
1271
+ * - Side effects (mutating external state)
1272
+ * - Stateful logic (use services for state)
1273
+ * - Complex external dependencies
1274
+ */
1275
+ /**
1276
+ * Character set for generating human-readable IDs.
1277
+ * Excludes confusing characters: 0, O, 1, l, I
1278
+ */
1279
+ const CHARSET = "23456789abcdefghjkmnpqrstuvwxyz";
1280
+ /**
1281
+ * Default length for generated server IDs (6 characters)
1282
+ */
1283
+ const DEFAULT_ID_LENGTH = 6;
1284
+ /**
1285
+ * Generate a short, human-readable server ID.
1286
+ *
1287
+ * Uses Node.js crypto.randomBytes for cryptographically secure randomness
1288
+ * with rejection sampling to avoid modulo bias.
1289
+ *
1290
+ * The generated ID:
1291
+ * - Is 6 characters long by default
1292
+ * - Uses only lowercase alphanumeric characters
1293
+ * - Excludes confusing characters (0, O, 1, l, I)
1294
+ *
1295
+ * @param length - Length of the ID to generate (default: 6)
1296
+ * @returns A random, human-readable ID
1297
+ *
1298
+ * @example
1299
+ * generateServerId() // "abc234"
1300
+ * generateServerId(4) // "x7mn"
1301
+ */
1302
+ function generateServerId(length = DEFAULT_ID_LENGTH) {
1303
+ const charsetLength = 31;
1304
+ const maxUnbiased = Math.floor(256 / charsetLength) * charsetLength - 1;
1305
+ let result = "";
1306
+ let remaining = length;
1307
+ while (remaining > 0) {
1308
+ const bytes = (0, node_crypto.randomBytes)(remaining);
1309
+ for (let i = 0; i < bytes.length && remaining > 0; i++) {
1310
+ const byte = bytes[i];
1311
+ if (byte > maxUnbiased) continue;
1312
+ result += CHARSET[byte % charsetLength];
1313
+ remaining--;
1314
+ }
1315
+ }
1316
+ return result;
1317
+ }
1318
+
1240
1319
  //#endregion
1241
1320
  //#region src/services/SkillService.ts
1242
1321
  /**
@@ -1337,13 +1416,13 @@ var SkillService = class {
1337
1416
  if (this.cachedSkills !== null) return this.cachedSkills;
1338
1417
  const skills = [];
1339
1418
  const loadedSkillNames = /* @__PURE__ */ new Set();
1340
- for (const skillPath of this.skillPaths) {
1419
+ const allDirSkills = await Promise.all(this.skillPaths.map(async (skillPath) => {
1341
1420
  const skillsDir = (0, node_path.isAbsolute)(skillPath) ? skillPath : (0, node_path.join)(this.cwd, skillPath);
1342
- const dirSkills = await this.loadSkillsFromDirectory(skillsDir, "project");
1343
- for (const skill of dirSkills) if (!loadedSkillNames.has(skill.name)) {
1344
- skills.push(skill);
1345
- loadedSkillNames.add(skill.name);
1346
- }
1421
+ return this.loadSkillsFromDirectory(skillsDir, "project");
1422
+ }));
1423
+ for (const dirSkills of allDirSkills) for (const skill of dirSkills) if (!loadedSkillNames.has(skill.name)) {
1424
+ skills.push(skill);
1425
+ loadedSkillNames.add(skill.name);
1347
1426
  }
1348
1427
  this.cachedSkills = skills;
1349
1428
  this.skillsByName = new Map(skills.map((skill) => [skill.name, skill]));
@@ -1381,9 +1460,15 @@ var SkillService = class {
1381
1460
  */
1382
1461
  async startWatching() {
1383
1462
  this.stopWatching();
1384
- for (const skillPath of this.skillPaths) {
1463
+ const existenceChecks = await Promise.all(this.skillPaths.map(async (skillPath) => {
1385
1464
  const skillsDir = (0, node_path.isAbsolute)(skillPath) ? skillPath : (0, node_path.join)(this.cwd, skillPath);
1386
- if (!await pathExists(skillsDir)) continue;
1465
+ return {
1466
+ skillsDir,
1467
+ exists: await pathExists(skillsDir)
1468
+ };
1469
+ }));
1470
+ for (const { skillsDir, exists } of existenceChecks) {
1471
+ if (!exists) continue;
1387
1472
  const abortController = new AbortController();
1388
1473
  this.watchers.push(abortController);
1389
1474
  this.watchDirectory(skillsDir, abortController.signal).catch((error) => {
@@ -1441,34 +1526,49 @@ var SkillService = class {
1441
1526
  } catch (error) {
1442
1527
  throw new SkillLoadError(`Failed to read skills directory: ${error instanceof Error ? error.message : "Unknown error"}`, dirPath, error instanceof Error ? error : void 0);
1443
1528
  }
1444
- for (const entry of entries) {
1529
+ const entryStats = await Promise.all(entries.map(async (entry) => {
1445
1530
  const entryPath = (0, node_path.join)(dirPath, entry);
1446
- let entryStat;
1447
1531
  try {
1448
- entryStat = await (0, node_fs_promises.stat)(entryPath);
1532
+ return {
1533
+ entry,
1534
+ entryPath,
1535
+ stat: await (0, node_fs_promises.stat)(entryPath),
1536
+ error: null
1537
+ };
1449
1538
  } catch (error) {
1450
1539
  console.warn(`Skipping entry ${entryPath}: ${error instanceof Error ? error.message : "Unknown error"}`);
1451
- continue;
1540
+ return {
1541
+ entry,
1542
+ entryPath,
1543
+ stat: null,
1544
+ error
1545
+ };
1452
1546
  }
1547
+ }));
1548
+ const skillFilesToLoad = [];
1549
+ for (const { entry, entryPath, stat: entryStat } of entryStats) {
1550
+ if (!entryStat) continue;
1453
1551
  if (entryStat.isDirectory()) {
1454
1552
  const skillFilePath = (0, node_path.join)(entryPath, "SKILL.md");
1455
- try {
1456
- if (await pathExists(skillFilePath)) {
1457
- const skill = await this.loadSkillFile(skillFilePath, location);
1458
- if (skill) skills.push(skill);
1459
- }
1460
- } catch (error) {
1461
- console.warn(`Skipping skill at ${skillFilePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
1462
- continue;
1463
- }
1464
- } else if (entry === "SKILL.md") try {
1465
- const skill = await this.loadSkillFile(entryPath, location);
1466
- if (skill) skills.push(skill);
1553
+ skillFilesToLoad.push({
1554
+ filePath: skillFilePath,
1555
+ isRootLevel: false
1556
+ });
1557
+ } else if (entry === "SKILL.md") skillFilesToLoad.push({
1558
+ filePath: entryPath,
1559
+ isRootLevel: true
1560
+ });
1561
+ }
1562
+ const loadResults = await Promise.all(skillFilesToLoad.map(async ({ filePath, isRootLevel }) => {
1563
+ try {
1564
+ if (!isRootLevel && !await pathExists(filePath)) return null;
1565
+ return await this.loadSkillFile(filePath, location);
1467
1566
  } catch (error) {
1468
- console.warn(`Skipping skill at ${entryPath}: ${error instanceof Error ? error.message : "Unknown error"}`);
1469
- continue;
1567
+ console.warn(`Skipping skill at ${filePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
1568
+ return null;
1470
1569
  }
1471
- }
1570
+ }));
1571
+ for (const skill of loadResults) if (skill) skills.push(skill);
1472
1572
  return skills;
1473
1573
  }
1474
1574
  /**
@@ -1514,7 +1614,7 @@ var SkillService = class {
1514
1614
  * Prefix added to skill names when they clash with MCP tool names.
1515
1615
  * This ensures skills can be uniquely identified even when a tool has the same name.
1516
1616
  */
1517
- const SKILL_PREFIX$1 = "skill__";
1617
+ const SKILL_PREFIX = "skill__";
1518
1618
  /**
1519
1619
  * Log prefix for skill detection messages.
1520
1620
  * Used to easily filter skill detection logs in stderr output.
@@ -1525,10 +1625,15 @@ const LOG_PREFIX_SKILL_DETECTION = "[skill-detection]";
1525
1625
  * Format: "prompt:{serverName}:{promptName}"
1526
1626
  */
1527
1627
  const PROMPT_LOCATION_PREFIX = "prompt:";
1628
+ /**
1629
+ * Default server ID used when no ID is provided via CLI or config.
1630
+ * This fallback is used when auto-generation also fails.
1631
+ */
1632
+ const DEFAULT_SERVER_ID = "unknown";
1528
1633
 
1529
1634
  //#endregion
1530
1635
  //#region src/templates/toolkit-description.liquid?raw
1531
- var toolkit_description_default = "<toolkit>\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";
1636
+ 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";
1532
1637
 
1533
1638
  //#endregion
1534
1639
  //#region src/tools/DescribeToolsTool.ts
@@ -1567,14 +1672,18 @@ var DescribeToolsTool = class DescribeToolsTool {
1567
1672
  liquid = new liquidjs.Liquid();
1568
1673
  /** Cache for auto-detected skills from prompt front-matter */
1569
1674
  autoDetectedSkillsCache = null;
1675
+ /** Unique server identifier for this one-mcp instance */
1676
+ serverId;
1570
1677
  /**
1571
1678
  * Creates a new DescribeToolsTool instance
1572
1679
  * @param clientManager - The MCP client manager for accessing remote servers
1573
1680
  * @param skillService - Optional skill service for loading skills
1681
+ * @param serverId - Unique server identifier for this one-mcp instance
1574
1682
  */
1575
- constructor(clientManager, skillService) {
1683
+ constructor(clientManager, skillService, serverId) {
1576
1684
  this.clientManager = clientManager;
1577
1685
  this.skillService = skillService;
1686
+ this.serverId = serverId || DEFAULT_SERVER_ID;
1578
1687
  }
1579
1688
  /**
1580
1689
  * Clears the cached auto-detected skills from prompt front-matter.
@@ -1601,44 +1710,42 @@ var DescribeToolsTool = class DescribeToolsTool {
1601
1710
  async detectSkillsFromPromptFrontMatter() {
1602
1711
  if (this.autoDetectedSkillsCache !== null) return this.autoDetectedSkillsCache;
1603
1712
  const clients = this.clientManager.getAllClients();
1604
- const autoDetectedSkills = [];
1605
1713
  let listPromptsFailures = 0;
1606
1714
  let fetchPromptFailures = 0;
1607
- const fetchPromises = [];
1608
- for (const client of clients) {
1715
+ const autoDetectedSkills = (await Promise.all(clients.map(async (client) => {
1716
+ const detectedSkills = [];
1609
1717
  const configuredPromptNames = new Set(client.prompts ? Object.keys(client.prompts) : []);
1610
- const listPromptsPromise = (async () => {
1611
- try {
1612
- const prompts = await client.listPrompts();
1613
- if (!prompts || prompts.length === 0) return;
1614
- const promptFetchPromises = prompts.map(async (promptInfo) => {
1615
- if (configuredPromptNames.has(promptInfo.name)) return;
1616
- try {
1617
- const skillExtraction = extractSkillFrontMatter(((await client.getPrompt(promptInfo.name)).messages || []).map((m) => {
1618
- const content = m.content;
1619
- if (typeof content === "string") return content;
1620
- if (content && typeof content === "object" && "text" in content) return String(content.text);
1621
- return "";
1622
- }).join("\n"));
1623
- if (skillExtraction) autoDetectedSkills.push({
1624
- serverName: client.serverName,
1625
- promptName: promptInfo.name,
1626
- skill: skillExtraction.skill
1627
- });
1628
- } catch (error) {
1629
- fetchPromptFailures++;
1630
- console.error(`${LOG_PREFIX_SKILL_DETECTION} Failed to fetch prompt '${promptInfo.name}' from ${client.serverName}: ${error instanceof Error ? error.message : "Unknown error"}`);
1631
- }
1632
- });
1633
- await Promise.all(promptFetchPromises);
1634
- } catch (error) {
1635
- listPromptsFailures++;
1636
- console.error(`${LOG_PREFIX_SKILL_DETECTION} Failed to list prompts from ${client.serverName}: ${error instanceof Error ? error.message : "Unknown error"}`);
1637
- }
1638
- })();
1639
- fetchPromises.push(listPromptsPromise);
1640
- }
1641
- await Promise.all(fetchPromises);
1718
+ try {
1719
+ const prompts = await client.listPrompts();
1720
+ if (!prompts || prompts.length === 0) return detectedSkills;
1721
+ const promptResults = await Promise.all(prompts.map(async (promptInfo) => {
1722
+ if (configuredPromptNames.has(promptInfo.name)) return null;
1723
+ try {
1724
+ const skillExtraction = extractSkillFrontMatter(((await client.getPrompt(promptInfo.name)).messages || []).map((m) => {
1725
+ const content = m.content;
1726
+ if (typeof content === "string") return content;
1727
+ if (content && typeof content === "object" && "text" in content) return String(content.text);
1728
+ return "";
1729
+ }).join("\n"));
1730
+ if (skillExtraction) return {
1731
+ serverName: client.serverName,
1732
+ promptName: promptInfo.name,
1733
+ skill: skillExtraction.skill
1734
+ };
1735
+ return null;
1736
+ } catch (error) {
1737
+ fetchPromptFailures++;
1738
+ console.error(`${LOG_PREFIX_SKILL_DETECTION} Failed to fetch prompt '${promptInfo.name}' from ${client.serverName}: ${error instanceof Error ? error.message : "Unknown error"}`);
1739
+ return null;
1740
+ }
1741
+ }));
1742
+ for (const result of promptResults) if (result) detectedSkills.push(result);
1743
+ } catch (error) {
1744
+ listPromptsFailures++;
1745
+ console.error(`${LOG_PREFIX_SKILL_DETECTION} Failed to list prompts from ${client.serverName}: ${error instanceof Error ? error.message : "Unknown error"}`);
1746
+ }
1747
+ return detectedSkills;
1748
+ }))).flat();
1642
1749
  if (listPromptsFailures > 0 || fetchPromptFailures > 0) console.error(`${LOG_PREFIX_SKILL_DETECTION} Completed with ${listPromptsFailures} server failure(s) and ${fetchPromptFailures} prompt failure(s). Detected ${autoDetectedSkills.length} skill(s).`);
1643
1750
  this.autoDetectedSkillsCache = autoDetectedSkills;
1644
1751
  return autoDetectedSkills;
@@ -1799,14 +1906,15 @@ var DescribeToolsTool = class DescribeToolsTool {
1799
1906
  const clashesWithMcpTool = allToolNames.has(skill.name);
1800
1907
  return {
1801
1908
  name: skill.name,
1802
- displayName: clashesWithMcpTool ? `${SKILL_PREFIX$1}${skill.name}` : skill.name,
1909
+ displayName: clashesWithMcpTool ? `${SKILL_PREFIX}${skill.name}` : skill.name,
1803
1910
  description: skill.description
1804
1911
  };
1805
1912
  });
1806
1913
  return {
1807
1914
  content: await this.liquid.parseAndRender(toolkit_description_default, {
1808
1915
  servers,
1809
- skills
1916
+ skills,
1917
+ serverId: this.serverId
1810
1918
  }),
1811
1919
  toolNames: allToolNames
1812
1920
  };
@@ -1888,40 +1996,42 @@ var DescribeToolsTool = class DescribeToolsTool {
1888
1996
  serverToolsMap.set(client.serverName, []);
1889
1997
  }
1890
1998
  }));
1891
- const foundTools = [];
1892
- const foundSkills = [];
1893
- const notFoundItems = [];
1894
- for (const requestedName of toolNames) {
1895
- if (requestedName.startsWith(SKILL_PREFIX$1)) {
1896
- const skillName = requestedName.slice(SKILL_PREFIX$1.length);
1999
+ const lookupResults = await Promise.all(toolNames.map(async (requestedName) => {
2000
+ const result$1 = {
2001
+ tools: [],
2002
+ skills: [],
2003
+ notFound: null
2004
+ };
2005
+ if (requestedName.startsWith(SKILL_PREFIX)) {
2006
+ const skillName = requestedName.slice(SKILL_PREFIX.length);
1897
2007
  if (this.skillService) {
1898
2008
  const skill = await this.skillService.getSkill(skillName);
1899
2009
  if (skill) {
1900
- foundSkills.push({
2010
+ result$1.skills.push({
1901
2011
  name: skill.name,
1902
2012
  location: skill.basePath,
1903
2013
  instructions: formatSkillInstructions(skill.name, skill.content)
1904
2014
  });
1905
- continue;
2015
+ return result$1;
1906
2016
  }
1907
2017
  }
1908
2018
  const promptSkillContent = await this.getPromptSkillContent(skillName);
1909
2019
  if (promptSkillContent) {
1910
- foundSkills.push(promptSkillContent);
1911
- continue;
2020
+ result$1.skills.push(promptSkillContent);
2021
+ return result$1;
1912
2022
  }
1913
- notFoundItems.push(requestedName);
1914
- continue;
2023
+ result$1.notFound = requestedName;
2024
+ return result$1;
1915
2025
  }
1916
2026
  const { serverName, actualToolName } = parseToolName(requestedName);
1917
2027
  if (serverName) {
1918
2028
  const serverTools = serverToolsMap.get(serverName);
1919
2029
  if (!serverTools) {
1920
- notFoundItems.push(requestedName);
1921
- continue;
2030
+ result$1.notFound = requestedName;
2031
+ return result$1;
1922
2032
  }
1923
2033
  const tool = serverTools.find((t) => t.name === actualToolName);
1924
- if (tool) foundTools.push({
2034
+ if (tool) result$1.tools.push({
1925
2035
  server: serverName,
1926
2036
  tool: {
1927
2037
  name: tool.name,
@@ -1929,52 +2039,61 @@ var DescribeToolsTool = class DescribeToolsTool {
1929
2039
  inputSchema: tool.inputSchema
1930
2040
  }
1931
2041
  });
1932
- else notFoundItems.push(requestedName);
1933
- } else {
1934
- const servers = toolToServers.get(actualToolName);
1935
- if (!servers || servers.length === 0) {
1936
- if (this.skillService) {
1937
- const skill = await this.skillService.getSkill(actualToolName);
1938
- if (skill) {
1939
- foundSkills.push({
1940
- name: skill.name,
1941
- location: skill.basePath,
1942
- instructions: formatSkillInstructions(skill.name, skill.content)
1943
- });
1944
- continue;
1945
- }
1946
- }
1947
- const promptSkillContent = await this.getPromptSkillContent(actualToolName);
1948
- if (promptSkillContent) {
1949
- foundSkills.push(promptSkillContent);
1950
- continue;
2042
+ else result$1.notFound = requestedName;
2043
+ return result$1;
2044
+ }
2045
+ const servers = toolToServers.get(actualToolName);
2046
+ if (!servers || servers.length === 0) {
2047
+ if (this.skillService) {
2048
+ const skill = await this.skillService.getSkill(actualToolName);
2049
+ if (skill) {
2050
+ result$1.skills.push({
2051
+ name: skill.name,
2052
+ location: skill.basePath,
2053
+ instructions: formatSkillInstructions(skill.name, skill.content)
2054
+ });
2055
+ return result$1;
1951
2056
  }
1952
- notFoundItems.push(requestedName);
1953
- continue;
1954
2057
  }
1955
- if (servers.length === 1) {
1956
- const server = servers[0];
1957
- const tool = serverToolsMap.get(server).find((t) => t.name === actualToolName);
1958
- foundTools.push({
1959
- server,
1960
- tool: {
1961
- name: tool.name,
1962
- description: tool.description,
1963
- inputSchema: tool.inputSchema
1964
- }
1965
- });
1966
- } else for (const server of servers) {
1967
- const tool = serverToolsMap.get(server).find((t) => t.name === actualToolName);
1968
- foundTools.push({
1969
- server,
1970
- tool: {
1971
- name: tool.name,
1972
- description: tool.description,
1973
- inputSchema: tool.inputSchema
1974
- }
1975
- });
2058
+ const promptSkillContent = await this.getPromptSkillContent(actualToolName);
2059
+ if (promptSkillContent) {
2060
+ result$1.skills.push(promptSkillContent);
2061
+ return result$1;
1976
2062
  }
2063
+ result$1.notFound = requestedName;
2064
+ return result$1;
2065
+ }
2066
+ if (servers.length === 1) {
2067
+ const server = servers[0];
2068
+ const tool = serverToolsMap.get(server).find((t) => t.name === actualToolName);
2069
+ result$1.tools.push({
2070
+ server,
2071
+ tool: {
2072
+ name: tool.name,
2073
+ description: tool.description,
2074
+ inputSchema: tool.inputSchema
2075
+ }
2076
+ });
2077
+ } else for (const server of servers) {
2078
+ const tool = serverToolsMap.get(server).find((t) => t.name === actualToolName);
2079
+ result$1.tools.push({
2080
+ server,
2081
+ tool: {
2082
+ name: tool.name,
2083
+ description: tool.description,
2084
+ inputSchema: tool.inputSchema
2085
+ }
2086
+ });
1977
2087
  }
2088
+ return result$1;
2089
+ }));
2090
+ const foundTools = [];
2091
+ const foundSkills = [];
2092
+ const notFoundItems = [];
2093
+ for (const result$1 of lookupResults) {
2094
+ foundTools.push(...result$1.tools);
2095
+ foundSkills.push(...result$1.skills);
2096
+ if (result$1.notFound) notFoundItems.push(result$1.notFound);
1978
2097
  }
1979
2098
  if (foundTools.length === 0 && foundSkills.length === 0) return {
1980
2099
  content: [{
@@ -2017,10 +2136,6 @@ var DescribeToolsTool = class DescribeToolsTool {
2017
2136
  //#endregion
2018
2137
  //#region src/tools/UseToolTool.ts
2019
2138
  /**
2020
- * Prefix used to identify skill invocations (e.g., skill__pdf)
2021
- */
2022
- const SKILL_PREFIX = "skill__";
2023
- /**
2024
2139
  * UseToolTool executes MCP tools and skills with proper error handling.
2025
2140
  *
2026
2141
  * This tool supports three invocation patterns:
@@ -2037,14 +2152,18 @@ var UseToolTool = class UseToolTool {
2037
2152
  static TOOL_NAME = "use_tool";
2038
2153
  clientManager;
2039
2154
  skillService;
2155
+ /** Unique server identifier for this one-mcp instance */
2156
+ serverId;
2040
2157
  /**
2041
2158
  * Creates a new UseToolTool instance
2042
2159
  * @param clientManager - The MCP client manager for accessing remote servers
2043
2160
  * @param skillService - Optional skill service for loading and executing skills
2161
+ * @param serverId - Unique server identifier for this one-mcp instance
2044
2162
  */
2045
- constructor(clientManager, skillService) {
2163
+ constructor(clientManager, skillService, serverId) {
2046
2164
  this.clientManager = clientManager;
2047
2165
  this.skillService = skillService;
2166
+ this.serverId = serverId || DEFAULT_SERVER_ID;
2048
2167
  }
2049
2168
  /**
2050
2169
  * Returns the MCP tool definition with name, description, and input schema.
@@ -2060,6 +2179,8 @@ var UseToolTool = class UseToolTool {
2060
2179
  description: `Execute an MCP tool (NOT Skill) with provided arguments. You MUST call describe_tools first to discover the tool's correct arguments. Then to use tool:
2061
2180
  - Provide toolName and toolArgs based on the schema
2062
2181
  - If multiple servers provide the same tool, specify serverName
2182
+
2183
+ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverId}".
2063
2184
  `,
2064
2185
  inputSchema: {
2065
2186
  type: "object",
@@ -2146,7 +2267,7 @@ var UseToolTool = class UseToolTool {
2146
2267
  try {
2147
2268
  const { toolName: inputToolName, toolArgs = {} } = input;
2148
2269
  if (inputToolName.startsWith(SKILL_PREFIX)) {
2149
- const skillName = inputToolName.slice(7);
2270
+ const skillName = inputToolName.slice(SKILL_PREFIX.length);
2150
2271
  if (this.skillService) {
2151
2272
  const skill = await this.skillService.getSkill(skillName);
2152
2273
  if (skill) return this.executeSkill(skill);
@@ -2281,6 +2402,7 @@ async function createServer(options) {
2281
2402
  } });
2282
2403
  const clientManager = new McpClientManagerService();
2283
2404
  let configSkills;
2405
+ let configId;
2284
2406
  if (options?.configFilePath) {
2285
2407
  let config;
2286
2408
  try {
@@ -2292,6 +2414,7 @@ async function createServer(options) {
2292
2414
  throw new Error(`Failed to load MCP configuration from '${options.configFilePath}': ${error instanceof Error ? error.message : String(error)}`);
2293
2415
  }
2294
2416
  configSkills = config.skills;
2417
+ configId = config.id;
2295
2418
  const failedConnections = [];
2296
2419
  const connectionPromises = Object.entries(config.mcpServers).map(async ([serverName, serverConfig]) => {
2297
2420
  try {
@@ -2310,13 +2433,15 @@ async function createServer(options) {
2310
2433
  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(", ")}`);
2311
2434
  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(", ")}`);
2312
2435
  }
2436
+ const serverId = options?.serverId || configId || generateServerId();
2437
+ console.error(`[one-mcp] Server ID: ${serverId}`);
2313
2438
  const skillsConfig = options?.skills || configSkills;
2314
2439
  const toolsRef = { describeTools: null };
2315
2440
  const skillService = skillsConfig && skillsConfig.paths.length > 0 ? new SkillService(process.cwd(), skillsConfig.paths, { onCacheInvalidated: () => {
2316
2441
  toolsRef.describeTools?.clearAutoDetectedSkillsCache();
2317
2442
  } }) : void 0;
2318
- const describeTools = new DescribeToolsTool(clientManager, skillService);
2319
- const useTool = new UseToolTool(clientManager, skillService);
2443
+ const describeTools = new DescribeToolsTool(clientManager, skillService, serverId);
2444
+ const useTool = new UseToolTool(clientManager, skillService, serverId);
2320
2445
  toolsRef.describeTools = describeTools;
2321
2446
  if (skillService) skillService.startWatching().catch((error) => {
2322
2447
  console.error(`[skill-watcher] File watcher failed (non-critical): ${error instanceof Error ? error.message : "Unknown error"}`);