directory_tree
Generate a JSON tree structure of files and directories, including 'name', 'type', and 'children' for directories, with 2-space indentation for readability. Works within specified allowed paths.
Instructions
Get a recursive tree view of files and directories as a JSON structure. Each entry includes 'name', 'type' (file/directory), and 'children' for directories. Files have no children array, while directories always have a children array (which may be empty). The output is formatted with 2-space indentation for readability. Only works within allowed directories.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes |
Input Schema (JSON Schema)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"properties": {
"path": {
"type": "string"
}
},
"required": [
"path"
],
"type": "object"
}
Implementation Reference
- src/filesystem/index.ts:524-575 (handler)The main handler function for the directory_tree tool. It recursively builds a JSON tree structure of the directory using fs.readdir, applies excludePatterns with minimatch, and returns formatted JSON.async (args: z.infer<typeof DirectoryTreeArgsSchema>) => { interface TreeEntry { name: string; type: 'file' | 'directory'; children?: TreeEntry[]; } const rootPath = args.path; async function buildTree(currentPath: string, excludePatterns: string[] = []): Promise<TreeEntry[]> { const validPath = await validatePath(currentPath); const entries = await fs.readdir(validPath, { withFileTypes: true }); const result: TreeEntry[] = []; for (const entry of entries) { const relativePath = path.relative(rootPath, path.join(currentPath, entry.name)); const shouldExclude = excludePatterns.some(pattern => { if (pattern.includes('*')) { return minimatch(relativePath, pattern, { dot: true }); } // For files: match exact name or as part of path // For directories: match as directory path return minimatch(relativePath, pattern, { dot: true }) || minimatch(relativePath, `**/${pattern}`, { dot: true }) || minimatch(relativePath, `**/${pattern}/**`, { dot: true }); }); if (shouldExclude) continue; const entryData: TreeEntry = { name: entry.name, type: entry.isDirectory() ? 'directory' : 'file' }; if (entry.isDirectory()) { const subPath = path.join(currentPath, entry.name); entryData.children = await buildTree(subPath, excludePatterns); } result.push(entryData); } return result; } const treeData = await buildTree(rootPath, args.excludePatterns); const text = JSON.stringify(treeData, null, 2); const contentBlock = { type: "text" as const, text }; return { content: [contentBlock], structuredContent: { content: [contentBlock] } }; }
- src/filesystem/index.ts:123-126 (schema)Zod schema defining input arguments for directory_tree: path (string) and optional excludePatterns (array of strings).const DirectoryTreeArgsSchema = z.object({ path: z.string(), excludePatterns: z.array(z.string()).optional().default([]) });
- src/filesystem/index.ts:508-576 (registration)Registration of the directory_tree tool using server.registerTool, including title, description, inputSchema (inline copy of schema), outputSchema, annotations, and the handler function.server.registerTool( "directory_tree", { title: "Directory Tree", description: "Get a recursive tree view of files and directories as a JSON structure. " + "Each entry includes 'name', 'type' (file/directory), and 'children' for directories. " + "Files have no children array, while directories always have a children array (which may be empty). " + "The output is formatted with 2-space indentation for readability. Only works within allowed directories.", inputSchema: { path: z.string(), excludePatterns: z.array(z.string()).optional().default([]) }, outputSchema: { content: z.string() }, annotations: { readOnlyHint: true } }, async (args: z.infer<typeof DirectoryTreeArgsSchema>) => { interface TreeEntry { name: string; type: 'file' | 'directory'; children?: TreeEntry[]; } const rootPath = args.path; async function buildTree(currentPath: string, excludePatterns: string[] = []): Promise<TreeEntry[]> { const validPath = await validatePath(currentPath); const entries = await fs.readdir(validPath, { withFileTypes: true }); const result: TreeEntry[] = []; for (const entry of entries) { const relativePath = path.relative(rootPath, path.join(currentPath, entry.name)); const shouldExclude = excludePatterns.some(pattern => { if (pattern.includes('*')) { return minimatch(relativePath, pattern, { dot: true }); } // For files: match exact name or as part of path // For directories: match as directory path return minimatch(relativePath, pattern, { dot: true }) || minimatch(relativePath, `**/${pattern}`, { dot: true }) || minimatch(relativePath, `**/${pattern}/**`, { dot: true }); }); if (shouldExclude) continue; const entryData: TreeEntry = { name: entry.name, type: entry.isDirectory() ? 'directory' : 'file' }; if (entry.isDirectory()) { const subPath = path.join(currentPath, entry.name); entryData.children = await buildTree(subPath, excludePatterns); } result.push(entryData); } return result; } const treeData = await buildTree(rootPath, args.excludePatterns); const text = JSON.stringify(treeData, null, 2); const contentBlock = { type: "text" as const, text }; return { content: [contentBlock], structuredContent: { content: [contentBlock] } }; } );