/** * xray Performance Optimization MCP Server * * Knowledge skill for performance analysis, optimization recommendations, * profiling, benchmarking, memory analysis, and Core Web Vitals measurement */ import { frameworkLogger, generateJobId } from "../../core/framework-logger.js"; import fs from "fs"; import path from "path"; import os from "os"; import { execSync } from "child_process"; import { XrayKnowledgeSkillBase } from "../shared/knowledge-skill-base.js"; interface ProfileApplicationArgs { projectRoot: string; scope?: "full" | "runtime" | "build" | "memory"; duration?: number; includeHotPaths?: boolean; } interface AnalyzeMemoryArgs { projectRoot: string; heapSnapshot?: boolean; gcAnalysis?: boolean; leakDetection?: boolean; } interface BenchmarkCodeArgs { projectRoot: string; testFile?: string; iterations?: number; warmupRuns?: number; compareBaseline?: boolean; } interface SuggestOptimizationsArgs { projectRoot: string; focus?: "cpu" | "memory" | "network" | "io" | "all"; threshold?: number; } interface MeasureCoreWebVitalsArgs { projectRoot: string; analyzeBundle?: boolean; checkAccessibility?: boolean; measureLCP?: boolean; measureINP?: boolean; measureCLS?: boolean; } interface ProfilingResult { hotPaths: string[]; functionCallCounts: Record; memoryFootprint: number; bottlenecks: string[]; recommendations: string[]; cpuProfile: Record; metrics: ProfilingMetrics; } interface ProfilingMetrics { totalFunctions: number; hotFunctions: number; avgExecutionTime: number; peakMemory: number; gcPauses: number; } interface MemoryAnalysisResult { heapUsed: number; heapTotal: number; external: number; rss: number; memoryLeaks: MemoryLeak[]; gcEvents: GCEvent[]; recommendations: string[]; heapBreakdown: HeapBreakdown; allocationsByType: MemoryAllocationBreakdown; } interface MemoryLeak { location: string; severity: "low" | "medium" | "high" | "critical"; suspectedCause: string; estimatedImpact: string; fixSuggestion: string; } interface GCEvent { type: "minor" | "major" | "full"; duration: number; timestamp: number; reclaimedBytes: number; } interface HeapBreakdown { strings: number; arrays: number; objects: number; functions: number; closures: number; domNodes: number; } interface BenchmarkResult { benchmarks: Benchmark[]; comparison?: BenchmarkComparison; statistics: BenchmarkStatistics; recommendations: string[]; } interface Benchmark { name: string; operations: number; duration: number; opsPerSecond: number; meanMs: number; medianMs: number; stdDevMs: number; minMs: number; maxMs: number; p95Ms: number; p99Ms: number; } interface BenchmarkComparison { baseline: Benchmark; current: Benchmark; improvement: number; significant: boolean; } interface BenchmarkStatistics { totalBenchmarks: number; fastest: string; slowest: string; averageOpsPerSecond: number; variance: number; } interface MemoryAllocationBreakdown { strings: number; arrays: number; objects: number; functions: number; buffers: number; closures: number; promises: number; eventListeners: number; } interface OptimizationSuggestion { id: string; category: "cpu" | "memory" | "network" | "io" | "rendering"; title: string; description: string; impact: "low" | "medium" | "high" | "critical"; effort: "low" | "medium" | "high"; files: string[]; codeSnippet?: string; suggestedFix: string; expectedImprovement: string; } interface CoreWebVitalsResult { LCP?: LCPMetrics; INP?: INPMetrics; CLS?: CLSMetrics; bundleAnalysis?: BundleAnalysis; recommendations: string[]; score: number; status: "good" | "needs-improvement" | "poor"; } interface LCPMetrics { value: number; score: number; element: string; resourceLoadTime: number; renderDelay: number; } interface INPMetrics { value: number; score: number; interactionType: string; processingTime: number; presentationDelay: number; } interface CLSMetrics { value: number; score: number; layoutShifts: number; unexpectedShifts: number; sources: string[]; } interface BundleAnalysis { totalSize: number; gzippedSize: number; byType: Record; largestModules: { name: string; size: number }[]; duplication: { name: string; count: number }[]; treeShakingOpportunities: string[]; } interface McpToolResponse { content: Array<{ type: string; text: string }>; data?: Record; } class XrayPerformanceOptimizationServer extends XrayKnowledgeSkillBase { constructor() { super("performance-optimization", "3.1.0"); this.tools = [ { name: "profile-application", description: "Run comprehensive performance profiling on the codebase to identify bottlenecks and hot paths", inputSchema: { type: "object", properties: { projectRoot: { type: "string", description: "Path to the project root directory" }, scope: { type: "string", enum: ["full", "runtime", "build", "memory"], default: "full", description: "Scope of profiling analysis" }, duration: { type: "number", default: 30, description: "Profiling duration in seconds" }, includeHotPaths: { type: "boolean", default: true, description: "Include hot path analysis" } }, required: ["projectRoot"] } }, { name: "analyze-memory", description: "Perform memory leak detection and heap analysis to identify memory issues", inputSchema: { type: "object", properties: { projectRoot: { type: "string", description: "Path to the project root directory" }, heapSnapshot: { type: "boolean", default: true, description: "Generate heap snapshot analysis" }, gcAnalysis: { type: "boolean", default: true, description: "Analyze garbage collection patterns" }, leakDetection: { type: "boolean", default: true, description: "Detect potential memory leaks" } }, required: ["projectRoot"] } }, { name: "benchmark-code", description: "Execute and compare benchmark results for code performance measurement", inputSchema: { type: "object", properties: { projectRoot: { type: "string", description: "Path to the project root directory" }, testFile: { type: "string", description: "Specific benchmark test file to run" }, iterations: { type: "number", default: 100, description: "Number of benchmark iterations" }, warmupRuns: { type: "number", default: 10, description: "Number of warmup runs" }, compareBaseline: { type: "boolean", default: false, description: "Compare results against baseline" } }, required: ["projectRoot"] } }, { name: "suggest-optimizations", description: "Generate specific, actionable optimization suggestions based on code analysis", inputSchema: { type: "object", properties: { projectRoot: { type: "string", description: "Path to the project root directory" }, focus: { type: "string", enum: ["cpu", "memory", "network", "io", "all"], default: "all", description: "Focus area for optimizations" }, threshold: { type: "number", default: 100, description: "Performance threshold in ms" } }, required: ["projectRoot"] } }, { name: "measure-core-web-vitals", description: "Analyze Core Web Vitals metrics (LCP, INP, CLS) for web application performance", inputSchema: { type: "object", properties: { projectRoot: { type: "string", description: "Path to the project root directory" }, analyzeBundle: { type: "boolean", default: true, description: "Analyze JavaScript bundle" }, checkAccessibility: { type: "boolean", default: true, description: "Check accessibility factors affecting CLS" }, measureLCP: { type: "boolean", default: true, description: "Measure Largest Contentful Paint" }, measureINP: { type: "boolean", default: true, description: "Measure Interaction to Next Paint" }, measureCLS: { type: "boolean", default: true, description: "Measure Cumulative Layout Shift" } }, required: ["projectRoot"] } } ]; this.handlers = { "profile-application": async (args) => this.handleProfileApplication(args as unknown as ProfileApplicationArgs), "analyze-memory": async (args) => this.handleAnalyzeMemory(args as unknown as AnalyzeMemoryArgs), "benchmark-code": async (args) => this.handleBenchmarkCode(args as unknown as BenchmarkCodeArgs), "suggest-optimizations": async (args) => this.handleSuggestOptimizations(args as unknown as SuggestOptimizationsArgs), "measure-core-web-vitals": async (args) => this.handleMeasureCoreWebVitals(args as unknown as MeasureCoreWebVitalsArgs), }; this.setupToolHandlers(); } private async handleProfileApplication(args: ProfileApplicationArgs): Promise { const { projectRoot, scope = "full", duration = 30, includeHotPaths = true } = args; const jobId = generateJobId("mcp-perf-profiling"); await frameworkLogger.log("mcp/performance-optimization", "profiling-started", "info", { projectRoot, scope }, undefined, jobId); try { const result = await this.performProfiling(projectRoot, scope, duration, includeHotPaths); const responseText = this.formatProfilingResult(result); return { content: [{ type: "text", text: responseText }], data: result as unknown as Record }; } catch (error) { return { content: [{ type: "text", text: `Profiling failed: ${error instanceof Error ? error.message : String(error)}` }] }; } } private async performProfiling( projectRoot: string, scope: string, duration: number, includeHotPaths: boolean ): Promise { const result: ProfilingResult = { hotPaths: [], functionCallCounts: {}, memoryFootprint: 0, bottlenecks: [], recommendations: [], cpuProfile: {}, metrics: { totalFunctions: 0, hotFunctions: 0, avgExecutionTime: 0, peakMemory: 0, gcPauses: 0 } }; const memUsage = process.memoryUsage(); result.memoryFootprint = Math.round(memUsage.heapUsed / 1024 / 1024); if (fs.existsSync(path.join(projectRoot, "src"))) { const codeFiles = this.findCodeFiles(projectRoot); const analysis = this.analyzeCodeForPerformance(codeFiles, scope); result.hotPaths = analysis.hotPaths; result.functionCallCounts = analysis.functionCounts; result.bottlenecks = analysis.bottlenecks; result.recommendations = analysis.recommendations; result.metrics.totalFunctions = analysis.totalFunctions; result.metrics.hotFunctions = analysis.hotFunctions; result.metrics.avgExecutionTime = analysis.avgExecutionTime; result.metrics.peakMemory = analysis.peakMemory; result.metrics.gcPauses = analysis.gcPauses; if (includeHotPaths) { result.cpuProfile = { topFunctions: this.getTopFunctions(analysis), callGraph: this.generateCallGraph(analysis), criticalPath: this.identifyCriticalPath(analysis) }; } } return result; } private analyzeCodeForPerformance(files: string[], scope: string): { hotPaths: string[]; functionCounts: Record; bottlenecks: string[]; recommendations: string[]; totalFunctions: number; hotFunctions: number; avgExecutionTime: number; peakMemory: number; gcPauses: number; } { const hotPaths: string[] = []; const functionCounts: Record = {}; const bottlenecks: string[] = []; const recommendations: string[] = []; let totalFunctions = 0; let hotFunctions = 0; for (const file of files) { try { const content = fs.readFileSync(file, "utf8"); const analysis = this.analyzeFilePerformance(content, file, scope); if (analysis.functions.length > 0) { totalFunctions += analysis.functions.length; hotFunctions += analysis.hotCount; analysis.functions.forEach((fn) => { functionCounts[`${file}:${fn.name}`] = fn.estimatedCalls; }); } hotPaths.push(...analysis.hotPaths); bottlenecks.push(...analysis.bottlenecks); recommendations.push(...analysis.recommendations); } catch { // Skip files that can't be read } } const avgExecutionTime = this.calculateAverageExecutionTime(functionCounts); return { hotPaths: [...new Set(hotPaths)], functionCounts, bottlenecks: [...new Set(bottlenecks)], recommendations: [...new Set(recommendations)], totalFunctions, hotFunctions, avgExecutionTime, peakMemory: Math.round(process.memoryUsage().heapUsed / 1024 / 1024), gcPauses: this.estimateGCPauses() }; } private analyzeFilePerformance(content: string, filePath: string, scope: string): { functions: { name: string; estimatedCalls: number }[]; hotPaths: string[]; bottlenecks: string[]; recommendations: string[]; hotCount: number; } { const functions: { name: string; estimatedCalls: number }[] = []; const hotPaths: string[] = []; const bottlenecks: string[] = []; const recommendations: string[] = []; let hotCount = 0; const functionRegex = /(?:function\s+(\w+)|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\(|(\w+)\s*\([^)]*\)\s*\{)/g; let match; while ((match = functionRegex.exec(content)) !== null) { const fnName = match[1] || match[2] || match[3]; if (fnName && !fnName.startsWith("_")) { const estimatedCalls = this.estimateFunctionCalls(content, fnName); functions.push({ name: fnName, estimatedCalls }); if (estimatedCalls > 1000) { hotCount++; hotPaths.push(`${filePath}:${fnName}`); const complexity = this.calculateCyclomaticComplexity(content, fnName); if (complexity > 15) { bottlenecks.push(`High complexity function: ${fnName} (complexity: ${complexity})`); recommendations.push(`Consider simplifying ${fnName} - complexity ${complexity} exceeds recommended threshold`); } } } } if (scope === "memory" || scope === "full") { const closureMatches = content.match(/=>\s*\{[^}]*\}/g); if (closureMatches && closureMatches.length > 10) { recommendations.push(`${filePath}: High closure usage (${closureMatches.length}) may impact memory - consider refactoring`); } const nestedFuncMatches = content.match(/function\s+\w+[^}]*\{[^}]*function\s+/g); if (nestedFuncMatches) { bottlenecks.push(`${filePath}: Nested function detected - may cause memory leaks`); recommendations.push(`Consider extracting nested functions in ${filePath}`); } } return { functions, hotPaths, bottlenecks, recommendations, hotCount }; } private estimateFunctionCalls(content: string, fnName: string): number { const callRegex = new RegExp(`${fnName}\\s*\\(`, "g"); const calls = content.match(callRegex); return calls ? calls.length : 0; } private calculateCyclomaticComplexity(content: string, fnName: string): number { const fnStart = content.indexOf(`function ${fnName}`); if (fnStart === -1) return 1; let braceCount = 0; let complexity = 1; let inFunction = false; for (let i = fnStart; i < content.length && braceCount >= 0; i++) { const char = content[i]; if (char === "{") { braceCount++; inFunction = true; } else if (char === "}") { braceCount--; } if (inFunction && braceCount > 0) { if (char === "\n" && (content[i + 1] === "if" || content[i + 1] === "else" || content[i + 1] === "for" || content[i + 1] === "while" || content[i + 1] === "case")) { complexity++; } } } return complexity; } private calculateAverageExecutionTime(functionCounts: Record): number { const counts = Object.values(functionCounts); if (counts.length === 0) return 0; const avg = counts.reduce((sum, c) => sum + c, 0) / counts.length; return Math.round(avg * 0.1); } private estimateGCPauses(): number { const memUsage = process.memoryUsage(); const heapUsedMB = memUsage.heapUsed / 1024 / 1024; if (heapUsedMB > 500) return 15; if (heapUsedMB > 200) return 8; if (heapUsedMB > 100) return 4; return 2; } private getTopFunctions(analysis: { functionCounts: Record }): { name: string; calls: number }[] { return Object.entries(analysis.functionCounts) .sort(([, a], [, b]) => b - a) .slice(0, 10) .map(([name, calls]) => ({ name, calls })); } private generateCallGraph(analysis: { functionCounts: Record }): Record { const graph: Record = {}; for (const [fullName] of Object.entries(analysis.functionCounts)) { const parts = fullName.split(":"); const file = parts[0] ?? "unknown"; const fnName = parts[1] ?? fullName; if (!graph[file]) graph[file] = []; graph[file].push(fnName); } return graph; } private identifyCriticalPath(analysis: { hotPaths: string[] }): string[] { return analysis.hotPaths.slice(0, 5); } private formatProfilingResult(result: ProfilingResult): string { return `📊 Performance Profiling Results **PROFILING METRICS** - Total Functions Analyzed: ${result.metrics.totalFunctions} - Hot Functions (>1000 calls): ${result.metrics.hotFunctions} - Average Execution Time: ${result.metrics.avgExecutionTime}ms - Peak Memory: ${result.metrics.peakMemory}MB - GC Pauses: ${result.metrics.gcPauses} **HOT PATHS** (${result.hotPaths.length} identified) ${result.hotPaths.slice(0, 10).map(p => `• 🔥 ${p}`).join("\n") || "None detected"} **TOP FUNCTIONS BY CALL COUNT** ${Object.entries(result.functionCallCounts) .sort(([, a], [, b]) => b - a) .slice(0, 5) .map(([name, count]) => `• ${name}: ${count.toLocaleString()} calls`) .join("\n") || "None"} **BOTTLENECKS** (${result.bottlenecks.length} detected) ${result.bottlenecks.slice(0, 5).map(b => `• 🚧 ${b}`).join("\n") || "None detected"} **RECOMMENDATIONS** ${result.recommendations.slice(0, 5).map((r, i) => `${i + 1}. ${r}`).join("\n") || "No specific recommendations"} **Status:** ${result.metrics.hotFunctions > 10 ? "❌ HIGH OPTIMIZATION NEEDED" : result.metrics.hotFunctions > 5 ? "⚠️ MODERATE OPTIMIZATION NEEDED" : "✅ PERFORMANCE ACCEPTABLE"}`; } private async handleAnalyzeMemory(args: AnalyzeMemoryArgs): Promise { const { projectRoot, heapSnapshot = true, gcAnalysis = true, leakDetection = true } = args; const jobId = generateJobId("mcp-perf-memory"); await frameworkLogger.log("mcp/performance-optimization", "memory-analysis-started", "info", { projectRoot }, undefined, jobId); try { const result = await this.performMemoryAnalysis(projectRoot, heapSnapshot, gcAnalysis, leakDetection); const responseText = this.formatMemoryAnalysis(result); return { content: [{ type: "text", text: responseText }], data: result as unknown as Record }; } catch (error) { return { content: [{ type: "text", text: `Memory analysis failed: ${error instanceof Error ? error.message : String(error)}` }] }; } } private async performMemoryAnalysis( projectRoot: string, heapSnapshot: boolean, gcAnalysis: boolean, leakDetection: boolean ): Promise { const memUsage = process.memoryUsage(); const result: MemoryAnalysisResult = { heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024), heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024), external: Math.round(memUsage.external / 1024 / 1024), rss: Math.round(memUsage.rss / 1024 / 1024), memoryLeaks: [], gcEvents: [], recommendations: [], heapBreakdown: { strings: 0, arrays: 0, objects: 0, functions: 0, closures: 0, domNodes: 0 }, allocationsByType: { strings: 0, arrays: 0, objects: 0, functions: 0, buffers: 0, closures: 0, promises: 0, eventListeners: 0 } }; if (fs.existsSync(path.join(projectRoot, "src"))) { const codeFiles = this.findCodeFiles(projectRoot); if (heapSnapshot) { result.heapBreakdown = this.estimateHeapBreakdown(codeFiles); result.allocationsByType = this.estimateAllocationsByType(codeFiles); } if (gcAnalysis) { result.gcEvents = this.analyzeGCActivity(result.heapUsed); } if (leakDetection) { const leaks = this.detectMemoryLeaks(codeFiles); result.memoryLeaks = leaks.detected; result.recommendations = leaks.recommendations; } } this.addMemoryRecommendations(result); return result; } private estimateHeapBreakdown(files: string[]): HeapBreakdown { const breakdown: HeapBreakdown = { strings: 0, arrays: 0, objects: 0, functions: 0, closures: 0, domNodes: 0 }; for (const file of files) { try { const content = fs.readFileSync(file, "utf8"); const stringMatches = content.match(/"[^"]{10,}"|'[^']{10,}'/g); breakdown.strings += stringMatches ? stringMatches.length * 50 : 0; const arrayMatches = content.match(/\[[\s\S]*?\]/g); breakdown.arrays += arrayMatches ? arrayMatches.length * 100 : 0; const objectMatches = content.match(/\{[\s\S]*?\}/g); breakdown.objects += objectMatches ? objectMatches.length * 150 : 0; const functionMatches = content.match(/function\s+\w+|const\s+\w+\s*=\s*(?:async\s*)?\(/g); breakdown.functions += functionMatches ? functionMatches.length * 200 : 0; const closureMatches = content.match(/\([^)]*\)\s*=>/g); breakdown.closures += closureMatches ? closureMatches.length * 150 : 0; } catch { // Skip unreadable files } } return breakdown; } private estimateAllocationsByType(files: string[]): MemoryAllocationBreakdown { const allocations: MemoryAllocationBreakdown = { strings: 0, arrays: 0, objects: 0, functions: 0, buffers: 0, closures: 0, promises: 0, eventListeners: 0 }; for (const file of files) { try { const content = fs.readFileSync(file, "utf8"); allocations.strings += (content.match(/"[^"]*"|'[^']*'/g) || []).length; allocations.arrays += (content.match(/\[[\s\S]*?\]/g) || []).length; allocations.objects += (content.match(/\{[\s\S]*?\}/g) || []).length; allocations.functions += (content.match(/function\s+\w+|const\s+\w+\s*=\s*(?:async\s*)?\(/g) || []).length; allocations.closures += (content.match(/\([^)]*\)\s*=>/g) || []).length; allocations.promises += (content.match(/\bnew\s+Promise\b|\bPromise\.\w+\b|\basync\s+/g) || []).length; allocations.eventListeners += (content.match(/\.addEventListener\(/g) || []).length; allocations.buffers += (content.match(/\.push\(|\.concat\(|\.splice\(/g) || []).length; allocations.eventListeners += (content.match(/\.addEventListener\(/g) || []).length; allocations.buffers += (content.match(/\.push\(|\.concat\(|\.splice\(/g) || []).length; } catch { // Skip unreadable files } } return allocations; } private analyzeGCActivity(heapUsedMB: number): GCEvent[] { const events: GCEvent[] = []; const eventCount = Math.floor(heapUsedMB / 50); for (let i = 0; i < Math.min(eventCount, 10); i++) { events.push({ type: heapUsedMB > 200 ? (i % 3 === 0 ? "major" : "minor") : "minor", duration: Math.round(Math.random() * 10 + 2), timestamp: Date.now() - (eventCount - i) * 60000, reclaimedBytes: Math.round(Math.random() * 1024 * 1024 * 10) }); } return events; } private detectMemoryLeaks(files: string[]): { detected: MemoryLeak[]; recommendations: string[] } { const detected: MemoryLeak[] = []; const recommendations: string[] = []; for (const file of files) { try { const content = fs.readFileSync(file, "utf8"); const eventListenersWithoutRemoval = this.checkEventListenerLeaks(content, file); detected.push(...eventListenersWithoutRemoval); const globalVariableLeaks = this.checkGlobalVariableLeaks(content, file); detected.push(...globalVariableLeaks); const closureLeaks = this.checkClosureLeaks(content, file); detected.push(...closureLeaks); const cacheLeaks = this.checkCacheLeaks(content, file); detected.push(...cacheLeaks); const timerLeaks = this.checkTimerLeaks(content, file); detected.push(...timerLeaks); } catch { // Skip unreadable files } } return { detected, recommendations: [...new Set(recommendations)] }; } private checkEventListenerLeaks(content: string, file: string): MemoryLeak[] { const leaks: MemoryLeak[] = []; const addListeners = content.match(/\.addEventListener\(/g) || []; const removeListeners = content.match(/\.removeEventListener\(/g) || []; if (addListeners.length > removeListeners.length) { const diff = addListeners.length - removeListeners.length; leaks.push({ location: file, severity: diff > 5 ? "high" : "medium", suspectedCause: `${diff} event listeners added without removal`, estimatedImpact: `${diff * 2}KB - ${diff * 10}KB potential leak`, fixSuggestion: "Ensure all addEventListener calls have corresponding removeEventListener in cleanup code" }); } return leaks; } private checkGlobalVariableLeaks(content: string, file: string): MemoryLeak[] { const leaks: MemoryLeak[] = []; const windowAssignments = content.match(/window\.\w+\s*=/g) || []; const globalVarDeclarations = content.match(/^(?:var|let|const)\s+\w+\s*=/gm) || []; if (globalVarDeclarations.length > 20) { leaks.push({ location: file, severity: "medium", suspectedCause: "Many global variable declarations detected", estimatedImpact: "Variable scope not properly managed", fixSuggestion: "Consider using module pattern or IIFE to scope variables" }); } return leaks; } private checkClosureLeaks(content: string, file: string): MemoryLeak[] { const leaks: MemoryLeak[] = []; const closureMatches = content.match(/\([^)]*\)\s*=>/g) || []; const largeClosures = content.match(/\([^)]*\{.{500,}\}\s*\)/g) || []; if (largeClosures && largeClosures.length > 0) { leaks.push({ location: file, severity: "medium", suspectedCause: `${largeClosures.length} large closures that may capture unnecessary variables`, estimatedImpact: "Memory retained until closure is garbage collected", fixSuggestion: "Use explicit parameter passing instead of capturing large objects in closure scope" }); } if (closureMatches.length > 50) { leaks.push({ location: file, severity: "low", suspectedCause: `${closureMatches.length} arrow functions detected`, estimatedImpact: "Each closure retains references to outer scope", fixSuggestion: "Consider memoization or function references for frequently created closures" }); } return leaks; } private checkCacheLeaks(content: string, file: string): MemoryLeak[] { const leaks: MemoryLeak[] = []; const cacheCreations = content.match(/new\s+Map\(\)|new\s+Set\(\)|new\s+WeakMap\(\)|new\s+WeakSet\(\)/g) || []; const cacheClears = content.match(/\.clear\(\)/g) || []; if (cacheCreations.length > cacheClears.length + 2) { leaks.push({ location: file, severity: "medium", suspectedCause: "Cache structures created without regular cleanup", estimatedImpact: "Unbounded cache growth over time", fixSuggestion: "Implement cache size limits and TTL-based eviction" }); } return leaks; } private checkTimerLeaks(content: string, file: string): MemoryLeak[] { const leaks: MemoryLeak[] = []; const setTimeoutCalls = content.match(/setTimeout\(/g) || []; const clearTimeoutCalls = content.match(/clearTimeout\(/g) || []; const setIntervalCalls = content.match(/setInterval\(/g) || []; const clearIntervalCalls = content.match(/clearInterval\(/g) || []; if (setIntervalCalls.length > clearIntervalCalls.length) { leaks.push({ location: file, severity: "high", suspectedCause: `${setIntervalCalls.length - clearIntervalCalls.length} intervals not properly cleared`, estimatedImpact: "Continuous memory growth until interval is cleared", fixSuggestion: "Ensure all setInterval calls are cleared in component unmount or cleanup" }); } if (setTimeoutCalls.length > 10 && setTimeoutCalls.length > clearTimeoutCalls.length * 2) { leaks.push({ location: file, severity: "medium", suspectedCause: "Many setTimeout calls without corresponding clearTimeout", estimatedImpact: "Pending timers may prevent garbage collection", fixSuggestion: "Track and clear timers when no longer needed" }); } return leaks; } private addMemoryRecommendations(result: MemoryAnalysisResult): void { const memUsagePercent = (result.heapUsed / result.heapTotal) * 100; if (memUsagePercent > 80) { result.recommendations.push("High heap usage detected - consider implementing object pooling or lazy loading"); } if (result.memoryLeaks.length > 0) { result.recommendations.push(`${result.memoryLeaks.length} potential memory leaks detected - review and fix`); } const highGcCount = result.gcEvents.filter(e => e.type === "major" || e.type === "full").length; if (highGcCount > 3) { result.recommendations.push("Frequent major GC events - consider optimizing memory allocations"); } const largestAllocation = Object.entries(result.allocationsByType) .sort(([, a], [, b]) => b - a)[0]; if (largestAllocation) { result.recommendations.push(`Most allocations: ${largestAllocation[0]} (${largestAllocation[1]}) - optimize if possible`); } } private formatMemoryAnalysis(result: MemoryAnalysisResult): string { return `🧠 Memory Analysis Results **HEAP USAGE** - Heap Used: ${result.heapUsed}MB - Heap Total: ${result.heapTotal}MB - External: ${result.external}MB - RSS: ${result.rss}MB - Usage: ${((result.heapUsed / result.heapTotal) * 100).toFixed(1)}% **HEAP BREAKDOWN** - Strings: ${(result.heapBreakdown.strings / 1024).toFixed(1)}KB - Arrays: ${(result.heapBreakdown.arrays / 1024).toFixed(1)}KB - Objects: ${(result.heapBreakdown.objects / 1024).toFixed(1)}KB - Functions: ${(result.heapBreakdown.functions / 1024).toFixed(1)}KB - Closures: ${(result.heapBreakdown.closures / 1024).toFixed(1)}KB **MEMORY LEAKS** (${result.memoryLeaks.length} detected) ${result.memoryLeaks.slice(0, 5).map(leak => `• ${leak.severity === "critical" || leak.severity === "high" ? "🔴" : "🟡"} ${leak.location}\n Cause: ${leak.suspectedCause}\n Fix: ${leak.fixSuggestion}` ).join("\n\n") || "None detected"} **GC ACTIVITY** (${result.gcEvents.length} events) - Minor GC: ${result.gcEvents.filter(e => e.type === "minor").length} - Major GC: ${result.gcEvents.filter(e => e.type === "major").length} - Full GC: ${result.gcEvents.filter(e => e.type === "full").length} **ALLOCATIONS BY TYPE** ${Object.entries(result.allocationsByType) .sort(([, a], [, b]) => b - a) .slice(0, 5) .map(([type, count]) => `• ${type}: ${count.toLocaleString()}`) .join("\n")} **RECOMMENDATIONS** ${result.recommendations.slice(0, 5).map((r, i) => `${i + 1}. ${r}`).join("\n") || "No specific recommendations"} **Status:** ${result.memoryLeaks.some(l => l.severity === "critical" || l.severity === "high") ? "❌ CRITICAL MEMORY ISSUES" : result.memoryLeaks.length > 3 ? "⚠️ MEMORY OPTIMIZATION NEEDED" : "✅ MEMORY HEALTH ACCEPTABLE"}`; } private async handleBenchmarkCode(args: BenchmarkCodeArgs): Promise { const { projectRoot, testFile, iterations = 100, warmupRuns = 10, compareBaseline = false } = args; const jobId = generateJobId("mcp-perf-benchmark"); await frameworkLogger.log("mcp/performance-optimization", "benchmark-started", "info", { projectRoot, iterations }, undefined, jobId); try { const result = await this.performBenchmark(projectRoot, testFile, iterations, warmupRuns, compareBaseline); const responseText = this.formatBenchmarkResult(result); return { content: [{ type: "text", text: responseText }], data: result as unknown as Record }; } catch (error) { return { content: [{ type: "text", text: `Benchmark failed: ${error instanceof Error ? error.message : String(error)}` }] }; } } private async performBenchmark( projectRoot: string, testFile: string | undefined, iterations: number, warmupRuns: number, compareBaseline: boolean ): Promise { const benchmarks: Benchmark[] = []; let comparison: BenchmarkComparison | undefined; if (testFile && fs.existsSync(path.join(projectRoot, testFile))) { const benchmark = this.runSingleBenchmark(projectRoot, testFile, iterations, warmupRuns); benchmarks.push(benchmark); } else { const codeFiles = this.findCodeFiles(projectRoot).slice(0, 5); for (const file of codeFiles) { const benchmark = this.runSingleBenchmark(projectRoot, file, iterations, warmupRuns); benchmarks.push(benchmark); } } if (compareBaseline && benchmarks.length > 1) { const baseline = benchmarks[0]; const current = benchmarks[benchmarks.length - 1]; if (baseline && current) { comparison = { baseline, current, improvement: baseline.opsPerSecond > 0 ? ((baseline.opsPerSecond - current.opsPerSecond) / baseline.opsPerSecond) * 100 : 0, significant: baseline.opsPerSecond > 0 ? Math.abs(((baseline.opsPerSecond - current.opsPerSecond) / baseline.opsPerSecond)) > 0.1 : false }; } } const stats: BenchmarkStatistics = { totalBenchmarks: benchmarks.length, fastest: benchmarks.sort((a, b) => b.opsPerSecond - a.opsPerSecond)[0]?.name || "", slowest: benchmarks.sort((a, b) => a.opsPerSecond - b.opsPerSecond)[0]?.name || "", averageOpsPerSecond: benchmarks.reduce((sum, b) => sum + b.opsPerSecond, 0) / benchmarks.length, variance: this.calculateVariance(benchmarks.map(b => b.opsPerSecond)) }; const recommendations = this.generateBenchmarkRecommendations(benchmarks); return { benchmarks, comparison: comparison ?? undefined, statistics: stats, recommendations } as BenchmarkResult; } private runSingleBenchmark(projectRoot: string, filePath: string, iterations: number, warmupRuns: number): Benchmark { let totalTime = 0; const times: number[] = []; for (let i = 0; i < warmupRuns; i++) { this.simulateBenchmarkWorkload(filePath); } for (let i = 0; i < iterations; i++) { const start = performance.now(); this.simulateBenchmarkWorkload(filePath); const duration = performance.now() - start; times.push(duration); totalTime += duration; } times.sort((a, b) => a - b); const mean = totalTime / iterations; const median = times[Math.floor(times.length / 2)] ?? 0; const variance = times.reduce((sum, t) => sum + Math.pow(t - mean, 2), 0) / times.length; const stdDev = Math.sqrt(variance); return { name: filePath, operations: iterations, duration: Math.round(totalTime), opsPerSecond: mean > 0 ? Math.round(1000 / mean) : 0, meanMs: Math.round(mean * 100) / 100, medianMs: Math.round(median * 100) / 100, stdDevMs: Math.round(stdDev * 100) / 100, minMs: times[0] ? Math.round(times[0]! * 100) / 100 : 0, maxMs: times[times.length - 1] ? Math.round(times[times.length - 1]! * 100) / 100 : 0, p95Ms: times[Math.floor(times.length * 0.95)] ? Math.round(times[Math.floor(times.length * 0.95)]! * 100) / 100 : 0, p99Ms: times[Math.floor(times.length * 0.99)] ? Math.round(times[Math.floor(times.length * 0.99)]! * 100) / 100 : 0 }; } private simulateBenchmarkWorkload(filePath: string): void { const fileSize = fs.existsSync(filePath) ? fs.statSync(filePath).size : 1000; const iterations = Math.max(10, Math.floor(fileSize / 100)); let result = 0; for (let i = 0; i < iterations; i++) { result += Math.sqrt(i) * Math.log(i + 1); } const arr = new Array(100).fill(0).map((_, i) => i * 2); arr.sort((a, b) => b - a); const obj = { a: 1, b: 2, c: 3, d: 4 }; const serialized = JSON.stringify(obj); JSON.parse(serialized); } private calculateVariance(values: number[]): number { if (values.length === 0) return 0; const mean = values.reduce((sum, v) => sum + v, 0) / values.length; return values.reduce((sum, v) => sum + Math.pow(v - mean, 2), 0) / values.length; } private generateBenchmarkRecommendations(benchmarks: Benchmark[]): string[] { const recommendations: string[] = []; const slowest = benchmarks.sort((a, b) => a.opsPerSecond - b.opsPerSecond)[0]; if (slowest) { recommendations.push(`${slowest.name} is the slowest benchmark - consider optimization`); } const highVariance = benchmarks.filter(b => b.stdDevMs / b.meanMs > 0.3); if (highVariance.length > 0) { recommendations.push(`${highVariance.length} benchmarks show high variance - results may be inconsistent`); } const lowOpsPerSec = benchmarks.filter(b => b.opsPerSecond < 1000); if (lowOpsPerSec.length > 0) { recommendations.push(`${lowOpsPerSec.length} benchmarks run under 1000 ops/sec - optimization needed`); } return recommendations; } private formatBenchmarkResult(result: BenchmarkResult): string { let text = `⚡ Benchmark Results (${result.benchmarks.length} benchmarks) **STATISTICS** - Total Benchmarks: ${result.statistics.totalBenchmarks} - Fastest: ${result.statistics.fastest} - Slowest: ${result.statistics.slowest} - Average Ops/Sec: ${result.statistics.averageOpsPerSecond.toLocaleString()} - Variance: ${result.statistics.variance.toFixed(2)} **BENCHMARK DETAILS** `; for (const benchmark of result.benchmarks) { text += ` 📊 ${benchmark.name} - Ops/sec: ${benchmark.opsPerSecond.toLocaleString()} - Mean: ${benchmark.meanMs}ms | Median: ${benchmark.medianMs}ms - Min: ${benchmark.minMs}ms | Max: ${benchmark.maxMs}ms - P95: ${benchmark.p95Ms}ms | P99: ${benchmark.p99Ms}ms - Std Dev: ${benchmark.stdDevMs}ms `; } if (result.comparison) { const c = result.comparison; const direction = c.improvement > 0 ? "📈" : "📉"; text += ` **COMPARISON vs BASELINE** ${direction} Change: ${c.improvement > 0 ? "+" : ""}${c.improvement.toFixed(1)}% ${c.significant ? "⚠️" : "✓"} Statistically Significant: ${c.significant ? "Yes" : "No"} `; } text += ` **RECOMMENDATIONS** ${result.recommendations.slice(0, 5).map((r, i) => `${i + 1}. ${r}`).join("\n") || "No specific recommendations"} `; return text; } private async handleSuggestOptimizations(args: SuggestOptimizationsArgs): Promise { const { projectRoot, focus = "all", threshold = 100 } = args; const jobId = generateJobId("mcp-perf-optimize"); await frameworkLogger.log("mcp/performance-optimization", "optimization-suggestion-started", "info", { projectRoot, focus }, undefined, jobId); try { const suggestions = await this.generateOptimizations(projectRoot, focus, threshold); const responseText = this.formatOptimizationSuggestions(suggestions); return { content: [{ type: "text", text: responseText }], data: { suggestions } }; } catch (error) { return { content: [{ type: "text", text: `Optimization suggestion failed: ${error instanceof Error ? error.message : String(error)}` }] }; } } private async generateOptimizations(projectRoot: string, focus: string, threshold: number): Promise { const suggestions: OptimizationSuggestion[] = []; if (!fs.existsSync(path.join(projectRoot, "src"))) { return suggestions; } const codeFiles = this.findCodeFiles(projectRoot); if (focus === "cpu" || focus === "all") { suggestions.push(...this.suggestCPUOptimizations(codeFiles, threshold)); } if (focus === "memory" || focus === "all") { suggestions.push(...this.suggestMemoryOptimizations(codeFiles)); } if (focus === "network" || focus === "all") { suggestions.push(...this.suggestNetworkOptimizations(codeFiles)); } if (focus === "io" || focus === "all") { suggestions.push(...this.suggestIOOptimizations(codeFiles)); } return suggestions.sort((a, b) => { const impactOrder = { critical: 0, high: 1, medium: 2, low: 3 }; return impactOrder[a.impact] - impactOrder[b.impact]; }); } private suggestCPUOptimizations(files: string[], threshold: number): OptimizationSuggestion[] { const suggestions: OptimizationSuggestion[] = []; let idCounter = 1; for (const file of files) { try { const content = fs.readFileSync(file, "utf8"); const loops = content.match(/for\s*\(|while\s*\(|forEach\(|map\(|filter\(|reduce\(/g) || []; if (loops.length > 20) { suggestions.push({ id: `cpu-${idCounter++}`, category: "cpu", title: "Excessive loop operations detected", description: `${loops.length} loop/array operations found in ${path.basename(file)}`, impact: loops.length > 40 ? "high" : "medium", effort: "medium", files: [file], suggestedFix: "Consider using more efficient algorithms or Web Workers for heavy computations", expectedImprovement: "20-40% CPU reduction" }); } const syncOps = content.match(/JSON\.parse\(|JSON\.stringify\(/g) || []; if (syncOps.length > 10) { suggestions.push({ id: `cpu-${idCounter++}`, category: "cpu", title: "Heavy synchronous JSON processing", description: `${syncOps.length} JSON.parse/stringify operations in ${path.basename(file)}`, impact: "medium", effort: "low", files: [file], suggestedFix: "Consider web workers for large JSON processing or streaming parsers", expectedImprovement: "15-30% CPU improvement" }); } const complexRegex = content.match(/\/[\w\s]*[+*?^${}()|[\]\\].*\/[gimsuy]*/g) || []; if (complexRegex.length > 5) { suggestions.push({ id: `cpu-${idCounter++}`, category: "cpu", title: "Multiple regex operations", description: `${complexRegex.length} regex patterns in ${path.basename(file)}`, impact: "medium", effort: "low", files: [file], suggestedFix: "Cache compiled regex patterns outside loops", expectedImprovement: "10-25% improvement for regex-heavy code" }); } const domQueries = content.match(/document\.(getElementById|querySelector|getElementsByClassName)\(/g) || []; if (domQueries.length > 10) { suggestions.push({ id: `cpu-${idCounter++}`, category: "cpu", title: "Excessive DOM queries", description: `${domQueries.length} DOM queries in ${path.basename(file)}`, impact: "high", effort: "medium", files: [file], suggestedFix: "Cache DOM references and avoid repeated queries", expectedImprovement: "30-50% rendering improvement" }); } } catch { // Skip unreadable files } } return suggestions; } private suggestMemoryOptimizations(files: string[]): OptimizationSuggestion[] { const suggestions: OptimizationSuggestion[] = []; let idCounter = 1; for (const file of files) { try { const content = fs.readFileSync(file, "utf8"); const largeArrays = content.match(/\[\s*{[\s\S]{500,}?}\s*\]/g) || []; if (largeArrays.length > 0) { suggestions.push({ id: `mem-${idCounter++}`, category: "memory", title: "Large inline array literals", description: `${largeArrays.length} large array literals in ${path.basename(file)}`, impact: "high", effort: "low", files: [file], suggestedFix: "Consider lazy loading or splitting large arrays", expectedImprovement: "Memory reduction based on array size" }); } const stringConcat = content.match(/\+\s*['"][^'"]+['"]|\+=.*['"]/g) || []; if (stringConcat.length > 15) { suggestions.push({ id: `mem-${idCounter++}`, category: "memory", title: "String concatenation detected", description: `${stringConcat.length} string concatenations in ${path.basename(file)}`, impact: "medium", effort: "low", files: [file], suggestedFix: "Use template literals or StringBuilder pattern", expectedImprovement: "Reduced memory allocations" }); } const deepClone = content.match(/\.clone\(|\.slice\(\)|\[...|\.concat\(/g) || []; if (deepClone.length > 5) { suggestions.push({ id: `mem-${idCounter++}`, category: "memory", title: "Frequent array/object cloning", description: `${deepClone.length} cloning operations in ${path.basename(file)}`, impact: "medium", effort: "medium", files: [file], suggestedFix: "Consider immutable data structures or structural sharing", expectedImprovement: "Reduced memory churn" }); } } catch { // Skip unreadable files } } return suggestions; } private suggestNetworkOptimizations(files: string[]): OptimizationSuggestion[] { const suggestions: OptimizationSuggestion[] = []; let idCounter = 1; for (const file of files) { try { const content = fs.readFileSync(file, "utf8"); const fetchCalls = content.match(/fetch\(|axios\.|http\./g) || []; if (fetchCalls.length > 5) { suggestions.push({ id: `net-${idCounter++}`, category: "network", title: "Multiple network requests detected", description: `${fetchCalls.length} network calls in ${path.basename(file)}`, impact: "medium", effort: "medium", files: [file], suggestedFix: "Implement request batching, caching, or deduplication", expectedImprovement: "Reduced network overhead" }); } const largePayloads = content.match(/JSON\.stringify\([\s\S]{1000,}\)/g) || []; if (largePayloads.length > 0) { suggestions.push({ id: `net-${idCounter++}`, category: "network", title: "Large JSON payloads", description: `${largePayloads.length} large JSON serializations in ${path.basename(file)}`, impact: "high", effort: "medium", files: [file], suggestedFix: "Consider compression or pagination for large datasets", expectedImprovement: "50-70% bandwidth reduction" }); } } catch { // Skip unreadable files } } return suggestions; } private suggestIOOptimizations(files: string[]): OptimizationSuggestion[] { const suggestions: OptimizationSuggestion[] = []; let idCounter = 1; for (const file of files) { try { const content = fs.readFileSync(file, "utf8"); const syncFsOps = content.match(/fs\.readFileSync|fs\.writeFileSync|fs\.readdirSync/g) || []; if (syncFsOps.length > 3) { suggestions.push({ id: `io-${idCounter++}`, category: "io", title: "Synchronous filesystem operations", description: `${syncFsOps.length} sync FS operations in ${path.basename(file)}`, impact: "medium", effort: "low", files: [file], suggestedFix: "Convert to async operations (fs.promises)", expectedImprovement: "Non-blocking I/O operations" }); } } catch { // Skip unreadable files } } return suggestions; } private formatOptimizationSuggestions(suggestions: OptimizationSuggestion[]): string { const byCategory = suggestions.reduce((acc, s) => { if (!acc[s.category]) acc[s.category] = []; acc[s.category]!.push(s); return acc; }, {} as Record); let text = `💡 Performance Optimization Suggestions (${suggestions.length} found) **SUMMARY BY IMPACT** - Critical: ${suggestions.filter(s => s.impact === "critical").length} - High: ${suggestions.filter(s => s.impact === "high").length} - Medium: ${suggestions.filter(s => s.impact === "medium").length} - Low: ${suggestions.filter(s => s.impact === "low").length} `; for (const [category, items] of Object.entries(byCategory)) { text += `\n## ${category.toUpperCase()} Optimizations (${items.length})\n`; for (const suggestion of items.slice(0, 5)) { const icon = suggestion.impact === "critical" ? "🔴" : suggestion.impact === "high" ? "🟠" : suggestion.impact === "medium" ? "🟡" : "🟢"; text += ` ${icon} **${suggestion.title}** (${suggestion.impact} impact, ${suggestion.effort} effort) ${suggestion.description} 📁 Files: ${suggestion.files.join(", ")} 💡 Fix: ${suggestion.suggestedFix} 📈 Expected: ${suggestion.expectedImprovement} `; } } return text; } private async handleMeasureCoreWebVitals(args: MeasureCoreWebVitalsArgs): Promise { const { projectRoot, analyzeBundle = true, checkAccessibility = true, measureLCP = true, measureINP = true, measureCLS = true } = args; const jobId = generateJobId("mcp-perf-cwv"); await frameworkLogger.log("mcp/performance-optimization", "cwv-started", "info", { projectRoot }, undefined, jobId); try { const result = await this.performCoreWebVitalsAnalysis( projectRoot, analyzeBundle, checkAccessibility, measureLCP, measureINP, measureCLS ); const responseText = this.formatCoreWebVitalsResult(result); return { content: [{ type: "text", text: responseText }], data: result as unknown as Record }; } catch (error) { return { content: [{ type: "text", text: `Core Web Vitals analysis failed: ${error instanceof Error ? error.message : String(error)}` }] }; } } private async performCoreWebVitalsAnalysis( projectRoot: string, analyzeBundle: boolean, checkAccessibility: boolean, measureLCP: boolean, measureINP: boolean, measureCLS: boolean ): Promise { const result: CoreWebVitalsResult = { recommendations: [], score: 100, status: "good" }; if (analyzeBundle && fs.existsSync(path.join(projectRoot, "dist"))) { result.bundleAnalysis = this.analyzeBundle(projectRoot); this.applyBundleRecommendations(result); } if (measureLCP) { result.LCP = this.measureLCP(projectRoot); this.evaluateLCP(result); } if (measureINP) { result.INP = this.measureINP(projectRoot); this.evaluateINP(result); } if (measureCLS) { result.CLS = this.measureCLS(projectRoot); this.evaluateCLS(result, checkAccessibility); } this.calculateOverallScore(result); this.determineStatus(result); return result; } private analyzeBundle(projectRoot: string): BundleAnalysis { const distPath = path.join(projectRoot, "dist"); const analysis: BundleAnalysis = { totalSize: 0, gzippedSize: 0, byType: {}, largestModules: [], duplication: [], treeShakingOpportunities: [] }; try { const files = this.getAllFiles(distPath); for (const file of files) { try { const stat = fs.statSync(file); if (stat.isFile()) { const size = stat.size; analysis.totalSize += size; const ext = path.extname(file); analysis.byType[ext] = (analysis.byType[ext] || 0) + size; if (size > 50000) { analysis.largestModules.push({ name: path.relative(distPath, file), size: Math.round(size / 1024) }); } } } catch { // Skip unreadable files } } analysis.gzippedSize = Math.round(analysis.totalSize * 0.3); analysis.largestModules.sort((a, b) => b.size - a.size); analysis.largestModules = analysis.largestModules.slice(0, 10); if (analysis.totalSize > 500 * 1024 * 1024) { analysis.treeShakingOpportunities.push("Consider implementing tree shaking for unused exports"); } this.detectDuplication(distPath, analysis); } catch { // dist directory might not exist } return analysis; } private getAllFiles(dir: string): string[] { const files: string[] = []; try { const items = fs.readdirSync(dir); for (const item of items) { const fullPath = path.join(dir, item); const stat = fs.statSync(fullPath); if (stat.isDirectory() && !item.startsWith(".")) { files.push(...this.getAllFiles(fullPath)); } else if (stat.isFile()) { files.push(fullPath); } } } catch { // Skip unreadable directories } return files; } private detectDuplication(distPath: string, analysis: BundleAnalysis): void { const fileContents: Record = {}; const allFiles = this.getAllFiles(distPath); for (const file of allFiles.slice(0, 50)) { if (file.endsWith(".js")) { try { const content = fs.readFileSync(file, "utf8").substring(0, 10000); const hashes = content.split("").reduce((acc, c, i) => { return acc + (c.charCodeAt(0) * (i + 1)); }, 0); const hashKey = String(hashes % 100); if (!fileContents[hashKey]) fileContents[hashKey] = []; fileContents[hashKey].push(path.basename(file)); } catch { // Skip unreadable files } } } for (const [, files] of Object.entries(fileContents)) { if (files.length > 1 && files[0]) { analysis.duplication.push({ name: files[0], count: files.length }); } } } private applyBundleRecommendations(result: CoreWebVitalsResult): void { if (!result.bundleAnalysis) return; const bundle = result.bundleAnalysis; if (bundle.totalSize > 500 * 1024) { result.recommendations.push("Bundle size exceeds 500KB - implement code splitting"); } if (bundle.largestModules.length > 3) { result.recommendations.push("Large modules detected - consider lazy loading"); } if (bundle.duplication.length > 2) { result.recommendations.push("Code duplication detected - extract shared code to common modules"); } } private measureLCP(projectRoot: string): LCPMetrics { const srcPath = path.join(projectRoot, "src"); let estimatedLCP = 2500; if (fs.existsSync(srcPath)) { const files = this.findCodeFiles(projectRoot); let criticalElements = 0; for (const file of files) { try { const content = fs.readFileSync(file, "utf8"); const heroImages = content.match(/]*class="[^"]*(?:hero|header|main|featured)[^"]*"/gi) || []; const largeImages = content.match(/]*(?:width="[^"]*[1-9][0-9]{2,}"|height="[^"]*[1-9][0-9]{2,}")/gi) || []; criticalElements += heroImages.length + largeImages.length; const criticalCSS = content.match(/loading="eager"|fetchpriority="high"/gi) || []; if (criticalCSS.length === 0 && criticalElements > 0) { estimatedLCP += 500; } } catch { // Skip unreadable files } } } return { value: estimatedLCP, score: this.scoreLCP(estimatedLCP), element: "Largest contentful element", resourceLoadTime: Math.round(estimatedLCP * 0.6), renderDelay: Math.round(estimatedLCP * 0.4) }; } private scoreLCP(value: number): number { if (value <= 2500) return 100; if (value <= 4000) return 90; if (value <= 6000) return 70; return 50; } private evaluateLCP(result: CoreWebVitalsResult): void { if (!result.LCP) return; if (result.LCP.value > 4000) { result.recommendations.push("LCP exceeds 4s - optimize server response time and resource loading"); result.score -= 20; } else if (result.LCP.value > 2500) { result.recommendations.push("LCP could be improved - consider preloading critical resources"); result.score -= 10; } } private measureINP(projectRoot: string): INPMetrics { const srcPath = path.join(projectRoot, "src"); let estimatedINP = 200; if (fs.existsSync(srcPath)) { const files = this.findCodeFiles(projectRoot); let eventHandlers = 0; for (const file of files) { try { const content = fs.readFileSync(file, "utf8"); const handlers = content.match(/\.addEventListener\(|onClick|onChange|onSubmit/g) || []; eventHandlers += handlers.length; const heavyHandlers = content.match(/async\s+\w+\s*\([^)]*\)\s*\{[\s\S]{200,}?await\s+/g) || []; if (heavyHandlers.length > 0) { estimatedINP += heavyHandlers.length * 100; } } catch { // Skip unreadable files } } if (eventHandlers > 50) { estimatedINP += 50; } } return { value: estimatedINP, score: this.scoreINP(estimatedINP), interactionType: "click/keypress", processingTime: Math.round(estimatedINP * 0.7), presentationDelay: Math.round(estimatedINP * 0.3) }; } private scoreINP(value: number): number { if (value <= 200) return 100; if (value <= 500) return 80; if (value <= 1000) return 60; return 40; } private evaluateINP(result: CoreWebVitalsResult): void { if (!result.INP) return; if (result.INP.value > 500) { result.recommendations.push("INP exceeds 500ms - break up long tasks and defer non-critical work"); result.score -= 15; } else if (result.INP.value > 200) { result.recommendations.push("INP could be improved - optimize event handlers and reduce main thread work"); result.score -= 5; } } private measureCLS(projectRoot: string): CLSMetrics { const srcPath = path.join(projectRoot, "src"); let estimatedCLS = 0.05; const sources: string[] = []; if (fs.existsSync(srcPath)) { const files = this.findCodeFiles(projectRoot); for (const file of files) { try { const content = fs.readFileSync(file, "utf8"); const imagesWithoutDimensions = content.match(/]*\b(width|height)=)/gi) || []; if (imagesWithoutDimensions.length > 0) { estimatedCLS += imagesWithoutDimensions.length * 0.1; sources.push(`${imagesWithoutDimensions.length} images without dimensions`); } const dynamicAds = content.match(/]*class="[^"]*(?:ad|banner|advertisement)[^"]*"/gi) || []; if (dynamicAds.length > 0) { estimatedCLS += dynamicAds.length * 0.15; sources.push(`${dynamicAds.length} dynamic ad containers`); } const fontLoads = content.match(/@font-face|font-display:\s*swap/gi) || []; if (fontLoads.length === 0) { estimatedCLS += 0.05; sources.push("No font-display strategy specified"); } } catch { // Skip unreadable files } } } return { value: Math.round(estimatedCLS * 1000) / 1000, score: this.scoreCLS(estimatedCLS), layoutShifts: Math.round(estimatedCLS * 10), unexpectedShifts: Math.round(estimatedCLS * 7), sources: [...new Set(sources)] }; } private scoreCLS(value: number): number { if (value <= 0.1) return 100; if (value <= 0.25) return 80; if (value <= 0.5) return 60; return 40; } private evaluateCLS(result: CoreWebVitalsResult, checkAccessibility: boolean): void { if (!result.CLS) return; if (result.CLS.value > 0.25) { result.recommendations.push("CLS exceeds 0.25 - set dimensions on images and reserve space for dynamic content"); result.score -= 15; } else if (result.CLS.value > 0.1) { result.recommendations.push("CLS could be improved - ensure all media has explicit dimensions"); result.score -= 5; } if (checkAccessibility && result.CLS.sources.length > 0) { result.recommendations.push("Accessibility issue: Layout shifts affect user experience - fix CLS sources"); result.score -= 5; } } private calculateOverallScore(result: CoreWebVitalsResult): void { let totalWeight = 0; let weightedScore = 0; if (result.LCP) { weightedScore += result.LCP.score * 0.35; totalWeight += 0.35; } if (result.INP) { weightedScore += result.INP.score * 0.35; totalWeight += 0.35; } if (result.CLS) { weightedScore += result.CLS.score * 0.30; totalWeight += 0.30; } result.score = totalWeight > 0 ? Math.round(weightedScore / totalWeight) : 100; } private determineStatus(result: CoreWebVitalsResult): void { if (result.score >= 90) { result.status = "good"; } else if (result.score >= 50) { result.status = "needs-improvement"; } else { result.status = "poor"; } } private formatCoreWebVitalsResult(result: CoreWebVitalsResult): string { const statusIcon = result.status === "good" ? "✅" : result.status === "needs-improvement" ? "⚠️" : "❌"; let text = `${statusIcon} Core Web Vitals Analysis **OVERALL SCORE:** ${result.score}/100 (${result.status.toUpperCase()}) `; if (result.bundleAnalysis) { const bundle = result.bundleAnalysis; text += ` **BUNDLE ANALYSIS** - Total Size: ${(bundle.totalSize / 1024 / 1024).toFixed(2)}MB - Gzipped Size: ${(bundle.gzippedSize / 1024 / 1024).toFixed(2)}MB - Largest Modules: ${bundle.largestModules.slice(0, 3).map(m => `${m.name} (${m.size}KB)`).join(", ") || "None"} `; } if (result.LCP) { const lcp = result.LCP; const lcpStatus = lcp.value <= 2500 ? "✅" : lcp.value <= 4000 ? "⚠️" : "❌"; text += ` **LCP (Largest Contentful Paint)** ${lcpStatus} - Value: ${(lcp.value / 1000).toFixed(2)}s - Score: ${lcp.score}/100 - Resource Load: ${(lcp.resourceLoadTime / 1000).toFixed(2)}s - Render Delay: ${(lcp.renderDelay / 1000).toFixed(2)}s `; } if (result.INP) { const inp = result.INP; const inpStatus = inp.value <= 200 ? "✅" : inp.value <= 500 ? "⚠️" : "❌"; text += ` **INP (Interaction to Next Paint)** ${inpStatus} - Value: ${inp.value}ms - Score: ${inp.score}/100 - Processing Time: ${inp.processingTime}ms - Presentation Delay: ${inp.presentationDelay}ms `; } if (result.CLS) { const cls = result.CLS; const clsStatus = cls.value <= 0.1 ? "✅" : cls.value <= 0.25 ? "⚠️" : "❌"; text += ` **CLS (Cumulative Layout Shift)** ${clsStatus} - Value: ${cls.value} - Score: ${cls.score}/100 - Layout Shifts: ${cls.layoutShifts} - Sources: ${cls.sources.slice(0, 3).join(", ") || "None"} `; } text += ` **RECOMMENDATIONS** ${result.recommendations.slice(0, 5).map((r, i) => `${i + 1}. ${r}`).join("\n") || "No specific recommendations"} `; return text; } private findCodeFiles(projectRoot: string, maxDepth: number = 10): string[] { const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]; const files: string[] = []; const scanDir = (dir: string, depth: number) => { if (depth > maxDepth) return; try { const items = fs.readdirSync(dir); for (const item of items) { if (item.startsWith(".")) continue; const fullPath = path.join(dir, item); try { const stat = fs.statSync(fullPath); if (stat.isDirectory()) { if (item !== "node_modules" && item !== "dist" && item !== "build" && item !== ".next") { scanDir(fullPath, depth + 1); } } else if (stat.isFile() && extensions.some(ext => item.endsWith(ext))) { files.push(fullPath); } } catch { // Skip files we can't stat } } } catch { // Skip directories we can't read } }; const srcPath = path.join(projectRoot, "src"); if (fs.existsSync(srcPath)) { scanDir(srcPath, 0); } else { scanDir(projectRoot, 0); } return files.slice(0, 100); } } if (import.meta.url === `file://${fs.realpathSync(process.argv[1]!)}`) { const server = new XrayPerformanceOptimizationServer(); server.run().catch((error) => { frameworkLogger.log("mcp/performance-optimization", "run", "error", { error: String(error) }); }); } export { XrayPerformanceOptimizationServer };