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
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. While it mentions recursion depth and filtering capabilities, it doesn't disclose important behavioral traits like: whether this operation is read-only or has side effects, performance characteristics for large directories, permission requirements, error handling, or what happens with symbolic links. The description provides basic functionality but lacks critical operational context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections (purpose, Args, Returns) and front-loads the core functionality. The 'Args' section could be more concise by grouping related parameters, but overall it's efficient with minimal wasted text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 6-parameter tool with no annotations and no output schema, the description provides adequate parameter documentation but lacks important contextual information. It doesn't explain the return format details (what 'Formatted directory tree' actually contains), error conditions, performance considerations, or how it differs meaningfully from simpler sibling tools like 'list_directory'.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description compensates well by explaining all 6 parameters in the 'Args' section. Each parameter gets a brief semantic explanation beyond just naming them (e.g., 'Maximum recursion depth' for max_depth, 'Optional glob pattern to filter entries' for pattern). However, it doesn't provide format details for patterns or depth constraints.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with a specific verb ('Get') and resource ('recursive tree view of files and directories'). It distinguishes from sibling tools like 'list_directory' (which likely lists without recursion) and 'calculate_directory_size' (which focuses on size calculation rather than tree structure).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for obtaining hierarchical directory views but doesn't explicitly state when to use this versus alternatives like 'list_directory' or 'search_files'. No guidance is provided about when not to use it or about performance considerations with deep recursion.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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