@agiflowai/one-mcp 0.3.12 → 0.3.14
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 +9 -3
- package/dist/cli.cjs +579 -241
- package/dist/cli.mjs +541 -203
- package/dist/index.cjs +18 -12
- package/dist/index.d.cts +332 -23
- package/dist/index.d.mts +333 -23
- package/dist/index.mjs +2 -2
- package/dist/{http-ClRbCldm.mjs → src-D7Yq1bTx.mjs} +700 -167
- package/dist/{http-B4_JfY6t.cjs → src-iTE9Cero.cjs} +723 -166
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -1,192 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const
|
|
2
|
+
const require_src = require('./src-iTE9Cero.cjs');
|
|
3
3
|
let node_fs_promises = require("node:fs/promises");
|
|
4
|
+
let node_fs = require("node:fs");
|
|
5
|
+
let node_crypto = require("node:crypto");
|
|
4
6
|
let node_path = require("node:path");
|
|
5
7
|
let liquidjs = require("liquidjs");
|
|
8
|
+
let node_child_process = require("node:child_process");
|
|
6
9
|
let commander = require("commander");
|
|
7
10
|
let __agiflowai_aicode_utils = require("@agiflowai/aicode-utils");
|
|
8
|
-
let node_child_process = require("node:child_process");
|
|
9
|
-
|
|
10
|
-
//#region src/templates/mcp-config.yaml.liquid?raw
|
|
11
|
-
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";
|
|
12
|
-
|
|
13
|
-
//#endregion
|
|
14
|
-
//#region src/templates/mcp-config.json?raw
|
|
15
|
-
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\": [\"/path/to/mcp-server/build/index.js\"],\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";
|
|
16
|
-
|
|
17
|
-
//#endregion
|
|
18
|
-
//#region src/commands/init.ts
|
|
19
|
-
/**
|
|
20
|
-
* Init Command
|
|
21
|
-
*
|
|
22
|
-
* DESIGN PATTERNS:
|
|
23
|
-
* - Command pattern with Commander for CLI argument parsing
|
|
24
|
-
* - Async/await pattern for asynchronous operations
|
|
25
|
-
* - Error handling pattern with try-catch and proper exit codes
|
|
26
|
-
*
|
|
27
|
-
* CODING STANDARDS:
|
|
28
|
-
* - Use async action handlers for asynchronous operations
|
|
29
|
-
* - Provide clear option descriptions and default values
|
|
30
|
-
* - Handle errors gracefully with process.exit()
|
|
31
|
-
* - Log progress and errors to console
|
|
32
|
-
* - Use Commander's .option() and .argument() for inputs
|
|
33
|
-
*
|
|
34
|
-
* AVOID:
|
|
35
|
-
* - Synchronous blocking operations in action handlers
|
|
36
|
-
* - Missing error handling (always use try-catch)
|
|
37
|
-
* - Hardcoded values (use options or environment variables)
|
|
38
|
-
* - Not exiting with appropriate exit codes on errors
|
|
39
|
-
*/
|
|
40
|
-
/**
|
|
41
|
-
* Initialize MCP configuration file
|
|
42
|
-
*/
|
|
43
|
-
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) => {
|
|
44
|
-
try {
|
|
45
|
-
const outputPath = (0, node_path.resolve)(options.output);
|
|
46
|
-
const isYaml = !options.json && (outputPath.endsWith(".yaml") || outputPath.endsWith(".yml"));
|
|
47
|
-
let content;
|
|
48
|
-
if (isYaml) {
|
|
49
|
-
const liquid = new liquidjs.Liquid();
|
|
50
|
-
let mcpServersData = null;
|
|
51
|
-
if (options.mcpServers) try {
|
|
52
|
-
const serversObj = JSON.parse(options.mcpServers);
|
|
53
|
-
mcpServersData = Object.entries(serversObj).map(([name, config]) => ({
|
|
54
|
-
name,
|
|
55
|
-
command: config.command,
|
|
56
|
-
args: config.args
|
|
57
|
-
}));
|
|
58
|
-
} catch (parseError) {
|
|
59
|
-
__agiflowai_aicode_utils.log.error("Failed to parse --mcp-servers JSON:", parseError instanceof Error ? parseError.message : String(parseError));
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
|
62
|
-
content = await liquid.parseAndRender(mcp_config_yaml_default, { mcpServers: mcpServersData });
|
|
63
|
-
} else content = mcp_config_default;
|
|
64
|
-
try {
|
|
65
|
-
await (0, node_fs_promises.writeFile)(outputPath, content, {
|
|
66
|
-
encoding: "utf-8",
|
|
67
|
-
flag: options.force ? "w" : "wx"
|
|
68
|
-
});
|
|
69
|
-
} catch (error) {
|
|
70
|
-
if (error && typeof error === "object" && "code" in error && error.code === "EEXIST") {
|
|
71
|
-
__agiflowai_aicode_utils.log.error(`Config file already exists: ${outputPath}`);
|
|
72
|
-
__agiflowai_aicode_utils.log.info("Use --force to overwrite");
|
|
73
|
-
process.exit(1);
|
|
74
|
-
}
|
|
75
|
-
throw error;
|
|
76
|
-
}
|
|
77
|
-
__agiflowai_aicode_utils.log.info(`MCP configuration file created: ${outputPath}`);
|
|
78
|
-
__agiflowai_aicode_utils.log.info("Next steps:");
|
|
79
|
-
__agiflowai_aicode_utils.log.info("1. Edit the configuration file to add your MCP servers");
|
|
80
|
-
__agiflowai_aicode_utils.log.info(`2. Run: one-mcp mcp-serve --config ${outputPath}`);
|
|
81
|
-
} catch (error) {
|
|
82
|
-
__agiflowai_aicode_utils.log.error("Error executing init:", error instanceof Error ? error.message : String(error));
|
|
83
|
-
process.exit(1);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
//#endregion
|
|
88
|
-
//#region src/types/index.ts
|
|
89
|
-
/**
|
|
90
|
-
* Transport mode constants
|
|
91
|
-
*/
|
|
92
|
-
const TRANSPORT_MODE = {
|
|
93
|
-
STDIO: "stdio",
|
|
94
|
-
HTTP: "http",
|
|
95
|
-
SSE: "sse"
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
//#endregion
|
|
99
|
-
//#region src/commands/mcp-serve.ts
|
|
100
|
-
/**
|
|
101
|
-
* MCP Serve Command
|
|
102
|
-
*
|
|
103
|
-
* DESIGN PATTERNS:
|
|
104
|
-
* - Command pattern with Commander for CLI argument parsing
|
|
105
|
-
* - Transport abstraction pattern for flexible deployment (stdio, HTTP, SSE)
|
|
106
|
-
* - Factory pattern for creating transport handlers
|
|
107
|
-
* - Graceful shutdown pattern with signal handling
|
|
108
|
-
*
|
|
109
|
-
* CODING STANDARDS:
|
|
110
|
-
* - Use async/await for asynchronous operations
|
|
111
|
-
* - Implement proper error handling with try-catch blocks
|
|
112
|
-
* - Handle process signals for graceful shutdown
|
|
113
|
-
* - Provide clear CLI options and help messages
|
|
114
|
-
*
|
|
115
|
-
* AVOID:
|
|
116
|
-
* - Hardcoded configuration values (use CLI options or environment variables)
|
|
117
|
-
* - Missing error handling for transport startup
|
|
118
|
-
* - Not cleaning up resources on shutdown
|
|
119
|
-
*/
|
|
120
|
-
/**
|
|
121
|
-
* Type guard to validate transport type
|
|
122
|
-
* @param type - The transport type string to validate
|
|
123
|
-
* @returns True if the type is a valid transport type
|
|
124
|
-
*/
|
|
125
|
-
function isValidTransportType(type) {
|
|
126
|
-
return type === "stdio" || type === "http" || type === "sse";
|
|
127
|
-
}
|
|
128
|
-
function isValidProxyMode(mode) {
|
|
129
|
-
return mode === "meta" || mode === "flat" || mode === "search";
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Start MCP server with given transport handler
|
|
133
|
-
* @param handler - The transport handler to start
|
|
134
|
-
*/
|
|
135
|
-
async function startServer(handler) {
|
|
136
|
-
await handler.start();
|
|
137
|
-
const shutdown = async (signal) => {
|
|
138
|
-
console.error(`\nReceived ${signal}, shutting down gracefully...`);
|
|
139
|
-
try {
|
|
140
|
-
await handler.stop();
|
|
141
|
-
process.exit(0);
|
|
142
|
-
} catch (error) {
|
|
143
|
-
console.error("Error during shutdown:", error);
|
|
144
|
-
process.exit(1);
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
148
|
-
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* MCP Serve command
|
|
152
|
-
*/
|
|
153
|
-
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").option("--definitions-cache <path>", "Path to prefetched tool/prompt/skill definitions cache file").option("--clear-definitions-cache", "Delete definitions cache before startup", false).option("--proxy-mode <mode>", "How one-mcp exposes downstream tools: meta, flat, or search", "meta").option("--id <id>", "Unique server identifier (overrides config file id, auto-generated if not provided)").action(async (options) => {
|
|
154
|
-
const transportType = options.type.toLowerCase();
|
|
155
|
-
if (!isValidTransportType(transportType)) {
|
|
156
|
-
console.error(`Unknown transport type: '${transportType}'. Valid options: stdio, http, sse`);
|
|
157
|
-
process.exit(1);
|
|
158
|
-
}
|
|
159
|
-
if (!isValidProxyMode(options.proxyMode)) {
|
|
160
|
-
console.error(`Unknown proxy mode: '${options.proxyMode}'. Valid options: meta, flat, search`);
|
|
161
|
-
process.exit(1);
|
|
162
|
-
}
|
|
163
|
-
try {
|
|
164
|
-
const serverOptions = {
|
|
165
|
-
configFilePath: options.config || require_http.findConfigFile() || void 0,
|
|
166
|
-
noCache: options.cache === false,
|
|
167
|
-
serverId: options.id,
|
|
168
|
-
definitionsCachePath: options.definitionsCache,
|
|
169
|
-
clearDefinitionsCache: options.clearDefinitionsCache,
|
|
170
|
-
proxyMode: options.proxyMode
|
|
171
|
-
};
|
|
172
|
-
if (transportType === "stdio") await startServer(new require_http.StdioTransportHandler(await require_http.createServer(serverOptions)));
|
|
173
|
-
else if (transportType === "http") await startServer(new require_http.HttpTransportHandler(await require_http.createServer(serverOptions), {
|
|
174
|
-
mode: TRANSPORT_MODE.HTTP,
|
|
175
|
-
port: options.port || Number(process.env.MCP_PORT) || 3e3,
|
|
176
|
-
host: options.host || process.env.MCP_HOST || "localhost"
|
|
177
|
-
}));
|
|
178
|
-
else if (transportType === "sse") await startServer(new require_http.SseTransportHandler(await require_http.createServer(serverOptions), {
|
|
179
|
-
mode: TRANSPORT_MODE.SSE,
|
|
180
|
-
port: options.port || Number(process.env.MCP_PORT) || 3e3,
|
|
181
|
-
host: options.host || process.env.MCP_HOST || "localhost"
|
|
182
|
-
}));
|
|
183
|
-
} catch (error) {
|
|
184
|
-
console.error(`Failed to start MCP server with transport '${transportType}' on ${options.host}:${options.port}:`, error);
|
|
185
|
-
process.exit(1);
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
11
|
|
|
189
|
-
//#endregion
|
|
190
12
|
//#region src/services/PrefetchService/constants.ts
|
|
191
13
|
/**
|
|
192
14
|
* PrefetchService Constants
|
|
@@ -531,7 +353,7 @@ var PrefetchService = class {
|
|
|
531
353
|
* @returns Promise with success status and output
|
|
532
354
|
*/
|
|
533
355
|
runCommand(command, args) {
|
|
534
|
-
return new Promise((resolve$
|
|
356
|
+
return new Promise((resolve$2) => {
|
|
535
357
|
const proc = (0, node_child_process.spawn)(command, args, {
|
|
536
358
|
stdio: [
|
|
537
359
|
STDIO_IGNORE,
|
|
@@ -549,13 +371,13 @@ var PrefetchService = class {
|
|
|
549
371
|
stderr += data.toString();
|
|
550
372
|
});
|
|
551
373
|
proc.on("close", (code) => {
|
|
552
|
-
resolve$
|
|
374
|
+
resolve$2({
|
|
553
375
|
success: code === EXIT_CODE_SUCCESS,
|
|
554
376
|
output: stdout || stderr
|
|
555
377
|
});
|
|
556
378
|
});
|
|
557
379
|
proc.on("error", (error) => {
|
|
558
|
-
resolve$
|
|
380
|
+
resolve$2({
|
|
559
381
|
success: false,
|
|
560
382
|
output: error.message
|
|
561
383
|
});
|
|
@@ -564,6 +386,475 @@ var PrefetchService = class {
|
|
|
564
386
|
}
|
|
565
387
|
};
|
|
566
388
|
|
|
389
|
+
//#endregion
|
|
390
|
+
//#region src/templates/mcp-config.yaml.liquid?raw
|
|
391
|
+
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";
|
|
392
|
+
|
|
393
|
+
//#endregion
|
|
394
|
+
//#region src/templates/mcp-config.json?raw
|
|
395
|
+
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\": [\"/path/to/mcp-server/build/index.js\"],\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";
|
|
396
|
+
|
|
397
|
+
//#endregion
|
|
398
|
+
//#region src/commands/init.ts
|
|
399
|
+
/**
|
|
400
|
+
* Init Command
|
|
401
|
+
*
|
|
402
|
+
* DESIGN PATTERNS:
|
|
403
|
+
* - Command pattern with Commander for CLI argument parsing
|
|
404
|
+
* - Async/await pattern for asynchronous operations
|
|
405
|
+
* - Error handling pattern with try-catch and proper exit codes
|
|
406
|
+
*
|
|
407
|
+
* CODING STANDARDS:
|
|
408
|
+
* - Use async action handlers for asynchronous operations
|
|
409
|
+
* - Provide clear option descriptions and default values
|
|
410
|
+
* - Handle errors gracefully with process.exit()
|
|
411
|
+
* - Log progress and errors to console
|
|
412
|
+
* - Use Commander's .option() and .argument() for inputs
|
|
413
|
+
*
|
|
414
|
+
* AVOID:
|
|
415
|
+
* - Synchronous blocking operations in action handlers
|
|
416
|
+
* - Missing error handling (always use try-catch)
|
|
417
|
+
* - Hardcoded values (use options or environment variables)
|
|
418
|
+
* - Not exiting with appropriate exit codes on errors
|
|
419
|
+
*/
|
|
420
|
+
/**
|
|
421
|
+
* Initialize MCP configuration file
|
|
422
|
+
*/
|
|
423
|
+
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) => {
|
|
424
|
+
try {
|
|
425
|
+
const outputPath = (0, node_path.resolve)(options.output);
|
|
426
|
+
const isYaml = !options.json && (outputPath.endsWith(".yaml") || outputPath.endsWith(".yml"));
|
|
427
|
+
let content;
|
|
428
|
+
if (isYaml) {
|
|
429
|
+
const liquid = new liquidjs.Liquid();
|
|
430
|
+
let mcpServersData = null;
|
|
431
|
+
if (options.mcpServers) try {
|
|
432
|
+
const serversObj = JSON.parse(options.mcpServers);
|
|
433
|
+
mcpServersData = Object.entries(serversObj).map(([name, config]) => ({
|
|
434
|
+
name,
|
|
435
|
+
command: config.command,
|
|
436
|
+
args: config.args
|
|
437
|
+
}));
|
|
438
|
+
} catch (parseError) {
|
|
439
|
+
__agiflowai_aicode_utils.log.error("Failed to parse --mcp-servers JSON:", parseError instanceof Error ? parseError.message : String(parseError));
|
|
440
|
+
process.exit(1);
|
|
441
|
+
}
|
|
442
|
+
content = await liquid.parseAndRender(mcp_config_yaml_default, { mcpServers: mcpServersData });
|
|
443
|
+
} else content = mcp_config_default;
|
|
444
|
+
try {
|
|
445
|
+
await (0, node_fs_promises.writeFile)(outputPath, content, {
|
|
446
|
+
encoding: "utf-8",
|
|
447
|
+
flag: options.force ? "w" : "wx"
|
|
448
|
+
});
|
|
449
|
+
} catch (error) {
|
|
450
|
+
if (error && typeof error === "object" && "code" in error && error.code === "EEXIST") {
|
|
451
|
+
__agiflowai_aicode_utils.log.error(`Config file already exists: ${outputPath}`);
|
|
452
|
+
__agiflowai_aicode_utils.log.info("Use --force to overwrite");
|
|
453
|
+
process.exit(1);
|
|
454
|
+
}
|
|
455
|
+
throw error;
|
|
456
|
+
}
|
|
457
|
+
__agiflowai_aicode_utils.log.info(`MCP configuration file created: ${outputPath}`);
|
|
458
|
+
__agiflowai_aicode_utils.log.info("Next steps:");
|
|
459
|
+
__agiflowai_aicode_utils.log.info("1. Edit the configuration file to add your MCP servers");
|
|
460
|
+
__agiflowai_aicode_utils.log.info(`2. Run: one-mcp mcp-serve --config ${outputPath}`);
|
|
461
|
+
} catch (error) {
|
|
462
|
+
__agiflowai_aicode_utils.log.error("Error executing init:", error instanceof Error ? error.message : String(error));
|
|
463
|
+
process.exit(1);
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
//#endregion
|
|
468
|
+
//#region src/commands/mcp-serve.ts
|
|
469
|
+
/**
|
|
470
|
+
* MCP Serve Command
|
|
471
|
+
*
|
|
472
|
+
* DESIGN PATTERNS:
|
|
473
|
+
* - Command pattern with Commander for CLI argument parsing
|
|
474
|
+
* - Transport abstraction pattern for flexible deployment (stdio, HTTP, SSE)
|
|
475
|
+
* - Factory pattern for creating transport handlers
|
|
476
|
+
* - Graceful shutdown pattern with signal handling
|
|
477
|
+
*
|
|
478
|
+
* CODING STANDARDS:
|
|
479
|
+
* - Use async/await for asynchronous operations
|
|
480
|
+
* - Implement proper error handling with try-catch blocks
|
|
481
|
+
* - Handle process signals for graceful shutdown
|
|
482
|
+
* - Provide clear CLI options and help messages
|
|
483
|
+
*
|
|
484
|
+
* AVOID:
|
|
485
|
+
* - Hardcoded configuration values (use CLI options or environment variables)
|
|
486
|
+
* - Missing error handling for transport startup
|
|
487
|
+
* - Not cleaning up resources on shutdown
|
|
488
|
+
*/
|
|
489
|
+
const CONFIG_FILE_NAMES = [
|
|
490
|
+
"mcp-config.yaml",
|
|
491
|
+
"mcp-config.yml",
|
|
492
|
+
"mcp-config.json"
|
|
493
|
+
];
|
|
494
|
+
const MCP_ENDPOINT_PATH = "/mcp";
|
|
495
|
+
const DEFAULT_PORT = 3e3;
|
|
496
|
+
const DEFAULT_HOST = "localhost";
|
|
497
|
+
const TRANSPORT_TYPE_STDIO = "stdio";
|
|
498
|
+
const TRANSPORT_TYPE_HTTP = "http";
|
|
499
|
+
const TRANSPORT_TYPE_SSE = "sse";
|
|
500
|
+
const TRANSPORT_TYPE_STDIO_HTTP = "stdio-http";
|
|
501
|
+
const RUNTIME_TRANSPORT = TRANSPORT_TYPE_HTTP;
|
|
502
|
+
const STDIO_HTTP_PROXY_STOP_LABEL = "Failed stopping stdio-http proxy";
|
|
503
|
+
const INTERNAL_HTTP_STOP_LABEL = "Failed stopping internal HTTP transport";
|
|
504
|
+
function toErrorMessage$6(error) {
|
|
505
|
+
return error instanceof Error ? error.message : String(error);
|
|
506
|
+
}
|
|
507
|
+
function isValidTransportType(type) {
|
|
508
|
+
return type === TRANSPORT_TYPE_STDIO || type === TRANSPORT_TYPE_HTTP || type === TRANSPORT_TYPE_SSE || type === TRANSPORT_TYPE_STDIO_HTTP;
|
|
509
|
+
}
|
|
510
|
+
function isValidProxyMode(mode) {
|
|
511
|
+
return mode === "meta" || mode === "flat" || mode === "search";
|
|
512
|
+
}
|
|
513
|
+
function isAddressInUseError(error) {
|
|
514
|
+
if (error instanceof Error && error.message.includes("EADDRINUSE")) return true;
|
|
515
|
+
if (typeof error !== "object" || error === null) return false;
|
|
516
|
+
if ("code" in error && error.code === "EADDRINUSE") return true;
|
|
517
|
+
if ("message" in error && typeof error.message === "string" && error.message.includes("EADDRINUSE")) return true;
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
520
|
+
async function pathExists(filePath) {
|
|
521
|
+
try {
|
|
522
|
+
await (0, node_fs_promises.access)(filePath, node_fs.constants.F_OK);
|
|
523
|
+
return true;
|
|
524
|
+
} catch {
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
async function findConfigFileAsync() {
|
|
529
|
+
try {
|
|
530
|
+
const projectPath = process.env.PROJECT_PATH;
|
|
531
|
+
if (projectPath) for (const fileName of CONFIG_FILE_NAMES) {
|
|
532
|
+
const configPath = (0, node_path.resolve)(projectPath, fileName);
|
|
533
|
+
if (await pathExists(configPath)) return configPath;
|
|
534
|
+
}
|
|
535
|
+
const cwd = process.cwd();
|
|
536
|
+
for (const fileName of CONFIG_FILE_NAMES) {
|
|
537
|
+
const configPath = (0, node_path.join)(cwd, fileName);
|
|
538
|
+
if (await pathExists(configPath)) return configPath;
|
|
539
|
+
}
|
|
540
|
+
return null;
|
|
541
|
+
} catch (error) {
|
|
542
|
+
throw new Error(`Failed to discover MCP config file: ${toErrorMessage$6(error)}`);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
async function resolveServerId(options, resolvedConfigPath) {
|
|
546
|
+
if (options.id) return options.id;
|
|
547
|
+
if (resolvedConfigPath) try {
|
|
548
|
+
const config = await new require_src.ConfigFetcherService({
|
|
549
|
+
configFilePath: resolvedConfigPath,
|
|
550
|
+
useCache: options.cache !== false
|
|
551
|
+
}).fetchConfiguration(options.cache === false);
|
|
552
|
+
if (config.id) return config.id;
|
|
553
|
+
} catch (error) {
|
|
554
|
+
throw new Error(`Failed to resolve server ID from config '${resolvedConfigPath}': ${toErrorMessage$6(error)}`);
|
|
555
|
+
}
|
|
556
|
+
return require_src.generateServerId();
|
|
557
|
+
}
|
|
558
|
+
function validateTransportType(type) {
|
|
559
|
+
if (!isValidTransportType(type)) throw new Error(`Unknown transport type: '${type}'. Valid options: ${TRANSPORT_TYPE_STDIO}, ${TRANSPORT_TYPE_HTTP}, ${TRANSPORT_TYPE_SSE}, ${TRANSPORT_TYPE_STDIO_HTTP}`);
|
|
560
|
+
return type;
|
|
561
|
+
}
|
|
562
|
+
function validateProxyMode(mode) {
|
|
563
|
+
if (!isValidProxyMode(mode)) throw new Error(`Unknown proxy mode: '${mode}'. Valid options: meta, flat, search`);
|
|
564
|
+
}
|
|
565
|
+
function createTransportConfig(options, mode) {
|
|
566
|
+
return {
|
|
567
|
+
mode,
|
|
568
|
+
port: options.port || Number(process.env.MCP_PORT) || DEFAULT_PORT,
|
|
569
|
+
host: options.host || process.env.MCP_HOST || DEFAULT_HOST
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
function createServerOptions(options, resolvedConfigPath, serverId) {
|
|
573
|
+
return {
|
|
574
|
+
configFilePath: resolvedConfigPath,
|
|
575
|
+
noCache: options.cache === false,
|
|
576
|
+
serverId,
|
|
577
|
+
definitionsCachePath: options.definitionsCache,
|
|
578
|
+
clearDefinitionsCache: options.clearDefinitionsCache,
|
|
579
|
+
proxyMode: options.proxyMode
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
function formatStartError(type, host, port, error) {
|
|
583
|
+
const startErrorMessage = toErrorMessage$6(error);
|
|
584
|
+
if (type === TRANSPORT_TYPE_STDIO) return `Failed to start MCP server with transport '${type}': ${startErrorMessage}`;
|
|
585
|
+
return `Failed to start MCP server with transport '${type}' on ${host}:${port}: ${startErrorMessage}`;
|
|
586
|
+
}
|
|
587
|
+
function createRuntimeRecord(serverId, config, shutdownToken, configPath) {
|
|
588
|
+
return {
|
|
589
|
+
serverId,
|
|
590
|
+
host: config.host ?? DEFAULT_HOST,
|
|
591
|
+
port: config.port ?? DEFAULT_PORT,
|
|
592
|
+
transport: RUNTIME_TRANSPORT,
|
|
593
|
+
shutdownToken,
|
|
594
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
595
|
+
pid: process.pid,
|
|
596
|
+
configPath
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
function createHttpAdminOptions(serverId, shutdownToken, onShutdownRequested) {
|
|
600
|
+
return {
|
|
601
|
+
serverId,
|
|
602
|
+
shutdownToken,
|
|
603
|
+
onShutdownRequested
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
async function removeRuntimeRecord(runtimeStateService, serverId) {
|
|
607
|
+
try {
|
|
608
|
+
await runtimeStateService.remove(serverId);
|
|
609
|
+
} catch (error) {
|
|
610
|
+
throw new Error(`Failed to remove runtime state for '${serverId}': ${toErrorMessage$6(error)}`);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
async function writeRuntimeRecord(runtimeStateService, record) {
|
|
614
|
+
try {
|
|
615
|
+
await runtimeStateService.write(record);
|
|
616
|
+
} catch (error) {
|
|
617
|
+
throw new Error(`Failed to persist runtime state for '${record.serverId}': ${toErrorMessage$6(error)}`);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
async function stopOwnedHttpTransport(handler, runtimeStateService, serverId) {
|
|
621
|
+
try {
|
|
622
|
+
try {
|
|
623
|
+
await handler.stop();
|
|
624
|
+
} catch (error) {
|
|
625
|
+
throw new Error(`Failed to stop owned HTTP transport '${serverId}': ${toErrorMessage$6(error)}`);
|
|
626
|
+
}
|
|
627
|
+
} finally {
|
|
628
|
+
await removeRuntimeRecord(runtimeStateService, serverId);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
async function cleanupFailedRuntimeStartup(handler, runtimeStateService, serverId) {
|
|
632
|
+
try {
|
|
633
|
+
try {
|
|
634
|
+
await handler.stop();
|
|
635
|
+
} catch (error) {
|
|
636
|
+
throw new Error(`Failed to stop HTTP transport during cleanup for '${serverId}': ${toErrorMessage$6(error)}`);
|
|
637
|
+
}
|
|
638
|
+
} finally {
|
|
639
|
+
await removeRuntimeRecord(runtimeStateService, serverId);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
async function stopTransportWithContext(label, stopOperation) {
|
|
643
|
+
try {
|
|
644
|
+
await stopOperation();
|
|
645
|
+
} catch (error) {
|
|
646
|
+
throw new Error(`${label}: ${toErrorMessage$6(error)}`);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
async function removeRuntimeRecordDuringStop(runtimeStateService, serverId) {
|
|
650
|
+
try {
|
|
651
|
+
await removeRuntimeRecord(runtimeStateService, serverId);
|
|
652
|
+
} catch (error) {
|
|
653
|
+
throw new Error(`Failed to remove runtime state during HTTP stop callback for '${serverId}': ${toErrorMessage$6(error)}`);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
async function createStdioHttpInternalTransport(serverOptions, config, adminOptions) {
|
|
657
|
+
try {
|
|
658
|
+
return new require_src.HttpTransportHandler(await require_src.createServer(serverOptions), config, adminOptions);
|
|
659
|
+
} catch (error) {
|
|
660
|
+
throw new Error(`Failed to create internal HTTP transport for stdio-http proxy: ${toErrorMessage$6(error)}`);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Start MCP server with given transport handler
|
|
665
|
+
* @param handler - The transport handler to start
|
|
666
|
+
* @param onStopped - Optional cleanup callback run after signal-based shutdown
|
|
667
|
+
*/
|
|
668
|
+
async function startServer(handler, onStopped) {
|
|
669
|
+
try {
|
|
670
|
+
await handler.start();
|
|
671
|
+
} catch (error) {
|
|
672
|
+
throw new Error(`Failed to start transport handler: ${toErrorMessage$6(error)}`);
|
|
673
|
+
}
|
|
674
|
+
const shutdown = async (signal) => {
|
|
675
|
+
console.error(`\nReceived ${signal}, shutting down gracefully...`);
|
|
676
|
+
try {
|
|
677
|
+
await handler.stop();
|
|
678
|
+
if (onStopped) await onStopped();
|
|
679
|
+
process.exit(0);
|
|
680
|
+
} catch (error) {
|
|
681
|
+
console.error(`Failed to gracefully stop transport during ${signal}: ${toErrorMessage$6(error)}`);
|
|
682
|
+
process.exit(1);
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
process.on("SIGINT", async () => await shutdown("SIGINT"));
|
|
686
|
+
process.on("SIGTERM", async () => await shutdown("SIGTERM"));
|
|
687
|
+
}
|
|
688
|
+
async function createAndStartHttpRuntime(serverOptions, config, resolvedConfigPath) {
|
|
689
|
+
const runtimeStateService = new require_src.RuntimeStateService();
|
|
690
|
+
const shutdownToken = (0, node_crypto.randomUUID)();
|
|
691
|
+
const runtimeRecord = createRuntimeRecord(serverOptions.serverId ?? require_src.generateServerId(), config, shutdownToken, resolvedConfigPath);
|
|
692
|
+
let handler;
|
|
693
|
+
let isStopping = false;
|
|
694
|
+
const stopHandler = async () => {
|
|
695
|
+
if (isStopping) return;
|
|
696
|
+
isStopping = true;
|
|
697
|
+
try {
|
|
698
|
+
await stopOwnedHttpTransport(handler, runtimeStateService, runtimeRecord.serverId);
|
|
699
|
+
process.exit(0);
|
|
700
|
+
} catch (error) {
|
|
701
|
+
throw new Error(`Failed to stop HTTP runtime '${runtimeRecord.serverId}' from admin shutdown: ${toErrorMessage$6(error)}`);
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
try {
|
|
705
|
+
handler = new require_src.HttpTransportHandler(await require_src.createServer(serverOptions), config, createHttpAdminOptions(runtimeRecord.serverId, shutdownToken, stopHandler));
|
|
706
|
+
} catch (error) {
|
|
707
|
+
throw new Error(`Failed to create HTTP runtime server: ${toErrorMessage$6(error)}`);
|
|
708
|
+
}
|
|
709
|
+
try {
|
|
710
|
+
await startServer(handler, async () => {
|
|
711
|
+
await removeRuntimeRecordDuringStop(runtimeStateService, runtimeRecord.serverId);
|
|
712
|
+
});
|
|
713
|
+
await writeRuntimeRecord(runtimeStateService, runtimeRecord);
|
|
714
|
+
} catch (error) {
|
|
715
|
+
await cleanupFailedRuntimeStartup(handler, runtimeStateService, runtimeRecord.serverId);
|
|
716
|
+
throw new Error(`Failed to start HTTP runtime '${runtimeRecord.serverId}': ${toErrorMessage$6(error)}`);
|
|
717
|
+
}
|
|
718
|
+
console.error(`Runtime state: http://${runtimeRecord.host}:${runtimeRecord.port} (${runtimeRecord.serverId})`);
|
|
719
|
+
}
|
|
720
|
+
async function stopInternalHttpTransport(stdioHttpHandler, httpHandler, ownsInternalHttpTransport) {
|
|
721
|
+
try {
|
|
722
|
+
const stopOperations = [stopTransportWithContext(STDIO_HTTP_PROXY_STOP_LABEL, async () => {
|
|
723
|
+
await stdioHttpHandler.stop();
|
|
724
|
+
})];
|
|
725
|
+
if (ownsInternalHttpTransport && httpHandler) stopOperations.push(stopTransportWithContext(INTERNAL_HTTP_STOP_LABEL, async () => {
|
|
726
|
+
await httpHandler.stop();
|
|
727
|
+
}));
|
|
728
|
+
await Promise.all(stopOperations);
|
|
729
|
+
} catch (error) {
|
|
730
|
+
throw new Error(`Failed to stop stdio-http transport: ${toErrorMessage$6(error)}`);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
async function startStdioTransport(serverOptions) {
|
|
734
|
+
try {
|
|
735
|
+
await startServer(new require_src.StdioTransportHandler(await require_src.createServer(serverOptions)));
|
|
736
|
+
} catch (error) {
|
|
737
|
+
throw new Error(`Failed to start stdio transport: ${toErrorMessage$6(error)}`);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
async function startSseTransport(serverOptions, config) {
|
|
741
|
+
try {
|
|
742
|
+
await startServer(new require_src.SseTransportHandler(await require_src.createServer(serverOptions), config));
|
|
743
|
+
} catch (error) {
|
|
744
|
+
throw new Error(`Failed to start SSE transport: ${toErrorMessage$6(error)}`);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
async function startStdioHttpTransport(serverOptions, config, resolvedConfigPath) {
|
|
748
|
+
try {
|
|
749
|
+
const stdioHttpHandler = new require_src.StdioHttpTransportHandler({ endpoint: new URL(`http://${config.host}:${config.port}${MCP_ENDPOINT_PATH}`) });
|
|
750
|
+
const runtimeStateService = new require_src.RuntimeStateService();
|
|
751
|
+
const serverId = serverOptions.serverId ?? require_src.generateServerId();
|
|
752
|
+
const shutdownToken = (0, node_crypto.randomUUID)();
|
|
753
|
+
let httpHandler = null;
|
|
754
|
+
let ownsInternalHttpTransport = false;
|
|
755
|
+
let isStopping = false;
|
|
756
|
+
const stopOwnedRuntime = async () => {
|
|
757
|
+
if (isStopping) return;
|
|
758
|
+
isStopping = true;
|
|
759
|
+
try {
|
|
760
|
+
await Promise.all([stopInternalHttpTransport(stdioHttpHandler, httpHandler, ownsInternalHttpTransport), removeRuntimeRecord(runtimeStateService, serverId)]);
|
|
761
|
+
ownsInternalHttpTransport = false;
|
|
762
|
+
process.exit(0);
|
|
763
|
+
} catch (error) {
|
|
764
|
+
console.error(`Unexpected error during admin shutdown: ${toErrorMessage$6(error)}`);
|
|
765
|
+
process.exit(1);
|
|
766
|
+
}
|
|
767
|
+
};
|
|
768
|
+
const adminOptions = createHttpAdminOptions(serverId, shutdownToken, stopOwnedRuntime);
|
|
769
|
+
await startServer({
|
|
770
|
+
async start() {
|
|
771
|
+
let initialProxyConnectError;
|
|
772
|
+
try {
|
|
773
|
+
await stdioHttpHandler.start();
|
|
774
|
+
return;
|
|
775
|
+
} catch (error) {
|
|
776
|
+
initialProxyConnectError = error;
|
|
777
|
+
}
|
|
778
|
+
try {
|
|
779
|
+
httpHandler = await createStdioHttpInternalTransport(serverOptions, config, adminOptions);
|
|
780
|
+
await httpHandler.start();
|
|
781
|
+
ownsInternalHttpTransport = true;
|
|
782
|
+
} catch (error) {
|
|
783
|
+
if (!isAddressInUseError(error)) throw new Error(`Failed to start internal HTTP transport for stdio-http proxy: ${toErrorMessage$6(error)}`);
|
|
784
|
+
}
|
|
785
|
+
try {
|
|
786
|
+
await stdioHttpHandler.start();
|
|
787
|
+
} catch (error) {
|
|
788
|
+
let rollbackStopErrorMessage = "";
|
|
789
|
+
if (ownsInternalHttpTransport && httpHandler) {
|
|
790
|
+
try {
|
|
791
|
+
await httpHandler.stop();
|
|
792
|
+
} catch (stopError) {
|
|
793
|
+
rollbackStopErrorMessage = toErrorMessage$6(stopError);
|
|
794
|
+
}
|
|
795
|
+
ownsInternalHttpTransport = false;
|
|
796
|
+
}
|
|
797
|
+
const retryErrorMessage = toErrorMessage$6(error);
|
|
798
|
+
const initialErrorMessage = toErrorMessage$6(initialProxyConnectError);
|
|
799
|
+
const rollbackMessage = rollbackStopErrorMessage ? `; rollback stop failed: ${rollbackStopErrorMessage}` : "";
|
|
800
|
+
throw new Error(`Failed to start stdio-http proxy bridge: initial connect failed (${initialErrorMessage}); retry failed (${retryErrorMessage})${rollbackMessage}`);
|
|
801
|
+
}
|
|
802
|
+
if (ownsInternalHttpTransport) try {
|
|
803
|
+
await writeRuntimeRecord(runtimeStateService, createRuntimeRecord(serverId, config, shutdownToken, resolvedConfigPath));
|
|
804
|
+
} catch (error) {
|
|
805
|
+
throw new Error(`Failed to persist runtime state for stdio-http server '${serverId}': ${toErrorMessage$6(error)}`);
|
|
806
|
+
}
|
|
807
|
+
},
|
|
808
|
+
async stop() {
|
|
809
|
+
try {
|
|
810
|
+
await Promise.all([stopInternalHttpTransport(stdioHttpHandler, httpHandler, ownsInternalHttpTransport), removeRuntimeRecord(runtimeStateService, serverId)]);
|
|
811
|
+
ownsInternalHttpTransport = false;
|
|
812
|
+
} catch (error) {
|
|
813
|
+
ownsInternalHttpTransport = false;
|
|
814
|
+
throw new Error(`Failed during stdio-http shutdown for '${serverId}': ${toErrorMessage$6(error)}`);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
} catch (error) {
|
|
819
|
+
throw new Error(`Failed to start stdio-http transport: ${toErrorMessage$6(error)}`);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
async function startTransport(transportType, options, resolvedConfigPath, serverOptions) {
|
|
823
|
+
try {
|
|
824
|
+
if (transportType === TRANSPORT_TYPE_STDIO) {
|
|
825
|
+
await startStdioTransport(serverOptions);
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
if (transportType === TRANSPORT_TYPE_HTTP) {
|
|
829
|
+
await createAndStartHttpRuntime(serverOptions, createTransportConfig(options, require_src.TRANSPORT_MODE.HTTP), resolvedConfigPath);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
if (transportType === TRANSPORT_TYPE_SSE) {
|
|
833
|
+
await startSseTransport(serverOptions, createTransportConfig(options, require_src.TRANSPORT_MODE.SSE));
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
await startStdioHttpTransport(serverOptions, createTransportConfig(options, require_src.TRANSPORT_MODE.HTTP), resolvedConfigPath);
|
|
837
|
+
} catch (error) {
|
|
838
|
+
throw new Error(`Failed to start transport '${transportType}': ${toErrorMessage$6(error)}`);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* MCP Serve command
|
|
843
|
+
*/
|
|
844
|
+
const mcpServeCommand = new commander.Command("mcp-serve").description("Start MCP server with specified transport").option("-t, --type <type>", `Transport type: ${TRANSPORT_TYPE_STDIO}, ${TRANSPORT_TYPE_HTTP}, ${TRANSPORT_TYPE_SSE}, or ${TRANSPORT_TYPE_STDIO_HTTP}`, TRANSPORT_TYPE_STDIO).option("-p, --port <port>", "Port to listen on (http/sse/stdio-http internal HTTP)", (val) => parseInt(val, 10), DEFAULT_PORT).option("--host <host>", "Host to bind to (http/sse/stdio-http internal HTTP)", DEFAULT_HOST).option("-c, --config <path>", "Path to MCP server configuration file").option("--no-cache", "Disable configuration caching, always reload from config file").option("--definitions-cache <path>", "Path to prefetched tool/prompt/skill definitions cache file").option("--clear-definitions-cache", "Delete definitions cache before startup", false).option("--proxy-mode <mode>", "How one-mcp exposes downstream tools: meta, flat, or search", "meta").option("--id <id>", "Unique server identifier (overrides config file id, auto-generated if not provided)").action(async (options) => {
|
|
845
|
+
try {
|
|
846
|
+
const transportType = validateTransportType(options.type.toLowerCase());
|
|
847
|
+
validateProxyMode(options.proxyMode);
|
|
848
|
+
const resolvedConfigPath = options.config || await findConfigFileAsync() || void 0;
|
|
849
|
+
await startTransport(transportType, options, resolvedConfigPath, createServerOptions(options, resolvedConfigPath, await resolveServerId(options, resolvedConfigPath)));
|
|
850
|
+
} catch (error) {
|
|
851
|
+
const rawTransportType = options.type.toLowerCase();
|
|
852
|
+
const transportType = isValidTransportType(rawTransportType) ? rawTransportType : TRANSPORT_TYPE_STDIO;
|
|
853
|
+
console.error(formatStartError(transportType, options.host, options.port, error));
|
|
854
|
+
process.exit(1);
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
|
|
567
858
|
//#endregion
|
|
568
859
|
//#region src/commands/list-tools.ts
|
|
569
860
|
/**
|
|
@@ -587,7 +878,7 @@ var PrefetchService = class {
|
|
|
587
878
|
* - Hardcoded values (use options or environment variables)
|
|
588
879
|
* - Not exiting with appropriate exit codes on errors
|
|
589
880
|
*/
|
|
590
|
-
function toErrorMessage$
|
|
881
|
+
function toErrorMessage$5(error) {
|
|
591
882
|
return error instanceof Error ? error.message : String(error);
|
|
592
883
|
}
|
|
593
884
|
function printSearchResults(result) {
|
|
@@ -608,29 +899,29 @@ function printSearchResults(result) {
|
|
|
608
899
|
}
|
|
609
900
|
const searchToolsCommand = new commander.Command("search-tools").description("Search proxied MCP tools by capability or server").option("-c, --config <path>", "Path to MCP server configuration file").option("-s, --server <name>", "Filter by server name").option("--capability <name>", "Filter by capability tag, summary, tool name, or description").option("--definitions-cache <path>", "Path to definitions cache file").option("-j, --json", "Output as JSON", false).action(async (options) => {
|
|
610
901
|
try {
|
|
611
|
-
const configFilePath = options.config ||
|
|
902
|
+
const configFilePath = options.config || require_src.findConfigFile();
|
|
612
903
|
if (!configFilePath) throw new Error("No config file found. Use --config or create mcp-config.yaml");
|
|
613
|
-
const config = await new
|
|
614
|
-
const clientManager = new
|
|
904
|
+
const config = await new require_src.ConfigFetcherService({ configFilePath }).fetchConfiguration();
|
|
905
|
+
const clientManager = new require_src.McpClientManagerService();
|
|
615
906
|
clientManager.registerServerConfigs(config.mcpServers);
|
|
616
907
|
const connectionPromises = Object.entries(config.mcpServers).map(async ([serverName, serverConfig]) => {
|
|
617
908
|
try {
|
|
618
909
|
await clientManager.connectToServer(serverName, serverConfig);
|
|
619
910
|
if (!options.json) console.error(`✓ Connected to ${serverName}`);
|
|
620
911
|
} catch (error) {
|
|
621
|
-
if (!options.json) console.error(`✗ Failed to connect to ${serverName}: ${toErrorMessage$
|
|
912
|
+
if (!options.json) console.error(`✗ Failed to connect to ${serverName}: ${toErrorMessage$5(error)}`);
|
|
622
913
|
}
|
|
623
914
|
});
|
|
624
915
|
await Promise.all(connectionPromises);
|
|
625
916
|
if (clientManager.getAllClients().length === 0) throw new Error("No MCP servers connected");
|
|
626
|
-
const cachePath = options.definitionsCache ||
|
|
917
|
+
const cachePath = options.definitionsCache || require_src.DefinitionsCacheService.getDefaultCachePath(configFilePath);
|
|
627
918
|
let cacheData;
|
|
628
919
|
try {
|
|
629
|
-
cacheData = await
|
|
920
|
+
cacheData = await require_src.DefinitionsCacheService.readFromFile(cachePath);
|
|
630
921
|
} catch {
|
|
631
922
|
cacheData = void 0;
|
|
632
923
|
}
|
|
633
|
-
const textBlock = (await new
|
|
924
|
+
const textBlock = (await new require_src.SearchListToolsTool(clientManager, new require_src.DefinitionsCacheService(clientManager, void 0, { cacheData })).execute({
|
|
634
925
|
capability: options.capability,
|
|
635
926
|
serverName: options.server
|
|
636
927
|
})).content.find((content) => content.type === "text");
|
|
@@ -642,7 +933,7 @@ const searchToolsCommand = new commander.Command("search-tools").description("Se
|
|
|
642
933
|
}
|
|
643
934
|
await clientManager.disconnectAll();
|
|
644
935
|
} catch (error) {
|
|
645
|
-
console.error(`Error executing search-tools: ${toErrorMessage$
|
|
936
|
+
console.error(`Error executing search-tools: ${toErrorMessage$5(error)}`);
|
|
646
937
|
process.exit(1);
|
|
647
938
|
}
|
|
648
939
|
});
|
|
@@ -675,13 +966,13 @@ const searchToolsCommand = new commander.Command("search-tools").description("Se
|
|
|
675
966
|
*/
|
|
676
967
|
const describeToolsCommand = new commander.Command("describe-tools").description("Describe specific MCP tools").argument("<toolNames...>", "Tool names to describe").option("-c, --config <path>", "Path to MCP server configuration file").option("-s, --server <name>", "Filter by server name").option("-j, --json", "Output as JSON", false).action(async (toolNames, options) => {
|
|
677
968
|
try {
|
|
678
|
-
const configFilePath = options.config ||
|
|
969
|
+
const configFilePath = options.config || require_src.findConfigFile();
|
|
679
970
|
if (!configFilePath) {
|
|
680
971
|
console.error("Error: No config file found. Use --config or create mcp-config.yaml");
|
|
681
972
|
process.exit(1);
|
|
682
973
|
}
|
|
683
|
-
const config = await new
|
|
684
|
-
const clientManager = new
|
|
974
|
+
const config = await new require_src.ConfigFetcherService({ configFilePath }).fetchConfiguration();
|
|
975
|
+
const clientManager = new require_src.McpClientManagerService();
|
|
685
976
|
const connectionPromises = Object.entries(config.mcpServers).map(async ([serverName, serverConfig]) => {
|
|
686
977
|
try {
|
|
687
978
|
await clientManager.connectToServer(serverName, serverConfig);
|
|
@@ -698,7 +989,7 @@ const describeToolsCommand = new commander.Command("describe-tools").description
|
|
|
698
989
|
}
|
|
699
990
|
const cwd = process.env.PROJECT_PATH || process.cwd();
|
|
700
991
|
const skillPaths = config.skills?.paths || [];
|
|
701
|
-
const skillService = skillPaths.length > 0 ? new
|
|
992
|
+
const skillService = skillPaths.length > 0 ? new require_src.SkillService(cwd, skillPaths) : void 0;
|
|
702
993
|
const foundTools = [];
|
|
703
994
|
const foundSkills = [];
|
|
704
995
|
const notFoundTools = [...toolNames];
|
|
@@ -833,7 +1124,7 @@ const describeToolsCommand = new commander.Command("describe-tools").description
|
|
|
833
1124
|
*/
|
|
834
1125
|
const useToolCommand = new commander.Command("use-tool").description("Execute an MCP tool with arguments").argument("<toolName>", "Tool name to execute").option("-c, --config <path>", "Path to MCP server configuration file").option("-s, --server <name>", "Server name (required if tool exists on multiple servers)").option("-a, --args <json>", "Tool arguments as JSON string", "{}").option("-j, --json", "Output as JSON", false).action(async (toolName, options) => {
|
|
835
1126
|
try {
|
|
836
|
-
const configFilePath = options.config ||
|
|
1127
|
+
const configFilePath = options.config || require_src.findConfigFile();
|
|
837
1128
|
if (!configFilePath) {
|
|
838
1129
|
console.error("Error: No config file found. Use --config or create mcp-config.yaml");
|
|
839
1130
|
process.exit(1);
|
|
@@ -845,8 +1136,8 @@ const useToolCommand = new commander.Command("use-tool").description("Execute an
|
|
|
845
1136
|
console.error("Error: Invalid JSON in --args");
|
|
846
1137
|
process.exit(1);
|
|
847
1138
|
}
|
|
848
|
-
const config = await new
|
|
849
|
-
const clientManager = new
|
|
1139
|
+
const config = await new require_src.ConfigFetcherService({ configFilePath }).fetchConfiguration();
|
|
1140
|
+
const clientManager = new require_src.McpClientManagerService();
|
|
850
1141
|
const connectionPromises = Object.entries(config.mcpServers).map(async ([serverName, serverConfig]) => {
|
|
851
1142
|
try {
|
|
852
1143
|
await clientManager.connectToServer(serverName, serverConfig);
|
|
@@ -916,7 +1207,7 @@ const useToolCommand = new commander.Command("use-tool").description("Execute an
|
|
|
916
1207
|
const cwd = process.env.PROJECT_PATH || process.cwd();
|
|
917
1208
|
const skillPaths = config.skills?.paths || [];
|
|
918
1209
|
if (skillPaths.length > 0) try {
|
|
919
|
-
const skillService = new
|
|
1210
|
+
const skillService = new require_src.SkillService(cwd, skillPaths);
|
|
920
1211
|
const skillName = toolName.startsWith("skill__") ? toolName.slice(7) : toolName;
|
|
921
1212
|
const skill = await skillService.getSkill(skillName);
|
|
922
1213
|
if (skill) {
|
|
@@ -1001,7 +1292,7 @@ const useToolCommand = new commander.Command("use-tool").description("Execute an
|
|
|
1001
1292
|
* - Hardcoded values (use options or environment variables)
|
|
1002
1293
|
* - Not exiting with appropriate exit codes on errors
|
|
1003
1294
|
*/
|
|
1004
|
-
function toErrorMessage$
|
|
1295
|
+
function toErrorMessage$4(error) {
|
|
1005
1296
|
return error instanceof Error ? error.message : String(error);
|
|
1006
1297
|
}
|
|
1007
1298
|
/**
|
|
@@ -1009,16 +1300,16 @@ function toErrorMessage$3(error) {
|
|
|
1009
1300
|
*/
|
|
1010
1301
|
const listResourcesCommand = new commander.Command("list-resources").description("List all available resources from connected MCP servers").option("-c, --config <path>", "Path to MCP server configuration file").option("-s, --server <name>", "Filter by server name").option("-j, --json", "Output as JSON", false).action(async (options) => {
|
|
1011
1302
|
try {
|
|
1012
|
-
const configFilePath = options.config ||
|
|
1303
|
+
const configFilePath = options.config || require_src.findConfigFile();
|
|
1013
1304
|
if (!configFilePath) throw new Error("No config file found. Use --config or create mcp-config.yaml");
|
|
1014
|
-
const config = await new
|
|
1015
|
-
const clientManager = new
|
|
1305
|
+
const config = await new require_src.ConfigFetcherService({ configFilePath }).fetchConfiguration();
|
|
1306
|
+
const clientManager = new require_src.McpClientManagerService();
|
|
1016
1307
|
await Promise.all(Object.entries(config.mcpServers).map(async ([serverName, serverConfig]) => {
|
|
1017
1308
|
try {
|
|
1018
1309
|
await clientManager.connectToServer(serverName, serverConfig);
|
|
1019
1310
|
if (!options.json) console.error(`✓ Connected to ${serverName}`);
|
|
1020
1311
|
} catch (error) {
|
|
1021
|
-
if (!options.json) console.error(`✗ Failed to connect to ${serverName}: ${toErrorMessage$
|
|
1312
|
+
if (!options.json) console.error(`✗ Failed to connect to ${serverName}: ${toErrorMessage$4(error)}`);
|
|
1022
1313
|
}
|
|
1023
1314
|
}));
|
|
1024
1315
|
const clients = options.server ? [clientManager.getClient(options.server)].filter((c) => c !== void 0) : clientManager.getAllClients();
|
|
@@ -1041,7 +1332,7 @@ const listResourcesCommand = new commander.Command("list-resources").description
|
|
|
1041
1332
|
}
|
|
1042
1333
|
}));
|
|
1043
1334
|
for (const { serverName, resources, error } of resourceResults) {
|
|
1044
|
-
if (error && !options.json) console.error(`Failed to list resources from ${serverName}: ${toErrorMessage$
|
|
1335
|
+
if (error && !options.json) console.error(`Failed to list resources from ${serverName}: ${toErrorMessage$4(error)}`);
|
|
1045
1336
|
resourcesByServer[serverName] = resources;
|
|
1046
1337
|
}
|
|
1047
1338
|
if (options.json) console.log(JSON.stringify(resourcesByServer, null, 2));
|
|
@@ -1055,7 +1346,7 @@ const listResourcesCommand = new commander.Command("list-resources").description
|
|
|
1055
1346
|
}
|
|
1056
1347
|
await clientManager.disconnectAll();
|
|
1057
1348
|
} catch (error) {
|
|
1058
|
-
console.error(`Error executing list-resources: ${toErrorMessage$
|
|
1349
|
+
console.error(`Error executing list-resources: ${toErrorMessage$4(error)}`);
|
|
1059
1350
|
process.exit(1);
|
|
1060
1351
|
}
|
|
1061
1352
|
});
|
|
@@ -1083,7 +1374,7 @@ const listResourcesCommand = new commander.Command("list-resources").description
|
|
|
1083
1374
|
* - Hardcoded values (use options or environment variables)
|
|
1084
1375
|
* - Not exiting with appropriate exit codes on errors
|
|
1085
1376
|
*/
|
|
1086
|
-
function toErrorMessage$
|
|
1377
|
+
function toErrorMessage$3(error) {
|
|
1087
1378
|
return error instanceof Error ? error.message : String(error);
|
|
1088
1379
|
}
|
|
1089
1380
|
/**
|
|
@@ -1091,16 +1382,16 @@ function toErrorMessage$2(error) {
|
|
|
1091
1382
|
*/
|
|
1092
1383
|
const readResourceCommand = new commander.Command("read-resource").description("Read a resource by URI from a connected MCP server").argument("<uri>", "Resource URI to read").option("-c, --config <path>", "Path to MCP server configuration file").option("-s, --server <name>", "Server name (required if resource exists on multiple servers)").option("-j, --json", "Output as JSON", false).action(async (uri, options) => {
|
|
1093
1384
|
try {
|
|
1094
|
-
const configFilePath = options.config ||
|
|
1385
|
+
const configFilePath = options.config || require_src.findConfigFile();
|
|
1095
1386
|
if (!configFilePath) throw new Error("No config file found. Use --config or create mcp-config.yaml");
|
|
1096
|
-
const config = await new
|
|
1097
|
-
const clientManager = new
|
|
1387
|
+
const config = await new require_src.ConfigFetcherService({ configFilePath }).fetchConfiguration();
|
|
1388
|
+
const clientManager = new require_src.McpClientManagerService();
|
|
1098
1389
|
await Promise.all(Object.entries(config.mcpServers).map(async ([serverName, serverConfig]) => {
|
|
1099
1390
|
try {
|
|
1100
1391
|
await clientManager.connectToServer(serverName, serverConfig);
|
|
1101
1392
|
if (!options.json) console.error(`✓ Connected to ${serverName}`);
|
|
1102
1393
|
} catch (error) {
|
|
1103
|
-
console.error(`✗ Failed to connect to ${serverName}: ${toErrorMessage$
|
|
1394
|
+
console.error(`✗ Failed to connect to ${serverName}: ${toErrorMessage$3(error)}`);
|
|
1104
1395
|
}
|
|
1105
1396
|
}));
|
|
1106
1397
|
const clients = clientManager.getAllClients();
|
|
@@ -1135,7 +1426,7 @@ const readResourceCommand = new commander.Command("read-resource").description("
|
|
|
1135
1426
|
const matchingServers = [];
|
|
1136
1427
|
for (const { serverName, hasResource, error } of searchResults) {
|
|
1137
1428
|
if (error) {
|
|
1138
|
-
console.error(`Failed to list resources from ${serverName}: ${toErrorMessage$
|
|
1429
|
+
console.error(`Failed to list resources from ${serverName}: ${toErrorMessage$3(error)}`);
|
|
1139
1430
|
continue;
|
|
1140
1431
|
}
|
|
1141
1432
|
if (hasResource) matchingServers.push(serverName);
|
|
@@ -1152,28 +1443,28 @@ const readResourceCommand = new commander.Command("read-resource").description("
|
|
|
1152
1443
|
else console.log(JSON.stringify(content, null, 2));
|
|
1153
1444
|
await clientManager.disconnectAll();
|
|
1154
1445
|
} catch (error) {
|
|
1155
|
-
console.error(`Error executing read-resource: ${toErrorMessage$
|
|
1446
|
+
console.error(`Error executing read-resource: ${toErrorMessage$3(error)}`);
|
|
1156
1447
|
process.exit(1);
|
|
1157
1448
|
}
|
|
1158
1449
|
});
|
|
1159
1450
|
|
|
1160
1451
|
//#endregion
|
|
1161
1452
|
//#region src/commands/list-prompts.ts
|
|
1162
|
-
function toErrorMessage$
|
|
1453
|
+
function toErrorMessage$2(error) {
|
|
1163
1454
|
return error instanceof Error ? error.message : String(error);
|
|
1164
1455
|
}
|
|
1165
1456
|
const listPromptsCommand = new commander.Command("list-prompts").description("List all available prompts from connected MCP servers").option("-c, --config <path>", "Path to MCP server configuration file").option("-s, --server <name>", "Filter by server name").option("-j, --json", "Output as JSON", false).action(async (options) => {
|
|
1166
1457
|
try {
|
|
1167
|
-
const configFilePath = options.config ||
|
|
1458
|
+
const configFilePath = options.config || require_src.findConfigFile();
|
|
1168
1459
|
if (!configFilePath) throw new Error("No config file found. Use --config or create mcp-config.yaml");
|
|
1169
|
-
const config = await new
|
|
1170
|
-
const clientManager = new
|
|
1460
|
+
const config = await new require_src.ConfigFetcherService({ configFilePath }).fetchConfiguration();
|
|
1461
|
+
const clientManager = new require_src.McpClientManagerService();
|
|
1171
1462
|
await Promise.all(Object.entries(config.mcpServers).map(async ([serverName, serverConfig]) => {
|
|
1172
1463
|
try {
|
|
1173
1464
|
await clientManager.connectToServer(serverName, serverConfig);
|
|
1174
1465
|
if (!options.json) console.error(`✓ Connected to ${serverName}`);
|
|
1175
1466
|
} catch (error) {
|
|
1176
|
-
if (!options.json) console.error(`✗ Failed to connect to ${serverName}: ${toErrorMessage$
|
|
1467
|
+
if (!options.json) console.error(`✗ Failed to connect to ${serverName}: ${toErrorMessage$2(error)}`);
|
|
1177
1468
|
}
|
|
1178
1469
|
}));
|
|
1179
1470
|
const clients = options.server ? [clientManager.getClient(options.server)].filter((client) => client !== void 0) : clientManager.getAllClients();
|
|
@@ -1184,7 +1475,7 @@ const listPromptsCommand = new commander.Command("list-prompts").description("Li
|
|
|
1184
1475
|
promptsByServer[client.serverName] = await client.listPrompts();
|
|
1185
1476
|
} catch (error) {
|
|
1186
1477
|
promptsByServer[client.serverName] = [];
|
|
1187
|
-
if (!options.json) console.error(`Failed to list prompts from ${client.serverName}: ${toErrorMessage$
|
|
1478
|
+
if (!options.json) console.error(`Failed to list prompts from ${client.serverName}: ${toErrorMessage$2(error)}`);
|
|
1188
1479
|
}
|
|
1189
1480
|
}));
|
|
1190
1481
|
if (options.json) console.log(JSON.stringify(promptsByServer, null, 2));
|
|
@@ -1204,19 +1495,19 @@ const listPromptsCommand = new commander.Command("list-prompts").description("Li
|
|
|
1204
1495
|
}
|
|
1205
1496
|
await clientManager.disconnectAll();
|
|
1206
1497
|
} catch (error) {
|
|
1207
|
-
console.error(`Error executing list-prompts: ${toErrorMessage$
|
|
1498
|
+
console.error(`Error executing list-prompts: ${toErrorMessage$2(error)}`);
|
|
1208
1499
|
process.exit(1);
|
|
1209
1500
|
}
|
|
1210
1501
|
});
|
|
1211
1502
|
|
|
1212
1503
|
//#endregion
|
|
1213
1504
|
//#region src/commands/get-prompt.ts
|
|
1214
|
-
function toErrorMessage(error) {
|
|
1505
|
+
function toErrorMessage$1(error) {
|
|
1215
1506
|
return error instanceof Error ? error.message : String(error);
|
|
1216
1507
|
}
|
|
1217
1508
|
const getPromptCommand = new commander.Command("get-prompt").description("Get a prompt by name from a connected MCP server").argument("<promptName>", "Prompt name to fetch").option("-c, --config <path>", "Path to MCP server configuration file").option("-s, --server <name>", "Server name (required if prompt exists on multiple servers)").option("-a, --args <json>", "Prompt arguments as JSON string", "{}").option("-j, --json", "Output as JSON", false).action(async (promptName, options) => {
|
|
1218
1509
|
try {
|
|
1219
|
-
const configFilePath = options.config ||
|
|
1510
|
+
const configFilePath = options.config || require_src.findConfigFile();
|
|
1220
1511
|
if (!configFilePath) throw new Error("No config file found. Use --config or create mcp-config.yaml");
|
|
1221
1512
|
let promptArgs = {};
|
|
1222
1513
|
try {
|
|
@@ -1224,14 +1515,14 @@ const getPromptCommand = new commander.Command("get-prompt").description("Get a
|
|
|
1224
1515
|
} catch {
|
|
1225
1516
|
throw new Error("Invalid JSON in --args");
|
|
1226
1517
|
}
|
|
1227
|
-
const config = await new
|
|
1228
|
-
const clientManager = new
|
|
1518
|
+
const config = await new require_src.ConfigFetcherService({ configFilePath }).fetchConfiguration();
|
|
1519
|
+
const clientManager = new require_src.McpClientManagerService();
|
|
1229
1520
|
await Promise.all(Object.entries(config.mcpServers).map(async ([serverName, serverConfig]) => {
|
|
1230
1521
|
try {
|
|
1231
1522
|
await clientManager.connectToServer(serverName, serverConfig);
|
|
1232
1523
|
if (!options.json) console.error(`✓ Connected to ${serverName}`);
|
|
1233
1524
|
} catch (error) {
|
|
1234
|
-
if (!options.json) console.error(`✗ Failed to connect to ${serverName}: ${toErrorMessage(error)}`);
|
|
1525
|
+
if (!options.json) console.error(`✗ Failed to connect to ${serverName}: ${toErrorMessage$1(error)}`);
|
|
1235
1526
|
}
|
|
1236
1527
|
}));
|
|
1237
1528
|
const clients = clientManager.getAllClients();
|
|
@@ -1254,7 +1545,7 @@ const getPromptCommand = new commander.Command("get-prompt").description("Get a
|
|
|
1254
1545
|
try {
|
|
1255
1546
|
if ((await client$1.listPrompts()).some((prompt$1) => prompt$1.name === promptName)) matchingServers.push(client$1.serverName);
|
|
1256
1547
|
} catch (error) {
|
|
1257
|
-
if (!options.json) console.error(`Failed to list prompts from ${client$1.serverName}: ${toErrorMessage(error)}`);
|
|
1548
|
+
if (!options.json) console.error(`Failed to list prompts from ${client$1.serverName}: ${toErrorMessage$1(error)}`);
|
|
1258
1549
|
}
|
|
1259
1550
|
}));
|
|
1260
1551
|
if (matchingServers.length === 0) throw new Error(`Prompt "${promptName}" not found on any connected server`);
|
|
@@ -1270,7 +1561,7 @@ const getPromptCommand = new commander.Command("get-prompt").description("Get a
|
|
|
1270
1561
|
}
|
|
1271
1562
|
await clientManager.disconnectAll();
|
|
1272
1563
|
} catch (error) {
|
|
1273
|
-
console.error(`Error executing get-prompt: ${toErrorMessage(error)}`);
|
|
1564
|
+
console.error(`Error executing get-prompt: ${toErrorMessage$1(error)}`);
|
|
1274
1565
|
process.exit(1);
|
|
1275
1566
|
}
|
|
1276
1567
|
});
|
|
@@ -1303,20 +1594,20 @@ const getPromptCommand = new commander.Command("get-prompt").description("Get a
|
|
|
1303
1594
|
*/
|
|
1304
1595
|
const prefetchCommand = new commander.Command("prefetch").description("Pre-download packages used by MCP servers (npx, pnpx, uvx, uv)").option("-c, --config <path>", "Path to MCP server configuration file").option("-p, --parallel", "Run prefetch commands in parallel", false).option("-d, --dry-run", "Show what would be prefetched without executing", false).option("-f, --filter <type>", "Filter by package manager type: npx, pnpx, uvx, or uv").option("--definitions-out <path>", "Write discovered definitions to a JSON or YAML cache file").option("--skip-packages", "Skip package prefetch and only build definitions cache", false).option("--clear-definitions-cache", "Delete the definitions cache file before continuing", false).action(async (options) => {
|
|
1305
1596
|
try {
|
|
1306
|
-
const configFilePath = options.config ||
|
|
1597
|
+
const configFilePath = options.config || require_src.findConfigFile();
|
|
1307
1598
|
if (!configFilePath) {
|
|
1308
1599
|
__agiflowai_aicode_utils.print.error("No MCP configuration file found.");
|
|
1309
1600
|
__agiflowai_aicode_utils.print.info("Use --config <path> to specify a config file, or run \"one-mcp init\" to create one.");
|
|
1310
1601
|
process.exit(1);
|
|
1311
1602
|
}
|
|
1312
1603
|
__agiflowai_aicode_utils.print.info(`Loading configuration from: ${configFilePath}`);
|
|
1313
|
-
const mcpConfig = await new
|
|
1604
|
+
const mcpConfig = await new require_src.ConfigFetcherService({
|
|
1314
1605
|
configFilePath,
|
|
1315
1606
|
useCache: false
|
|
1316
1607
|
}).fetchConfiguration(true);
|
|
1317
|
-
const serverId = mcpConfig.id ||
|
|
1318
|
-
const configHash =
|
|
1319
|
-
const effectiveDefinitionsPath = options.definitionsOut ||
|
|
1608
|
+
const serverId = mcpConfig.id || require_src.generateServerId();
|
|
1609
|
+
const configHash = require_src.DefinitionsCacheService.generateConfigHash(mcpConfig);
|
|
1610
|
+
const effectiveDefinitionsPath = options.definitionsOut || require_src.DefinitionsCacheService.getDefaultCachePath(configFilePath);
|
|
1320
1611
|
const prefetchService = new PrefetchService({
|
|
1321
1612
|
mcpConfig,
|
|
1322
1613
|
filter: options.filter,
|
|
@@ -1327,7 +1618,7 @@ const prefetchCommand = new commander.Command("prefetch").description("Pre-downl
|
|
|
1327
1618
|
const shouldWriteDefinitions = Boolean(options.definitionsOut);
|
|
1328
1619
|
if (options.clearDefinitionsCache) if (options.dryRun) __agiflowai_aicode_utils.print.info(`Would clear definitions cache: ${effectiveDefinitionsPath}`);
|
|
1329
1620
|
else {
|
|
1330
|
-
await
|
|
1621
|
+
await require_src.DefinitionsCacheService.clearFile(effectiveDefinitionsPath);
|
|
1331
1622
|
__agiflowai_aicode_utils.print.success(`Cleared definitions cache: ${effectiveDefinitionsPath}`);
|
|
1332
1623
|
}
|
|
1333
1624
|
if (shouldPrefetchPackages) if (packages.length === 0) {
|
|
@@ -1371,9 +1662,9 @@ const prefetchCommand = new commander.Command("prefetch").description("Pre-downl
|
|
|
1371
1662
|
if (shouldWriteDefinitions) {
|
|
1372
1663
|
__agiflowai_aicode_utils.print.newline();
|
|
1373
1664
|
__agiflowai_aicode_utils.print.info("Collecting definitions cache...");
|
|
1374
|
-
const clientManager = new
|
|
1665
|
+
const clientManager = new require_src.McpClientManagerService();
|
|
1375
1666
|
const skillPaths = mcpConfig.skills?.paths || [];
|
|
1376
|
-
const definitionsCacheService = new
|
|
1667
|
+
const definitionsCacheService = new require_src.DefinitionsCacheService(clientManager, skillPaths.length > 0 ? new require_src.SkillService(process.cwd(), skillPaths) : void 0);
|
|
1377
1668
|
await Promise.all(Object.entries(mcpConfig.mcpServers).map(async ([serverName, serverConfig]) => {
|
|
1378
1669
|
try {
|
|
1379
1670
|
await clientManager.connectToServer(serverName, serverConfig);
|
|
@@ -1385,10 +1676,10 @@ const prefetchCommand = new commander.Command("prefetch").description("Pre-downl
|
|
|
1385
1676
|
const definitionsCache = await definitionsCacheService.collectForCache({
|
|
1386
1677
|
configPath: configFilePath,
|
|
1387
1678
|
configHash,
|
|
1388
|
-
oneMcpVersion:
|
|
1679
|
+
oneMcpVersion: require_src.version,
|
|
1389
1680
|
serverId
|
|
1390
1681
|
});
|
|
1391
|
-
await
|
|
1682
|
+
await require_src.DefinitionsCacheService.writeToFile(effectiveDefinitionsPath, definitionsCache);
|
|
1392
1683
|
__agiflowai_aicode_utils.print.success(`Definitions cache written: ${effectiveDefinitionsPath} (${Object.keys(definitionsCache.servers).length} servers, ${definitionsCache.skills.length} skills)`);
|
|
1393
1684
|
if (definitionsCache.failures.length > 0) __agiflowai_aicode_utils.print.warning(`Definitions cache completed with ${definitionsCache.failures.length} server failure(s)`);
|
|
1394
1685
|
await clientManager.disconnectAll();
|
|
@@ -1400,6 +1691,52 @@ const prefetchCommand = new commander.Command("prefetch").description("Pre-downl
|
|
|
1400
1691
|
}
|
|
1401
1692
|
});
|
|
1402
1693
|
|
|
1694
|
+
//#endregion
|
|
1695
|
+
//#region src/commands/stop.ts
|
|
1696
|
+
/**
|
|
1697
|
+
* Stop Command
|
|
1698
|
+
*
|
|
1699
|
+
* Stops a running HTTP one-mcp server using the authenticated admin endpoint
|
|
1700
|
+
* and the persisted runtime registry.
|
|
1701
|
+
*/
|
|
1702
|
+
function toErrorMessage(error) {
|
|
1703
|
+
return error instanceof Error ? error.message : String(error);
|
|
1704
|
+
}
|
|
1705
|
+
function printStopResult(result) {
|
|
1706
|
+
console.log(`Stopped one-mcp server '${result.serverId}'.`);
|
|
1707
|
+
console.log(`Endpoint: http://${result.host}:${result.port}`);
|
|
1708
|
+
console.log(`Result: ${result.message}`);
|
|
1709
|
+
}
|
|
1710
|
+
/**
|
|
1711
|
+
* Stop a running HTTP one-mcp server.
|
|
1712
|
+
*/
|
|
1713
|
+
const stopCommand = new commander.Command("stop").description("Stop a running HTTP one-mcp server").option("--id <id>", "Target server ID from the runtime registry").option("--host <host>", "Target runtime host").option("--port <port>", "Target runtime port", (value) => Number.parseInt(value, 10)).option("-c, --config <path>", "Reserved for future config-based targeting support").option("--token <token>", "Override the persisted shutdown token").option("--force", "Skip server ID verification against the /health response", false).option("-j, --json", "Output as JSON", false).option("--timeout <ms>", "Maximum time to wait for shutdown completion", (value) => Number.parseInt(value, 10), 5e3).action(async (options) => {
|
|
1714
|
+
try {
|
|
1715
|
+
if (options.config) console.error("Warning: --config is not used yet; runtime resolution uses the persisted registry.");
|
|
1716
|
+
const result = await new require_src.StopServerService().stop({
|
|
1717
|
+
serverId: options.id,
|
|
1718
|
+
host: options.host,
|
|
1719
|
+
port: options.port,
|
|
1720
|
+
token: options.token,
|
|
1721
|
+
force: options.force,
|
|
1722
|
+
timeoutMs: options.timeout
|
|
1723
|
+
});
|
|
1724
|
+
if (options.json) {
|
|
1725
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1726
|
+
return;
|
|
1727
|
+
}
|
|
1728
|
+
printStopResult(result);
|
|
1729
|
+
} catch (error) {
|
|
1730
|
+
const errorMessage = `Error executing stop: ${toErrorMessage(error)}`;
|
|
1731
|
+
if (options.json) console.log(JSON.stringify({
|
|
1732
|
+
ok: false,
|
|
1733
|
+
error: errorMessage
|
|
1734
|
+
}, null, 2));
|
|
1735
|
+
else console.error(errorMessage);
|
|
1736
|
+
process.exit(1);
|
|
1737
|
+
}
|
|
1738
|
+
});
|
|
1739
|
+
|
|
1403
1740
|
//#endregion
|
|
1404
1741
|
//#region src/cli.ts
|
|
1405
1742
|
/**
|
|
@@ -1426,7 +1763,7 @@ const prefetchCommand = new commander.Command("prefetch").description("Pre-downl
|
|
|
1426
1763
|
async function main() {
|
|
1427
1764
|
try {
|
|
1428
1765
|
const program = new commander.Command();
|
|
1429
|
-
program.name("one-mcp").description("One MCP server package").version(
|
|
1766
|
+
program.name("one-mcp").description("One MCP server package").version(require_src.version);
|
|
1430
1767
|
program.addCommand(initCommand);
|
|
1431
1768
|
program.addCommand(mcpServeCommand);
|
|
1432
1769
|
program.addCommand(searchToolsCommand);
|
|
@@ -1437,6 +1774,7 @@ async function main() {
|
|
|
1437
1774
|
program.addCommand(listPromptsCommand);
|
|
1438
1775
|
program.addCommand(getPromptCommand);
|
|
1439
1776
|
program.addCommand(prefetchCommand);
|
|
1777
|
+
program.addCommand(stopCommand);
|
|
1440
1778
|
await program.parseAsync(process.argv);
|
|
1441
1779
|
} catch (error) {
|
|
1442
1780
|
console.error(`CLI execution failed: ${error instanceof Error ? error.message : error}`);
|