Skip to main content
Glama
safurrier

MCP Filesystem Server

search_files

Find files and directories matching patterns with recursive search, content matching, and exclusion options to locate specific items in filesystems.

Instructions

Recursively search for files and directories matching a pattern.

Args:
    path: Starting directory
    pattern: Glob pattern to match against filenames
    recursive: Whether to search subdirectories
    exclude_patterns: Optional patterns to exclude
    content_match: Optional text to search within files
    max_results: Maximum number of results to return
    format: Output format ('text' or 'json')
    ctx: MCP context

Returns:
    Search results

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pathYes
patternYes
recursiveNo
exclude_patternsNo
content_matchNo
max_resultsNo
formatNotext

Implementation Reference

  • Registration of the 'search_files' MCP tool via @mcp.tool() decorator, including the wrapper handler function that formats results and delegates core logic to operations.search_files
    @mcp.tool()
    async def search_files(
        path: str,
        pattern: str,
        ctx: Context,
        recursive: bool = True,
        exclude_patterns: Optional[List[str]] = None,
        content_match: Optional[str] = None,
        max_results: int = 100,
        format: str = "text",
    ) -> str:
        """Recursively search for files and directories matching a pattern.
    
        Args:
            path: Starting directory
            pattern: Glob pattern to match against filenames
            recursive: Whether to search subdirectories
            exclude_patterns: Optional patterns to exclude
            content_match: Optional text to search within files
            max_results: Maximum number of results to return
            format: Output format ('text' or 'json')
            ctx: MCP context
    
        Returns:
            Search results
        """
        try:
            components = get_components()
            results = await components["operations"].search_files(
                path, pattern, recursive, exclude_patterns, content_match, max_results
            )
    
            if format.lower() == "json":
                return json.dumps(results, indent=2)
    
            # Format as text
            if not results:
                return "No matching files found"
    
            lines = []
            for item in results:
                is_dir = item.get("is_directory", False)
                type_label = "[DIR]" if is_dir else "[FILE]"
                size = f" ({item['size']:,} bytes)" if not is_dir else ""
                lines.append(f"{type_label} {item['path']}{size}")
    
            return f"Found {len(results)} matching files:\n\n" + "\n".join(lines)
        except Exception as e:
            return f"Error searching files: {str(e)}"
  • Core implementation of search_files logic in FileOperations class, handling file pattern matching, content search, and result formatting.
    async def search_files(
        self,
        root_path: Union[str, Path],
        pattern: str,
        recursive: bool = True,
        exclude_patterns: Optional[List[str]] = None,
        content_match: Optional[str] = None,
        max_results: int = 1000,
        encoding: str = "utf-8",
    ) -> List[Dict]:
        """Search for files matching pattern and/or containing text.
    
        Args:
            root_path: Starting directory for search
            pattern: Glob pattern to match against filenames
            recursive: Whether to search subdirectories
            exclude_patterns: Optional patterns to exclude
            content_match: Optional text to search within files
            max_results: Maximum number of results to return
            encoding: Text encoding for content matching
    
        Returns:
            List of matching file information
    
        Raises:
            ValueError: If root_path is outside allowed directories
        """
        # Find files matching the pattern
        matching_files = await self.validator.find_matching_files(
            root_path, pattern, recursive, exclude_patterns
        )
    
        results = []
    
        # If we don't need to match content, just return file info
        if content_match is None:
            for file_path in matching_files[:max_results]:
                try:
                    # Skip directories if pattern matched them
                    if file_path.is_dir():
                        continue
    
                    info = FileInfo(file_path)
                    results.append(info.to_dict())
                except (PermissionError, FileNotFoundError):
                    # Skip files we can't access
                    pass
    
            return results
    
        # If we need to match content, check each file
        for file_path in matching_files:
            if len(results) >= max_results:
                break
    
            try:
                # Skip directories
                if file_path.is_dir():
                    continue
    
                # Skip very large files for content matching (>10MB)
                if file_path.stat().st_size > 10_000_000:
                    continue
    
                # Check if file contains the search text
                try:
                    content = await anyio.to_thread.run_sync(
                        file_path.read_text, encoding
                    )
                    if content_match in content:
                        info = FileInfo(file_path)
                        results.append(info.to_dict())
                except UnicodeDecodeError:
                    # Skip binary files
                    pass
    
            except (PermissionError, FileNotFoundError):
                # Skip files we can't access
                pass
    
        return results
  • Helper function in PathValidator that finds files matching glob patterns within allowed directories, used by search_files.
    async def find_matching_files(
        self,
        root_path: Union[str, Path],
        pattern: str,
        recursive: bool = True,
        exclude_patterns: Optional[List[str]] = None,
    ) -> List[Path]:
        """Find files matching a pattern within allowed directories.
    
        Args:
            root_path: Starting directory for search
            pattern: Glob pattern to match against filenames
            recursive: Whether to search subdirectories
            exclude_patterns: Optional patterns to exclude
    
        Returns:
            List of matching file paths
    
        Raises:
            ValueError: If root_path is outside allowed directories
        """
        abs_path, allowed = await self.validate_path(root_path)
        if not allowed:
            raise ValueError(f"Search path outside allowed directories: {root_path}")
    
        if not abs_path.is_dir():
            raise ValueError(f"Search path is not a directory: {abs_path}")
    
        results = []
        exclude_regexes = []
    
        # Compile exclude patterns if provided
        if exclude_patterns:
            for exclude in exclude_patterns:
                try:
                    exclude_regexes.append(re.compile(exclude))
                except re.error:
                    logger.warning(f"Invalid exclude pattern: {exclude}")
    
        # Use glob for pattern matching
        glob_pattern = "**/" + pattern if recursive else pattern
        for matched_path in abs_path.glob(glob_pattern):
            # Skip if matched by exclude pattern
            path_str = str(matched_path)
            excluded = False
            for exclude_re in exclude_regexes:
                if exclude_re.search(path_str):
                    excluded = True
                    break
    
            if not excluded:
                # Verify path is still allowed (e.g., in case of symlinks)
                if self.is_path_allowed(matched_path):
                    results.append(matched_path)
    
        return results

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/safurrier/mcp-filesystem'

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