actions.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. "use server";
  2. import {
  3. createClient,
  4. executeRequest,
  5. listTools,
  6. removeClient,
  7. } from "./client";
  8. import { MCPClientLogger } from "./logger";
  9. import {
  10. DEFAULT_MCP_CONFIG,
  11. McpClientData,
  12. McpConfigData,
  13. McpRequestMessage,
  14. ServerConfig,
  15. ServerStatusResponse,
  16. } from "./types";
  17. import fs from "fs/promises";
  18. import path from "path";
  19. import { getServerSideConfig } from "../config/server";
  20. const logger = new MCPClientLogger("MCP Actions");
  21. const CONFIG_PATH = path.join(process.cwd(), "app/mcp/mcp_config.json");
  22. const clientsMap = new Map<string, McpClientData>();
  23. // 获取客户端状态
  24. export async function getClientsStatus(): Promise<
  25. Record<string, ServerStatusResponse>
  26. > {
  27. const config = await getMcpConfigFromFile();
  28. const result: Record<string, ServerStatusResponse> = {};
  29. for (const clientId of Object.keys(config.mcpServers)) {
  30. const status = clientsMap.get(clientId);
  31. const serverConfig = config.mcpServers[clientId];
  32. if (!serverConfig) {
  33. result[clientId] = { status: "undefined", errorMsg: null };
  34. continue;
  35. }
  36. if (serverConfig.status === "paused") {
  37. result[clientId] = { status: "paused", errorMsg: null };
  38. continue;
  39. }
  40. if (!status) {
  41. result[clientId] = { status: "undefined", errorMsg: null };
  42. continue;
  43. }
  44. if (
  45. status.client === null &&
  46. status.tools === null &&
  47. status.errorMsg === null
  48. ) {
  49. result[clientId] = { status: "initializing", errorMsg: null };
  50. continue;
  51. }
  52. if (status.errorMsg) {
  53. result[clientId] = { status: "error", errorMsg: status.errorMsg };
  54. continue;
  55. }
  56. if (status.client) {
  57. result[clientId] = { status: "active", errorMsg: null };
  58. continue;
  59. }
  60. result[clientId] = { status: "error", errorMsg: "Client not found" };
  61. }
  62. return result;
  63. }
  64. // 获取客户端工具
  65. export async function getClientTools(clientId: string) {
  66. return clientsMap.get(clientId)?.tools ?? null;
  67. }
  68. // 获取可用客户端数量
  69. export async function getAvailableClientsCount() {
  70. let count = 0;
  71. clientsMap.forEach((map) => !map.errorMsg && count++);
  72. return count;
  73. }
  74. // 获取所有客户端工具
  75. export async function getAllTools() {
  76. const result = [];
  77. for (const [clientId, status] of clientsMap.entries()) {
  78. result.push({
  79. clientId,
  80. tools: status.tools,
  81. });
  82. }
  83. return result;
  84. }
  85. // 初始化单个客户端
  86. async function initializeSingleClient(
  87. clientId: string,
  88. serverConfig: ServerConfig,
  89. ) {
  90. // 如果服务器状态是暂停,则不初始化
  91. if (serverConfig.status === "paused") {
  92. logger.info(`Skipping initialization for paused client [${clientId}]`);
  93. return;
  94. }
  95. logger.info(`Initializing client [${clientId}]...`);
  96. // 先设置初始化状态
  97. clientsMap.set(clientId, {
  98. client: null,
  99. tools: null,
  100. errorMsg: null, // null 表示正在初始化
  101. });
  102. // 异步初始化
  103. createClient(clientId, serverConfig)
  104. .then(async (client) => {
  105. const tools = await listTools(client);
  106. logger.info(
  107. `Supported tools for [${clientId}]: ${JSON.stringify(tools, null, 2)}`,
  108. );
  109. clientsMap.set(clientId, { client, tools, errorMsg: null });
  110. logger.success(`Client [${clientId}] initialized successfully`);
  111. })
  112. .catch((error) => {
  113. clientsMap.set(clientId, {
  114. client: null,
  115. tools: null,
  116. errorMsg: error instanceof Error ? error.message : String(error),
  117. });
  118. logger.error(`Failed to initialize client [${clientId}]: ${error}`);
  119. });
  120. }
  121. // 初始化系统
  122. export async function initializeMcpSystem() {
  123. logger.info("MCP Actions starting...");
  124. try {
  125. // 检查是否已有活跃的客户端
  126. if (clientsMap.size > 0) {
  127. logger.info("MCP system already initialized, skipping...");
  128. return;
  129. }
  130. const config = await getMcpConfigFromFile();
  131. // 初始化所有客户端
  132. for (const [clientId, serverConfig] of Object.entries(config.mcpServers)) {
  133. await initializeSingleClient(clientId, serverConfig);
  134. }
  135. return config;
  136. } catch (error) {
  137. logger.error(`Failed to initialize MCP system: ${error}`);
  138. throw error;
  139. }
  140. }
  141. // 添加服务器
  142. export async function addMcpServer(clientId: string, config: ServerConfig) {
  143. try {
  144. const currentConfig = await getMcpConfigFromFile();
  145. const isNewServer = !(clientId in currentConfig.mcpServers);
  146. // 如果是新服务器,设置默认状态为 active
  147. if (isNewServer && !config.status) {
  148. config.status = "active";
  149. }
  150. const newConfig = {
  151. ...currentConfig,
  152. mcpServers: {
  153. ...currentConfig.mcpServers,
  154. [clientId]: config,
  155. },
  156. };
  157. await updateMcpConfig(newConfig);
  158. // 只有新服务器或状态为 active 的服务器才初始化
  159. if (isNewServer || config.status === "active") {
  160. await initializeSingleClient(clientId, config);
  161. }
  162. return newConfig;
  163. } catch (error) {
  164. logger.error(`Failed to add server [${clientId}]: ${error}`);
  165. throw error;
  166. }
  167. }
  168. // 暂停服务器
  169. export async function pauseMcpServer(clientId: string) {
  170. try {
  171. const currentConfig = await getMcpConfigFromFile();
  172. const serverConfig = currentConfig.mcpServers[clientId];
  173. if (!serverConfig) {
  174. throw new Error(`Server ${clientId} not found`);
  175. }
  176. // 先更新配置
  177. const newConfig: McpConfigData = {
  178. ...currentConfig,
  179. mcpServers: {
  180. ...currentConfig.mcpServers,
  181. [clientId]: {
  182. ...serverConfig,
  183. status: "paused",
  184. },
  185. },
  186. };
  187. await updateMcpConfig(newConfig);
  188. // 然后关闭客户端
  189. const client = clientsMap.get(clientId);
  190. if (client?.client) {
  191. await removeClient(client.client);
  192. }
  193. clientsMap.delete(clientId);
  194. return newConfig;
  195. } catch (error) {
  196. logger.error(`Failed to pause server [${clientId}]: ${error}`);
  197. throw error;
  198. }
  199. }
  200. // 恢复服务器
  201. export async function resumeMcpServer(clientId: string): Promise<void> {
  202. try {
  203. const currentConfig = await getMcpConfigFromFile();
  204. const serverConfig = currentConfig.mcpServers[clientId];
  205. if (!serverConfig) {
  206. throw new Error(`Server ${clientId} not found`);
  207. }
  208. // 先尝试初始化客户端
  209. logger.info(`Trying to initialize client [${clientId}]...`);
  210. try {
  211. const client = await createClient(clientId, serverConfig);
  212. const tools = await listTools(client);
  213. clientsMap.set(clientId, { client, tools, errorMsg: null });
  214. logger.success(`Client [${clientId}] initialized successfully`);
  215. // 初始化成功后更新配置
  216. const newConfig: McpConfigData = {
  217. ...currentConfig,
  218. mcpServers: {
  219. ...currentConfig.mcpServers,
  220. [clientId]: {
  221. ...serverConfig,
  222. status: "active" as const,
  223. },
  224. },
  225. };
  226. await updateMcpConfig(newConfig);
  227. } catch (error) {
  228. const currentConfig = await getMcpConfigFromFile();
  229. const serverConfig = currentConfig.mcpServers[clientId];
  230. // 如果配置中存在该服务器,则更新其状态为 error
  231. if (serverConfig) {
  232. serverConfig.status = "error";
  233. await updateMcpConfig(currentConfig);
  234. }
  235. // 初始化失败
  236. clientsMap.set(clientId, {
  237. client: null,
  238. tools: null,
  239. errorMsg: error instanceof Error ? error.message : String(error),
  240. });
  241. logger.error(`Failed to initialize client [${clientId}]: ${error}`);
  242. throw error;
  243. }
  244. } catch (error) {
  245. logger.error(`Failed to resume server [${clientId}]: ${error}`);
  246. throw error;
  247. }
  248. }
  249. // 移除服务器
  250. export async function removeMcpServer(clientId: string) {
  251. try {
  252. const currentConfig = await getMcpConfigFromFile();
  253. const { [clientId]: _, ...rest } = currentConfig.mcpServers;
  254. const newConfig = {
  255. ...currentConfig,
  256. mcpServers: rest,
  257. };
  258. await updateMcpConfig(newConfig);
  259. // 关闭并移除客户端
  260. const client = clientsMap.get(clientId);
  261. if (client?.client) {
  262. await removeClient(client.client);
  263. }
  264. clientsMap.delete(clientId);
  265. return newConfig;
  266. } catch (error) {
  267. logger.error(`Failed to remove server [${clientId}]: ${error}`);
  268. throw error;
  269. }
  270. }
  271. // 重启所有客户端
  272. export async function restartAllClients() {
  273. logger.info("Restarting all clients...");
  274. try {
  275. // 关闭所有客户端
  276. for (const client of clientsMap.values()) {
  277. if (client.client) {
  278. await removeClient(client.client);
  279. }
  280. }
  281. // 清空状态
  282. clientsMap.clear();
  283. // 重新初始化
  284. const config = await getMcpConfigFromFile();
  285. for (const [clientId, serverConfig] of Object.entries(config.mcpServers)) {
  286. await initializeSingleClient(clientId, serverConfig);
  287. }
  288. return config;
  289. } catch (error) {
  290. logger.error(`Failed to restart clients: ${error}`);
  291. throw error;
  292. }
  293. }
  294. // 执行 MCP 请求
  295. export async function executeMcpAction(
  296. clientId: string,
  297. request: McpRequestMessage,
  298. ) {
  299. try {
  300. const client = clientsMap.get(clientId);
  301. if (!client?.client) {
  302. throw new Error(`Client ${clientId} not found`);
  303. }
  304. logger.info(`Executing request for [${clientId}]`);
  305. return await executeRequest(client.client, request);
  306. } catch (error) {
  307. logger.error(`Failed to execute request for [${clientId}]: ${error}`);
  308. throw error;
  309. }
  310. }
  311. // 获取 MCP 配置文件
  312. export async function getMcpConfigFromFile(): Promise<McpConfigData> {
  313. try {
  314. const configStr = await fs.readFile(CONFIG_PATH, "utf-8");
  315. return JSON.parse(configStr);
  316. } catch (error) {
  317. logger.error(`Failed to load MCP config, using default config: ${error}`);
  318. return DEFAULT_MCP_CONFIG;
  319. }
  320. }
  321. // 更新 MCP 配置文件
  322. async function updateMcpConfig(config: McpConfigData): Promise<void> {
  323. try {
  324. await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2));
  325. } catch (error) {
  326. throw error;
  327. }
  328. }
  329. // 检查 MCP 是否启用
  330. export async function isMcpEnabled() {
  331. try {
  332. const serverConfig = getServerSideConfig();
  333. return serverConfig.enableMcp;
  334. } catch (error) {
  335. logger.error(`Failed to check MCP status: ${error}`);
  336. return false;
  337. }
  338. }