UNPKG

@spaik/mcp-server-roi

Version:

MCP server for AI ROI prediction and tracking with Monte Carlo simulations

224 lines 9.82 kB
import { mcpDb } from '../db/supabase.js'; import { structuredLogger, PerformanceTracker, createToolLogger } from '../utils/structured-logger.js'; import { ValidationError } from '../utils/errors.js'; import { DutchBenchmarkValidator } from '../services/dutch-benchmark-validator.js'; // Enhanced Transaction manager with correlation IDs class TransactionManager { logger; rollbackStack = []; constructor(correlationId) { this.logger = createToolLogger('TransactionManager', correlationId); } async executeWithRollback(operation, rollbackOperation, operationName) { const tracker = new PerformanceTracker(`transaction.${operationName}`, this.logger.correlationId); try { this.logger.debug(`Executing operation: ${operationName}`); const result = await operation(); this.rollbackStack.push(rollbackOperation); tracker.end(true); return result; } catch (error) { tracker.error(error); structuredLogger.logStructuredError(error, { component: 'TransactionManager', operation: operationName, correlationId: this.logger.correlationId }); throw error; } } async rollbackAll() { const tracker = new PerformanceTracker('transaction.rollback', this.logger.correlationId); this.logger.info('Starting rollback', { operations: this.rollbackStack.length }); // Execute rollbacks in reverse order while (this.rollbackStack.length > 0) { const rollback = this.rollbackStack.pop(); try { await rollback(); tracker.checkpoint(`Rollback ${this.rollbackStack.length + 1} completed`); } catch (error) { this.logger.error('Rollback operation failed', error); // Continue with other rollbacks even if one fails } } tracker.end(true); } } // Enhanced Retry manager with correlation IDs class RetryManager { logger; constructor(correlationId) { this.logger = createToolLogger('RetryManager', correlationId); } async retry(operation, options = { maxAttempts: 3, backoffMs: 1000, operationName: 'operation' }) { const { maxAttempts = 3, backoffMs = 1000, operationName } = options; for (let attempt = 1; attempt <= maxAttempts; attempt++) { const tracker = new PerformanceTracker(`retry.${operationName}.attempt${attempt}`, this.logger.correlationId); try { this.logger.debug(`Attempt ${attempt} of ${maxAttempts} for ${operationName}`); const result = await operation(); tracker.end(true); return result; } catch (error) { tracker.error(error); if (attempt === maxAttempts) { this.logger.error(`All ${maxAttempts} attempts failed for ${operationName}`, error); throw error; } const delay = backoffMs * Math.pow(2, attempt - 1); // Exponential backoff this.logger.warn(`Attempt ${attempt} failed, retrying in ${delay}ms`, { error: error.message, nextDelay: delay }); await new Promise(resolve => setTimeout(resolve, delay)); } } throw new Error('Retry logic failed unexpectedly'); } } // Main predict ROI function with correlation ID support export async function predictROIWithCorrelation(params, correlationId) { // Create main logger with correlation ID const logger = createToolLogger('predict-roi', correlationId); const tracker = new PerformanceTracker('predict-roi.total', logger.correlationId); logger.info('Starting ROI prediction', { hasProject: !!params.project, useCaseCount: params.use_cases?.length || 0, enableBenchmarks: params.enable_benchmarks }); const transaction = new TransactionManager(logger.correlationId); const retryManager = new RetryManager(logger.correlationId); try { // Step 1: Validate input tracker.checkpoint('validation-start'); const validatedParams = await validateInput(params, logger); tracker.checkpoint('validation-complete'); // Step 2: Create project tracker.checkpoint('project-creation-start'); const projectId = await transaction.executeWithRollback(async () => { const result = await retryManager.retry(() => createProject(validatedParams, logger), { operationName: 'create-project' }); return result; }, async () => { logger.debug('Rolling back project creation', { projectId }); await mcpDb.from('projects').delete().eq('id', projectId); }, 'create-project'); tracker.checkpoint('project-creation-complete'); // Step 3: Create use cases tracker.checkpoint('use-cases-creation-start'); const useCaseResults = await Promise.all(validatedParams.use_cases.map((useCase, index) => transaction.executeWithRollback(async () => { const childLogger = logger.child(`use-case-${index}`, { useCaseName: useCase.name }); return createUseCase(projectId, useCase, childLogger); }, async () => { logger.debug('Rolling back use case creation', { useCaseName: useCase.name }); }, `create-use-case-${index}`))); tracker.checkpoint('use-cases-creation-complete'); // Step 4: Run calculations tracker.checkpoint('calculations-start'); const calculations = await performCalculations(projectId, validatedParams, useCaseResults, logger); tracker.checkpoint('calculations-complete'); // Step 5: Apply Dutch validation if enabled if (validatedParams.enable_benchmarks) { tracker.checkpoint('dutch-validation-start'); const validationLogger = logger.child('dutch-validation'); const validator = new DutchBenchmarkValidator(process.env.PERPLEXITY_API_KEY); const validation = await validator.validateProjectInputs({ industry: validatedParams.project.industry, useCases: validatedParams.use_cases, implementationCosts: validatedParams.implementation_costs, timelineMonths: validatedParams.timeline_months }); validationLogger.info('Dutch validation completed', { isValid: validation.isValid, issueCount: validation.validationIssues.length }); tracker.checkpoint('dutch-validation-complete'); } // Success! tracker.end(true); logger.info('ROI prediction completed successfully', { projectId, totalInvestment: calculations.financial_metrics.total_investment, expectedROI: calculations.financial_metrics.expected_roi, paybackPeriod: calculations.financial_metrics.payback_period_months }); return calculations; } catch (error) { tracker.error(error); // Log structured error with full context structuredLogger.logStructuredError(error, { component: 'predict-roi', operation: 'predict-roi', correlationId: logger.correlationId, params: { projectName: params.project?.name, useCaseCount: params.use_cases?.length } }); // Attempt rollback logger.error('ROI prediction failed, initiating rollback', error); await transaction.rollbackAll(); throw error; } } // Helper functions with logger support async function validateInput(params, logger) { const validationLogger = logger.child('validation'); validationLogger.debug('Validating input parameters'); try { // Validation logic here return params; // Simplified for example } catch (error) { validationLogger.error('Input validation failed', error); throw new ValidationError('Invalid input parameters', { params }); } } async function createProject(params, logger) { const dbLogger = structuredLogger.createDb('insert', 'projects', logger.correlationId); dbLogger.debug('Creating project', { projectName: params.project.name }); // Database operation here return 'project-id'; // Simplified for example } async function createUseCase(projectId, useCase, logger) { logger.debug('Creating use case', { projectId, useCaseName: useCase.name }); // Database operation here return { id: 'use-case-id', ...useCase }; // Simplified for example } async function performCalculations(projectId, params, useCaseResults, logger) { const calcLogger = logger.child('calculations'); const tracker = new PerformanceTracker('roi-calculations', logger.correlationId); calcLogger.info('Starting ROI calculations', { useCaseCount: useCaseResults.length }); try { // Calculation logic here tracker.checkpoint('npv-calculation'); tracker.checkpoint('irr-calculation'); tracker.checkpoint('payback-calculation'); tracker.checkpoint('monte-carlo-simulation'); tracker.end(true); return { financial_metrics: { total_investment: 100000, expected_roi: 150, payback_period_months: 18 } }; } catch (error) { tracker.error(error); throw error; } } //# sourceMappingURL=predict-roi-with-correlation.js.map