| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- "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;
- }
- }
|