import { createClient, executeRequest, listTools, removeClient, } from "./client"; import { MCPClientLogger } from "./logger"; import { DEFAULT_MCP_CONFIG, McpClientData, McpConfigData, McpRequestMessage, ServerConfig, ServerStatusResponse, } from "./types"; const JSON_INDENT = 2; const logger = new MCPClientLogger("MCP Actions"); const getConfigPath = async () => { if (EXPORT_MODE) { return "/mcp/config.json"; } else { const path = await import("path"); return path.join(process.cwd(), "app/mcp/mcp_config.json"); } }; class ClientsMap extends Map { toolToClientMap: Map; constructor() { super(); this.toolToClientMap = new Map(); } set(clientId: string, data: McpClientData): this { super.set(clientId, data); if (data?.tools?.tools) { for (const tool of data.tools.tools) { if (tool.name) { this.toolToClientMap.set(tool.name, clientId); } } } else { this.purgeToolMappings(clientId); } return this; } delete(clientId: string): boolean { const ret = clientsMap.delete(clientId); this.purgeToolMappings(clientId); return ret; } clear(): void { super.clear(); this.toolToClientMap.clear(); } getByToolName(toolName: string) { const toolClientId = clientsMap.toolToClientMap.get(toolName); if (toolClientId) { return clientsMap.get(toolClientId); } } private purgeToolMappings(clientId: string) { for (const [tool, mappedId] of this.toolToClientMap.entries()) { if (mappedId === clientId) { this.toolToClientMap.delete(tool); } } } } const clientsMap = new ClientsMap(); // 获取客户端状态 export async function getClientsStatus(): Promise< Record > { const config = await getMcpConfigFromFile(); const result: Record = {}; for (const clientId of Object.keys(config.mcpServers)) { const status = clientsMap.get(clientId); const serverConfig = config.mcpServers[clientId]; if (!serverConfig) { result[clientId] = { status: "undefined", errorMsg: null }; continue; } if (serverConfig.status === "paused") { result[clientId] = { status: "paused", errorMsg: null }; continue; } if (!status) { result[clientId] = { status: "undefined", errorMsg: null }; continue; } if ( status.client === null && status.tools === null && status.errorMsg === null ) { result[clientId] = { status: "initializing", errorMsg: null }; continue; } if (status.errorMsg) { result[clientId] = { status: "error", errorMsg: status.errorMsg }; continue; } if (status.client) { result[clientId] = { status: "active", errorMsg: null }; continue; } result[clientId] = { status: "error", errorMsg: "Client not found" }; } return result; } // 获取客户端工具 export async function getClientTools(clientId: string) { return clientsMap.get(clientId)?.tools ?? null; } // 获取可用客户端数量 export async function getAvailableClientsCount() { let count = 0; clientsMap.forEach((map) => !map.errorMsg && count++); return count; } // 获取所有客户端工具 export async function getAllTools() { const result = []; for (const [clientId, status] of clientsMap.entries()) { result.push({ clientId, tools: status.tools, }); } return result; } // 初始化单个客户端 async function initializeSingleClient( clientId: string, serverConfig: ServerConfig, ) { // 如果服务器状态是暂停,则不初始化 if (serverConfig.status === "paused") { logger.info(`Skipping initialization for paused client [${clientId}]`); return; } logger.info(`Initializing client [${clientId}]...`); // 先设置初始化状态 clientsMap.set(clientId, { client: null, tools: null, errorMsg: null, // null 表示正在初始化 }); // 异步初始化 createClient(clientId, serverConfig) .then(async (client) => { const tools = await listTools(client); logger.info( `Supported tools for [${clientId}]: ${JSON.stringify(tools, null, 2)}`, ); clientsMap.set(clientId, { client, tools, errorMsg: null }); logger.success(`Client [${clientId}] initialized successfully`); }) .catch((error) => { clientsMap.set(clientId, { client: null, tools: null, errorMsg: error instanceof Error ? error.message : String(error), }); logger.error(`Failed to initialize client [${clientId}]: ${error}`); }); } // 初始化系统 export async function initializeMcpSystem() { logger.info("MCP Actions starting..."); try { // 检查是否已有活跃的客户端 if (clientsMap.size > 0) { logger.info("MCP system already initialized, skipping..."); return; } const config = await getMcpConfigFromFile(); // 初始化所有客户端 for (const [clientId, serverConfig] of Object.entries(config.mcpServers)) { await initializeSingleClient(clientId, serverConfig); } return config; } catch (error) { logger.error(`Failed to initialize MCP system: ${error}`); throw error; } } // 添加服务器 export async function addMcpServer(clientId: string, config: ServerConfig) { try { const currentConfig = await getMcpConfigFromFile(); const isNewServer = !(clientId in currentConfig.mcpServers); // 如果是新服务器,设置默认状态为 active if (isNewServer && !config.status) { config.status = "active"; } const newConfig = { ...currentConfig, mcpServers: { ...currentConfig.mcpServers, [clientId]: config, }, }; await updateMcpConfig(newConfig); // 只有新服务器或状态为 active 的服务器才初始化 if (isNewServer || config.status === "active") { await initializeSingleClient(clientId, config); } return newConfig; } catch (error) { logger.error(`Failed to add server [${clientId}]: ${error}`); throw error; } } // 暂停服务器 export async function pauseMcpServer(clientId: string) { try { const currentConfig = await getMcpConfigFromFile(); const serverConfig = currentConfig.mcpServers[clientId]; if (!serverConfig) { throw new Error(`Server ${clientId} not found`); } // 先更新配置 const newConfig: McpConfigData = { ...currentConfig, mcpServers: { ...currentConfig.mcpServers, [clientId]: { ...serverConfig, status: "paused", }, }, }; await updateMcpConfig(newConfig); // 然后关闭客户端 const client = clientsMap.get(clientId); if (client?.client) { await removeClient(client.client); } clientsMap.delete(clientId); return newConfig; } catch (error) { logger.error(`Failed to pause server [${clientId}]: ${error}`); throw error; } } // 恢复服务器 export async function resumeMcpServer(clientId: string): Promise { try { const currentConfig = await getMcpConfigFromFile(); const serverConfig = currentConfig.mcpServers[clientId]; if (!serverConfig) { throw new Error(`Server ${clientId} not found`); } // 先尝试初始化客户端 logger.info(`Trying to initialize client [${clientId}]...`); try { const client = await createClient(clientId, serverConfig); const tools = await listTools(client); clientsMap.set(clientId, { client, tools, errorMsg: null }); logger.success(`Client [${clientId}] initialized successfully`); // 初始化成功后更新配置 const newConfig: McpConfigData = { ...currentConfig, mcpServers: { ...currentConfig.mcpServers, [clientId]: { ...serverConfig, status: "active" as const, }, }, }; await updateMcpConfig(newConfig); } catch (error) { const currentConfig = await getMcpConfigFromFile(); const serverConfig = currentConfig.mcpServers[clientId]; // 如果配置中存在该服务器,则更新其状态为 error if (serverConfig) { serverConfig.status = "error"; await updateMcpConfig(currentConfig); } // 初始化失败 clientsMap.set(clientId, { client: null, tools: null, errorMsg: error instanceof Error ? error.message : String(error), }); logger.error(`Failed to initialize client [${clientId}]: ${error}`); throw error; } } catch (error) { logger.error(`Failed to resume server [${clientId}]: ${error}`); throw error; } } // 移除服务器 export async function removeMcpServer(clientId: string) { try { const currentConfig = await getMcpConfigFromFile(); const { [clientId]: _, ...rest } = currentConfig.mcpServers; const newConfig = { ...currentConfig, mcpServers: rest, }; await updateMcpConfig(newConfig); // 关闭并移除客户端 const client = clientsMap.get(clientId); if (client?.client) { await removeClient(client.client); } clientsMap.delete(clientId); return newConfig; } catch (error) { logger.error(`Failed to remove server [${clientId}]: ${error}`); throw error; } } // 重启所有客户端 export async function restartAllClients() { logger.info("Restarting all clients..."); try { // 关闭所有客户端 for (const client of clientsMap.values()) { if (client.client) { await removeClient(client.client); } } // 清空状态 clientsMap.clear(); // 重新初始化 const config = await getMcpConfigFromFile(); for (const [clientId, serverConfig] of Object.entries(config.mcpServers)) { await initializeSingleClient(clientId, serverConfig); } return config; } catch (error) { logger.error(`Failed to restart clients: ${error}`); throw error; } } // 执行 MCP 请求 export async function executeMcpAction( clientId: string, request: McpRequestMessage, ) { try { let client = clientsMap.get(clientId); if ( !client && request.params?.name && typeof request.params.name === "string" ) { client = clientsMap.getByToolName(request.params.name); } if (!client?.client) { throw new Error(`Client ${clientId} not found`); } logger.info(`Executing request for [${clientId}]`); return await executeRequest(client.client, request); } catch (error) { logger.error(`Failed to execute request for [${clientId}]: ${error}`); throw error; } } // 获取 MCP 配置文件 export async function getMcpConfigFromFile(): Promise { try { if (EXPORT_MODE) { const res = await fetch(await getConfigPath()); const config: McpConfigData = await res.json(); const storage = localStorage; const storedConfig_str = storage.getItem("McpConfig"); if (storedConfig_str) { const storedConfig: McpConfigData = JSON.parse(storedConfig_str); // Create a merged configuration that combines both sources const merged = { ...config.mcpServers }; if (storedConfig.mcpServers) { // Ensure we process all servers from stored config for (const id in storedConfig.mcpServers) { merged[id] = { ...merged[id], ...storedConfig.mcpServers[id] }; } } config.mcpServers = merged; } return config; } else { const fs = await import("fs/promises"); const configStr = await fs.readFile(await getConfigPath(), "utf-8"); return JSON.parse(configStr); } } catch (error) { logger.error(`Failed to load MCP config, using default config: ${error}`); return DEFAULT_MCP_CONFIG; } } // 更新 MCP 配置文件 async function updateMcpConfig(config: McpConfigData): Promise { if (EXPORT_MODE) { try { const storage = localStorage; storage.setItem("McpConfig", JSON.stringify(config)); } catch (storageError) { logger.warn(`Failed to save MCP config to localStorage: ${storageError}`); // Continue execution without storage } } else { const fs = await import("fs/promises"); const path = await import("path"); // 确保目录存在 await fs.mkdir(path.dirname(await getConfigPath()), { recursive: true }); await fs.writeFile( await getConfigPath(), JSON.stringify(config, null, JSON_INDENT), ); } } // 检查 MCP 是否启用 export async function isMcpEnabled() { try { const config = await getMcpConfigFromFile(); if (typeof config.enableMcp === "boolean") { return config.enableMcp; } if (EXPORT_MODE) { const { getClientConfig } = await import("../config/client"); const clientConfig = getClientConfig(); return clientConfig?.enableMcp === true; } else { const { getServerSideConfig } = await import("../config/server"); const serverConfig = getServerSideConfig(); return serverConfig.enableMcp; } } catch (error) { logger.error(`Failed to check MCP status: ${error}`); return false; } }