@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.
@@ -4,7 +4,7 @@ import { access, mkdir, readFile, readdir, stat, unlink, watch, writeFile } from
4
4
  import { existsSync } from "node:fs";
5
5
  import yaml from "js-yaml";
6
6
  import { z } from "zod";
7
- import { createHash, randomUUID } from "node:crypto";
7
+ import { createHash, randomBytes, randomUUID } from "node:crypto";
8
8
  import { dirname, isAbsolute, join, resolve } from "node:path";
9
9
  import { tmpdir } from "node:os";
10
10
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -202,6 +202,7 @@ const ClaudeCodeStdioServerSchema = z.object({
202
202
  env: z.record(z.string(), z.string()).optional(),
203
203
  disabled: z.boolean().optional(),
204
204
  instruction: z.string().optional(),
205
+ timeout: z.number().positive().optional(),
205
206
  config: AdditionalConfigSchema
206
207
  });
207
208
  const ClaudeCodeHttpServerSchema = z.object({
@@ -210,6 +211,7 @@ const ClaudeCodeHttpServerSchema = z.object({
210
211
  type: z.enum(["http", "sse"]).optional(),
211
212
  disabled: z.boolean().optional(),
212
213
  instruction: z.string().optional(),
214
+ timeout: z.number().positive().optional(),
213
215
  config: AdditionalConfigSchema
214
216
  });
215
217
  const ClaudeCodeServerConfigSchema = z.union([ClaudeCodeStdioServerSchema, ClaudeCodeHttpServerSchema]);
@@ -240,6 +242,7 @@ const SkillsConfigSchema = z.object({ paths: z.array(z.string()) });
240
242
  * Full Claude Code MCP configuration schema
241
243
  */
242
244
  const ClaudeCodeMcpConfigSchema = z.object({
245
+ id: z.string().optional(),
243
246
  mcpServers: z.record(z.string(), ClaudeCodeServerConfigSchema),
244
247
  remoteConfigs: z.array(RemoteConfigSourceSchema).optional(),
245
248
  skills: SkillsConfigSchema.optional()
@@ -280,6 +283,7 @@ const McpServerConfigSchema = z.discriminatedUnion("transport", [
280
283
  toolBlacklist: z.array(z.string()).optional(),
281
284
  omitToolDescription: z.boolean().optional(),
282
285
  prompts: z.record(z.string(), InternalPromptConfigSchema).optional(),
286
+ timeout: z.number().positive().optional(),
283
287
  transport: z.literal("stdio"),
284
288
  config: McpStdioConfigSchema
285
289
  }),
@@ -289,6 +293,7 @@ const McpServerConfigSchema = z.discriminatedUnion("transport", [
289
293
  toolBlacklist: z.array(z.string()).optional(),
290
294
  omitToolDescription: z.boolean().optional(),
291
295
  prompts: z.record(z.string(), InternalPromptConfigSchema).optional(),
296
+ timeout: z.number().positive().optional(),
292
297
  transport: z.literal("http"),
293
298
  config: McpHttpConfigSchema
294
299
  }),
@@ -298,6 +303,7 @@ const McpServerConfigSchema = z.discriminatedUnion("transport", [
298
303
  toolBlacklist: z.array(z.string()).optional(),
299
304
  omitToolDescription: z.boolean().optional(),
300
305
  prompts: z.record(z.string(), InternalPromptConfigSchema).optional(),
306
+ timeout: z.number().positive().optional(),
301
307
  transport: z.literal("sse"),
302
308
  config: McpSseConfigSchema
303
309
  })
@@ -306,6 +312,7 @@ const McpServerConfigSchema = z.discriminatedUnion("transport", [
306
312
  * Full internal MCP configuration schema
307
313
  */
308
314
  const InternalMcpConfigSchema = z.object({
315
+ id: z.string().optional(),
309
316
  mcpServers: z.record(z.string(), McpServerConfigSchema),
310
317
  skills: SkillsConfigSchema.optional()
311
318
  });
@@ -331,6 +338,7 @@ function transformClaudeCodeConfig(claudeConfig) {
331
338
  toolBlacklist: stdioConfig.config?.toolBlacklist,
332
339
  omitToolDescription: stdioConfig.config?.omitToolDescription,
333
340
  prompts: stdioConfig.config?.prompts,
341
+ timeout: stdioConfig.timeout,
334
342
  transport: "stdio",
335
343
  config: {
336
344
  command: interpolatedCommand,
@@ -349,6 +357,7 @@ function transformClaudeCodeConfig(claudeConfig) {
349
357
  toolBlacklist: httpConfig.config?.toolBlacklist,
350
358
  omitToolDescription: httpConfig.config?.omitToolDescription,
351
359
  prompts: httpConfig.config?.prompts,
360
+ timeout: httpConfig.timeout,
352
361
  transport,
353
362
  config: {
354
363
  url: interpolatedUrl,
@@ -358,6 +367,7 @@ function transformClaudeCodeConfig(claudeConfig) {
358
367
  }
359
368
  }
360
369
  return {
370
+ id: claudeConfig.id,
361
371
  mcpServers: transformedServers,
362
372
  skills: claudeConfig.skills
363
373
  };
@@ -518,22 +528,21 @@ var RemoteConfigCacheService = class {
518
528
  try {
519
529
  if (!existsSync(this.cacheDir)) return;
520
530
  const now = Date.now();
521
- const files = await readdir(this.cacheDir);
522
- let expiredCount = 0;
523
- for (const file of files) {
524
- if (!file.endsWith(".json")) continue;
531
+ const jsonFiles = (await readdir(this.cacheDir)).filter((file) => file.endsWith(".json"));
532
+ const expiredCount = (await Promise.all(jsonFiles.map(async (file) => {
525
533
  const filePath = join(this.cacheDir, file);
526
534
  try {
527
535
  const content = await readFile(filePath, "utf-8");
528
536
  if (now > JSON.parse(content).expiresAt) {
529
537
  await unlink(filePath);
530
- expiredCount++;
538
+ return true;
531
539
  }
540
+ return false;
532
541
  } catch (error) {
533
542
  await unlink(filePath).catch(() => {});
534
- expiredCount++;
543
+ return true;
535
544
  }
536
- }
545
+ }))).filter(Boolean).length;
537
546
  if (expiredCount > 0) console.error(`Cleaned up ${expiredCount} expired remote config cache entries`);
538
547
  } catch (error) {
539
548
  console.error("Failed to clean expired remote config cache:", error);
@@ -549,14 +558,15 @@ var RemoteConfigCacheService = class {
549
558
  totalSize: 0
550
559
  };
551
560
  const jsonFiles = (await readdir(this.cacheDir)).filter((file) => file.endsWith(".json"));
552
- let totalSize = 0;
553
- for (const file of jsonFiles) {
561
+ const totalSize = (await Promise.all(jsonFiles.map(async (file) => {
554
562
  const filePath = join(this.cacheDir, file);
555
563
  try {
556
564
  const content = await readFile(filePath, "utf-8");
557
- totalSize += Buffer.byteLength(content, "utf-8");
558
- } catch {}
559
- }
565
+ return Buffer.byteLength(content, "utf-8");
566
+ } catch {
567
+ return 0;
568
+ }
569
+ }))).reduce((sum, size) => sum + size, 0);
560
570
  return {
561
571
  totalEntries: jsonFiles.length,
562
572
  totalSize
@@ -807,6 +817,8 @@ var ConfigFetcherService = class {
807
817
 
808
818
  //#endregion
809
819
  //#region src/services/McpClientManagerService.ts
820
+ /** Default connection timeout in milliseconds (30 seconds) */
821
+ const DEFAULT_CONNECTION_TIMEOUT_MS = 3e4;
810
822
  /**
811
823
  * MCP Client wrapper for managing individual server connections
812
824
  * This is an internal class used by McpClientManagerService
@@ -912,8 +924,10 @@ var McpClientManagerService = class {
912
924
  }
913
925
  /**
914
926
  * Connect to an MCP server based on its configuration with timeout
927
+ * Uses the timeout from server config, falling back to default (30s)
915
928
  */
916
- async connectToServer(serverName, config, timeoutMs = 1e4) {
929
+ async connectToServer(serverName, config) {
930
+ const timeoutMs = config.timeout ?? DEFAULT_CONNECTION_TIMEOUT_MS;
917
931
  if (this.clients.has(serverName)) throw new Error(`Client for ${serverName} is already connected`);
918
932
  const client = new Client({
919
933
  name: `@agiflowai/one-mcp-client`,
@@ -1208,6 +1222,71 @@ function extractSkillFrontMatter(content) {
1208
1222
  return null;
1209
1223
  }
1210
1224
 
1225
+ //#endregion
1226
+ //#region src/utils/generateServerId.ts
1227
+ /**
1228
+ * generateServerId Utilities
1229
+ *
1230
+ * DESIGN PATTERNS:
1231
+ * - Pure functions with no side effects
1232
+ * - Single responsibility per function
1233
+ * - Functional programming approach
1234
+ *
1235
+ * CODING STANDARDS:
1236
+ * - Export individual functions, not classes
1237
+ * - Use descriptive function names with verbs
1238
+ * - Add JSDoc comments for complex logic
1239
+ * - Keep functions small and focused
1240
+ *
1241
+ * AVOID:
1242
+ * - Side effects (mutating external state)
1243
+ * - Stateful logic (use services for state)
1244
+ * - Complex external dependencies
1245
+ */
1246
+ /**
1247
+ * Character set for generating human-readable IDs.
1248
+ * Excludes confusing characters: 0, O, 1, l, I
1249
+ */
1250
+ const CHARSET = "23456789abcdefghjkmnpqrstuvwxyz";
1251
+ /**
1252
+ * Default length for generated server IDs (6 characters)
1253
+ */
1254
+ const DEFAULT_ID_LENGTH = 6;
1255
+ /**
1256
+ * Generate a short, human-readable server ID.
1257
+ *
1258
+ * Uses Node.js crypto.randomBytes for cryptographically secure randomness
1259
+ * with rejection sampling to avoid modulo bias.
1260
+ *
1261
+ * The generated ID:
1262
+ * - Is 6 characters long by default
1263
+ * - Uses only lowercase alphanumeric characters
1264
+ * - Excludes confusing characters (0, O, 1, l, I)
1265
+ *
1266
+ * @param length - Length of the ID to generate (default: 6)
1267
+ * @returns A random, human-readable ID
1268
+ *
1269
+ * @example
1270
+ * generateServerId() // "abc234"
1271
+ * generateServerId(4) // "x7mn"
1272
+ */
1273
+ function generateServerId(length = DEFAULT_ID_LENGTH) {
1274
+ const charsetLength = 31;
1275
+ const maxUnbiased = Math.floor(256 / charsetLength) * charsetLength - 1;
1276
+ let result = "";
1277
+ let remaining = length;
1278
+ while (remaining > 0) {
1279
+ const bytes = randomBytes(remaining);
1280
+ for (let i = 0; i < bytes.length && remaining > 0; i++) {
1281
+ const byte = bytes[i];
1282
+ if (byte > maxUnbiased) continue;
1283
+ result += CHARSET[byte % charsetLength];
1284
+ remaining--;
1285
+ }
1286
+ }
1287
+ return result;
1288
+ }
1289
+
1211
1290
  //#endregion
1212
1291
  //#region src/services/SkillService.ts
1213
1292
  /**
@@ -1308,13 +1387,13 @@ var SkillService = class {
1308
1387
  if (this.cachedSkills !== null) return this.cachedSkills;
1309
1388
  const skills = [];
1310
1389
  const loadedSkillNames = /* @__PURE__ */ new Set();
1311
- for (const skillPath of this.skillPaths) {
1390
+ const allDirSkills = await Promise.all(this.skillPaths.map(async (skillPath) => {
1312
1391
  const skillsDir = isAbsolute(skillPath) ? skillPath : join(this.cwd, skillPath);
1313
- const dirSkills = await this.loadSkillsFromDirectory(skillsDir, "project");
1314
- for (const skill of dirSkills) if (!loadedSkillNames.has(skill.name)) {
1315
- skills.push(skill);
1316
- loadedSkillNames.add(skill.name);
1317
- }
1392
+ return this.loadSkillsFromDirectory(skillsDir, "project");
1393
+ }));
1394
+ for (const dirSkills of allDirSkills) for (const skill of dirSkills) if (!loadedSkillNames.has(skill.name)) {
1395
+ skills.push(skill);
1396
+ loadedSkillNames.add(skill.name);
1318
1397
  }
1319
1398
  this.cachedSkills = skills;
1320
1399
  this.skillsByName = new Map(skills.map((skill) => [skill.name, skill]));
@@ -1352,9 +1431,15 @@ var SkillService = class {
1352
1431
  */
1353
1432
  async startWatching() {
1354
1433
  this.stopWatching();
1355
- for (const skillPath of this.skillPaths) {
1434
+ const existenceChecks = await Promise.all(this.skillPaths.map(async (skillPath) => {
1356
1435
  const skillsDir = isAbsolute(skillPath) ? skillPath : join(this.cwd, skillPath);
1357
- if (!await pathExists(skillsDir)) continue;
1436
+ return {
1437
+ skillsDir,
1438
+ exists: await pathExists(skillsDir)
1439
+ };
1440
+ }));
1441
+ for (const { skillsDir, exists } of existenceChecks) {
1442
+ if (!exists) continue;
1358
1443
  const abortController = new AbortController();
1359
1444
  this.watchers.push(abortController);
1360
1445
  this.watchDirectory(skillsDir, abortController.signal).catch((error) => {
@@ -1412,34 +1497,49 @@ var SkillService = class {
1412
1497
  } catch (error) {
1413
1498
  throw new SkillLoadError(`Failed to read skills directory: ${error instanceof Error ? error.message : "Unknown error"}`, dirPath, error instanceof Error ? error : void 0);
1414
1499
  }
1415
- for (const entry of entries) {
1500
+ const entryStats = await Promise.all(entries.map(async (entry) => {
1416
1501
  const entryPath = join(dirPath, entry);
1417
- let entryStat;
1418
1502
  try {
1419
- entryStat = await stat(entryPath);
1503
+ return {
1504
+ entry,
1505
+ entryPath,
1506
+ stat: await stat(entryPath),
1507
+ error: null
1508
+ };
1420
1509
  } catch (error) {
1421
1510
  console.warn(`Skipping entry ${entryPath}: ${error instanceof Error ? error.message : "Unknown error"}`);
1422
- continue;
1511
+ return {
1512
+ entry,
1513
+ entryPath,
1514
+ stat: null,
1515
+ error
1516
+ };
1423
1517
  }
1518
+ }));
1519
+ const skillFilesToLoad = [];
1520
+ for (const { entry, entryPath, stat: entryStat } of entryStats) {
1521
+ if (!entryStat) continue;
1424
1522
  if (entryStat.isDirectory()) {
1425
1523
  const skillFilePath = join(entryPath, "SKILL.md");
1426
- try {
1427
- if (await pathExists(skillFilePath)) {
1428
- const skill = await this.loadSkillFile(skillFilePath, location);
1429
- if (skill) skills.push(skill);
1430
- }
1431
- } catch (error) {
1432
- console.warn(`Skipping skill at ${skillFilePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
1433
- continue;
1434
- }
1435
- } else if (entry === "SKILL.md") try {
1436
- const skill = await this.loadSkillFile(entryPath, location);
1437
- if (skill) skills.push(skill);
1524
+ skillFilesToLoad.push({
1525
+ filePath: skillFilePath,
1526
+ isRootLevel: false
1527
+ });
1528
+ } else if (entry === "SKILL.md") skillFilesToLoad.push({
1529
+ filePath: entryPath,
1530
+ isRootLevel: true
1531
+ });
1532
+ }
1533
+ const loadResults = await Promise.all(skillFilesToLoad.map(async ({ filePath, isRootLevel }) => {
1534
+ try {
1535
+ if (!isRootLevel && !await pathExists(filePath)) return null;
1536
+ return await this.loadSkillFile(filePath, location);
1438
1537
  } catch (error) {
1439
- console.warn(`Skipping skill at ${entryPath}: ${error instanceof Error ? error.message : "Unknown error"}`);
1440
- continue;
1538
+ console.warn(`Skipping skill at ${filePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
1539
+ return null;
1441
1540
  }
1442
- }
1541
+ }));
1542
+ for (const skill of loadResults) if (skill) skills.push(skill);
1443
1543
  return skills;
1444
1544
  }
1445
1545
  /**
@@ -1485,7 +1585,7 @@ var SkillService = class {
1485
1585
  * Prefix added to skill names when they clash with MCP tool names.
1486
1586
  * This ensures skills can be uniquely identified even when a tool has the same name.
1487
1587
  */
1488
- const SKILL_PREFIX$1 = "skill__";
1588
+ const SKILL_PREFIX = "skill__";
1489
1589
  /**
1490
1590
  * Log prefix for skill detection messages.
1491
1591
  * Used to easily filter skill detection logs in stderr output.
@@ -1496,10 +1596,15 @@ const LOG_PREFIX_SKILL_DETECTION = "[skill-detection]";
1496
1596
  * Format: "prompt:{serverName}:{promptName}"
1497
1597
  */
1498
1598
  const PROMPT_LOCATION_PREFIX = "prompt:";
1599
+ /**
1600
+ * Default server ID used when no ID is provided via CLI or config.
1601
+ * This fallback is used when auto-generation also fails.
1602
+ */
1603
+ const DEFAULT_SERVER_ID = "unknown";
1499
1604
 
1500
1605
  //#endregion
1501
1606
  //#region src/templates/toolkit-description.liquid?raw
1502
- 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";
1607
+ 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";
1503
1608
 
1504
1609
  //#endregion
1505
1610
  //#region src/tools/DescribeToolsTool.ts
@@ -1538,14 +1643,18 @@ var DescribeToolsTool = class DescribeToolsTool {
1538
1643
  liquid = new Liquid();
1539
1644
  /** Cache for auto-detected skills from prompt front-matter */
1540
1645
  autoDetectedSkillsCache = null;
1646
+ /** Unique server identifier for this one-mcp instance */
1647
+ serverId;
1541
1648
  /**
1542
1649
  * Creates a new DescribeToolsTool instance
1543
1650
  * @param clientManager - The MCP client manager for accessing remote servers
1544
1651
  * @param skillService - Optional skill service for loading skills
1652
+ * @param serverId - Unique server identifier for this one-mcp instance
1545
1653
  */
1546
- constructor(clientManager, skillService) {
1654
+ constructor(clientManager, skillService, serverId) {
1547
1655
  this.clientManager = clientManager;
1548
1656
  this.skillService = skillService;
1657
+ this.serverId = serverId || DEFAULT_SERVER_ID;
1549
1658
  }
1550
1659
  /**
1551
1660
  * Clears the cached auto-detected skills from prompt front-matter.
@@ -1572,44 +1681,42 @@ var DescribeToolsTool = class DescribeToolsTool {
1572
1681
  async detectSkillsFromPromptFrontMatter() {
1573
1682
  if (this.autoDetectedSkillsCache !== null) return this.autoDetectedSkillsCache;
1574
1683
  const clients = this.clientManager.getAllClients();
1575
- const autoDetectedSkills = [];
1576
1684
  let listPromptsFailures = 0;
1577
1685
  let fetchPromptFailures = 0;
1578
- const fetchPromises = [];
1579
- for (const client of clients) {
1686
+ const autoDetectedSkills = (await Promise.all(clients.map(async (client) => {
1687
+ const detectedSkills = [];
1580
1688
  const configuredPromptNames = new Set(client.prompts ? Object.keys(client.prompts) : []);
1581
- const listPromptsPromise = (async () => {
1582
- try {
1583
- const prompts = await client.listPrompts();
1584
- if (!prompts || prompts.length === 0) return;
1585
- const promptFetchPromises = prompts.map(async (promptInfo) => {
1586
- if (configuredPromptNames.has(promptInfo.name)) return;
1587
- try {
1588
- const skillExtraction = extractSkillFrontMatter(((await client.getPrompt(promptInfo.name)).messages || []).map((m) => {
1589
- const content = m.content;
1590
- if (typeof content === "string") return content;
1591
- if (content && typeof content === "object" && "text" in content) return String(content.text);
1592
- return "";
1593
- }).join("\n"));
1594
- if (skillExtraction) autoDetectedSkills.push({
1595
- serverName: client.serverName,
1596
- promptName: promptInfo.name,
1597
- skill: skillExtraction.skill
1598
- });
1599
- } catch (error) {
1600
- fetchPromptFailures++;
1601
- console.error(`${LOG_PREFIX_SKILL_DETECTION} Failed to fetch prompt '${promptInfo.name}' from ${client.serverName}: ${error instanceof Error ? error.message : "Unknown error"}`);
1602
- }
1603
- });
1604
- await Promise.all(promptFetchPromises);
1605
- } catch (error) {
1606
- listPromptsFailures++;
1607
- console.error(`${LOG_PREFIX_SKILL_DETECTION} Failed to list prompts from ${client.serverName}: ${error instanceof Error ? error.message : "Unknown error"}`);
1608
- }
1609
- })();
1610
- fetchPromises.push(listPromptsPromise);
1611
- }
1612
- await Promise.all(fetchPromises);
1689
+ try {
1690
+ const prompts = await client.listPrompts();
1691
+ if (!prompts || prompts.length === 0) return detectedSkills;
1692
+ const promptResults = await Promise.all(prompts.map(async (promptInfo) => {
1693
+ if (configuredPromptNames.has(promptInfo.name)) return null;
1694
+ try {
1695
+ const skillExtraction = extractSkillFrontMatter(((await client.getPrompt(promptInfo.name)).messages || []).map((m) => {
1696
+ const content = m.content;
1697
+ if (typeof content === "string") return content;
1698
+ if (content && typeof content === "object" && "text" in content) return String(content.text);
1699
+ return "";
1700
+ }).join("\n"));
1701
+ if (skillExtraction) return {
1702
+ serverName: client.serverName,
1703
+ promptName: promptInfo.name,
1704
+ skill: skillExtraction.skill
1705
+ };
1706
+ return null;
1707
+ } catch (error) {
1708
+ fetchPromptFailures++;
1709
+ console.error(`${LOG_PREFIX_SKILL_DETECTION} Failed to fetch prompt '${promptInfo.name}' from ${client.serverName}: ${error instanceof Error ? error.message : "Unknown error"}`);
1710
+ return null;
1711
+ }
1712
+ }));
1713
+ for (const result of promptResults) if (result) detectedSkills.push(result);
1714
+ } catch (error) {
1715
+ listPromptsFailures++;
1716
+ console.error(`${LOG_PREFIX_SKILL_DETECTION} Failed to list prompts from ${client.serverName}: ${error instanceof Error ? error.message : "Unknown error"}`);
1717
+ }
1718
+ return detectedSkills;
1719
+ }))).flat();
1613
1720
  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).`);
1614
1721
  this.autoDetectedSkillsCache = autoDetectedSkills;
1615
1722
  return autoDetectedSkills;
@@ -1770,14 +1877,15 @@ var DescribeToolsTool = class DescribeToolsTool {
1770
1877
  const clashesWithMcpTool = allToolNames.has(skill.name);
1771
1878
  return {
1772
1879
  name: skill.name,
1773
- displayName: clashesWithMcpTool ? `${SKILL_PREFIX$1}${skill.name}` : skill.name,
1880
+ displayName: clashesWithMcpTool ? `${SKILL_PREFIX}${skill.name}` : skill.name,
1774
1881
  description: skill.description
1775
1882
  };
1776
1883
  });
1777
1884
  return {
1778
1885
  content: await this.liquid.parseAndRender(toolkit_description_default, {
1779
1886
  servers,
1780
- skills
1887
+ skills,
1888
+ serverId: this.serverId
1781
1889
  }),
1782
1890
  toolNames: allToolNames
1783
1891
  };
@@ -1859,40 +1967,42 @@ var DescribeToolsTool = class DescribeToolsTool {
1859
1967
  serverToolsMap.set(client.serverName, []);
1860
1968
  }
1861
1969
  }));
1862
- const foundTools = [];
1863
- const foundSkills = [];
1864
- const notFoundItems = [];
1865
- for (const requestedName of toolNames) {
1866
- if (requestedName.startsWith(SKILL_PREFIX$1)) {
1867
- const skillName = requestedName.slice(SKILL_PREFIX$1.length);
1970
+ const lookupResults = await Promise.all(toolNames.map(async (requestedName) => {
1971
+ const result$1 = {
1972
+ tools: [],
1973
+ skills: [],
1974
+ notFound: null
1975
+ };
1976
+ if (requestedName.startsWith(SKILL_PREFIX)) {
1977
+ const skillName = requestedName.slice(SKILL_PREFIX.length);
1868
1978
  if (this.skillService) {
1869
1979
  const skill = await this.skillService.getSkill(skillName);
1870
1980
  if (skill) {
1871
- foundSkills.push({
1981
+ result$1.skills.push({
1872
1982
  name: skill.name,
1873
1983
  location: skill.basePath,
1874
1984
  instructions: formatSkillInstructions(skill.name, skill.content)
1875
1985
  });
1876
- continue;
1986
+ return result$1;
1877
1987
  }
1878
1988
  }
1879
1989
  const promptSkillContent = await this.getPromptSkillContent(skillName);
1880
1990
  if (promptSkillContent) {
1881
- foundSkills.push(promptSkillContent);
1882
- continue;
1991
+ result$1.skills.push(promptSkillContent);
1992
+ return result$1;
1883
1993
  }
1884
- notFoundItems.push(requestedName);
1885
- continue;
1994
+ result$1.notFound = requestedName;
1995
+ return result$1;
1886
1996
  }
1887
1997
  const { serverName, actualToolName } = parseToolName(requestedName);
1888
1998
  if (serverName) {
1889
1999
  const serverTools = serverToolsMap.get(serverName);
1890
2000
  if (!serverTools) {
1891
- notFoundItems.push(requestedName);
1892
- continue;
2001
+ result$1.notFound = requestedName;
2002
+ return result$1;
1893
2003
  }
1894
2004
  const tool = serverTools.find((t) => t.name === actualToolName);
1895
- if (tool) foundTools.push({
2005
+ if (tool) result$1.tools.push({
1896
2006
  server: serverName,
1897
2007
  tool: {
1898
2008
  name: tool.name,
@@ -1900,52 +2010,61 @@ var DescribeToolsTool = class DescribeToolsTool {
1900
2010
  inputSchema: tool.inputSchema
1901
2011
  }
1902
2012
  });
1903
- else notFoundItems.push(requestedName);
1904
- } else {
1905
- const servers = toolToServers.get(actualToolName);
1906
- if (!servers || servers.length === 0) {
1907
- if (this.skillService) {
1908
- const skill = await this.skillService.getSkill(actualToolName);
1909
- if (skill) {
1910
- foundSkills.push({
1911
- name: skill.name,
1912
- location: skill.basePath,
1913
- instructions: formatSkillInstructions(skill.name, skill.content)
1914
- });
1915
- continue;
1916
- }
1917
- }
1918
- const promptSkillContent = await this.getPromptSkillContent(actualToolName);
1919
- if (promptSkillContent) {
1920
- foundSkills.push(promptSkillContent);
1921
- continue;
2013
+ else result$1.notFound = requestedName;
2014
+ return result$1;
2015
+ }
2016
+ const servers = toolToServers.get(actualToolName);
2017
+ if (!servers || servers.length === 0) {
2018
+ if (this.skillService) {
2019
+ const skill = await this.skillService.getSkill(actualToolName);
2020
+ if (skill) {
2021
+ result$1.skills.push({
2022
+ name: skill.name,
2023
+ location: skill.basePath,
2024
+ instructions: formatSkillInstructions(skill.name, skill.content)
2025
+ });
2026
+ return result$1;
1922
2027
  }
1923
- notFoundItems.push(requestedName);
1924
- continue;
1925
2028
  }
1926
- if (servers.length === 1) {
1927
- const server = servers[0];
1928
- const tool = serverToolsMap.get(server).find((t) => t.name === actualToolName);
1929
- foundTools.push({
1930
- server,
1931
- tool: {
1932
- name: tool.name,
1933
- description: tool.description,
1934
- inputSchema: tool.inputSchema
1935
- }
1936
- });
1937
- } else for (const server of servers) {
1938
- const tool = serverToolsMap.get(server).find((t) => t.name === actualToolName);
1939
- foundTools.push({
1940
- server,
1941
- tool: {
1942
- name: tool.name,
1943
- description: tool.description,
1944
- inputSchema: tool.inputSchema
1945
- }
1946
- });
2029
+ const promptSkillContent = await this.getPromptSkillContent(actualToolName);
2030
+ if (promptSkillContent) {
2031
+ result$1.skills.push(promptSkillContent);
2032
+ return result$1;
1947
2033
  }
2034
+ result$1.notFound = requestedName;
2035
+ return result$1;
2036
+ }
2037
+ if (servers.length === 1) {
2038
+ const server = servers[0];
2039
+ const tool = serverToolsMap.get(server).find((t) => t.name === actualToolName);
2040
+ result$1.tools.push({
2041
+ server,
2042
+ tool: {
2043
+ name: tool.name,
2044
+ description: tool.description,
2045
+ inputSchema: tool.inputSchema
2046
+ }
2047
+ });
2048
+ } else for (const server of servers) {
2049
+ const tool = serverToolsMap.get(server).find((t) => t.name === actualToolName);
2050
+ result$1.tools.push({
2051
+ server,
2052
+ tool: {
2053
+ name: tool.name,
2054
+ description: tool.description,
2055
+ inputSchema: tool.inputSchema
2056
+ }
2057
+ });
1948
2058
  }
2059
+ return result$1;
2060
+ }));
2061
+ const foundTools = [];
2062
+ const foundSkills = [];
2063
+ const notFoundItems = [];
2064
+ for (const result$1 of lookupResults) {
2065
+ foundTools.push(...result$1.tools);
2066
+ foundSkills.push(...result$1.skills);
2067
+ if (result$1.notFound) notFoundItems.push(result$1.notFound);
1949
2068
  }
1950
2069
  if (foundTools.length === 0 && foundSkills.length === 0) return {
1951
2070
  content: [{
@@ -1988,10 +2107,6 @@ var DescribeToolsTool = class DescribeToolsTool {
1988
2107
  //#endregion
1989
2108
  //#region src/tools/UseToolTool.ts
1990
2109
  /**
1991
- * Prefix used to identify skill invocations (e.g., skill__pdf)
1992
- */
1993
- const SKILL_PREFIX = "skill__";
1994
- /**
1995
2110
  * UseToolTool executes MCP tools and skills with proper error handling.
1996
2111
  *
1997
2112
  * This tool supports three invocation patterns:
@@ -2008,14 +2123,18 @@ var UseToolTool = class UseToolTool {
2008
2123
  static TOOL_NAME = "use_tool";
2009
2124
  clientManager;
2010
2125
  skillService;
2126
+ /** Unique server identifier for this one-mcp instance */
2127
+ serverId;
2011
2128
  /**
2012
2129
  * Creates a new UseToolTool instance
2013
2130
  * @param clientManager - The MCP client manager for accessing remote servers
2014
2131
  * @param skillService - Optional skill service for loading and executing skills
2132
+ * @param serverId - Unique server identifier for this one-mcp instance
2015
2133
  */
2016
- constructor(clientManager, skillService) {
2134
+ constructor(clientManager, skillService, serverId) {
2017
2135
  this.clientManager = clientManager;
2018
2136
  this.skillService = skillService;
2137
+ this.serverId = serverId || DEFAULT_SERVER_ID;
2019
2138
  }
2020
2139
  /**
2021
2140
  * Returns the MCP tool definition with name, description, and input schema.
@@ -2031,6 +2150,8 @@ var UseToolTool = class UseToolTool {
2031
2150
  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:
2032
2151
  - Provide toolName and toolArgs based on the schema
2033
2152
  - If multiple servers provide the same tool, specify serverName
2153
+
2154
+ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverId}".
2034
2155
  `,
2035
2156
  inputSchema: {
2036
2157
  type: "object",
@@ -2117,7 +2238,7 @@ var UseToolTool = class UseToolTool {
2117
2238
  try {
2118
2239
  const { toolName: inputToolName, toolArgs = {} } = input;
2119
2240
  if (inputToolName.startsWith(SKILL_PREFIX)) {
2120
- const skillName = inputToolName.slice(7);
2241
+ const skillName = inputToolName.slice(SKILL_PREFIX.length);
2121
2242
  if (this.skillService) {
2122
2243
  const skill = await this.skillService.getSkill(skillName);
2123
2244
  if (skill) return this.executeSkill(skill);
@@ -2252,6 +2373,7 @@ async function createServer(options) {
2252
2373
  } });
2253
2374
  const clientManager = new McpClientManagerService();
2254
2375
  let configSkills;
2376
+ let configId;
2255
2377
  if (options?.configFilePath) {
2256
2378
  let config;
2257
2379
  try {
@@ -2263,6 +2385,7 @@ async function createServer(options) {
2263
2385
  throw new Error(`Failed to load MCP configuration from '${options.configFilePath}': ${error instanceof Error ? error.message : String(error)}`);
2264
2386
  }
2265
2387
  configSkills = config.skills;
2388
+ configId = config.id;
2266
2389
  const failedConnections = [];
2267
2390
  const connectionPromises = Object.entries(config.mcpServers).map(async ([serverName, serverConfig]) => {
2268
2391
  try {
@@ -2281,13 +2404,15 @@ async function createServer(options) {
2281
2404
  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(", ")}`);
2282
2405
  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(", ")}`);
2283
2406
  }
2407
+ const serverId = options?.serverId || configId || generateServerId();
2408
+ console.error(`[one-mcp] Server ID: ${serverId}`);
2284
2409
  const skillsConfig = options?.skills || configSkills;
2285
2410
  const toolsRef = { describeTools: null };
2286
2411
  const skillService = skillsConfig && skillsConfig.paths.length > 0 ? new SkillService(process.cwd(), skillsConfig.paths, { onCacheInvalidated: () => {
2287
2412
  toolsRef.describeTools?.clearAutoDetectedSkillsCache();
2288
2413
  } }) : void 0;
2289
- const describeTools = new DescribeToolsTool(clientManager, skillService);
2290
- const useTool = new UseToolTool(clientManager, skillService);
2414
+ const describeTools = new DescribeToolsTool(clientManager, skillService, serverId);
2415
+ const useTool = new UseToolTool(clientManager, skillService, serverId);
2291
2416
  toolsRef.describeTools = describeTools;
2292
2417
  if (skillService) skillService.startWatching().catch((error) => {
2293
2418
  console.error(`[skill-watcher] File watcher failed (non-critical): ${error instanceof Error ? error.message : "Unknown error"}`);