UNPKG

@pluggedin/pluggedin-mcp-proxy

Version:

Unified MCP proxy that aggregates all your MCP servers (STDIO, SSE, Streamable HTTP) into one powerful interface. Access any tool through a single connection, search across unified documents with built-in RAG, and receive notifications from any model. Tes

191 lines (190 loc) 8.69 kB
import { sessionManager } from "../session-manager.js"; import { logMcpActivity, createExecutionTimer } from "../notification-logger.js"; import { debugLog, debugError } from "../debug-log.js"; import { sanitizeErrorMessage } from "../security-utils.js"; /** * Handles execution of dynamic tools from connected MCP servers. * Routes tool calls to the appropriate server based on the tool-to-server mapping. */ export class DynamicToolHandlers { toolToServerMap; instructionToServerMap; constructor(toolToServerMap, instructionToServerMap) { this.toolToServerMap = toolToServerMap; this.instructionToServerMap = instructionToServerMap; } /** * Handle calls to dynamic tools (tools from connected MCP servers) */ async handleDynamicTool(toolName, args) { try { // Use proper debug logging instead of console.log // Check if this is a dynamic tool if (!this.toolToServerMap) { debugError(`[DynamicToolHandler] toolToServerMap is undefined!`); throw new Error('toolToServerMap is not initialized'); } const toolMapping = this.toolToServerMap[toolName]; if (!toolMapping) { debugLog(`[DynamicToolHandler] Tool ${toolName} not found in map`); return null; // Not a dynamic tool } const { originalName, serverUuid } = toolMapping; debugLog(`[CallTool Handler] Mapped tool ${toolName} to server ${serverUuid} with original name ${originalName}`); // Use SessionManager for centralized session management if (!sessionManager) { throw new Error(`SessionManager is not initialized`); } const session = sessionManager.getSessionByServerUuid(serverUuid); if (!session) { throw new Error(`No session found for server ${serverUuid}. Available sessions: ${Array.from(sessionManager.getAllSessions().keys()).join(', ')}`); } if (!session.client) { debugError(`[DynamicToolHandler] Session structure:`, session); throw new Error(`Session for server ${serverUuid} has no client property. Session keys: ${Object.keys(session).join(', ')}`); } const timer = createExecutionTimer(); try { debugLog(`[CallTool Handler] Calling tool ${originalName} on server ${serverUuid}`); const response = await session.client.request({ method: "tools/call", params: { name: originalName, arguments: args ?? {} }, }); // Log successful tool call logMcpActivity({ action: 'tool_call', serverName: 'unknown', // We don't have serverName in ConnectedClient serverUuid: serverUuid, itemName: originalName, success: true, executionTime: timer.stop(), }).catch(() => { }); // Ignore notification errors debugLog(`[CallTool Handler] Tool ${originalName} response:`, response); if (response && typeof response === "object" && Array.isArray(response.content)) { // Standard response structure return { content: response.content, isError: !!response.isError, }; } else if (response && typeof response === "object" && typeof response.content === "string") { // If content is a string, wrap it in the expected array structure return { content: [{ type: "text", text: response.content }], isError: !!response.isError, }; } else { // Unexpected structure: log warning and return standardized error debugError(`[CallTool Handler] Unexpected response structure from tool ${originalName}:`, response); return { content: [ { type: "text", text: "Tool response format was not recognized. Please contact support or try again.", }, ], isError: true, }; } } catch (toolError) { debugError(`[CallTool Handler] Error calling tool ${originalName}:`, toolError); // Log failed tool call logMcpActivity({ action: 'tool_call', serverName: 'unknown', // We don't have serverName in ConnectedClient serverUuid: serverUuid, itemName: originalName, success: false, errorMessage: toolError instanceof Error ? toolError.message : String(toolError), executionTime: timer.stop(), }).catch(() => { }); // Ignore notification errors throw new Error(sanitizeErrorMessage(toolError)); } } catch (outerError) { debugError(`[DynamicToolHandler] Outer error in handleDynamicTool:`, outerError); throw outerError; } } /** * Handle custom instruction execution */ async handleCustomInstruction(instructionName, args) { const serverUuid = this.instructionToServerMap[instructionName]; if (!serverUuid) { return null; // Not a custom instruction } // Use SessionManager for centralized session management if (!sessionManager) { throw new Error(`SessionManager is not initialized`); } const session = sessionManager.getSessionByServerUuid(serverUuid); if (!session || !session.serverCapabilities) { throw new Error(`No active session found for server ${serverUuid}. Please ensure the server is connected.`); } const timer = createExecutionTimer(); try { // Find the actual instruction content const server = session.serverCapabilities; const instruction = server?.customInstructions?.find((inst) => { // Instructions should have consistent names set during discovery if (!inst.name) { debugError(`[CustomInstruction Handler] Warning: Instruction without name found`); return false; } return inst.name === instructionName; }); if (!instruction) { throw new Error(`Instruction ${instructionName} not found on server`); } // Log instruction execution logMcpActivity({ action: 'tool_call', serverName: session.serverName || 'unknown', serverUuid: serverUuid, itemName: instructionName, success: true, executionTime: timer.stop(), }).catch(() => { }); // Ignore notification errors // Return the instruction content return { content: [{ type: "text", text: `Executing custom instruction from ${session.serverName || 'unknown'}:\n\n${instruction.instruction}` }], isError: false, }; } catch (error) { // Log failed instruction execution logMcpActivity({ action: 'tool_call', serverName: session.serverName || 'unknown', serverUuid: serverUuid, itemName: instructionName, success: false, errorMessage: error instanceof Error ? error.message : String(error), executionTime: timer.stop(), }).catch(() => { }); // Ignore notification errors throw new Error(sanitizeErrorMessage(error)); } } /** * Get tool information for a dynamic tool */ getToolInfo(toolName) { return this.toolToServerMap[toolName] || null; } /** * Get server UUID for a custom instruction */ getInstructionServerUuid(instructionName) { return this.instructionToServerMap[instructionName] || null; } }