From 8342b86e58961b428c3590792204978eeffe5660 Mon Sep 17 00:00:00 2001 From: Cotton Seed Date: Tue, 1 Apr 2025 16:01:19 -0400 Subject: [PATCH] [filesystem] Added an excludePatterns input to the directory_tree tool, similar to the search_files tool. Also, document the directory_tree tool in READE.md. --- src/filesystem/README.md | 9 +++++++++ src/filesystem/index.ts | 22 +++++++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/filesystem/README.md b/src/filesystem/README.md index 3d3fb48556..ffb905b8ce 100644 --- a/src/filesystem/README.md +++ b/src/filesystem/README.md @@ -64,6 +64,15 @@ Node.js server implementing Model Context Protocol (MCP) for filesystem operatio - List directory contents with [FILE] or [DIR] prefixes - Input: `path` (string) +- **directory_tree** + - 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 + - Inputs: + - `path` (string): Starting directory + - `excludePatterns` (string[]): Exclude any patterns. Glob formats are supported. + - Output is formatted with 2-space indentation for readability + - **move_file** - Move or rename files and directories - Inputs: diff --git a/src/filesystem/index.ts b/src/filesystem/index.ts index c544ff2571..2655b6e554 100644 --- a/src/filesystem/index.ts +++ b/src/filesystem/index.ts @@ -129,6 +129,7 @@ const ListDirectoryArgsSchema = z.object({ const DirectoryTreeArgsSchema = z.object({ path: z.string(), + excludePatterns: z.array(z.string()).optional().default([]) }); const MoveFileArgsSchema = z.object({ @@ -393,6 +394,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { "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). " + + "Supports excluding files and directories using glob patterns. " + "The output is formatted with 2-space indentation for readability. Only works within allowed directories.", inputSchema: zodToJsonSchema(DirectoryTreeArgsSchema) as ToolInput, }, @@ -542,12 +544,25 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { children?: TreeEntry[]; } - async function buildTree(currentPath: string): Promise { + async function buildTree(rootPath: string, currentPath: string, excludePatterns: string[] = []): Promise { const validPath = await validatePath(currentPath); const entries = await fs.readdir(validPath, {withFileTypes: true}); const result: TreeEntry[] = []; for (const entry of entries) { + const fullPath = path.join(currentPath, entry.name); + + // Check if path matches any exclude pattern + const relativePath = path.relative(rootPath, fullPath); + const shouldExclude = excludePatterns.some(pattern => { + const globPattern = pattern.includes('*') ? pattern : `**/${pattern}/**`; + return minimatch(relativePath, globPattern, { dot: true }); + }); + + if (shouldExclude) { + continue; + } + const entryData: TreeEntry = { name: entry.name, type: entry.isDirectory() ? 'directory' : 'file' @@ -555,7 +570,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { if (entry.isDirectory()) { const subPath = path.join(currentPath, entry.name); - entryData.children = await buildTree(subPath); + entryData.children = await buildTree(rootPath, subPath, excludePatterns); } result.push(entryData); @@ -564,7 +579,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { return result; } - const treeData = await buildTree(parsed.data.path); + const validRootPath = await validatePath(parsed.data.path); + const treeData = await buildTree(validRootPath, validRootPath, parsed.data.excludePatterns); return { content: [{ type: "text",