Spaces:
Sleeping
Sleeping
"use server"; | |
import { | |
createClient, | |
executeRequest, | |
listTools, | |
removeClient, | |
} from "./client"; | |
import { MCPClientLogger } from "./logger"; | |
import { | |
DEFAULT_MCP_CONFIG, | |
McpClientData, | |
McpConfigData, | |
McpRequestMessage, | |
ServerConfig, | |
ServerStatusResponse, | |
} from "./types"; | |
import fs from "fs/promises"; | |
import path from "path"; | |
import { getServerSideConfig } from "../config/server"; | |
const logger = new MCPClientLogger("MCP Actions"); | |
const CONFIG_PATH = path.join(process.cwd(), "app/mcp/mcp_config.json"); | |
const clientsMap = new Map<string, McpClientData>(); | |
// 获取客户端状态 | |
export async function getClientsStatus(): Promise< | |
Record<string, ServerStatusResponse> | |
> { | |
const config = await getMcpConfigFromFile(); | |
const result: Record<string, ServerStatusResponse> = {}; | |
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<void> { | |
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 { | |
const client = clientsMap.get(clientId); | |
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<McpConfigData> { | |
try { | |
const configStr = await fs.readFile(CONFIG_PATH, "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<void> { | |
try { | |
// 确保目录存在 | |
await fs.mkdir(path.dirname(CONFIG_PATH), { recursive: true }); | |
await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2)); | |
} catch (error) { | |
throw error; | |
} | |
} | |
// 检查 MCP 是否启用 | |
export async function isMcpEnabled() { | |
try { | |
const serverConfig = getServerSideConfig(); | |
return serverConfig.enableMcp; | |
} catch (error) { | |
logger.error(`Failed to check MCP status: ${error}`); | |
return false; | |
} | |
} | |