@agiflowai/one-mcp 0.2.3 → 0.2.5

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/dist/cli.cjs CHANGED
@@ -1,20 +1,20 @@
1
1
  #!/usr/bin/env node
2
- const require_http = require('./http-3v8zyDO3.cjs');
2
+ const require_http = require('./http-Q8LPwwwP.cjs');
3
3
  let node_fs_promises = require("node:fs/promises");
4
4
  let node_path = require("node:path");
5
+ let liquidjs = require("liquidjs");
5
6
  let commander = require("commander");
6
7
  let __agiflowai_aicode_utils = require("@agiflowai/aicode-utils");
7
8
 
8
9
  //#region src/types/index.ts
9
10
  /**
10
- * Transport mode types
11
+ * Transport mode constants
11
12
  */
12
- let TransportMode = /* @__PURE__ */ function(TransportMode$1) {
13
- TransportMode$1["STDIO"] = "stdio";
14
- TransportMode$1["HTTP"] = "http";
15
- TransportMode$1["SSE"] = "sse";
16
- return TransportMode$1;
17
- }({});
13
+ const TRANSPORT_MODE = {
14
+ STDIO: "stdio",
15
+ HTTP: "http",
16
+ SSE: "sse"
17
+ };
18
18
 
19
19
  //#endregion
20
20
  //#region src/commands/mcp-serve.ts
