list_directory
List directory contents with file metadata, respecting configured path and depth restrictions to enforce policy compliance.
Instructions
List directory entries with metadata, honoring path and depth policy.
Tool call template
Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.
{
"id": 1,
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"arguments": {
"path": "<path>"
},
"name": "list_directory"
}
}Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | ||
| ctx | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- src/tools/file_tools.py:287-344 (handler)The actual implementation of the list_directory tool handler. Lists directory entries with metadata (name, kind, size, modified time), enforces path policy (check_path_policy) and depth policy (max_directory_depth), and logs results via append_log_entry.
def list_directory(path: str, ctx: Context | None = None) -> str: """List directory entries with metadata, honoring path and depth policy.""" context_tokens = activate_runtime_context(ctx) path = str(pathlib.Path(WORKSPACE_ROOT) / path) if not os.path.isabs(path) else path try: refresh_policy_if_changed() path_check = check_path_policy(path, tool="list_directory") if path_check: result = PolicyResult(allowed=False, reason=path_check[0], decision_tier="blocked", matched_rule=path_check[1]) else: result = PolicyResult(allowed=True, reason="allowed", decision_tier="allowed", matched_rule=None) if result.allowed: if not os.path.exists(path): append_log_entry(build_log_entry("list_directory", result, path=path, error="path not found")) return f"Error: path not found: {path}" if not os.path.isdir(path): append_log_entry(build_log_entry("list_directory", result, path=path, error="not a directory")) return f"Error: '{path}' is a file, not a directory" depth = relative_depth(path) max_depth = POLICY.get("allowed", {}).get("max_directory_depth", 5) if depth > max_depth: result = PolicyResult( allowed=False, reason=f"Directory depth {depth} exceeds the policy limit of {max_depth} (allowed.max_directory_depth): '{path}'", decision_tier="blocked", matched_rule="allowed.max_directory_depth", ) append_log_entry(build_log_entry("list_directory", result, path=path)) if not result.allowed: return f"[POLICY BLOCK] {result.reason}" lines = [f"Contents of {path}:"] try: entries = sorted(os.scandir(path), key=lambda e: (e.is_file(), e.name)) except OSError as e: return f"Error reading directory: {e}" for entry in entries: try: stat = entry.stat(follow_symlinks=False) mtime = datetime.datetime.fromtimestamp(stat.st_mtime, datetime.UTC).isoformat().replace("+00:00", "Z") kind = "file" if entry.is_file(follow_symlinks=False) else "directory" size = f"{stat.st_size} bytes" if kind == "file" else "-" lines.append(f" {entry.name} [{kind}] size={size} modified={mtime}") except OSError: lines.append(f" {entry.name} [unreadable]") if len(lines) == 1: lines.append(" (empty)") return "\n".join(lines) finally: reset_runtime_context(context_tokens) - src/server.py:21-31 (registration)Registers list_directory as an MCP tool on the FastMCP server via mcp.tool()(list_directory) at line 29.
for tool in [ server_info, restore_backup, execute_command, read_file, write_file, edit_file, delete_file, list_directory, ]: mcp.tool()(tool) - src/tools/__init__.py:2-14 (registration)Re-exports list_directory from tools.file_tools and includes it in __all__.
from .file_tools import delete_file, edit_file, list_directory, read_file, write_file from .restore_tools import restore_backup __all__ = [ "server_info", "execute_command", "read_file", "write_file", "edit_file", "delete_file", "list_directory", "restore_backup", ] - src/mcp_config_manager.py:30-39 (registration)Lists 'list_directory' as one of the configured MCP tools in AIRG_MCP_TOOLS for config management.
AIRG_MCP_TOOLS = [ "server_info", "restore_backup", "execute_command", "read_file", "write_file", "edit_file", "delete_file", "list_directory", ] - tests/test_attacker_suite.py:210-225 (helper)Test that exercises list_directory to verify it returns directory contents (e.g., 'demo.txt').
def test_file_tools_read_write_list_delete_flow(self): (self.workspace / "nested").mkdir(parents=True, exist_ok=True) write_result = write_file("nested/demo.txt", "hello world") self.assertIn("Successfully wrote", write_result) read_result = read_file("nested/demo.txt") self.assertEqual(read_result, "hello world") listing = list_directory("nested") self.assertIn("demo.txt", listing) delete_result = delete_file("nested/demo.txt") self.assertIn("Successfully deleted", delete_result) def test_edit_file_flow(self): self._write("nested/edit.txt", "alpha beta gamma")