Skip to main content
Glama

list_dir

Scan directories to list files and subfolders, optionally with recursive searching and ignored file filtering.

Instructions

Lists files and directories in the given directory (optionally with recursion). Returns a JSON object with the names of directories and files within the given directory.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
relative_pathYesThe relative path to the directory to list; pass "." to scan the project root.
recursiveYesWhether to scan subdirectories recursively.
skip_ignored_filesNoWhether to skip files and directories that are ignored.
max_answer_charsNoIf the output is longer than this number of characters, no content will be returned. -1 means the default value from the config will be used. Don't adjust unless there is really no other way to get the content required for the task.

Implementation Reference

  • The handler function for the 'list_dir' tool. It lists files and directories in a given relative path, optionally recursively, skipping ignored files, and returns a JSON object with directories and files.
    class ListDirTool(Tool):
        """
        Lists files and directories in the given directory (optionally with recursion).
        """
    
        def apply(self, relative_path: str, recursive: bool, skip_ignored_files: bool = False, max_answer_chars: int = -1) -> str:
            """
            Lists files and directories in the given directory (optionally with recursion).
    
            :param relative_path: the relative path to the directory to list; pass "." to scan the project root
            :param recursive: whether to scan subdirectories recursively
            :param skip_ignored_files: whether to skip files and directories that are ignored
            :param max_answer_chars: if the output is longer than this number of characters,
                no content will be returned. -1 means the default value from the config will be used.
                Don't adjust unless there is really no other way to get the content required for the task.
            :return: a JSON object with the names of directories and files within the given directory
            """
            # Check if the directory exists before validation
            if not self.project.relative_path_exists(relative_path):
                error_info = {
                    "error": f"Directory not found: {relative_path}",
                    "project_root": self.get_project_root(),
                    "hint": "Check if the path is correct relative to the project root",
                }
                return self._to_json(error_info)
    
            self.project.validate_relative_path(relative_path, require_not_ignored=skip_ignored_files)
    
            dirs, files = scan_directory(
                os.path.join(self.get_project_root(), relative_path),
                relative_to=self.get_project_root(),
                recursive=recursive,
                is_ignored_dir=self.project.is_ignored_path if skip_ignored_files else None,
                is_ignored_file=self.project.is_ignored_path if skip_ignored_files else None,
            )
    
            result = self._to_json({"dirs": dirs, "files": files})
            return self._limit_length(result, max_answer_chars)
  • The get_name_from_cls method derives the tool name 'list_dir' from the class name 'ListDirTool' by removing 'Tool' suffix and converting to snake_case.
    @classmethod
    def get_name_from_cls(cls) -> str:
        name = cls.__name__
        if name.endswith("Tool"):
            name = name[:-4]
        # convert to snake_case
        name = "".join(["_" + c.lower() if c.isupper() else c for c in name]).lstrip("_")
        return name
    
    def get_name(self) -> str:
        return self.get_name_from_cls()
  • ToolRegistry.__init__ automatically discovers all subclasses of Tool in serena.tools packages and registers ListDirTool as 'list_dir'.
    for cls in iter_subclasses(Tool):
        if not any(cls.__module__.startswith(pkg) for pkg in tool_packages):
            continue
        is_optional = issubclass(cls, ToolMarkerOptional)
        name = cls.get_name_from_cls()
        if name in self._tool_dict:
            raise ValueError(f"Duplicate tool name found: {name}. Tool classes must have unique names.")
        self._tool_dict[name] = RegisteredTool(tool_class=cls, is_optional=is_optional, tool_name=name)
  • Derives the input schema and description for the MCP tool from the apply method's signature and docstring using func_metadata.
    def get_apply_fn_metadata_from_cls(cls) -> FuncMetadata:
        """Get the metadata for the apply method from the class (static metadata).
        Needed for creating MCP tools in a separate process without running into serialization issues.
        """
        # First try to get from __dict__ to handle dynamic docstring changes
        if "apply" in cls.__dict__:
            apply_fn = cls.__dict__["apply"]
        else:
            # Fall back to getattr for inherited methods
            apply_fn = getattr(cls, "apply", None)
            if apply_fn is None:
                raise AttributeError(f"apply method not defined in {cls}. Did you forget to implement it?")
    
        return func_metadata(apply_fn, skip_names=["self", "cls"])
  • __init__.py imports file_tools making ListDirTool available for discovery by ToolRegistry.
    from .tools_base import *
    from .file_tools import *
    from .symbol_tools import *

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/oraios/serena'

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