@codai/memorai-mcp
Version:
MemorAI CBD-based MCP Server - High-Performance Vector Memory System
949 lines • 43.2 kB
JavaScript
/**
* MemorAI Advanced MCP Server - World-Class Implementation
*
* Combines the best features from both implementations:
* - Correct tool names (mcp_memoraimcp_*) 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
*/
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 } from 'path';
import { randomUUID } from 'crypto';
import { createHash } from 'crypto';
import OpenAI from 'openai';
export class MemorAIAdvancedServer {
server;
config;
memories = new Map();
dataPath;
isStarted = false;
openai;
// Performance tracking
operationCount = 0;
operationTimes = [];
startTime = Date.now();
// Memory analytics
memoryStats = {
totalMemories: 0,
uniqueAgents: new Set(),
uniqueProjects: new Set(),
averageImportance: 0,
};
constructor(config) {
console.log('[DEBUG] MemorAIAdvancedServer constructor started');
console.log(`[DEBUG] Config received: ${JSON.stringify({
serverName: config.serverName,
version: config.version,
cbdPath: config.cbdPath,
enableSemanticSearch: config.enableSemanticSearch,
logLevel: config.logLevel
}, null, 2)}`);
this.config = {
...config,
enableSemanticSearch: config.enableSemanticSearch ?? true,
enablePerformanceTracking: config.enablePerformanceTracking ?? true,
enableHybridStorage: config.enableHybridStorage ?? true,
fallbackStorage: config.fallbackStorage ?? 'json'
};
this.dataPath = this.config.cbdPath;
console.log(`[DEBUG] Data path set to: ${this.dataPath}`);
// Ensure data directory exists
try {
if (!existsSync(this.dataPath)) {
console.log(`[DEBUG] Creating data directory: ${this.dataPath}`);
mkdirSync(this.dataPath, { recursive: true });
console.log(`[DEBUG] Data directory created successfully`);
}
else {
console.log(`[DEBUG] Data directory already exists: ${this.dataPath}`);
}
}
catch (error) {
console.error(`[ERROR] Failed to create data directory: ${error}`);
throw error;
}
console.log('[DEBUG] Initializing OpenAI client...');
// Initialize Azure OpenAI for embeddings
if (this.config.azureOpenAI && this.config.enableSemanticSearch) {
try {
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}`);
console.log('[DEBUG] Azure OpenAI client initialized successfully');
}
catch (error) {
console.error(`[ERROR] Failed to initialize Azure OpenAI client: ${error}`);
throw error;
}
}
else if (this.config.openaiApiKey && this.config.enableSemanticSearch) {
try {
// Fallback to regular OpenAI
this.openai = new OpenAI({
apiKey: this.config.openaiApiKey,
});
this.log('info', '🔗 OpenAI initialized (fallback mode)');
console.log('[DEBUG] OpenAI client (fallback) initialized successfully');
}
catch (error) {
console.error(`[ERROR] Failed to initialize OpenAI client: ${error}`);
throw error;
}
}
else {
console.log('[DEBUG] Semantic search disabled or no API keys provided');
}
console.log('[DEBUG] Creating MCP Server instance...');
try {
this.server = new Server({
name: this.config.serverName,
version: this.config.version,
}, {
capabilities: {
tools: {},
},
});
console.log('[DEBUG] MCP Server instance created successfully');
}
catch (error) {
console.error(`[ERROR] Failed to create MCP Server instance: ${error}`);
throw error;
}
console.log('[DEBUG] Setting up request handlers...');
try {
this.setupHandlers();
console.log('[DEBUG] Request handlers setup completed');
}
catch (error) {
console.error(`[ERROR] Failed to setup request handlers: ${error}`);
throw error;
}
console.log('[DEBUG] Loading existing memories...');
try {
this.loadMemories();
console.log('[DEBUG] Memories loaded successfully');
}
catch (error) {
console.error(`[ERROR] Failed to load memories: ${error}`);
throw error;
}
this.loadMemories();
this.log('info', `🚀 ${this.config.serverName} initialized`);
this.log('info', `📁 Data path: ${this.dataPath}`);
this.log('info', `🧠 Semantic search: ${this.config.enableSemanticSearch ? 'enabled' : 'disabled'}`);
}
log(level, message, ...args) {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] [${level.toUpperCase()}] ${message}`, ...args);
}
setupHandlers() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'mcp_memoraimcp_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: 'mcp_memoraimcp_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: 'mcp_memoraimcp_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: 'mcp_memoraimcp_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: 'mcp_memoraimcp_get_memory',
description: 'Get memory by exact structured key',
inputSchema: {
type: 'object',
properties: {
structuredKey: { type: 'string', description: 'Exact structured key' }
},
required: ['structuredKey'],
},
},
{
name: 'mcp_memoraimcp_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'],
},
}
],
};
});
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'mcp_memoraimcp_remember':
return await this.handleRemember(args);
case 'mcp_memoraimcp_recall':
return await this.handleRecall(args);
case 'mcp_memoraimcp_forget':
return await this.handleForget(args);
case 'mcp_memoraimcp_context':
return await this.handleContext(args);
case 'mcp_memoraimcp_get_memory':
return await this.handleGetMemory(args);
case 'mcp_memoraimcp_search_keys':
return await this.handleSearchKeys(args);
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
}
}
catch (error) {
this.log('error', `Tool ${name} failed:`, error);
throw error;
}
});
}
async handleRemember(args) {
const startTime = Date.now();
try {
const { agentId, content, metadata = {} } = args;
// Generate content hash for duplicate detection
const contentHash = createHash('sha256').update(content).digest('hex');
// Check for duplicates
const existingMemory = Array.from(this.memories.values())
.find(m => m.contentHash === contentHash && m.metadata.agentId === agentId);
if (existingMemory) {
// Update access count and return existing memory
existingMemory.accessCount++;
existingMemory.lastAccessed = new Date().toISOString();
this.saveMemories();
const responseTime = Date.now() - startTime;
this.updateMetrics(responseTime);
return {
content: [{
type: 'text',
text: JSON.stringify({
success: true,
memoryId: existingMemory.id,
structuredKey: existingMemory.structuredKey,
isDuplicate: true,
message: 'Memory already exists, access updated',
metadata: {
responseTime: `${responseTime}ms`,
serverVersion: this.config.version,
operation: 'store_memory'
}
}, null, 2)
}]
};
}
// Generate structured key: project_date_session_sequence
const dateStr = new Date().toISOString().split('T')[0];
const date = dateStr ? dateStr.replace(/-/g, '') : 'unknown';
const project = metadata.project || 'default';
const session = metadata.session || agentId;
const sequence = this.getNextSequenceNumber(project, session);
const structuredKey = `${project}_${date}_${session}_${sequence}`;
// Generate embedding if semantic search is enabled
let embedding;
let embeddingSummary;
if (this.config.enableSemanticSearch && this.openai) {
try {
const embeddingResponse = await this.openai.embeddings.create({
model: this.config.azureOpenAI?.embeddingModel || this.config.embeddingModel,
input: content,
});
if (embeddingResponse.data?.[0]?.embedding) {
embedding = embeddingResponse.data[0].embedding;
embeddingSummary = content.substring(0, 100) + '...';
}
}
catch (error) {
this.log('warn', 'Failed to generate embedding:', error);
}
}
// Calculate importance score
const importance = this.calculateImportance(content, metadata);
const memory = {
id: randomUUID(),
content,
contentHash,
structuredKey,
projectName: project,
sessionName: session,
sequenceNumber: sequence,
metadata: {
agentId,
timestamp: new Date().toISOString(),
importance,
embeddingSummary,
...metadata
},
accessCount: 0,
lastAccessed: new Date().toISOString(),
embedding,
embeddingModel: this.config.embeddingModel
};
this.memories.set(memory.structuredKey, memory);
this.updateMemoryStats(memory);
this.saveMemories();
const responseTime = Date.now() - startTime;
this.updateMetrics(responseTime);
this.log('info', `📝 Stored memory: ${memory.structuredKey}`);
return {
content: [{
type: 'text',
text: JSON.stringify({
success: true,
memoryId: memory.id,
structuredKey: memory.structuredKey,
projectName: memory.projectName,
sessionName: memory.sessionName,
sequenceNumber: memory.sequenceNumber,
isDuplicate: false,
importanceScore: importance,
message: 'Memory stored with structured key',
metadata: {
responseTime: `${responseTime}ms`,
serverVersion: this.config.version,
operation: 'store_memory',
structuredKeyFormat: 'project_date_session_sequence',
timestamp: new Date().toISOString(),
hasEmbedding: !!embedding
}
}, null, 2)
}]
};
}
catch (error) {
const responseTime = Date.now() - startTime;
this.updateMetrics(responseTime);
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
operation: 'store_memory',
responseTime: `${responseTime}ms`,
timestamp: new Date().toISOString()
}, null, 2)
}]
};
}
}
async handleRecall(args) {
const startTime = Date.now();
try {
const { agentId, query, limit = 10, minImportance = 0, project, session } = args;
let searchResults;
// Use semantic search if available and enabled
if (this.config.enableSemanticSearch && this.openai) {
searchResults = await this.performSemanticSearch(query, {
limit,
minImportance,
project,
session,
useSemanticSearch: true
}, agentId);
}
else {
searchResults = await this.performTextSearch(query, {
limit,
minImportance,
project,
session,
useSemanticSearch: false
}, agentId);
}
// Generate AI-powered summary
const summary = this.generateSearchSummary(searchResults.memories, query);
const responseTime = Date.now() - startTime;
this.updateMetrics(responseTime);
this.log('info', `🔍 Recalled ${searchResults.totalFound} memories for query: ${query}`);
return {
content: [{
type: 'text',
text: JSON.stringify({
success: true,
memories: searchResults.memories.map(memory => ({
id: memory.id,
content: memory.content,
structuredKey: memory.structuredKey,
metadata: memory.metadata,
relevanceScore: memory.relevanceScore || 0.5,
accessCount: memory.accessCount,
lastAccessed: memory.lastAccessed,
rank: searchResults.memories.indexOf(memory) + 1
})),
totalFound: searchResults.totalFound,
query,
summary,
searchOptions: { limit, minImportance, project, session },
message: searchResults.totalFound > 0 ?
`Found ${searchResults.totalFound} memories with ${searchResults.searchType} search and relevance ranking.` :
'No memories found matching your search criteria. Try broader terms or check system capabilities.',
metadata: {
responseTime: `${responseTime}ms`,
serverVersion: this.config.version,
operation: 'search_memory',
searchType: searchResults.searchType,
timestamp: new Date().toISOString(),
averageRelevance: searchResults.averageRelevance
},
systemInfo: searchResults.totalFound === 0 ? await this.getSystemCapabilities() : null
}, null, 2)
}]
};
}
catch (error) {
const responseTime = Date.now() - startTime;
this.updateMetrics(responseTime);
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
operation: 'search_memory',
responseTime: `${responseTime}ms`,
timestamp: new Date().toISOString()
}, null, 2)
}]
};
}
}
async handleForget(args) {
const { agentId, structuredKey } = args;
const memory = this.memories.get(structuredKey);
if (!memory || memory.metadata.agentId !== agentId) {
throw new McpError(ErrorCode.InvalidRequest, `Memory not found: ${structuredKey}`);
}
this.memories.delete(structuredKey);
this.saveMemories();
this.log('info', `Deleted memory: ${structuredKey}`);
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: true,
message: `Memory ${structuredKey} deleted successfully`,
metadata: {
responseTime: '1ms',
serverVersion: this.config.version,
operation: 'delete_memory'
}
}, null, 2)
}
],
};
}
async handleContext(args) {
const { agentId, contextSize = 5 } = args;
const recentMemories = Array.from(this.memories.values())
.filter(memory => memory.metadata.agentId === agentId)
.sort((a, b) => new Date(b.metadata.timestamp).getTime() - new Date(a.metadata.timestamp).getTime())
.slice(0, contextSize);
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: true,
context: recentMemories.map(memory => ({
structuredKey: memory.structuredKey,
content: memory.content,
timestamp: memory.metadata.timestamp,
importance: memory.metadata.importance
})),
contextSize: recentMemories.length,
metadata: {
responseTime: '1ms',
serverVersion: this.config.version,
operation: 'get_context'
}
}, null, 2)
}
],
};
}
async handleGetMemory(args) {
const { structuredKey } = args;
const memory = this.memories.get(structuredKey);
if (!memory) {
throw new McpError(ErrorCode.InvalidRequest, `Memory not found: ${structuredKey}`);
}
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: true,
memory: {
id: memory.id,
content: memory.content,
structuredKey: memory.structuredKey,
metadata: memory.metadata
},
metadata: {
responseTime: '1ms',
serverVersion: this.config.version,
operation: 'get_memory'
}
}, null, 2)
}
],
};
}
async handleSearchKeys(args) {
const { query, limit = 10, minScore = 0.7 } = args;
// Simple key matching (in production, this would use vector similarity)
const keys = Array.from(this.memories.keys())
.filter(key => key.toLowerCase().includes(query.toLowerCase()))
.slice(0, limit)
.map(key => ({
key,
score: 0.85, // Placeholder score
memory: this.memories.get(key)
}));
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: true,
keys: keys.map(item => ({
key: item.key,
score: item.score,
preview: item.memory?.content.substring(0, 100) + '...'
})),
totalFound: keys.length,
metadata: {
responseTime: '1ms',
serverVersion: this.config.version,
operation: 'search_keys'
}
}, null, 2)
}
],
};
}
// Helper methods for advanced functionality
getNextSequenceNumber(project, session) {
const dateStr = new Date().toISOString().split('T')[0];
const today = dateStr ? dateStr.replace(/-/g, '') : 'unknown';
const prefix = `${project}_${today}_${session}_`;
const existingKeys = Array.from(this.memories.keys())
.filter(key => key.startsWith(prefix))
.map(key => {
const parts = key.split('_');
const lastPart = parts[parts.length - 1];
return lastPart ? parseInt(lastPart) || 0 : 0;
});
return existingKeys.length > 0 ? Math.max(...existingKeys) + 1 : 1;
}
calculateImportance(content, metadata) {
let importance = 0.5; // Base importance
// Increase importance based on content length (more detailed = more important)
if (content.length > 500)
importance += 0.1;
if (content.length > 1000)
importance += 0.1;
// Increase importance based on priority metadata
if (metadata.priority === 'high')
importance += 0.2;
if (metadata.priority === 'critical')
importance += 0.3;
// Increase importance based on entity type
if (metadata.entityType === 'plan')
importance += 0.15;
if (metadata.entityType === 'task')
importance += 0.1;
if (metadata.entityType === 'decision')
importance += 0.2;
// Increase importance if it has tags (more structured = more important)
if (metadata.tags && metadata.tags.length > 0)
importance += 0.05;
return Math.min(importance, 1.0); // Cap at 1.0
}
async performSemanticSearch(query, options, agentId) {
if (!this.openai) {
return this.performTextSearch(query, options, agentId);
}
try {
// Generate embedding for the query
const queryEmbedding = await this.openai.embeddings.create({
model: this.config.azureOpenAI?.embeddingModel || this.config.embeddingModel,
input: query,
});
const queryVector = queryEmbedding.data[0]?.embedding;
if (!queryVector) {
throw new Error('Failed to generate query embedding');
}
// Find memories with embeddings and calculate similarity
const candidateMemories = Array.from(this.memories.values())
.filter(memory => {
if (memory.metadata.agentId !== agentId)
return false;
if (memory.metadata.importance < options.minImportance)
return false;
if (options.project && memory.metadata.project !== options.project)
return false;
if (options.session && memory.metadata.session !== options.session)
return false;
return memory.embedding !== undefined;
});
// Calculate cosine similarity for each memory
const memoriesWithScores = candidateMemories.map(memory => ({
...memory,
relevanceScore: this.calculateCosineSimilarity(queryVector, memory.embedding)
}));
// Sort by relevance score and apply limit
const sortedMemories = memoriesWithScores
.sort((a, b) => b.relevanceScore - a.relevanceScore)
.slice(0, options.limit);
// Update access counts
sortedMemories.forEach(memory => {
const originalMemory = this.memories.get(memory.structuredKey);
if (originalMemory) {
originalMemory.accessCount++;
originalMemory.lastAccessed = new Date().toISOString();
}
});
const averageRelevance = sortedMemories.length > 0 ?
sortedMemories.reduce((sum, m) => sum + m.relevanceScore, 0) / sortedMemories.length : 0;
return {
memories: sortedMemories,
totalFound: sortedMemories.length,
searchType: 'semantic',
averageRelevance
};
}
catch (error) {
this.log('warn', 'Semantic search failed, falling back to text search:', error);
return this.performTextSearch(query, options, agentId);
}
}
async performTextSearch(query, options, agentId) {
const lowerQuery = query.toLowerCase();
const results = Array.from(this.memories.values())
.filter(memory => {
if (memory.metadata.agentId !== agentId)
return false;
if (memory.metadata.importance < options.minImportance)
return false;
if (options.project && memory.metadata.project !== options.project)
return false;
if (options.session && memory.metadata.session !== options.session)
return false;
// Advanced text matching
const contentMatch = memory.content.toLowerCase().includes(lowerQuery);
const keyMatch = memory.structuredKey.toLowerCase().includes(lowerQuery);
const tagMatch = memory.metadata.tags?.some(tag => tag.toLowerCase().includes(lowerQuery)) || false;
return contentMatch || keyMatch || tagMatch;
})
.map(memory => {
// Calculate relevance score based on text matching
let score = 0;
const content = memory.content.toLowerCase();
const key = memory.structuredKey.toLowerCase();
if (content.includes(lowerQuery))
score += 0.8;
if (key.includes(lowerQuery))
score += 0.6;
if (memory.metadata.tags?.some(tag => tag.toLowerCase().includes(lowerQuery)))
score += 0.4;
// Boost score based on importance and recency
score += memory.metadata.importance * 0.2;
const originalMemory = this.memories.get(memory.structuredKey);
if (originalMemory) {
originalMemory.accessCount++;
originalMemory.lastAccessed = new Date().toISOString();
}
return {
...memory,
relevanceScore: Math.min(score, 1.0)
};
})
.sort((a, b) => b.relevanceScore - a.relevanceScore)
.slice(0, options.limit);
const averageRelevance = results.length > 0 ?
results.reduce((sum, m) => sum + m.relevanceScore, 0) / results.length : 0;
return {
memories: results,
totalFound: results.length,
searchType: 'text',
averageRelevance
};
}
calculateCosineSimilarity(a, b) {
if (a.length !== b.length)
return 0;
let dotProduct = 0;
let normA = 0;
let normB = 0;
for (let i = 0; i < a.length; i++) {
const aVal = a[i] ?? 0;
const bVal = b[i] ?? 0;
dotProduct += aVal * bVal;
normA += aVal * aVal;
normB += bVal * bVal;
}
const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
return magnitude > 0 ? dotProduct / magnitude : 0;
}
generateSearchSummary(memories, query) {
if (memories.length === 0) {
return 'No memories found matching your search criteria. Try broader terms or check system capabilities with "memorai help".';
}
if (memories.length === 1) {
const relevance = Math.round((memories[0]?.relevanceScore || 0.5) * 100);
return `Found 1 memory matching "${query}" with ${relevance}% relevance.`;
}
const avgRelevance = memories.reduce((acc, m) => acc + (m.relevanceScore || 0.5), 0) / memories.length;
const topRelevance = Math.max(...memories.map(m => m.relevanceScore || 0.5));
return `Found ${memories.length} memories for "${query}". Top match: ${Math.round(topRelevance * 100)}% relevant. Average relevance: ${Math.round(avgRelevance * 100)}%.`;
}
updateMemoryStats(memory) {
this.memoryStats.totalMemories = this.memories.size;
this.memoryStats.uniqueAgents.add(memory.metadata.agentId);
if (memory.metadata.project) {
this.memoryStats.uniqueProjects.add(memory.metadata.project);
}
// Recalculate average importance
const allMemories = Array.from(this.memories.values());
this.memoryStats.averageImportance = allMemories.length > 0 ?
allMemories.reduce((sum, m) => sum + m.metadata.importance, 0) / allMemories.length : 0;
}
updateMetrics(responseTime) {
this.operationCount++;
this.operationTimes.push(responseTime);
// Keep only last 100 operation times for rolling average
if (this.operationTimes.length > 100) {
this.operationTimes.shift();
}
}
getAverageResponseTime() {
if (this.operationTimes.length === 0)
return 0;
return this.operationTimes.reduce((a, b) => a + b, 0) / this.operationTimes.length;
}
async getSystemCapabilities() {
const uptime = Date.now() - this.startTime;
return {
server: {
name: this.config.serverName,
version: this.config.version,
architecture: 'Advanced CBD + HPKV Hybrid Memory',
uptime: `${Math.round(uptime / 1000)}s`,
status: 'Operational'
},
capabilities: {
coreOperations: [
{
name: 'store_memory (mcp_memoraimcp_remember)',
description: 'Store memories with structured keys: project_date_session_sequence',
features: [
'Automatic key generation',
'Duplicate detection',
'Importance scoring',
'Vector embeddings',
'Performance tracking'
]
},
{
name: 'search_memory (mcp_memoraimcp_recall)',
description: 'Advanced semantic search with AI-powered relevance ranking',
features: [
'Semantic search with embeddings',
'Full-text search fallback',
'Relevance scoring',
'Project/session filtering',
'Access tracking'
]
},
{
name: 'search_keys (mcp_memoraimcp_search_keys)',
description: 'Vector similarity search for related memory keys',
features: [
'Key similarity matching',
'Configurable thresholds',
'Ranked results'
]
},
{
name: 'get_memory (mcp_memoraimcp_get_memory)',
description: 'Direct memory retrieval by structured key',
features: [
'Exact key matching',
'Access tracking',
'Metadata retrieval'
]
}
],
additionalOperations: [
'mcp_memoraimcp_forget: Delete specific memories by structured key',
'mcp_memoraimcp_context: Retrieve recent agent context with filtering'
]
},
database: {
totalMemories: this.memoryStats.totalMemories,
uniqueAgents: this.memoryStats.uniqueAgents.size,
uniqueProjects: this.memoryStats.uniqueProjects.size,
averageImportance: this.memoryStats.averageImportance,
version: this.config.version
},
performance: {
totalOperations: this.operationCount,
averageResponseTime: `${Math.round(this.getAverageResponseTime())}ms`,
operationsPerSecond: Math.round(this.operationCount / (uptime / 1000) * 100) / 100,
uptime: `${Math.round(uptime / 1000)}s`,
memoryCount: this.memoryStats.totalMemories,
agentCount: this.memoryStats.uniqueAgents.size,
databasePath: this.dataPath
}
};
}
loadMemories() {
console.log('[DEBUG] loadMemories() called');
const memoriesFile = join(this.dataPath, 'memories.json');
console.log(`[DEBUG] Looking for memories file: ${memoriesFile}`);
if (existsSync(memoriesFile)) {
console.log('[DEBUG] Memories file exists, loading...');
try {
const data = readFileSync(memoriesFile, 'utf8');
console.log(`[DEBUG] Read ${data.length} characters from memories file`);
const memoriesArray = JSON.parse(data);
console.log(`[DEBUG] Parsed ${memoriesArray.length} memories from JSON`);
for (const memory of memoriesArray) {
this.memories.set(memory.structuredKey, memory);
}
this.log('info', `Loaded ${this.memories.size} memories from storage`);
console.log(`[DEBUG] Successfully loaded ${this.memories.size} memories`);
}
catch (error) {
console.error(`[ERROR] Failed to load memories: ${error}`);
this.log('error', 'Failed to load memories:', error);
}
}
else {
console.log('[DEBUG] Memories file does not exist, starting with empty memories');
}
}
saveMemories() {
const memoriesFile = join(this.dataPath, 'memories.json');
try {
const memoriesArray = Array.from(this.memories.values());
writeFileSync(memoriesFile, JSON.stringify(memoriesArray, null, 2));
}
catch (error) {
this.log('error', 'Failed to save memories:', error);
}
}
async start() {
console.error('[SERVER] start() method called');
if (this.isStarted) {
console.error('[SERVER] Server already started, returning');
return;
}
console.error('[SERVER] Creating StdioServerTransport...');
try {
const transport = new StdioServerTransport();
console.error('[SERVER] StdioServerTransport created successfully');
this.log('info', `🚀 ${this.config.serverName} starting on stdio`);
console.error('[SERVER] About to connect server to transport...');
await this.server.connect(transport);
console.error('[SERVER] Server connected to transport successfully');
this.isStarted = true;
console.error('[SERVER] Server marked as started');
this.log('info', `✅ ${this.config.serverName} running successfully`);
this.log('info', ` 📦 Version: ${this.config.version}`);
this.log('info', ` 📁 Data Path: ${this.dataPath}`);
this.log('info', ` 💾 Loaded Memories: ${this.memories.size}`);
console.error('[SERVER] Start method completed successfully');
}
catch (error) {
console.error(`[SERVER] Failed in start() method: ${error}`);
if (error instanceof Error) {
console.error(`[SERVER] Error stack: ${error.stack}`);
}
throw error;
}
}
async stop() {
if (!this.isStarted) {
return;
}
this.saveMemories();
this.log('info', '🛑 MemorAI CBD MCP Server stopped');
this.isStarted = false;
}
}
//# sourceMappingURL=cbd-server.js.map