Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/filesystem/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
24 changes: 24 additions & 0 deletions src/filesystem/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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'
Expand Down