@mnemox/mcp-server-lite
Version:
MCP Server for MnemoX Lite - Semantic Memory for LLMs
324 lines (302 loc) • 9.52 kB
JavaScript
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);
});