@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.
- package/README.md +34 -0
- package/dist/cli.cjs +564 -52
- package/dist/cli.mjs +565 -53
- package/dist/{http-B4NAfsQl.cjs → http-BzrxGEr-.cjs} +270 -145
- package/dist/{http-DSkkpGJU.mjs → http-DeUYygKb.mjs} +271 -146
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.mts +8 -0
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
|
@@ -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
|
|
551
|
-
|
|
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
|
-
|
|
567
|
+
return true;
|
|
560
568
|
}
|
|
569
|
+
return false;
|
|
561
570
|
} catch (error) {
|
|
562
571
|
await (0, node_fs_promises.unlink)(filePath).catch(() => {});
|
|
563
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
if (
|
|
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 ${
|
|
1469
|
-
|
|
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
|
|
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
|
|
1608
|
-
|
|
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
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
const
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
|
|
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
|
|
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
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2015
|
+
return result$1;
|
|
1906
2016
|
}
|
|
1907
2017
|
}
|
|
1908
2018
|
const promptSkillContent = await this.getPromptSkillContent(skillName);
|
|
1909
2019
|
if (promptSkillContent) {
|
|
1910
|
-
|
|
1911
|
-
|
|
2020
|
+
result$1.skills.push(promptSkillContent);
|
|
2021
|
+
return result$1;
|
|
1912
2022
|
}
|
|
1913
|
-
|
|
1914
|
-
|
|
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
|
-
|
|
1921
|
-
|
|
2030
|
+
result$1.notFound = requestedName;
|
|
2031
|
+
return result$1;
|
|
1922
2032
|
}
|
|
1923
2033
|
const tool = serverTools.find((t) => t.name === actualToolName);
|
|
1924
|
-
if (tool)
|
|
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
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
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
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
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(
|
|
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"}`);
|