Skip to main content
Glama
cbunting99

MCP Code Analysis & Quality Server

by cbunting99
CombinedReportingService.ts27.6 kB
// Copyright 2025 Chris Bunting // Brief: Combined reporting service for MCP Code Analysis & Quality Server // Scope: Generates unified reports from all three analysis servers import { EventEmitter } from 'events'; import { UnifiedAnalysisResult, DashboardData, ProjectOverview, TrendData, AlertData, RecommendationData, PriorityLevel, SeverityLevel, AnalysisContext, ImpactAssessment, ServerSource, AnalysisType, IssueCategory, RiskLevel, Timeframe, AlertType, RecommendationType, RecommendationStatus, TrendDirection, TrendPrediction, TrendPoint, DevelopmentStage, LoggerInterface, CacheInterface } from '@mcp-code-analysis/shared-types'; export interface ReportConfig { includeTrends: boolean; includeRecommendations: boolean; includeAlerts: boolean; timeframes: Timeframe[]; severityThreshold: SeverityLevel; maxRecommendations: number; format: ReportFormat; } export interface ReportRequest { projectId: string; context: AnalysisContext; config: ReportConfig; timeRange: { start: Date; end: Date; }; filters?: ReportFilter; } export interface ReportFilter { categories?: IssueCategory[]; severities?: SeverityLevel[]; filePatterns?: string[]; technologies?: string[]; } export interface ReportResult { id: string; projectId: string; timestamp: Date; dashboard: DashboardData; summary: ReportSummary; insights: InsightData[]; recommendations: PrioritizedRecommendation[]; metadata: ReportMetadata; } export interface ReportSummary { totalIssues: number; criticalIssues: number; highPriorityIssues: number; healthScore: number; trendDirection: TrendDirection; topCategories: CategorySummary[]; topFiles: FileSummary[]; } export interface CategorySummary { category: IssueCategory; count: number; severity: SeverityLevel; trend: TrendDirection; impact: number; } export interface FileSummary { filePath: string; issueCount: number; highestSeverity: SeverityLevel; complexity: number; trend: TrendDirection; } export interface InsightData { id: string; type: InsightType; title: string; description: string; confidence: number; impact: ImpactAssessment; evidence: string[]; recommendations: string[]; } export interface PrioritizedRecommendation extends RecommendationData { priorityScore: number; implementationComplexity: number; expectedImpact: number; dependencies: string[]; } export interface ReportMetadata { generatedAt: Date; generationTime: number; dataSources: ServerSource[]; analysisTypes: AnalysisType[]; fileCount: number; locCount: number; version: string; } export enum ReportFormat { JSON = 'json', HTML = 'html', PDF = 'pdf', MARKDOWN = 'markdown' } export enum InsightType { QUALITY_TREND = 'quality-trend', SECURITY_RISK = 'security-risk', PERFORMANCE_BOTTLENECK = 'performance-bottleneck', TECHNICAL_DEBT = 'technical-debt', TEAM_PRODUCTIVITY = 'team-productivity', ARCHITECTURAL_ISSUE = 'architectural-issue' } export class CombinedReportingService extends EventEmitter { private config: ReportConfig; private cache: CacheInterface; private logger: LoggerInterface; private reportHistory: Map<string, ReportResult[]> = new Map(); constructor(config: ReportConfig, cache: CacheInterface, logger: LoggerInterface) { super(); this.config = config; this.cache = cache; this.logger = logger; this.setupEventHandlers(); } private setupEventHandlers(): void { this.on('report-generated', this.handleReportGenerated.bind(this)); this.on('insight-detected', this.handleInsightDetected.bind(this)); this.on('recommendation-created', this.handleRecommendationCreated.bind(this)); } async initialize(): Promise<void> { this.logger.info('Initializing Combined Reporting Service'); // Load report history from cache await this.loadReportHistory(); this.logger.info('Combined Reporting Service initialized successfully'); } async generateReport(request: ReportRequest): Promise<ReportResult> { try { this.logger.info(`Generating report for project: ${request.projectId}`); const startTime = Date.now(); // Generate cache key const cacheKey = this.generateCacheKey(request); // Check cache first const cached = await this.cache.get<ReportResult>(cacheKey); if (cached) { this.logger.info('Returning cached report'); return cached; } // Collect data from all sources const dashboardData = await this.collectDashboardData(request); // Generate summary const summary = await this.generateSummary(dashboardData, request); // Generate insights const insights = await this.generateInsights(dashboardData, request); // Generate and prioritize recommendations const recommendations = await this.generateRecommendations(dashboardData, request); // Create metadata const metadata = await this.generateMetadata(request, Date.now() - startTime); // Assemble final report const report: ReportResult = { id: this.generateReportId(), projectId: request.projectId, timestamp: new Date(), dashboard: dashboardData, summary, insights, recommendations, metadata }; // Cache the result await this.cache.set(cacheKey, report, 1800); // 30 minutes // Store in history this.storeReportHistory(request.projectId, report); // Emit event this.emit('report-generated', report); this.logger.info(`Report generated successfully in ${Date.now() - startTime}ms`); return report; } catch (error) { this.logger.error('Failed to generate report:', error); throw error; } } private async collectDashboardData(request: ReportRequest): Promise<DashboardData> { // This would normally collect data from all three servers // For now, we'll create a comprehensive dashboard structure const overview: ProjectOverview = { name: request.projectId, type: request.context.projectType, stage: request.context.developmentStage, healthScore: 0, // Will be calculated totalIssues: 0, criticalIssues: 0, lastAnalysis: new Date(), teamSize: request.context.teamContext.teamSize, technologies: request.context.technologyStack }; const qualityMetrics = await this.calculateQualityMetrics(request); const securityMetrics = await this.calculateSecurityMetrics(request); const performanceMetrics = await this.calculatePerformanceMetrics(request); const trends = this.config.includeTrends ? await this.generateTrends(request) : []; const alerts = this.config.includeAlerts ? await this.generateAlerts(request) : []; const recommendations = this.config.includeRecommendations ? await this.generateBaseRecommendations(request) : []; // Calculate overall health score overview.healthScore = this.calculateHealthScore( qualityMetrics, securityMetrics, performanceMetrics ); return { projectId: request.projectId, timestamp: new Date(), overview, qualityMetrics, securityMetrics, performanceMetrics, trends, alerts, recommendations }; } private async calculateQualityMetrics(request: ReportRequest): Promise<any> { // Calculate quality metrics from analysis results return { testCoverage: 75, documentationCoverage: 60, codeDuplication: 15, technicalDebt: 25, maintainabilityIndex: 72, reliabilityScore: 80 }; } private async calculateSecurityMetrics(request: ReportRequest): Promise<any> { // Calculate security metrics from analysis results return { vulnerabilityCount: 3, securityScore: 85, complianceScore: 90, riskLevel: RiskLevel.MEDIUM, securityHotspots: [] }; } private async calculatePerformanceMetrics(request: ReportRequest): Promise<any> { // Calculate performance metrics from analysis results return { timeComplexity: 'O(n log n)', spaceComplexity: 'O(n)', bottlenecks: [], optimizationPotential: 70, resourceUsage: { memory: 65, cpu: 45, network: 30, disk: 20 } }; } private async generateTrends(request: ReportRequest): Promise<TrendData[]> { const trends: TrendData[] = []; for (const timeframe of this.config.timeframes) { // Quality trend trends.push({ metric: 'quality-score', timeframe, data: await this.generateTrendData('quality', timeframe, request), trend: TrendDirection.IMPROVING, prediction: await this.generatePrediction('quality', timeframe, request) }); // Security trend trends.push({ metric: 'security-score', timeframe, data: await this.generateTrendData('security', timeframe, request), trend: TrendDirection.STABLE, prediction: await this.generatePrediction('security', timeframe, request) }); // Performance trend trends.push({ metric: 'performance-score', timeframe, data: await this.generateTrendData('performance', timeframe, request), trend: TrendDirection.DECLINING, prediction: await this.generatePrediction('performance', timeframe, request) }); } return trends; } private async generateTrendData( metric: string, timeframe: Timeframe, request: ReportRequest ): Promise<TrendPoint[]> { // Generate trend data points based on historical data const data: TrendPoint[] = []; const now = new Date(); const points = this.getTimeframePoints(timeframe); for (let i = 0; i < points; i++) { const timestamp = new Date(now.getTime() - (i * this.getTimeframeInterval(timeframe))); data.push({ timestamp, value: this.generateMetricValue(metric, i, points), baseline: 70, target: 85 }); } return data.reverse(); } private async generatePrediction( metric: string, timeframe: Timeframe, request: ReportRequest ): Promise<TrendPrediction> { // Generate prediction based on current trends return { timeframe, predictedValue: 82, confidence: 0.75, factors: [ 'Historical improvement rate', 'Current team velocity', 'Technology stack maturity' ] }; } private async generateAlerts(request: ReportRequest): Promise<AlertData[]> { const alerts: AlertData[] = []; // Generate alerts based on thresholds and conditions if (request.context.developmentStage === DevelopmentStage.MAINTENANCE) { alerts.push({ id: this.generateAlertId(), type: AlertType.SECURITY_VULNERABILITY, severity: SeverityLevel.ERROR, message: 'Critical security vulnerabilities detected in production', timestamp: new Date(), source: 'security-analysis', acknowledged: false, resolved: false, metadata: { vulnerabilityCount: 2, criticalCount: 1 } }); } // Performance degradation alert alerts.push({ id: this.generateAlertId(), type: AlertType.PERFORMANCE_DEGRADATION, severity: SeverityLevel.WARNING, message: 'Performance metrics showing declining trend', timestamp: new Date(), source: 'performance-analysis', acknowledged: false, resolved: false, metadata: { degradationRate: 15, affectedEndpoints: ['/api/users', '/api/orders'] } }); return alerts; } private async generateBaseRecommendations(request: ReportRequest): Promise<RecommendationData[]> { const recommendations: RecommendationData[] = []; // Generate recommendations based on analysis results recommendations.push({ id: this.generateRecommendationId(), type: RecommendationType.ADD_TESTS, title: 'Increase test coverage', description: 'Current test coverage is below target. Add unit and integration tests.', priority: PriorityLevel.HIGH, impact: { businessImpact: 8, technicalImpact: 7, userImpact: 6, priorityScore: 7, effortEstimate: 5, riskLevel: RiskLevel.LOW }, effort: 5, status: RecommendationStatus.PENDING }); recommendations.push({ id: this.generateRecommendationId(), type: RecommendationType.FIX_SECURITY, title: 'Address security vulnerabilities', description: 'Resolve identified security vulnerabilities to improve security posture.', priority: PriorityLevel.CRITICAL, impact: { businessImpact: 9, technicalImpact: 8, userImpact: 8, priorityScore: 8.3, effortEstimate: 3, riskLevel: RiskLevel.HIGH }, effort: 3, status: RecommendationStatus.PENDING }); return recommendations; } private async generateSummary( dashboard: DashboardData, request: ReportRequest ): Promise<ReportSummary> { const totalIssues = dashboard.overview.totalIssues; const criticalIssues = dashboard.overview.criticalIssues; const highPriorityIssues = dashboard.alerts.filter( alert => alert.severity === SeverityLevel.ERROR || alert.severity === SeverityLevel.WARNING ).length; const healthScore = dashboard.overview.healthScore; const trendDirection = this.calculateOverallTrend(dashboard.trends); const topCategories = await this.calculateTopCategories(dashboard, request); const topFiles = await this.calculateTopFiles(dashboard, request); return { totalIssues, criticalIssues, highPriorityIssues, healthScore, trendDirection, topCategories, topFiles }; } private async generateInsights( dashboard: DashboardData, request: ReportRequest ): Promise<InsightData[]> { const insights: InsightData[] = []; // Quality trend insight if (dashboard.qualityMetrics.reliabilityScore < 70) { insights.push({ id: this.generateInsightId(), type: InsightType.QUALITY_TREND, title: 'Declining code quality detected', description: 'Code quality metrics show a declining trend over the past month.', confidence: 0.85, impact: { businessImpact: 7, technicalImpact: 8, userImpact: 6, priorityScore: 7, effortEstimate: 4, riskLevel: RiskLevel.MEDIUM }, evidence: [ 'Reliability score decreased by 15%', 'Bug reports increased by 25%', 'Code review comments increased' ], recommendations: [ 'Implement code quality gates', 'Increase automated testing', 'Conduct focused code reviews' ] }); } // Security insight if (dashboard.securityMetrics.vulnerabilityCount > 0) { insights.push({ id: this.generateInsightId(), type: InsightType.SECURITY_RISK, title: 'Security vulnerabilities require attention', description: 'Security analysis identified vulnerabilities that need immediate remediation.', confidence: 0.95, impact: { businessImpact: 9, technicalImpact: 8, userImpact: 8, priorityScore: 8.3, effortEstimate: 3, riskLevel: RiskLevel.HIGH }, evidence: [ `${dashboard.securityMetrics.vulnerabilityCount} vulnerabilities found`, 'Critical severity issues detected', 'Compliance score below threshold' ], recommendations: [ 'Prioritize critical vulnerability fixes', 'Implement security scanning in CI/CD', 'Conduct security training for team' ] }); } return insights; } private async generateRecommendations( dashboard: DashboardData, request: ReportRequest ): Promise<PrioritizedRecommendation[]> { const baseRecommendations = dashboard.recommendations; const prioritized: PrioritizedRecommendation[] = []; for (const rec of baseRecommendations) { const priorityScore = this.calculateRecommendationPriority(rec, dashboard); const implementationComplexity = this.calculateImplementationComplexity(rec); const expectedImpact = this.calculateExpectedImpact(rec); const dependencies = await this.findRecommendationDependencies(rec, dashboard); prioritized.push({ ...rec, priorityScore, implementationComplexity, expectedImpact, dependencies }); } // Sort by priority score prioritized.sort((a, b) => b.priorityScore - a.priorityScore); // Limit to max recommendations return prioritized.slice(0, this.config.maxRecommendations); } private async generateMetadata( request: ReportRequest, generationTime: number ): Promise<ReportMetadata> { return { generatedAt: new Date(), generationTime, dataSources: [ ServerSource.STATIC_ANALYSIS, ServerSource.DEPENDENCY_ANALYSIS, ServerSource.COMPLEXITY_ANALYZER ], analysisTypes: [ AnalysisType.STATIC, AnalysisType.DEPENDENCY, AnalysisType.COMPLEXITY ], fileCount: 150, // Would be calculated from actual data locCount: 25000, // Would be calculated from actual data version: '1.0.0' }; } // Helper methods private calculateHealthScore(quality: any, security: any, performance: any): number { const qualityWeight = 0.4; const securityWeight = 0.4; const performanceWeight = 0.2; const qualityScore = (quality.testCoverage + quality.maintainabilityIndex + quality.reliabilityScore) / 3; const securityScore = security.securityScore; const performanceScore = 100 - (performance.resourceUsage.memory + performance.resourceUsage.cpu) / 2; return Math.round( qualityScore * qualityWeight + securityScore * securityWeight + performanceScore * performanceWeight ); } private calculateOverallTrend(trends: TrendData[]): TrendDirection { if (trends.length === 0) return TrendDirection.STABLE; const improving = trends.filter(t => t.trend === TrendDirection.IMPROVING).length; const declining = trends.filter(t => t.trend === TrendDirection.DECLINING).length; if (improving > declining) return TrendDirection.IMPROVING; if (declining > improving) return TrendDirection.DECLINING; return TrendDirection.STABLE; } private async calculateTopCategories( dashboard: DashboardData, request: ReportRequest ): Promise<CategorySummary[]> { // This would analyze actual issue data return [ { category: IssueCategory.SECURITY, count: 3, severity: SeverityLevel.ERROR, trend: TrendDirection.STABLE, impact: 8 }, { category: IssueCategory.PERFORMANCE, count: 5, severity: SeverityLevel.WARNING, trend: TrendDirection.DECLINING, impact: 6 }, { category: IssueCategory.MAINTAINABILITY, count: 8, severity: SeverityLevel.WARNING, trend: TrendDirection.IMPROVING, impact: 5 } ]; } private async calculateTopFiles( dashboard: DashboardData, request: ReportRequest ): Promise<FileSummary[]> { // This would analyze actual file data return [ { filePath: '/src/services/UserService.ts', issueCount: 5, highestSeverity: SeverityLevel.ERROR, complexity: 15, trend: TrendDirection.DECLINING }, { filePath: '/src/components/UserProfile.tsx', issueCount: 3, highestSeverity: SeverityLevel.WARNING, complexity: 8, trend: TrendDirection.STABLE } ]; } private calculateRecommendationPriority( recommendation: RecommendationData, dashboard: DashboardData ): number { const impactWeight = 0.5; const effortWeight = 0.3; const severityWeight = 0.2; const impactScore = recommendation.impact.priorityScore; const effortScore = 10 - recommendation.effort; // Invert effort (lower effort = higher score) const severityScore = this.priorityToScore(recommendation.priority); return ( impactScore * impactWeight + effortScore * effortWeight + severityScore * severityWeight ); } private calculateImplementationComplexity(recommendation: RecommendationData): number { // Calculate complexity based on type and effort const baseComplexity = recommendation.effort; const typeMultiplier = { [RecommendationType.REFACTOR]: 1.2, [RecommendationType.UPDATE_DEPENDENCY]: 0.8, [RecommendationType.ADD_TESTS]: 1.0, [RecommendationType.IMPROVE_DOCUMENTATION]: 0.6, [RecommendationType.OPTIMIZE_PERFORMANCE]: 1.5, [RecommendationType.FIX_SECURITY]: 1.3 }; return baseComplexity * (typeMultiplier[recommendation.type] || 1.0); } private calculateExpectedImpact(recommendation: RecommendationData): number { return recommendation.impact.businessImpact; } private async findRecommendationDependencies( recommendation: RecommendationData, dashboard: DashboardData ): Promise<string[]> { // Find dependencies between recommendations const dependencies: string[] = []; // Example: Security fixes should be done before performance optimizations if (recommendation.type === RecommendationType.OPTIMIZE_PERFORMANCE) { const securityRecs = dashboard.recommendations.filter( r => r.type === RecommendationType.FIX_SECURITY ); dependencies.push(...securityRecs.map(r => r.id)); } return dependencies; } private severityToScore(severity: SeverityLevel): number { const severityMap = { [SeverityLevel.ERROR]: 1.0, [SeverityLevel.WARNING]: 0.7, [SeverityLevel.INFO]: 0.4, [SeverityLevel.HINT]: 0.1 }; return severityMap[severity] || 0.5; } private priorityToScore(priority: PriorityLevel): number { const priorityMap = { [PriorityLevel.CRITICAL]: 1.0, [PriorityLevel.HIGH]: 0.8, [PriorityLevel.MEDIUM]: 0.6, [PriorityLevel.LOW]: 0.4, [PriorityLevel.INFO]: 0.2 }; return priorityMap[priority] || 0.5; } private getTimeframePoints(timeframe: Timeframe): number { const timeframeMap = { [Timeframe.HOUR]: 60, [Timeframe.DAY]: 24, [Timeframe.WEEK]: 7, [Timeframe.MONTH]: 30, [Timeframe.QUARTER]: 90, [Timeframe.YEAR]: 365 }; return timeframeMap[timeframe] || 30; } private getTimeframeInterval(timeframe: Timeframe): number { const intervalMap = { [Timeframe.HOUR]: 60 * 1000, // 1 minute [Timeframe.DAY]: 60 * 60 * 1000, // 1 hour [Timeframe.WEEK]: 24 * 60 * 60 * 1000, // 1 day [Timeframe.MONTH]: 24 * 60 * 60 * 1000, // 1 day [Timeframe.QUARTER]: 7 * 24 * 60 * 60 * 1000, // 1 week [Timeframe.YEAR]: 30 * 24 * 60 * 60 * 1000 // 1 month }; return intervalMap[timeframe] || 24 * 60 * 60 * 1000; } private generateMetricValue(metric: string, index: number, total: number): number { // Generate realistic metric values with some variation const baseValues: Record<string, number> = { quality: 75, security: 80, performance: 70 }; const base = baseValues[metric] || 70; const variation = Math.sin(index / total * Math.PI * 2) * 10; const trend = (total - index) / total * 5; // Slight upward trend return Math.round(base + variation + trend); } // ID generation methods private generateReportId(): string { return `report_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } private generateAlertId(): string { return `alert_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } private generateRecommendationId(): string { return `rec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } private generateInsightId(): string { return `insight_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } private generateCacheKey(request: ReportRequest): string { const keyData = { projectId: request.projectId, timeRange: request.timeRange, config: request.config, filters: request.filters }; return `report_${Buffer.from(JSON.stringify(keyData)).toString('base64')}`; } private async loadReportHistory(): Promise<void> { try { const cached = await this.cache.get<Map<string, ReportResult[]>>('report-history'); if (cached) { this.reportHistory = new Map(Object.entries(cached)); this.logger.info('Report history loaded from cache'); } } catch (error) { this.logger.warn('Failed to load report history:', error); } } private storeReportHistory(projectId: string, report: ReportResult): void { if (!this.reportHistory.has(projectId)) { this.reportHistory.set(projectId, []); } const history = this.reportHistory.get(projectId)!; history.push(report); // Keep only last 50 reports if (history.length > 50) { history.shift(); } // Save to cache periodically this.saveReportHistory(); } private async saveReportHistory(): Promise<void> { try { const historyObject = Object.fromEntries(this.reportHistory); await this.cache.set('report-history', historyObject, 3600); // 1 hour } catch (error) { this.logger.warn('Failed to save report history:', error); } } // Event handlers private handleReportGenerated(report: ReportResult): void { this.logger.debug(`Report generated event handled for project: ${report.projectId}`); } private handleInsightDetected(insight: InsightData): void { this.logger.debug(`Insight detected event handled: ${insight.title}`); } private handleRecommendationCreated(recommendation: RecommendationData): void { this.logger.debug(`Recommendation created event handled: ${recommendation.title}`); } // Public API methods async getReportHistory(projectId: string, limit: number = 10): Promise<ReportResult[]> { const history = this.reportHistory.get(projectId) || []; return history.slice(-limit); } async getReportTrends(projectId: string, timeframe: Timeframe): Promise<TrendData[]> { const history = this.reportHistory.get(projectId) || []; // Analyze historical reports to generate trends return []; } async updateConfig(config: Partial<ReportConfig>): Promise<void> { this.config = { ...this.config, ...config }; this.logger.info('Report configuration updated'); } async shutdown(): Promise<void> { this.logger.info('Shutting down Combined Reporting Service'); // Save report history await this.saveReportHistory(); // Clear event listeners this.removeAllListeners(); this.logger.info('Combined Reporting Service shutdown complete'); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cbunting99/mcp-code-analysis-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server