search_vault
Search for text across all notes in an Obsidian vault using queries with case-sensitive and regex options.
Instructions
Search for text across all notes in vault
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| vault_path | Yes | Path to the Obsidian vault | |
| query | Yes | Search query | |
| options | No |
Implementation Reference
- src/tools/search.ts:16-85 (handler)Main handler function for the 'search_vault' tool. Searches all markdown files in the vault for the given query, supports regex and case sensitivity options, returns matches with line numbers and context.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:125-151 (schema)Input schema for the search_vault tool, defining parameters: vault_path (required), query (required), and optional options for case_sensitive and regex.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:122-152 (registration)Registration of the search_vault tool in the TOOLS array used for listing available tools.{ 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)Dispatch logic in the tool call handler switch statement that invokes handleSearchVault for search_vault calls.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)Type definition for search results used internally by the handler.interface SearchResult { path: string; matches: Array<{ line: number; content: string; context: string; }>; matchCount: number; }