Thomas G. Lopes
MCPs (#91)
52c6f5c unverified
raw
history blame
3.24 kB
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" }),
};
}
};