@origindot./zigzag
Version:
Comprehensive MCP server providing AI agents with visual, development, and design validation tools
369 lines • 15.6 kB
JavaScript
import si from 'systeminformation';
export class PerformanceMonitorTool {
definition = {
name: 'performance_monitor',
description: 'Monitor application performance metrics and system resources',
inputSchema: {
type: 'object',
properties: {
target: {
type: 'string',
description: 'Application or process to monitor'
},
duration: {
type: 'number',
default: 30,
description: 'Monitoring duration in seconds'
},
metrics: {
type: 'array',
items: {
type: 'string',
enum: ['cpu', 'memory', 'gpu', 'network', 'disk', 'fps']
},
default: ['cpu', 'memory'],
description: 'Metrics to collect'
},
interval: {
type: 'number',
default: 1,
description: 'Sampling interval in seconds'
}
},
required: ['target']
}
};
async execute(args) {
try {
const metrics = await this.collectMetrics(args.target, args.duration || 30, args.metrics || ['cpu', 'memory'], args.interval || 1);
const analysis = this.analyzePerformance(metrics);
const recommendations = this.generatePerformanceRecommendations(analysis);
return {
content: [{
type: 'text',
text: JSON.stringify({
summary: analysis.summary,
detailed_metrics: metrics,
performance_analysis: analysis,
recommendations,
alerts: analysis.alerts
}, null, 2)
}],
isError: false
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [{
type: 'text',
text: `Performance monitoring failed: ${errorMessage}`
}],
isError: true
};
}
}
async collectMetrics(target, duration, metricsToCollect, interval) {
const metrics = {
timestamp: [],
cpu: [],
memory: [],
gpu: [],
network: [],
disk: [],
fps: []
};
const startTime = Date.now();
const endTime = startTime + (duration * 1000);
// Find process ID for the target application
const targetPid = await this.findProcessId(target);
while (Date.now() < endTime) {
const timestamp = new Date().toISOString();
metrics.timestamp.push(timestamp);
for (const metric of metricsToCollect) {
try {
switch (metric) {
case 'cpu':
const cpuData = await si.currentLoad();
metrics.cpu.push({
usage: cpuData.currentLoad,
user: cpuData.currentLoadUser,
system: cpuData.currentLoadSystem
});
break;
case 'memory':
const memData = await si.mem();
metrics.memory.push({
used: memData.used,
free: memData.free,
usage_percent: (memData.used / memData.total) * 100
});
break;
case 'gpu':
try {
const gpuData = await si.graphics();
if (gpuData.controllers && gpuData.controllers.length > 0) {
metrics.gpu.push({
utilization: gpuData.controllers[0].utilizationGpu || 0,
memory_used: gpuData.controllers[0].memoryUsed || 0
});
}
else {
metrics.gpu.push({ utilization: 0, memory_used: 0 });
}
}
catch {
metrics.gpu.push({ utilization: 0, memory_used: 0 });
}
break;
case 'network':
const netData = await si.networkStats();
if (netData && netData.length > 0) {
metrics.network.push({
rx_bytes: netData[0].rx_bytes || 0,
tx_bytes: netData[0].tx_bytes || 0
});
}
else {
metrics.network.push({ rx_bytes: 0, tx_bytes: 0 });
}
break;
case 'disk':
const diskData = await si.disksIO();
metrics.disk.push({
read_bytes: diskData.rIO_sec || 0,
write_bytes: diskData.wIO_sec || 0
});
break;
case 'fps':
// FPS measurement would require integration with the target application
// For now, we'll simulate with a placeholder
metrics.fps.push(this.simulateFPS());
break;
}
}
catch (error) {
console.error(`Error collecting ${metric} metric:`, error);
}
}
await new Promise(resolve => setTimeout(resolve, interval * 1000));
}
return metrics;
}
async findProcessId(target) {
try {
const processes = await si.processes();
const targetProcess = processes.list.find(p => p.name.toLowerCase().includes(target.toLowerCase()) ||
p.command.toLowerCase().includes(target.toLowerCase()));
return targetProcess ? targetProcess.pid : null;
}
catch {
return null;
}
}
simulateFPS() {
// Simulate FPS with some variation
const baseFPS = 60;
const variation = (Math.random() - 0.5) * 10;
return Math.max(30, Math.min(120, baseFPS + variation));
}
analyzePerformance(metrics) {
const analysis = {
summary: {},
trends: {},
alerts: [],
bottlenecks: []
};
// CPU analysis
if (metrics.cpu.length > 0) {
const cpuValues = metrics.cpu.map(m => m.usage);
const avgCPU = this.average(cpuValues);
const maxCPU = Math.max(...cpuValues);
const minCPU = Math.min(...cpuValues);
analysis.summary.cpu = {
average: Math.round(avgCPU),
peak: Math.round(maxCPU),
status: avgCPU > 80 ? 'critical' : avgCPU > 60 ? 'warning' : 'normal'
};
if (avgCPU > 80) {
analysis.alerts.push({
type: 'high_cpu_usage',
severity: 'high',
message: `Average CPU usage ${avgCPU.toFixed(1)}% is critically high`
});
analysis.bottlenecks.push('CPU');
}
// Detect CPU spikes
const cpuSpikes = this.detectSpikes(cpuValues, 90);
if (cpuSpikes.length > 0) {
analysis.alerts.push({
type: 'cpu_spikes',
severity: 'medium',
message: `Detected ${cpuSpikes.length} CPU usage spikes above 90%`
});
}
}
// Memory analysis
if (metrics.memory.length > 0) {
const memoryValues = metrics.memory.map(m => m.usage_percent);
const avgMemory = this.average(memoryValues);
const maxMemory = Math.max(...memoryValues);
analysis.summary.memory = {
average: Math.round(avgMemory),
peak: Math.round(maxMemory),
status: avgMemory > 90 ? 'critical' : avgMemory > 75 ? 'warning' : 'normal'
};
if (avgMemory > 90) {
analysis.alerts.push({
type: 'high_memory_usage',
severity: 'high',
message: `Average memory usage ${avgMemory.toFixed(1)}% is critically high`
});
analysis.bottlenecks.push('Memory');
}
// Check for memory leaks
const memoryTrend = this.calculateTrend(memoryValues);
if (memoryTrend > 0.5) { // Increasing trend
analysis.alerts.push({
type: 'potential_memory_leak',
severity: 'medium',
message: 'Memory usage shows increasing trend, possible memory leak'
});
}
}
// FPS analysis
if (metrics.fps.length > 0) {
const avgFPS = this.average(metrics.fps);
const minFPS = Math.min(...metrics.fps);
analysis.summary.fps = {
average: Math.round(avgFPS),
minimum: minFPS,
status: avgFPS < 30 ? 'critical' : avgFPS < 60 ? 'warning' : 'excellent'
};
if (avgFPS < 30) {
analysis.alerts.push({
type: 'low_fps',
severity: 'medium',
message: `Average FPS ${avgFPS.toFixed(1)} is below optimal threshold`
});
analysis.bottlenecks.push('Rendering');
}
// Detect FPS drops
const fpsDrops = this.detectDrops(metrics.fps, 30);
if (fpsDrops.length > 0) {
analysis.alerts.push({
type: 'fps_drops',
severity: 'low',
message: `Detected ${fpsDrops.length} FPS drops below 30`
});
}
}
return analysis;
}
generatePerformanceRecommendations(analysis) {
const recommendations = [];
if (analysis.summary.cpu?.status === 'critical') {
recommendations.push({
category: 'cpu',
priority: 'high',
title: 'Optimize CPU Usage',
description: 'CPU usage is critically high, consider optimization strategies',
actions: [
'Profile CPU-intensive functions using performance profilers',
'Implement debouncing for frequent operations',
'Consider Web Workers for heavy calculations',
'Optimize loops and algorithms',
'Implement lazy loading for expensive operations'
]
});
}
else if (analysis.summary.cpu?.status === 'warning') {
recommendations.push({
category: 'cpu',
priority: 'medium',
title: 'Monitor CPU Usage',
description: 'CPU usage is elevated, monitor for patterns',
actions: [
'Identify peak usage times',
'Consider caching frequently computed values',
'Review background processes'
]
});
}
if (analysis.summary.memory?.status === 'critical') {
recommendations.push({
category: 'memory',
priority: 'high',
title: 'Reduce Memory Usage',
description: 'Memory usage is critically high, implement memory optimization',
actions: [
'Check for memory leaks using heap profilers',
'Implement object pooling for frequently created objects',
'Optimize image and asset loading',
'Review data structures for efficiency',
'Implement proper cleanup in destructors/unmount'
]
});
}
if (analysis.summary.fps?.status !== 'excellent') {
recommendations.push({
category: 'rendering',
priority: 'medium',
title: 'Improve Rendering Performance',
description: 'Frame rate can be improved for better user experience',
actions: [
'Optimize CSS animations using transform and opacity',
'Reduce DOM manipulations and batch updates',
'Use GPU acceleration with will-change property',
'Implement virtual scrolling for long lists',
'Debounce resize and scroll event handlers'
]
});
}
// Add general recommendations based on bottlenecks
if (analysis.bottlenecks.length > 0) {
recommendations.push({
category: 'general',
priority: 'high',
title: 'Address Performance Bottlenecks',
description: `Identified bottlenecks: ${analysis.bottlenecks.join(', ')}`,
actions: [
'Use performance profiling tools to identify specific issues',
'Consider implementing performance budgets',
'Set up continuous performance monitoring',
'Review architecture for scalability issues'
]
});
}
return recommendations;
}
average(values) {
if (values.length === 0)
return 0;
return values.reduce((sum, val) => sum + val, 0) / values.length;
}
detectSpikes(values, threshold) {
return values.map((val, idx) => ({ val, idx }))
.filter(item => item.val > threshold)
.map(item => item.idx);
}
detectDrops(values, threshold) {
return values.map((val, idx) => ({ val, idx }))
.filter(item => item.val < threshold)
.map(item => item.idx);
}
calculateTrend(values) {
if (values.length < 2)
return 0;
// Simple linear regression to detect trend
const n = values.length;
const indices = Array.from({ length: n }, (_, i) => i);
const sumX = indices.reduce((a, b) => a + b, 0);
const sumY = values.reduce((a, b) => a + b, 0);
const sumXY = indices.reduce((sum, x, i) => sum + x * values[i], 0);
const sumX2 = indices.reduce((sum, x) => sum + x * x, 0);
const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
return slope;
}
}
//# sourceMappingURL=performance-monitor.js.map