How the `list_directory` Tool Handles Recursive Listings with Context Overflow Protection
The list_directory tool enforces a default depth limit of 2 levels and caps nested directories at 100 items each, with explicit warnings when truncation occurs.
Recursively listing directories in an AI-powered environment requires strict safeguards. The DesktopCommanderMCP repository implements list_directory as a bounded, deterministic tool that prevents massive file trees from overwhelming model context windows while delivering useful hierarchical data.
Depth-Based Recursion Control
The tool accepts an optional depth parameter that defaults to 2 levels. This limit propagates through the internal listRecursive helper function, which terminates early when currentDepth <= 0.
In [src/tools/filesystem.ts](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/filesystem.ts#L720-L785), the recursion logic follows this pattern:
async function listRecursive(
dirPath: string,
currentDepth: number,
prefix: string = ''
): Promise<string[]> {
if (currentDepth <= 0) {
return []; // Hard stop at depth limit
}
const entries = await fs.readdir(dirPath, { withFileTypes: true });
// ... process and recurse for subdirectories with currentDepth - 1
}
This approach guarantees predictable output size regardless of how deeply nested the actual filesystem may be.
Per-Directory Item Limits (Context Overflow Protection)
The core overflow protection mechanism is MAX_NESTED_ITEMS = 100, applied to every directory except the top-level target. When a nested directory contains more than 100 entries, the tool:
- Emits exactly 100 formatted lines
- Appends a warning indicating the hidden count
- Continues recursion only on the shown items
This prevents notoriously large directories—such as node_modules/, .git/objects/, or vendor/—from saturating the response.
Warning and Visibility System
When truncation occurs, the tool inserts an explicit warning line:
[WARNING] /path/to/large-dir: 237 items hidden (showing first 100 of 337 total)
This pattern appears in the implementation at approximately line 765 of filesystem.ts. The warning serves two purposes: it keeps users informed of data completeness, and it signals to downstream AI processors that the listing is partial.
Robust Error Handling
The tool distinguishes multiple failure modes with structured prefixes:
| Error Type | Prefix | Typical Cause |
|---|---|---|
| Not found | [NOT_FOUND] |
ENOENT—path does not exist |
| Access denied | [DENIED] |
EPERM, EACCES, ETIMEDOUT—permission or timeout issues |
| General error | [ERROR] |
Unexpected fs failures |
Each error produces a single descriptive line rather than throwing, allowing the listing to continue for accessible portions of the requested tree.
Path Validation Before Recursion
Before entering any subdirectory, the tool invokes validatePath(fullPath) as implemented in the same file. Failed validation causes silent skipping of that branch, with the entry already marked appropriately. This enforces sandbox boundaries and prevents traversal outside allowed roots.
Output Format and Parser Compatibility
Every emitted line uses a machine-readable prefix convention:
[DIR] relative/path/to/directory
[FILE] relative/path/to/file.ext
This format requires no additional path manipulation by consuming code, making it ideal for both human-readable tree views and automated parsing by AI agents.
Complete Usage Examples
Default shallow listing (depth 2)
const entries = await listDirectory('./src');
// Output: ['[DIR] components', '[FILE] index.ts', '[DIR] utils', ...]
Explicit deep listing (depth 4)
const deep = await listDirectory('/home/user/projects', 4);
Triggering overflow protection
const limited = await listDirectory('/var/log', 2);
// May include: '[WARNING] /var/log/nginx: 412 items hidden (showing first 100 of 512 total)'
Handling inaccessible paths
const result = await listDirectory('/root/secret');
// Output: ['[DENIED] /root/secret — not accessible (permission denied)']
Source File References
| File | Lines | Responsibility |
|---|---|---|
[src/tools/filesystem.ts](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/filesystem.ts#L720-L785) |
720–785 | Core listDirectory implementation with overflow protection |
[src/handlers/filesystem-handlers.ts](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/handlers/filesystem-handlers.ts) |
— | Tool registration and CLI integration |
[src/utils/files/base.ts](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/files/base.ts) |
— | Notes read_file fallback to list_directory for directories |
Summary
- Hard depth limit (
default: 2) prevents unbounded recursion regardless of filesystem depth MAX_NESTED_ITEMS = 100caps per-directory output, with explicit truncation warnings- Structured error prefixes maintain flow continuity when individual paths fail
validatePathenforcement preserves sandbox boundaries at every recursion step- Machine-readable format supports both AI parsing and human tree rendering
Frequently Asked Questions
What happens if I request depth 0 or a negative number?
The tool treats depth <= 0 as a request for zero recursion, returning only the top-level directory entry itself with no children enumerated.
Why is the 100-item limit applied only to nested directories?
The top-level directory is exempt from MAX_NESTED_ITEMS to ensure the user's explicitly requested target is fully visible. Subdirectories receive the limit because they are discovered incidentally and may contain unbounded automatic-generated content.
Can the overflow limits be configured at runtime?
No. Both the default depth (2) and MAX_NESTED_ITEMS (100) are compile-time constants in the current implementation. Callers must implement additional filtering if they need different bounds.
How does the tool handle circular symlinks or mount loops?
Each recursion step validates the path before proceeding, and the depth limit provides an absolute upper bound on traversal. The tool does not maintain a visited-set for symlink detection, relying instead on depth exhaustion to prevent infinite loops.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →