Skip to main content
Glama

MCP Memory Service

memory-retrieval.js8.89 kB
/** * On-Demand Memory Retrieval Hook * Allows users to manually request context refresh when needed */ const fs = require('fs').promises; const path = require('path'); const https = require('https'); // Import utilities const { detectProjectContext } = require('../utilities/project-detector'); const { scoreMemoryRelevance } = require('../utilities/memory-scorer'); const { formatMemoriesForContext } = require('../utilities/context-formatter'); /** * Load hook configuration */ async function loadConfig() { try { const configPath = path.join(__dirname, '../config.json'); const configData = await fs.readFile(configPath, 'utf8'); return JSON.parse(configData); } catch (error) { console.warn('[Memory Retrieval] Using default configuration:', error.message); return { memoryService: { endpoint: 'https://narrowbox.local:8443', apiKey: 'test-key-123', maxMemoriesPerSession: 5 } }; } } /** * Query memory service for relevant memories */ async function queryMemoryService(endpoint, apiKey, query) { return new Promise((resolve, reject) => { const url = new URL('/mcp', endpoint); const postData = JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name: 'retrieve_memory', arguments: { query: query.semanticQuery || '', n_results: query.limit || 5 } } }); const options = { hostname: url.hostname, port: url.port || 8443, path: url.pathname, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData), 'Authorization': `Bearer ${apiKey}` }, rejectUnauthorized: false // For self-signed certificates }; const req = https.request(options, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { try { const response = JSON.parse(data); if (response.result && response.result.content) { let textData = response.result.content[0].text; try { // Convert Python dict format to JSON format safely textData = textData .replace(/'/g, '"') .replace(/True/g, 'true') .replace(/False/g, 'false') .replace(/None/g, 'null'); const memories = JSON.parse(textData); resolve(memories.results || memories.memories || []); } catch (conversionError) { console.warn('[Memory Retrieval] Could not parse memory response:', conversionError.message); resolve([]); } } else { resolve([]); } } catch (parseError) { console.warn('[Memory Retrieval] Parse error:', parseError.message); resolve([]); } }); }); req.on('error', (error) => { console.warn('[Memory Retrieval] Network error:', error.message); resolve([]); }); req.write(postData); req.end(); }); } /** * On-demand memory retrieval function */ async function retrieveMemories(context) { try { console.log('[Memory Retrieval] On-demand memory retrieval requested...'); // Load configuration const config = await loadConfig(); // Detect project context const projectContext = await detectProjectContext(context.workingDirectory || process.cwd()); console.log(`[Memory Retrieval] Project context: ${projectContext.name} (${projectContext.language})`); // Parse user query if provided const userQuery = context.query || context.message || ''; // Build memory query const memoryQuery = { tags: [ projectContext.name, `language:${projectContext.language}`, 'key-decisions', 'architecture', 'recent-insights' ].filter(Boolean), semanticQuery: userQuery.length > 0 ? `${projectContext.name} ${userQuery}` : `${projectContext.name} project context decisions architecture`, limit: config.memoryService.maxMemoriesPerSession || 5, timeFilter: 'last-month' }; // Query memory service const memories = await queryMemoryService( config.memoryService.endpoint, config.memoryService.apiKey, memoryQuery ); if (memories.length > 0) { console.log(`[Memory Retrieval] Found ${memories.length} relevant memories`); // Score memories for relevance const scoredMemories = scoreMemoryRelevance(memories, projectContext); // Take top scored memories const topMemories = scoredMemories.slice(0, config.memoryService.maxMemoriesPerSession || 5); // Format memories for display const contextMessage = formatMemoriesForContext(topMemories, projectContext, { includeScore: true, // Show scores for manual retrieval groupByCategory: topMemories.length > 3, maxMemories: config.memoryService.maxMemoriesPerSession || 5, includeTimestamp: true }); // Output formatted context if (context.displayResult) { await context.displayResult(contextMessage); console.log('[Memory Retrieval] Successfully displayed memory context'); } else { // Fallback: log context console.log('\n=== RETRIEVED MEMORY CONTEXT ==='); console.log(contextMessage); console.log('=== END CONTEXT ===\n'); } return { success: true, memoriesFound: memories.length, memoriesShown: topMemories.length, context: contextMessage }; } else { const message = `## 📋 Memory Retrieval\n\nNo relevant memories found for query: "${userQuery || 'project context'}"\n\nTry a different search term or check if your memory service is running.`; if (context.displayResult) { await context.displayResult(message); } else { console.log(message); } return { success: false, memoriesFound: 0, memoriesShown: 0, context: message }; } } catch (error) { console.error('[Memory Retrieval] Error retrieving memories:', error.message); const errorMessage = `## ❌ Memory Retrieval Error\n\n${error.message}\n\nCheck your memory service configuration and connection.`; if (context.displayResult) { await context.displayResult(errorMessage); } return { success: false, error: error.message }; } } /** * Hook metadata for Claude Code */ module.exports = { name: 'on-demand-memory-retrieval', version: '1.0.0', description: 'Retrieve relevant memories on user request', trigger: 'manual', // This hook is triggered manually handler: retrieveMemories, config: { async: true, timeout: 10000, priority: 'normal' } }; // Direct execution support for testing if (require.main === module) { // Test the retrieval with mock context const mockContext = { workingDirectory: process.cwd(), query: 'architecture decisions', displayResult: async (message) => { console.log('=== MOCK DISPLAY RESULT ==='); console.log(message); console.log('=== END MOCK DISPLAY ==='); } }; retrieveMemories(mockContext) .then(result => console.log('Retrieval test completed:', result)) .catch(error => console.error('Retrieval test failed:', error)); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/doobidoo/mcp-memory-service'

If you have feedback or need assistance with the MCP directory API, please join our Discord server