search_notes
Search all notes in your Obsidian vault using full-text queries. Filter by folder, case sensitivity, and result count to find specific information quickly.
Instructions
Full-text search across all notes in the vault
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search query string | |
| caseSensitive | No | Whether the search should be case sensitive | |
| maxResults | No | Maximum number of results to return | |
| folder | No | Limit search to a specific folder |
Implementation Reference
- src/lib/vault.ts:159-222 (handler)The handler logic for searching notes within a vault.
export async function searchNotes( vaultPath: string, query: string, options?: { caseSensitive?: boolean; maxResults?: number; folder?: string; }, ): Promise<SearchResult[]> { const caseSensitive = options?.caseSensitive ?? false; const maxResults = options?.maxResults ?? 100; const notes = await listNotes(vaultPath, options?.folder); const results: SearchResult[] = []; const searchQuery = caseSensitive ? query : query.toLowerCase(); for (const notePath of notes) { if (results.length >= maxResults) break; let content: string; try { content = await readNote(vaultPath, notePath); } catch { console.error(`Failed to read note during search: ${notePath}`); continue; } const lines = content.split("\n"); const matches: SearchMatch[] = []; for (let i = 0; i < lines.length; i++) { const line = lines[i]; const compareLine = caseSensitive ? line : line.toLowerCase(); let startIndex = 0; while (true) { const col = compareLine.indexOf(searchQuery, startIndex); if (col === -1) break; matches.push({ line: i + 1, content: line.trim(), column: col, }); startIndex = col + searchQuery.length; } } if (matches.length > 0) { results.push({ path: resolveVaultPath(vaultPath, notePath), relativePath: notePath, matches, score: matches.length, }); } } // Sort by score descending results.sort((a, b) => b.score - a.score); return results.slice(0, maxResults); } - src/tools/read.ts:12-74 (registration)Tool registration for 'search_notes' and its tool call handler.
server.registerTool( "search_notes", { description: "Full-text search across all notes in the vault", inputSchema: { query: z.string().describe("Search query string"), caseSensitive: z .boolean() .optional() .default(false) .describe("Whether the search should be case sensitive"), maxResults: z .number() .optional() .default(20) .describe("Maximum number of results to return"), folder: z .string() .optional() .describe("Limit search to a specific folder"), }, }, async ({ query, caseSensitive, maxResults, folder }) => { try { const results = await searchNotes(vaultPath, query, { caseSensitive, maxResults, folder, }); if (results.length === 0) { return { content: [ { type: "text" as const, text: `No results found for "${query}"`, }, ], }; } const lines: string[] = [ `Found ${results.length} result(s) for "${query}":`, "", ]; for (const result of results) { lines.push(`## ${result.relativePath}`); for (const match of result.matches) { lines.push(` Line ${match.line}: ${match.content}`); } lines.push(""); } return { content: [{ type: "text" as const, text: lines.join("\n") }], }; } catch (err) { console.error("search_notes error:", err); return errorResult(`Error searching notes: ${err instanceof Error ? err.message : String(err)}`); } }, );