Support glob pattern in search_files tool (#745)

Co-authored-by: Adam Jones <adamj+git@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Adam Jones <adamj@anthropic.com>
This commit is contained in:
Finn Andersen 2025-08-23 09:37:53 +03:00 • committed by GitHub
commit fd886fac9c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 14 additions and 14 deletions

View file

@ -145,12 +145,12 @@ The server's directory access control follows this flow:
- Fails if destination exists - Fails if destination exists
- **search_files** - **search_files**
- Recursively search for files/directories - Recursively search for files/directories that match or do not match patterns
- Inputs: - Inputs:
- `path` (string): Starting directory - `path` (string): Starting directory
- `pattern` (string): Search pattern - `pattern` (string): Search pattern
- `excludePatterns` (string[]): Exclude any patterns. Glob formats are supported. - `excludePatterns` (string[]): Exclude any patterns.
- Case-insensitive matching - Glob-style pattern matching
- Returns full paths to matches - Returns full paths to matches
- **directory_tree** - **directory_tree**

View file

@ -316,7 +316,7 @@ describe('Lib Functions', () => {
const result = await searchFilesWithValidation( const result = await searchFilesWithValidation(
testDir, testDir,
'test', '*test*',
allowedDirs, allowedDirs,
{ excludePatterns: ['*.log', 'node_modules'] } { excludePatterns: ['*.log', 'node_modules'] }
); );
@ -346,7 +346,7 @@ describe('Lib Functions', () => {
const result = await searchFilesWithValidation( const result = await searchFilesWithValidation(
testDir, testDir,
'test', '*test*',
allowedDirs, allowedDirs,
{} {}
); );
@ -370,7 +370,7 @@ describe('Lib Functions', () => {
const result = await searchFilesWithValidation( const result = await searchFilesWithValidation(
testDir, testDir,
'test', '*test*',
allowedDirs, allowedDirs,
{ excludePatterns: ['*.backup'] } { excludePatterns: ['*.backup'] }
); );

View file

@ -277,9 +277,9 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
name: "search_files", name: "search_files",
description: description:
"Recursively search for files and directories matching a pattern. " + "Recursively search for files and directories matching a pattern. " +
"Searches through all subdirectories from the starting path. The search " + "The patterns should be glob-style patterns that match paths relative to the working directory. " +
"is case-insensitive and matches partial names. Returns full paths to all " + "Use pattern like '*.ext' to match files in current directory, and '**/*.ext' to match files in all subdirectories. " +
"matching items. Great for finding files when you don't know their exact location. " + "Returns full paths to all matching items. Great for finding files when you don't know their exact location. " +
"Only searches within allowed directories.", "Only searches within allowed directories.",
inputSchema: zodToJsonSchema(SearchFilesArgsSchema) as ToolInput, inputSchema: zodToJsonSchema(SearchFilesArgsSchema) as ToolInput,
}, },

View file

@ -367,14 +367,14 @@ export async function searchFilesWithValidation(
await validatePath(fullPath); await validatePath(fullPath);
const relativePath = path.relative(rootPath, fullPath); const relativePath = path.relative(rootPath, fullPath);
const shouldExclude = excludePatterns.some(excludePattern => { const shouldExclude = excludePatterns.some(excludePattern =>
const globPattern = excludePattern.includes('*') ? excludePattern : `**/${excludePattern}/**`; minimatch(relativePath, excludePattern, { dot: true })
return minimatch(relativePath, globPattern, { dot: true }); );
});
if (shouldExclude) continue; if (shouldExclude) continue;
if (entry.name.toLowerCase().includes(pattern.toLowerCase())) { // Use glob matching for the search pattern
if (minimatch(relativePath, pattern, { dot: true })) {
results.push(fullPath); results.push(fullPath);
} }