search_vault
Search across all notes in an Obsidian vault with customizable options like case sensitivity and regex. Use this tool to quickly locate specific text without plugins or REST API.
Instructions
Search for text across all notes in vault
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| options | No | ||
| query | Yes | Search query | |
| vault_path | Yes | Path to the Obsidian vault |
Implementation Reference
- src/tools/search.ts:16-85 (handler)The handler function that implements the core logic for the 'search_vault' tool, searching for a query across Markdown files in an Obsidian vault.export async function handleSearchVault( vaultManager: VaultManager, vaultPath: string, query: string, options?: { case_sensitive?: boolean; regex?: boolean; } ) { await vaultManager.validateVault(vaultPath); const files = await vaultManager.listMarkdownFiles(vaultPath); const results: SearchResult[] = []; // Prepare search pattern let searchPattern: RegExp; if (options?.regex) { searchPattern = new RegExp(query, options.case_sensitive ? 'g' : 'gi'); } else { // Escape special regex characters if not in regex mode const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); searchPattern = new RegExp(escaped, options?.case_sensitive ? 'g' : 'gi'); } for (const file of files) { try { const filePath = path.join(vaultPath, file); const content = await fs.readFile(filePath, 'utf-8'); const lines = content.split('\n'); const matches: SearchResult['matches'] = []; lines.forEach((line, index) => { if (searchPattern.test(line)) { // Get context (previous and next line) const prevLine = index > 0 ? lines[index - 1] : ''; const nextLine = index < lines.length - 1 ? lines[index + 1] : ''; matches.push({ line: index + 1, content: line.trim(), context: [prevLine.trim(), line.trim(), nextLine.trim()] .filter(l => l) .join(' ... '), }); } // Reset lastIndex for global regex searchPattern.lastIndex = 0; }); if (matches.length > 0) { results.push({ path: file, matches, matchCount: matches.length, }); } } catch (error) { logger.warn(`Could not search file ${file}:`, error); } } return { query, options, resultCount: results.length, totalMatches: results.reduce((sum, r) => sum + r.matchCount, 0), results, }; }
- src/index.ts:122-152 (schema)The tool definition including name, description, and input schema for 'search_vault' used in tool listing.{ name: 'search_vault', description: 'Search for text across all notes in vault', inputSchema: { type: 'object', properties: { vault_path: { type: 'string', description: 'Path to the Obsidian vault', }, query: { type: 'string', description: 'Search query', }, options: { type: 'object', properties: { case_sensitive: { type: 'boolean', default: false, }, regex: { type: 'boolean', default: false, }, }, }, }, required: ['vault_path', 'query'], }, },
- src/index.ts:210-220 (registration)The dispatch case in the main tool request handler that routes 'search_vault' calls to the handleSearchVault function.case 'search_vault': if (!args || typeof args !== 'object' || !('vault_path' in args) || !('query' in args)) { throw new McpError(ErrorCode.InvalidParams, 'Missing required parameters'); } result = await handleSearchVault( vaultManager, args.vault_path as string, args.query as string, args.options as any ); break;
- src/tools/search.ts:6-14 (schema)Internal TypeScript interface defining the structure of search results used by the handler.interface SearchResult { path: string; matches: Array<{ line: number; content: string; context: string; }>; matchCount: number; }