mirror of
				https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
				synced 2025-11-04 00:03:46 +08:00 
			
		
		
		
	Compare commits
	
		
			3 Commits
		
	
	
		
			9f969903ed
			...
			b72013e9c3
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					b72013e9c3 | ||
| 
						 | 
					62d32f317d | ||
| 
						 | 
					d1baabae14 | 
@@ -22,7 +22,6 @@ import {
 | 
			
		||||
  resumeMcpServer,
 | 
			
		||||
} from "../mcp/actions";
 | 
			
		||||
import {
 | 
			
		||||
  ListToolsResponse,
 | 
			
		||||
  ToolSchema,
 | 
			
		||||
  McpConfigData,
 | 
			
		||||
  PresetServer,
 | 
			
		||||
@@ -48,7 +47,7 @@ export function McpMarketPage() {
 | 
			
		||||
  const [searchText, setSearchText] = useState("");
 | 
			
		||||
  const [userConfig, setUserConfig] = useState<Record<string, any>>({});
 | 
			
		||||
  const [editingServerId, setEditingServerId] = useState<string | undefined>();
 | 
			
		||||
  const [tools, setTools] = useState<ListToolsResponse | null>(null);
 | 
			
		||||
  const [tools, setTools] = useState<ToolSchema[] | null>(null);
 | 
			
		||||
  const [viewingServerId, setViewingServerId] = useState<string | undefined>();
 | 
			
		||||
  const [isLoading, setIsLoading] = useState(false);
 | 
			
		||||
  const [config, setConfig] = useState<McpConfigData>();
 | 
			
		||||
@@ -232,7 +231,7 @@ export function McpMarketPage() {
 | 
			
		||||
    try {
 | 
			
		||||
      const result = await getClientTools(id);
 | 
			
		||||
      if (result) {
 | 
			
		||||
        setTools(result);
 | 
			
		||||
        setTools(result?.tools);
 | 
			
		||||
      } else {
 | 
			
		||||
        throw new Error("Failed to load tools");
 | 
			
		||||
      }
 | 
			
		||||
@@ -733,8 +732,8 @@ export function McpMarketPage() {
 | 
			
		||||
              <div className={styles["tools-list"]}>
 | 
			
		||||
                {isLoading ? (
 | 
			
		||||
                  <div>Loading...</div>
 | 
			
		||||
                ) : tools?.tools ? (
 | 
			
		||||
                  tools.tools.map((tool: ToolSchema, index: number) => (
 | 
			
		||||
                ) : tools ? (
 | 
			
		||||
                  tools.map((tool: ToolSchema, index: number) => (
 | 
			
		||||
                    <div key={index} className={styles["tool-item"]}>
 | 
			
		||||
                      <div className={styles["tool-name"]}>{tool.name}</div>
 | 
			
		||||
                      <div className={styles["tool-description"]}>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										491
									
								
								app/mcp/actions.base.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										491
									
								
								app/mcp/actions.base.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,491 @@
 | 
			
		||||
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<string, McpClientData> {
 | 
			
		||||
  toolToClientMap: Map<string, string>;
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super();
 | 
			
		||||
    this.toolToClientMap = new Map<string, string>();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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<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 {
 | 
			
		||||
    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<McpConfigData> {
 | 
			
		||||
  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<void> {
 | 
			
		||||
  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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								app/mcp/actions.client.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								app/mcp/actions.client.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export * from "./actions.base";
 | 
			
		||||
							
								
								
									
										2
									
								
								app/mcp/actions.server.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								app/mcp/actions.server.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
"use server";
 | 
			
		||||
export * from "./actions.base";
 | 
			
		||||
@@ -1,447 +1,56 @@
 | 
			
		||||
if (!EXPORT_MODE) {
 | 
			
		||||
  ("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 { McpRequestMessage, ServerConfig } from "./types";
 | 
			
		||||
 | 
			
		||||
const logger = new MCPClientLogger("MCP Actions");
 | 
			
		||||
let actionsHost: typeof import("./actions.base") | undefined;
 | 
			
		||||
 | 
			
		||||
const getConfigPath = async () => {
 | 
			
		||||
const actions = async () => {
 | 
			
		||||
  if (!actionsHost) {
 | 
			
		||||
    if (EXPORT_MODE) {
 | 
			
		||||
    return "/mcp/config.json";
 | 
			
		||||
      actionsHost = await import("./actions.client");
 | 
			
		||||
    } else {
 | 
			
		||||
    const path = await import("path");
 | 
			
		||||
    return path.join(process.cwd(), "app/mcp/mcp_config.json");
 | 
			
		||||
      actionsHost = await import("./actions.server");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return actionsHost;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const clientsMap = new Map<string, McpClientData>();
 | 
			
		||||
export const getAvailableClientsCount = async () => {
 | 
			
		||||
  return (await actions()).getAvailableClientsCount();
 | 
			
		||||
};
 | 
			
		||||
export const isMcpEnabled = async () => {
 | 
			
		||||
  return (await actions()).isMcpEnabled();
 | 
			
		||||
};
 | 
			
		||||
export const initializeMcpSystem = async () => {
 | 
			
		||||
  return (await actions()).initializeMcpSystem();
 | 
			
		||||
};
 | 
			
		||||
export const addMcpServer = async (clientId: string, config: ServerConfig) => {
 | 
			
		||||
  return (await actions()).addMcpServer(clientId, config);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 获取客户端状态
 | 
			
		||||
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(
 | 
			
		||||
export const getClientsStatus = async () => {
 | 
			
		||||
  return (await actions()).getClientsStatus();
 | 
			
		||||
};
 | 
			
		||||
export const getClientTools = async (clientId: string) => {
 | 
			
		||||
  return (await actions()).getClientTools(clientId);
 | 
			
		||||
};
 | 
			
		||||
export const getMcpConfigFromFile = async () => {
 | 
			
		||||
  return (await actions()).getMcpConfigFromFile();
 | 
			
		||||
};
 | 
			
		||||
export const pauseMcpServer = async (clientId: string) => {
 | 
			
		||||
  return (await actions()).pauseMcpServer(clientId);
 | 
			
		||||
};
 | 
			
		||||
export const restartAllClients = async () => {
 | 
			
		||||
  return (await actions()).restartAllClients();
 | 
			
		||||
};
 | 
			
		||||
export const resumeMcpServer = async (clientId: string) => {
 | 
			
		||||
  return (await actions()).resumeMcpServer(clientId);
 | 
			
		||||
};
 | 
			
		||||
export const executeMcpAction = async (
 | 
			
		||||
  clientId: string,
 | 
			
		||||
  request: McpRequestMessage,
 | 
			
		||||
) {
 | 
			
		||||
  try {
 | 
			
		||||
    let client = clientsMap.get(clientId);
 | 
			
		||||
    if (!client) {
 | 
			
		||||
      client = [...clientsMap.values()].find(
 | 
			
		||||
        (c) =>
 | 
			
		||||
          c.tools?.tools &&
 | 
			
		||||
          c.tools.tools.find((t) => t.name == 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<McpConfigData> {
 | 
			
		||||
  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);
 | 
			
		||||
        const merged = { ...config.mcpServers };
 | 
			
		||||
        if (storedConfig.mcpServers) {
 | 
			
		||||
          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<void> {
 | 
			
		||||
  try {
 | 
			
		||||
    // 确保目录存在
 | 
			
		||||
    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, 2),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    throw error;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 检查 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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
) => {
 | 
			
		||||
  return (await actions()).executeMcpAction(clientId, request);
 | 
			
		||||
};
 | 
			
		||||
export const getAllTools = async () => {
 | 
			
		||||
  return (await actions()).getAllTools();
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,9 @@ export async function createClient(
 | 
			
		||||
    transport = new SSEClientTransport(new URL(config.url));
 | 
			
		||||
  } else {
 | 
			
		||||
    if (EXPORT_MODE) {
 | 
			
		||||
      throw new Error("Cannot use stdio transport in export mode");
 | 
			
		||||
      throw new Error(
 | 
			
		||||
        "Cannot use stdio transport in export mode. Please use SSE transport configuration instead.",
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      const { StdioClientTransport } = await import(
 | 
			
		||||
        "@modelcontextprotocol/sdk/client/stdio.js"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user