Skip to main content
Glama

mem0 Memory System

#!/usr/bin/env node /** * MCP server for interacting with Mem0.ai memory storage. * Provides tools to add and search memories. * * Supports two modes: * 1. Cloud mode: Uses Mem0's hosted API with MEM0_API_KEY * 2. Local mode: Uses in-memory storage with OPENAI_API_KEY for embeddings */ // Create a wrapper around console to safely redirect logs from libraries // This ensures MCP protocol communication is not affected class SafeLogger { private originalConsoleLog: typeof console.log; constructor() { // Store the original console.log this.originalConsoleLog = console.log; // Redirect console.log to stderr only for our module console.log = (...args) => { // Check if it's from the mem0ai library or our code const stack = new Error().stack || ''; if (stack.includes('mem0ai') || stack.includes('mem0-mcp')) { console.error('[redirected log]', ...args); } else { // Keep normal behavior for MCP protocol and other code this.originalConsoleLog.apply(console, args); } }; } // Restore original behavior restore() { console.log = this.originalConsoleLog; } } // Apply the safe logger const safeLogger = new SafeLogger(); // Disable debug logs in any libraries that respect these environment variables process.env.DEBUG = ''; // Disable debug logs process.env.NODE_DEBUG = ''; // Disable Node.js internal debugging process.env.DEBUG_COLORS = 'no'; // Disable color output in logs process.env.NODE_ENV = process.env.NODE_ENV || 'production'; // Use production mode by default process.env.LOG_LEVEL = 'error'; // Set log level to error only process.env.SILENT = 'true'; // Some libraries respect this process.env.QUIET = 'true'; // Some libraries respect this // IMPORTANT: Don't globally override stdout as it breaks MCP protocol // We'll use more targeted approaches in specific methods import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode, } from "@modelcontextprotocol/sdk/types.js"; import { Memory } from "mem0ai/oss"; // For local in-memory storage // Using dynamic import for cloud API to avoid TypeScript issues let MemoryClient: any = null; // Type for the arguments received by the MCP tool handlers interface Mem0AddToolArgs { content: string; userId?: string; sessionId?: string; // This maps to run_id in Mem0 API agentId?: string; // The LLM/agent making the tool call appId?: string; // Application identifier (legacy parameter) projectId?: string; // Project identifier (for mem0 Pro plan project organization) orgId?: string; // Organization identifier (for mem0 organization-level management) metadata?: any; // Advanced Mem0 API parameters includes?: string; excludes?: string; infer?: boolean; outputFormat?: string; customCategories?: any; customInstructions?: string; immutable?: boolean; expirationDate?: string; } interface Mem0SearchToolArgs { query: string; userId?: string; sessionId?: string; // This maps to run_id in Mem0 API agentId?: string; // The LLM/agent making the tool call appId?: string; // Application identifier (legacy parameter) projectId?: string; // Project identifier (for mem0 Pro plan project organization) orgId?: string; // Organization identifier (for mem0 organization-level management) filters?: any; threshold?: number; // Advanced Mem0 API search parameters topK?: number; fields?: string[]; rerank?: boolean; keywordSearch?: boolean; filterMemories?: boolean; } interface Mem0DeleteToolArgs { memoryId: string; userId?: string; agentId?: string; // The LLM/agent making the tool call appId?: string; // Application identifier (legacy parameter) projectId?: string; // Project identifier (for mem0 Pro plan project organization) orgId?: string; // Organization identifier (for mem0 organization-level management) } // Message type for Mem0 API type Mem0Message = { role: "user" | "assistant" | "system"; content: string; }; class Mem0MCPServer { private server: Server; private isCloudMode: boolean = false; private isSupabaseMode: boolean = false; private localClient?: Memory; private cloudClient?: any; private supabaseClient?: Memory; private isReady: boolean = false; constructor() { console.error("Initializing Mem0 MCP Server..."); // Check for Mem0 API key first (for cloud mode) const mem0ApiKey = process.env.MEM0_API_KEY; // Check for Supabase credentials (for Supabase mode) const supabaseUrl = process.env.SUPABASE_URL; const supabaseKey = process.env.SUPABASE_KEY; // Check for OpenAI API key (for local mode) const openaiApiKey = process.env.OPENAI_API_KEY; // Initialize MCP Server this.server = new Server( { // These should match package.json name: "@pinkpixel/mem0-mcp", version: "0.6.1", }, { capabilities: { // Only tools capability needed for now tools: {}, }, } ); this.setupToolHandlers(); // Determine the mode based on available keys (priority: Cloud > Supabase > Local) if (mem0ApiKey) { console.error("Using Mem0 cloud storage mode with MEM0_API_KEY"); this.isCloudMode = true; // Dynamic import for cloud client import('mem0ai').then(module => { try { MemoryClient = module.default; // Get default app_id and agent_id for fallbacks const defaultAppId = process.env.DEFAULT_APP_ID; const defaultAgentId = process.env.DEFAULT_AGENT_ID; // Initialize with basic options ONLY - DO NOT set org/project IDs at client level // as they would override per-request parameters const clientOptions: any = { apiKey: mem0ApiKey, // Disable debug logs in the client if possible debug: false, verbose: false, silent: true }; // NOTE: We intentionally do NOT set organizationId/projectId at client level // because client-level settings override per-request parameters, preventing // environment variable fallbacks and tool parameter overrides from working this.cloudClient = new MemoryClient(clientOptions); console.error("Cloud client initialized successfully with options:", { hasApiKey: !!mem0ApiKey, hasDefaultAppId: !!defaultAppId, hasDefaultAgentId: !!defaultAgentId }); this.isReady = true; } catch (error) { console.error("Error in cloud client initialization:", error); } }).catch(error => { console.error("Error initializing cloud client:", error); process.exit(1); }); } else if (supabaseUrl && supabaseKey) { console.error("Using Supabase storage mode with SUPABASE_URL and SUPABASE_KEY"); this.isSupabaseMode = true; try { // Initialize Supabase client with vector store and history store // Using exact configuration format from mem0 docs const supabaseConfig = { vectorStore: { provider: "supabase", config: { collectionName: "memories", embeddingModelDims: 1536, supabaseUrl: supabaseUrl, supabaseKey: supabaseKey, tableName: "memories", }, }, historyStore: { provider: 'supabase', config: { supabaseUrl: supabaseUrl, supabaseKey: supabaseKey, tableName: 'memory_history', }, }, // Embedder configuration for OpenAI embedder: { provider: 'openai', config: { apiKey: process.env.OPENAI_API_KEY, model: 'text-embedding-3-small', }, }, }; this.supabaseClient = new Memory(supabaseConfig); console.error("Supabase client initialized successfully"); this.isReady = true; } catch (error) { console.error("Error initializing Supabase client:", error); process.exit(1); } } else if (openaiApiKey) { console.error("Using local in-memory storage mode with OPENAI_API_KEY"); this.isCloudMode = false; try { // Initialize with silent options if available this.localClient = new Memory({ vectorStore: { provider: "memory", config: { collectionName: "mem0_default_collection" } } // Add silent options if supported by the mem0ai library // Options like debug, silent, verbose don't exist in the type but might be supported at runtime }); console.error("Local client initialized successfully"); this.isReady = true; } catch (error) { console.error("Error initializing local client:", error); process.exit(1); } } else { console.error("Error: One of the following must be provided:"); console.error(" - MEM0_API_KEY (for Mem0 cloud storage)"); console.error(" - SUPABASE_URL + SUPABASE_KEY (for Supabase storage)"); console.error(" - OPENAI_API_KEY (for local in-memory storage)"); process.exit(1); } process.on('SIGINT', async () => { console.error("Received SIGINT signal, shutting down..."); // Restore original console.log before exit safeLogger.restore(); await this.server.close(); process.exit(0); }); process.on('SIGTERM', async () => { console.error("Received SIGTERM signal, shutting down..."); // Restore original console.log before exit safeLogger.restore(); await this.server.close(); process.exit(0); }); // Cleanup on uncaught exceptions process.on('uncaughtException', (error) => { console.error("Uncaught exception:", error); // Restore original console.log before exit safeLogger.restore(); process.exit(1); }); } /** * Sets up handlers for MCP tool-related requests. */ private setupToolHandlers(): void { // Handler for listing available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "add_memory", description: "Stores a piece of text as a memory in Mem0.", inputSchema: { type: "object", properties: { content: { type: "string", description: "The text content to store as memory.", }, userId: { type: "string", description: "User ID to associate with the memory. If not provided, uses DEFAULT_USER_ID environment variable.", }, sessionId: { type: "string", description: "Optional session ID to associate with the memory.", }, agentId: { type: "string", description: "Optional agent ID - identifies the LLM/agent making the tool call. If not provided, uses DEFAULT_AGENT_ID environment variable.", }, appId: { type: "string", description: "Optional app ID - application identifier (legacy parameter). If not provided, uses DEFAULT_APP_ID environment variable.", }, projectId: { type: "string", description: "Optional project ID - for mem0 Pro plan project organization (e.g., proj_ABC123). If not provided, uses DEFAULT_PROJECT_ID environment variable.", }, orgId: { type: "string", description: "Optional organization ID - for mem0 organization-level management. If not provided, uses DEFAULT_ORG_ID environment variable.", }, metadata: { type: "object", description: "Optional key-value metadata.", }, includes: { type: "string", description: "Optional specific preferences to include in the memory (for cloud API).", }, excludes: { type: "string", description: "Optional specific preferences to exclude from the memory (for cloud API).", }, infer: { type: "boolean", description: "Optional whether to infer memories or directly store messages (default: true, for cloud API).", }, outputFormat: { type: "string", description: "Optional format version, either v1.0 (deprecated) or v1.1 (recommended, for cloud API).", }, customCategories: { type: "object", description: "Optional list of categories with names and descriptions (for cloud API).", }, customInstructions: { type: "string", description: "Optional project-specific guidelines for handling and organizing memories (for cloud API).", }, immutable: { type: "boolean", description: "Optional whether the memory is immutable (default: false, for cloud API).", }, expirationDate: { type: "string", description: "Optional when the memory will expire (format: YYYY-MM-DD, for cloud API).", }, }, required: ["content"], }, }, { name: "search_memory", description: "Searches stored memories in Mem0 based on a query.", inputSchema: { type: "object", properties: { query: { type: "string", description: "The search query.", }, userId: { type: "string", description: "User ID to filter search. If not provided, uses DEFAULT_USER_ID environment variable.", }, sessionId: { type: "string", description: "Optional session ID to filter search.", }, agentId: { type: "string", description: "Optional agent ID - identifies the LLM/agent making the tool call. If not provided, uses DEFAULT_AGENT_ID environment variable.", }, appId: { type: "string", description: "Optional app ID - application identifier (legacy parameter). If not provided, uses DEFAULT_APP_ID environment variable.", }, projectId: { type: "string", description: "Optional project ID - for mem0 Pro plan project organization (e.g., proj_ABC123). If not provided, uses DEFAULT_PROJECT_ID environment variable.", }, orgId: { type: "string", description: "Optional organization ID - for mem0 organization-level management. If not provided, uses DEFAULT_ORG_ID environment variable.", }, filters: { type: "object", description: "Optional key-value filters for metadata.", }, threshold: { type: "number", description: "Optional similarity threshold for results (for cloud API).", }, topK: { type: "number", description: "Optional number of top results to return (default: 10, for cloud API).", }, fields: { type: "array", items: { type: "string" }, description: "Optional specific fields to include in the response (for cloud API).", }, rerank: { type: "boolean", description: "Optional whether to rerank the memories (default: false, for cloud API).", }, keywordSearch: { type: "boolean", description: "Optional whether to search based on keywords (default: false, for cloud API).", }, filterMemories: { type: "boolean", description: "Optional whether to filter the memories (default: false, for cloud API).", }, }, required: ["query"], }, }, { name: "delete_memory", description: "Deletes a specific memory by ID from Mem0.", inputSchema: { type: "object", properties: { memoryId: { type: "string", description: "The unique ID of the memory to delete.", }, userId: { type: "string", description: "User ID associated with the memory. If not provided, uses DEFAULT_USER_ID environment variable.", }, agentId: { type: "string", description: "Optional agent ID - identifies the LLM/agent making the tool call. If not provided, uses DEFAULT_AGENT_ID environment variable.", }, appId: { type: "string", description: "Optional app ID - application identifier (legacy parameter). If not provided, uses DEFAULT_APP_ID environment variable.", }, projectId: { type: "string", description: "Optional project ID - for mem0 Pro plan project organization (e.g., proj_ABC123). If not provided, uses DEFAULT_PROJECT_ID environment variable.", }, orgId: { type: "string", description: "Optional organization ID - for mem0 organization-level management. If not provided, uses DEFAULT_ORG_ID environment variable.", }, }, required: ["memoryId"], }, }, ], }; }); // Handler for call tool requests this.server.setRequestHandler(CallToolRequestSchema, async (request) => { if (!this.isReady) { throw new McpError(ErrorCode.InternalError, "Memory client is still initializing. Please try again in a moment."); } try { const { name } = request.params; const args = request.params.arguments || {}; if (name === "add_memory") { const toolArgs = args as unknown as Mem0AddToolArgs; return await this.handleAddMemory(toolArgs); } else if (name === "search_memory") { const toolArgs = args as unknown as Mem0SearchToolArgs; return await this.handleSearchMemory(toolArgs); } else if (name === "delete_memory") { const toolArgs = args as unknown as Mem0DeleteToolArgs; return await this.handleDeleteMemory(toolArgs); } else { throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } } catch (error: any) { if (error instanceof McpError) { throw error; } console.error(`Error executing tool:`, error); throw new McpError(ErrorCode.InternalError, `Error executing tool: ${error.message || 'Unknown error'}`); } }); } /** * Handles adding a memory using either local or cloud client. */ private async handleAddMemory(args: Mem0AddToolArgs): Promise<any> { const { content, userId, sessionId, agentId, appId, projectId, orgId, metadata, includes, excludes, infer, outputFormat, customCategories, customInstructions, immutable, expirationDate } = args; if (!content) { throw new McpError(ErrorCode.InvalidParams, "Missing required argument: content"); } // Use DEFAULT_USER_ID as fallback if userId is not provided const finalUserId = userId || process.env.DEFAULT_USER_ID; if (!finalUserId) { throw new McpError(ErrorCode.InvalidParams, "Missing required argument: userId (and no DEFAULT_USER_ID environment variable set)"); } console.error(`Adding memory for user ${finalUserId}`); if (this.isCloudMode && this.cloudClient) { try { // Get all parameters - parameter takes precedence over environment const finalAppId = appId || process.env.DEFAULT_APP_ID; const finalAgentId = agentId || process.env.DEFAULT_AGENT_ID; const finalProjectId = projectId || process.env.DEFAULT_PROJECT_ID; const finalOrgId = orgId || process.env.DEFAULT_ORG_ID; console.error(`Parameter resolution:`); console.error(` Input: agentId=${agentId}, appId=${appId}, projectId=${projectId}, orgId=${orgId}`); console.error(` Environment: DEFAULT_AGENT_ID=${process.env.DEFAULT_AGENT_ID}, DEFAULT_APP_ID=${process.env.DEFAULT_APP_ID}, DEFAULT_PROJECT_ID=${process.env.DEFAULT_PROJECT_ID}, DEFAULT_ORG_ID=${process.env.DEFAULT_ORG_ID}`); console.error(` Final: finalAgentId=${finalAgentId}, finalAppId=${finalAppId}, finalProjectId=${finalProjectId}, finalOrgId=${finalOrgId}`); // Format message for the cloud API const messages: Mem0Message[] = [{ role: "user", content }]; // Cloud API options - using snake_case for API parameters // Note: Mem0 docs recommend version="v2" for add operations (v1 is deprecated) const options: any = { user_id: finalUserId, version: "v2" }; // Add all parameters if available (using snake_case for API) if (finalAppId) options.app_id = finalAppId; if (finalAgentId) options.agent_id = finalAgentId; if (finalProjectId) options.project_id = finalProjectId; if (finalOrgId) options.org_id = finalOrgId; // Map sessionId to run_id (using snake_case) if (sessionId) options.run_id = sessionId; if (metadata) options.metadata = metadata; console.error(`API call options:`, JSON.stringify(options, null, 2)); // Add advanced Mem0 API parameters (using snake_case) if (includes) options.includes = includes; if (excludes) options.excludes = excludes; if (infer !== undefined) options.infer = infer; if (outputFormat) options.output_format = outputFormat; if (customCategories) options.custom_categories = customCategories; if (customInstructions) options.custom_instructions = customInstructions; if (immutable !== undefined) options.immutable = immutable; if (expirationDate) options.expiration_date = expirationDate; // API call - try direct REST API approach first for better parameter support let result; let usedDirectAPI = false; // Always try direct REST API first when app_id or run_id are provided if (finalAppId || sessionId) { console.error("Using direct REST API due to app_id or run_id parameters"); try { const apiUrl = 'https://api.mem0.ai/v1/memories/'; const requestBody = { messages: messages, ...options }; console.error("Making direct API call with body:", JSON.stringify(requestBody, null, 2)); const response = await fetch(apiUrl, { method: 'POST', headers: { 'Authorization': `Token ${process.env.MEM0_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(requestBody) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Direct API call failed: ${response.status} ${response.statusText} - ${errorText}`); } result = await response.json(); usedDirectAPI = true; console.error("Memory added successfully using direct REST API"); } catch (directError: any) { console.error("Direct API call failed, falling back to SDK:", directError.message); // Fall through to SDK attempt } } // Try SDK if direct API wasn't used or failed if (!usedDirectAPI) { try { result = await this.cloudClient.add(messages, options); console.error("Memory added successfully using cloud API SDK"); } catch (sdkError: any) { console.error("SDK method failed:", sdkError.message); throw sdkError; } } return { content: [{ type: "text", text: `Memory added successfully. Result: ${JSON.stringify(result)}` }], }; } catch (error: any) { console.error("Error adding memory using cloud API:", error); throw new McpError(ErrorCode.InternalError, `Error adding memory: ${error.message}`); } } else if (this.isSupabaseMode && this.supabaseClient) { try { // Format message for the Supabase storage API const messages: Mem0Message[] = [{ role: "user", content }]; // Supabase storage options - using camelCase for local SDK const options: any = { userId: finalUserId, sessionId, metadata }; // Add all parameters if available const finalAppId = appId || process.env.DEFAULT_APP_ID; const finalAgentId = agentId || process.env.DEFAULT_AGENT_ID; const finalProjectId = projectId || process.env.DEFAULT_PROJECT_ID; const finalOrgId = orgId || process.env.DEFAULT_ORG_ID; if (finalAppId) options.appId = finalAppId; if (finalAgentId) options.agentId = finalAgentId; if (finalProjectId) options.projectId = finalProjectId; if (finalOrgId) options.orgId = finalOrgId; console.error(`Adding memory to Supabase for user ${finalUserId}`); // API call const result = await this.supabaseClient.add(messages, options); console.error("Memory added successfully using Supabase storage"); return { content: [{ type: "text", text: `Memory added successfully. Result: ${JSON.stringify(result)}` }], }; } catch (error: any) { console.error("Error adding memory using Supabase storage:", error); throw new McpError(ErrorCode.InternalError, `Error adding memory: ${error.message}`); } } else if (this.localClient) { try { // Format message for the local storage API const messages: Mem0Message[] = [{ role: "user", content }]; // Local storage options - using camelCase const options: any = { userId: finalUserId, sessionId, metadata }; // API call const result = await this.localClient.add(messages, options); console.error("Memory added successfully using local storage"); return { content: [{ type: "text", text: `Memory added successfully. Result: ${JSON.stringify(result)}` }], }; } catch (error: any) { console.error("Error adding memory using local storage:", error); throw new McpError(ErrorCode.InternalError, `Error adding memory: ${error.message}`); } } else { throw new McpError(ErrorCode.InternalError, "No memory client is available"); } } /** * Handles searching memories using either local or cloud client. */ private async handleSearchMemory(args: Mem0SearchToolArgs): Promise<any> { const { query, userId, sessionId, agentId, appId, projectId, orgId, filters, threshold, topK, fields, rerank, keywordSearch, filterMemories } = args; if (!query) { throw new McpError(ErrorCode.InvalidParams, "Missing required argument: query"); } // Use DEFAULT_USER_ID as fallback if userId is not provided const finalUserId = userId || process.env.DEFAULT_USER_ID; if (!finalUserId) { throw new McpError(ErrorCode.InvalidParams, "Missing required argument: userId (and no DEFAULT_USER_ID environment variable set)"); } console.error(`Searching memories for query "${query}" and user ${finalUserId}`); if (this.isCloudMode && this.cloudClient) { try { // Get all parameters - parameter takes precedence over environment const finalAppId = appId || process.env.DEFAULT_APP_ID; const finalAgentId = agentId || process.env.DEFAULT_AGENT_ID; const finalProjectId = projectId || process.env.DEFAULT_PROJECT_ID; const finalOrgId = orgId || process.env.DEFAULT_ORG_ID; // Cloud API options - using snake_case for API parameters // Note: Search operations don't use version parameter (only for add operations) const options: any = { user_id: finalUserId }; // Add all parameters if available (using snake_case) if (finalAppId) options.app_id = finalAppId; if (finalAgentId) options.agent_id = finalAgentId; if (finalProjectId) options.project_id = finalProjectId; if (finalOrgId) options.org_id = finalOrgId; // Map sessionId to run_id and other parameters (using snake_case) if (sessionId) options.run_id = sessionId; if (filters) options.filters = filters; // Only add threshold if it's a valid number (not null or undefined) if (threshold !== undefined && threshold !== null) { options.threshold = threshold; } // Don't set a default threshold - let the API use its own defaults // Add advanced search parameters (using snake_case) if (topK !== undefined) options.top_k = topK; if (fields) options.fields = fields; if (rerank !== undefined) options.rerank = rerank; if (keywordSearch !== undefined) options.keyword_search = keywordSearch; if (filterMemories !== undefined) options.filter_memories = filterMemories; // API call - try direct REST API approach first for better parameter support let results; let usedDirectAPI = false; // Always try direct REST API first when app_id or run_id are provided if (finalAppId || sessionId) { console.error("Using direct REST API for search due to app_id or run_id parameters"); try { const apiUrl = 'https://api.mem0.ai/v1/memories/search'; const requestBody = { query: query, ...options }; console.error("Making direct search API call with body:", JSON.stringify(requestBody, null, 2)); const response = await fetch(apiUrl, { method: 'POST', headers: { 'Authorization': `Token ${process.env.MEM0_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(requestBody) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Direct search API call failed: ${response.status} ${response.statusText} - ${errorText}`); } results = await response.json(); usedDirectAPI = true; console.error("Search completed successfully using direct REST API"); } catch (directError: any) { console.error("Direct search API call failed, falling back to SDK:", directError.message); // Fall through to SDK attempt } } // Try SDK if direct API wasn't used or failed if (!usedDirectAPI) { try { results = await this.cloudClient.search(query, options); console.error("Search completed successfully using cloud API SDK"); } catch (sdkError: any) { console.error("SDK search method failed:", sdkError.message); throw sdkError; } } // Handle potential array or object result const resultsArray = Array.isArray(results) ? results : [results]; console.error(`Found ${resultsArray.length} memories using cloud API`); return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }], }; } catch (error: any) { console.error("Error searching memories using cloud API:", error); throw new McpError(ErrorCode.InternalError, `Error searching memories: ${error.message}`); } } else if (this.isSupabaseMode && this.supabaseClient) { try { // Get all parameters - parameter takes precedence over environment const finalAppId = appId || process.env.DEFAULT_APP_ID; const finalAgentId = agentId || process.env.DEFAULT_AGENT_ID; const finalProjectId = projectId || process.env.DEFAULT_PROJECT_ID; const finalOrgId = orgId || process.env.DEFAULT_ORG_ID; // Supabase storage options - using camelCase for local SDK const options: any = { userId: finalUserId, sessionId, filters }; // Add all parameters if available if (finalAppId) options.appId = finalAppId; if (finalAgentId) options.agentId = finalAgentId; if (finalProjectId) options.projectId = finalProjectId; if (finalOrgId) options.orgId = finalOrgId; console.error(`Searching Supabase memories for query "${query}" and user ${finalUserId}`); // API call const results = await this.supabaseClient.search(query, options); // Handle potential array or object result const resultsArray = Array.isArray(results) ? results : [results]; console.error(`Found ${resultsArray.length} memories using Supabase storage`); return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }], }; } catch (error: any) { console.error("Error searching memories using Supabase storage:", error); throw new McpError(ErrorCode.InternalError, `Error searching memories: ${error.message}`); } } else if (this.localClient) { try { // Local storage options const options: any = { userId: finalUserId, sessionId, filters }; // API call const results = await this.localClient.search(query, options); // Handle potential array or object result const resultsArray = Array.isArray(results) ? results : [results]; console.error(`Found ${resultsArray.length} memories using local storage`); return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }], }; } catch (error: any) { console.error("Error searching memories using local storage:", error); throw new McpError(ErrorCode.InternalError, `Error searching memories: ${error.message}`); } } else { throw new McpError(ErrorCode.InternalError, "No memory client is available"); } } /** * Handles deleting a memory using either local or cloud client. */ private async handleDeleteMemory(args: Mem0DeleteToolArgs): Promise<any> { const { memoryId, userId, agentId, appId, projectId, orgId } = args; if (!memoryId) { throw new McpError(ErrorCode.InvalidParams, "Missing required argument: memoryId"); } // Use DEFAULT_USER_ID as fallback if userId is not provided const finalUserId = userId || process.env.DEFAULT_USER_ID; if (!finalUserId) { throw new McpError(ErrorCode.InvalidParams, "Missing required argument: userId (and no DEFAULT_USER_ID environment variable set)"); } console.error(`Attempting to delete memory with ID ${memoryId} for user ${finalUserId}`); if (this.isCloudMode && this.cloudClient) { try { // Get all parameters - parameter takes precedence over environment const finalAppId = appId || process.env.DEFAULT_APP_ID; const finalAgentId = agentId || process.env.DEFAULT_AGENT_ID; const finalProjectId = projectId || process.env.DEFAULT_PROJECT_ID; const finalOrgId = orgId || process.env.DEFAULT_ORG_ID; // Cloud API options - using snake_case for API parameters // Note: Delete memory uses v1 API, no version parameter needed const options: any = { memory_id: memoryId, user_id: finalUserId }; // Add all parameters if available (using snake_case) if (finalAppId) options.app_id = finalAppId; if (finalAgentId) options.agent_id = finalAgentId; if (finalProjectId) options.project_id = finalProjectId; if (finalOrgId) options.org_id = finalOrgId; // Try to use the API's deleteMemory method through the client try { // @ts-ignore - We'll try to access this method even if TypeScript doesn't recognize it await this.cloudClient.deleteMemory(memoryId); console.error(`Memory ${memoryId} deleted successfully using cloud API's deleteMemory`); } catch (innerError) { // If that fails, try to use a generic request method console.error("Using fallback delete method for cloud API"); await fetch(`https://api.mem0.ai/v1/memories/${memoryId}/`, { method: 'DELETE', headers: { 'Authorization': `Token ${process.env.MEM0_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(options) }); console.error(`Memory ${memoryId} deleted successfully using direct API request`); } return { content: [{ type: "text", text: `Memory ${memoryId} deleted successfully` }], }; } catch (error: any) { console.error("Error deleting memory using cloud API:", error); throw new McpError(ErrorCode.InternalError, `Error deleting memory: ${error.message}`); } } else if (this.isSupabaseMode && this.supabaseClient) { try { // For Supabase storage, try to use the deleteMemory method try { // @ts-ignore - We'll try to access this method even if TypeScript doesn't recognize it await this.supabaseClient.deleteMemory(memoryId); console.error(`Memory ${memoryId} deleted successfully using Supabase storage deleteMemory`); } catch (innerError) { // If direct method fails, try to access through any internal methods console.error("Using fallback delete method for Supabase storage"); // @ts-ignore - Accessing potentially private properties if (this.supabaseClient._vectorstore && typeof this.supabaseClient._vectorstore.delete === 'function') { // @ts-ignore await this.supabaseClient._vectorstore.delete({ ids: [memoryId] }); console.error(`Memory ${memoryId} deleted successfully using Supabase vectorstore delete`); } else { throw new Error("Supabase client does not support memory deletion"); } } return { content: [{ type: "text", text: `Memory ${memoryId} deleted successfully` }], }; } catch (error: any) { console.error("Error deleting memory using Supabase storage:", error); throw new McpError(ErrorCode.InternalError, `Error deleting memory: ${error.message || "Supabase client does not support memory deletion"}`); } } else if (this.localClient) { try { // For local storage, we need to find a way to delete the memory // Since we don't have direct access to deleteMemory, we'll try to access it indirectly try { // @ts-ignore - We'll try to access this method even if TypeScript doesn't recognize it await this.localClient.deleteMemory(memoryId); console.error(`Memory ${memoryId} deleted successfully using local storage deleteMemory`); } catch (innerError) { // If direct method fails, try to access through any internal methods console.error("Using fallback delete method for local storage"); // @ts-ignore - Accessing potentially private properties if (this.localClient._vectorstore && typeof this.localClient._vectorstore.delete === 'function') { // @ts-ignore await this.localClient._vectorstore.delete({ ids: [memoryId] }); console.error(`Memory ${memoryId} deleted successfully using vectorstore delete`); } else { throw new Error("Local client does not support memory deletion"); } } return { content: [{ type: "text", text: `Memory ${memoryId} deleted successfully` }], }; } catch (error: any) { console.error("Error deleting memory using local storage:", error); throw new McpError(ErrorCode.InternalError, `Error deleting memory: ${error.message || "Local client does not support memory deletion"}`); } } else { throw new McpError(ErrorCode.InternalError, "No memory client is available"); } } /** * Starts the MCP server. */ public async start(): Promise<void> { console.error("Starting Mem0 MCP Server..."); const transport = new StdioServerTransport(); await this.server.connect(transport); console.error("Mem0 MCP Server is running."); } } // Start the server const server = new Mem0MCPServer(); server.start().catch((error) => { console.error("Failed to start server:", error); // Restore original console.log before exit safeLogger.restore(); process.exit(1); });

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/pinkpixel-dev/mem0-mcp'

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