@@ -39,7 +39,16 @@ let TransportMode = /* @__PURE__ */ function(TransportMode$1) {
39
39
  * - Not cleaning up resources on shutdown
40
40
  */
41
41
  /**
42
+ * Type guard to validate transport type
43
+ * @param type - The transport type string to validate
44
+ * @returns True if the type is a valid transport type
45
+ */
46
+ function isValidTransportType(type) {
47
+ return type === "stdio" || type === "http" || type === "sse";
48
+ }
49
+ /**
42
50
  * Start MCP server with given transport handler
51
+ * @param handler - The transport handler to start
43
52
  */
44
53
  async function startServer(handler) {
45
54
  await handler.start();
@@ -59,30 +68,30 @@ async function startServer(handler) {
59
68
  /**
60
69
  * MCP Serve command
61
70
  */
62
- const mcpServeCommand = new commander.Command("mcp-serve").description("Start MCP server with specified transport").option("-t, --type <type>", "Transport type: stdio, http, or sse", "stdio").option("-p, --port <port>", "Port to listen on (http/sse only)", (val) => parseInt(val, 10), 3e3).option("--host <host>", "Host to bind to (http/sse only)", "localhost").option("-c, --config <path>", "Path to MCP server configuration file").option("--no-cache", "Force reload configuration from source, bypassing cache").action(async (options) => {
71
+ const mcpServeCommand = new commander.Command("mcp-serve").description("Start MCP server with specified transport").option("-t, --type <type>", "Transport type: stdio, http, or sse", "stdio").option("-p, --port <port>", "Port to listen on (http/sse only)", (val) => parseInt(val, 10), 3e3).option("--host <host>", "Host to bind to (http/sse only)", "localhost").option("-c, --config <path>", "Path to MCP server configuration file").option("--no-cache", "Disable configuration caching, always reload from config file").action(async (options) => {
72
+ const transportType = options.type.toLowerCase();
73
+ if (!isValidTransportType(transportType)) {
74
+ console.error(`Unknown transport type: '${transportType}'. Valid options: stdio, http, sse`);
75
+ process.exit(1);
76
+ }
63
77
  try {
64
- const transportType = options.type.toLowerCase();
65
78
  const serverOptions = {
66
- configFilePath: options.config || require_http.findConfigFile(),
79
+ configFilePath: options.config || require_http.findConfigFile() || void 0,
67
80
  noCache: options.cache === false
68
81
  };
69
82
  if (transportType === "stdio") await startServer(new require_http.StdioTransportHandler(await require_http.createServer(serverOptions)));
70
83
  else if (transportType === "http") await startServer(new require_http.HttpTransportHandler(await require_http.createServer(serverOptions), {
71
- mode: TransportMode.HTTP,
84
+ mode: TRANSPORT_MODE.HTTP,
72
85
  port: options.port || Number(process.env.MCP_PORT) || 3e3,
73
86
  host: options.host || process.env.MCP_HOST || "localhost"
74
87
  }));
75
88
  else if (transportType === "sse") await startServer(new require_http.SseTransportHandler(await require_http.createServer(serverOptions), {
76
- mode: TransportMode.SSE,
89
+ mode: TRANSPORT_MODE.SSE,
77
90
  port: options.port || Number(process.env.MCP_PORT) || 3e3,
78
91
  host: options.host || process.env.MCP_HOST || "localhost"
79
92
  }));
80
- else {
81
- console.error(`Unknown transport type: ${transportType}. Use: stdio, http, or sse`);
82
- process.exit(1);
83
- }
84
93
  } catch (error) {
85
- console.error("Failed to start MCP server:", error);
94
+ console.error(`Failed to start MCP server with transport '${transportType}' on ${options.host}:${options.port}:`, error);
86
95
  process.exit(1);
87
96
  }
88
97
  });
@@ -212,7 +221,11 @@ const describeToolsCommand = new commander.Command("describe-tools").description
212
221
  console.error("No MCP servers connected");
213
222
  process.exit(1);
214
223
  }
224
+ const cwd = process.env.PROJECT_PATH || process.cwd();
225
+ const skillPaths = config.skills?.paths || [];
226
+ const skillService = skillPaths.length > 0 ? new require_http.SkillService(cwd, skillPaths) : void 0;
215
227
  const foundTools = [];
228
+ const foundSkills = [];
216
229
  const notFoundTools = [...toolNames];
217
230
  for (const client of clients) {
218
231
  if (options.server && client.serverName !== options.server) continue;
@@ -235,8 +248,30 @@ const describeToolsCommand = new commander.Command("describe-tools").description
235
248
  if (!options.json) console.error(`Failed to list tools from ${client.serverName}:`, error);
236
249
  }
237
250
  }
251
+ if (skillService && notFoundTools.length > 0) {
252
+ const skillsToCheck = [...notFoundTools];
253
+ for (const toolName of skillsToCheck) {
254
+ const skillName = toolName.startsWith("skill__") ? toolName.slice(7) : toolName;
255
+ const skill = await skillService.getSkill(skillName);
256
+ if (skill) {
257
+ foundSkills.push({
258
+ name: skill.name,
259
+ location: skill.basePath,
260
+ instructions: skill.content
261
+ });
262
+ const idx = notFoundTools.indexOf(toolName);
263
+ if (idx > -1) notFoundTools.splice(idx, 1);
264
+ }
265
+ }
266
+ }
267
+ const nextSteps = [];
268
+ if (foundTools.length > 0) nextSteps.push("For MCP tools: Use the use_tool function with toolName and toolArgs based on the inputSchema above.");
269
+ if (foundSkills.length > 0) nextSteps.push(`For skill, just follow skill's description to continue.`);
238
270
  if (options.json) {
239
- const result = { tools: foundTools };
271
+ const result = {};
272
+ if (foundTools.length > 0) result.tools = foundTools;
273
+ if (foundSkills.length > 0) result.skills = foundSkills;
274
+ if (nextSteps.length > 0) result.nextSteps = nextSteps;
240
275
  if (notFoundTools.length > 0) result.notFound = notFoundTools;
241
276
  console.log(JSON.stringify(result, null, 2));
242
277
  } else {
@@ -251,9 +286,23 @@ const describeToolsCommand = new commander.Command("describe-tools").description
251
286
  console.log("");
252
287
  }
253
288
  }
254
- if (notFoundTools.length > 0) console.error(`\nTools not found: ${notFoundTools.join(", ")}`);
255
- if (foundTools.length === 0) {
256
- console.error("No tools found");
289
+ if (foundSkills.length > 0) {
290
+ console.log("\nFound skills:\n");
291
+ for (const skill of foundSkills) {
292
+ console.log(`Skill: ${skill.name}`);
293
+ console.log(`Location: ${skill.location}`);
294
+ console.log(`Instructions:\n${skill.instructions}`);
295
+ console.log("");
296
+ }
297
+ }
298
+ if (nextSteps.length > 0) {
299
+ console.log("\nNext steps:");
300
+ for (const step of nextSteps) console.log(` • ${step}`);
301
+ console.log("");
302
+ }
303
+ if (notFoundTools.length > 0) console.error(`\nTools/skills not found: ${notFoundTools.join(", ")}`);
304
+ if (foundTools.length === 0 && foundSkills.length === 0) {
305
+ console.error("No tools or skills found");
257
306
  process.exit(1);
258
307
  }
259
308
  }
@@ -354,7 +403,29 @@ const useToolCommand = new commander.Command("use-tool").description("Execute an
354
403
  if (!options.json) console.error(`Failed to list tools from ${client$1.serverName}:`, error);
355
404
  }
356
405
  if (matchingServers.length === 0) {
357
- console.error(`Tool "${toolName}" not found on any connected server`);
406
+ const cwd = process.env.PROJECT_PATH || process.cwd();
407
+ const skillPaths = config.skills?.paths || [];
408
+ if (skillPaths.length > 0) try {
409
+ const skillService = new require_http.SkillService(cwd, skillPaths);
410
+ const skillName = toolName.startsWith("skill__") ? toolName.slice(7) : toolName;
411
+ const skill = await skillService.getSkill(skillName);
412
+ if (skill) {
413
+ const result = { content: [{
414
+ type: "text",
415
+ text: skill.content
416
+ }] };
417
+ if (options.json) console.log(JSON.stringify(result, null, 2));
418
+ else {
419
+ console.log("\nSkill content:");
420
+ console.log(skill.content);
421
+ }
422
+ await clientManager.disconnectAll();
423
+ return;
424
+ }
425
+ } catch (error) {
426
+ if (!options.json) console.error(`Failed to lookup skill "${toolName}":`, error);
427
+ }
428
+ console.error(`Tool or skill "${toolName}" not found on any connected server or configured skill paths`);
358
429
  await clientManager.disconnectAll();
359
430
  process.exit(1);
360
431
  }
@@ -398,12 +469,12 @@ const useToolCommand = new commander.Command("use-tool").description("Execute an
398
469
  });
399
470
 
400
471
  //#endregion
401
- //#region src/templates/mcp-config.yaml?raw
402
- var mcp_config_default = "# MCP Server Configuration\n# This file configures the MCP servers that one-mcp will connect to\n#\n# Environment Variable Interpolation:\n# Use ${VAR_NAME} syntax to reference environment variables\n# Example: ${HOME}, ${API_KEY}, ${DATABASE_URL}\n#\n# Instructions:\n# - config.instruction: Server's default instruction (from server documentation)\n# - instruction: User override (optional, takes precedence over config.instruction)\n# - config.toolBlacklist: Array of tool names to hide/block from this server\n# - config.omitToolDescription: Boolean to show only tool names without descriptions (saves tokens)\n\n# Remote Configuration Sources (OPTIONAL)\n# Fetch and merge configurations from remote URLs\n# Remote configs are merged with local configs based on merge strategy\n#\n# SECURITY: SSRF Protection is ENABLED by default\n# - Only HTTPS URLs are allowed (set security.enforceHttps: false to allow HTTP)\n# - Private IPs and localhost are blocked (set security.allowPrivateIPs: true for internal networks)\n# - Blocked ranges: 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16\nremoteConfigs:\n # Example 1: Basic remote config with default security\n # - url: ${AGIFLOW_URL}/api/v1/mcp-configs\n # headers:\n # Authorization: Bearer ${AGIFLOW_API_KEY}\n # mergeStrategy: local-priority # Options: local-priority (default), remote-priority, merge-deep\n #\n # Example 2: Remote config with custom security settings (for internal networks)\n # - url: ${INTERNAL_URL}/mcp-configs\n # headers:\n # Authorization: Bearer ${INTERNAL_TOKEN}\n # security:\n # allowPrivateIPs: true # Allow internal IPs (default: false)\n # enforceHttps: false # Allow HTTP (default: true, HTTPS only)\n # mergeStrategy: local-priority\n #\n # Example 3: Remote config with additional validation (OPTIONAL)\n # - url: ${AGIFLOW_URL}/api/v1/mcp-configs\n # headers:\n # Authorization: Bearer ${AGIFLOW_API_KEY}\n # X-API-Key: ${AGIFLOW_API_KEY}\n # security:\n # enforceHttps: true # Require HTTPS (default: true)\n # allowPrivateIPs: false # Block private IPs (default: false)\n # validation: # OPTIONAL: Additional regex validation on top of security checks\n # url: ^https://.*\\.agiflow\\.io/.* # OPTIONAL: Regex pattern to validate URL format\n # headers: # OPTIONAL: Regex patterns to validate header values\n # Authorization: ^Bearer [A-Za-z0-9_-]+$\n # X-API-Key: ^[A-Za-z0-9_-]{32,}$\n # mergeStrategy: local-priority\n\nmcpServers:\n # Example MCP server using stdio transport\n example-server:\n command: node\n args:\n - /path/to/mcp-server/build/index.js\n env:\n # Environment variables for the MCP server\n LOG_LEVEL: info\n # You can use environment variable interpolation:\n # DATABASE_URL: ${DATABASE_URL}\n # API_KEY: ${MY_API_KEY}\n config:\n # Server's default instruction (from server documentation)\n instruction: Use this server for...\n # Optional: Block specific tools from being listed or executed\n # toolBlacklist:\n # - dangerous_tool_name\n # - another_blocked_tool\n # Optional: Omit tool descriptions to save tokens (default: false)\n # omitToolDescription: true\n # instruction: Optional user override - takes precedence over config.instruction\n\n # Example MCP server using SSE transport with environment variables\n # remote-server:\n # url: https://example.com/mcp\n # type: sse\n # headers:\n # # Use ${VAR_NAME} to interpolate environment variables\n # Authorization: Bearer ${API_KEY}\n # config:\n # instruction: This server provides tools for...\n # # Optional: Block specific tools from being listed or executed\n # # toolBlacklist:\n # # - tool_to_block\n # # Optional: Omit tool descriptions to save tokens (default: false)\n # # omitToolDescription: true\n # # instruction: Optional user override\n";
472
+ //#region src/templates/mcp-config.yaml.liquid?raw
473
+ var mcp_config_yaml_default = "# MCP Server Configuration\n# This file configures the MCP servers that one-mcp will connect to\n#\n# Environment Variable Interpolation:\n# Use ${VAR_NAME} syntax to reference environment variables\n# Example: ${HOME}, ${API_KEY}, ${DATABASE_URL}\n#\n# Instructions:\n# - config.instruction: Server's default instruction (from server documentation)\n# - instruction: User override (optional, takes precedence over config.instruction)\n# - config.toolBlacklist: Array of tool names to hide/block from this server\n# - config.omitToolDescription: Boolean to show only tool names without descriptions (saves tokens)\n\n# Remote Configuration Sources (OPTIONAL)\n# Fetch and merge configurations from remote URLs\n# Remote configs are merged with local configs based on merge strategy\n#\n# SECURITY: SSRF Protection is ENABLED by default\n# - Only HTTPS URLs are allowed (set security.enforceHttps: false to allow HTTP)\n# - Private IPs and localhost are blocked (set security.allowPrivateIPs: true for internal networks)\n# - Blocked ranges: 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16\nremoteConfigs:\n # Example 1: Basic remote config with default security\n # - url: ${AGIFLOW_URL}/api/v1/mcp-configs\n # headers:\n # Authorization: Bearer ${AGIFLOW_API_KEY}\n # mergeStrategy: local-priority # Options: local-priority (default), remote-priority, merge-deep\n #\n # Example 2: Remote config with custom security settings (for internal networks)\n # - url: ${INTERNAL_URL}/mcp-configs\n # headers:\n # Authorization: Bearer ${INTERNAL_TOKEN}\n # security:\n # allowPrivateIPs: true # Allow internal IPs (default: false)\n # enforceHttps: false # Allow HTTP (default: true, HTTPS only)\n # mergeStrategy: local-priority\n #\n # Example 3: Remote config with additional validation (OPTIONAL)\n # - url: ${AGIFLOW_URL}/api/v1/mcp-configs\n # headers:\n # Authorization: Bearer ${AGIFLOW_API_KEY}\n # X-API-Key: ${AGIFLOW_API_KEY}\n # security:\n # enforceHttps: true # Require HTTPS (default: true)\n # allowPrivateIPs: false # Block private IPs (default: false)\n # validation: # OPTIONAL: Additional regex validation on top of security checks\n # url: ^https://.*\\.agiflow\\.io/.* # OPTIONAL: Regex pattern to validate URL format\n # headers: # OPTIONAL: Regex patterns to validate header values\n # Authorization: ^Bearer [A-Za-z0-9_-]+$\n # X-API-Key: ^[A-Za-z0-9_-]{32,}$\n # mergeStrategy: local-priority\n\nmcpServers:\n{%- if mcpServers %}{% for server in mcpServers %}\n {{ server.name }}:\n command: {{ server.command }}\n args:{% for arg in server.args %}\n - '{{ arg }}'{% endfor %}\n # env:\n # LOG_LEVEL: info\n # # API_KEY: ${MY_API_KEY}\n # config:\n # instruction: Use this server for...\n # # toolBlacklist:\n # # - tool_to_block\n # # omitToolDescription: true\n{% endfor %}\n # Example MCP server using SSE transport\n # remote-server:\n # url: https://example.com/mcp\n # type: sse\n # headers:\n # Authorization: Bearer ${API_KEY}\n # config:\n # instruction: This server provides tools for...\n{% else %}\n # Example MCP server using stdio transport\n example-server:\n command: node\n args:\n - /path/to/mcp-server/build/index.js\n env:\n # Environment variables for the MCP server\n LOG_LEVEL: info\n # You can use environment variable interpolation:\n # DATABASE_URL: ${DATABASE_URL}\n # API_KEY: ${MY_API_KEY}\n config:\n # Server's default instruction (from server documentation)\n instruction: Use this server for...\n # Optional: Block specific tools from being listed or executed\n # toolBlacklist:\n # - dangerous_tool_name\n # - another_blocked_tool\n # Optional: Omit tool descriptions to save tokens (default: false)\n # omitToolDescription: true\n # instruction: Optional user override - takes precedence over config.instruction\n\n # Example MCP server using SSE transport with environment variables\n # remote-server:\n # url: https://example.com/mcp\n # type: sse\n # headers:\n # # Use ${VAR_NAME} to interpolate environment variables\n # Authorization: Bearer ${API_KEY}\n # config:\n # instruction: This server provides tools for...\n # # Optional: Block specific tools from being listed or executed\n # # toolBlacklist:\n # # - tool_to_block\n # # Optional: Omit tool descriptions to save tokens (default: false)\n # # omitToolDescription: true\n # # instruction: Optional user override\n{% endif %}\n";
403
474
 
404
475
  //#endregion
405
476
  //#region src/templates/mcp-config.json?raw
406
- var mcp_config_default$1 = "{\n \"_comment\": \"MCP Server Configuration - Use ${VAR_NAME} syntax for environment variable interpolation\",\n \"_instructions\": \"config.instruction: Server's default instruction | instruction: User override (takes precedence)\",\n \"mcpServers\": {\n \"example-server\": {\n \"command\": \"node\",\n \"args\": [\n \"/path/to/mcp-server/build/index.js\"\n ],\n \"env\": {\n \"LOG_LEVEL\": \"info\",\n \"_comment\": \"You can use environment variable interpolation:\",\n \"_example_DATABASE_URL\": \"${DATABASE_URL}\",\n \"_example_API_KEY\": \"${MY_API_KEY}\"\n },\n \"config\": {\n \"instruction\": \"Use this server for...\"\n },\n \"_instruction_override\": \"Optional user override - takes precedence over config.instruction\"\n }\n }\n}\n";
477
+ var mcp_config_default = "{\n \"_comment\": \"MCP Server Configuration - Use ${VAR_NAME} syntax for environment variable interpolation\",\n \"_instructions\": \"config.instruction: Server's default instruction | instruction: User override (takes precedence)\",\n \"mcpServers\": {\n \"example-server\": {\n \"command\": \"node\",\n \"args\": [\n \"/path/to/mcp-server/build/index.js\"\n ],\n \"env\": {\n \"LOG_LEVEL\": \"info\",\n \"_comment\": \"You can use environment variable interpolation:\",\n \"_example_DATABASE_URL\": \"${DATABASE_URL}\",\n \"_example_API_KEY\": \"${MY_API_KEY}\"\n },\n \"config\": {\n \"instruction\": \"Use this server for...\"\n },\n \"_instruction_override\": \"Optional user override - takes precedence over config.instruction\"\n }\n }\n}\n";
407
478
 
408
479
  //#endregion
409
480
  //#region src/commands/init.ts
@@ -431,12 +502,29 @@ var mcp_config_default$1 = "{\n \"_comment\": \"MCP Server Configuration - Use
431
502
  /**
432
503
  * Initialize MCP configuration file
433
504
  */
434
- const initCommand = new commander.Command("init").description("Initialize MCP configuration file").option("-o, --output <path>", "Output file path", "mcp-config.yaml").option("--json", "Generate JSON config instead of YAML", false).option("-f, --force", "Overwrite existing config file", false).action(async (options) => {
505
+ const initCommand = new commander.Command("init").description("Initialize MCP configuration file").option("-o, --output <path>", "Output file path", "mcp-config.yaml").option("--json", "Generate JSON config instead of YAML", false).option("-f, --force", "Overwrite existing config file", false).option("--mcp-servers <json>", "JSON string of MCP servers to add to config (optional)").action(async (options) => {
435
506
  try {
436
507
  const outputPath = (0, node_path.resolve)(options.output);
437
- const template = !options.json && (outputPath.endsWith(".yaml") || outputPath.endsWith(".yml")) ? mcp_config_default : mcp_config_default$1;
508
+ const isYaml = !options.json && (outputPath.endsWith(".yaml") || outputPath.endsWith(".yml"));
509
+ let content;
510
+ if (isYaml) {
511
+ const liquid = new liquidjs.Liquid();
512
+ let mcpServersData = null;
513
+ if (options.mcpServers) try {
514
+ const serversObj = JSON.parse(options.mcpServers);
515
+ mcpServersData = Object.entries(serversObj).map(([name, config]) => ({
516
+ name,
517
+ command: config.command,
518
+ args: config.args
519
+ }));
520
+ } catch (parseError) {
521
+ __agiflowai_aicode_utils.log.error("Failed to parse --mcp-servers JSON:", parseError instanceof Error ? parseError.message : String(parseError));
522
+ process.exit(1);
523
+ }
524
+ content = await liquid.parseAndRender(mcp_config_yaml_default, { mcpServers: mcpServersData });
525
+ } else content = mcp_config_default;
438
526
  try {
439
- await (0, node_fs_promises.writeFile)(outputPath, template, {
527
+ await (0, node_fs_promises.writeFile)(outputPath, content, {
440
528
  encoding: "utf-8",
441
529
  flag: options.force ? "w" : "wx"
442
530
  });
@@ -460,7 +548,7 @@ const initCommand = new commander.Command("init").description("Initialize MCP co
460
548
 
461
549
  //#endregion
462
550
  //#region package.json
463
- var version = "0.2.2";
551
+ var version = "0.2.4";
464
552
 
465
553
  //#endregion
466
554
  //#region src/cli.ts
package/dist/cli.mjs CHANGED
@@ -1,20 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import { a as findConfigFile, i as createServer, n as SseTransportHandler, o as McpClientManagerService, r as StdioTransportHandler, s as ConfigFetcherService, t as HttpTransportHandler } from "./http-CzQfsUEI.mjs";
2
+ import { a as findConfigFile, c as ConfigFetcherService, i as createServer, n as SseTransportHandler, o as SkillService, r as StdioTransportHandler, s as McpClientManagerService, t as HttpTransportHandler } from "./http-BKDyW8YB.mjs";
3
3
  import { writeFile } from "node:fs/promises";
4
4
  import { resolve } from "node:path";
5
+ import { Liquid } from "liquidjs";
5
6
  import { Command } from "commander";
6
7
  import { log } from "@agiflowai/aicode-utils";
7
8
 
8
9
  //#region src/types/index.ts
9
10
  /**
10
- * Transport mode types
11
+ * Transport mode constants
11
12
  */
12
- let TransportMode = /* @__PURE__ */ function(TransportMode$1) {
13
- TransportMode$1["STDIO"] = "stdio";
14
- TransportMode$1["HTTP"] = "http";
15
- TransportMode$1["SSE"] = "sse";
16
- return TransportMode$1;
17
- }({});
13
+ const TRANSPORT_MODE = {
14
+ STDIO: "stdio",
15
+ HTTP: "http",
16
+ SSE: "sse"
17
+ };
18
18
 
19
19
  //#endregion
20
20
  //#region src/commands/mcp-serve.ts
@@ -39,7 +39,16 @@ let TransportMode = /* @__PURE__ */ function(TransportMode$1) {
39
39
  * - Not cleaning up resources on shutdown
40
40
  */
41
41
  /**
42
+ * Type guard to validate transport type
43
+ * @param type - The transport type string to validate
44
+ * @returns True if the type is a valid transport type
45
+ */
46
+ function isValidTransportType(type) {
47
+ return type === "stdio" || type === "http" || type === "sse";
48
+ }
49
+ /**
42
50
  * Start MCP server with given transport handler
51
+ * @param handler - The transport handler to start
43
52
  */
44
53
  async function startServer(handler) {
45
54
  await handler.start();
@@ -59,30 +68,30 @@ async function startServer(handler) {
59
68
  /**
60
69
  * MCP Serve command
61
70
  */
62
- const mcpServeCommand = new Command("mcp-serve").description("Start MCP server with specified transport").option("-t, --type <type>", "Transport type: stdio, http, or sse", "stdio").option("-p, --port <port>", "Port to listen on (http/sse only)", (val) => parseInt(val, 10), 3e3).option("--host <host>", "Host to bind to (http/sse only)", "localhost").option("-c, --config <path>", "Path to MCP server configuration file").option("--no-cache", "Force reload configuration from source, bypassing cache").action(async (options) => {
71
+ const mcpServeCommand = new Command("mcp-serve").description("Start MCP server with specified transport").option("-t, --type <type>", "Transport type: stdio, http, or sse", "stdio").option("-p, --port <port>", "Port to listen on (http/sse only)", (val) => parseInt(val, 10), 3e3).option("--host <host>", "Host to bind to (http/sse only)", "localhost").option("-c, --config <path>", "Path to MCP server configuration file").option("--no-cache", "Disable configuration caching, always reload from config file").action(async (options) => {
72
+ const transportType = options.type.toLowerCase();
73
+ if (!isValidTransportType(transportType)) {
74
+ console.error(`Unknown transport type: '${transportType}'. Valid options: stdio, http, sse`);
75
+ process.exit(1);
76
+ }
63
77
  try {
64
- const transportType = options.type.toLowerCase();
65
78
  const serverOptions = {
66
- configFilePath: options.config || findConfigFile(),
79
+ configFilePath: options.config || findConfigFile() || void 0,
67
80
  noCache: options.cache === false
68
81
  };
69
82
  if (transportType === "stdio") await startServer(new StdioTransportHandler(await createServer(serverOptions)));
70
83
  else if (transportType === "http") await startServer(new HttpTransportHandler(await createServer(serverOptions), {
71
- mode: TransportMode.HTTP,
84
+ mode: TRANSPORT_MODE.HTTP,
72
85
  port: options.port || Number(process.env.MCP_PORT) || 3e3,
73
86
  host: options.host || process.env.MCP_HOST || "localhost"
74
87
  }));
75
88
  else if (transportType === "sse") await startServer(new SseTransportHandler(await createServer(serverOptions), {
76
- mode: TransportMode.SSE,
89
+ mode: TRANSPORT_MODE.SSE,
77
90
  port: options.port || Number(process.env.MCP_PORT) || 3e3,
78
91
  host: options.host || process.env.MCP_HOST || "localhost"
79
92
  }));
80
- else {
81
- console.error(`Unknown transport type: ${transportType}. Use: stdio, http, or sse`);
82
- process.exit(1);
83
- }
84
93
  } catch (error) {
85
- console.error("Failed to start MCP server:", error);
94
+ console.error(`Failed to start MCP server with transport '${transportType}' on ${options.host}:${options.port}:`, error);
86
95
  process.exit(1);
87
96
  }
88
97
  });
@@ -212,7 +221,11 @@ const describeToolsCommand = new Command("describe-tools").description("Describe
212
221
  console.error("No MCP servers connected");
213
222
  process.exit(1);
214
223
  }
224
+ const cwd = process.env.PROJECT_PATH || process.cwd();
225
+ const skillPaths = config.skills?.paths || [];
226
+ const skillService = skillPaths.length > 0 ? new SkillService(cwd, skillPaths) : void 0;
215
227
  const foundTools = [];
228
+ const foundSkills = [];
216
229
  const notFoundTools = [...toolNames];
217
230
  for (const client of clients) {
218
231
  if (options.server && client.serverName !== options.server) continue;
@@ -235,8 +248,30 @@ const describeToolsCommand = new Command("describe-tools").description("Describe
235
248
  if (!options.json) console.error(`Failed to list tools from ${client.serverName}:`, error);
236
249
  }
237
250
  }
251
+ if (skillService && notFoundTools.length > 0) {
252
+ const skillsToCheck = [...notFoundTools];
253
+ for (const toolName of skillsToCheck) {
254
+ const skillName = toolName.startsWith("skill__") ? toolName.slice(7) : toolName;
255
+ const skill = await skillService.getSkill(skillName);
256
+ if (skill) {
257
+ foundSkills.push({
258
+ name: skill.name,
259
+ location: skill.basePath,
260
+ instructions: skill.content
261
+ });
262
+ const idx = notFoundTools.indexOf(toolName);
263
+ if (idx > -1) notFoundTools.splice(idx, 1);
264
+ }
265
+ }
266
+ }
267
+ const nextSteps = [];
268
+ if (foundTools.length > 0) nextSteps.push("For MCP tools: Use the use_tool function with toolName and toolArgs based on the inputSchema above.");
269
+ if (foundSkills.length > 0) nextSteps.push(`For skill, just follow skill's description to continue.`);
238
270
  if (options.json) {
239
- const result = { tools: foundTools };
271
+ const result = {};
272
+ if (foundTools.length > 0) result.tools = foundTools;
273
+ if (foundSkills.length > 0) result.skills = foundSkills;
274
+ if (nextSteps.length > 0) result.nextSteps = nextSteps;
240
275
  if (notFoundTools.length > 0) result.notFound = notFoundTools;
241
276
  console.log(JSON.stringify(result, null, 2));
242
277
  } else {
@@ -251,9 +286,23 @@ const describeToolsCommand = new Command("describe-tools").description("Describe
251
286
  console.log("");
252
287
  }
253
288
  }
254
- if (notFoundTools.length > 0) console.error(`\nTools not found: ${notFoundTools.join(", ")}`);
255
- if (foundTools.length === 0) {
256
- console.error("No tools found");
289
+ if (foundSkills.length > 0) {
290
+ console.log("\nFound skills:\n");
291
+ for (const skill of foundSkills) {
292
+ console.log(`Skill: ${skill.name}`);
293
+ console.log(`Location: ${skill.location}`);
294
+ console.log(`Instructions:\n${skill.instructions}`);
295
+ console.log("");
296
+ }
297
+ }
298
+ if (nextSteps.length > 0) {
299
+ console.log("\nNext steps:");
300
+ for (const step of nextSteps) console.log(` • ${step}`);
301
+ console.log("");
302
+ }
303
+ if (notFoundTools.length > 0) console.error(`\nTools/skills not found: ${notFoundTools.join(", ")}`);
304
+ if (foundTools.length === 0 && foundSkills.length === 0) {
305
+ console.error("No tools or skills found");
257
306
  process.exit(1);
258
307
  }
259
308
  }
@@ -354,7 +403,29 @@ const useToolCommand = new Command("use-tool").description("Execute an MCP tool
354
403
  if (!options.json) console.error(`Failed to list tools from ${client$1.serverName}:`, error);
355
404
  }
356
405
  if (matchingServers.length === 0) {
357
- console.error(`Tool "${toolName}" not found on any connected server`);
406
+ const cwd = process.env.PROJECT_PATH || process.cwd();
407
+ const skillPaths = config.skills?.paths || [];
408
+ if (skillPaths.length > 0) try {
409
+ const skillService = new SkillService(cwd, skillPaths);
410
+ const skillName = toolName.startsWith("skill__") ? toolName.slice(7) : toolName;
411
+ const skill = await skillService.getSkill(skillName);
412
+ if (skill) {
413
+ const result = { content: [{
414
+ type: "text",
415
+ text: skill.content
416
+ }] };
417
+ if (options.json) console.log(JSON.stringify(result, null, 2));
418
+ else {
419
+ console.log("\nSkill content:");
420
+ console.log(skill.content);
421
+ }
422
+ await clientManager.disconnectAll();
423
+ return;
424
+ }
425
+ } catch (error) {
426
+ if (!options.json) console.error(`Failed to lookup skill "${toolName}":`, error);
427
+ }
428
+ console.error(`Tool or skill "${toolName}" not found on any connected server or configured skill paths`);
358
429
  await clientManager.disconnectAll();
359
430
  process.exit(1);
360
431
  }
@@ -398,12 +469,12 @@ const useToolCommand = new Command("use-tool").description("Execute an MCP tool
398
469
  });
399
470
 
400
471
  //#endregion
401
- //#region src/templates/mcp-config.yaml?raw
402
- var mcp_config_default = "# MCP Server Configuration\n# This file configures the MCP servers that one-mcp will connect to\n#\n# Environment Variable Interpolation:\n# Use ${VAR_NAME} syntax to reference environment variables\n# Example: ${HOME}, ${API_KEY}, ${DATABASE_URL}\n#\n# Instructions:\n# - config.instruction: Server's default instruction (from server documentation)\n# - instruction: User override (optional, takes precedence over config.instruction)\n# - config.toolBlacklist: Array of tool names to hide/block from this server\n# - config.omitToolDescription: Boolean to show only tool names without descriptions (saves tokens)\n\n# Remote Configuration Sources (OPTIONAL)\n# Fetch and merge configurations from remote URLs\n# Remote configs are merged with local configs based on merge strategy\n#\n# SECURITY: SSRF Protection is ENABLED by default\n# - Only HTTPS URLs are allowed (set security.enforceHttps: false to allow HTTP)\n# - Private IPs and localhost are blocked (set security.allowPrivateIPs: true for internal networks)\n# - Blocked ranges: 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16\nremoteConfigs:\n # Example 1: Basic remote config with default security\n # - url: ${AGIFLOW_URL}/api/v1/mcp-configs\n # headers:\n # Authorization: Bearer ${AGIFLOW_API_KEY}\n # mergeStrategy: local-priority # Options: local-priority (default), remote-priority, merge-deep\n #\n # Example 2: Remote config with custom security settings (for internal networks)\n # - url: ${INTERNAL_URL}/mcp-configs\n # headers:\n # Authorization: Bearer ${INTERNAL_TOKEN}\n # security:\n # allowPrivateIPs: true # Allow internal IPs (default: false)\n # enforceHttps: false # Allow HTTP (default: true, HTTPS only)\n # mergeStrategy: local-priority\n #\n # Example 3: Remote config with additional validation (OPTIONAL)\n # - url: ${AGIFLOW_URL}/api/v1/mcp-configs\n # headers:\n # Authorization: Bearer ${AGIFLOW_API_KEY}\n # X-API-Key: ${AGIFLOW_API_KEY}\n # security:\n # enforceHttps: true # Require HTTPS (default: true)\n # allowPrivateIPs: false # Block private IPs (default: false)\n # validation: # OPTIONAL: Additional regex validation on top of security checks\n # url: ^https://.*\\.agiflow\\.io/.* # OPTIONAL: Regex pattern to validate URL format\n # headers: # OPTIONAL: Regex patterns to validate header values\n # Authorization: ^Bearer [A-Za-z0-9_-]+$\n # X-API-Key: ^[A-Za-z0-9_-]{32,}$\n # mergeStrategy: local-priority\n\nmcpServers:\n # Example MCP server using stdio transport\n example-server:\n command: node\n args:\n - /path/to/mcp-server/build/index.js\n env:\n # Environment variables for the MCP server\n LOG_LEVEL: info\n # You can use environment variable interpolation:\n # DATABASE_URL: ${DATABASE_URL}\n # API_KEY: ${MY_API_KEY}\n config:\n # Server's default instruction (from server documentation)\n instruction: Use this server for...\n # Optional: Block specific tools from being listed or executed\n # toolBlacklist:\n # - dangerous_tool_name\n # - another_blocked_tool\n # Optional: Omit tool descriptions to save tokens (default: false)\n # omitToolDescription: true\n # instruction: Optional user override - takes precedence over config.instruction\n\n # Example MCP server using SSE transport with environment variables\n # remote-server:\n # url: https://example.com/mcp\n # type: sse\n # headers:\n # # Use ${VAR_NAME} to interpolate environment variables\n # Authorization: Bearer ${API_KEY}\n # config:\n # instruction: This server provides tools for...\n # # Optional: Block specific tools from being listed or executed\n # # toolBlacklist:\n # # - tool_to_block\n # # Optional: Omit tool descriptions to save tokens (default: false)\n # # omitToolDescription: true\n # # instruction: Optional user override\n";
472
+ //#region src/templates/mcp-config.yaml.liquid?raw
473
+ var mcp_config_yaml_default = "# MCP Server Configuration\n# This file configures the MCP servers that one-mcp will connect to\n#\n# Environment Variable Interpolation:\n# Use ${VAR_NAME} syntax to reference environment variables\n# Example: ${HOME}, ${API_KEY}, ${DATABASE_URL}\n#\n# Instructions:\n# - config.instruction: Server's default instruction (from server documentation)\n# - instruction: User override (optional, takes precedence over config.instruction)\n# - config.toolBlacklist: Array of tool names to hide/block from this server\n# - config.omitToolDescription: Boolean to show only tool names without descriptions (saves tokens)\n\n# Remote Configuration Sources (OPTIONAL)\n# Fetch and merge configurations from remote URLs\n# Remote configs are merged with local configs based on merge strategy\n#\n# SECURITY: SSRF Protection is ENABLED by default\n# - Only HTTPS URLs are allowed (set security.enforceHttps: false to allow HTTP)\n# - Private IPs and localhost are blocked (set security.allowPrivateIPs: true for internal networks)\n# - Blocked ranges: 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16\nremoteConfigs:\n # Example 1: Basic remote config with default security\n # - url: ${AGIFLOW_URL}/api/v1/mcp-configs\n # headers:\n # Authorization: Bearer ${AGIFLOW_API_KEY}\n # mergeStrategy: local-priority # Options: local-priority (default), remote-priority, merge-deep\n #\n # Example 2: Remote config with custom security settings (for internal networks)\n # - url: ${INTERNAL_URL}/mcp-configs\n # headers:\n # Authorization: Bearer ${INTERNAL_TOKEN}\n # security:\n # allowPrivateIPs: true # Allow internal IPs (default: false)\n # enforceHttps: false # Allow HTTP (default: true, HTTPS only)\n # mergeStrategy: local-priority\n #\n # Example 3: Remote config with additional validation (OPTIONAL)\n # - url: ${AGIFLOW_URL}/api/v1/mcp-configs\n # headers:\n # Authorization: Bearer ${AGIFLOW_API_KEY}\n # X-API-Key: ${AGIFLOW_API_KEY}\n # security:\n # enforceHttps: true # Require HTTPS (default: true)\n # allowPrivateIPs: false # Block private IPs (default: false)\n # validation: # OPTIONAL: Additional regex validation on top of security checks\n # url: ^https://.*\\.agiflow\\.io/.* # OPTIONAL: Regex pattern to validate URL format\n # headers: # OPTIONAL: Regex patterns to validate header values\n # Authorization: ^Bearer [A-Za-z0-9_-]+$\n # X-API-Key: ^[A-Za-z0-9_-]{32,}$\n # mergeStrategy: local-priority\n\nmcpServers:\n{%- if mcpServers %}{% for server in mcpServers %}\n {{ server.name }}:\n command: {{ server.command }}\n args:{% for arg in server.args %}\n - '{{ arg }}'{% endfor %}\n # env:\n # LOG_LEVEL: info\n # # API_KEY: ${MY_API_KEY}\n # config:\n # instruction: Use this server for...\n # # toolBlacklist:\n # # - tool_to_block\n # # omitToolDescription: true\n{% endfor %}\n # Example MCP server using SSE transport\n # remote-server:\n # url: https://example.com/mcp\n # type: sse\n # headers:\n # Authorization: Bearer ${API_KEY}\n # config:\n # instruction: This server provides tools for...\n{% else %}\n # Example MCP server using stdio transport\n example-server:\n command: node\n args:\n - /path/to/mcp-server/build/index.js\n env:\n # Environment variables for the MCP server\n LOG_LEVEL: info\n # You can use environment variable interpolation:\n # DATABASE_URL: ${DATABASE_URL}\n # API_KEY: ${MY_API_KEY}\n config:\n # Server's default instruction (from server documentation)\n instruction: Use this server for...\n # Optional: Block specific tools from being listed or executed\n # toolBlacklist:\n # - dangerous_tool_name\n # - another_blocked_tool\n # Optional: Omit tool descriptions to save tokens (default: false)\n # omitToolDescription: true\n # instruction: Optional user override - takes precedence over config.instruction\n\n # Example MCP server using SSE transport with environment variables\n # remote-server:\n # url: https://example.com/mcp\n # type: sse\n # headers:\n # # Use ${VAR_NAME} to interpolate environment variables\n # Authorization: Bearer ${API_KEY}\n # config:\n # instruction: This server provides tools for...\n # # Optional: Block specific tools from being listed or executed\n # # toolBlacklist:\n # # - tool_to_block\n # # Optional: Omit tool descriptions to save tokens (default: false)\n # # omitToolDescription: true\n # # instruction: Optional user override\n{% endif %}\n";
403
474
 
404
475
  //#endregion
405
476
  //#region src/templates/mcp-config.json?raw
406
- var mcp_config_default$1 = "{\n \"_comment\": \"MCP Server Configuration - Use ${VAR_NAME} syntax for environment variable interpolation\",\n \"_instructions\": \"config.instruction: Server's default instruction | instruction: User override (takes precedence)\",\n \"mcpServers\": {\n \"example-server\": {\n \"command\": \"node\",\n \"args\": [\n \"/path/to/mcp-server/build/index.js\"\n ],\n \"env\": {\n \"LOG_LEVEL\": \"info\",\n \"_comment\": \"You can use environment variable interpolation:\",\n \"_example_DATABASE_URL\": \"${DATABASE_URL}\",\n \"_example_API_KEY\": \"${MY_API_KEY}\"\n },\n \"config\": {\n \"instruction\": \"Use this server for...\"\n },\n \"_instruction_override\": \"Optional user override - takes precedence over config.instruction\"\n }\n }\n}\n";
477
+ var mcp_config_default = "{\n \"_comment\": \"MCP Server Configuration - Use ${VAR_NAME} syntax for environment variable interpolation\",\n \"_instructions\": \"config.instruction: Server's default instruction | instruction: User override (takes precedence)\",\n \"mcpServers\": {\n \"example-server\": {\n \"command\": \"node\",\n \"args\": [\n \"/path/to/mcp-server/build/index.js\"\n ],\n \"env\": {\n \"LOG_LEVEL\": \"info\",\n \"_comment\": \"You can use environment variable interpolation:\",\n \"_example_DATABASE_URL\": \"${DATABASE_URL}\",\n \"_example_API_KEY\": \"${MY_API_KEY}\"\n },\n \"config\": {\n \"instruction\": \"Use this server for...\"\n },\n \"_instruction_override\": \"Optional user override - takes precedence over config.instruction\"\n }\n }\n}\n";
407
478
 
408
479
  //#endregion
409
480
  //#region src/commands/init.ts
@@ -431,12 +502,29 @@ var mcp_config_default$1 = "{\n \"_comment\": \"MCP Server Configuration - Use
431
502
  /**
432
503
  * Initialize MCP configuration file
433
504
  */
434
- const initCommand = new Command("init").description("Initialize MCP configuration file").option("-o, --output <path>", "Output file path", "mcp-config.yaml").option("--json", "Generate JSON config instead of YAML", false).option("-f, --force", "Overwrite existing config file", false).action(async (options) => {
505
+ const initCommand = new Command("init").description("Initialize MCP configuration file").option("-o, --output <path>", "Output file path", "mcp-config.yaml").option("--json", "Generate JSON config instead of YAML", false).option("-f, --force", "Overwrite existing config file", false).option("--mcp-servers <json>", "JSON string of MCP servers to add to config (optional)").action(async (options) => {
435
506
  try {
436
507
  const outputPath = resolve(options.output);
437
- const template = !options.json && (outputPath.endsWith(".yaml") || outputPath.endsWith(".yml")) ? mcp_config_default : mcp_config_default$1;
508
+ const isYaml = !options.json && (outputPath.endsWith(".yaml") || outputPath.endsWith(".yml"));
509
+ let content;
510
+ if (isYaml) {
511
+ const liquid = new Liquid();
512
+ let mcpServersData = null;
513
+ if (options.mcpServers) try {
514
+ const serversObj = JSON.parse(options.mcpServers);
515
+ mcpServersData = Object.entries(serversObj).map(([name, config]) => ({
516
+ name,
517
+ command: config.command,
518
+ args: config.args
519
+ }));
520
+ } catch (parseError) {
521
+ log.error("Failed to parse --mcp-servers JSON:", parseError instanceof Error ? parseError.message : String(parseError));
522
+ process.exit(1);
523
+ }
524
+ content = await liquid.parseAndRender(mcp_config_yaml_default, { mcpServers: mcpServersData });
525
+ } else content = mcp_config_default;
438
526
  try {
439
- await writeFile(outputPath, template, {
527
+ await writeFile(outputPath, content, {
440
528
  encoding: "utf-8",
441
529
  flag: options.force ? "w" : "wx"
442
530
  });
@@ -460,7 +548,7 @@ const initCommand = new Command("init").description("Initialize MCP configuratio
460
548
 
461
549
  //#endregion
462
550
  //#region package.json
463
- var version = "0.2.2";
551
+ var version = "0.2.4";
464
552
 
465
553
  //#endregion
466
554
  //#region src/cli.ts