#!/usr/bin/env node
import fs from 'fs/promises';
import path from 'path';
import { setTimeout } from 'timers/promises';
/**
* Real-time monitoring tool for MCP Self-Learning Server
*
* Watches data files, logs, and system metrics
*/
class ServerMonitor {
constructor(options = {}) {
this.interval = options.interval || 5000; // 5 seconds
this.showDetails = options.details || false;
this.dataPath = path.join(process.cwd(), 'data');
this.logsPath = path.join(process.cwd(), 'logs');
this.running = true;
this.lastData = {
patterns: 0,
knowledge: 0,
learningCycles: 0,
logSize: 0
};
}
async start() {
console.log('๐ MCP Self-Learning Server Monitor');
console.log('===================================');
console.log(`๐ก Monitoring every ${this.interval/1000}s (Press Ctrl+C to stop)\n`);
// Handle graceful shutdown
process.on('SIGINT', () => {
this.running = false;
console.log('\n๐ Monitor stopped');
process.exit(0);
});
while (this.running) {
await this.collectMetrics();
await setTimeout(this.interval);
}
}
async collectMetrics() {
try {
const timestamp = new Date().toLocaleTimeString();
// Learning data metrics
const learningData = await this.readLearningData();
// Log metrics
const logMetrics = await this.readLogMetrics();
// System metrics
const systemMetrics = this.getSystemMetrics();
// Display metrics
console.clear();
console.log('๐ MCP Self-Learning Server Monitor');
console.log('===================================');
console.log(`๐ Last updated: ${timestamp}\n`);
this.displayLearningMetrics(learningData);
this.displayLogMetrics(logMetrics);
this.displaySystemMetrics(systemMetrics);
if (this.showDetails) {
this.displayDetailedMetrics(learningData);
}
console.log('\n๐ก Press Ctrl+C to stop monitoring');
} catch (error) {
console.error('โ Error collecting metrics:', error.message);
}
}
async readLearningData() {
try {
const filePath = path.join(this.dataPath, 'learning-engine.json');
const content = await fs.readFile(filePath, 'utf8');
const data = JSON.parse(content);
return {
patterns: Array.isArray(data.patterns) ? data.patterns.length : 0,
knowledge: Array.isArray(data.knowledge) ? data.knowledge.length : 0,
learningCycles: data.metrics?.learningCycles || 0,
totalInteractions: data.metrics?.totalInteractions || 0,
successRate: data.metrics?.successRate || 0,
averageResponseTime: data.metrics?.averageResponseTime || 0,
lastSaved: new Date(data.timestamp).toLocaleString(),
fileSize: content.length
};
} catch (error) {
return {
patterns: 0,
knowledge: 0,
learningCycles: 0,
totalInteractions: 0,
successRate: 0,
averageResponseTime: 0,
lastSaved: 'Never',
fileSize: 0,
error: error.message
};
}
}
async readLogMetrics() {
try {
const filePath = path.join(this.logsPath, 'mcp-server.log');
const stats = await fs.stat(filePath);
const content = await fs.readFile(filePath, 'utf8');
const lines = content.split('\n').filter(line => line.trim());
// Count log levels
const levels = {
info: lines.filter(line => line.includes('INFO')).length,
warn: lines.filter(line => line.includes('WARN')).length,
error: lines.filter(line => line.includes('ERROR')).length,
debug: lines.filter(line => line.includes('DEBUG')).length
};
return {
fileSize: Math.round(stats.size / 1024 * 10) / 10, // KB
lines: lines.length,
lastModified: stats.mtime.toLocaleString(),
levels
};
} catch (error) {
return {
fileSize: 0,
lines: 0,
lastModified: 'Never',
levels: { info: 0, warn: 0, error: 0, debug: 0 },
error: error.message
};
}
}
getSystemMetrics() {
const usage = process.memoryUsage();
const uptime = process.uptime();
return {
memoryUsage: {
rss: Math.round(usage.rss / 1024 / 1024 * 10) / 10,
heapUsed: Math.round(usage.heapUsed / 1024 / 1024 * 10) / 10,
heapTotal: Math.round(usage.heapTotal / 1024 / 1024 * 10) / 10
},
uptime: {
seconds: Math.round(uptime),
formatted: this.formatUptime(uptime)
}
};
}
displayLearningMetrics(data) {
console.log('๐ง Learning Engine Status:');
console.log(` Patterns: ${data.patterns} ${this.getChangeIndicator('patterns', data.patterns)}`);
console.log(` Knowledge Items: ${data.knowledge} ${this.getChangeIndicator('knowledge', data.knowledge)}`);
console.log(` Learning Cycles: ${data.learningCycles} ${this.getChangeIndicator('learningCycles', data.learningCycles)}`);
console.log(` Total Interactions: ${data.totalInteractions}`);
console.log(` Success Rate: ${(data.successRate * 100).toFixed(1)}%`);
console.log(` Avg Response: ${data.averageResponseTime}ms`);
console.log(` Last Saved: ${data.lastSaved}`);
console.log(` Data File Size: ${Math.round(data.fileSize / 1024 * 10) / 10}KB`);
if (data.error) {
console.log(` โ Error: ${data.error}`);
}
console.log('');
}
displayLogMetrics(data) {
console.log('๐ Logging Status:');
console.log(` Log File Size: ${data.fileSize}KB ${this.getChangeIndicator('logSize', data.fileSize)}`);
console.log(` Total Lines: ${data.lines}`);
console.log(` Last Modified: ${data.lastModified}`);
console.log(` Log Levels: INFO:${data.levels.info} WARN:${data.levels.warn} ERROR:${data.levels.error} DEBUG:${data.levels.debug}`);
if (data.error) {
console.log(` โ Error: ${data.error}`);
}
console.log('');
}
displaySystemMetrics(data) {
console.log('๐ป System Metrics:');
console.log(` Memory (RSS): ${data.memoryUsage.rss}MB`);
console.log(` Memory (Heap): ${data.memoryUsage.heapUsed}MB / ${data.memoryUsage.heapTotal}MB`);
console.log(` Monitor Uptime: ${data.uptime.formatted}`);
console.log('');
}
displayDetailedMetrics(data) {
console.log('๐ Detailed Information:');
console.log(' Recent Activity:');
// Show changes since last check
Object.keys(this.lastData).forEach(key => {
if (data[key] !== this.lastData[key] && data[key] !== undefined) {
const change = data[key] - this.lastData[key];
const sign = change > 0 ? '+' : '';
console.log(` ${key}: ${sign}${change}`);
}
});
if (Object.keys(this.lastData).every(key => data[key] === this.lastData[key])) {
console.log(' No changes detected');
}
console.log('');
}
getChangeIndicator(key, currentValue) {
const lastValue = this.lastData[key];
const change = currentValue - lastValue;
if (change > 0) {
this.lastData[key] = currentValue;
return `(+${change})`;
} else if (change < 0) {
this.lastData[key] = currentValue;
return `(${change})`;
}
this.lastData[key] = currentValue;
return '';
}
formatUptime(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
if (hours > 0) {
return `${hours}h ${minutes}m ${secs}s`;
} else if (minutes > 0) {
return `${minutes}m ${secs}s`;
} else {
return `${secs}s`;
}
}
}
// CLI interface
async function main() {
const args = process.argv.slice(2);
const options = {
interval: 5000,
details: false
};
// Parse command line arguments
for (let i = 0; i < args.length; i++) {
switch (args[i]) {
case '--interval':
case '-i':
options.interval = parseInt(args[++i]) * 1000;
break;
case '--details':
case '-d':
options.details = true;
break;
case '--help':
case '-h':
console.log('MCP Server Monitor\n');
console.log('Usage: node monitor.js [options]\n');
console.log('Options:');
console.log(' -i, --interval <seconds> Monitoring interval (default: 5)');
console.log(' -d, --details Show detailed metrics');
console.log(' -h, --help Show this help');
process.exit(0);
}
}
const monitor = new ServerMonitor(options);
await monitor.start();
}
// Run if executed directly
if (import.meta.url === `file://${process.argv[1]}`) {
await main();
}