Spaces:
Sleeping
Sleeping
| 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<string, any>; | |
| clients: any[]; | |
| cleanup: () => Promise<void>; | |
| } | |
| /** | |
| * 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<MCPClientManager> { | |
| // 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<string, string>); | |
| // 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<void> { | |
| // Clean up the MCP clients | |
| for (const client of clients) { | |
| try { | |
| await client.close(); | |
| } catch (error) { | |
| console.error("Error closing MCP client:", error); | |
| } | |
| } | |
| } | |