UNPKG

@grebyn/toolflow-mcp-server

Version:

MCP server for managing other MCP servers - discover, install, organize into bundles, and automate with workflows. Uses StreamableHTTP transport with dual OAuth/API key authentication.

103 lines 3.96 kB
/** * MCP Server Logger Utility * * Provides centralized logging for MCP tool executions * Non-blocking and failure-resilient */ import { getServerConfig } from '../config/server-config.js'; import { sanitizeData, calculateDataSize, getAuthHeader, determineExecutionStatus, extractErrorMessage, withRetry } from './shared-logging.js'; // Sanitization is now handled by shared utilities /** * Main logging function - fire and forget */ export async function logToolExecution(context, logContext) { try { const config = getServerConfig(); const endpoint = `${config.apiEndpoint}/logs`; // Sanitize data using shared utilities const sanitizedArgs = sanitizeData(logContext.toolArgs); const sanitizedResponse = sanitizeData(logContext.toolResponse); // Prepare log entry with new fields const logEntry = { tool_name: logContext.toolName, tool_args: sanitizedArgs, tool_response: sanitizedResponse, execution_status: logContext.executionStatus || 'success', error_message: logContext.errorMessage, execution_time_ms: logContext.executionTimeMs, response_size: logContext.toolResponse ? calculateDataSize(logContext.toolResponse) : undefined, transport_type: logContext.transportType, session_id: logContext.sessionId || context.sessionId, client_identifier: logContext.clientIdentifier, ip_address: logContext.ipAddress, user_agent: logContext.userAgent }; // Get authentication header using shared utility const authHeader = getAuthHeader(context); // Fire and forget with retry - don't await withRetry(async () => { const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': authHeader }, body: JSON.stringify(logEntry) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP ${response.status}: ${errorText}`); } }).catch(() => { // Silent fail - logging errors should not break tool execution }); } catch (error) { // Silently fail - logging should never break tool execution } } /** * Wrapper to time and log tool execution */ export async function withLogging(context, toolName, toolArgs, transportType, additionalContext, executeFunction) { const startTime = Date.now(); let toolResponse; let error; try { // Execute the actual tool const result = await executeFunction(); toolResponse = result; // Log successful execution with response logToolExecution(context, { toolName, toolArgs, toolResponse: result, executionStatus: 'success', executionTimeMs: Date.now() - startTime, transportType, ...additionalContext }); return result; } catch (err) { error = err; // Use shared utilities to determine error details const executionStatus = determineExecutionStatus(error); const errorMessage = extractErrorMessage(error); // Log failed execution with error details as response logToolExecution(context, { toolName, toolArgs, toolResponse: { error: errorMessage, error_type: executionStatus }, executionStatus, errorMessage, executionTimeMs: Date.now() - startTime, transportType, ...additionalContext }); // Re-throw the error throw error; } } // Client identifier extraction is now handled by shared utilities //# sourceMappingURL=logger.js.map