search_files
Search for files and directories recursively by matching a pattern, starting from a specified path. Returns full paths to matches, supporting case-insensitive and partial name searches. Automatically excludes unwanted items and operates within secure directory boundaries.
Instructions
Recursively search for files and directories matching a pattern. Searches through all subdirectories from the starting path. The search is case-insensitive and matches partial names. Returns full paths to all matching items. Great for finding files when you don't know their exact location. Only searches within allowed directories.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| excludePatterns | No | Patterns to exclude from search results | |
| path | Yes | Root path to start searching from | |
| pattern | Yes | Pattern to match against filenames and directories |
Implementation Reference
- src/index.ts:606-680 (handler)Main execution logic for the 'search_files' tool. Validates input using SearchFilesArgsSchema, performs recursive directory traversal starting from the given path, matches filenames against the pattern (case-insensitive substring match), skips excluded patterns using minimatch, collects full paths of matches, and returns formatted results.case 'search_files': { const parsed = SearchFilesArgsSchema.safeParse(a) if (!parsed.success) { throw new FileSystemError(`Invalid arguments for ${name}`, 'INVALID_ARGS', undefined, { errors: parsed.error.format(), }) } const validPath = await validatePath(parsed.data.path, config) const patternLower = parsed.data.pattern.toLowerCase() const results: string[] = [] async function search(currentPath: string) { try { const entries = await fs.readdir(currentPath, { withFileTypes: true }) for (const entry of entries) { const fullPath = path.join(currentPath, entry.name) try { await validatePath(fullPath, config) const relativePath = path.relative(validPath, fullPath) // Check if the path should be excluded const shouldExclude = parsed.data && parsed.data.excludePatterns.some((excludePattern) => { const globPattern = excludePattern.includes('*') ? excludePattern : `**/${excludePattern}**` return minimatch(relativePath, globPattern, { nocase: true }) }) if (shouldExclude) { continue } // Check if the name matches the search pattern if (entry.name.toLowerCase().includes(patternLower)) { results.push(fullPath) } // Recursively search subdirectories if (entry.isDirectory()) { await search(fullPath) } } catch (error) { // Skip paths we can't access or validate continue } } } catch (error) { // Skip directories we can't read return } } await search(validPath) await logger.debug(`Search complete: ${parsed.data.pattern}`, { resultCount: results.length, }) endMetric() return { content: [ { type: 'text', text: results.length > 0 ? `Found ${results.length} matches:\n${results.join('\n')}` : 'No matches found', }, ], } }
- src/index.ts:133-141 (schema)Zod schema defining the input arguments for the search_files tool: root path to search from, search pattern (case-insensitive substring), and optional array of exclude patterns.const SearchFilesArgsSchema = z.object({ path: z.string().describe('Root path to start searching from'), pattern: z.string().describe('Pattern to match against filenames and directories'), excludePatterns: z .array(z.string()) .optional() .default([]) .describe('Patterns to exclude from search results'), })
- src/index.ts:307-314 (registration)Tool registration in the ListTools response, providing name, description, and input schema reference for search_files.name: 'search_files', description: 'Recursively search for files and directories matching a pattern. ' + 'Searches through all subdirectories from the starting path. The search ' + 'is case-insensitive and matches partial names. Returns full paths to all ' + "matching items. Great for finding files when you don't know their exact location. " + 'Only searches within allowed directories.', inputSchema: zodToJsonSchema(SearchFilesArgsSchema) as ToolInput,