monitoring-tools.ts•20.4 kB
import { Tool } from '@modelcontextprotocol/sdk/types.js';
import { SemanticEngine } from '../../engines/semantic-engine.js';
import { PatternEngine } from '../../engines/pattern-engine.js';
import { SQLiteDatabase } from '../../storage/sqlite-db.js';
import { existsSync, statSync } from 'fs';
import { join, dirname } from 'path';
import { PathValidator } from '../../utils/path-validator.js';
import { config } from '../../config/config.js';
import { Logger } from '../../utils/logger.js';
export class MonitoringTools {
constructor(
private semanticEngine: SemanticEngine,
private patternEngine: PatternEngine,
private database: SQLiteDatabase
) { }
get tools(): Tool[] {
return [
{
name: 'get_system_status',
description: 'Get comprehensive system status including intelligence data, performance metrics, and health indicators',
inputSchema: {
type: 'object',
properties: {
includeMetrics: {
type: 'boolean',
description: 'Include detailed performance metrics',
default: true
},
includeDiagnostics: {
type: 'boolean',
description: 'Include system diagnostics',
default: false
}
}
}
},
{
name: 'get_intelligence_metrics',
description: 'Get detailed metrics about the intelligence database and learning state',
inputSchema: {
type: 'object',
properties: {
includeBreakdown: {
type: 'boolean',
description: 'Include detailed breakdown by type and confidence',
default: true
}
}
}
},
{
name: 'get_performance_status',
description: 'Get performance metrics including database size, query times, and resource usage',
inputSchema: {
type: 'object',
properties: {
runBenchmark: {
type: 'boolean',
description: 'Run a quick performance benchmark',
default: false
}
}
}
},
{
name: 'health_check',
description: 'Verify In-Memoria setup and configuration for a project. Checks database accessibility, project structure, and API keys.',
inputSchema: {
type: 'object',
properties: {
path: {
type: 'string',
description: 'Project path to check (defaults to current directory)'
}
}
}
}
];
}
async getSystemStatus(args: {
includeMetrics?: boolean;
includeDiagnostics?: boolean;
}): Promise<any> {
const includeMetrics = args.includeMetrics !== false;
const includeDiagnostics = args.includeDiagnostics || false;
Logger.info('📊 Gathering system status...');
const status = {
timestamp: new Date().toISOString(),
version: '0.5.5',
status: 'operational',
components: {} as any,
intelligence: {} as any,
performance: {} as any,
diagnostics: {} as any
};
try {
// Database component status
status.components.database = await this.getDatabaseStatus();
// Intelligence system status
status.intelligence = await this.getIntelligenceStatus();
// Performance metrics (if requested)
if (includeMetrics) {
status.performance = await this.getPerformanceMetrics();
}
// Diagnostics (if requested)
if (includeDiagnostics) {
status.diagnostics = await this.getBasicDiagnostics();
}
// Overall health assessment
status.status = this.assessOverallHealth(status);
return {
success: true,
status,
message: `System is ${status.status}`,
summary: this.generateStatusSummary(status)
};
} catch (error: unknown) {
Logger.info('❌ Status check failed:', error);
return {
success: false,
status: 'error',
error: error instanceof Error ? error.message : String(error),
message: 'Failed to retrieve system status'
};
}
}
async getIntelligenceMetrics(args: {
includeBreakdown?: boolean;
}): Promise<any> {
const includeBreakdown = args.includeBreakdown !== false;
Logger.info('🧠 Analyzing intelligence metrics...');
try {
const concepts = this.database.getSemanticConcepts();
const patterns = this.database.getDeveloperPatterns();
const metrics = {
concepts: {
total: concepts.length,
...(includeBreakdown && { breakdown: {} as any })
},
patterns: {
total: patterns.length,
...(includeBreakdown && { breakdown: {} as any })
},
quality: {} as any,
coverage: {} as any,
timestamps: {} as any
};
if (includeBreakdown && concepts.length > 0) {
// Concepts breakdown
const conceptsByType = concepts.reduce((acc, concept) => {
acc[concept.conceptType] = (acc[concept.conceptType] || 0) + 1;
return acc;
}, {} as Record<string, number>);
const conceptsByConfidence = {
high: concepts.filter(c => c.confidenceScore >= 0.8).length,
medium: concepts.filter(c => c.confidenceScore >= 0.5 && c.confidenceScore < 0.8).length,
low: concepts.filter(c => c.confidenceScore < 0.5).length
};
metrics.concepts.breakdown = {
byType: conceptsByType,
byConfidence: conceptsByConfidence
};
// Quality metrics
const avgConfidence = concepts.reduce((sum, c) => sum + c.confidenceScore, 0) / concepts.length;
metrics.quality.averageConfidence = Math.round(avgConfidence * 100) / 100;
metrics.quality.highConfidenceRatio = conceptsByConfidence.high / concepts.length;
}
if (includeBreakdown && patterns.length > 0) {
// Patterns breakdown
const patternsByType = patterns.reduce((acc, pattern) => {
acc[pattern.patternType] = (acc[pattern.patternType] || 0) + 1;
return acc;
}, {} as Record<string, number>);
const patternsByFrequency = {
frequent: patterns.filter(p => p.frequency >= 10).length,
common: patterns.filter(p => p.frequency >= 3 && p.frequency < 10).length,
rare: patterns.filter(p => p.frequency < 3).length
};
metrics.patterns.breakdown = {
byType: patternsByType,
byFrequency: patternsByFrequency
};
// Pattern quality
const avgFrequency = patterns.reduce((sum, p) => sum + p.frequency, 0) / patterns.length;
metrics.quality.averagePatternFrequency = Math.round(avgFrequency * 100) / 100;
}
// Timestamps
if (concepts.length > 0) {
const latestConcept = concepts.reduce((latest, current) =>
new Date(current.createdAt) > new Date(latest.createdAt) ? current : latest
);
metrics.timestamps.lastConceptLearned = latestConcept.createdAt;
}
if (patterns.length > 0) {
const latestPattern = patterns.reduce((latest, current) =>
new Date(current.createdAt) > new Date(latest.createdAt) ? current : latest
);
metrics.timestamps.lastPatternLearned = latestPattern.createdAt;
}
return {
success: true,
metrics,
message: `Intelligence metrics: ${concepts.length} concepts, ${patterns.length} patterns`
};
} catch (error: unknown) {
Logger.info('❌ Intelligence metrics failed:', error);
return {
success: false,
error: error instanceof Error ? error.message : String(error),
message: 'Failed to retrieve intelligence metrics'
};
}
}
async getPerformanceStatus(args: {
runBenchmark?: boolean;
}): Promise<any> {
const runBenchmark = args.runBenchmark || false;
Logger.info('⚡ Checking performance status...');
try {
const performance = {
database: {} as any,
memory: {} as any,
system: {} as any,
benchmark: {} as any
};
// Database performance
const dbPath = this.getDatabasePath();
if (existsSync(dbPath)) {
const stats = statSync(dbPath);
performance.database.size = {
bytes: stats.size,
mb: Math.round(stats.size / (1024 * 1024) * 100) / 100
};
performance.database.lastModified = stats.mtime.toISOString();
// Query performance test
const queryStart = Date.now();
const concepts = this.database.getSemanticConcepts();
const queryTime = Date.now() - queryStart;
performance.database.queryPerformance = {
conceptQueryTime: queryTime,
conceptCount: concepts.length,
performanceRating: queryTime < 100 ? 'excellent' :
queryTime < 500 ? 'good' :
queryTime < 1000 ? 'fair' : 'poor'
};
}
// Memory usage
const memUsage = process.memoryUsage();
performance.memory = {
rss: Math.round(memUsage.rss / 1024 / 1024),
heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024),
heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024),
external: Math.round(memUsage.external / 1024 / 1024),
unit: 'MB'
};
// System info
performance.system = {
nodeVersion: process.version,
platform: `${process.platform}-${process.arch}`,
uptime: Math.round(process.uptime())
};
// Benchmark (if requested)
if (runBenchmark) {
Logger.info('🏃 Running performance benchmark...');
performance.benchmark = await this.runQuickBenchmark();
}
return {
success: true,
performance,
message: `Performance: DB ${performance.database?.size?.mb}MB, Memory ${performance.memory.heapUsed}MB`
};
} catch (error: unknown) {
Logger.info('❌ Performance check failed:', error);
return {
success: false,
error: error instanceof Error ? error.message : String(error),
message: 'Failed to retrieve performance metrics'
};
}
}
private async getDatabaseStatus(): Promise<any> {
try {
const concepts = this.database.getSemanticConcepts();
const patterns = this.database.getDeveloperPatterns();
const migrator = this.database.getMigrator();
const currentVersion = migrator.getCurrentVersion();
const latestVersion = migrator.getLatestVersion();
return {
status: 'healthy',
connected: true,
schemaVersion: currentVersion,
latestVersion: latestVersion,
needsMigration: currentVersion < latestVersion,
dataCount: {
concepts: concepts.length,
patterns: patterns.length
}
};
} catch (error: unknown) {
return {
status: 'error',
connected: false,
error: error instanceof Error ? error.message : String(error)
};
}
}
private async getIntelligenceStatus(): Promise<any> {
try {
const concepts = this.database.getSemanticConcepts();
const patterns = this.database.getDeveloperPatterns();
const hasData = concepts.length > 0 || patterns.length > 0;
const dataQuality = hasData ? 'good' : 'empty';
return {
status: hasData ? 'ready' : 'needs_learning',
dataQuality,
conceptCount: concepts.length,
patternCount: patterns.length,
lastUpdate: this.getLastUpdateTime(concepts, patterns)
};
} catch (error: unknown) {
return {
status: 'error',
error: error instanceof Error ? error.message : String(error)
};
}
}
private async getPerformanceMetrics(): Promise<any> {
const memUsage = process.memoryUsage();
return {
memory: {
heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024),
heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024)
},
database: {
size: this.getDatabaseSize()
},
uptime: Math.round(process.uptime())
};
}
private async getBasicDiagnostics(): Promise<any> {
return {
nodeVersion: process.version,
platform: process.platform,
arch: process.arch,
environment: {
hasOpenAI: !!process.env.OPENAI_API_KEY,
workingDirectory: process.cwd()
}
};
}
private assessOverallHealth(status: any): string {
if (status.components.database?.status === 'error') return 'critical';
if (status.intelligence?.status === 'error') return 'degraded';
if (status.intelligence?.status === 'needs_learning') return 'ready_for_learning';
return 'operational';
}
private generateStatusSummary(status: any): string {
const db = status.components.database;
const intel = status.intelligence;
if (status.status === 'operational') {
return `All systems operational. ${intel.conceptCount} concepts, ${intel.patternCount} patterns ready.`;
} else if (status.status === 'ready_for_learning') {
return 'System ready but needs learning. Run auto_learn_if_needed to get started.';
} else {
return 'System issues detected. Check diagnostics for details.';
}
}
private getDatabasePath(): string {
// This is a simplified version - in reality you'd get this from configuration
return join(process.cwd(), 'in-memoria.db');
}
private getDatabaseSize(): number {
try {
const dbPath = this.getDatabasePath();
if (existsSync(dbPath)) {
const stats = statSync(dbPath);
return Math.round(stats.size / 1024 / 1024 * 100) / 100; // MB
}
} catch (error) {
// Ignore errors
}
return 0;
}
private getLastUpdateTime(concepts: any[], patterns: any[]): string | null {
let latestTime = 0;
for (const concept of concepts) {
const time = new Date(concept.createdAt).getTime();
if (time > latestTime) latestTime = time;
}
for (const pattern of patterns) {
const time = new Date(pattern.createdAt).getTime();
if (time > latestTime) latestTime = time;
}
return latestTime > 0 ? new Date(latestTime).toISOString() : null;
}
private async runQuickBenchmark(): Promise<any> {
const benchmark = {
conceptQuery: 0,
patternQuery: 0,
memoryBaseline: process.memoryUsage().heapUsed,
memoryDelta: 0
};
// Concept query benchmark
const conceptStart = Date.now();
this.database.getSemanticConcepts();
benchmark.conceptQuery = Date.now() - conceptStart;
// Pattern query benchmark
const patternStart = Date.now();
this.database.getDeveloperPatterns();
benchmark.patternQuery = Date.now() - patternStart;
// Memory after operations
const memoryAfter = process.memoryUsage().heapUsed;
benchmark.memoryDelta = Math.round((memoryAfter - benchmark.memoryBaseline) / 1024);
return benchmark;
}
async healthCheck(args: { path?: string }): Promise<{
status: 'healthy' | 'warning' | 'error';
checks: {
name: string;
status: 'pass' | 'fail' | 'warning';
message: string;
}[];
summary: string;
}> {
const checks: Array<{ name: string; status: 'pass' | 'fail' | 'warning'; message: string }> = [];
const projectPath = args.path || process.cwd();
Logger.info(`🏥 Running health check for: ${projectPath}`);
// Check 1: Project path exists
try {
const pathExists = existsSync(projectPath);
checks.push({
name: 'Project Path',
status: pathExists ? 'pass' : 'fail',
message: pathExists
? `Path exists: ${projectPath}`
: `Path does not exist: ${projectPath}`
});
} catch (error) {
checks.push({
name: 'Project Path',
status: 'fail',
message: `Error checking path: ${error}`
});
}
// Check 2: Project structure validation
try {
const looksValid = PathValidator.looksLikeProjectRoot(projectPath);
const description = PathValidator.describePath(projectPath);
checks.push({
name: 'Project Structure',
status: looksValid ? 'pass' : 'warning',
message: looksValid
? `Valid project detected: ${description}`
: `No common project markers found. ${description}`
});
} catch (error) {
checks.push({
name: 'Project Structure',
status: 'warning',
message: `Could not analyze project structure: ${error}`
});
}
// Check 3: Database directory accessibility
try {
const dbPath = config.getDatabasePath(projectPath);
const dbDir = dirname(dbPath);
const dirExists = existsSync(dbDir);
checks.push({
name: 'Database Directory',
status: dirExists ? 'pass' : 'warning',
message: dirExists
? `Directory exists and accessible: ${dbDir}`
: `Directory will be created on first use: ${dbDir}`
});
// Check if database file exists
if (existsSync(dbPath)) {
const stats = statSync(dbPath);
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
checks.push({
name: 'Database File',
status: 'pass',
message: `Database exists (${sizeMB} MB): ${dbPath}`
});
} else {
checks.push({
name: 'Database File',
status: 'warning',
message: `Database not yet created: ${dbPath}`
});
}
} catch (error) {
checks.push({
name: 'Database Directory',
status: 'fail',
message: `Error checking database: ${error}`
});
}
// Check 4: OpenAI API Key (optional but recommended)
checks.push({
name: 'OpenAI API Key',
status: process.env.OPENAI_API_KEY ? 'pass' : 'warning',
message: process.env.OPENAI_API_KEY
? 'API key configured (vector embeddings enabled)'
: 'No API key - vector embeddings disabled (optional feature)'
});
// Check 5: Intelligence data status
try {
const dbPath = config.getDatabasePath(projectPath);
if (existsSync(dbPath)) {
const tempDb = new SQLiteDatabase(dbPath);
try {
const concepts = tempDb.getSemanticConcepts();
const patterns = tempDb.getDeveloperPatterns();
const hasIntelligence = concepts.length > 0 || patterns.length > 0;
checks.push({
name: 'Intelligence Data',
status: hasIntelligence ? 'pass' : 'warning',
message: hasIntelligence
? `Intelligence available: ${concepts.length} concepts, ${patterns.length} patterns`
: 'No intelligence data - run learning to populate'
});
} finally {
tempDb.close();
}
} else {
checks.push({
name: 'Intelligence Data',
status: 'warning',
message: 'Database not initialized - run learning to create'
});
}
} catch (error) {
checks.push({
name: 'Intelligence Data',
status: 'warning',
message: `Could not check intelligence: ${error}`
});
}
// Determine overall status
const hasFailures = checks.some(c => c.status === 'fail');
const hasWarnings = checks.some(c => c.status === 'warning');
const overallStatus: 'healthy' | 'warning' | 'error' =
hasFailures ? 'error' : hasWarnings ? 'warning' : 'healthy';
// Generate summary
const passCount = checks.filter(c => c.status === 'pass').length;
const warnCount = checks.filter(c => c.status === 'warning').length;
const failCount = checks.filter(c => c.status === 'fail').length;
let summary = `Health check ${overallStatus}: ${passCount} passed`;
if (warnCount > 0) summary += `, ${warnCount} warnings`;
if (failCount > 0) summary += `, ${failCount} failures`;
if (overallStatus === 'error') {
summary += '. Critical issues detected - please address failures.';
} else if (overallStatus === 'warning') {
summary += '. Some recommendations available - see warnings.';
} else {
summary += '. All systems operational.';
}
return {
status: overallStatus,
checks,
summary
};
}
}