Search Files
search_filesRecursively search for files and directories by glob patterns. Return full paths to matching items, solving the problem of locating files in unknown directories.
Instructions
Recursively search for files and directories matching a pattern. The patterns should be glob-style patterns that match paths relative to the working directory. Use pattern like '.ext' to match files in current directory, and '**/.ext' to match files in all subdirectories. 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 |
|---|---|---|---|
| path | Yes | ||
| pattern | Yes | ||
| excludePatterns | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| content | Yes |
Implementation Reference
- src/filesystem/index.ts:626-653 (registration)Registers the 'search_files' tool on the MCP server with title, description, input/output schemas, and the handler function.
server.registerTool( "search_files", { title: "Search Files", description: "Recursively search for files and directories matching a pattern. " + "The patterns should be glob-style patterns that match paths relative to the working directory. " + "Use pattern like '*.ext' to match files in current directory, and '**/*.ext' to match files in all subdirectories. " + "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: { path: z.string(), pattern: z.string(), excludePatterns: z.array(z.string()).optional().default([]) }, outputSchema: { content: z.string() }, annotations: { readOnlyHint: true } }, async (args: z.infer<typeof SearchFilesArgsSchema>) => { const validPath = await validatePath(args.path); const results = await searchFilesWithValidation(validPath, args.pattern, allowedDirectories, { excludePatterns: args.excludePatterns }); const text = results.length > 0 ? results.join("\n") : "No matches found"; return { content: [{ type: "text" as const, text }], structuredContent: { content: text } }; } ); - src/filesystem/index.ts:644-652 (handler)The async handler function for the 'search_files' tool. Validates the path, then delegates to searchFilesWithValidation to find matching files recursively. Returns joined results or 'No matches found'.
async (args: z.infer<typeof SearchFilesArgsSchema>) => { const validPath = await validatePath(args.path); const results = await searchFilesWithValidation(validPath, args.pattern, allowedDirectories, { excludePatterns: args.excludePatterns }); const text = results.length > 0 ? results.join("\n") : "No matches found"; return { content: [{ type: "text" as const, text }], structuredContent: { content: text } }; } - src/filesystem/index.ts:152-156 (schema)Zod schema for search_files arguments: path (string), pattern (string), and optional excludePatterns (string array, default []).
const SearchFilesArgsSchema = z.object({ path: z.string(), pattern: z.string(), excludePatterns: z.array(z.string()).optional().default([]) }); - src/filesystem/lib.ts:374-415 (helper)Core implementation of searchFilesWithValidation. Recursively walks the directory tree, uses minimatch glob matching against the pattern, and respects excludePatterns and allowed directories.
export async function searchFilesWithValidation( rootPath: string, pattern: string, allowedDirectories: string[], options: SearchOptions = {} ): Promise<string[]> { const { excludePatterns = [] } = options; const results: string[] = []; async function search(currentPath: string) { const entries = await fs.readdir(currentPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(currentPath, entry.name); try { await validatePath(fullPath); const relativePath = path.relative(rootPath, fullPath); const shouldExclude = excludePatterns.some(excludePattern => minimatch(relativePath, excludePattern, { dot: true }) ); if (shouldExclude) continue; // Use glob matching for the search pattern if (minimatch(relativePath, pattern, { dot: true })) { results.push(fullPath); } if (entry.isDirectory()) { await search(fullPath); } } catch { continue; } } } await search(rootPath); return results; }