@agiflowai/one-mcp 0.2.4 → 0.2.6

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.
@@ -6,12 +6,16 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
8
  var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
- key = keys[i];
11
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
- get: ((k) => from[k]).bind(null, key),
13
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
- });
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) {
13
+ __defProp(to, key, {
14
+ get: ((k) => from[k]).bind(null, key),
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ });
17
+ }
18
+ }
15
19
  }
16
20
  return to;
17
21
  };
@@ -34,6 +38,7 @@ let node_os = require("node:os");
34
38
  let __modelcontextprotocol_sdk_client_index_js = require("@modelcontextprotocol/sdk/client/index.js");
35
39
  let __modelcontextprotocol_sdk_client_stdio_js = require("@modelcontextprotocol/sdk/client/stdio.js");
36
40
  let __modelcontextprotocol_sdk_client_sse_js = require("@modelcontextprotocol/sdk/client/sse.js");
41
+ let liquidjs = require("liquidjs");
37
42
  let __modelcontextprotocol_sdk_server_stdio_js = require("@modelcontextprotocol/sdk/server/stdio.js");
38
43
  let __modelcontextprotocol_sdk_server_sse_js = require("@modelcontextprotocol/sdk/server/sse.js");
39
44
  let express = require("express");
@@ -199,10 +204,25 @@ function validateRemoteConfigSource(source) {
199
204
  * Claude Code / Claude Desktop standard MCP config format
200
205
  * This is the format users write in their config files
201
206
  */
207
+ /**
208
+ * Prompt skill configuration schema
209
+ * Converts a prompt to an executable skill
210
+ */
211
+ const PromptSkillConfigSchema = zod.z.object({
212
+ name: zod.z.string(),
213
+ description: zod.z.string(),
214
+ folder: zod.z.string().optional()
215
+ });
216
+ /**
217
+ * Prompt configuration schema
218
+ * Supports converting prompts to skills
219
+ */
220
+ const PromptConfigSchema = zod.z.object({ skill: PromptSkillConfigSchema.optional() });
202
221
  const AdditionalConfigSchema = zod.z.object({
203
222
  instruction: zod.z.string().optional(),
204
223
  toolBlacklist: zod.z.array(zod.z.string()).optional(),
205
- omitToolDescription: zod.z.boolean().optional()
224
+ omitToolDescription: zod.z.boolean().optional(),
225
+ prompts: zod.z.record(zod.z.string(), PromptConfigSchema).optional()
206
226
  }).optional();
207
227
  const ClaudeCodeStdioServerSchema = zod.z.object({
208
228
  command: zod.z.string(),
@@ -241,11 +261,16 @@ const RemoteConfigSourceSchema = zod.z.object({
241
261
  ]).optional()
242
262
  });
243
263
  /**
264
+ * Skills configuration schema
265
+ */
266
+ const SkillsConfigSchema = zod.z.object({ paths: zod.z.array(zod.z.string()) });
267
+ /**
244
268
  * Full Claude Code MCP configuration schema
245
269
  */
246
270
  const ClaudeCodeMcpConfigSchema = zod.z.object({
247
271
  mcpServers: zod.z.record(zod.z.string(), ClaudeCodeServerConfigSchema),
248
- remoteConfigs: zod.z.array(RemoteConfigSourceSchema).optional()
272
+ remoteConfigs: zod.z.array(RemoteConfigSourceSchema).optional(),
273
+ skills: SkillsConfigSchema.optional()
249
274
  });
250
275
  /**
251
276
  * Internal MCP config format
@@ -264,12 +289,25 @@ const McpSseConfigSchema = zod.z.object({
264
289
  url: zod.z.string().url(),
265
290
  headers: zod.z.record(zod.z.string(), zod.z.string()).optional()
266
291
  });
292
+ /**
293
+ * Internal prompt skill configuration schema
294
+ */
295
+ const InternalPromptSkillConfigSchema = zod.z.object({
296
+ name: zod.z.string(),
297
+ description: zod.z.string(),
298
+ folder: zod.z.string().optional()
299
+ });
300
+ /**
301
+ * Internal prompt configuration schema
302
+ */
303
+ const InternalPromptConfigSchema = zod.z.object({ skill: InternalPromptSkillConfigSchema.optional() });
267
304
  const McpServerConfigSchema = zod.z.discriminatedUnion("transport", [
268
305
  zod.z.object({
269
306
  name: zod.z.string(),
270
307
  instruction: zod.z.string().optional(),
271
308
  toolBlacklist: zod.z.array(zod.z.string()).optional(),
272
309
  omitToolDescription: zod.z.boolean().optional(),
310
+ prompts: zod.z.record(zod.z.string(), InternalPromptConfigSchema).optional(),
273
311
  transport: zod.z.literal("stdio"),
274
312
  config: McpStdioConfigSchema
275
313
  }),
@@ -278,6 +316,7 @@ const McpServerConfigSchema = zod.z.discriminatedUnion("transport", [
278
316
  instruction: zod.z.string().optional(),
279
317
  toolBlacklist: zod.z.array(zod.z.string()).optional(),
280
318
  omitToolDescription: zod.z.boolean().optional(),
319
+ prompts: zod.z.record(zod.z.string(), InternalPromptConfigSchema).optional(),
281
320
  transport: zod.z.literal("http"),
282
321
  config: McpHttpConfigSchema
283
322
  }),
@@ -286,6 +325,7 @@ const McpServerConfigSchema = zod.z.discriminatedUnion("transport", [
286
325
  instruction: zod.z.string().optional(),
287
326
  toolBlacklist: zod.z.array(zod.z.string()).optional(),
288
327
  omitToolDescription: zod.z.boolean().optional(),
328
+ prompts: zod.z.record(zod.z.string(), InternalPromptConfigSchema).optional(),
289
329
  transport: zod.z.literal("sse"),
290
330
  config: McpSseConfigSchema
291
331
  })
@@ -293,7 +333,10 @@ const McpServerConfigSchema = zod.z.discriminatedUnion("transport", [
293
333
  /**
294
334
  * Full internal MCP configuration schema
295
335
  */
296
- const InternalMcpConfigSchema = zod.z.object({ mcpServers: zod.z.record(zod.z.string(), McpServerConfigSchema) });
336
+ const InternalMcpConfigSchema = zod.z.object({
337
+ mcpServers: zod.z.record(zod.z.string(), McpServerConfigSchema),
338
+ skills: SkillsConfigSchema.optional()
339
+ });
297
340
  /**
298
341
  * Transform Claude Code config to internal format
299
342
  * Converts standard Claude Code MCP configuration to normalized internal format
@@ -315,6 +358,7 @@ function transformClaudeCodeConfig(claudeConfig) {
315
358
  instruction: stdioConfig.instruction || stdioConfig.config?.instruction,
316
359
  toolBlacklist: stdioConfig.config?.toolBlacklist,
317
360
  omitToolDescription: stdioConfig.config?.omitToolDescription,
361
+ prompts: stdioConfig.config?.prompts,
318
362
  transport: "stdio",
319
363
  config: {
320
364
  command: interpolatedCommand,
@@ -332,6 +376,7 @@ function transformClaudeCodeConfig(claudeConfig) {
332
376
  instruction: httpConfig.instruction || httpConfig.config?.instruction,
333
377
  toolBlacklist: httpConfig.config?.toolBlacklist,
334
378
  omitToolDescription: httpConfig.config?.omitToolDescription,
379
+ prompts: httpConfig.config?.prompts,
335
380
  transport,
336
381
  config: {
337
382
  url: interpolatedUrl,
@@ -340,7 +385,10 @@ function transformClaudeCodeConfig(claudeConfig) {
340
385
  };
341
386
  }
342
387
  }
343
- return { mcpServers: transformedServers };
388
+ return {
389
+ mcpServers: transformedServers,
390
+ skills: claudeConfig.skills
391
+ };
344
392
  }
345
393
  /**
346
394
  * Parse and validate MCP config from raw JSON
@@ -789,12 +837,14 @@ var ConfigFetcherService = class {
789
837
  //#region src/services/McpClientManagerService.ts
790
838
  /**
791
839
  * MCP Client wrapper for managing individual server connections
840
+ * This is an internal class used by McpClientManagerService
792
841
  */
793
842
  var McpClient = class {
794
843
  serverName;
795
844
  serverInstruction;
796
845
  toolBlacklist;
797
846
  omitToolDescription;
847
+ prompts;
798
848
  transport;
799
849
  client;
800
850
  childProcess;
@@ -804,6 +854,7 @@ var McpClient = class {
804
854
  this.serverInstruction = config.instruction;
805
855
  this.toolBlacklist = config.toolBlacklist;
806
856
  this.omitToolDescription = config.omitToolDescription;
857
+ this.prompts = config.prompts;
807
858
  this.transport = transport;
808
859
  this.client = client;
809
860
  }
@@ -899,7 +950,8 @@ var McpClientManagerService = class {
899
950
  const mcpClient = new McpClient(serverName, config.transport, client, {
900
951
  instruction: config.instruction,
901
952
  toolBlacklist: config.toolBlacklist,
902
- omitToolDescription: config.omitToolDescription
953
+ omitToolDescription: config.omitToolDescription,
954
+ prompts: config.prompts
903
955
  });
904
956
  try {
905
957
  await Promise.race([this.performConnection(mcpClient, config), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`Connection timeout after ${timeoutMs}ms`)), timeoutMs))]);
@@ -976,6 +1028,261 @@ var McpClientManagerService = class {
976
1028
  }
977
1029
  };
978
1030
 
1031
+ //#endregion
1032
+ //#region src/services/SkillService.ts
1033
+ /**
1034
+ * SkillService
1035
+ *
1036
+ * DESIGN PATTERNS:
1037
+ * - Service pattern for business logic encapsulation
1038
+ * - Single responsibility principle
1039
+ * - Lazy loading pattern for skill discovery
1040
+ *
1041
+ * CODING STANDARDS:
1042
+ * - Use async/await for asynchronous operations
1043
+ * - Throw descriptive errors for error cases
1044
+ * - Keep methods focused and well-named
1045
+ * - Document complex logic with comments
1046
+ *
1047
+ * AVOID:
1048
+ * - Mixing concerns (keep focused on single domain)
1049
+ * - Direct tool implementation (services should be tool-agnostic)
1050
+ */
1051
+ /**
1052
+ * Error thrown when skill loading fails
1053
+ */
1054
+ var SkillLoadError = class extends Error {
1055
+ constructor(message, filePath, cause) {
1056
+ super(message);
1057
+ this.filePath = filePath;
1058
+ this.cause = cause;
1059
+ this.name = "SkillLoadError";
1060
+ }
1061
+ };
1062
+ /**
1063
+ * Check if a path exists asynchronously
1064
+ * @param path - Path to check
1065
+ * @returns true if path exists, false otherwise
1066
+ * @throws Error for unexpected filesystem errors (permission denied, etc.)
1067
+ */
1068
+ async function pathExists(path) {
1069
+ try {
1070
+ await (0, node_fs_promises.access)(path);
1071
+ return true;
1072
+ } catch (error) {
1073
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
1074
+ throw new Error(`Failed to check path existence for "${path}": ${error instanceof Error ? error.message : "Unknown error"}`);
1075
+ }
1076
+ }
1077
+ /**
1078
+ * Service for loading and managing skills from configured skill directories.
1079
+ *
1080
+ * Skills are markdown files with YAML frontmatter that can be invoked via
1081
+ * the skill__ prefix in describe_tools and use_tool.
1082
+ *
1083
+ * Skills are only enabled when explicitly configured via the `skills.paths` array
1084
+ * in the MCP config.
1085
+ *
1086
+ * @example
1087
+ * // Config with skills enabled:
1088
+ * // skills:
1089
+ * // paths:
1090
+ * // - ".claude/skills"
1091
+ * // - "/absolute/path/to/skills"
1092
+ *
1093
+ * const skillService = new SkillService('/project/root', ['.claude/skills']);
1094
+ * const skills = await skillService.getSkills();
1095
+ */
1096
+ var SkillService = class {
1097
+ cwd;
1098
+ skillPaths;
1099
+ cachedSkills = null;
1100
+ skillsByName = null;
1101
+ /**
1102
+ * Creates a new SkillService instance
1103
+ * @param cwd - Current working directory for resolving relative paths
1104
+ * @param skillPaths - Array of paths to skills directories
1105
+ */
1106
+ constructor(cwd, skillPaths) {
1107
+ this.cwd = cwd;
1108
+ this.skillPaths = skillPaths;
1109
+ }
1110
+ /**
1111
+ * Get all available skills from configured directories.
1112
+ * Results are cached after first load.
1113
+ *
1114
+ * Skills from earlier entries in the config take precedence over
1115
+ * skills with the same name from later entries.
1116
+ *
1117
+ * @returns Array of loaded skills
1118
+ * @throws SkillLoadError if a critical error occurs during loading
1119
+ */
1120
+ async getSkills() {
1121
+ if (this.cachedSkills !== null) return this.cachedSkills;
1122
+ const skills = [];
1123
+ const loadedSkillNames = /* @__PURE__ */ new Set();
1124
+ for (const skillPath of this.skillPaths) {
1125
+ const skillsDir = (0, node_path.isAbsolute)(skillPath) ? skillPath : (0, node_path.join)(this.cwd, skillPath);
1126
+ const dirSkills = await this.loadSkillsFromDirectory(skillsDir, "project");
1127
+ for (const skill of dirSkills) if (!loadedSkillNames.has(skill.name)) {
1128
+ skills.push(skill);
1129
+ loadedSkillNames.add(skill.name);
1130
+ }
1131
+ }
1132
+ this.cachedSkills = skills;
1133
+ this.skillsByName = new Map(skills.map((skill) => [skill.name, skill]));
1134
+ return skills;
1135
+ }
1136
+ /**
1137
+ * Get a specific skill by name with O(1) lookup from cache.
1138
+ * @param name - The skill name (without skill__ prefix)
1139
+ * @returns The skill if found, undefined otherwise
1140
+ */
1141
+ async getSkill(name) {
1142
+ if (this.skillsByName === null) await this.getSkills();
1143
+ return this.skillsByName?.get(name);
1144
+ }
1145
+ /**
1146
+ * Clears the cached skills to force a fresh reload on the next getSkills() or getSkill() call.
1147
+ * Use this when skill files have been modified on disk.
1148
+ */
1149
+ clearCache() {
1150
+ this.cachedSkills = null;
1151
+ this.skillsByName = null;
1152
+ }
1153
+ /**
1154
+ * Load skills from a directory.
1155
+ * Supports both flat structure (SKILL.md) and nested structure (name/SKILL.md).
1156
+ *
1157
+ * @param dirPath - Path to the skills directory
1158
+ * @param location - Whether this is a 'project' or 'user' skill directory
1159
+ * @returns Array of successfully loaded skills (skips invalid skills)
1160
+ * @throws SkillLoadError if there's a critical I/O error
1161
+ *
1162
+ * @example
1163
+ * // Load skills from project directory
1164
+ * const skills = await this.loadSkillsFromDirectory('/path/to/.claude/skills', 'project');
1165
+ * // Returns: [{ name: 'pdf', description: '...', location: 'project', ... }]
1166
+ */
1167
+ async loadSkillsFromDirectory(dirPath, location) {
1168
+ const skills = [];
1169
+ try {
1170
+ if (!await pathExists(dirPath)) return skills;
1171
+ } catch (error) {
1172
+ throw new SkillLoadError(`Cannot access skills directory: ${error instanceof Error ? error.message : "Unknown error"}`, dirPath, error instanceof Error ? error : void 0);
1173
+ }
1174
+ let entries;
1175
+ try {
1176
+ entries = await (0, node_fs_promises.readdir)(dirPath);
1177
+ } catch (error) {
1178
+ throw new SkillLoadError(`Failed to read skills directory: ${error instanceof Error ? error.message : "Unknown error"}`, dirPath, error instanceof Error ? error : void 0);
1179
+ }
1180
+ for (const entry of entries) {
1181
+ const entryPath = (0, node_path.join)(dirPath, entry);
1182
+ let entryStat;
1183
+ try {
1184
+ entryStat = await (0, node_fs_promises.stat)(entryPath);
1185
+ } catch (error) {
1186
+ console.warn(`Skipping entry ${entryPath}: ${error instanceof Error ? error.message : "Unknown error"}`);
1187
+ continue;
1188
+ }
1189
+ if (entryStat.isDirectory()) {
1190
+ const skillFilePath = (0, node_path.join)(entryPath, "SKILL.md");
1191
+ try {
1192
+ if (await pathExists(skillFilePath)) {
1193
+ const skill = await this.loadSkillFile(skillFilePath, location);
1194
+ if (skill) skills.push(skill);
1195
+ }
1196
+ } catch (error) {
1197
+ console.warn(`Skipping skill at ${skillFilePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
1198
+ continue;
1199
+ }
1200
+ } else if (entry === "SKILL.md") try {
1201
+ const skill = await this.loadSkillFile(entryPath, location);
1202
+ if (skill) skills.push(skill);
1203
+ } catch (error) {
1204
+ console.warn(`Skipping skill at ${entryPath}: ${error instanceof Error ? error.message : "Unknown error"}`);
1205
+ continue;
1206
+ }
1207
+ }
1208
+ return skills;
1209
+ }
1210
+ /**
1211
+ * Load a single skill file and parse its frontmatter.
1212
+ *
1213
+ * @param filePath - Path to the SKILL.md file
1214
+ * @param location - Whether this is a 'project' or 'user' skill
1215
+ * @returns The loaded skill, or null if the file is invalid (missing required frontmatter)
1216
+ * @throws SkillLoadError if there's an I/O error reading the file
1217
+ *
1218
+ * @example
1219
+ * // Load a skill from a file
1220
+ * const skill = await this.loadSkillFile('/path/to/pdf/SKILL.md', 'project');
1221
+ * // Returns: { name: 'pdf', description: 'PDF skill', location: 'project', content: '...', basePath: '/path/to/pdf' }
1222
+ * // Returns null if frontmatter is missing name or description
1223
+ */
1224
+ async loadSkillFile(filePath, location) {
1225
+ let content;
1226
+ try {
1227
+ content = await (0, node_fs_promises.readFile)(filePath, "utf-8");
1228
+ } catch (error) {
1229
+ throw new SkillLoadError(`Failed to read skill file: ${error instanceof Error ? error.message : "Unknown error"}`, filePath, error instanceof Error ? error : void 0);
1230
+ }
1231
+ const { metadata, body } = this.parseFrontmatter(content);
1232
+ if (!metadata.name || !metadata.description) return null;
1233
+ return {
1234
+ name: metadata.name,
1235
+ description: metadata.description,
1236
+ location,
1237
+ content: body,
1238
+ basePath: (0, node_path.dirname)(filePath)
1239
+ };
1240
+ }
1241
+ /**
1242
+ * Parse YAML frontmatter from markdown content.
1243
+ * Frontmatter is delimited by --- at start and end.
1244
+ *
1245
+ * @param content - Full markdown content with frontmatter
1246
+ * @returns Parsed metadata and body content
1247
+ *
1248
+ * @example
1249
+ * // Input content:
1250
+ * // ---
1251
+ * // name: my-skill
1252
+ * // description: A sample skill
1253
+ * // ---
1254
+ * // # Skill Content
1255
+ * // This is the skill body.
1256
+ *
1257
+ * const result = parseFrontmatter(content);
1258
+ * // result.metadata = { name: 'my-skill', description: 'A sample skill' }
1259
+ * // result.body = '# Skill Content\nThis is the skill body.'
1260
+ */
1261
+ parseFrontmatter(content) {
1262
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
1263
+ if (!match) return {
1264
+ metadata: {},
1265
+ body: content
1266
+ };
1267
+ const [, frontmatter, body] = match;
1268
+ const metadata = {};
1269
+ const lines = frontmatter.split("\n");
1270
+ for (const line of lines) {
1271
+ const colonIndex = line.indexOf(":");
1272
+ if (colonIndex > 0) {
1273
+ const key = line.slice(0, colonIndex).trim();
1274
+ let value = line.slice(colonIndex + 1).trim();
1275
+ if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
1276
+ if (key === "name" || key === "description" || key === "license") metadata[key] = value;
1277
+ }
1278
+ }
1279
+ return {
1280
+ metadata,
1281
+ body: body.trim()
1282
+ };
1283
+ }
1284
+ };
1285
+
979
1286
  //#endregion
980
1287
  //#region src/utils/findConfigFile.ts
981
1288
  /**
@@ -1045,15 +1352,165 @@ function parseToolName(toolName) {
1045
1352
  return { actualToolName: toolName };
1046
1353
  }
1047
1354
 
1355
+ //#endregion
1356
+ //#region src/templates/skills-description.liquid?raw
1357
+ var skills_description_default = "{% if skills.size > 0 %}\n<skills>\n<instructions>\nWhen users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.\n\nHow to use skills:\n- Invoke skills using this tool with the skill name only (no arguments)\n- When you invoke a skill, you will see <command-message>The \"{name}\" skill is loading</command-message>\n- The skill's prompt will expand and provide detailed instructions on how to complete the task\n- Examples:\n - `skill: \"pdf\"` - invoke the pdf skill\n - `skill: \"xlsx\"` - invoke the xlsx skill\n - `skill: \"ms-office-suite:pdf\"` - invoke using fully qualified name\n\nImportant:\n- Only use skills listed in <available_skills> below\n- Do not invoke a skill that is already running\n- Do not use this tool for built-in CLI commands (like /help, /clear, etc.)\n</instructions>\n\n<available_skills>\n{% for skill in skills -%}\n<item><name>{{ skill.displayName }}</name><description>{{ skill.description }}</description></item>\n{% endfor -%}\n</available_skills>\n</skills>\n{% endif %}\n";
1358
+
1359
+ //#endregion
1360
+ //#region src/templates/mcp-servers-description.liquid?raw
1361
+ var mcp_servers_description_default = "<mcp_servers>\n<instructions>\nBefore you use any tools above, you MUST call this tool with a list of tool names to learn how to use them properly before use_tool; this includes:\n- Arguments schema needed to pass to the tool use\n- Description about each tool\n\nThis tool is optimized for batch queries - you can request multiple tools at once for better performance.\n</instructions>\n\n{% for server in servers -%}\n<server name=\"{{ server.name }}\">\n{% if server.instruction -%}\n<instruction>{{ server.instruction }}</instruction>\n{% endif -%}\n<tools>\n{% if server.omitToolDescription -%}\n{{ server.toolNames | join: \", \" }}\n{% else -%}\n{% for tool in server.tools -%}\n<item><name>{{ tool.displayName }}</name><description>{{ tool.description | default: \"No description\" }}</description></item>\n{% endfor -%}\n{% endif -%}\n</tools>\n</server>\n{% endfor -%}\n</mcp_servers>\n";
1362
+
1048
1363
  //#endregion
1049
1364
  //#region src/tools/DescribeToolsTool.ts
1365
+ /**
1366
+ * Prefix used to identify skill invocations
1367
+ */
1368
+ const SKILL_PREFIX$1 = "skill__";
1369
+ /**
1370
+ * DescribeToolsTool provides progressive disclosure of MCP tools and skills.
1371
+ *
1372
+ * This tool lists available tools from all connected MCP servers and skills
1373
+ * from the configured skills directories. Users can query for specific tools
1374
+ * or skills to get detailed input schemas and descriptions.
1375
+ *
1376
+ * Tool naming conventions:
1377
+ * - Unique tools: use plain name (e.g., "browser_click")
1378
+ * - Clashing tools: use serverName__toolName format (e.g., "playwright__click")
1379
+ * - Skills: use skill__skillName format (e.g., "skill__pdf")
1380
+ *
1381
+ * @example
1382
+ * const tool = new DescribeToolsTool(clientManager, skillService);
1383
+ * const definition = await tool.getDefinition();
1384
+ * const result = await tool.execute({ toolNames: ['browser_click', 'skill__pdf'] });
1385
+ */
1050
1386
  var DescribeToolsTool = class DescribeToolsTool {
1051
1387
  static TOOL_NAME = "describe_tools";
1052
1388
  clientManager;
1053
- constructor(clientManager) {
1389
+ skillService;
1390
+ liquid = new liquidjs.Liquid();
1391
+ /**
1392
+ * Creates a new DescribeToolsTool instance
1393
+ * @param clientManager - The MCP client manager for accessing remote servers
1394
+ * @param skillService - Optional skill service for loading skills
1395
+ */
1396
+ constructor(clientManager, skillService) {
1054
1397
  this.clientManager = clientManager;
1398
+ this.skillService = skillService;
1055
1399
  }
1056
- async getDefinition() {
1400
+ /**
1401
+ * Collects skills derived from prompt configurations across all connected MCP servers.
1402
+ * Prompts with a skill configuration are converted to skill format for display.
1403
+ *
1404
+ * @returns Array of skill template data derived from prompts
1405
+ */
1406
+ collectPromptSkills() {
1407
+ const clients = this.clientManager.getAllClients();
1408
+ const promptSkills = [];
1409
+ for (const client of clients) {
1410
+ if (!client.prompts) continue;
1411
+ for (const promptConfig of Object.values(client.prompts)) if (promptConfig.skill) promptSkills.push({
1412
+ name: promptConfig.skill.name,
1413
+ displayName: promptConfig.skill.name,
1414
+ description: promptConfig.skill.description
1415
+ });
1416
+ }
1417
+ return promptSkills;
1418
+ }
1419
+ /**
1420
+ * Finds a prompt-based skill by name from all connected MCP servers.
1421
+ * Returns the prompt name and skill config for fetching the prompt content.
1422
+ *
1423
+ * @param skillName - The skill name to search for
1424
+ * @returns Object with serverName, promptName, and skill config, or undefined if not found
1425
+ */
1426
+ findPromptSkill(skillName) {
1427
+ if (!skillName) return void 0;
1428
+ const clients = this.clientManager.getAllClients();
1429
+ for (const client of clients) {
1430
+ if (!client.prompts) continue;
1431
+ for (const [promptName, promptConfig] of Object.entries(client.prompts)) if (promptConfig.skill && promptConfig.skill.name === skillName) return {
1432
+ serverName: client.serverName,
1433
+ promptName,
1434
+ skill: promptConfig.skill
1435
+ };
1436
+ }
1437
+ }
1438
+ /**
1439
+ * Retrieves skill content from a prompt-based skill configuration.
1440
+ * Fetches the prompt from the MCP server and extracts text content.
1441
+ *
1442
+ * @param skillName - The skill name being requested
1443
+ * @returns SkillDescription if found and successfully fetched, undefined otherwise
1444
+ */
1445
+ async getPromptSkillContent(skillName) {
1446
+ const promptSkill = this.findPromptSkill(skillName);
1447
+ if (!promptSkill) return void 0;
1448
+ const client = this.clientManager.getClient(promptSkill.serverName);
1449
+ if (!client) {
1450
+ console.error(`Client not found for server '${promptSkill.serverName}' when fetching prompt skill '${skillName}'`);
1451
+ return;
1452
+ }
1453
+ try {
1454
+ const instructions = (await client.getPrompt(promptSkill.promptName)).messages?.map((m) => {
1455
+ const content = m.content;
1456
+ if (typeof content === "string") return content;
1457
+ if (content && typeof content === "object" && "text" in content) return String(content.text);
1458
+ return "";
1459
+ }).join("\n") || "";
1460
+ return {
1461
+ name: promptSkill.skill.name,
1462
+ location: promptSkill.skill.folder || `prompt:${promptSkill.serverName}/${promptSkill.promptName}`,
1463
+ instructions
1464
+ };
1465
+ } catch (error) {
1466
+ console.error(`Failed to get prompt-based skill '${skillName}': ${error instanceof Error ? error.message : "Unknown error"}`);
1467
+ return;
1468
+ }
1469
+ }
1470
+ /**
1471
+ * Builds the skills section of the tool description using a Liquid template.
1472
+ *
1473
+ * Retrieves all available skills from the SkillService and prompt-based skills,
1474
+ * then renders them using the skills-description.liquid template. Skills are only
1475
+ * prefixed with skill__ when their name clashes with an MCP tool or another skill.
1476
+ *
1477
+ * @param mcpToolNames - Set of MCP tool names to check for clashes
1478
+ * @returns Rendered skills section string with available skills list
1479
+ */
1480
+ async buildSkillsSection(mcpToolNames) {
1481
+ const rawSkills = this.skillService ? await this.skillService.getSkills() : [];
1482
+ const promptSkills = this.collectPromptSkills();
1483
+ const allSkillsData = [...rawSkills.map((skill) => ({
1484
+ name: skill.name,
1485
+ displayName: skill.name,
1486
+ description: skill.description
1487
+ })), ...promptSkills];
1488
+ const skillNameCounts = /* @__PURE__ */ new Map();
1489
+ for (const skill of allSkillsData) skillNameCounts.set(skill.name, (skillNameCounts.get(skill.name) || 0) + 1);
1490
+ const skills = allSkillsData.map((skill) => {
1491
+ const clashesWithMcpTool = mcpToolNames.has(skill.name);
1492
+ const clashesWithOtherSkill = (skillNameCounts.get(skill.name) || 0) > 1;
1493
+ const needsPrefix = clashesWithMcpTool || clashesWithOtherSkill;
1494
+ return {
1495
+ name: skill.name,
1496
+ displayName: needsPrefix ? `${SKILL_PREFIX$1}${skill.name}` : skill.name,
1497
+ description: skill.description
1498
+ };
1499
+ });
1500
+ return this.liquid.parseAndRender(skills_description_default, { skills });
1501
+ }
1502
+ /**
1503
+ * Builds the MCP servers section of the tool description using a Liquid template.
1504
+ *
1505
+ * Collects all tools from connected MCP servers, detects name clashes,
1506
+ * and renders them using the mcp-servers-description.liquid template.
1507
+ *
1508
+ * Tool names are prefixed with serverName__ when the same tool exists
1509
+ * on multiple servers to avoid ambiguity.
1510
+ *
1511
+ * @returns Object with rendered servers section and set of all tool names for skill clash detection
1512
+ */
1513
+ async buildServersSection() {
1057
1514
  const clients = this.clientManager.getAllClients();
1058
1515
  const toolToServers = /* @__PURE__ */ new Map();
1059
1516
  const serverToolsMap = /* @__PURE__ */ new Map();
@@ -1072,30 +1529,63 @@ var DescribeToolsTool = class DescribeToolsTool {
1072
1529
  serverToolsMap.set(client.serverName, []);
1073
1530
  }
1074
1531
  }));
1075
- const serverDescriptions = clients.map((client) => {
1076
- const tools = serverToolsMap.get(client.serverName) || [];
1077
- const formatToolName = (toolName) => {
1078
- return (toolToServers.get(toolName) || []).length > 1 ? `${client.serverName}__${toolName}` : toolName;
1532
+ /**
1533
+ * Formats tool name with server prefix if the tool exists on multiple servers
1534
+ * @param toolName - The original tool name
1535
+ * @param serverName - The server providing this tool
1536
+ * @returns Tool name prefixed with serverName__ if clashing, otherwise plain name
1537
+ */
1538
+ const formatToolName = (toolName, serverName) => {
1539
+ return (toolToServers.get(toolName) || []).length > 1 ? `${serverName}__${toolName}` : toolName;
1540
+ };
1541
+ const allToolNames = /* @__PURE__ */ new Set();
1542
+ const servers = clients.map((client) => {
1543
+ const formattedTools = (serverToolsMap.get(client.serverName) || []).map((t) => ({
1544
+ displayName: formatToolName(t.name, client.serverName),
1545
+ description: t.description
1546
+ }));
1547
+ for (const tool of formattedTools) allToolNames.add(tool.displayName);
1548
+ return {
1549
+ name: client.serverName,
1550
+ instruction: client.serverInstruction,
1551
+ omitToolDescription: client.omitToolDescription || false,
1552
+ tools: formattedTools,
1553
+ toolNames: formattedTools.map((t) => t.displayName)
1079
1554
  };
1080
- const toolList = client.omitToolDescription ? tools.map((t) => formatToolName(t.name)).join(", ") : tools.map((t) => `${formatToolName(t.name)}: """${t.description || "No description"}"""`).join("\n");
1081
- const instructionLine = client.serverInstruction ? `\n"""${client.serverInstruction}"""` : "";
1082
- return `\n\n### Server: ${client.serverName}${instructionLine}\n\n- Available tools:\n${toolList || "No tools available"}`;
1083
1555
  });
1556
+ return {
1557
+ content: await this.liquid.parseAndRender(mcp_servers_description_default, { servers }),
1558
+ toolNames: allToolNames
1559
+ };
1560
+ }
1561
+ /**
1562
+ * Gets the tool definition including available servers, tools, and skills.
1563
+ *
1564
+ * The definition includes:
1565
+ * - List of all connected MCP servers with their available tools
1566
+ * - List of available skills with skill__ prefix
1567
+ * - Usage instructions for querying tool/skill details
1568
+ *
1569
+ * Tool names are prefixed with serverName__ when the same tool name
1570
+ * exists on multiple servers to avoid ambiguity.
1571
+ *
1572
+ * @returns Tool definition with description and input schema
1573
+ */
1574
+ async getDefinition() {
1575
+ const serversResult = await this.buildServersSection();
1576
+ const skillsSection = await this.buildSkillsSection(serversResult.toolNames);
1084
1577
  return {
1085
1578
  name: DescribeToolsTool.TOOL_NAME,
1086
- description: `## Available MCP Servers:${serverDescriptions.join("")}
1087
-
1088
- ## Usage:
1089
- Before you use any tools above, you MUST call this tool with a list of tool names to learn how to use them properly before use_tool; this includes:
1090
- - Arguments schema needed to pass to the tool use
1091
- - Description about each tool
1092
-
1093
- This tool is optimized for batch queries - you can request multiple tools at once for better performance.`,
1579
+ description: `${serversResult.content}
1580
+ ${skillsSection}`,
1094
1581
  inputSchema: {
1095
1582
  type: "object",
1096
1583
  properties: { toolNames: {
1097
1584
  type: "array",
1098
- items: { type: "string" },
1585
+ items: {
1586
+ type: "string",
1587
+ minLength: 1
1588
+ },
1099
1589
  description: "List of tool names to get detailed information about",
1100
1590
  minItems: 1
1101
1591
  } },
@@ -1104,6 +1594,17 @@ This tool is optimized for batch queries - you can request multiple tools at onc
1104
1594
  }
1105
1595
  };
1106
1596
  }
1597
+ /**
1598
+ * Executes tool description lookup for the requested tool and skill names.
1599
+ *
1600
+ * Handles three types of lookups:
1601
+ * 1. skill__name - Returns skill information from SkillService
1602
+ * 2. serverName__toolName - Returns tool from specific server
1603
+ * 3. plainToolName - Returns tool(s) from all servers (multiple if clashing)
1604
+ *
1605
+ * @param input - Object containing toolNames array
1606
+ * @returns CallToolResult with tool/skill descriptions or error
1607
+ */
1107
1608
  async execute(input) {
1108
1609
  try {
1109
1610
  const { toolNames } = input;
@@ -1121,9 +1622,13 @@ This tool is optimized for batch queries - you can request multiple tools at onc
1121
1622
  try {
1122
1623
  const tools = await client.listTools();
1123
1624
  const blacklist = new Set(client.toolBlacklist || []);
1124
- const filteredTools = tools.filter((t) => !blacklist.has(t.name));
1125
- serverToolsMap.set(client.serverName, filteredTools);
1126
- for (const tool of filteredTools) {
1625
+ const typedTools = tools.filter((t) => !blacklist.has(t.name)).map((t) => ({
1626
+ name: t.name,
1627
+ description: t.description,
1628
+ inputSchema: t.inputSchema
1629
+ }));
1630
+ serverToolsMap.set(client.serverName, typedTools);
1631
+ for (const tool of typedTools) {
1127
1632
  if (!toolToServers.has(tool.name)) toolToServers.set(tool.name, []);
1128
1633
  toolToServers.get(tool.name).push(client.serverName);
1129
1634
  }
@@ -1133,13 +1638,35 @@ This tool is optimized for batch queries - you can request multiple tools at onc
1133
1638
  }
1134
1639
  }));
1135
1640
  const foundTools = [];
1136
- const notFoundTools = [];
1137
- for (const requestedToolName of toolNames) {
1138
- const { serverName, actualToolName } = parseToolName(requestedToolName);
1641
+ const foundSkills = [];
1642
+ const notFoundItems = [];
1643
+ for (const requestedName of toolNames) {
1644
+ if (requestedName.startsWith(SKILL_PREFIX$1)) {
1645
+ const skillName = requestedName.slice(7);
1646
+ if (this.skillService) {
1647
+ const skill = await this.skillService.getSkill(skillName);
1648
+ if (skill) {
1649
+ foundSkills.push({
1650
+ name: skill.name,
1651
+ location: skill.basePath,
1652
+ instructions: skill.content
1653
+ });
1654
+ continue;
1655
+ }
1656
+ }
1657
+ const promptSkillContent = await this.getPromptSkillContent(skillName);
1658
+ if (promptSkillContent) {
1659
+ foundSkills.push(promptSkillContent);
1660
+ continue;
1661
+ }
1662
+ notFoundItems.push(requestedName);
1663
+ continue;
1664
+ }
1665
+ const { serverName, actualToolName } = parseToolName(requestedName);
1139
1666
  if (serverName) {
1140
1667
  const serverTools = serverToolsMap.get(serverName);
1141
1668
  if (!serverTools) {
1142
- notFoundTools.push(requestedToolName);
1669
+ notFoundItems.push(requestedName);
1143
1670
  continue;
1144
1671
  }
1145
1672
  const tool = serverTools.find((t) => t.name === actualToolName);
@@ -1151,11 +1678,27 @@ This tool is optimized for batch queries - you can request multiple tools at onc
1151
1678
  inputSchema: tool.inputSchema
1152
1679
  }
1153
1680
  });
1154
- else notFoundTools.push(requestedToolName);
1681
+ else notFoundItems.push(requestedName);
1155
1682
  } else {
1156
1683
  const servers = toolToServers.get(actualToolName);
1157
1684
  if (!servers || servers.length === 0) {
1158
- notFoundTools.push(requestedToolName);
1685
+ if (this.skillService) {
1686
+ const skill = await this.skillService.getSkill(actualToolName);
1687
+ if (skill) {
1688
+ foundSkills.push({
1689
+ name: skill.name,
1690
+ location: skill.basePath,
1691
+ instructions: skill.content
1692
+ });
1693
+ continue;
1694
+ }
1695
+ }
1696
+ const promptSkillContent = await this.getPromptSkillContent(actualToolName);
1697
+ if (promptSkillContent) {
1698
+ foundSkills.push(promptSkillContent);
1699
+ continue;
1700
+ }
1701
+ notFoundItems.push(requestedName);
1159
1702
  continue;
1160
1703
  }
1161
1704
  if (servers.length === 1) {
@@ -1182,17 +1725,27 @@ This tool is optimized for batch queries - you can request multiple tools at onc
1182
1725
  }
1183
1726
  }
1184
1727
  }
1185
- if (foundTools.length === 0) return {
1728
+ if (foundTools.length === 0 && foundSkills.length === 0) return {
1186
1729
  content: [{
1187
1730
  type: "text",
1188
- text: `None of the requested tools found on any connected server.\nRequested: ${toolNames.join(", ")}\nUse describe_tools to see available tools.`
1731
+ text: `None of the requested tools/skills found.\nRequested: ${toolNames.join(", ")}\nUse describe_tools to see available tools and skills.`
1189
1732
  }],
1190
1733
  isError: true
1191
1734
  };
1192
- const result = { tools: foundTools };
1193
- if (notFoundTools.length > 0) {
1194
- result.notFound = notFoundTools;
1195
- result.warnings = [`Tools not found: ${notFoundTools.join(", ")}`];
1735
+ const result = {};
1736
+ const nextSteps = [];
1737
+ if (foundTools.length > 0) {
1738
+ result.tools = foundTools;
1739
+ nextSteps.push("For MCP tools: Use the use_tool function with toolName and toolArgs based on the inputSchema above.");
1740
+ }
1741
+ if (foundSkills.length > 0) {
1742
+ result.skills = foundSkills;
1743
+ nextSteps.push(`For skill, just follow skill's description to continue.`);
1744
+ }
1745
+ if (nextSteps.length > 0) result.nextSteps = nextSteps;
1746
+ if (notFoundItems.length > 0) {
1747
+ result.notFound = notFoundItems;
1748
+ result.warnings = [`Items not found: ${notFoundItems.join(", ")}`];
1196
1749
  }
1197
1750
  return { content: [{
1198
1751
  type: "text",
@@ -1212,16 +1765,48 @@ This tool is optimized for batch queries - you can request multiple tools at onc
1212
1765
 
1213
1766
  //#endregion
1214
1767
  //#region src/tools/UseToolTool.ts
1768
+ /**
1769
+ * Prefix used to identify skill invocations (e.g., skill__pdf)
1770
+ */
1771
+ const SKILL_PREFIX = "skill__";
1772
+ /**
1773
+ * UseToolTool executes MCP tools and skills with proper error handling.
1774
+ *
1775
+ * This tool supports three invocation patterns:
1776
+ * 1. skill__skillName - Invokes a skill from the configured skills directory
1777
+ * 2. serverName__toolName - Invokes a tool on a specific MCP server
1778
+ * 3. plainToolName - Searches all servers for a unique tool match
1779
+ *
1780
+ * @example
1781
+ * const tool = new UseToolTool(clientManager, skillService);
1782
+ * await tool.execute({ toolName: 'skill__pdf' }); // Invoke a skill
1783
+ * await tool.execute({ toolName: 'playwright__browser_click', toolArgs: { ref: 'btn' } });
1784
+ */
1215
1785
  var UseToolTool = class UseToolTool {
1216
1786
  static TOOL_NAME = "use_tool";
1217
1787
  clientManager;
1218
- constructor(clientManager) {
1788
+ skillService;
1789
+ /**
1790
+ * Creates a new UseToolTool instance
1791
+ * @param clientManager - The MCP client manager for accessing remote servers
1792
+ * @param skillService - Optional skill service for loading and executing skills
1793
+ */
1794
+ constructor(clientManager, skillService) {
1219
1795
  this.clientManager = clientManager;
1796
+ this.skillService = skillService;
1220
1797
  }
1798
+ /**
1799
+ * Returns the MCP tool definition with name, description, and input schema.
1800
+ *
1801
+ * The definition describes how to use this tool to execute MCP tools or skills,
1802
+ * including the skill__ prefix format for skill invocations.
1803
+ *
1804
+ * @returns The tool definition conforming to MCP spec
1805
+ */
1221
1806
  getDefinition() {
1222
1807
  return {
1223
1808
  name: UseToolTool.TOOL_NAME,
1224
- description: `Execute an MCP tool with provided arguments. You MUST call describe_tools first to discover the tool's correct arguments. Then to use tool:
1809
+ 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:
1225
1810
  - Provide toolName and toolArgs based on the schema
1226
1811
  - If multiple servers provide the same tool, specify serverName
1227
1812
  `,
@@ -1230,7 +1815,8 @@ var UseToolTool = class UseToolTool {
1230
1815
  properties: {
1231
1816
  toolName: {
1232
1817
  type: "string",
1233
- description: "Name of the tool to execute"
1818
+ description: "Name of the tool to execute",
1819
+ minLength: 1
1234
1820
  },
1235
1821
  toolArgs: {
1236
1822
  type: "object",
@@ -1242,9 +1828,88 @@ var UseToolTool = class UseToolTool {
1242
1828
  }
1243
1829
  };
1244
1830
  }
1831
+ /**
1832
+ * Returns guidance message for skill invocation.
1833
+ *
1834
+ * Skills are not executed via use_tool - they provide instructions that should
1835
+ * be followed directly. This method returns a message directing users to use
1836
+ * describe_tools to get the skill details and follow its instructions.
1837
+ *
1838
+ * @param skill - The skill that was requested
1839
+ * @returns CallToolResult with guidance message
1840
+ */
1841
+ executeSkill(skill) {
1842
+ return { content: [{
1843
+ type: "text",
1844
+ text: `Skill "${skill.name}" found. Skills provide instructions and should not be executed via use_tool.\n\nUse describe_tools to view the skill details at: ${skill.basePath}\n\nThen follow the skill's instructions directly.`
1845
+ }] };
1846
+ }
1847
+ /**
1848
+ * Finds a prompt-based skill by name from all connected MCP servers.
1849
+ *
1850
+ * @param skillName - The skill name to search for
1851
+ * @returns PromptSkillMatch if found, undefined otherwise
1852
+ */
1853
+ findPromptSkill(skillName) {
1854
+ if (!skillName) return void 0;
1855
+ const clients = this.clientManager.getAllClients();
1856
+ for (const client of clients) {
1857
+ if (!client.prompts) continue;
1858
+ for (const [promptName, promptConfig] of Object.entries(client.prompts)) if (promptConfig.skill && promptConfig.skill.name === skillName) return {
1859
+ serverName: client.serverName,
1860
+ promptName,
1861
+ skill: promptConfig.skill
1862
+ };
1863
+ }
1864
+ }
1865
+ /**
1866
+ * Returns guidance message for prompt-based skill invocation.
1867
+ *
1868
+ * @param promptSkill - The prompt skill match that was found
1869
+ * @returns CallToolResult with guidance message
1870
+ */
1871
+ executePromptSkill(promptSkill) {
1872
+ const location = promptSkill.skill.folder || `prompt:${promptSkill.serverName}/${promptSkill.promptName}`;
1873
+ return { content: [{
1874
+ type: "text",
1875
+ text: `Skill "${promptSkill.skill.name}" found. Skills provide instructions and should not be executed via use_tool.\n\nUse describe_tools to view the skill details at: ${location}\n\nThen follow the skill's instructions directly.`
1876
+ }] };
1877
+ }
1878
+ /**
1879
+ * Executes a tool or skill based on the provided input.
1880
+ *
1881
+ * Handles three invocation patterns:
1882
+ * 1. skill__skillName - Routes to skill execution via SkillService
1883
+ * 2. serverName__toolName - Routes to specific MCP server
1884
+ * 3. plainToolName - Searches all servers for unique match
1885
+ *
1886
+ * Edge cases:
1887
+ * - Returns error if skill not found when using skill__ prefix
1888
+ * - Returns error if tool is blacklisted on target server
1889
+ * - Returns disambiguation message if tool exists on multiple servers
1890
+ *
1891
+ * @param input - The tool/skill name and optional arguments
1892
+ * @returns CallToolResult with execution output or error
1893
+ */
1245
1894
  async execute(input) {
1246
1895
  try {
1247
1896
  const { toolName: inputToolName, toolArgs = {} } = input;
1897
+ if (inputToolName.startsWith(SKILL_PREFIX)) {
1898
+ const skillName = inputToolName.slice(7);
1899
+ if (this.skillService) {
1900
+ const skill = await this.skillService.getSkill(skillName);
1901
+ if (skill) return this.executeSkill(skill);
1902
+ }
1903
+ const promptSkill = this.findPromptSkill(skillName);
1904
+ if (promptSkill) return this.executePromptSkill(promptSkill);
1905
+ return {
1906
+ content: [{
1907
+ type: "text",
1908
+ text: `Skill "${skillName}" not found. Use describe_tools to see available skills.`
1909
+ }],
1910
+ isError: true
1911
+ };
1912
+ }
1248
1913
  const clients = this.clientManager.getAllClients();
1249
1914
  const { serverName, actualToolName } = parseToolName(inputToolName);
1250
1915
  if (serverName) {
@@ -1286,13 +1951,21 @@ var UseToolTool = class UseToolTool {
1286
1951
  return null;
1287
1952
  }));
1288
1953
  matchingServers.push(...results.filter((r) => r !== null));
1289
- if (matchingServers.length === 0) return {
1290
- content: [{
1291
- type: "text",
1292
- text: `Tool "${actualToolName}" not found on any connected server. Use describe_tools to see available tools.`
1293
- }],
1294
- isError: true
1295
- };
1954
+ if (matchingServers.length === 0) {
1955
+ if (this.skillService) {
1956
+ const skill = await this.skillService.getSkill(actualToolName);
1957
+ if (skill) return this.executeSkill(skill);
1958
+ }
1959
+ const promptSkill = this.findPromptSkill(actualToolName);
1960
+ if (promptSkill) return this.executePromptSkill(promptSkill);
1961
+ return {
1962
+ content: [{
1963
+ type: "text",
1964
+ text: `Tool or skill "${actualToolName}" not found. Use describe_tools to see available tools and skills.`
1965
+ }],
1966
+ isError: true
1967
+ };
1968
+ }
1296
1969
  if (matchingServers.length > 1) return {
1297
1970
  content: [{
1298
1971
  type: "text",
@@ -1351,34 +2024,114 @@ async function createServer(options) {
1351
2024
  const server = new __modelcontextprotocol_sdk_server_index_js.Server({
1352
2025
  name: "@agiflowai/one-mcp",
1353
2026
  version: "0.1.0"
1354
- }, { capabilities: { tools: {} } });
2027
+ }, { capabilities: {
2028
+ tools: {},
2029
+ prompts: {}
2030
+ } });
1355
2031
  const clientManager = new McpClientManagerService();
1356
- if (options?.configFilePath) try {
1357
- const config = await new ConfigFetcherService({
1358
- configFilePath: options.configFilePath,
1359
- useCache: !options.noCache
1360
- }).fetchConfiguration(options.noCache || false);
2032
+ let configSkills;
2033
+ if (options?.configFilePath) {
2034
+ let config;
2035
+ try {
2036
+ config = await new ConfigFetcherService({
2037
+ configFilePath: options.configFilePath,
2038
+ useCache: !options.noCache
2039
+ }).fetchConfiguration(options.noCache || false);
2040
+ } catch (error) {
2041
+ throw new Error(`Failed to load MCP configuration from '${options.configFilePath}': ${error instanceof Error ? error.message : String(error)}`);
2042
+ }
2043
+ configSkills = config.skills;
2044
+ const failedConnections = [];
1361
2045
  const connectionPromises = Object.entries(config.mcpServers).map(async ([serverName, serverConfig]) => {
1362
2046
  try {
1363
2047
  await clientManager.connectToServer(serverName, serverConfig);
1364
2048
  console.error(`Connected to MCP server: ${serverName}`);
1365
2049
  } catch (error) {
2050
+ const err = error instanceof Error ? error : new Error(String(error));
2051
+ failedConnections.push({
2052
+ serverName,
2053
+ error: err
2054
+ });
1366
2055
  console.error(`Failed to connect to ${serverName}:`, error);
1367
2056
  }
1368
2057
  });
1369
2058
  await Promise.all(connectionPromises);
1370
- } catch (error) {
1371
- console.error("Failed to load MCP configuration:", error);
2059
+ 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(", ")}`);
2060
+ 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(", ")}`);
1372
2061
  }
1373
- const describeTools = new DescribeToolsTool(clientManager);
1374
- const useTool = new UseToolTool(clientManager);
2062
+ const skillsConfig = options?.skills || configSkills;
2063
+ const skillService = skillsConfig && skillsConfig.paths.length > 0 ? new SkillService(process.cwd(), skillsConfig.paths) : void 0;
2064
+ const describeTools = new DescribeToolsTool(clientManager, skillService);
2065
+ const useTool = new UseToolTool(clientManager, skillService);
1375
2066
  server.setRequestHandler(__modelcontextprotocol_sdk_types_js.ListToolsRequestSchema, async () => ({ tools: [await describeTools.getDefinition(), useTool.getDefinition()] }));
1376
2067
  server.setRequestHandler(__modelcontextprotocol_sdk_types_js.CallToolRequestSchema, async (request) => {
1377
2068
  const { name, arguments: args } = request.params;
1378
- if (name === DescribeToolsTool.TOOL_NAME) return await describeTools.execute(args);
1379
- if (name === UseToolTool.TOOL_NAME) return await useTool.execute(args);
2069
+ if (name === DescribeToolsTool.TOOL_NAME) try {
2070
+ return await describeTools.execute(args);
2071
+ } catch (error) {
2072
+ throw new Error(`Failed to execute ${name}: ${error instanceof Error ? error.message : String(error)}`);
2073
+ }
2074
+ if (name === UseToolTool.TOOL_NAME) try {
2075
+ return await useTool.execute(args);
2076
+ } catch (error) {
2077
+ throw new Error(`Failed to execute ${name}: ${error instanceof Error ? error.message : String(error)}`);
2078
+ }
1380
2079
  throw new Error(`Unknown tool: ${name}`);
1381
2080
  });
2081
+ server.setRequestHandler(__modelcontextprotocol_sdk_types_js.ListPromptsRequestSchema, async () => {
2082
+ const clients = clientManager.getAllClients();
2083
+ const promptToServers = /* @__PURE__ */ new Map();
2084
+ const serverPromptsMap = /* @__PURE__ */ new Map();
2085
+ await Promise.all(clients.map(async (client) => {
2086
+ try {
2087
+ const prompts = await client.listPrompts();
2088
+ serverPromptsMap.set(client.serverName, prompts);
2089
+ for (const prompt of prompts) {
2090
+ if (!promptToServers.has(prompt.name)) promptToServers.set(prompt.name, []);
2091
+ promptToServers.get(prompt.name).push(client.serverName);
2092
+ }
2093
+ } catch (error) {
2094
+ console.error(`Failed to list prompts from ${client.serverName}:`, error);
2095
+ serverPromptsMap.set(client.serverName, []);
2096
+ }
2097
+ }));
2098
+ const aggregatedPrompts = [];
2099
+ for (const client of clients) {
2100
+ const prompts = serverPromptsMap.get(client.serverName) || [];
2101
+ for (const prompt of prompts) {
2102
+ const hasClash = (promptToServers.get(prompt.name) || []).length > 1;
2103
+ aggregatedPrompts.push({
2104
+ name: hasClash ? `${client.serverName}__${prompt.name}` : prompt.name,
2105
+ description: prompt.description,
2106
+ arguments: prompt.arguments
2107
+ });
2108
+ }
2109
+ }
2110
+ return { prompts: aggregatedPrompts };
2111
+ });
2112
+ server.setRequestHandler(__modelcontextprotocol_sdk_types_js.GetPromptRequestSchema, async (request) => {
2113
+ const { name, arguments: args } = request.params;
2114
+ const clients = clientManager.getAllClients();
2115
+ const { serverName, actualToolName: actualPromptName } = parseToolName(name);
2116
+ if (serverName) {
2117
+ const client$1 = clientManager.getClient(serverName);
2118
+ if (!client$1) throw new Error(`Server not found: ${serverName}`);
2119
+ return await client$1.getPrompt(actualPromptName, args);
2120
+ }
2121
+ const serversWithPrompt = [];
2122
+ await Promise.all(clients.map(async (client$1) => {
2123
+ try {
2124
+ if ((await client$1.listPrompts()).some((p) => p.name === name)) serversWithPrompt.push(client$1.serverName);
2125
+ } catch (error) {
2126
+ console.error(`Failed to list prompts from ${client$1.serverName}:`, error);
2127
+ }
2128
+ }));
2129
+ if (serversWithPrompt.length === 0) throw new Error(`Prompt not found: ${name}`);
2130
+ if (serversWithPrompt.length > 1) throw new Error(`Prompt "${name}" exists on multiple servers: ${serversWithPrompt.join(", ")}. Use the prefixed format (e.g., "${serversWithPrompt[0]}__${name}") to specify which server to use.`);
2131
+ const client = clientManager.getClient(serversWithPrompt[0]);
2132
+ if (!client) throw new Error(`Server not found: ${serversWithPrompt[0]}`);
2133
+ return await client.getPrompt(name, args);
2134
+ });
1382
2135
  return server;
1383
2136
  }
1384
2137
 
@@ -1746,6 +2499,12 @@ Object.defineProperty(exports, 'McpClientManagerService', {
1746
2499
  return McpClientManagerService;
1747
2500
  }
1748
2501
  });
2502
+ Object.defineProperty(exports, 'SkillService', {
2503
+ enumerable: true,
2504
+ get: function () {
2505
+ return SkillService;
2506
+ }
2507
+ });
1749
2508
  Object.defineProperty(exports, 'SseTransportHandler', {
1750
2509
  enumerable: true,
1751
2510
  get: function () {