search_files
Recursively search for files and directories matching a pattern from a specified starting path. Returns full paths to matches, supporting case-insensitive and partial name searches. Ideal for locating files in allowed directories across subdirectories.
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 | ||
| path | Yes | ||
| pattern | Yes |
Input Schema (JSON Schema)
Implementation Reference
- src/filesystem/index.ts:625-633 (handler)Handler function that executes the search_files tool: validates input path, delegates to searchFilesWithValidation helper, formats results as newline-separated list or 'No matches found', and returns MCP CallToolResult.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:133-137 (schema)Zod schema for search_files tool inputs: path (search root), pattern (glob pattern to match), excludePatterns (optional array of glob patterns to exclude).const SearchFilesArgsSchema = z.object({ path: z.string(), pattern: z.string(), excludePatterns: z.array(z.string()).optional().default([]) });
- src/filesystem/index.ts:607-634 (registration)MCP server registration of 'search_files' tool, specifying title, description, input/output schemas, annotations (read-only), and references 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/lib.ts:351-392 (helper)Core recursive file search implementation using minimatch glob patterns. Validates each path, applies exclusions, collects matching full paths, traverses directories recursively.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; }