From 07875ad1a1ae37574808802fc5ddf26864dd6241 Mon Sep 17 00:00:00 2001 From: sohamd22 <85427822+sohamd22@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:59:08 +0000 Subject: [PATCH] update forget implementation in mcp (#773) ### TL;DR Enhanced the `forgetMemory` method to try exact content matching first, then fall back to semantic search with a high similarity threshold for more precise memory deletion. ### What changed? The `forgetMemory` method now uses a two-step approach: first attempting exact content matching via the API, and if that fails with a 404, falling back to semantic search with a similarity threshold of 0.85. The search method also accepts an optional threshold parameter. Error messages now distinguish between exact matches and semantic matches, including similarity scores in the response. ### How to test? 1. Call `forgetMemory` with the exact content of an existing memory to verify direct deletion 2. Call `forgetMemory` with similar but not identical content to test the semantic search fallback 3. Call `forgetMemory` with completely unrelated content to verify the "no matching memory found" response 4. Verify that success messages indicate whether deletion used exact matching or semantic matching with similarity scores ### Why make this change? This approach provides more precise memory deletion by prioritizing exact matches while still offering a fallback for similar content. The high similarity threshold (0.85) ensures that only very similar memories are deleted when exact matches aren't found, reducing the risk of accidentally deleting unrelated memories. --- apps/mcp/src/client.ts | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/apps/mcp/src/client.ts b/apps/mcp/src/client.ts index b008b0f3..df185f3f 100644 --- a/apps/mcp/src/client.ts +++ b/apps/mcp/src/client.ts @@ -154,18 +154,39 @@ export class SupermemoryClient { } } - // Delete/forget memory by searching first + // Delete/forget memory - try exact match first, then semantic search async forgetMemory( content: string, ): Promise<{ success: boolean; message: string; containerTag: string }> { try { - // First search for the memory - const searchResult = await this.search(content, 5) + // Try exact content matching first + try { + const result = await this.client.memories.forget({ + content: content, + containerTag: this.containerTag, + }) + + return { + success: true, + message: `Successfully forgot memory (exact match) with ID: ${result.id}`, + containerTag: this.containerTag, + } + } catch (error: any) { + // If not 404, it's a real error - re-throw it + if (error?.status !== 404) { + throw error + } + // Otherwise continue to semantic search fallback + } + + // Fallback to semantic search if exact match fails + const SIMILARITY_THRESHOLD = 0.85 // High threshold - only very similar memories + const searchResult = await this.search(content, 5, SIMILARITY_THRESHOLD) if (searchResult.results.length === 0) { return { success: false, - message: "No matching memory found to forget.", + message: `No matching memory found to forget. Tried exact match and semantic search with similarity threshold ${SIMILARITY_THRESHOLD}.`, containerTag: this.containerTag, } } @@ -176,10 +197,12 @@ export class SupermemoryClient { return { success: false, message: - "No matching memory found to forget (only document chunks matched).", + "No matching memory found to forget (only document chunks matched in semantic search).", containerTag: this.containerTag, } } + + // Delete using the ID from semantic search await this.client.memories.forget({ id: memoryToDelete.id, containerTag: this.containerTag, @@ -189,7 +212,7 @@ export class SupermemoryClient { getMemoryText(memoryToDelete) || memoryToDelete.content || "" return { success: true, - message: `Forgot: "${limitByChars(memoryText, 100)}"`, + message: `Forgot similar memory (semantic match, similarity: ${memoryToDelete.similarity.toFixed(2)}): "${limitByChars(memoryText, 100)}"`, containerTag: this.containerTag, } } catch (error) { @@ -198,13 +221,14 @@ export class SupermemoryClient { } // Search memories using SDK - async search(query: string, limit = 10): Promise { + async search(query: string, limit = 10, threshold?: number): Promise { try { const result = await this.client.search.memories({ q: query, limit, containerTag: this.containerTag, searchMode: "hybrid", + threshold, // Optional threshold parameter }) // Normalize and limit response size — preserve memory vs chunk distinction