Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
import { Client } from "@modelcontextprotocol/sdk/client/index.js"; | |
import type { MCPServerConfig, McpToolSchema, OpenAIFunctionSchema } from "./types.js"; | |
import { debugError, debugLog } from "./utils.js"; | |
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; | |
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; | |
export const mcpToolToOpenAIFunction = (tool: McpToolSchema): OpenAIFunctionSchema => { | |
return { | |
type: "function", | |
function: { | |
name: tool.name, | |
description: tool.name, | |
parameters: tool.inputSchema, | |
strict: true, | |
}, | |
}; | |
}; | |
export type MCPServerConnection = { client: Client; tools: OpenAIFunctionSchema[] }; | |
export const connectToMCPServers = async (servers: MCPServerConfig[]): Promise<MCPServerConnection[]> => { | |
const connections: MCPServerConnection[] = []; | |
await Promise.allSettled( | |
servers.map(async server => { | |
try { | |
const conn: MCPServerConnection = { | |
client: new Client({ | |
name: "playground-client" + crypto.randomUUID(), | |
version: "0.0.1", | |
}), | |
tools: [], | |
}; | |
debugLog(`Connecting to MCP server: ${server.name} (${server.url})`); | |
let transport; | |
const url = new URL(server.url); | |
if (server.protocol === "sse") { | |
transport = new SSEClientTransport(url); | |
} else { | |
transport = new StreamableHTTPClientTransport(url); | |
} | |
await conn.client.connect(transport); | |
const { tools: mcpTools } = await conn.client.listTools(); | |
const serverTools = mcpTools.map(mcpToolToOpenAIFunction); | |
conn.tools.push(...serverTools); | |
debugLog(`Connected to ${server.name} with ${mcpTools.length} tools`); | |
connections.push(conn); | |
} catch (error) { | |
debugError(`Failed to connect to MCP server ${server.name}:`, error); | |
} | |
}), | |
); | |
return connections; | |
}; | |
export const executeMcpTool = async ( | |
connections: MCPServerConnection[], | |
toolCall: { id: string; function: { name: string; arguments: string } }, | |
) => { | |
try { | |
debugLog(`Executing tool: ${toolCall.function.name}`); | |
debugLog(`Tool arguments:`, JSON.parse(toolCall.function.arguments)); | |
// Try to find the tool in any of the connected clients | |
let result = null; | |
for (const conn of connections) { | |
try { | |
const toolExists = conn.tools.some(tool => tool.function?.name === toolCall.function.name); | |
if (!toolExists) continue; | |
debugLog(`Found tool ${toolCall.function.name}`); | |
result = await conn.client.callTool({ | |
name: toolCall.function.name, | |
arguments: JSON.parse(toolCall.function.arguments), | |
}); | |
} catch (clientError) { | |
debugError(`Failed to execute tool on client:`, clientError); | |
continue; | |
} | |
} | |
if (!result) { | |
throw new Error(`Tool ${toolCall.function.name} not found in any connected MCP server`); | |
} | |
// mcpLog(`Tool result:`, result.content); | |
return { | |
tool_call_id: toolCall.id, | |
role: "tool" as const, | |
content: JSON.stringify(result.content), | |
}; | |
} catch (error) { | |
debugError(`Tool execution failed:`, error); | |
return { | |
tool_call_id: toolCall.id, | |
role: "tool" as const, | |
content: JSON.stringify({ error: error instanceof Error ? error.message : "Tool execution failed" }), | |
}; | |
} | |
}; | |