UNPKG

blade-ai

Version:

🗡️ Blade - 智能 AI 助手命令行工具

1,314 lines (1,304 loc) 36.1 kB
#!/usr/bin/env node import { __require } from "./chunk-7N7GSU6K.js"; // src/mcp/client/MCPClient.ts import { EventEmitter } from "events"; import WebSocket from "ws"; var MCPClient = class extends EventEmitter { constructor() { super(); this.sessions = /* @__PURE__ */ new Map(); this.connections = /* @__PURE__ */ new Map(); this.messageId = 0; this.clientInfo = { name: "blade-ai", version: "1.2.5", capabilities: { sampling: {} } }; } /** * 连接到 MCP 服务器 */ async connect(config) { const sessionId = `${config.name}-${Date.now()}`; try { let connection; switch (config.transport) { case "ws": connection = await this.connectWebSocket(config); break; case "stdio": connection = await this.connectStdio(config); break; case "sse": throw new Error("SSE transport not implemented yet"); default: throw new Error(`Unsupported transport: ${config.transport}`); } const session = { id: sessionId, config, connected: true, lastActivity: /* @__PURE__ */ new Date() }; this.sessions.set(sessionId, session); this.connections.set(sessionId, connection); await this.performHandshake(sessionId); this.emit("connected", session); return session; } catch (error) { this.emit("error", error); throw error; } } /** * 断开连接 */ async disconnect(sessionId) { const session = this.sessions.get(sessionId); const connection = this.connections.get(sessionId); if (session) { session.connected = false; } if (connection) { if (connection instanceof WebSocket) { connection.close(); } else if (connection.kill) { connection.kill(); } this.connections.delete(sessionId); } this.sessions.delete(sessionId); this.emit("disconnected", sessionId); } /** * 获取所有会话 */ getSessions() { return Array.from(this.sessions.values()); } /** * 获取指定会话 */ getSession(sessionId) { return this.sessions.get(sessionId); } /** * 列出资源 */ async listResources(sessionId) { const response = await this.sendRequest(sessionId, { method: "resources/list", params: {} }); return response.resources || []; } /** * 读取资源内容 */ async readResource(sessionId, uri) { const response = await this.sendRequest(sessionId, { method: "resources/read", params: { uri } }); return response.contents[0]; } /** * 列出工具 */ async listTools(sessionId) { const response = await this.sendRequest(sessionId, { method: "tools/list", params: {} }); return response.tools || []; } /** * 调用工具 */ async callTool(sessionId, toolCall) { const response = await this.sendRequest(sessionId, { method: "tools/call", params: { name: toolCall.name, arguments: toolCall.arguments } }); return response; } /** * 发送请求到 MCP 服务器 */ async sendRequest(sessionId, request) { const connection = this.connections.get(sessionId); const session = this.sessions.get(sessionId); if (!connection || !session?.connected) { throw new Error(`Session ${sessionId} is not connected`); } const message = { jsonrpc: "2.0", id: ++this.messageId, ...request }; return new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error("Request timeout")); }, session.config.timeout || 3e4); const handleMessage = (data) => { try { const response = typeof data === "string" ? JSON.parse(data) : data; if (response.id === message.id) { clearTimeout(timeout); connection.off?.("message", handleMessage); if (response.error) { reject(new Error(response.error.message)); } else { resolve(response.result); } } } catch (error) { reject(error); } }; if (connection instanceof WebSocket) { connection.on("message", handleMessage); connection.send(JSON.stringify(message)); } else { const responseHandler = (data) => { try { const response = JSON.parse(data.toString().trim()); if (response.id === message.id) { clearTimeout(timeout); connection.stdout?.off("data", responseHandler); if (response.error) { reject(new Error(response.error.message)); } else { resolve(response.result); } } } catch (error) { } }; connection.stdout?.on("data", responseHandler); connection.stdin?.write(JSON.stringify(message) + "\n"); } }); } /** * WebSocket 连接 */ async connectWebSocket(config) { if (!config.endpoint) { throw new Error("WebSocket endpoint is required"); } const ws = new WebSocket(config.endpoint); return new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error("Connection timeout")); }, config.timeout || 1e4); ws.on("open", () => { clearTimeout(timeout); resolve(ws); }); ws.on("error", (error) => { clearTimeout(timeout); reject(error); }); ws.on("message", (data) => { this.handleMessage(data.toString()); }); }); } /** * Stdio 连接 */ async connectStdio(config) { const { spawn } = await import("child_process"); if (!config.command) { throw new Error("Command is required for stdio transport"); } const childProcess = spawn(config.command, config.args || [], { stdio: ["pipe", "pipe", "pipe"], env: { ...process.env, ...config.env } }); return new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error("Process start timeout")); }, config.timeout || 1e4); childProcess.on("spawn", () => { clearTimeout(timeout); resolve(childProcess); }); childProcess.on("error", (error) => { clearTimeout(timeout); reject(error); }); childProcess.stdout?.on("data", (data) => { this.handleMessage(data.toString()); }); }); } /** * 处理消息 */ handleMessage(data) { try { const message = JSON.parse(data); this.emit("message", message); } catch (error) { this.emit("error", new Error(`Invalid JSON message: ${data}`)); } } /** * 执行握手 */ async performHandshake(sessionId) { try { const response = await this.sendRequest(sessionId, { method: "initialize", params: { protocolVersion: "2024-11-05", capabilities: this.clientInfo.capabilities, clientInfo: { name: this.clientInfo.name, version: this.clientInfo.version } } }); const session = this.sessions.get(sessionId); if (session) { session.serverInfo = { name: response.serverInfo?.name || "Unknown", version: response.serverInfo?.version || "0.0.0", capabilities: response.capabilities || {} }; } const connection = this.connections.get(sessionId); if (connection instanceof WebSocket) { connection.send( JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }) ); } else { connection.stdin?.write( JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }) + "\n" ); } } catch (error) { throw new Error(`Handshake failed: ${error}`); } } }; // src/mcp/server/MCPServer.ts import { EventEmitter as EventEmitter3 } from "events"; import { createServer } from "http"; import { WebSocketServer } from "ws"; // src/tools/ToolManager.ts import { randomUUID } from "crypto"; import { EventEmitter as EventEmitter2 } from "events"; // src/tools/types.ts var ToolValidationError = class extends Error { constructor(message, field, value) { super(message); this.field = field; this.value = value; this.name = "ToolValidationError"; } }; var ToolExecutionError = class extends Error { constructor(message, toolName, originalError) { super(message); this.toolName = toolName; this.originalError = originalError; this.name = "ToolExecutionError"; } }; var ToolRegistrationError = class extends Error { constructor(message, toolName) { super(message); this.toolName = toolName; this.name = "ToolRegistrationError"; } }; // src/tools/validator.ts var ToolValidator = class { /** * 验证工具参数 */ static validateParameters(parameters, schema, required = []) { for (const requiredParam of required) { if (!(requiredParam in parameters) || parameters[requiredParam] === void 0) { throw new ToolValidationError(`\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570: ${requiredParam}`, requiredParam); } } for (const [key, value] of Object.entries(parameters)) { const paramSchema = schema[key]; if (!paramSchema) { throw new ToolValidationError(`\u672A\u77E5\u53C2\u6570: ${key}`, key, value); } this.validateValue(value, paramSchema, key); } } /** * 验证单个值 */ static validateValue(value, schema, fieldPath) { if (!this.isValidType(value, schema.type)) { throw new ToolValidationError( `\u53C2\u6570 ${fieldPath} \u7C7B\u578B\u9519\u8BEF\uFF0C\u671F\u671B: ${schema.type}\uFF0C\u5B9E\u9645: ${typeof value}`, fieldPath, value ); } if (schema.enum && !schema.enum.includes(value)) { throw new ToolValidationError( `\u53C2\u6570 ${fieldPath} \u503C\u5FC5\u987B\u662F: ${schema.enum.join(", ")} \u4E2D\u7684\u4E00\u4E2A`, fieldPath, value ); } if (schema.type === "array" && Array.isArray(value) && schema.items) { value.forEach((item, index) => { this.validateValue(item, schema.items, `${fieldPath}[${index}]`); }); } if (schema.type === "object" && value && typeof value === "object" && schema.properties) { for (const [key, subValue] of Object.entries(value)) { const subSchema = schema.properties[key]; if (subSchema) { this.validateValue(subValue, subSchema, `${fieldPath}.${key}`); } } } } /** * 检查值类型是否匹配 */ static isValidType(value, expectedType) { if (value === null || value === void 0) { return true; } switch (expectedType) { case "string": return typeof value === "string"; case "number": return typeof value === "number" && !isNaN(value); case "boolean": return typeof value === "boolean"; case "array": return Array.isArray(value); case "object": return typeof value === "object" && !Array.isArray(value); default: return false; } } /** * 应用默认值 */ static applyDefaults(parameters, schema) { const result = { ...parameters }; for (const [key, paramSchema] of Object.entries(schema)) { if (!(key in result) && paramSchema.default !== void 0) { result[key] = paramSchema.default; } } return result; } /** * 清理参数(移除未定义的参数) */ static sanitizeParameters(parameters, schema) { const result = {}; for (const [key, value] of Object.entries(parameters)) { if (key in schema && value !== void 0) { result[key] = value; } } return result; } /** * 生成参数文档 */ static generateDocumentation(schema, required = []) { const docs = []; docs.push("\u53C2\u6570\u8BF4\u660E:"); for (const [key, paramSchema] of Object.entries(schema)) { const isRequired = required.includes(key); const requiredMark = isRequired ? " *" : ""; const defaultValue = paramSchema.default !== void 0 ? ` (\u9ED8\u8BA4: ${paramSchema.default})` : ""; const enumValues = paramSchema.enum ? ` (\u53EF\u9009\u503C: ${paramSchema.enum.join(", ")})` : ""; docs.push(` ${key}${requiredMark}: ${paramSchema.type}${defaultValue}${enumValues}`); if (paramSchema.description) { docs.push(` ${paramSchema.description}`); } } if (required.length > 0) { docs.push(""); docs.push("* \u8868\u793A\u5FC5\u9700\u53C2\u6570"); } return docs.join("\n"); } }; // src/tools/ToolManager.ts var ToolManager = class extends EventEmitter2 { constructor(config = {}) { super(); this.tools = /* @__PURE__ */ new Map(); this.toolStates = /* @__PURE__ */ new Map(); this.executionHistory = []; this.runningExecutions = /* @__PURE__ */ new Map(); this.config = { debug: false, maxConcurrency: 10, executionTimeout: 3e4, // 30秒 logHistory: true, maxHistorySize: 1e3, ...config }; this.log("\u5DE5\u5177\u7BA1\u7406\u5668\u5DF2\u521D\u59CB\u5316", { config: this.config }); } /** * 注册工具 */ async registerTool(tool, options = {}) { try { this.validateToolDefinition(tool); if (this.tools.has(tool.name) && !options.override) { throw new ToolRegistrationError( `\u5DE5\u5177 "${tool.name}" \u5DF2\u5B58\u5728\uFF0C\u4F7F\u7528 override: true \u5F3A\u5236\u8986\u76D6`, tool.name ); } this.tools.set(tool.name, tool); this.toolStates.set(tool.name, { enabled: options.enabled ?? true, permissions: options.permissions ?? [] }); this.log(`\u5DE5\u5177 "${tool.name}" \u6CE8\u518C\u6210\u529F`, { version: tool.version, category: tool.category, enabled: options.enabled ?? true }); this.emit("toolRegistered", { toolName: tool.name, tool, options }); } catch (error) { this.log(`\u5DE5\u5177 "${tool.name}" \u6CE8\u518C\u5931\u8D25`, { error: error.message }); throw error; } } /** * 注销工具 */ unregisterTool(toolName) { const existed = this.tools.has(toolName); if (existed) { this.tools.delete(toolName); this.toolStates.delete(toolName); this.log(`\u5DE5\u5177 "${toolName}" \u5DF2\u6CE8\u9500`); this.emit("toolUnregistered", { toolName }); } return existed; } /** * 获取所有已注册的工具 */ getTools() { return Array.from(this.tools.values()); } /** * 获取特定工具 */ getTool(toolName) { return this.tools.get(toolName); } /** * 检查工具是否存在 */ hasTool(toolName) { return this.tools.has(toolName); } /** * 启用/禁用工具 */ setToolEnabled(toolName, enabled) { const state = this.toolStates.get(toolName); if (!state) { throw new ToolRegistrationError(`\u5DE5\u5177 "${toolName}" \u4E0D\u5B58\u5728`, toolName); } state.enabled = enabled; this.log(`\u5DE5\u5177 "${toolName}" ${enabled ? "\u5DF2\u542F\u7528" : "\u5DF2\u7981\u7528"}`); this.emit("toolStateChanged", { toolName, enabled }); } /** * 检查工具是否启用 */ isToolEnabled(toolName) { const state = this.toolStates.get(toolName); return state?.enabled ?? false; } /** * 调用工具 */ async callTool(request) { const requestId = randomUUID(); const startTime = Date.now(); try { if (this.runningExecutions.size >= this.config.maxConcurrency) { throw new ToolExecutionError("\u8FBE\u5230\u6700\u5927\u5E76\u53D1\u6267\u884C\u9650\u5236", request.toolName); } const tool = this.tools.get(request.toolName); if (!tool) { throw new ToolExecutionError(`\u5DE5\u5177 "${request.toolName}" \u4E0D\u5B58\u5728`, request.toolName); } if (!this.isToolEnabled(request.toolName)) { throw new ToolExecutionError(`\u5DE5\u5177 "${request.toolName}" \u5DF2\u7981\u7528`, request.toolName); } const context = { executionId: requestId, timestamp: startTime, ...request.context }; let processedParams = ToolValidator.applyDefaults(request.parameters, tool.parameters); processedParams = ToolValidator.sanitizeParameters(processedParams, tool.parameters); ToolValidator.validateParameters(processedParams, tool.parameters, tool.required); this.log(`\u5F00\u59CB\u6267\u884C\u5DE5\u5177 "${request.toolName}"`, { requestId, parameters: processedParams }); this.emit("toolCallStarted", { requestId, toolName: request.toolName, parameters: processedParams, context }); const executionPromise = this.executeToolWithTimeout(tool, processedParams); this.runningExecutions.set(requestId, executionPromise); const result = await executionPromise; result.duration = Date.now() - startTime; this.log(`\u5DE5\u5177 "${request.toolName}" \u6267\u884C\u5B8C\u6210`, { requestId, duration: result.duration, success: result.success }); const response = { requestId, toolName: request.toolName, result, context }; if (this.config.logHistory) { this.addToHistory({ executionId: requestId, toolName: request.toolName, parameters: processedParams, result, context, createdAt: /* @__PURE__ */ new Date() }); } this.emit("toolCallCompleted", response); return response; } catch (error) { const result = { success: false, error: error.message, duration: Date.now() - startTime }; const response = { requestId, toolName: request.toolName, result, context: { executionId: requestId, timestamp: startTime, ...request.context } }; this.log(`\u5DE5\u5177 "${request.toolName}" \u6267\u884C\u5931\u8D25`, { requestId, error: error.message }); this.emit("toolCallFailed", { ...response, error }); return response; } finally { this.runningExecutions.delete(requestId); } } /** * 获取执行历史 */ getExecutionHistory(limit) { const history = [...this.executionHistory]; return limit ? history.slice(-limit) : history; } /** * 清空执行历史 */ clearHistory() { this.executionHistory = []; this.log("\u6267\u884C\u5386\u53F2\u5DF2\u6E05\u7A7A"); } /** * 获取工具统计信息 */ getStats() { const stats = { totalTools: this.tools.size, enabledTools: 0, runningExecutions: this.runningExecutions.size, totalExecutions: this.executionHistory.length, successfulExecutions: 0, failedExecutions: 0 }; for (const state of this.toolStates.values()) { if (state.enabled) { stats.enabledTools++; } } for (const history of this.executionHistory) { if (history.result.success) { stats.successfulExecutions++; } else { stats.failedExecutions++; } } return stats; } /** * 验证工具定义 */ validateToolDefinition(tool) { if (!tool.name || typeof tool.name !== "string") { throw new ToolRegistrationError("\u5DE5\u5177\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32"); } if (!tool.description || typeof tool.description !== "string") { throw new ToolRegistrationError("\u5DE5\u5177\u63CF\u8FF0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32"); } if (!tool.parameters || typeof tool.parameters !== "object") { throw new ToolRegistrationError("\u5DE5\u5177\u53C2\u6570\u5B9A\u4E49\u5FC5\u987B\u662F\u5BF9\u8C61"); } if (typeof tool.execute !== "function") { throw new ToolRegistrationError("\u5DE5\u5177\u6267\u884C\u51FD\u6570\u5FC5\u987B\u662F\u51FD\u6570"); } } /** * 执行工具并设置超时 */ async executeToolWithTimeout(tool, parameters) { const startTime = Date.now(); return new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { reject( new ToolExecutionError(`\u5DE5\u5177\u6267\u884C\u8D85\u65F6 (${this.config.executionTimeout}ms)`, tool.name) ); }, this.config.executionTimeout); Promise.resolve(tool.execute(parameters)).then((result) => { clearTimeout(timeoutId); const duration = Date.now() - startTime; if (result && typeof result === "object" && "success" in result) { resolve({ ...result, duration: result.duration || duration }); } else { resolve({ success: true, data: result, duration }); } }).catch((error) => { clearTimeout(timeoutId); reject(new ToolExecutionError(`\u5DE5\u5177\u6267\u884C\u9519\u8BEF: ${error.message}`, tool.name, error)); }); }); } /** * 添加到历史记录 */ addToHistory(history) { this.executionHistory.push(history); if (this.executionHistory.length > this.config.maxHistorySize) { this.executionHistory = this.executionHistory.slice(-this.config.maxHistorySize); } } /** * 日志记录 */ log(message, data) { if (this.config.debug) { console.log(`[ToolManager] ${message}`, data || ""); } } }; // src/mcp/server/MCPServer.ts var MCPServer = class extends EventEmitter3 { constructor(config, toolManager) { super(); this.config = config; this.clients = /* @__PURE__ */ new Map(); this.messageId = 0; this.serverInfo = { name: "blade-ai-server", version: "1.2.5", capabilities: { resources: { subscribe: true, listChanged: true }, tools: { listChanged: false }, prompts: { listChanged: false } } }; this.toolManager = toolManager || new ToolManager(); } /** * 启动服务器 */ async start() { const port = this.config.port || 3001; const host = this.config.host || "localhost"; if (this.config.transport === "ws") { this.server = createServer(); this.wsServer = new WebSocketServer({ server: this.server }); this.wsServer.on("connection", (ws, request) => { this.handleConnection(ws, request); }); this.server.listen(port, host, () => { console.log(`MCP Server listening on ws://${host}:${port}`); this.emit("started", { host, port }); }); } else if (this.config.transport === "stdio") { this.handleStdioConnection(); } } /** * 停止服务器 */ async stop() { if (this.wsServer) { this.wsServer.close(); } if (this.server) { this.server.close(); } this.clients.clear(); this.emit("stopped"); } /** * 处理 WebSocket 连接 */ handleConnection(ws, request) { const clientId = `client-${Date.now()}-${Math.random()}`; this.clients.set(clientId, ws); console.log(`Client connected: ${clientId}`); ws.on("message", async (data) => { try { const message = JSON.parse(data.toString()); await this.handleMessage(clientId, message); } catch (error) { this.sendError(clientId, -32700, "Parse error", error); } }); ws.on("close", () => { this.clients.delete(clientId); console.log(`Client disconnected: ${clientId}`); }); ws.on("error", (error) => { console.error(`WebSocket error for ${clientId}:`, error); this.clients.delete(clientId); }); } /** * 处理 Stdio 连接 */ handleStdioConnection() { process.stdin.on("data", async (data) => { try { const lines = data.toString().trim().split("\n"); for (const line of lines) { if (line.trim()) { const message = JSON.parse(line); await this.handleMessage("stdio", message); } } } catch (error) { this.sendErrorToStdio(-32700, "Parse error", error); } }); console.log("MCP Server listening on stdio"); this.emit("started", { transport: "stdio" }); } /** * 处理消息 */ async handleMessage(clientId, message) { try { switch (message.method) { case "initialize": await this.handleInitialize(clientId, message); break; case "notifications/initialized": break; case "resources/list": await this.handleListResources(clientId, message); break; case "resources/read": await this.handleReadResource(clientId, message); break; case "tools/list": await this.handleListTools(clientId, message); break; case "tools/call": await this.handleCallTool(clientId, message); break; case "prompts/list": await this.handleListPrompts(clientId, message); break; case "prompts/get": await this.handleGetPrompt(clientId, message); break; default: this.sendError(clientId, -32601, `Unknown method: ${message.method}`); } } catch (error) { this.sendError(clientId, -32603, "Internal error", error); } } /** * 处理初始化请求 */ async handleInitialize(clientId, message) { const clientInfo = message.params?.clientInfo; this.sendResponse(clientId, message.id, { protocolVersion: "2024-11-05", capabilities: this.serverInfo.capabilities, serverInfo: { name: this.serverInfo.name, version: this.serverInfo.version } }); console.log( `Client initialized: ${clientInfo?.name || "Unknown"} v${clientInfo?.version || "0.0.0"}` ); } /** * 处理列出资源 */ async handleListResources(clientId, message) { const resources = [ { uri: "file://workspace", name: "Current Workspace", description: "Files and directories in the current workspace", mimeType: "application/json" }, { uri: "git://status", name: "Git Status", description: "Current git repository status", mimeType: "application/json" }, { uri: "git://log", name: "Git Log", description: "Recent git commits", mimeType: "application/json" } ]; this.sendResponse(clientId, message.id, { resources }); } /** * 处理读取资源 */ async handleReadResource(clientId, message) { const uri = message.params?.uri; try { let content; if (uri === "file://workspace") { const { readdir } = await import("fs/promises"); const files = await readdir(process.cwd()); content = { uri, mimeType: "application/json", text: JSON.stringify(files, null, 2) }; } else if (uri === "git://status") { const { execSync } = await import("child_process"); const status = execSync("git status --porcelain", { encoding: "utf-8" }); content = { uri, mimeType: "text/plain", text: status }; } else if (uri === "git://log") { const { execSync } = await import("child_process"); const log = execSync("git log --oneline -10", { encoding: "utf-8" }); content = { uri, mimeType: "text/plain", text: log }; } else { throw new Error(`Unknown resource URI: ${uri}`); } this.sendResponse(clientId, message.id, { contents: [content] }); } catch (error) { this.sendError(clientId, -32603, `Failed to read resource: ${uri}`, error); } } /** * 处理列出工具 */ async handleListTools(clientId, message) { const bladeTools = this.toolManager.getTools(); const tools = bladeTools.map((tool) => ({ name: tool.name, description: tool.description, inputSchema: { type: "object", properties: tool.parameters || {}, required: tool.required || [] } })); this.sendResponse(clientId, message.id, { tools }); } /** * 处理工具调用 */ async handleCallTool(clientId, message) { const toolName = message.params?.name; const toolArgs = message.params?.arguments; try { const response = await this.toolManager.callTool({ toolName, parameters: toolArgs, context: { executionId: `mcp-${Date.now()}`, timestamp: Date.now() } }); const mcpResponse = { content: [ { type: "text", text: typeof response.result.data === "string" ? response.result.data : JSON.stringify(response.result.data, null, 2) } ], isError: !response.result.success }; this.sendResponse(clientId, message.id, mcpResponse); } catch (error) { const response = { content: [ { type: "text", text: error instanceof Error ? error.message : String(error) } ], isError: true }; this.sendResponse(clientId, message.id, response); } } /** * 处理列出提示 */ async handleListPrompts(clientId, message) { const prompts = [ { name: "code_review", description: "Review code for quality, security, and best practices", arguments: [ { name: "file_path", description: "Path to the file to review", required: true } ] }, { name: "generate_docs", description: "Generate documentation for code", arguments: [ { name: "file_path", description: "Path to the file to document", required: true } ] } ]; this.sendResponse(clientId, message.id, { prompts }); } /** * 处理获取提示 */ async handleGetPrompt(clientId, message) { const promptName = message.params?.name; const args = message.params?.arguments; this.sendResponse(clientId, message.id, { description: `Generated prompt for ${promptName}`, messages: [ { role: "user", content: { type: "text", text: `Please ${promptName} for the following parameters: ${JSON.stringify(args)}` } } ] }); } /** * 发送响应 */ sendResponse(clientId, messageId, result) { const response = { jsonrpc: "2.0", id: messageId, result }; if (clientId === "stdio") { process.stdout.write(JSON.stringify(response) + "\n"); } else { const client = this.clients.get(clientId); if (client) { client.send(JSON.stringify(response)); } } } /** * 发送错误 */ sendError(clientId, code, message, data) { const response = { jsonrpc: "2.0", error: { code, message, data } }; if (clientId === "stdio") { process.stdout.write(JSON.stringify(response) + "\n"); } else { const client = this.clients.get(clientId); if (client) { client.send(JSON.stringify(response)); } } } /** * 发送错误到 Stdio */ sendErrorToStdio(code, message, data) { this.sendError("stdio", code, message, data); } }; // src/mcp/config/MCPConfig.ts import { existsSync, readFileSync, writeFileSync } from "fs"; import { homedir } from "os"; import { join } from "path"; var MCPConfig = class { constructor(configPath) { this.configPath = configPath || join(homedir(), ".blade", "mcp-config.json"); this.loadConfig(); } /** * 加载配置 */ loadConfig() { if (existsSync(this.configPath)) { try { const content = readFileSync(this.configPath, "utf-8"); this.config = JSON.parse(content); } catch (error) { console.warn("Failed to load MCP config, using defaults"); this.config = this.getDefaultConfig(); } } else { this.config = this.getDefaultConfig(); } this.config = { ...this.getDefaultConfig(), ...this.config, servers: { ...this.getDefaultConfig().servers, ...this.config.servers } }; } /** * 保存配置 */ saveConfig() { try { const dir = this.configPath.substring(0, this.configPath.lastIndexOf("/")); if (!existsSync(dir)) { const { mkdirSync } = __require("fs"); mkdirSync(dir, { recursive: true }); } writeFileSync(this.configPath, JSON.stringify(this.config, null, 2)); } catch (error) { throw new Error(`Failed to save MCP config: ${error}`); } } /** * 获取默认配置 */ getDefaultConfig() { return { servers: {}, client: { timeout: 3e4, retryAttempts: 3, retryDelay: 1e3 }, server: { port: 3001, host: "localhost", transport: "ws", auth: { enabled: false } } }; } /** * 添加服务器配置 */ addServer(name, config) { this.config.servers[name] = config; this.saveConfig(); } /** * 移除服务器配置 */ removeServer(name) { delete this.config.servers[name]; this.saveConfig(); } /** * 获取服务器配置 */ getServer(name) { return this.config.servers[name]; } /** * 获取所有服务器配置 */ getServers() { return this.config.servers; } /** * 更新客户端配置 */ updateClientConfig(config) { this.config.client = { ...this.config.client, ...config }; this.saveConfig(); } /** * 获取客户端配置 */ getClientConfig() { return this.config.client; } /** * 更新服务器配置 */ updateServerConfig(config) { this.config.server = { ...this.config.server, ...config }; this.saveConfig(); } /** * 获取服务器配置 */ getServerConfig() { return this.config.server; } /** * 验证服务器配置 */ validateServerConfig(config) { const errors = []; if (!config.name) { errors.push("Server name is required"); } if (!config.transport) { errors.push("Transport type is required"); } if (config.transport === "ws" && !config.endpoint) { errors.push("WebSocket endpoint is required for ws transport"); } if (config.transport === "stdio" && !config.command) { errors.push("Command is required for stdio transport"); } if (config.timeout && config.timeout < 1e3) { errors.push("Timeout must be at least 1000ms"); } return errors; } /** * 导出配置 */ exportConfig() { return JSON.parse(JSON.stringify(this.config)); } /** * 导入配置 */ importConfig(config) { this.config = { ...this.getDefaultConfig(), ...config, servers: { ...this.getDefaultConfig().servers, ...config.servers } }; this.saveConfig(); } /** * 重置为默认配置 */ reset() { this.config = this.getDefaultConfig(); this.saveConfig(); } }; var mcpConfig = new MCPConfig(); export { MCPClient, ToolValidationError, ToolExecutionError, ToolRegistrationError, ToolValidator, ToolManager, MCPServer, MCPConfig, mcpConfig };