Skip to main content
Glama
lofcz

MCP Smart Filesystem Server

by lofcz

find_files

Search for files by name using pattern matching with wildcards, character matching, and multiple extensions to quickly locate specific files in your filesystem.

Instructions

Find files by name using fast pattern matching.

PATTERN EXAMPLES:

  • Exact name: "config.json"

  • Wildcard: "*.config" or "Handler"

  • Multiple extensions: "*.{ts,tsx,js}"

TIPS:

  • Use * for any characters

  • Use ? for single character

  • Use {a,b,c} for alternatives

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
patternYesFilename pattern. Examples: 'Component.tsx', '*.json', '*Handler*', '*.{ts,tsx}'
pathNoLimit search to specific directory

Implementation Reference

  • Core implementation of the find_files tool. Uses ripgrep --files to list all files in allowed directories, then filters by custom glob pattern matching using matchPattern.
    export async function ripgrepFindFiles( pattern: string, searchPath: string | undefined, allowedDirs: string[] ): Promise<string[]> { const args = ['--files', '--no-config']; let searchPaths: string[]; if (searchPath) { try { const validated = await validatePath(path.join(allowedDirs[0], searchPath)); searchPaths = [validated]; } catch (error) { throw new Error(`Invalid search path: ${error instanceof Error ? error.message : String(error)}`); } } else { searchPaths = allowedDirs; } args.push(...searchPaths); return new Promise((resolve, reject) => { const rg = spawn('rg', args); let stdout = ''; let stderr = ''; rg.stdout.on('data', (data) => stdout += data); rg.stderr.on('data', (data) => stderr += data); rg.on('close', (code) => { if (code === 0 || code === 1) { const allFiles = stdout.trim().split('\n').filter(f => f); // Filter by pattern (simple glob matching) const matching = allFiles.filter(file => { const filename = path.basename(file); return matchPattern(filename, pattern); }); resolve(matching); } else { reject(new Error(`ripgrep failed: ${stderr}`)); } }); rg.on('error', (error) => { reject(new Error(`Failed to spawn ripgrep: ${error.message}`)); }); }); }
  • index.ts:207-234 (registration)
    Registration of the 'find_files' tool in the tools array, including name, description, and input schema.
    { name: 'find_files', description: `Find files by name using fast pattern matching. PATTERN EXAMPLES: - Exact name: "config.json" - Wildcard: "*.config" or "*Handler*" - Multiple extensions: "*.{ts,tsx,js}" TIPS: - Use * for any characters - Use ? for single character - Use {a,b,c} for alternatives`, inputSchema: { type: 'object', properties: { pattern: { type: 'string', description: "Filename pattern. Examples: 'Component.tsx', '*.json', '*Handler*', '*.{ts,tsx}'" }, path: { type: 'string', description: 'Limit search to specific directory' } }, required: ['pattern'] } },
  • Tool dispatch handler for 'find_files' in the main CallToolRequestSchema handler. Calls ripgrepFindFiles and adds metadata like size and line counts to results.
    case 'find_files': { const schema = z.object({ pattern: z.string(), path: z.string().optional() }); const { pattern, path } = schema.parse(args); const files = await ripgrepFindFiles(pattern, path, getAllowedDirectories()); // Get metadata for each file const filesWithMetadata = await Promise.all( files.map(async (file) => { try { const stats = await getFileStats(file); let lines: number | undefined; if (stats.isFile && stats.size < 10 * 1024 * 1024) { const isBinary = await isBinaryFile(file); if (!isBinary) { try { const content = await readFileContent(file); lines = countLines(content); } catch { // Ignore } } } return { path: file, size: `${stats.size} bytes`, lines, matchType: 'match', hint: `Matches pattern '${pattern}'` }; } catch { return { path: file, matchType: 'match' }; } }) ); return { content: [{ type: 'text', text: JSON.stringify({ query: { pattern, searchedIn: path || getAllowedDirectories().join(', ') }, files: filesWithMetadata, summary: { totalFiles: filesWithMetadata.length, searchTimeMs: 0 } }, null, 2) }] };
  • Helper function used by ripgrepFindFiles to match filenames against glob patterns, converting globs to regex.
    function matchPattern(filename: string, pattern: string): boolean { // If pattern doesn't contain wildcards, add implicit wildcards for partial matching const hasWildcards = pattern.includes('*') || pattern.includes('?') || pattern.includes('{'); if (!hasWildcards) { // For patterns without wildcards, match if the pattern appears anywhere in the filename return filename.toLowerCase().includes(pattern.toLowerCase()); } // Convert glob pattern to regex const regex = new RegExp( '^' + pattern .replace(/\./g, '\\.') .replace(/\*/g, '.*') .replace(/\?/g, '.') .replace(/\{([^}]+)\}/g, (_, alternatives) => `(${alternatives.replace(/,/g, '|')})`) + '$', 'i' ); return regex.test(filename); }
  • Zod schema for input validation in the find_files handler.
    const schema = z.object({ pattern: z.string(), path: z.string().optional() });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/lofcz/mcp-filesystem-smart'

If you have feedback or need assistance with the MCP directory API, please join our Discord server