m9k_file_history
Search past conversations and metadata to find when specific files were discussed or modified, using indexed conversation history and file path references.
Instructions
Find past conversations that touched a specific file. Searches metadata (tool_use file_path) and text content.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| filePath | Yes | File path to search for (e.g. "src/server.ts") | |
| limit | No | Max results | |
| source | No | Filter by source type. Default: all sources. |
Implementation Reference
- src/tools/specialized.ts:32-117 (handler)Handler implementation for 'm9k_file_history' tool.
async ({ filePath, limit }) => { const basename = path.basename(filePath); // Strategy 1: LIKE on metadata_json for the file path const metadataRows = ctx.db .prepare( `SELECT c.id, c.session_id, c.idx, c.user_content, c.assistant_content, c.timestamp, c.metadata_json, s.project FROM conv_chunks c JOIN conv_sessions s ON c.session_id = s.id WHERE c.metadata_json LIKE ? AND c.deleted_at IS NULL ORDER BY c.timestamp DESC LIMIT ?`, ) .all(`%${filePath}%`, limit * 2) as Array<{ id: string; session_id: string; idx: number; user_content: string; assistant_content: string; timestamp: string; metadata_json: string; project: string; }>; // Strategy 2: FTS5 on the basename let ftsRows: typeof metadataRows = []; try { ftsRows = ctx.db .prepare( `SELECT c.id, c.session_id, c.idx, c.user_content, c.assistant_content, c.timestamp, c.metadata_json, s.project FROM conv_chunks_fts JOIN conv_chunks c ON conv_chunks_fts.rowid = c.rowid JOIN conv_sessions s ON c.session_id = s.id WHERE conv_chunks_fts MATCH ? AND c.deleted_at IS NULL ORDER BY c.timestamp DESC LIMIT ?`, ) .all(basename, limit * 2) as typeof metadataRows; } catch { // FTS5 syntax error — skip } // Dedup by chunkId, metadata results first (more precise) const seen = new Set<string>(); const combined: Array<{ chunkId: string; sessionId: string; project: string; timestamp: string; snippet: string; filePaths: string[]; }> = []; for (const row of [...metadataRows, ...ftsRows]) { if (seen.has(row.id)) continue; seen.add(row.id); let filePaths: string[] = []; try { const meta = JSON.parse(row.metadata_json) as { filePaths?: string[] }; filePaths = meta.filePaths ?? []; } catch { // ignore parse error } combined.push({ chunkId: row.id, sessionId: row.session_id, project: row.project, timestamp: row.timestamp, snippet: (row.user_content + ' ' + row.assistant_content).slice(0, 150), filePaths, }); } // Sort by timestamp DESC, take top limit combined.sort((a, b) => b.timestamp.localeCompare(a.timestamp)); return { content: [{ type: 'text' as const, text: JSON.stringify(combined.slice(0, limit)) }], }; }, - src/tools/specialized.ts:17-24 (schema)Input schema definition for the 'm9k_file_history' tool.
inputSchema: { filePath: z.string().describe('File path to search for (e.g. "src/server.ts")'), limit: z.number().int().min(1).max(50).default(10).describe('Max results'), source: z .enum(['conversations', 'git', 'files']) .optional() .describe('Filter by source type. Default: all sources.'), }, - src/tools/specialized.ts:11-31 (registration)Registration of the 'm9k_file_history' tool.
export function registerSpecializedTools(server: McpServer, ctx: ToolContext): void { server.registerTool( 'm9k_file_history', { description: 'Find past conversations that touched a specific file. Searches metadata (tool_use file_path) and text content.', inputSchema: { filePath: z.string().describe('File path to search for (e.g. "src/server.ts")'), limit: z.number().int().min(1).max(50).default(10).describe('Max results'), source: z .enum(['conversations', 'git', 'files']) .optional() .describe('Filter by source type. Default: all sources.'), }, annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false, }, },