@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/README.md +139 -280
- package/dist/cli.cjs +118 -30
- package/dist/cli.mjs +118 -30
- package/dist/{http-CzQfsUEI.mjs → http-BKDyW8YB.mjs} +596 -59
- package/dist/{http-3v8zyDO3.cjs → http-Q8LPwwwP.cjs} +609 -62
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +160 -18
- package/dist/index.d.mts +160 -18
- package/dist/index.mjs +1 -1
- package/package.json +4 -2
package/dist/cli.cjs
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const require_http = require('./http-
|
|
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
|
|
11
|
+
* Transport mode constants
|
|
11
12
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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", "
|
|
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:
|
|
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:
|
|
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(
|
|
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 = {
|
|
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 (
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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.
|
|
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
|
|
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
|
|
11
|
+
* Transport mode constants
|
|
11
12
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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", "
|
|
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:
|
|
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:
|
|
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(
|
|
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 = {
|
|
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 (
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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.
|
|
551
|
+
var version = "0.2.4";
|
|
464
552
|
|
|
465
553
|
//#endregion
|
|
466
554
|
//#region src/cli.ts
|