ls
List directory contents including files and folders with details like name, type, size, and modification date. Configure visibility of hidden or ignored items and apply filters for specific file patterns.
Instructions
List immediate directory contents (non-recursive): name, path, type, size, modified date. Omit path for workspace root. includeIgnored=true for node_modules etc. For recursive search, use find.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | Base directory (default: root). Absolute path required if multiple roots. | |
| includeHidden | No | Include hidden items (starting with .) | |
| includeIgnored | No | Include ignored items (node_modules, .git, etc). | |
| maxDepth | No | Max recursion depth when pattern is provided | |
| maxEntries | No | Maximum entries to return before truncation. Default: 20000 | |
| sortBy | No | Sort field (name, size, modified, type) | name |
| pattern | No | Optional glob pattern filter (e.g. "**/*.ts") | |
| includeSymlinkTargets | No | Resolve and include symlink targets in results | |
| cursor | No | Pagination cursor from a previous response |
Implementation Reference
- src/tools/list-directory.ts:217-285 (handler)The main handler function for the 'ls' tool.
async function handleListDirectory( args: z.infer<typeof ListDirectoryInputSchema>, signal?: AbortSignal ): Promise<ToolResponse<z.infer<typeof ListDirectoryOutputSchema>>> { const dirPath = resolvePathOrRoot(args.path); const pageSize = args.maxEntries; const options: Parameters<typeof listDirectory>[1] = { includeHidden: args.includeHidden, excludePatterns: args.includeIgnored ? [] : DEFAULT_EXCLUDE_PATTERNS, sortBy: args.sortBy, includeSymlinkTargets: args.includeSymlinkTargets, ...(args.maxDepth !== undefined ? { maxDepth: args.maxDepth } : {}), maxEntries: MAX_LIST_ENTRIES, ...(args.pattern !== undefined ? { pattern: args.pattern } : {}), ...(signal ? { signal } : {}), }; const fingerprint = buildListFingerprint(args); let result: Awaited<ReturnType<typeof listDirectory>>; let cursorOffset = 0; let snapshotId: string | undefined; if (args.cursor) { const cursor = decodeListCursor(args.cursor); const snapshot = listSnapshots.get(cursor.snapshotId); if (snapshot?.fingerprint !== fingerprint) { throw new McpError( ErrorCode.E_INVALID_INPUT, 'Invalid cursor: the cursor value is malformed or expired.' ); } const { offset, snapshotId: storedSnapshotId } = cursor; cursorOffset = offset; snapshotId = storedSnapshotId; result = { path: snapshot.path, entries: snapshot.entries, summary: snapshot.summary, }; } else { result = await listDirectory(dirPath, options); } const displayEntries = result.entries.slice( cursorOffset, cursorOffset + pageSize ); if (!args.cursor && displayEntries.length < result.entries.length) { snapshotId = storeListSnapshot({ path: result.path, entries: result.entries, summary: result.summary, fingerprint, }); } const nextCursor = resolveNextListCursor( snapshotId, cursorOffset, displayEntries.length, result.entries.length ); const displayResult = { ...result, entries: displayEntries }; return buildToolResponse( buildListTextResult(displayResult, nextCursor), buildStructuredListResult(displayResult, nextCursor) ); } - src/tools/list-directory.ts:287-343 (registration)Registration function for the 'ls' tool.
export function registerListDirectoryTool( server: McpServer, options: ToolRegistrationOptions = {} ): void { const handler = ( args: z.infer<typeof ListDirectoryInputSchema>, extra: ToolExtra ): Promise<ToolResult<z.infer<typeof ListDirectoryOutputSchema>>> => executeToolWithDiagnostics({ toolName: 'ls', extra, outputSchema: ListDirectoryOutputSchema, context: { path: args.path ?? '.' }, run: (signal) => handleListDirectory(args, signal), onError: (error) => buildToolErrorResponse( error, ErrorCode.E_NOT_DIRECTORY, args.path ?? '.' ), }); const wrappedHandler = wrapToolHandler(handler, { guard: options.isInitialized, progressMessage: (args) => `≣ ls: ${args.path ? path.basename(args.path) : '.'}`, completionMessage: (args, result) => { const base = args.path ? path.basename(args.path) : '.'; if (result.isError) return `≣ ls: ${base} • failed`; const sc = result.structuredContent; const count = sc.totalEntries ?? 0; return `≣ ls: ${base} • ${count} ${count === 1 ? 'entry' : 'entries'}`; }, }); const validatedHandler = withValidatedArgs( ListDirectoryInputSchema, wrappedHandler ); if ( registerToolTaskIfAvailable( server, 'ls', LIST_DIRECTORY_TOOL, validatedHandler, options.iconInfo, options.isInitialized ) ) return; server.registerTool( 'ls', withDefaultIcons({ ...LIST_DIRECTORY_TOOL }, options.iconInfo), validatedHandler ); } - src/tools/list-directory.ts:37-49 (schema)The tool contract definition for 'ls'.
export const LIST_DIRECTORY_TOOL: ToolContract = { name: 'ls', title: 'List Directory', description: 'List immediate directory contents (non-recursive): name, path, type, size, modified date. ' + 'Omit path for workspace root. `includeIgnored=true` for node_modules etc. ' + 'For recursive search, use `find`.', inputSchema: ListDirectoryInputSchema, outputSchema: ListDirectoryOutputSchema, annotations: READ_ONLY_TOOL_ANNOTATIONS, taskSupport: 'optional', nuances: ['`pattern` enables filtered recursive traversal up to `maxDepth`.'], } as const;