Skip to main content
Glama
MCPOrchestrator.ts6 kB
/** * MCP Orchestrator - Manages connections to multiple MCP servers */ import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; import type { MCPServerConfig, MCPConnection, MCPTool, } from "../types/index.js"; import { logInfo, logError, logDebug } from "../utils/logger.js"; export class MCPOrchestrator { private connections: Map<string, MCPConnection> = new Map(); /** * Connect to an MCP server */ async connectServer(config: MCPServerConfig): Promise<void> { logInfo("Connecting to MCP server", { name: config.name, transport: config.transport, }); try { if (config.transport === "stdio") { await this.connectStdioServer(config); } else { throw new Error(`Transport ${config.transport} not yet implemented`); } logInfo("Successfully connected to MCP server", { name: config.name }); } catch (error) { logError("Failed to connect to MCP server", { name: config.name, error: error instanceof Error ? error.message : String(error), }); throw error; } } /** * Connect to stdio-based MCP server */ private async connectStdioServer(config: MCPServerConfig): Promise<void> { if (!config.command || !config.args) { throw new Error("Stdio transport requires command and args"); } // Create transport // Filter out undefined env variables const env = Object.fromEntries( Object.entries({ ...process.env, ...config.env }).filter( ([_, v]) => v !== undefined, ), ) as Record<string, string>; const transport = new StdioClientTransport({ command: config.command, args: config.args, env, }); // Create MCP client const client = new Client( { name: "code2mcp-orchestrator", version: "1.0.0", }, { capabilities: {}, }, ); // Connect await client.connect(transport); // Fetch available tools const toolsResponse = await client.listTools(); const tools: MCPTool[] = toolsResponse.tools.map((tool) => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema as any, })); // Store connection this.connections.set(config.name, { name: config.name, config, client, tools, connected: true, }); logDebug("Fetched tools from MCP server", { name: config.name, toolCount: tools.length, toolNames: tools.map((t) => t.name), }); } /** * Call a tool on an MCP server * * @param toolName Format: "server__tool" or just "tool" * @param args Tool arguments */ async callTool(toolName: string, args: any): Promise<any> { logDebug("Calling MCP tool", { toolName, args }); // Parse tool name: "google_drive__get_document" -> ["google_drive", "get_document"] const parts = toolName.split("__"); let serverName: string; let actualToolName: string; if (parts.length >= 2) { serverName = parts[0]; actualToolName = parts.slice(1).join("__"); } else { // If no server prefix, try to find the tool in any server const connection = this.findToolInAnyServer(toolName); if (!connection) { throw new Error(`Tool not found: ${toolName}`); } serverName = connection.name; actualToolName = toolName; } const connection = this.connections.get(serverName); if (!connection) { throw new Error(`MCP server not found: ${serverName}`); } if (!connection.connected) { throw new Error(`MCP server not connected: ${serverName}`); } try { // Call the tool const result = await connection.client.callTool({ name: actualToolName, arguments: args, }); logDebug("MCP tool call successful", { toolName, resultType: typeof result, }); return result; } catch (error) { logError("MCP tool call failed", { toolName, error: error instanceof Error ? error.message : String(error), }); throw error; } } /** * Find a tool in any connected server */ private findToolInAnyServer(toolName: string): MCPConnection | null { for (const connection of this.connections.values()) { const tool = connection.tools.find((t) => t.name === toolName); if (tool) { return connection; } } return null; } /** * Get all tools from all connected servers * Tool names are prefixed with server name: "server__tool" */ getAllTools(): MCPTool[] { const allTools: MCPTool[] = []; for (const [serverName, connection] of this.connections) { const prefixedTools = connection.tools.map((tool) => ({ ...tool, name: `${serverName}__${tool.name}`, description: tool.description ? `[${serverName}] ${tool.description}` : `[${serverName}] ${tool.name}`, })); allTools.push(...prefixedTools); } return allTools; } /** * Get connection info for a server */ getConnection(serverName: string): MCPConnection | undefined { return this.connections.get(serverName); } /** * Get all server names */ getServerNames(): string[] { return Array.from(this.connections.keys()); } /** * Disconnect from all servers */ async disconnect(): Promise<void> { logInfo("Disconnecting from all MCP servers"); for (const [name, connection] of this.connections) { try { await connection.client.close(); connection.connected = false; logDebug("Disconnected from MCP server", { name }); } catch (error) { logError("Error disconnecting from MCP server", { name, error: error instanceof Error ? error.message : String(error), }); } } this.connections.clear(); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/blas0/code2mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server