@spaik/mcp-server-roi
Version:
MCP server for AI ROI prediction and tracking with Monte Carlo simulations
493 lines • 19.9 kB
JavaScript
import { z } from 'zod';
import { createLogger } from '../utils/logger.js';
// ML model input features
export const ProjectFeaturesSchema = z.object({
// Financial features
totalInvestment: z.number(),
expectedROI: z.number(),
paybackPeriodMonths: z.number(),
netPresentValue: z.number(),
// Project characteristics
useCaseCount: z.number(),
timelineMonths: z.number(),
implementationComplexity: z.number().min(0).max(10),
// Industry factors
industry: z.string(),
companySize: z.enum(['small', 'medium', 'large', 'enterprise']),
// Risk factors
technicalRisk: z.number().min(0).max(1),
organizationalRisk: z.number().min(0).max(1),
marketRisk: z.number().min(0).max(1),
// Historical data (if available)
similarProjectSuccessRate: z.number().min(0).max(1).optional(),
industryAverageROI: z.number().optional()
});
// ML prediction output
export const MLPredictionSchema = z.object({
successProbability: z.number().min(0).max(1),
confidenceInterval: z.object({
lower: z.number(),
upper: z.number()
}),
riskScore: z.number().min(0).max(10),
predictedActualROI: z.number(),
predictedDelayMonths: z.number().min(0),
keyRiskFactors: z.array(z.object({
factor: z.string(),
impact: z.enum(['low', 'medium', 'high']),
mitigation: z.string()
})),
synergies: z.array(z.object({
withProject: z.string(),
type: z.string(),
estimatedValue: z.number()
})).optional()
});
// Comparison result with ML insights
export const MLComparisonResultSchema = z.object({
projectId: z.string(),
baseMetrics: z.object({
roi: z.number(),
paybackPeriod: z.number(),
npv: z.number(),
totalInvestment: z.number()
}),
mlPredictions: MLPredictionSchema,
ranking: z.object({
overall: z.number(),
byROI: z.number(),
byRisk: z.number(),
bySpeed: z.number()
}),
recommendation: z.enum(['strongly_recommend', 'recommend', 'consider', 'reconsider', 'not_recommended']),
insights: z.array(z.string())
});
/**
* Machine Learning engine for advanced project comparison
* Uses ensemble methods for robust predictions
*/
export class MLComparisonEngine {
logger = createLogger({ service: 'MLComparisonEngine' });
// Simplified Random Forest implementation
forests = [];
treeCount = 100;
maxDepth = 10;
constructor() {
// Initialize with pre-trained weights (in production, load from file)
this.initializeForests();
}
/**
* Extract features from project data for ML processing
*/
extractFeatures(projection, useCases, industry, companySize) {
// Calculate implementation complexity based on use cases
const complexityScore = this.calculateComplexityScore(useCases);
// Assess risk factors
const risks = this.assessRiskFactors(projection, useCases, industry);
return {
// Financial features
totalInvestment: projection.calculations.total_investment,
expectedROI: projection.calculations.five_year_roi,
paybackPeriodMonths: projection.calculations.payback_period_months || 0,
netPresentValue: projection.calculations.net_present_value,
// Project characteristics
useCaseCount: useCases.length,
timelineMonths: projection.timeline_months,
implementationComplexity: complexityScore,
// Industry factors
industry,
companySize: companySize || 'medium',
// Risk factors
technicalRisk: risks.technical,
organizationalRisk: risks.organizational,
marketRisk: risks.market,
// Historical data (would come from database in production)
similarProjectSuccessRate: this.getHistoricalSuccessRate(industry, complexityScore),
industryAverageROI: this.getIndustryAverageROI(industry)
};
}
/**
* Generate ML predictions for a project
*/
async predict(features) {
this.logger.debug('Generating ML predictions', { industry: features.industry });
// Ensemble prediction using Random Forest
const predictions = this.forests.map(tree => tree.predict(features));
// Aggregate predictions
const successProbabilities = predictions.map(p => p.success);
const avgSuccess = this.mean(successProbabilities);
const stdSuccess = this.standardDeviation(successProbabilities);
// Calculate risk score
const riskScore = this.calculateRiskScore(features);
// Predict actual ROI with variance
const roiAdjustment = this.predictROIAdjustment(features);
const predictedActualROI = features.expectedROI * roiAdjustment;
// Predict delays
const predictedDelayMonths = this.predictDelays(features);
// Identify key risk factors
const keyRiskFactors = this.identifyKeyRisks(features);
return {
successProbability: avgSuccess,
confidenceInterval: {
lower: Math.max(0, avgSuccess - 2 * stdSuccess),
upper: Math.min(1, avgSuccess + 2 * stdSuccess)
},
riskScore,
predictedActualROI,
predictedDelayMonths,
keyRiskFactors
};
}
/**
* Compare multiple projects with ML insights
*/
async compareProjects(projects) {
this.logger.info('Comparing projects with ML', { count: projects.length });
// Extract features and generate predictions for all projects
const projectPredictions = await Promise.all(projects.map(async (project) => {
const features = this.extractFeatures(project.projection, project.useCases, project.industry, project.companySize);
const predictions = await this.predict(features);
// Detect synergies between projects
const synergies = this.detectSynergies(project, projects);
return {
project,
features,
predictions: { ...predictions, synergies }
};
}));
// Rank projects by different criteria
const rankings = this.rankProjects(projectPredictions);
// Generate comparison results
return projectPredictions.map((pp, index) => {
const recommendation = this.generateRecommendation(pp.predictions, rankings.get(pp.project.id));
const insights = this.generateInsights(pp);
return {
projectId: pp.project.id,
baseMetrics: {
roi: pp.project.projection.calculations.five_year_roi,
paybackPeriod: pp.project.projection.calculations.payback_period_months || 0,
npv: pp.project.projection.calculations.net_present_value,
totalInvestment: pp.project.projection.calculations.total_investment
},
mlPredictions: pp.predictions,
ranking: rankings.get(pp.project.id),
recommendation,
insights
};
});
}
/**
* Calculate complexity score based on use cases
*/
calculateComplexityScore(useCases) {
let score = 0;
for (const useCase of useCases) {
// Base complexity from implementation details
const implComplexity = useCase.implementation?.complexity_score || 5;
score += implComplexity;
// Additional factors
if (useCase.implementation?.dependencies?.length) {
score += useCase.implementation.dependencies.length * 0.5;
}
if (useCase.implementation?.risk_factors?.length) {
score += useCase.implementation.risk_factors.length * 0.3;
}
}
// Normalize to 0-10 scale
return Math.min(10, score / useCases.length);
}
/**
* Assess risk factors
*/
assessRiskFactors(projection, useCases, industry) {
// Technical risk based on complexity and dependencies
const avgComplexity = this.mean(useCases.map(uc => uc.implementation?.complexity_score || 5));
const technicalRisk = avgComplexity / 10;
// Organizational risk based on timeline and scale
const organizationalRisk = Math.min(1, projection.timeline_months / 24);
// Market risk based on industry
const marketRiskMap = {
'technology': 0.3,
'financial_services': 0.5,
'healthcare': 0.6,
'retail': 0.4,
'manufacturing': 0.3,
'education': 0.2,
'government': 0.4,
'other': 0.5
};
const marketRisk = marketRiskMap[industry] || 0.5;
return { technical: technicalRisk, organizational: organizationalRisk, market: marketRisk };
}
/**
* Calculate overall risk score
*/
calculateRiskScore(features) {
const weights = {
technical: 0.4,
organizational: 0.3,
market: 0.3
};
const weightedRisk = features.technicalRisk * weights.technical +
features.organizationalRisk * weights.organizational +
features.marketRisk * weights.market;
// Scale to 0-10
return weightedRisk * 10;
}
/**
* Predict ROI adjustment factor based on ML model
*/
predictROIAdjustment(features) {
// Simplified model: adjust based on risk and complexity
const riskFactor = 1 - (features.technicalRisk * 0.2 + features.organizationalRisk * 0.1);
const complexityFactor = 1 - (features.implementationComplexity / 10) * 0.15;
const industryFactor = features.similarProjectSuccessRate || 0.75;
return riskFactor * complexityFactor * industryFactor;
}
/**
* Predict project delays
*/
predictDelays(features) {
// Base delay on complexity and timeline
const complexityDelay = features.implementationComplexity * 0.5;
const scaleDelay = Math.log10(features.totalInvestment / 100000) * 2;
return Math.max(0, Math.round(complexityDelay + scaleDelay));
}
/**
* Identify key risk factors
*/
identifyKeyRisks(features) {
const risks = [];
if (features.technicalRisk > 0.7) {
risks.push({
factor: 'High Technical Complexity',
impact: 'high',
mitigation: 'Consider phased implementation and additional technical expertise'
});
}
if (features.paybackPeriodMonths > 24) {
risks.push({
factor: 'Extended Payback Period',
impact: 'medium',
mitigation: 'Focus on quick wins and interim value delivery'
});
}
if (features.useCaseCount > 5) {
risks.push({
factor: 'Multiple Use Cases',
impact: 'medium',
mitigation: 'Prioritize use cases and implement in waves'
});
}
if (features.organizationalRisk > 0.6) {
risks.push({
factor: 'Organizational Change Management',
impact: 'high',
mitigation: 'Invest in change management and stakeholder alignment'
});
}
return risks;
}
/**
* Detect synergies between projects
*/
detectSynergies(project, allProjects) {
const synergies = [];
for (const other of allProjects) {
if (other.id === project.id)
continue;
// Same industry synergy
if (other.industry === project.industry) {
synergies.push({
withProject: other.id,
type: 'Shared industry knowledge',
estimatedValue: project.projection.calculations.total_investment * 0.05
});
}
// Overlapping use cases
const commonCategories = project.useCases
.map((uc) => uc.category)
.filter((cat) => other.useCases.some((ouc) => ouc.category === cat));
if (commonCategories.length > 0) {
synergies.push({
withProject: other.id,
type: `Shared capabilities: ${commonCategories.join(', ')}`,
estimatedValue: project.projection.calculations.total_investment * 0.1
});
}
}
return synergies;
}
/**
* Rank projects by multiple criteria
*/
rankProjects(projectPredictions) {
const rankings = new Map();
// Sort by different criteria
const byROI = [...projectPredictions].sort((a, b) => b.predictions.predictedActualROI - a.predictions.predictedActualROI);
const byRisk = [...projectPredictions].sort((a, b) => a.predictions.riskScore - b.predictions.riskScore);
const bySpeed = [...projectPredictions].sort((a, b) => (a.features.paybackPeriodMonths + a.predictions.predictedDelayMonths) -
(b.features.paybackPeriodMonths + b.predictions.predictedDelayMonths));
// Calculate overall ranking (weighted)
const weights = { roi: 0.4, risk: 0.3, speed: 0.3 };
for (let i = 0; i < projectPredictions.length; i++) {
const pp = projectPredictions[i];
const roiRank = byROI.findIndex(p => p.project.id === pp.project.id) + 1;
const riskRank = byRisk.findIndex(p => p.project.id === pp.project.id) + 1;
const speedRank = bySpeed.findIndex(p => p.project.id === pp.project.id) + 1;
const overallScore = roiRank * weights.roi +
riskRank * weights.risk +
speedRank * weights.speed;
rankings.set(pp.project.id, {
overall: 0, // Will be set after sorting
byROI: roiRank,
byRisk: riskRank,
bySpeed: speedRank,
score: overallScore
});
}
// Set overall rankings
const sortedByOverall = Array.from(rankings.entries())
.sort((a, b) => a[1].score - b[1].score);
sortedByOverall.forEach((entry, index) => {
entry[1].overall = index + 1;
delete entry[1].score;
});
return rankings;
}
/**
* Generate recommendation based on predictions and rankings
*/
generateRecommendation(predictions, ranking) {
if (predictions.successProbability > 0.8 && ranking.overall === 1) {
return 'strongly_recommend';
}
else if (predictions.successProbability > 0.7 && ranking.overall <= 2) {
return 'recommend';
}
else if (predictions.successProbability > 0.5 && predictions.riskScore < 6) {
return 'consider';
}
else if (predictions.successProbability > 0.3) {
return 'reconsider';
}
else {
return 'not_recommended';
}
}
/**
* Generate human-readable insights
*/
generateInsights(projectPrediction) {
const insights = [];
const { predictions, features } = projectPrediction;
// Success probability insight
if (predictions.successProbability > 0.8) {
insights.push('High success probability indicates strong project viability');
}
else if (predictions.successProbability < 0.5) {
insights.push('Success probability below 50% suggests significant risks');
}
// ROI adjustment insight
const roiDiff = Math.abs(predictions.predictedActualROI - features.expectedROI);
if (roiDiff > features.expectedROI * 0.2) {
insights.push(`ML model predicts ${predictions.predictedActualROI > features.expectedROI ? 'higher' : 'lower'} ` +
`actual ROI than projected (${roiDiff.toFixed(1)}% difference)`);
}
// Delay insight
if (predictions.predictedDelayMonths > 3) {
insights.push(`Expect approximately ${predictions.predictedDelayMonths} months delay based on complexity`);
}
// Synergy insights
if (predictions.synergies && predictions.synergies.length > 0) {
const totalSynergyValue = predictions.synergies
.reduce((sum, s) => sum + s.estimatedValue, 0);
insights.push(`Potential synergies with other projects could add $${totalSynergyValue.toLocaleString()} in value`);
}
// Risk insights
if (predictions.keyRiskFactors.some((r) => r.impact === 'high')) {
insights.push('High-impact risks require immediate mitigation planning');
}
return insights;
}
/**
* Initialize Random Forest (simplified implementation)
*/
initializeForests() {
// In production, load pre-trained models
// For now, create trees with random weights
for (let i = 0; i < this.treeCount; i++) {
this.forests.push(new DecisionTree(this.maxDepth));
}
}
// Statistical utilities
mean(values) {
return values.reduce((a, b) => a + b, 0) / values.length;
}
standardDeviation(values) {
const avg = this.mean(values);
const squaredDiffs = values.map(v => Math.pow(v - avg, 2));
return Math.sqrt(this.mean(squaredDiffs));
}
// Historical data (in production, query from database)
getHistoricalSuccessRate(industry, complexity) {
const baseRates = {
'technology': 0.75,
'financial_services': 0.70,
'healthcare': 0.65,
'retail': 0.72,
'manufacturing': 0.68,
'education': 0.73,
'government': 0.60,
'other': 0.70
};
const base = baseRates[industry] || 0.70;
// Adjust for complexity
return base * (1 - complexity / 20);
}
getIndustryAverageROI(industry) {
const averages = {
'technology': 35,
'financial_services': 25,
'healthcare': 20,
'retail': 30,
'manufacturing': 28,
'education': 18,
'government': 15,
'other': 25
};
return averages[industry] || 25;
}
}
/**
* Simplified Decision Tree for Random Forest
*/
class DecisionTree {
maxDepth;
weights = new Map();
constructor(maxDepth) {
this.maxDepth = maxDepth;
// Initialize with random weights (in production, use trained weights)
const features = [
'totalInvestment', 'expectedROI', 'paybackPeriodMonths',
'useCaseCount', 'implementationComplexity', 'technicalRisk'
];
features.forEach(feature => {
this.weights.set(feature, Math.random() * 2 - 1);
});
}
predict(features) {
// Simplified linear model (in production, use proper tree structure)
let score = 0.5; // Base probability
score += (features.expectedROI / 100) * (this.weights.get('expectedROI') || 0);
score -= (features.paybackPeriodMonths / 60) * (this.weights.get('paybackPeriodMonths') || 0);
score -= features.implementationComplexity / 10 * (this.weights.get('implementationComplexity') || 0);
score -= features.technicalRisk * (this.weights.get('technicalRisk') || 0);
score += Math.log10(features.totalInvestment) / 10 * (this.weights.get('totalInvestment') || 0);
// Sigmoid to bound between 0 and 1
return {
success: 1 / (1 + Math.exp(-score))
};
}
}
//# sourceMappingURL=ml-comparison-engine.js.map