Skip to main content
Glama
safurrier

MCP Filesystem Server

directory_tree

Generate a recursive tree view of files and directories with customizable depth, filtering, and output formats.

Instructions

Get a recursive tree view of files and directories.

Args:
    path: Root directory
    max_depth: Maximum recursion depth
    include_files: Whether to include files (not just directories)
    pattern: Optional glob pattern to filter entries
    exclude_patterns: Optional patterns to exclude
    format: Output format ('text' or 'json')
    ctx: MCP context

Returns:
    Formatted directory tree

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pathYes
max_depthNo
include_filesNo
patternNo
exclude_patternsNo
formatNotext

Implementation Reference

  • MCP tool handler and registration for 'directory_tree'. Dispatches to JSON or formatted output based on 'format' parameter.
    @mcp.tool()
    async def directory_tree(
        path: str,
        ctx: Context,
        max_depth: int = 3,
        include_files: bool = True,
        pattern: Optional[str] = None,
        exclude_patterns: Optional[List[str]] = None,
        format: str = "text",
    ) -> str:
        """Get a recursive tree view of files and directories.
    
        Args:
            path: Root directory
            max_depth: Maximum recursion depth
            include_files: Whether to include files (not just directories)
            pattern: Optional glob pattern to filter entries
            exclude_patterns: Optional patterns to exclude
            format: Output format ('text' or 'json')
            ctx: MCP context
    
        Returns:
            Formatted directory tree
        """
        try:
            components = get_components()
            if format.lower() == "json":
                tree = await components["advanced"].directory_tree(
                    path, max_depth, include_files, pattern, exclude_patterns
                )
                return json.dumps(tree, indent=2)
            else:
                tree_text = await components["advanced"].directory_tree_formatted(
                    path, max_depth, include_files, pattern, exclude_patterns
                )
                return tree_text
        except Exception as e:
            return f"Error creating directory tree: {str(e)}"
  • Data class for directory tree nodes, with methods to convert to dict (JSON output) and format as tree text.
    class DirectoryTreeNode:
        """Node in a directory tree."""
    
        def __init__(self, path: Path, is_dir: bool = False, depth: int = 0):
            """Initialize a tree node.
    
            Args:
                path: Path this node represents
                is_dir: Whether this is a directory
                depth: Depth in the tree (0 = root)
            """
            self.path = path
            self.name = path.name or str(path)  # Use path string for root
            self.is_dir = is_dir
            self.depth = depth
            self.children: List["DirectoryTreeNode"] = []
    
        def add_child(self, child: "DirectoryTreeNode") -> None:
            """Add a child node.
    
            Args:
                child: Child node to add
            """
            self.children.append(child)
    
        def to_dict(self) -> Dict:
            """Convert to dictionary representation.
    
            Returns:
                Dictionary representing this node and its children
            """
            result: Dict[str, Union[str, List[Dict[str, Any]]]] = {
                "name": self.name,
                "path": str(self.path),
                "type": "directory" if self.is_dir else "file",
            }
    
            if self.is_dir:
                result["children"] = [child.to_dict() for child in self.children]
    
            return result
    
        def format(self, include_files: bool = True, line_prefix: str = "") -> List[str]:
            """Format this node as text lines.
    
            Args:
                include_files: Whether to include files (not just directories)
                line_prefix: Prefix for each line (used for recursion)
    
            Returns:
                List of formatted lines
            """
            result: List[str] = []
    
            # Skip files if not requested
            if not include_files and not self.is_dir:
                return result
    
            # Format this node
            node_type = "πŸ“" if self.is_dir else "πŸ“„"
            result.append(f"{line_prefix}{node_type} {self.name}")
    
            # Format children
            if self.children:
                for i, child in enumerate(
                    sorted(self.children, key=lambda x: (not x.is_dir, x.name))
                ):
                    is_last = i == len(self.children) - 1
                    if is_last:
                        child_prefix = f"{line_prefix}└── "
                        next_prefix = f"{line_prefix}    "
                    else:
                        child_prefix = f"{line_prefix}β”œβ”€β”€ "
                        next_prefix = f"{line_prefix}β”‚   "
    
                    result.extend(child.format(include_files, child_prefix + next_prefix))
    
            return result
  • Builds the directory tree structure as a nested dict, called by handler for JSON format.
    async def directory_tree(
        self,
        root_path: Union[str, Path],
        max_depth: int = 3,
        include_files: bool = True,
        pattern: Optional[str] = None,
        exclude_patterns: Optional[List[str]] = None,
    ) -> Dict:
        """Build a directory tree structure.
    
        Args:
            root_path: Root directory for the tree
            max_depth: Maximum depth to traverse
            include_files: Whether to include files (not just directories)
            pattern: Optional glob pattern to filter entries
            exclude_patterns: Optional patterns to exclude
    
        Returns:
            Dictionary representation of the directory tree
    
        Raises:
            ValueError: If root_path is outside allowed directories
        """
        abs_path, allowed = await self.validator.validate_path(root_path)
        if not allowed:
            raise ValueError(f"Path outside allowed directories: {root_path}")
    
        if not abs_path.is_dir():
            raise ValueError(f"Not a directory: {root_path}")
    
        # Compile exclude patterns if provided
        exclude_regexes = []
        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}")
    
        # Create root node
        root_node = DirectoryTreeNode(abs_path, True, 0)
    
        # Build tree recursively
        await self._build_tree_node(
            root_node, max_depth, include_files, pattern, exclude_regexes
        )
    
        return root_node.to_dict()
  • Builds the directory tree and formats it as ASCII art text, called by handler for text format.
    async def directory_tree_formatted(
        self,
        root_path: Union[str, Path],
        max_depth: int = 3,
        include_files: bool = True,
        pattern: Optional[str] = None,
        exclude_patterns: Optional[List[str]] = None,
    ) -> str:
        """Build a formatted directory tree.
    
        Args:
            root_path: Root directory for the tree
            max_depth: Maximum depth to traverse
            include_files: Whether to include files (not just directories)
            pattern: Optional glob pattern to filter entries
            exclude_patterns: Optional patterns to exclude
    
        Returns:
            Formatted string representation of the directory tree
        """
        abs_path, allowed = await self.validator.validate_path(root_path)
        if not allowed:
            raise ValueError(f"Path outside allowed directories: {root_path}")
    
        if not abs_path.is_dir():
            raise ValueError(f"Not a directory: {root_path}")
    
        # Compile exclude patterns if provided
        exclude_regexes = []
        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}")
    
        # Create root node
        root_node = DirectoryTreeNode(abs_path, True, 0)
    
        # Build tree recursively
        await self._build_tree_node(
            root_node, max_depth, include_files, pattern, exclude_regexes
        )
    
        # Format the tree
        formatted = root_node.format(include_files)
        return "\n".join(formatted)
  • Recursive helper that traverses directories, applies filters, and builds the tree nodes.
    async def _build_tree_node(
        self,
        node: DirectoryTreeNode,
        max_depth: int,
        include_files: bool,
        pattern: Optional[str],
        exclude_regexes: List[re.Pattern],
    ) -> None:
        """Recursively build a directory tree node.
    
        Args:
            node: Current node to populate
            max_depth: Maximum depth to traverse
            include_files: Whether to include files
            pattern: Optional glob pattern to filter entries
            exclude_regexes: Compiled regular expressions to exclude
        """
        # Stop if we've reached the maximum depth
        if node.depth >= max_depth:
            return
    
        try:
            entries = await anyio.to_thread.run_sync(list, node.path.iterdir())
    
            for entry in entries:
                # Skip if matched by exclude pattern
                path_str = str(entry)
                excluded = False
                for exclude_re in exclude_regexes:
                    if exclude_re.search(path_str):
                        excluded = True
                        break
    
                if excluded:
                    continue
    
                # Apply pattern filter if specified
                if pattern and not entry.match(pattern):
                    continue
    
                try:
                    is_dir = entry.is_dir()
    
                    # Skip files if not requested
                    if not include_files and not is_dir:
                        continue
    
                    # Create and add the child node
                    child = DirectoryTreeNode(entry, is_dir, node.depth + 1)
                    node.add_child(child)
    
                    # Recursively build the tree for directories
                    if is_dir:
                        await self._build_tree_node(
                            child, max_depth, include_files, pattern, exclude_regexes
                        )
    
                except (PermissionError, FileNotFoundError):
                    # Skip entries we can't access
                    pass
    
        except (PermissionError, FileNotFoundError):
            # Skip directories we can't access
            pass

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