localnest_memory_suggest_relations
Identify potential connections between memory entries by analyzing semantic similarity, helping users discover relevant links without creating them automatically.
Instructions
Find semantically similar memory entries that could be linked to a given memory. Uses dense embeddings (all-MiniLM-L6-v2) when available, falls back to token overlap. Returns candidates ranked by similarity without creating any relations — use localnest_memory_add_relation to confirm.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| threshold | No | ||
| max_results | No | ||
| response_format | No | json |
Implementation Reference
- src/services/memory/relations.js:4-73 (handler)Implementation of the suggestRelations logic that calculates semantic similarity for memory entries.
export async function suggestRelations(adapter, memoryId, { threshold = 0.55, maxResults = 10 } = {}) { const id = String(memoryId || '').trim(); if (!id) throw new Error('id is required'); const source = await adapter.get('SELECT * FROM memory_entries WHERE id = ?', [id]); if (!source) throw new Error(`memory not found: ${id}`); let sourceEmbedding = null; if (source.embedding_json) { try { sourceEmbedding = JSON.parse(source.embedding_json); } catch { // Invalid embedding payloads fall back to token overlap scoring. } } const linked = await adapter.all( `SELECT target_id AS other_id FROM memory_relations WHERE source_id = ? UNION SELECT source_id AS other_id FROM memory_relations WHERE target_id = ?`, [id, id] ); const linkedSet = new Set(linked.map((r) => r.other_id)); linkedSet.add(id); const candidates = await adapter.all( `SELECT id, title, summary, embedding_json FROM memory_entries WHERE id != ? AND status = 'active' ORDER BY importance DESC, updated_at DESC LIMIT 200`, [id] ); const scored = []; if (sourceEmbedding) { for (const row of candidates) { if (linkedSet.has(row.id)) continue; if (!row.embedding_json) continue; try { const emb = JSON.parse(row.embedding_json); const cosine = cosineSimilarity(sourceEmbedding, emb); const score = (cosine + 1) / 2; if (score >= threshold) { scored.push({ memory_id: row.id, title: row.title, similarity: Number(score.toFixed(3)) }); } } catch { // Ignore malformed candidate embeddings. } } } else { const sourceTerms = splitTerms(`${source.title} ${source.summary}`); for (const row of candidates) { if (linkedSet.has(row.id)) continue; const rowTerms = splitTerms(`${row.title} ${row.summary}`); const score = scoreTokenOverlap(sourceTerms, rowTerms); if (score >= threshold) { scored.push({ memory_id: row.id, title: row.title, similarity: Number(score.toFixed(3)) }); } } } scored.sort((a, b) => b.similarity - a.similarity); return { id, source_title: source.title, count: scored.length, threshold, using_embeddings: sourceEmbedding !== null, suggestions: scored.slice(0, maxResults) }; } - src/mcp/tools/memory-store.js:230-252 (registration)MCP tool registration for localnest_memory_suggest_relations, which calls the suggestRelations service function.
registerJsonTool( ['localnest_memory_suggest_relations'], { title: 'Memory Suggest Relations', description: 'Find semantically similar memory entries that could be linked to a given memory. Uses dense embeddings (all-MiniLM-L6-v2) when available, falls back to token overlap. Returns candidates ranked by similarity without creating any relations — use localnest_memory_add_relation to confirm.', inputSchema: { id: z.string().min(1), threshold: z.number().min(0).max(1).default(0.55), max_results: z.number().int().min(1).max(50).default(10) }, annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false } }, async ({ id, threshold, max_results }) => normalizeMemorySuggestionResult( await memory.suggestRelations(id, { threshold, maxResults: max_results }), id, threshold ) );