import { Client as MCPClient } from "@modelcontextprotocol/sdk/client/index.js"; import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; export interface KeyValuePair { key: string; value: string; } export interface MCPServerConfig { url: string; type: "sse" | "stdio"; command?: string; args?: string[]; env?: KeyValuePair[]; headers?: KeyValuePair[]; } export interface MCPClientManager { tools: Record; clients: any[]; cleanup: () => Promise; } /** * Initialize MCP clients for API calls * This uses the already running persistent HTTP or SSE servers */ export async function initializeMCPClients( mcpServers: MCPServerConfig[] = [], abortSignal?: AbortSignal ): Promise { // Initialize tools let tools = {}; const mcpClients: any[] = []; // Process each MCP server configuration for (const mcpServer of mcpServers) { try { const headers = mcpServer.headers?.reduce((acc, header) => { if (header.key) acc[header.key] = header.value || ""; return acc; }, {} as Record); // All servers are handled as HTTP or SSE // SSE is only when URL ends with /sse // which is the heuristic used by other clients const transport = mcpServer.url.endsWith("/sse") ? new SSEClientTransport(new URL(mcpServer.url), { requestInit: { headers, }, }) : new StreamableHTTPClientTransport(new URL(mcpServer.url), { requestInit: { headers, }, }); const mcpClient = new MCPClient({ name: "mcp-chat-client", version: "0.1.0", }); await mcpClient.connect(transport); mcpClients.push(mcpClient); const mcptools = await mcpClient.listTools(); console.log(`MCP tools from ${mcpServer.url}:`, Object.keys(mcptools)); // Add MCP tools to tools object tools = { ...tools, ...mcptools }; } catch (error) { console.error("Failed to initialize MCP client:", error); // Continue with other servers instead of failing the entire request } } // Register cleanup for all clients if an abort signal is provided if (abortSignal && mcpClients.length > 0) { abortSignal.addEventListener("abort", async () => { await cleanupMCPClients(mcpClients); }); } return { tools, clients: mcpClients, cleanup: async () => await cleanupMCPClients(mcpClients), }; } async function cleanupMCPClients(clients: any[]): Promise { // Clean up the MCP clients for (const client of clients) { try { await client.close(); } catch (error) { console.error("Error closing MCP client:", error); } } }