search_files
Search recursively for files and directories within allowed paths in the MCP Filesystem Server. Match partial names case-insensitively and specify exclusions to filter results. Ideal for locating files without knowing their exact location.
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)The handler function for the 'search_files' tool. Validates input using SearchFilesArgsSchema, resolves and validates the root search path, performs a recursive case-insensitive substring search on filenames using fs.readdir, applies exclusion patterns with minimatch glob matching, collects full matching paths, and returns a formatted list of results or 'No matches found'.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, search pattern (substring match), 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:306-315 (registration)Registration of the 'search_files' tool in the ListTools response, providing name, detailed description, and converted JSON schema for input validation.{ 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, },