Memory Suggest Relations
localnest_memory_suggest_relationsIdentify 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
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| threshold | No | ||
| max_results | No | ||
| response_format | No | json |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| data | Yes | ||
| meta | No |
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 ) );