@spaik/mcp-server-roi
Version:
MCP server for AI ROI prediction and tracking with Monte Carlo simulations
224 lines • 9.82 kB
JavaScript
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