UNPKG

@mnemox/mcp-server-lite

Version:

MCP Server for MnemoX Lite - Semantic Memory for LLMs

324 lines (302 loc) 9.52 kB
#!/usr/bin/env node import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import fetch from 'node-fetch'; import { z } from 'zod'; const server = new McpServer({ name: 'mnemox-lite', version: '1.1.1', }); const API_URL = process.env.MNEMOX_API_URL || 'http://localhost:3000/api/mcp'; const API_KEY = process.env.MNEMOX_API_KEY || 'demo-key'; const USER_ID = process.env.MNEMOX_USER_ID || 'demo-user'; // Función helper para llamar a la API de MnemoX async function callMnemoXAPI(operation, data) { try { const response = await fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${API_KEY}` }, body: JSON.stringify({ operation, ...data }) }); if (!response.ok) { throw new Error(`API Error: ${response.status} ${response.statusText}`); } return await response.json(); } catch (error) { console.error(`Error calling MnemoX API:`, error); throw error; } } // Store information tool server.tool( 'store_information', { content: z.string().describe('The information to store in memory'), tags: z.array(z.string()).optional().describe('Tags to categorize this information'), source: z.string().optional().default('claude_conversation').describe('Source of this information') }, async ({ content, tags = [], source = 'claude_conversation' }) => { try { const result = await callMnemoXAPI('store_memory', { content, type: 'user_input', scope: { userId: USER_ID, projectId: 'general' }, tags, domain_specific: true }); if (result.success) { return { content: [{ type: 'text', text: `✅ Information stored successfully!\n\nFragment ID: ${result.data?.fragment_id || 'Unknown'}\nContent: ${content.substring(0, 100)}${content.length > 100 ? '...' : ''}` }] }; } else { return { content: [{ type: 'text', text: `❌ Error: ${result.error || 'Unknown error occurred'}` }], isError: true }; } } catch (error) { return { content: [{ type: 'text', text: `❌ Error calling MnemoX: ${error.message}` }], isError: true }; } } ); // Search memory tool server.tool( 'search_memory', { query: z.string().describe('What to search for in memory'), maxResults: z.number().min(1).max(50).optional().default(10).describe('Maximum number of results to return'), similarityThreshold: z.number().min(0).max(1).optional().default(0.7).describe('Minimum similarity score (0.0 - 1.0)') }, async ({ query, maxResults = 10, similarityThreshold = 0.7 }) => { try { const result = await callMnemoXAPI('search_memory', { query, scope: { userId: USER_ID, projectId: 'general' }, limit: maxResults, include_general: true, cross_project_search: false, domain_boost: true }); if (result.success) { if (result.data?.results?.length > 0) { let responseText = `🔍 Found ${result.data.results.length} relevant memories:\n\n`; result.data.results.forEach((fragment, index) => { responseText += `${index + 1}. [Score: ${fragment.relevance || 'N/A'}] ${fragment.content.substring(0, 200)}${fragment.content.length > 200 ? '...' : ''}\n\n`; }); return { content: [{ type: 'text', text: responseText }] }; } else { return { content: [{ type: 'text', text: `🔍 No relevant memories found for: "${query}"` }] }; } } else { return { content: [{ type: 'text', text: `❌ Error: ${result.error || 'Unknown error occurred'}` }], isError: true }; } } catch (error) { return { content: [{ type: 'text', text: `❌ Error calling MnemoX: ${error.message}` }], isError: true }; } } ); // Relate information tool server.tool( 'relate_information', { fragmentId: z.string().describe('ID of the memory fragment to find related information for'), maxResults: z.number().min(1).max(50).optional().default(10).describe('Maximum number of related items to return') }, async ({ fragmentId, maxResults = 10 }) => { try { const result = await callMnemoXAPI('search_memory', { query: `related to fragment ${fragmentId}`, scope: { userId: USER_ID, projectId: 'general' }, limit: maxResults, include_general: true }); if (result.success) { if (result.data?.results?.length > 0) { let responseText = `🔗 Found ${result.data.results.length} related memories:\n\n`; result.data.results.forEach((item, index) => { responseText += `${index + 1}. [Relation: semantic] ${item.content.substring(0, 200)}${item.content.length > 200 ? '...' : ''}\n\n`; }); return { content: [{ type: 'text', text: responseText }] }; } else { return { content: [{ type: 'text', text: `🔗 No related memories found for fragment: ${fragmentId}` }] }; } } else { return { content: [{ type: 'text', text: `❌ Error: ${result.error || 'Unknown error occurred'}` }], isError: true }; } } catch (error) { return { content: [{ type: 'text', text: `❌ Error calling MnemoX: ${error.message}` }], isError: true }; } } ); // Contextualize information tool server.tool( 'contextualize_information', { query: z.string().describe('Description of the context or theme to find/create'), createIfNotExists: z.boolean().optional().default(true).describe('Whether to create the context if it does not exist'), maxFragments: z.number().min(1).max(200).optional().default(50).describe('Maximum number of fragments to include in context') }, async ({ query, createIfNotExists = true, maxFragments = 50 }) => { try { const result = await callMnemoXAPI('create_context', { name: query, description: `Context for: ${query}`, scope: { userId: USER_ID, projectId: 'general' }, auto_populate: createIfNotExists, max_fragments: maxFragments }); if (result.success) { return { content: [{ type: 'text', text: `📚 Context "${query}" ${result.data?.context_created ? 'created' : 'found'} with ${result.data?.fragments_added || 0} fragments` }] }; } else { return { content: [{ type: 'text', text: `❌ Error: ${result.error || 'Unknown error occurred'}` }], isError: true }; } } catch (error) { return { content: [{ type: 'text', text: `❌ Error calling MnemoX: ${error.message}` }], isError: true }; } } ); // Get memory stats tool server.tool( 'get_memory_stats', {}, async () => { try { const result = await callMnemoXAPI('get_system_status', { scope: { userId: USER_ID, projectId: 'general' } }); if (result.success) { const stats = result.data?.system_status || {}; return { content: [{ type: 'text', text: `📊 Memory Statistics:\n• Total Fragments: ${stats.total_fragments || 0}\n• Active Users: ${stats.active_users || 0}\n• System Health: ${stats.health_status || 'Unknown'}` }] }; } else { return { content: [{ type: 'text', text: `❌ Error: ${result.error || 'Unknown error occurred'}` }], isError: true }; } } catch (error) { return { content: [{ type: 'text', text: `❌ Error calling MnemoX: ${error.message}` }], isError: true }; } } ); // Iniciar servidor async function main() { const transport = new StdioServerTransport(); await server.connect(transport); // Log de inicio (a stderr para no interferir con MCP) console.error('🧠 MnemoX MCP Server v1.1.1 started'); console.error(`📡 API URL: ${API_URL}`); console.error(`🔑 API Key: ${API_KEY.substring(0, 8)}...`); console.error(`👤 User ID: ${USER_ID}`); } main().catch((error) => { console.error('Failed to start MnemoX MCP Server:', error); process.exit(1); });