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
TableJSON 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; }