UNPKG

@codai/memorai-mcp

Version:

MemorAI CBD-based MCP Server - High-Performance Vector Memory System

939 lines (927 loc) 157 kB
#!/usr/bin/env node /** * MemorAI MCP Unified Server - Production-Ready Implementation * * Consolidated server combining the best features from all implementations: * - Correct tool names (*) for VS Code MCP compatibility * - CBD backend for high-performance and reliability * - HPKV-inspired architecture with structured keys * - Advanced semantic search with OpenAI embeddings * - Performance tracking and analytics * - Hybrid storage with fallback mechanisms * - Simplified configuration and startup */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs'; import { join, resolve } from 'path'; import { randomUUID, createHash } from 'crypto'; import { config } from 'dotenv'; import OpenAI from 'openai'; import packageJson from '../package.json' with { type: 'json' }; import { MemoryRelationshipEngine } from './relationship-engine.js'; import { AdvancedSearchEngine } from './search-intelligence.js'; import { MemoryAnalyticsEngine } from './analytics-engine.js'; import { MemoryRecommendationEngine } from './recommendation-engine.js'; import { MemoryEvolutionEngine } from './evolution-engine.js'; import { RealTimeLearningEngine } from './learning-engine.js'; import { EnhancedPredictiveMemoryEngine } from './enhanced-predictive-engine.js'; import { MemoryFederationEngine } from './federation-engine.js'; export class MemorAIUnifiedServer { server; config; memories = new Map(); dataPath; isStarted = false; openai; // Enhanced engines relationshipEngine; searchEngine; analyticsEngine; recommendationEngine; evolutionEngine; learningEngine; enhancedPredictiveEngine; federationEngine; // Performance tracking operationCount = 0; operationTimes = []; startTime = Date.now(); // Memory analytics memoryStats = { totalMemories: 0, uniqueAgents: new Set(), uniqueProjects: new Set(), averageImportance: 0, }; constructor(config) { this.config = { ...config, enableSemanticSearch: config.enableSemanticSearch ?? true, enablePerformanceTracking: config.enablePerformanceTracking ?? true, enableHybridStorage: config.enableHybridStorage ?? true, fallbackStorage: config.fallbackStorage ?? 'json' }; this.dataPath = this.config.cbdPath; // Ensure data directory exists if (!existsSync(this.dataPath)) { mkdirSync(this.dataPath, { recursive: true }); } // Initialize OpenAI client if (this.config.azureOpenAI && this.config.enableSemanticSearch) { this.openai = new OpenAI({ apiKey: this.config.azureOpenAI.apiKey, baseURL: `${this.config.azureOpenAI.endpoint}/openai/deployments/${this.config.azureOpenAI.embeddingDeployment}`, defaultQuery: { 'api-version': this.config.azureOpenAI.apiVersion }, defaultHeaders: { 'api-key': this.config.azureOpenAI.apiKey, }, }); this.log('info', `🔗 Azure OpenAI initialized with deployment: ${this.config.azureOpenAI.embeddingDeployment}`); } else if (this.config.openaiApiKey && this.config.enableSemanticSearch) { this.openai = new OpenAI({ apiKey: this.config.openaiApiKey, }); this.log('info', '🔗 OpenAI initialized (fallback mode)'); } // Initialize enhanced engines this.relationshipEngine = new MemoryRelationshipEngine(this.openai); this.searchEngine = new AdvancedSearchEngine(this.openai); this.analyticsEngine = new MemoryAnalyticsEngine(this.openai, this.memories); this.recommendationEngine = new MemoryRecommendationEngine(this.openai, this.memories); this.evolutionEngine = new MemoryEvolutionEngine(this.openai, this.memories); this.learningEngine = new RealTimeLearningEngine(this.openai, this.memories); this.enhancedPredictiveEngine = new EnhancedPredictiveMemoryEngine(this.openai, this.memories); this.federationEngine = new MemoryFederationEngine(this.openai, this.memories); this.log('info', '🔗 Advanced engines initialized (Relationship + Search + Analytics + Recommendations + Evolution + Learning + Enhanced Prediction + Federation)'); // Initialize MCP Server this.server = new Server({ name: this.config.serverName, version: this.config.version, }, { capabilities: { tools: {}, }, }); this.setupHandlers(); this.loadMemories(); this.log('info', `🚀 ${this.config.serverName} initialized`); } log(level, message, ...args) { const timestamp = new Date().toISOString(); console.error(`[${timestamp}] [${level.toUpperCase()}] ${message}`, ...args); } /** * Initialize CBD Service - Check for service availability and optionally start it */ async initializeCBDService() { try { // Check if CBD service is available const serviceAvailable = await this.checkCBDService(); if (serviceAvailable) { this.log('info', 'CBD service is available, will use hybrid mode (service + file fallback)'); return; } this.log('info', 'CBD service not available, checking if we can start it locally...'); // Try to start CBD service if possible const serviceStarted = await this.startCBDService(); if (serviceStarted) { this.log('info', 'Successfully started local CBD service'); // Wait for service to initialize await this.sleep(3000); // Verify it's working const isWorking = await this.checkCBDService(); if (isWorking) { this.log('info', 'CBD service is now running and available'); } else { this.log('warn', 'CBD service started but not responding, using file-based fallback'); } } else { this.log('info', 'Could not start CBD service, using file-based storage only'); } } catch (error) { this.log('warn', 'CBD service initialization failed, using file-based fallback:', error); } } async checkCBDService() { try { const response = await fetch('http://localhost:4180/health', { method: 'GET', headers: { 'Content-Type': 'application/json' }, signal: AbortSignal.timeout(3000) // 3 second timeout }); return response.ok; } catch (error) { return false; } } async startCBDService() { try { const { spawn } = await import('child_process'); const { join } = await import('path'); // Look for CBD package in common locations const cbdPackagePath = this.findCBDPackage(); if (!cbdPackagePath) { this.log('info', 'CBD package not found locally, trying npx...'); return await this.startCBDViaNPX(); } this.log('info', `Starting CBD service from: ${cbdPackagePath}`); // Start CBD service const cbdProcess = spawn('npm', ['run', 'service'], { cwd: cbdPackagePath, stdio: ['ignore', 'pipe', 'pipe'], shell: true, detached: false }); if (cbdProcess.stdout) { cbdProcess.stdout.on('data', (data) => { this.log('info', `[CBD] ${data.toString().trim()}`); }); } if (cbdProcess.stderr) { cbdProcess.stderr.on('data', (data) => { this.log('warn', `[CBD] ${data.toString().trim()}`); }); } return true; } catch (error) { this.log('error', 'Failed to start CBD service:', error); return false; } } async startCBDViaNPX() { try { const { spawn } = await import('child_process'); this.log('info', 'Starting CBD service via npx...'); const cbdProcess = spawn('npx', ['@codai/cbd', 'service'], { stdio: ['ignore', 'pipe', 'pipe'], shell: true, detached: false }); if (cbdProcess.stdout) { cbdProcess.stdout.on('data', (data) => { this.log('info', `[CBD-NPX] ${data.toString().trim()}`); }); } return true; } catch (error) { this.log('error', 'Failed to start CBD via npx:', error); return false; } } findCBDPackage() { try { const { join } = require('path'); const { existsSync, readFileSync } = require('fs'); // Common paths where CBD package might be located const possiblePaths = [ // Monorepo structure join(process.cwd(), '..', '..', 'packages', 'cbd'), join(process.cwd(), '..', 'packages', 'cbd'), join(process.cwd(), 'packages', 'cbd'), // Relative paths join(__dirname, '..', '..', '..', 'cbd'), join(__dirname, '..', '..', '..', '..', 'cbd'), // Environment variable process.env.CBD_PACKAGE_PATH ].filter(Boolean); for (const path of possiblePaths) { try { const packageJsonPath = join(path, 'package.json'); if (existsSync(packageJsonPath)) { const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')); if (packageJson.name === '@codai/cbd') { this.log('info', `Found CBD package at: ${path}`); return path; } } } catch (err) { // Continue searching } } return null; } catch (error) { this.log('error', 'Error finding CBD package:', error); return null; } } sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } setupHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'remember', description: 'Store a new memory with metadata', inputSchema: { type: 'object', properties: { agentId: { type: 'string', description: 'Agent identifier' }, content: { type: 'string', description: 'Memory content to store' }, metadata: { type: 'object', properties: { entityType: { type: 'string' }, priority: { type: 'string' }, project: { type: 'string' }, session: { type: 'string' }, tags: { type: 'array', items: { type: 'string' } } } } }, required: ['agentId', 'content'], }, }, { name: 'recall', description: 'Search and retrieve memories', inputSchema: { type: 'object', properties: { agentId: { type: 'string', description: 'Agent identifier' }, query: { type: 'string', description: 'Search query' }, limit: { type: 'number', description: 'Maximum results', default: 10 }, minImportance: { type: 'number', description: 'Minimum importance score', default: 0 }, project: { type: 'string', description: 'Filter by project' }, session: { type: 'string', description: 'Filter by session' } }, required: ['agentId', 'query'], }, }, { name: 'forget', description: 'Delete a memory by structured key', inputSchema: { type: 'object', properties: { agentId: { type: 'string', description: 'Agent identifier' }, structuredKey: { type: 'string', description: 'Structured key of memory to delete' } }, required: ['agentId', 'structuredKey'], }, }, { name: 'context', description: 'Get recent context for agent', inputSchema: { type: 'object', properties: { agentId: { type: 'string', description: 'Agent identifier' }, contextSize: { type: 'number', description: 'Number of recent memories', default: 5 } }, required: ['agentId'], }, }, { name: 'get_memory', description: 'Get memory by exact structured key', inputSchema: { type: 'object', properties: { structuredKey: { type: 'string', description: 'Exact structured key' } }, required: ['structuredKey'], }, }, { name: 'search_keys', description: 'Vector similarity search for memory keys', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Query for finding similar memory keys' }, limit: { type: 'number', description: 'Maximum keys to return', default: 10 }, minScore: { type: 'number', description: 'Minimum similarity score', default: 0.7 } }, required: ['query'], }, }, { name: 'link_memories', description: 'Create a relationship between two memories', inputSchema: { type: 'object', properties: { agentId: { type: 'string', description: 'Agent identifier' }, sourceMemoryKey: { type: 'string', description: 'Structured key of source memory' }, targetMemoryKey: { type: 'string', description: 'Structured key of target memory' }, relationshipType: { type: 'string', description: 'Type of relationship', enum: ['related', 'references', 'follows', 'contradicts', 'updates', 'similar', 'contains', 'explains'] }, strength: { type: 'number', description: 'Relationship strength (0.0-1.0)', default: 0.5 }, context: { type: 'string', description: 'Optional context for the relationship' } }, required: ['agentId', 'sourceMemoryKey', 'targetMemoryKey', 'relationshipType'], }, }, { name: 'get_relationships', description: 'Get relationships for a memory', inputSchema: { type: 'object', properties: { agentId: { type: 'string', description: 'Agent identifier' }, memoryKey: { type: 'string', description: 'Structured key of memory' }, maxDepth: { type: 'number', description: 'How many relationship hops to traverse', default: 1 }, relationshipTypes: { type: 'array', items: { type: 'string' }, description: 'Filter by relationship types' } }, required: ['agentId', 'memoryKey'], }, }, { name: 'explore_graph', description: 'Explore the knowledge graph from a starting memory', inputSchema: { type: 'object', properties: { agentId: { type: 'string', description: 'Agent identifier' }, startingMemoryKey: { type: 'string', description: 'Starting point for exploration' }, explorationRadius: { type: 'number', description: 'How far to explore', default: 2 }, includeWeakLinks: { type: 'boolean', description: 'Include weak relationships', default: false } }, required: ['agentId', 'startingMemoryKey'], }, }, { name: 'get_analytics', description: 'Generate comprehensive analytics and insights for memory usage', inputSchema: { type: 'object', properties: { agentId: { type: 'string', description: 'Agent identifier' }, reportType: { type: 'string', description: 'Type of analytics report', enum: ['usage', 'patterns', 'health', 'gaps', 'recommendations'] }, timeRange: { type: 'object', properties: { start: { type: 'string', description: 'Start date (ISO string)' }, end: { type: 'string', description: 'End date (ISO string)' } } }, includeVisualizations: { type: 'boolean', description: 'Include visualization data', default: false } }, required: ['agentId', 'reportType'], }, }, { name: 'get_recommendations', description: 'Get intelligent recommendations for memory management optimization', inputSchema: { type: 'object', properties: { agentId: { type: 'string', description: 'Agent identifier' }, recommendationType: { type: 'string', description: 'Type of recommendations', enum: ['review', 'create', 'connect', 'cleanup', 'all'] }, maxRecommendations: { type: 'number', description: 'Maximum number of recommendations', default: 10 } }, required: ['agentId'], }, }, { name: 'get_insights', description: 'Get AI-powered insights about memory patterns and knowledge gaps', inputSchema: { type: 'object', properties: { agentId: { type: 'string', description: 'Agent identifier' }, insightType: { type: 'string', description: 'Type of insights to generate', enum: ['trending_topics', 'memory_clusters', 'knowledge_map', 'activity_heatmap', 'gap_analysis'] }, parameters: { type: 'object', description: 'Additional parameters for insight generation' } }, required: ['agentId', 'insightType'], }, }, { name: 'evolve_memory', description: 'Automatically update memory based on new information', inputSchema: { type: 'object', properties: { memoryId: { type: 'string', description: 'Memory ID to evolve' }, newInformation: { type: 'string', description: 'New information to integrate' }, context: { type: 'object', properties: { source: { type: 'string', description: 'Source of new information' }, confidence: { type: 'number', description: 'Confidence in new information (0-1)' }, timestamp: { type: 'string', description: 'Timestamp of new information' } } } }, required: ['memoryId', 'newInformation'], }, }, { name: 'resolve_conflicts', description: 'Detect and resolve conflicts between memories', inputSchema: { type: 'object', properties: { memoryIds: { type: 'array', items: { type: 'string' }, description: 'Memory IDs to check for conflicts' }, resolutionStrategy: { type: 'string', enum: ['auto', 'conservative', 'aggressive'], description: 'Strategy for conflict resolution' } }, required: ['memoryIds'], }, }, { name: 'consolidate_memories', description: 'Consolidate related memories for better organization', inputSchema: { type: 'object', properties: { memoryIds: { type: 'array', items: { type: 'string' }, description: 'Memory IDs to consolidate' }, consolidationType: { type: 'string', enum: ['merge', 'summarize', 'restructure', 'cross_reference'], description: 'Type of consolidation to perform', default: 'merge' } }, required: ['memoryIds'], }, }, { name: 'manage_lifecycle', description: 'Automatically manage memory lifecycle (archive, promote, clean)', inputSchema: { type: 'object', properties: { agentId: { type: 'string', description: 'Agent identifier' } }, required: ['agentId'], }, }, // Phase 4: Enhanced Predictive & Learning Tools { name: 'predict_enhanced', description: 'Enhanced memory need prediction with learning integration', inputSchema: { type: 'object', properties: { agentId: { type: 'string', description: 'Agent identifier' }, context: { type: 'object', properties: { currentTask: { type: 'string' }, recentMemories: { type: 'array', items: { type: 'string' } }, timeOfDay: { type: 'string' }, urgency: { type: 'string', enum: ['low', 'medium', 'high', 'critical'] } }, required: ['currentTask'] } }, required: ['agentId', 'context'], }, }, { name: 'predict_structure', description: 'Predict optimal memory structure based on usage patterns', inputSchema: { type: 'object', properties: { agentId: { type: 'string', description: 'Agent identifier' } }, required: ['agentId'], }, }, { name: 'predict_evolution', description: 'Predict memory evolution with learning-enhanced accuracy', inputSchema: { type: 'object', properties: { memoryId: { type: 'string', description: 'Memory ID to analyze' }, timeHorizon: { type: 'string', description: 'Time horizon for prediction', default: '1 month' } }, required: ['memoryId'], }, }, { name: 'learn_from_usage', description: 'Learn from usage patterns to improve future predictions', inputSchema: { type: 'object', properties: { agentId: { type: 'string', description: 'Agent identifier' }, usagePatterns: { type: 'array', items: { type: 'object', properties: { memoryId: { type: 'string' }, accessFrequency: { type: 'number' }, successRate: { type: 'number' }, accessTiming: { type: 'array', items: { type: 'string' } }, contextPatterns: { type: 'array', items: { type: 'string' } } }, required: ['memoryId', 'accessFrequency', 'successRate'] } } }, required: ['agentId', 'usagePatterns'], }, }, { name: 'adapt_organization', description: 'Adapt memory organization based on effectiveness metrics', inputSchema: { type: 'object', properties: { agentId: { type: 'string', description: 'Agent identifier' }, effectivenessMetrics: { type: 'object', properties: { retrievalSuccessRate: { type: 'number' }, averageRetrievalTime: { type: 'number' }, memoryUtilizationRate: { type: 'number' }, contextAccuracy: { type: 'number' }, collaborationEffectiveness: { type: 'number' }, overallSatisfaction: { type: 'number' } }, required: ['retrievalSuccessRate', 'averageRetrievalTime'] } }, required: ['agentId', 'effectivenessMetrics'], }, }, { name: 'optimize_retrieval', description: 'Optimize memory retrieval based on query patterns and performance', inputSchema: { type: 'object', properties: { queryPatterns: { type: 'array', items: { type: 'object', properties: { agentId: { type: 'string' }, query: { type: 'string' }, queryType: { type: 'string', enum: ['semantic', 'keyword', 'structured', 'hybrid'] }, frequency: { type: 'number' }, successRate: { type: 'number' } }, required: ['agentId', 'query', 'frequency', 'successRate'] } }, performanceMetrics: { type: 'object', properties: { agentId: { type: 'string' }, totalQueries: { type: 'number' }, averageResponseTime: { type: 'number' }, successRate: { type: 'number' }, userSatisfactionScore: { type: 'number' } }, required: ['agentId', 'totalQueries', 'averageResponseTime'] } }, required: ['queryPatterns', 'performanceMetrics'], }, }, { name: 'share_memory', description: 'Share a memory with another agent with specific permissions', inputSchema: { type: 'object', properties: { sourceAgentId: { type: 'string', description: 'Agent sharing the memory' }, targetAgentId: { type: 'string', description: 'Agent receiving the memory' }, memoryId: { type: 'string', description: 'ID of memory to share' }, permissions: { type: 'object', properties: { accessLevel: { type: 'string', enum: ['read', 'read-write', 'admin'] }, expirationTime: { type: 'string' }, allowModification: { type: 'boolean' }, allowDeletion: { type: 'boolean' }, allowSharing: { type: 'boolean' }, contextRestrictions: { type: 'array', items: { type: 'string' } }, projectRestrictions: { type: 'array', items: { type: 'string' } } }, required: ['accessLevel', 'allowModification', 'allowDeletion', 'allowSharing'] } }, required: ['sourceAgentId', 'targetAgentId', 'memoryId', 'permissions'], }, }, { name: 'federated_query', description: 'Perform a distributed query across multiple agents', inputSchema: { type: 'object', properties: { requestingAgentId: { type: 'string', description: 'Agent making the request' }, query: { type: 'string', description: 'Search query' }, targetAgents: { type: 'array', items: { type: 'string' }, description: 'Agent IDs to query' }, queryType: { type: 'string', enum: ['search', 'recommendation', 'insight', 'verification'] }, priority: { type: 'string', enum: ['low', 'medium', 'high', 'urgent'] }, responseTimeout: { type: 'number', description: 'Timeout in seconds' }, aggregationMethod: { type: 'string', enum: ['union', 'intersection', 'weighted', 'consensus'] } }, required: ['requestingAgentId', 'query', 'targetAgents', 'queryType', 'aggregationMethod'], }, }, { name: 'collective_insights', description: 'Generate collective insights from multiple agents about a topic', inputSchema: { type: 'object', properties: { participatingAgents: { type: 'array', items: { type: 'string' }, description: 'Agent IDs to include' }, topic: { type: 'string', description: 'Topic for collective analysis' } }, required: ['participatingAgents', 'topic'], }, }, { name: 'collaborative_learning', description: 'Enable real-time collaborative learning across agents', inputSchema: { type: 'object', properties: { participatingAgents: { type: 'array', items: { type: 'string' }, description: 'Agent IDs to include' }, learningObjective: { type: 'string', description: 'Objective for collaborative learning' } }, required: ['participatingAgents', 'learningObjective'], }, }, { name: 'synchronize_federation', description: 'Synchronize memories across federated agents', inputSchema: { type: 'object', properties: { participatingAgents: { type: 'array', items: { type: 'string' }, description: 'Agent IDs to synchronize' } }, required: ['participatingAgents'], }, } ], }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'remember': return await this.handleRemember(args); case 'recall': return await this.handleRecall(args); case 'forget': return await this.handleForget(args); case 'context': return await this.handleContext(args); case 'get_memory': return await this.handleGetMemory(args); case 'search_keys': return await this.handleSearchKeys(args); case 'link_memories': return await this.handleLinkMemories(args); case 'get_relationships': return await this.handleGetRelationships(args); case 'explore_graph': return await this.handleExploreGraph(args); case 'get_analytics': this.log('info', '=== get_analytics tool called ==='); this.log('info', 'Arguments received:', JSON.stringify(args)); const agentId = args?.agentId || 'unknown'; const reportType = args?.reportType || 'usage'; const timeRange = args?.timeRange || 'week'; const includePatterns = args?.includePatterns !== false; try { // Comprehensive memory analysis with safe operations let memoryCount = 0; let totalRelationships = 0; let totalImportance = 0; const projects = new Set(); const entityTypes = new Set(); const priorities = new Map(); for (const [key, memory] of this.memories.entries()) { if (memory?.metadata?.agentId === agentId) { memoryCount++; // Safe relationship counting if (memory.relationships && Array.isArray(memory.relationships)) { totalRelationships += memory.relationships.length; } // Safe importance accumulation if (typeof memory.metadata?.importance === 'number') { totalImportance += memory.metadata.importance; } else { totalImportance += 0.5; // default importance } // Safe metadata extraction if (memory.metadata?.project) { projects.add(memory.metadata.project); } if (memory.metadata?.entityType) { entityTypes.add(memory.metadata.entityType); } if (memory.metadata?.priority) { const priority = memory.metadata.priority; priorities.set(priority, (priorities.get(priority) || 0) + 1); } } } const avgImportance = memoryCount > 0 ? totalImportance / memoryCount : 0; const avgRelationships = memoryCount > 0 ? totalRelationships / memoryCount : 0; // Calculate health scores const overallHealth = memoryCount > 0 ? Math.min(100, Math.round((avgImportance * 40) + (avgRelationships * 30) + 30)) : 0; const organizationScore = Math.min(100, projects.size * 20); const contentQuality = Math.min(100, entityTypes.size * 25); const result = { content: [{ type: 'text', text: `Memory Analytics Report for Agent: ${agentId} CONFIGURATION: • Report Type: ${reportType} • Time Range: ${timeRange} • Include Patterns: ${includePatterns} • Timestamp: ${new Date().toISOString()} USAGE METRICS: • Total Memories: ${memoryCount} • Average Importance: ${avgImportance.toFixed(2)} • Total Relationships: ${totalRelationships} • Avg Relationships per Memory: ${avgRelationships.toFixed(1)} ORGANIZATION: • Projects: ${projects.size} (${Array.from(projects).slice(0, 3).join(', ')}${projects.size > 3 ? `, +${projects.size - 3} more` : ''}) • Entity Types: ${entityTypes.size} (${Array.from(entityTypes).slice(0, 3).join(', ')}${entityTypes.size > 3 ? `, +${entityTypes.size - 3} more` : ''}) • Priority Distribution: ${Array.from(priorities.entries()).map(([p, c]) => `${p}(${c})`).join(', ') || 'None set'} HEALTH SCORES: • Overall Health: ${overallHealth}% • Organization Score: ${organizationScore}% • Content Quality: ${contentQuality}% • Relationship Quality: ${totalRelationships > 0 ? Math.min(100, avgRelationships * 50) : 0}% INSIGHTS: • Memory Activity: ${memoryCount > 0 ? 'Active' : 'No memories found'} • Data Quality: ${avgImportance > 0.7 ? 'Excellent' : avgImportance > 0.5 ? 'Good' : avgImportance > 0.3 ? 'Fair' : 'Needs improvement'} • Organization Level: ${projects.size > 3 ? 'Well organized' : projects.size > 1 ? 'Moderate organization' : 'Basic organization'} • Connectivity: ${avgRelationships > 2 ? 'Highly connected' : avgRelationships > 1 ? 'Well connected' : avgRelationships > 0 ? 'Some connections' : 'Isolated memories'} Analytics completed successfully in ${Date.now() - Date.now()}ms.` }] }; this.log('info', '=== get_analytics completed successfully ==='); return result; } catch (error) { this.log('error', '=== get_analytics failed ===', error); return { content: [{ type: 'text', text: `Analytics Error for Agent: ${agentId} Error: ${error?.message || 'Unknown error'} Report Type: ${reportType} Time Range: ${timeRange} System Memory Count: ${this.memories.size} Please check the server logs for more details. Stack trace: ${error?.stack || 'Not available'}` }] }; } case 'get_recommendations': return await this.handleGetRecommendations(args); case 'get_insights': this.log('info', '=== get_insights tool called ==='); this.log('info', 'Arguments received:', JSON.stringify(args)); const insightsAgentId = args?.agentId || 'unknown'; const insightType = args?.insightType || 'trending_topics'; const includeParameters = args?.parameters || {}; try { // Simple insights generation without complex analytics engine let memoryCount = 0; const topics = new Map(); const projects = new Set(); const entityTypes = new Set(); const importanceScores = []; const recentMemories = []; for (const [key, memory] of this.memories.entries()) { if (memory?.metadata?.agentId === insightsAgentId) { memoryCount++; // Extract topics from content (simple word frequency) if (memory.content) { const words = memory.content.toLowerCase() .split(/\s+/) .filter(word => word.length > 4) .slice(0, 10); // top 10 words words.forEach(word => { topics.set(word, (topics.get(word) || 0) + 1); }); } // Collect metadata if (memory.metadata?.project) projects.add(memory.metadata.project); if (memory.metadata?.entityType) entityTypes.add(memory.metadata.entityType); if (typeof memory.metadata?.importance === 'number') { importanceScores.push(memory.metadata.importance); } // Track recent memories (basic) recentMemories.push({ key: memory.structuredKey, content: memory.content.substring(0, 100) + '...', importance: memory.metadata?.importance || 0.5 }); } } // Generate insights based on type let insightContent = ''; switch (insightType) { case 'trending_topics': const topTopics = Array.from(topics.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, 5); insightContent = `Trending Topics Analysis for Agent: ${insightsAgentId} TOP TRENDING TOPICS: ${topTopics.map((topic, i) => `${i + 1}. "${topic[0]}" (mentioned ${topic[1]} times)`).join('\n')} TOPIC INSIGHTS: • Most frequent topic: ${topTopics[0] ? topTopics[0][0] : 'No data'} • Topic diversity: ${topics.size} unique topics identified • Content richness: ${topics.size > 10 ? 'High' : topics.size > 5 ? 'Moderate' : 'Low'}`; break; case 'memory_clusters':