UNPKG

superaugment

Version:

Enterprise-grade MCP server with world-class C++ analysis, robust error handling, and production-ready architecture for VS Code Augment

296 lines 10.1 kB
/** * Base Tool Class for SuperAugment * * Provides a standardized foundation for all SuperAugment tools with * unified error handling, logging, validation, and performance monitoring. */ import { z } from 'zod'; import { logger } from '../utils/logger.js'; import { SuperAugmentError, ToolExecutionError, ErrorCode, } from '../errors/ErrorTypes.js'; import { globalErrorHandler } from '../errors/ErrorHandler.js'; /** * Abstract base class for all SuperAugment tools */ export class BaseTool { configManager; metrics; maxExecutionTime; maxMemoryUsage; constructor(configManager, options = {}) { this.configManager = configManager; this.maxExecutionTime = options.maxExecutionTime || 300000; // 5 minutes default this.maxMemoryUsage = options.maxMemoryUsage || 512 * 1024 * 1024; // 512MB default this.metrics = { totalExecutions: 0, successfulExecutions: 0, failedExecutions: 0, averageExecutionTime: 0, totalExecutionTime: 0, peakMemoryUsage: 0, }; } /** * Main execution method with full error handling and monitoring */ async execute(args) { const context = { startTime: new Date(), toolName: this.name, arguments: args, requestId: this.generateRequestId(), }; try { // Pre-execution setup await this.preExecute(context); // Validate input const validatedArgs = await this.validateInput(args); // Execute with monitoring const result = await this.executeWithMonitoring(validatedArgs, context); // Post-execution cleanup await this.postExecute(context, result); return result; } catch (error) { await this.handleExecutionError(error, context); throw error; // Re-throw after handling } } /** * Validate input arguments using the tool's schema */ async validateInput(args) { try { return this.inputSchema.parse(args); } catch (error) { throw new ToolExecutionError(`Invalid input arguments for tool '${this.name}': ${error instanceof Error ? error.message : 'Unknown validation error'}`, this.name, ErrorCode.TOOL_INVALID_ARGUMENTS, { additionalInfo: { validationError: error, providedArgs: args } }, error instanceof Error ? error : undefined); } } /** * Execute tool with performance monitoring and timeout */ async executeWithMonitoring(args, context) { const startMemory = process.memoryUsage(); // Set up timeout const timeoutPromise = new Promise((_, reject) => { setTimeout(() => { reject(new ToolExecutionError(`Tool '${this.name}' execution timed out after ${this.maxExecutionTime}ms`, this.name, ErrorCode.TOOL_TIMEOUT, { additionalInfo: { timeout: this.maxExecutionTime } })); }, this.maxExecutionTime); }); try { // Execute with timeout const result = await Promise.race([ this.executeInternal(args, context), timeoutPromise ]); // Check memory usage const endMemory = process.memoryUsage(); const memoryUsed = endMemory.heapUsed - startMemory.heapUsed; if (memoryUsed > this.maxMemoryUsage) { logger.warn(`Tool '${this.name}' exceeded memory threshold`, { memoryUsed, maxMemoryUsage: this.maxMemoryUsage, toolName: this.name, }); } // Update metrics this.updateMetrics(context, true, memoryUsed); // Add metadata to result const executionTime = Date.now() - context.startTime.getTime(); return { ...result, metadata: { ...result.metadata, executionTime, memoryUsage: memoryUsed, }, }; } catch (error) { this.updateMetrics(context, false); throw error; } } /** * Pre-execution hook for setup and validation */ async preExecute(context) { logger.info(`Starting execution of tool '${this.name}'`, { toolName: this.name, requestId: context.requestId, arguments: this.sanitizeArgsForLogging(context.arguments), }); // Check if tool is in a healthy state await this.healthCheck(); } /** * Post-execution hook for cleanup and logging */ async postExecute(context, _result) { const executionTime = Date.now() - context.startTime.getTime(); logger.info(`Completed execution of tool '${this.name}'`, { toolName: this.name, requestId: context.requestId, executionTime, success: true, }); } /** * Handle execution errors with proper context and recovery */ async handleExecutionError(error, context) { const executionTime = Date.now() - context.startTime.getTime(); const errorContext = { toolName: this.name, additionalInfo: { requestId: context.requestId, executionTime, arguments: this.sanitizeArgsForLogging(context.arguments), }, }; // Use global error handler for consistent error processing try { await globalErrorHandler.handleError(error, errorContext); } catch (handledError) { // Log the final error state logger.error(`Tool '${this.name}' execution failed`, { toolName: this.name, requestId: context.requestId, executionTime, error: handledError instanceof SuperAugmentError ? handledError.toJSON() : handledError, }); throw handledError; } } /** * Health check to ensure tool is ready for execution */ async healthCheck() { // Check if configuration is available if (!this.configManager) { throw new ToolExecutionError(`Tool '${this.name}' is not properly configured`, this.name, ErrorCode.INVALID_CONFIGURATION); } // Subclasses can override this for specific health checks } /** * Update tool execution metrics */ updateMetrics(context, success, memoryUsed = 0) { const executionTime = Date.now() - context.startTime.getTime(); this.metrics.totalExecutions++; this.metrics.totalExecutionTime += executionTime; this.metrics.averageExecutionTime = this.metrics.totalExecutionTime / this.metrics.totalExecutions; this.metrics.lastExecutionTime = new Date(); if (success) { this.metrics.successfulExecutions++; } else { this.metrics.failedExecutions++; } if (memoryUsed > this.metrics.peakMemoryUsage) { this.metrics.peakMemoryUsage = memoryUsed; } } /** * Sanitize arguments for logging (remove sensitive data) */ sanitizeArgsForLogging(args) { const sensitiveKeys = ['password', 'token', 'key', 'secret', 'auth']; const sanitized = { ...args }; for (const key of Object.keys(sanitized)) { if (sensitiveKeys.some(sensitive => key.toLowerCase().includes(sensitive))) { sanitized[key] = '[REDACTED]'; } } return sanitized; } /** * Generate unique request ID for tracking */ generateRequestId() { return `${this.name}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; } /** * Get tool execution metrics */ getMetrics() { return { ...this.metrics }; } /** * Reset tool metrics */ resetMetrics() { this.metrics = { totalExecutions: 0, successfulExecutions: 0, failedExecutions: 0, averageExecutionTime: 0, totalExecutionTime: 0, peakMemoryUsage: 0, }; } /** * Get tool health status */ async getHealthStatus() { try { await this.healthCheck(); return { healthy: true, metrics: this.getMetrics(), }; } catch (error) { return { healthy: false, metrics: this.getMetrics(), lastError: error instanceof Error ? error.message : 'Unknown error', }; } } /** * Utility method for creating standardized tool responses */ createResponse(text, additionalContent = [], metadata = {}) { return { content: [ { type: 'text', text, }, ...additionalContent, ], metadata: { executionTime: 0, ...metadata, }, }; } /** * Utility method for creating error responses */ createErrorResponse(error, includeDetails = false) { const userMessage = error.getUserMessage(); const content = [ { type: 'text', text: `❌ ${userMessage}`, }, ]; if (includeDetails && error.context.additionalInfo) { content.push({ type: 'text', text: `\n**Error Details:**\n${JSON.stringify(error.context.additionalInfo, null, 2)}`, }); } return { content, metadata: { executionTime: 0, warnings: [`Error: ${error.code} - ${error.severity}`], }, }; } } //# sourceMappingURL=BaseTool.js.map