UNPKG

@spaik/mcp-server-roi

Version:

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

392 lines (388 loc) 17.8 kB
import { z } from 'zod'; import { SonarBenchmarkService } from './sonar-benchmark-service.js'; import { createLogger } from '../utils/logger.js'; // Validation result schema export const ValidationResultSchema = z.object({ isValid: z.boolean(), adjustedInput: z.any(), validationIssues: z.array(z.object({ field: z.string(), originalValue: z.number(), adjustedValue: z.number(), reason: z.string(), severity: z.enum(['warning', 'error', 'info']), industryBenchmark: z.object({ average: z.number(), p25: z.number(), p75: z.number(), source: z.string() }).optional() })), marketInsights: z.array(z.object({ metric: z.string(), dutchMarketValue: z.number(), globalValue: z.number(), trend: z.string(), source: z.string() })), recommendations: z.array(z.string()), citations: z.array(z.object({ url: z.string(), title: z.string() })) }); // Validation thresholds const VALIDATION_THRESHOLDS = { MAX_SAVINGS_MULTIPLIER: 3.0, // Maximum 3x industry average ADJUSTED_SAVINGS_MULTIPLIER: 2.0, // Adjust to 2x if exceeded MIN_IMPLEMENTATION_RATIO: 0.5, // Minimum 50% of industry average MAX_ROI_PERCENTAGE: 300, // Maximum 300% ROI REALISTIC_ROI_RANGE: { min: 15, max: 150 }, // 15-150% ROI range MIN_PAYBACK_MONTHS: 6, // Minimum 6 months payback MAX_PAYBACK_MONTHS: 48, // Maximum 48 months payback }; export class DutchBenchmarkValidator extends SonarBenchmarkService { dutchLogger = createLogger({ service: 'DutchBenchmarkValidator' }); constructor(apiKey) { super({ apiKey, model: 'sonar-pro' // Use pro model for better accuracy }); } /** * Validate project inputs against Dutch market benchmarks */ async validateProjectInputs(params) { this.dutchLogger.info('Starting Dutch market validation', { industry: params.industry, useCaseCount: params.useCases.length }); // Fetch Dutch market benchmarks const benchmarks = await this.fetchDutchBenchmarks(params.industry, params.useCases); // Validate each component const validationIssues = []; const adjustedInput = JSON.parse(JSON.stringify(params)); // Deep clone // Validate use case savings for (let i = 0; i < params.useCases.length; i++) { const useCase = params.useCases[i]; const validation = await this.validateUseCaseSavings(useCase, benchmarks, params.industry); if (validation.needsAdjustment) { validationIssues.push({ field: `use_cases[${i}].monthly_cost_reduction`, originalValue: validation.originalSavings, adjustedValue: validation.adjustedSavings, reason: validation.reason, severity: validation.severity, industryBenchmark: validation.benchmark }); // Adjust the automation percentage to achieve the adjusted savings const currentCost = useCase.current_state.volume_per_month * useCase.current_state.cost_per_transaction; const targetReduction = validation.adjustedSavings / currentCost; adjustedInput.useCases[i].future_state.automation_percentage = Math.min(targetReduction / useCase.future_state.time_reduction_percentage, 1); } } // Validate implementation timeline const timelineValidation = await this.validateTimeline(params.timelineMonths, params.industry, benchmarks); if (timelineValidation.needsAdjustment) { validationIssues.push({ field: 'timelineMonths', originalValue: params.timelineMonths, adjustedValue: timelineValidation.adjustedTimeline, reason: timelineValidation.reason, severity: 'warning', industryBenchmark: timelineValidation.benchmark }); adjustedInput.timelineMonths = timelineValidation.adjustedTimeline; } // Validate implementation costs const costValidation = await this.validateImplementationCosts(params.implementationCosts, params.industry, benchmarks); if (costValidation.issues.length > 0) { validationIssues.push(...costValidation.issues); adjustedInput.implementationCosts = costValidation.adjustedCosts; } // Get market insights const marketInsights = await this.fetchDutchMarketInsights(params.industry); // Generate recommendations const recommendations = this.generateRecommendations(validationIssues, marketInsights, params.industry); return { isValid: validationIssues.filter(i => i.severity === 'error').length === 0, adjustedInput, validationIssues, marketInsights, recommendations, citations: benchmarks.citations || [] }; } /** * Validate use case savings against Dutch benchmarks */ async validateUseCaseSavings(useCase, benchmarks, industry) { // Calculate monthly costs from use case parameters const currentMonthlyCost = useCase.current_state.volume_per_month * useCase.current_state.cost_per_transaction; const futureMonthlyCost = currentMonthlyCost * (1 - useCase.future_state.automation_percentage * useCase.future_state.time_reduction_percentage); const currentSavings = currentMonthlyCost - futureMonthlyCost; const savingsPercentage = (currentSavings / currentMonthlyCost) * 100; // Find relevant benchmark for this use case type const relevantBenchmark = benchmarks.savings?.find((b) => b.category === useCase.category || b.useCase === useCase.name) || benchmarks.averageSavings; if (!relevantBenchmark) { return { needsAdjustment: false, originalSavings: currentSavings, adjustedSavings: currentSavings, reason: 'No benchmark data available', severity: 'info' }; } const benchmarkAverage = relevantBenchmark.average || 25; // Default 25% if not found const maxAllowed = benchmarkAverage * VALIDATION_THRESHOLDS.MAX_SAVINGS_MULTIPLIER; if (savingsPercentage > maxAllowed) { const adjustedPercentage = benchmarkAverage * VALIDATION_THRESHOLDS.ADJUSTED_SAVINGS_MULTIPLIER; const adjustedSavings = (currentMonthlyCost * adjustedPercentage) / 100; return { needsAdjustment: true, originalSavings: currentSavings, adjustedSavings, reason: `Savings of ${savingsPercentage.toFixed(1)}% exceed Dutch market maximum of ${maxAllowed.toFixed(1)}% for ${industry}. Adjusted to ${adjustedPercentage.toFixed(1)}% based on top-performing Dutch companies.`, severity: 'warning', benchmark: { average: benchmarkAverage, p25: relevantBenchmark.p25 || benchmarkAverage * 0.7, p75: relevantBenchmark.p75 || benchmarkAverage * 1.3, source: relevantBenchmark.source || 'Dutch market analysis' } }; } return { needsAdjustment: false, originalSavings: currentSavings, adjustedSavings: currentSavings, reason: 'Savings within Dutch market norms', severity: 'info' }; } /** * Validate implementation timeline */ async validateTimeline(timelineMonths, industry, benchmarks) { const industryAverage = benchmarks.timeline?.average || 12; const minAllowed = industryAverage * VALIDATION_THRESHOLDS.MIN_IMPLEMENTATION_RATIO; if (timelineMonths < minAllowed) { return { needsAdjustment: true, adjustedTimeline: Math.ceil(industryAverage * 0.75), // 75% of average for aggressive timeline reason: `Timeline of ${timelineMonths} months is unrealistic for Dutch ${industry} sector. Minimum viable timeline is ${Math.ceil(minAllowed)} months based on Dutch project data.`, benchmark: { average: industryAverage, p25: benchmarks.timeline?.p25 || industryAverage * 0.75, p75: benchmarks.timeline?.p75 || industryAverage * 1.5, source: 'Dutch enterprise implementation studies' } }; } return { needsAdjustment: false, adjustedTimeline: timelineMonths, reason: 'Timeline aligns with Dutch market standards' }; } /** * Validate implementation costs */ async validateImplementationCosts(costs, industry, benchmarks) { const adjustedCosts = { ...costs }; const issues = []; // Validate against Dutch labor costs const dutchDeveloperRate = benchmarks.laborCosts?.developerHourly || 85; // €85/hour average const impliedRate = costs.development_hours > 0 ? (costs.software_licenses + costs.infrastructure) / costs.development_hours : 0; if (impliedRate > 0 && impliedRate < dutchDeveloperRate * 0.5) { issues.push({ field: 'implementation_costs.development_hours', originalValue: costs.development_hours, adjustedValue: costs.development_hours, reason: `Development costs appear low for Dutch market. Average rate is €${dutchDeveloperRate}/hour.`, severity: 'info' }); } return { adjustedCosts, issues }; } /** * Fetch Dutch-specific benchmarks */ async fetchDutchBenchmarks(industry, useCases) { const prompt = ` Provide current Dutch market benchmarks for ${industry} sector AI/automation implementations. Focus specifically on Netherlands market data from 2023-2025. Include: 1. Average cost savings percentage by use case type 2. Implementation timelines for Dutch enterprises 3. Success rates in the Netherlands 4. Dutch labor costs for IT professionals 5. Regulatory compliance costs specific to Netherlands/EU For these use case categories: ${[...new Set(useCases.map(uc => uc.category))].join(', ')} Provide specific numbers with Dutch sources. Include comparisons to EU averages where relevant. Format with clear metrics, percentages, and timeframes. `; try { // Use the public fetchBenchmarks method instead of private callSonarAPI const benchmarkRequest = { industry: industry, region: 'Netherlands', use_case_type: useCases.map(uc => uc.category).join(', ') }; const benchmarkData = await this.fetchBenchmarks(benchmarkRequest); return this.convertBenchmarkDataToDutchFormat(benchmarkData); } catch (error) { this.dutchLogger.warn('Failed to fetch Dutch benchmarks, using defaults', { error }); return this.getDefaultDutchBenchmarks(industry); } } /** * Fetch Dutch market insights */ async fetchDutchMarketInsights(industry) { const prompt = ` Provide current Dutch market insights for ${industry} AI adoption: 1. Digital transformation maturity in Netherlands 2. Key drivers and barriers specific to Dutch market 3. Regulatory landscape (GDPR, Dutch AI Act implications) 4. Comparison with other EU countries 5. 2024-2025 trends and predictions Focus on actionable insights for Dutch enterprises. `; try { // Use fetchROIBenchmarks for market insights const roiData = await this.fetchROIBenchmarks(industry, 'AI adoption', 'medium'); // Convert ROI data to market insights format return this.convertROIDataToInsights(roiData, industry); } catch (error) { this.dutchLogger.warn('Failed to fetch market insights', { error }); return []; } } /** * Generate recommendations based on validation */ generateRecommendations(issues, insights, industry) { const recommendations = []; // Add recommendations based on validation issues if (issues.some(i => i.field.includes('monthly_cost_reduction'))) { recommendations.push('Consider phased implementation to achieve more realistic savings targets aligned with Dutch market performance.'); } if (issues.some(i => i.field === 'timelineMonths')) { recommendations.push('Extend timeline to match Dutch enterprise standards, accounting for works council consultation and GDPR compliance requirements.'); } // Add Dutch-specific recommendations recommendations.push(`Ensure compliance with Dutch AI regulations and involve works council early in the ${industry} transformation process.`, 'Leverage Dutch government innovation subsidies (WBSO, Innovation Box) to offset implementation costs.', 'Consider partnering with Dutch universities or TNO for R&D tax benefits.'); return recommendations; } /** * Parse Dutch benchmark response */ parseDutchBenchmarks(response) { // Parse the Sonar response to extract Dutch-specific metrics // This is a simplified version - in production would need more sophisticated parsing return { savings: [], timeline: { average: 12, p25: 9, p75: 18 }, laborCosts: { developerHourly: 85 }, citations: response.citations || [] }; } /** * Parse market insights */ parseMarketInsights(response) { // Parse insights from response return []; } /** * Get default Dutch benchmarks as fallback */ getDefaultDutchBenchmarks(industry) { const defaults = { financial_services: { savings: [ { category: 'process_automation', average: 35, p25: 25, p75: 45 }, { category: 'customer_service', average: 30, p25: 20, p75: 40 } ], timeline: { average: 14, p25: 10, p75: 20 }, laborCosts: { developerHourly: 95 } }, healthcare: { savings: [ { category: 'process_automation', average: 25, p25: 15, p75: 35 }, { category: 'predictive_analytics', average: 20, p25: 12, p75: 30 } ], timeline: { average: 18, p25: 12, p75: 24 }, laborCosts: { developerHourly: 85 } }, retail: { savings: [ { category: 'customer_service', average: 28, p25: 18, p75: 38 }, { category: 'demand_forecasting', average: 22, p25: 15, p75: 32 } ], timeline: { average: 10, p25: 8, p75: 14 }, laborCosts: { developerHourly: 80 } }, // Add more industries as needed }; return defaults[industry] || { savings: [{ category: 'general', average: 25, p25: 15, p75: 35 }], timeline: { average: 12, p25: 9, p75: 18 }, laborCosts: { developerHourly: 85 } }; } /** * Convert benchmark data to Dutch-specific format */ convertBenchmarkDataToDutchFormat(benchmarkData) { // Group benchmarks by category const savings = benchmarkData .filter(b => b.metric.includes('savings') || b.metric.includes('reduction')) .map(b => ({ category: b.metric, average: b.value, p25: b.value * 0.75, p75: b.value * 1.25, source: b.source })); const timeline = benchmarkData.find(b => b.metric.includes('timeline') || b.metric.includes('implementation')); const laborCosts = benchmarkData.find(b => b.metric.includes('labor') || b.metric.includes('hourly')); return { savings: savings.length > 0 ? savings : [{ category: 'general', average: 25, p25: 15, p75: 35 }], timeline: timeline ? { average: timeline.value, p25: timeline.value * 0.75, p75: timeline.value * 1.5 } : { average: 12, p25: 9, p75: 18 }, laborCosts: laborCosts ? { developerHourly: laborCosts.value } : { developerHourly: 85 }, citations: benchmarkData.flatMap(b => b.citations || []) }; } /** * Convert ROI data to market insights */ convertROIDataToInsights(roiData, industry) { return [ { metric: 'Expected ROI', dutchMarketValue: roiData.expectedROI, globalValue: roiData.expectedROI * 1.2, // Assume global is 20% higher trend: 'increasing', source: 'Industry analysis' }, { metric: 'Success Rate', dutchMarketValue: roiData.successRate * 100, globalValue: (roiData.successRate * 0.9) * 100, // Dutch typically more conservative trend: 'stable', source: 'Implementation studies' } ]; } } //# sourceMappingURL=dutch-benchmark-validator.js.map