list_directory
List and organize files and directories in a specified path with clear [FILE] and [DIR] prefixes. Use absolute paths for reliable results, as relative paths may fail. Works within allowed directories for structured file navigation.
Instructions
Get a detailed listing of all files and directories in a specified path.
Use this instead of 'execute_command' with ls/dir commands.
Results distinguish between files and directories with [FILE] and [DIR] prefixes.
Only works within allowed directories.
IMPORTANT: Always use absolute paths for reliability. Paths are automatically normalized regardless of slash direction. Relative paths may fail as they depend on the current working directory. Tilde paths (~/...) might not work in all contexts. Unless the user explicitly asks for relative paths, use absolute paths.
This command can be referenced as "DC: ..." or "use Desktop Commander to ..." in your instructions.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes |
Implementation Reference
- The main handler function for the 'list_directory' tool. Parses input arguments using ListDirectoryArgsSchema, calls the listDirectory helper function, formats the results as text, and returns a ServerResult./** * Handle list_directory command */ export async function handleListDirectory(args: unknown): Promise<ServerResult> { try { const startTime = Date.now(); const parsed = ListDirectoryArgsSchema.parse(args); const entries = await listDirectory(parsed.path, parsed.depth); const duration = Date.now() - startTime; const resultText = entries.join('\n'); return { content: [{ type: "text", text: resultText }], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return createErrorResponse(errorMessage); } }
- src/tools/schemas.ts:105-108 (schema)Zod schema defining the input parameters for the list_directory tool: 'path' (required string) and 'depth' (optional number, defaults to 2). Used for validation in the handler.export const ListDirectoryArgsSchema = z.object({ path: z.string(), depth: z.number().optional().default(2), });
- src/server.ts:421-454 (registration)Tool registration in the MCP listTools handler. Defines the tool name, description, input schema reference (ListDirectoryArgsSchema), and annotations for the protocol.{ name: "list_directory", description: ` Get a detailed listing of all files and directories in a specified path. Use this instead of 'execute_command' with ls/dir commands. Results distinguish between files and directories with [FILE] and [DIR] prefixes. Supports recursive listing with the 'depth' parameter (default: 2): - depth=1: Only direct contents of the directory - depth=2: Contents plus one level of subdirectories - depth=3+: Multiple levels deep CONTEXT OVERFLOW PROTECTION: - Top-level directory shows ALL items - Nested directories are limited to 100 items maximum per directory - When a nested directory has more than 100 items, you'll see a warning like: [WARNING] node_modules: 500 items hidden (showing first 100 of 600 total) - This prevents overwhelming the context with large directories like node_modules Results show full relative paths from the root directory being listed. Example output with depth=2: [DIR] src [FILE] src/index.ts [DIR] src/tools [FILE] src/tools/filesystem.ts If a directory cannot be accessed, it will show [DENIED] instead. Only works within allowed directories. ${PATH_GUIDANCE} ${CMD_PREFIX_DESCRIPTION}`, inputSchema: zodToJsonSchema(ListDirectoryArgsSchema), annotations: {
- src/tools/filesystem.ts:940-999 (helper)Core helper function that performs the recursive directory listing. Validates path, reads directories recursively up to the specified depth, formats entries with [DIR]/[FILE] prefixes, handles access denials and item limits for performance.export async function listDirectory(dirPath: string, depth: number = 2): Promise<string[]> { const validPath = await validatePath(dirPath); const results: string[] = []; const MAX_NESTED_ITEMS = 100; // Maximum items to show per nested directory async function listRecursive(currentPath: string, currentDepth: number, relativePath: string = '', isTopLevel: boolean = true): Promise<void> { if (currentDepth <= 0) return; let entries; try { entries = await fs.readdir(currentPath, { withFileTypes: true }); } catch (error) { // If we can't read this directory (permission denied), show as denied const displayPath = relativePath || path.basename(currentPath); results.push(`[DENIED] ${displayPath}`); return; } // Apply filtering for nested directories (not top level) const totalEntries = entries.length; let entriesToShow = entries; let filteredCount = 0; if (!isTopLevel && totalEntries > MAX_NESTED_ITEMS) { entriesToShow = entries.slice(0, MAX_NESTED_ITEMS); filteredCount = totalEntries - MAX_NESTED_ITEMS; } for (const entry of entriesToShow) { const fullPath = path.join(currentPath, entry.name); const displayPath = relativePath ? path.join(relativePath, entry.name) : entry.name; // Add this entry to results results.push(`${entry.isDirectory() ? "[DIR]" : "[FILE]"} ${displayPath}`); // If it's a directory and we have depth remaining, recurse if (entry.isDirectory() && currentDepth > 1) { try { // Validate the path before recursing await validatePath(fullPath); await listRecursive(fullPath, currentDepth - 1, displayPath, false); } catch (error) { // If validation fails or we can't access it, it will be marked as denied // when we try to read it in the recursive call continue; } } } // Add warning message if items were filtered if (filteredCount > 0) { const displayPath = relativePath || path.basename(currentPath); results.push(`[WARNING] ${displayPath}: ${filteredCount} items hidden (showing first ${MAX_NESTED_ITEMS} of ${totalEntries} total)`); } } await listRecursive(validPath, depth, '', true); return results; }