UNPKG

@spaik/mcp-server-roi

Version:

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

619 lines 25.6 kB
import { z } from 'zod'; import { createLogger } from '../utils/logger.js'; /** * Predictive Analytics Service * * Provides advanced predictive capabilities including success probability, * timeline predictions, risk correlation, and outcome modeling. */ // Prediction schemas export const SuccessProbabilitySchema = z.object({ overall: z.number().min(0).max(1), factors: z.array(z.object({ name: z.string(), weight: z.number(), score: z.number().min(0).max(1), impact: z.enum(['positive', 'negative']) })), confidence_interval: z.tuple([z.number(), z.number()]), key_drivers: z.array(z.string()) }); export const TimelinePredictionSchema = z.object({ expected_completion: z.string().datetime(), confidence: z.number().min(0).max(1), milestones: z.array(z.object({ name: z.string(), expected_date: z.string().datetime(), probability: z.number().min(0).max(1), dependencies: z.array(z.string()) })), critical_path: z.array(z.string()), buffer_recommendation: z.number() // Days }); export const RiskCorrelationSchema = z.object({ correlations: z.array(z.object({ factor1: z.string(), factor2: z.string(), correlation: z.number().min(-1).max(1), significance: z.number().min(0).max(1) })), risk_clusters: z.array(z.object({ name: z.string(), factors: z.array(z.string()), combined_impact: z.enum(['low', 'medium', 'high', 'critical']) })), mitigation_priorities: z.array(z.string()) }); export const OutcomePredictionSchema = z.object({ scenarios: z.array(z.object({ name: z.string(), probability: z.number().min(0).max(1), roi: z.number(), payback_months: z.number(), key_assumptions: z.array(z.string()) })), most_likely: z.string(), variance_drivers: z.array(z.object({ factor: z.string(), sensitivity: z.number(), controllable: z.boolean() })) }); export class PredictiveAnalytics { logger = createLogger({ component: 'PredictiveAnalytics' }); // Historical success patterns SUCCESS_PATTERNS = { high_success: { quick_payback: { threshold: 12, weight: 0.25 }, moderate_complexity: { max_use_cases: 7, weight: 0.2 }, proven_technology: { categories: ['automation', 'analytics'], weight: 0.15 }, strong_sponsorship: { investment: 500000, weight: 0.2 }, clear_metrics: { confidence: 0.85, weight: 0.2 } } }; /** * Predict success probability for ROI projects */ async predictProjectSuccess(projectData, historicalData) { this.logger.debug('Predicting project success probability'); const factors = this.evaluateSuccessFactors(projectData); const overall = this.calculateOverallSuccess(factors); const confidence = this.calculateConfidenceInterval(overall, projectData); const keyDrivers = this.identifyKeyDrivers(factors); return { overall, factors, confidence_interval: confidence, key_drivers: keyDrivers }; } /** * Predict timeline with milestones and critical path */ async predictTimeline(projectData, useCases) { this.logger.debug('Predicting project timeline'); const milestones = this.generateMilestones(projectData, useCases); const criticalPath = this.identifyCriticalPath(milestones); const completion = this.calculateExpectedCompletion(milestones, criticalPath); const buffer = this.recommendBuffer(projectData, milestones); return { expected_completion: completion.date, confidence: completion.confidence, milestones, critical_path: criticalPath, buffer_recommendation: buffer }; } /** * Analyze risk correlations and clusters */ async analyzeRiskCorrelations(risks, projectContext) { this.logger.debug('Analyzing risk correlations'); const correlations = this.calculateRiskCorrelations(risks); const clusters = this.identifyRiskClusters(correlations, risks); const priorities = this.prioritizeMitigations(clusters, correlations); return { correlations, risk_clusters: clusters, mitigation_priorities: priorities }; } /** * Predict project outcomes with scenarios */ async predictOutcomes(projectData, marketConditions) { this.logger.debug('Predicting project outcomes'); const scenarios = this.generateScenarios(projectData, marketConditions); const mostLikely = this.identifyMostLikelyScenario(scenarios, projectData); const varianceDrivers = this.analyzeVarianceDrivers(scenarios, projectData); return { scenarios, most_likely: mostLikely, variance_drivers: varianceDrivers }; } /** * Advanced ML-based pattern matching */ async matchHistoricalPatterns(currentProject, historicalProjects) { this.logger.debug('Matching historical patterns'); const similarities = this.calculateProjectSimilarities(currentProject, historicalProjects); const topMatches = similarities .sort((a, b) => b.similarity_score - a.similarity_score) .slice(0, 5); const successIndicators = this.extractSuccessIndicators(topMatches); const warningSigns = this.extractWarningSigns(topMatches); return { similar_projects: topMatches, success_indicators: successIndicators, warning_signs: warningSigns }; } // Private helper methods evaluateSuccessFactors(projectData) { const factors = []; const patterns = this.SUCCESS_PATTERNS.high_success; // Quick payback factor if (projectData.summary?.payback_period_months <= patterns.quick_payback.threshold) { factors.push({ name: 'Quick Payback Period', weight: patterns.quick_payback.weight, score: 0.9, impact: 'positive' }); } else { factors.push({ name: 'Extended Payback Period', weight: patterns.quick_payback.weight, score: 0.4, impact: 'negative' }); } // Complexity factor const useCaseCount = projectData.use_cases?.length || 0; if (useCaseCount <= patterns.moderate_complexity.max_use_cases) { factors.push({ name: 'Manageable Complexity', weight: patterns.moderate_complexity.weight, score: 0.85, impact: 'positive' }); } else { factors.push({ name: 'High Complexity', weight: patterns.moderate_complexity.weight, score: 0.3, impact: 'negative' }); } // Technology maturity factor const hasProvenTech = projectData.use_cases?.some((uc) => patterns.proven_technology.categories.includes(uc.category)); if (hasProvenTech) { factors.push({ name: 'Proven Technology', weight: patterns.proven_technology.weight, score: 0.8, impact: 'positive' }); } // Investment level factor if (projectData.summary?.total_investment >= patterns.strong_sponsorship.investment) { factors.push({ name: 'Strong Investment Commitment', weight: patterns.strong_sponsorship.weight, score: 0.75, impact: 'positive' }); } // Confidence factor if (projectData.metadata?.confidence_level >= patterns.clear_metrics.confidence) { factors.push({ name: 'High Confidence Data', weight: patterns.clear_metrics.weight, score: 0.9, impact: 'positive' }); } return factors; } calculateOverallSuccess(factors) { const weightedSum = factors.reduce((sum, factor) => { const adjustedScore = factor.impact === 'positive' ? factor.score : (1 - factor.score); return sum + (adjustedScore * factor.weight); }, 0); const totalWeight = factors.reduce((sum, factor) => sum + factor.weight, 0); return Math.min(0.95, Math.max(0.05, weightedSum / totalWeight)); } calculateConfidenceInterval(mean, projectData) { // Simplified confidence interval calculation const dataQuality = projectData.metadata?.confidence_level || 0.8; const sampleSize = projectData.use_cases?.length || 1; // Larger projects have more uncertainty const uncertainty = 0.1 + (0.05 * Math.log10(sampleSize)); const adjustedUncertainty = uncertainty * (2 - dataQuality); return [ Math.max(0, mean - adjustedUncertainty), Math.min(1, mean + adjustedUncertainty) ]; } identifyKeyDrivers(factors) { return factors .filter(f => f.weight >= 0.2) .sort((a, b) => b.weight - a.weight) .map(f => f.name); } generateMilestones(projectData, useCases) { const milestones = []; const startDate = new Date(); const totalMonths = projectData.timeline_months || 12; // Planning phase milestones.push({ name: 'Project Kickoff & Planning', expected_date: new Date(startDate.getTime() + 30 * 24 * 60 * 60 * 1000).toISOString(), probability: 0.95, dependencies: [] }); // Implementation phases const implementationMonths = totalMonths * 0.6; const useCasesPerPhase = Math.ceil(useCases.length / 3); for (let phase = 0; phase < 3; phase++) { const phaseUseCases = useCases.slice(phase * useCasesPerPhase, (phase + 1) * useCasesPerPhase); if (phaseUseCases.length > 0) { const phaseDate = new Date(startDate.getTime() + (30 + (implementationMonths / 3 * phase * 30)) * 24 * 60 * 60 * 1000); milestones.push({ name: `Phase ${phase + 1}: ${phaseUseCases.map(uc => uc.name).join(', ')}`, expected_date: phaseDate.toISOString(), probability: 0.85 - (phase * 0.05), dependencies: phase > 0 ? [`Phase ${phase}: `] : ['Project Kickoff & Planning'] }); } } // Go-live milestone milestones.push({ name: 'Full Production Deployment', expected_date: new Date(startDate.getTime() + (totalMonths * 30 * 24 * 60 * 60 * 1000)).toISOString(), probability: 0.75, dependencies: milestones.slice(-3).map(m => m.name) }); return milestones; } identifyCriticalPath(milestones) { // Simplified critical path - in reality would use CPM algorithm return milestones .filter(m => m.dependencies.length > 0 || m.name.includes('Phase')) .map(m => m.name); } calculateExpectedCompletion(milestones, criticalPath) { const lastMilestone = milestones[milestones.length - 1]; const criticalMilestones = milestones.filter(m => criticalPath.includes(m.name)); // Calculate confidence based on critical path probabilities const pathConfidence = criticalMilestones.reduce((conf, m) => conf * m.probability, 1); return { date: lastMilestone.expected_date, confidence: pathConfidence }; } recommendBuffer(projectData, milestones) { const complexity = projectData.use_cases?.length || 1; const avgProbability = milestones.reduce((sum, m) => sum + m.probability, 0) / milestones.length; // Base buffer of 10% of timeline const baseBuffer = (projectData.timeline_months || 12) * 0.1 * 30; // Adjust for complexity and uncertainty const complexityFactor = Math.log10(complexity + 1); const uncertaintyFactor = 2 - avgProbability; return Math.round(baseBuffer * complexityFactor * uncertaintyFactor); } calculateRiskCorrelations(risks) { const correlations = []; // Common correlation patterns const correlationPatterns = { 'Implementation Complexity': { 'User Adoption': 0.7, 'Timeline Slippage': 0.8, 'Budget Overrun': 0.6 }, 'Technology Maturity': { 'Integration Risk': 0.75, 'Performance Issues': 0.65 }, 'Resource Availability': { 'Timeline Slippage': 0.85, 'Quality Issues': 0.6 } }; // Calculate correlations based on patterns for (let i = 0; i < risks.length; i++) { for (let j = i + 1; j < risks.length; j++) { const risk1 = risks[i].factor; const risk2 = risks[j].factor; let correlation = 0; let found = false; // Check predefined patterns for (const [pattern, corrs] of Object.entries(correlationPatterns)) { if (risk1.includes(pattern) && corrs[risk2]) { correlation = corrs[risk2]; found = true; break; } else if (risk2.includes(pattern) && corrs[risk1]) { correlation = corrs[risk1]; found = true; break; } } if (found) { correlations.push({ factor1: risk1, factor2: risk2, correlation, significance: correlation > 0.7 ? 0.95 : 0.8 }); } } } return correlations; } identifyRiskClusters(correlations, risks) { const clusters = []; // Group highly correlated risks const highCorrelations = correlations.filter(c => c.correlation > 0.6); const riskGroups = new Map(); highCorrelations.forEach(corr => { let group1 = Array.from(riskGroups.entries()).find(([_, risks]) => risks.has(corr.factor1)); let group2 = Array.from(riskGroups.entries()).find(([_, risks]) => risks.has(corr.factor2)); if (group1 && group2 && group1[0] !== group2[0]) { // Merge groups group2[1].forEach(risk => group1[1].add(risk)); riskGroups.delete(group2[0]); } else if (group1) { group1[1].add(corr.factor2); } else if (group2) { group2[1].add(corr.factor1); } else { // Create new group const groupName = `Risk Cluster ${riskGroups.size + 1}`; riskGroups.set(groupName, new Set([corr.factor1, corr.factor2])); } }); // Convert to clusters with impact assessment riskGroups.forEach((factors, name) => { const clusterRisks = risks.filter(r => factors.has(r.factor)); const maxImpact = Math.max(...clusterRisks.map(r => r.impact === 'critical' ? 4 : r.impact === 'high' ? 3 : r.impact === 'medium' ? 2 : 1)); clusters.push({ name, factors: Array.from(factors), combined_impact: maxImpact === 4 ? 'critical' : maxImpact === 3 ? 'high' : maxImpact === 2 ? 'medium' : 'low' }); }); return clusters; } prioritizeMitigations(clusters, correlations) { // Prioritize based on cluster impact and correlation strength const priorities = clusters .map(cluster => ({ name: cluster.factors[0], // Primary factor score: (cluster.combined_impact === 'critical' ? 4 : cluster.combined_impact === 'high' ? 3 : cluster.combined_impact === 'medium' ? 2 : 1) * cluster.factors.length })) .sort((a, b) => b.score - a.score) .map(p => p.name); return priorities; } generateScenarios(projectData, marketConditions) { const baseROI = projectData.summary?.expected_roi || 100; const basePayback = projectData.summary?.payback_period_months || 18; const scenarios = [ { name: 'Conservative', probability: 0.25, roi: baseROI * 0.7, payback_months: basePayback * 1.4, key_assumptions: [ 'Slower adoption than expected', 'Higher implementation costs', 'Limited initial scope' ] }, { name: 'Expected', probability: 0.5, roi: baseROI, payback_months: basePayback, key_assumptions: [ 'Normal adoption curve', 'On-budget implementation', 'Planned scope delivery' ] }, { name: 'Optimistic', probability: 0.2, roi: baseROI * 1.3, payback_months: basePayback * 0.8, key_assumptions: [ 'Rapid user adoption', 'Efficiency gains in implementation', 'Additional use cases identified' ] }, { name: 'Best Case', probability: 0.05, roi: baseROI * 1.6, payback_months: basePayback * 0.6, key_assumptions: [ 'Viral adoption across organization', 'Significant cost savings discovered', 'Network effects realized' ] } ]; // Adjust for market conditions if provided if (marketConditions?.sentiment === 'bullish') { scenarios.forEach(s => { s.probability = s.name === 'Optimistic' || s.name === 'Best Case' ? s.probability * 1.2 : s.probability * 0.8; }); } // Normalize probabilities const totalProb = scenarios.reduce((sum, s) => sum + s.probability, 0); scenarios.forEach(s => s.probability = s.probability / totalProb); return scenarios; } identifyMostLikelyScenario(scenarios, projectData) { // Adjust probabilities based on project characteristics const adjustedScenarios = scenarios.map(scenario => { let adjustedProb = scenario.probability; // High confidence data favors expected/optimistic if (projectData.metadata?.confidence_level > 0.9) { if (scenario.name === 'Expected' || scenario.name === 'Optimistic') { adjustedProb *= 1.2; } } // Quick payback favors optimistic scenarios if (projectData.summary?.payback_period_months < 12) { if (scenario.name === 'Optimistic' || scenario.name === 'Best Case') { adjustedProb *= 1.15; } } return { ...scenario, adjustedProb }; }); // Find scenario with highest adjusted probability const mostLikely = adjustedScenarios.reduce((prev, curr) => curr.adjustedProb > prev.adjustedProb ? curr : prev); return mostLikely.name; } analyzeVarianceDrivers(scenarios, projectData) { const drivers = []; // Calculate ROI variance const roiValues = scenarios.map(s => s.roi); const roiVariance = this.calculateVariance(roiValues); // Identify main variance drivers const potentialDrivers = [ { factor: 'Adoption Rate', sensitivity: 0.35, controllable: true, relevance: projectData.use_cases?.some((uc) => uc.category === 'customer_service' || uc.category === 'hr_recruiting') }, { factor: 'Implementation Efficiency', sensitivity: 0.25, controllable: true, relevance: true }, { factor: 'Market Conditions', sensitivity: 0.2, controllable: false, relevance: projectData.summary?.total_investment > 1000000 }, { factor: 'Technology Performance', sensitivity: 0.3, controllable: true, relevance: projectData.use_cases?.some((uc) => uc.category === 'analytics' || uc.name.includes('ML')) }, { factor: 'Regulatory Changes', sensitivity: 0.15, controllable: false, relevance: ['financial_services', 'healthcare'].includes(projectData.project?.industry) } ]; // Filter and sort by relevance and sensitivity return potentialDrivers .filter(d => d.relevance) .sort((a, b) => b.sensitivity - a.sensitivity) .map(({ factor, sensitivity, controllable }) => ({ factor, sensitivity, controllable })); } calculateProjectSimilarities(current, historical) { return historical.map(hist => { const similarities = []; let score = 0; // Industry match if (current.project?.industry === hist.project?.industry) { score += 0.3; similarities.push(`Same industry: ${current.project.industry}`); } // Investment range match (within 50%) const currentInv = current.summary?.total_investment || 0; const histInv = hist.summary?.total_investment || 0; if (Math.abs(currentInv - histInv) / currentInv < 0.5) { score += 0.2; similarities.push('Similar investment scale'); } // Use case category overlap const currentCategories = new Set(current.use_cases?.map((uc) => uc.category) || []); const histCategories = new Set(hist.use_cases?.map((uc) => uc.category) || []); const overlap = Array.from(currentCategories).filter(c => histCategories.has(c)).length; const overlapRatio = overlap / Math.max(currentCategories.size, histCategories.size); score += overlapRatio * 0.25; if (overlap > 0) { similarities.push(`${overlap} common use case categories`); } // Complexity similarity const currentComplexity = current.use_cases?.length || 0; const histComplexity = hist.use_cases?.length || 0; if (Math.abs(currentComplexity - histComplexity) <= 2) { score += 0.15; similarities.push('Similar project complexity'); } // Timeline similarity const currentTimeline = current.timeline_months || 12; const histTimeline = hist.timeline_months || 12; if (Math.abs(currentTimeline - histTimeline) <= 3) { score += 0.1; similarities.push('Similar implementation timeline'); } return { project_id: hist.project_id, similarity_score: score, outcome: hist.outcome || 'Unknown', key_similarities: similarities }; }); } extractSuccessIndicators(matches) { const successfulProjects = matches.filter(m => m.outcome === 'Successful' || m.outcome === 'Exceeded Expectations'); const indicators = new Set(); successfulProjects.forEach(project => { project.key_similarities.forEach((sim) => { indicators.add(sim); }); }); return Array.from(indicators); } extractWarningSigns(matches) { const failedProjects = matches.filter(m => m.outcome === 'Failed' || m.outcome === 'Underperformed'); const warnings = new Set(); failedProjects.forEach(project => { project.key_similarities.forEach((sim) => { warnings.add(`Risk factor: ${sim}`); }); }); return Array.from(warnings); } calculateVariance(values) { const mean = values.reduce((sum, val) => sum + val, 0) / values.length; const squaredDiffs = values.map(val => Math.pow(val - mean, 2)); return squaredDiffs.reduce((sum, diff) => sum + diff, 0) / values.length; } } // Export singleton instance export const predictiveAnalytics = new PredictiveAnalytics(); //# sourceMappingURL=predictive-analytics.js.map