actions.ts 9.6 KB

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