search_memory
Search memories with full-text search, returning ranked results boosted by importance. Use at session start to load relevant context.
Instructions
Search memories using full-text search. Returns ranked results with higher-importance memories boosted. Use at session start to load relevant context.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search query (keywords or phrases) | |
| category | No | Filter by category | |
| project | No | Filter by project | |
| limit | No | Maximum results to return |
Implementation Reference
- src/tools/search.ts:5-49 (handler)The MCP tool handler that registers and implements the 'search_memory' tool. Accepts query, optional category, project, and limit; delegates to storage.search() and returns JSON results.
export function registerSearchMemory( server: McpServer, storage: RekindleStorage ): void { server.tool( "search_memory", "Search memories using full-text search. Returns ranked results with higher-importance memories boosted. Use at session start to load relevant context.", { query: z.string().describe("Search query (keywords or phrases)"), category: z .enum(["preference", "lesson", "context", "relationship", "general"]) .optional() .describe("Filter by category"), project: z.string().optional().describe("Filter by project"), limit: z .number() .min(1) .max(100) .default(10) .describe("Maximum results to return"), }, async ({ query, category, project, limit }) => { const results = storage.search(query, { category, project, limit }); return { content: [ { type: "text" as const, text: JSON.stringify({ count: results.length, results: results.map((r) => ({ id: r.id, content: r.content, category: r.category, importance: r.importance, project: r.project, created_at: r.created_at, retrieval_count: r.retrieval_count, })), }), }, ], }; } ); } - src/storage/sqlite.ts:169-228 (helper)The storage.search() method performs FTS5 full-text search with BM25 ranking, boosted by importance. Filters by category/project, updates retrieval_count and last_accessed, and returns SearchResult objects.
search( query: string, opts: { category?: string; project?: string; limit?: number } = {} ): SearchResult[] { const limit = opts.limit ?? 10; let sql = ` SELECT m.*, bm25(memories_fts) AS fts_rank FROM memories_fts fts JOIN memories m ON m.rowid = fts.rowid WHERE memories_fts MATCH ? `; const ftsQuery = query .split(/\s+/) .filter((w) => w.length > 0) .map((w) => `"${w.replace(/"/g, '""')}"`) .join(" OR "); const params: (string | number)[] = [ftsQuery]; if (opts.category) { sql += ` AND m.category = ?`; params.push(opts.category); } if (opts.project) { sql += ` AND m.project = ?`; params.push(opts.project); } sql += ` ORDER BY (fts_rank * (m.importance / 10.0)) LIMIT ?`; params.push(limit); const rows = this.db.prepare(sql).all(...params) as (Memory & { fts_rank: number })[]; const now = new Date().toISOString().replace("T", " ").slice(0, 19); const updateAccess = this.db.prepare(` UPDATE memories SET retrieval_count = retrieval_count + 1, last_accessed = ? WHERE id = ? `); const updateMany = this.db.transaction((ids: string[]) => { for (const id of ids) { updateAccess.run(now, id); } }); updateMany(rows.map((r) => r.id)); return rows.map((row) => ({ id: row.id, content: row.content, category: row.category, importance: row.importance, project: row.project, type: row.type, source: row.source, session_id: row.session_id, created_at: row.created_at, updated_at: row.updated_at, retrieval_count: row.retrieval_count + 1, last_accessed: now, rank: row.fts_rank, })); } - src/storage/sqlite.ts:33-35 (schema)SearchResult interface extending Memory with a rank field for FTS ranking.
export interface SearchResult extends Memory { rank: number; } - src/server.ts:5-5 (registration)The 'search_memory' tool is registered in createServer() by importing and calling registerSearchMemory(server, storage).
import { registerSearchMemory } from "./tools/search.js";