@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
JavaScript
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;
}
}