UNPKG

@codai/cbd

Version:

Codai Better Database - High-Performance Vector Memory System with HPKV-inspired architecture and MCP server

437 lines 18.4 kB
#!/usr/bin/env node /** * Enhanced MemoraiMCP Server with CBD (Codai Better Database) Backend * Merges HPKV-inspired architecture with existing MCP functionality */ 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 { z } from 'zod'; import { createCBDEngine, validateCBDConfig } from '@codai/cbd'; // Tool schemas const RememberSchema = z.object({ agentId: z.string(), userRequest: z.string(), assistantResponse: z.string(), metadata: z.object({ project: z.string().optional(), session: z.string().optional(), priority: z.enum(['low', 'medium', 'high', 'critical']).optional(), tags: z.array(z.string()).optional(), type: z.string().optional(), }).optional(), }); const SearchMemorySchema = z.object({ query: z.string(), limit: z.number().min(1).max(50).optional(), confidenceThreshold: z.number().min(0).max(1).optional(), }); const SearchKeysSchema = z.object({ query: z.string(), topK: z.number().min(1).max(50).optional(), minScore: z.number().min(0).max(1).optional(), }); const GetMemorySchema = z.object({ structuredKey: z.string(), }); const RecallSchema = z.object({ agentId: z.string(), query: z.string(), limit: z.number().min(1).max(100).optional(), minImportance: z.number().min(0).max(1).optional(), project: z.string().optional(), session: z.string().optional(), }); const ForgetSchema = z.object({ agentId: z.string(), structuredKey: z.string(), }); const ContextSchema = z.object({ agentId: z.string(), contextSize: z.number().min(1).max(20).optional(), }); class EnhancedMemoraiMCPServer { server; cbdEngine; initialized = false; constructor() { this.server = new Server({ name: 'enhanced-memorai-mcp', version: '8.0.0', }, { capabilities: { tools: {}, }, }); this.initializeCBD(); this.setupToolHandlers(); } initializeCBD() { try { // Initialize CBD with configuration const config = { storage: { type: 'cbd-native', dataPath: process.env.CBD_DATA_PATH || './enhanced-memorai-cbd-data' }, embedding: { model: 'openai', apiKey: process.env.OPENAI_API_KEY || undefined, modelName: 'text-embedding-ada-002' } }; const validation = validateCBDConfig(config); if (!validation.valid) { console.warn('CBD configuration issues:', validation.errors); } this.cbdEngine = createCBDEngine(config); console.log('🚀 Enhanced MemoraiMCP with CBD backend initialized'); } catch (error) { console.error('Failed to initialize CBD engine:', error); throw error; } } async ensureInitialized() { if (!this.initialized) { await this.cbdEngine.initialize(); this.initialized = true; } } setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'store_memory', description: 'Store a conversation exchange (HPKV API)', inputSchema: { type: 'object', properties: { userRequest: { type: 'string', description: 'User request/question' }, assistantResponse: { type: 'string', description: 'Assistant response' }, metadata: { type: 'object', description: 'Memory metadata (project, session, agent info)', properties: { projectName: { type: 'string' }, sessionName: { type: 'string' }, agentId: { type: 'string' }, priority: { type: 'string', enum: ['low', 'medium', 'high', 'critical'] }, tags: { type: 'array', items: { type: 'string' } } }, required: ['projectName', 'sessionName', 'agentId'] } }, required: ['userRequest', 'assistantResponse', 'metadata'] } }, { name: 'search_memory', description: 'Semantic search with AI-powered summarization (HPKV API)', inputSchema: SearchMemorySchema }, { name: 'search_keys', description: 'Search for memory keys using vector similarity (HPKV API)', inputSchema: SearchKeysSchema }, { name: 'get_memory', description: 'Retrieve memory by exact structured key (HPKV API)', inputSchema: GetMemorySchema }, // Backward compatibility tools { name: 'remember', description: 'Store a memory with agent isolation (legacy compatibility)', inputSchema: RememberSchema }, { name: 'recall', description: 'Search memories with semantic understanding (legacy compatibility)', inputSchema: RecallSchema }, { name: 'forget', description: 'Delete a memory by structured key (legacy compatibility)', inputSchema: ForgetSchema }, { name: 'context', description: 'Get recent context for agent (legacy compatibility)', inputSchema: ContextSchema } ] }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { await this.ensureInitialized(); switch (name) { case 'store_memory': return await this.handleStoreMemory(args); case 'search_memory': return await this.handleSearchMemory(args); case 'search_keys': return await this.handleSearchKeys(args); case 'get_memory': return await this.handleGetMemory(args); 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); default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } } catch (error) { console.error(`Error in ${name}:`, error); throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } }); } // HPKV API Implementations async handleStoreMemory(args) { const parsed = z.object({ userRequest: z.string(), assistantResponse: z.string(), metadata: z.object({ projectName: z.string(), sessionName: z.string(), agentId: z.string(), priority: z.string().optional(), tags: z.array(z.string()).optional() }) }).parse(args); const structuredKey = await this.cbdEngine.store_memory(parsed.userRequest, parsed.assistantResponse, parsed.metadata); return { content: [{ type: 'text', text: JSON.stringify({ success: true, structuredKey, message: 'Conversation exchange stored successfully with semantic embeddings', metadata: { operation: 'store_memory', timestamp: new Date().toISOString(), cbdVersion: '8.0.0' } }, null, 2) }] }; } async handleSearchMemory(args) { const parsed = SearchMemorySchema.parse(args); const result = await this.cbdEngine.search_memory(parsed.query, parsed.limit || 10, parsed.confidenceThreshold || 0.5); return { content: [{ type: 'text', text: JSON.stringify({ success: true, summary: result.summary, memories: result.memories.map(m => ({ structuredKey: m.memory.structuredKey, relevanceScore: m.relevanceScore, confidence: m.confidence, userRequest: m.memory.userRequest, assistantResponse: m.memory.assistantResponse, projectName: m.memory.projectName, sessionName: m.memory.sessionName, createdAt: m.memory.createdAt })), metadata: { operation: 'search_memory', query: parsed.query, resultsCount: result.memories.length, timestamp: new Date().toISOString() } }, null, 2) }] }; } async handleSearchKeys(args) { const parsed = SearchKeysSchema.parse(args); const results = await this.cbdEngine.search_keys(parsed.query, parsed.topK || 10, parsed.minScore || 0.3); return { content: [{ type: 'text', text: JSON.stringify({ success: true, keys: results, metadata: { operation: 'search_keys', query: parsed.query, resultsCount: results.length, timestamp: new Date().toISOString() } }, null, 2) }] }; } async handleGetMemory(args) { const parsed = GetMemorySchema.parse(args); const memory = await this.cbdEngine.get_memory(parsed.structuredKey); return { content: [{ type: 'text', text: JSON.stringify({ success: true, memory: memory ? { structuredKey: memory.structuredKey, userRequest: memory.userRequest, assistantResponse: memory.assistantResponse, projectName: memory.projectName, sessionName: memory.sessionName, agentId: memory.agentId, metadata: memory.metadata, confidenceScore: memory.confidenceScore, createdAt: memory.createdAt, updatedAt: memory.updatedAt } : null, metadata: { operation: 'get_memory', structuredKey: parsed.structuredKey, found: !!memory, timestamp: new Date().toISOString() } }, null, 2) }] }; } // Legacy compatibility implementations async handleRemember(args) { const parsed = RememberSchema.parse(args); // Convert legacy format to CBD format const metadata = { projectName: parsed.metadata?.project || 'general', sessionName: parsed.metadata?.session || 'default', agentId: parsed.agentId, priority: parsed.metadata?.priority, tags: parsed.metadata?.tags, type: parsed.metadata?.type }; const structuredKey = await this.cbdEngine.store_memory(parsed.userRequest, parsed.assistantResponse, metadata); return { content: [{ type: 'text', text: JSON.stringify({ success: true, structuredKey, message: 'Memory stored successfully (legacy compatibility mode)', metadata: { operation: 'remember', agentId: parsed.agentId, timestamp: new Date().toISOString() } }, null, 2) }] }; } async handleRecall(args) { const parsed = RecallSchema.parse(args); const result = await this.cbdEngine.search_memory(parsed.query, parsed.limit || 10, parsed.minImportance || 0.0); // Convert CBD format to legacy format const memories = result.memories.map(m => ({ structured_key: m.memory.structuredKey, content: m.memory.userRequest, response: m.memory.assistantResponse, agent_id: m.memory.agentId, project: m.memory.projectName, session: m.memory.sessionName, metadata: m.memory.metadata, confidence_score: m.confidence, relevance_score: m.relevanceScore, created_at: m.memory.createdAt })); return { content: [{ type: 'text', text: JSON.stringify({ success: true, memories, summary: result.summary.summary, totalFound: memories.length, query: parsed.query, searchOptions: { limit: parsed.limit, minImportance: parsed.minImportance }, metadata: { operation: 'recall', agentId: parsed.agentId, timestamp: new Date().toISOString(), compatibilityMode: true } }, null, 2) }] }; } async handleForget(args) { const parsed = ForgetSchema.parse(args); const memory = await this.cbdEngine.get_memory(parsed.structuredKey); // Note: CBD doesn't have delete in the current implementation // This would need to be added to the StorageAdapter interface return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'Delete functionality not yet implemented in CBD backend', found: !!memory, metadata: { operation: 'forget', structuredKey: parsed.structuredKey, timestamp: new Date().toISOString() } }, null, 2) }] }; } async handleContext(args) { const parsed = ContextSchema.parse(args); // Use search_memory to get recent context const result = await this.cbdEngine.search_memory(`recent context for ${parsed.agentId}`, parsed.contextSize || 5, 0.0); const context = result.memories.slice(0, parsed.contextSize || 5).map(m => ({ structured_key: m.memory.structuredKey, content: m.memory.userRequest, response: m.memory.assistantResponse, created_at: m.memory.createdAt, project: m.memory.projectName, session: m.memory.sessionName })); return { content: [{ type: 'text', text: JSON.stringify({ success: true, context, agentId: parsed.agentId, contextSize: context.length, metadata: { operation: 'context', timestamp: new Date().toISOString(), compatibilityMode: true } }, null, 2) }] }; } async start() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('🤖 Enhanced MemoraiMCP Server with CBD backend running on stdio'); } } // Start the server const server = new EnhancedMemoraiMCPServer(); server.start().catch((error) => { console.error('Failed to start server:', error); process.exit(1); }); //# sourceMappingURL=enhanced-memorai-server.js.map