search_notes
Search Markdown notes in your Obsidian vault by specifying a query to find notes with matching content, enabling targeted retrieval of information across your notes.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes |
Implementation Reference
- src/vault/notes.ts:30-72 (handler)The core search logic: walks all markdown files, performs substring matching on path/frontmatter/content, scores results (path match=+4, frontmatter=+2, content=+1), sorts by score, and returns top 20 matches with excerpts.
export async function searchNotes(query: string) { const q = query.trim().toLowerCase(); if (!q) return []; const files = await walkMarkdownFiles(VAULT_ROOT); const matches: Array<{ path: string; score: number; excerpt: string }> = []; for (const fullPath of files) { const raw = await fs.readFile(fullPath, "utf-8"); const parsed = matter(raw); const relativePath = toRelativeVaultPath(fullPath); const haystack = `${relativePath}\n${JSON.stringify(parsed.data)}\n${parsed.content}`.toLowerCase(); const idx = haystack.indexOf(q); if (idx >= 0) { const contentLower = parsed.content.toLowerCase(); const contentIdx = contentLower.indexOf(q); const excerpt = contentIdx >= 0 ? parsed.content .slice( Math.max(0, contentIdx - 120), Math.min(parsed.content.length, contentIdx + 220), ) .trim() : ""; let score = 1; if (relativePath.toLowerCase().includes(q)) score += 4; if (JSON.stringify(parsed.data).toLowerCase().includes(q)) score += 2; if (contentIdx >= 0) score += 1; matches.push({ path: relativePath, score, excerpt }); } } return matches .sort((a, b) => b.score - a.score || a.path.localeCompare(b.path)) .slice(0, 20); } - src/tools/search-notes.ts:5-15 (handler)The MCP tool handler: registers 'search_notes' with a 'query' string input, calls the vault searchNotes function, and returns text content with JSON results.
export function register(server: McpServer): void { server.registerTool( "search_notes", { inputSchema: { query: z.string().min(1) } }, async ({ query }) => { const results = await searchNotes(query); return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }], }; }, ); - src/tools/search-notes.ts:8-8 (schema)Input schema for the search_notes tool: requires a non-empty string 'query' parameter validated via Zod.
{ inputSchema: { query: z.string().min(1) } }, - src/server.ts:14-14 (registration)Registration: calls searchNotes.register(server) to add the tool to the MCP server.
searchNotes.register(server); - src/vault/fs-utils.ts:14-14 (helper)Helper: walkMarkdownFiles recursively walks the vault directory (skipping .obsidian, .git, node_modules) and returns all .md file paths.
export async function walkMarkdownFiles(dir: string): Promise<string[]> {