From a468ebdb0979b77a25ef6336ba0e05c4ff1a28eb Mon Sep 17 00:00:00 2001 From: vipmax Date: Sun, 30 Mar 2025 20:38:10 +0300 Subject: [PATCH] feat(ignore): add MCP_FILESYSTEM_IGNORE_PATTERNS for excluding directories --- src/filesystem/README.md | 16 ++++++++++++++++ src/filesystem/index.ts | 24 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/filesystem/README.md b/src/filesystem/README.md index 3d3fb48556..79dab4a9ee 100644 --- a/src/filesystem/README.md +++ b/src/filesystem/README.md @@ -151,6 +151,22 @@ Docker build: docker build -t mcp/filesystem -f src/filesystem/Dockerfile . ``` +## Exclude directories +Exclude dirs like `.venv`, `.git` with `MCP_FILESYSTEM_IGNORE_PATTERNS` env variable + +```json +{ + "mcpServers": { + "filesystem": { + ... + "env": { + "MCP_FILESYSTEM_IGNORE_PATTERNS": ".venv,.git,", + } + } + } +} +``` + ## License This MCP server is licensed under the MIT License. This means you are free to use, modify, and distribute the software, subject to the terms and conditions of the MIT License. For more details, please see the LICENSE file in the project repository. diff --git a/src/filesystem/index.ts b/src/filesystem/index.ts index c544ff2571..736a11d26c 100644 --- a/src/filesystem/index.ts +++ b/src/filesystem/index.ts @@ -34,11 +34,32 @@ function expandHome(filepath: string): string { return filepath; } +function getIgnorePatterns(): string[] { + const ignoreEnv: string | undefined = process.env.MCP_FILESYSTEM_IGNORE_PATTERNS; + if (!ignoreEnv) return []; + + return ignoreEnv + .split(',') + .map(pattern => pattern.trim()) + .map(pattern => pattern.includes('*') ? pattern : `*${pattern}*`); +} + + +function isIgnored(filePath: string, ignored: string[]): boolean { + if (!ignored.length) return false; + + return ignored.some(pattern => { + return minimatch(filePath, pattern, { dot: true }) + }); +} + // Store allowed directories in normalized form const allowedDirectories = args.map(dir => normalizePath(path.resolve(expandHome(dir))) ); +const ignorePatterns = getIgnorePatterns(); + // Validate that all directories exist and are accessible await Promise.all(args.map(async (dir) => { try { @@ -523,6 +544,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { const validPath = await validatePath(parsed.data.path); const entries = await fs.readdir(validPath, { withFileTypes: true }); const formatted = entries + .filter((entry) => !isIgnored(entry.name, ignorePatterns)) .map((entry) => `${entry.isDirectory() ? "[DIR]" : "[FILE]"} ${entry.name}`) .join("\n"); return { @@ -548,6 +570,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { const result: TreeEntry[] = []; for (const entry of entries) { + if (isIgnored(entry.name, ignorePatterns)) continue; + const entryData: TreeEntry = { name: entry.name, type: entry.isDirectory() ? 'directory' : 'file'