get_file_tree
Retrieve the file tree structure of a code project to understand its organization and contents, with options to filter using .gitignore rules and custom exclusions.
Instructions
Retrieves the file tree structure of the project.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | The target directory path. | |
| use_gitignore | No | Whether to use .gitignore rules. | |
| ignore_git | No | Whether to ignore the .git directory. | |
| custom_blacklist | No | Custom blacklist items. |
Implementation Reference
- src/tools/get_file_tree.js:90-142 (handler)Main handler function that implements the core logic of the get_file_tree tool: path validation, file listing, tree building, rendering, and result formatting.
async function handleRequest(parameters) { console.error('get_file_tree: Starting execution'); const startTime = Date.now(); const { path: targetPath, use_gitignore, ignore_git, custom_blacklist } = parameters; if (!targetPath) { throw new Error("Missing required parameter: 'path'."); } // Resolve to absolute path - assuming the input path might be relative to CWD const absolutePath = path.resolve(targetPath); console.error(`get_file_tree: Resolved path to ${absolutePath}`); // Validate path existence and type try { const stats = await fs.stat(absolutePath); if (!stats.isDirectory()) { throw new Error(`Path '${targetPath}' is not a directory.`); } console.error(`get_file_tree: Validated path exists and is a directory`); } catch (error) { if (error.code === 'ENOENT') { throw new Error(`Path '${targetPath}' not found.`); } // Rethrow other stat errors (like permission issues) throw new Error(`Error accessing path '${targetPath}': ${error.message}`); } // List files using the core lister and provided filter options console.error(`get_file_tree: Listing files in ${absolutePath}`); const fileList = await listFiles(absolutePath, { useGitignore: use_gitignore || false, // Pass through params correctly, default to false for speed ignoreGit: ignore_git || true, customBlacklist: custom_blacklist || [] }); console.error(`get_file_tree: Found ${fileList.length} files`); // Build and render the tree structure console.error(`get_file_tree: Building tree object`); const treeObject = buildTreeObject(fileList); const rootDirName = path.basename(absolutePath); // Render the tree starting from the root object, prefix indicates level console.error(`get_file_tree: Rendering tree`); const treeString = rootDirName + '/\n' + renderTree(treeObject); const executionTime = Date.now() - startTime; console.error(`get_file_tree: Execution completed in ${executionTime}ms`); return { file_tree: treeString // Return the result in the expected format }; } - src/mcp-server.js:121-145 (registration)Registers the get_file_tree tool with the MCP server, including input schema validation using Zod and a wrapper that calls the handler with logging and result adaptation.
if (getFileTreeHandler) { server.tool( 'get_file_tree', 'Retrieves the file tree structure of the project.', { path: z.string().optional().describe('The target directory path.'), use_gitignore: z.boolean().optional().describe('Whether to use .gitignore rules.'), ignore_git: z.boolean().optional().describe('Whether to ignore the .git directory.'), custom_blacklist: z.array(z.string()).optional().describe('Custom blacklist items.') }, async (params) => { logInfo(`Executing get_file_tree tool with params: ${JSON.stringify(params)}`); try { const startTime = Date.now(); const result = await getFileTreeHandler(params); const executionTime = Date.now() - startTime; logDebug(`get_file_tree completed in ${executionTime}ms`); return adaptToolResult(result); } catch (error) { logError('Error in get_file_tree tool:', error); throw error; } } ); } - src/mcp-server.js:126-129 (schema)Zod schema defining the input parameters for the get_file_tree tool.
path: z.string().optional().describe('The target directory path.'), use_gitignore: z.boolean().optional().describe('Whether to use .gitignore rules.'), ignore_git: z.boolean().optional().describe('Whether to ignore the .git directory.'), custom_blacklist: z.array(z.string()).optional().describe('Custom blacklist items.') - src/tools/get_file_tree.js:11-47 (helper)Helper function to build a hierarchical tree object from a flat list of file paths, used in the handler.
function buildTreeObject(filePaths) { const tree = {}; // Sort paths alphabetically for consistent tree structure const sortedPaths = [...filePaths].sort((a, b) => a.localeCompare(b)); sortedPaths.forEach(filePath => { // Normalize path separators just in case const parts = filePath.replace(/\\/g, '/').split('/'); let currentLevel = tree; for (let i = 0; i < parts.length; i++) { const part = parts[i]; if (!part) continue; // Skip empty parts potentially caused by leading/trailing slashes if (i === parts.length - 1) { // It's a file if (!currentLevel._files) { currentLevel._files = []; } // Avoid adding duplicates if path normalization leads to same entry if (!currentLevel._files.includes(part)) { currentLevel._files.push(part); } } else { // It's a directory if (!currentLevel[part]) { currentLevel[part] = {}; // Create directory node if it doesn't exist } // Ensure we don't try to traverse into a file node mistakenly marked earlier if (typeof currentLevel[part] === 'object' && currentLevel[part] !== null) { currentLevel = currentLevel[part]; } else { // Handle potential path conflict (e.g., 'a/b' file and 'a/b/c' directory) // This basic builder assumes valid, non-conflicting paths from listFiles console.warn(`Path conflict or unexpected structure processing: ${filePath}`); break; // Stop processing this conflicting path } } } }); return tree; } - src/tools/get_file_tree.js:54-79 (helper)Helper function to render the tree object into an ASCII art string representation, used in the handler.
function renderTree(node, prefix = '') { let result = ''; // Get directory names, sort them const folders = Object.keys(node).filter(key => key !== '_files').sort((a, b) => a.localeCompare(b)); // Get file names, sort them const files = node._files ? [...node._files].sort((a, b) => a.localeCompare(b)) : []; const totalItems = folders.length + files.length; let itemCount = 0; // Render folders first folders.forEach((folder) => { itemCount++; const isLast = itemCount === totalItems; const connector = isLast ? '└── ' : '├── '; const childPrefix = isLast ? ' ' : '│ '; // Connector for children result += prefix + connector + folder + '/\n'; result += renderTree(node[folder], prefix + childPrefix); // Recurse into subdirectory }); // Render files files.forEach((file) => { itemCount++; const isLast = itemCount === totalItems; const connector = isLast ? '└── ' : '├── '; result += prefix + connector + file + '\n'; }); return result; }