A secure, read-only MCP server for filesystem scanning, searching, and analysis with comprehensive security validation.
| Feature | Description |
|---|---|
| 📂 Directory Listing | List and explore directory contents with recursive support |
| 🔍 File Search | Find files using glob patterns like **/*.ts |
| 📝 Content Search | Search text within files using regex with context lines |
| 📊 Directory Analysis | Get statistics, file types, largest files, and recently modified files |
| 🌳 Directory Tree | JSON tree structure optimized for AI parsing |
| 📄 File Reading | Read single or multiple files with head/tail and line range support |
| 🖼️ Media File Support | Read binary files (images, audio, video) as base64 |
| 🔒 Security First | Path validation, symlink escape protection, and access control |
| ⚡ Parallel Operations | Efficient batch file reading with configurable concurrency |
| Task | Tool |
|---|---|
| Explore project structure | list_directory |
| Find specific file types | search_files |
| Search for code patterns/text | search_content |
| Understand codebase statistics | analyze_directory |
| Get AI-friendly project overview | directory_tree |
| Read source code | read_file |
| Batch read multiple files | read_multiple_files |
| Get file metadata (size, dates) | get_file_info |
| Read images or binary files | read_media_file |
| Check available directories | list_allowed_directories |
# Works in current directory automatically!
npx -y @j0hanz/filesystem-context-mcp@latestOr specify directories explicitly:
npx -y @j0hanz/filesystem-context-mcp@latest /path/to/your/projectAdd to your VS Code settings (.vscode/mcp.json):
{
"servers": {
"filesystem-context": {
"command": "npx",
"args": [
"-y",
"@j0hanz/filesystem-context-mcp@latest",
"${workspaceFolder}"
]
}
}
}Tip:
${workspaceFolder}automatically uses your current VS Code workspace. You can also omit it and the server will use its current working directory.
Add to your Claude Desktop configuration (claude_desktop_config.json):
{
"mcpServers": {
"filesystem-context": {
"command": "npx",
"args": ["-y", "@j0hanz/filesystem-context-mcp@latest"]
}
}
}Note: Claude Desktop will use the current working directory automatically. No path arguments needed!
npx -y @j0hanz/filesystem-context-mcp@latest /path/to/dir1 /path/to/dir2npm install -g @j0hanz/filesystem-context-mcp
filesystem-context-mcp /path/to/your/projectgit clone https://github.com/j0hanz/filesystem-context-mcp-server.git
cd filesystem-context-mcp-server
npm install
npm run build
node dist/index.js /path/to/your/projectThe server determines which directories to access in this order:
- CLI Arguments - Explicitly passed paths take highest priority
- MCP Roots Protocol - Directories provided by the MCP client
- Current Working Directory - Automatic fallback for plug-and-play experience
This means you can run the server with zero configuration and it will work!
Optionally specify one or more directory paths as arguments:
filesystem-context-mcp /home/user/project /home/user/docsIf no CLI arguments are provided, the server will use the MCP Roots protocol to receive allowed directories from the client (if supported).
If neither CLI arguments nor MCP Roots provide directories, the server automatically uses the current working directory. This makes it truly plug-and-play!
| Variable | Description |
|---|---|
NODE_ENV |
Set to production for optimized performance |
List all directories that this server is allowed to access.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| (none) | - | - | - | No parameters required |
Returns: Array of allowed directory paths.
List contents of a directory with optional recursive listing.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | ✅ | - | Directory path to list |
recursive |
boolean | ❌ | false |
List recursively |
includeHidden |
boolean | ❌ | false |
Include hidden files |
maxDepth |
number | ❌ | 10 |
Maximum depth for recursive listing (0-100) |
maxEntries |
number | ❌ | - | Maximum entries to return (1-100,000) |
sortBy |
string | ❌ | name |
Sort by: name, size, modified, type |
includeSymlinkTargets |
boolean | ❌ | false |
Include symlink target paths |
Returns: List of entries with name, type, size, and modified date.
Search for files using glob patterns.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | ✅ | - | Base directory to search from |
pattern |
string | ✅ | - | Glob pattern (e.g., **/*.ts, src/**/*.js) |
excludePatterns |
string[] | ❌ | [] |
Patterns to exclude |
maxResults |
number | ❌ | - | Maximum matches to return (1-10,000) |
sortBy |
string | ❌ | path |
Sort by: name, size, modified, path |
maxDepth |
number | ❌ | - | Maximum directory depth to search (1-100) |
Returns: List of matching files with path, type, size, and modified date.
Example:
{
"path": "/project",
"pattern": "**/*.ts",
"excludePatterns": ["node_modules/**", "dist/**"]
}Read the contents of a text file.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | ✅ | - | File path to read |
encoding |
string | ❌ | utf-8 |
File encoding (utf-8, ascii, base64, etc.) |
maxSize |
number | ❌ | 10MB | Maximum file size in bytes |
lineStart |
number | ❌ | - | Start line (1-indexed) for reading a range |
lineEnd |
number | ❌ | - | End line (inclusive) for reading a range |
head |
number | ❌ | - | Read only first N lines |
tail |
number | ❌ | - | Read only last N lines |
Note: Cannot specify both
headandtailsimultaneously. UselineStart/lineEndfor range reading.
Returns: File contents as text.
Read multiple files in parallel for efficient batch operations.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
paths |
string[] | ✅ | - | Array of file paths (max 100) |
encoding |
string | ❌ | utf-8 |
File encoding |
maxSize |
number | ❌ | 10MB | Maximum file size per file |
head |
number | ❌ | - | Read only first N lines of each file |
tail |
number | ❌ | - | Read only last N lines of each file |
Returns: Array of results with content or error for each file.
Get detailed metadata about a file or directory.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | ✅ | - | Path to file or directory |
Returns: Metadata including name, type, size, created/modified/accessed timestamps, permissions, MIME type, and symlink target (if applicable).
Search for text content within files using regular expressions.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | ✅ | - | Base directory to search in |
pattern |
string | ✅ | - | Regex pattern to search for |
filePattern |
string | ❌ | **/* |
Glob pattern to filter files |
excludePatterns |
string[] | ❌ | [] |
Glob patterns to exclude |
caseSensitive |
boolean | ❌ | false |
Case-sensitive search |
maxResults |
number | ❌ | 100 |
Maximum number of results (1-10,000) |
maxFileSize |
number | ❌ | 1MB | Maximum file size to scan |
maxFilesScanned |
number | ❌ | - | Maximum files to scan before stopping |
timeoutMs |
number | ❌ | - | Timeout in milliseconds (100-3,600,000) |
skipBinary |
boolean | ❌ | true |
Skip binary files |
contextLines |
number | ❌ | 0 |
Lines of context before/after match (0-10) |
wholeWord |
boolean | ❌ | false |
Match whole words only |
isLiteral |
boolean | ❌ | false |
Treat pattern as literal string (escape special) |
Returns: Matching lines with file path, line number, content, and optional context.
Example:
{
"path": "/project/src",
"pattern": "TODO|FIXME",
"filePattern": "**/*.ts",
"contextLines": 2
}Analyze a directory structure and return statistics.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | ✅ | - | Directory to analyze |
maxDepth |
number | ❌ | 10 |
Maximum depth to analyze (0-100) |
topN |
number | ❌ | 10 |
Number of top items to return (1-1000) |
excludePatterns |
string[] | ❌ | [] |
Glob patterns to exclude |
includeHidden |
boolean | ❌ | false |
Include hidden files and directories |
Returns: Statistics including total files/directories, total size, file type distribution, largest files, and recently modified files.
Get a JSON tree structure of a directory, optimized for AI parsing.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | ✅ | - | Directory path to build tree from |
maxDepth |
number | ❌ | 5 |
Maximum depth to traverse (0-50) |
excludePatterns |
string[] | ❌ | [] |
Glob patterns to exclude |
includeHidden |
boolean | ❌ | false |
Include hidden files and directories |
includeSize |
boolean | ❌ | false |
Include file sizes in the tree |
maxFiles |
number | ❌ | - | Maximum total files to include (1-100k) |
Returns: Hierarchical tree structure with file/directory nodes.
Read a binary/media file and return it as base64-encoded data.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | ✅ | - | Path to the media file |
maxSize |
number | ❌ | 50MB | Maximum file size in bytes (max 500MB) |
Supported formats: Images (PNG, JPG, GIF, WebP, SVG, etc.), Audio (MP3, WAV, FLAC, etc.), Video (MP4, WebM, etc.), Fonts (TTF, WOFF, etc.), PDFs, and more.
Returns: Base64-encoded data with MIME type, size, and dimensions (for images).
VS Code
Add to .vscode/mcp.json (recommended) or .vscode/settings.json:
{
"servers": {
"filesystem-context": {
"command": "npx",
"args": [
"-y",
"@j0hanz/filesystem-context-mcp@latest",
"${workspaceFolder}"
]
}
}
}Note:
${workspaceFolder}is expanded by VS Code to the current workspace path.
Claude Desktop
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"filesystem-context": {
"command": "npx",
"args": ["-y", "@j0hanz/filesystem-context-mcp@latest"]
}
}
}Cursor
Add to Cursor's MCP configuration:
{
"mcpServers": {
"filesystem-context": {
"command": "npx",
"args": ["-y", "@j0hanz/filesystem-context-mcp@latest"]
}
}
}Windsurf
Add to Windsurf's MCP configuration:
{
"mcpServers": {
"filesystem-context": {
"command": "npx",
"args": ["-y", "@j0hanz/filesystem-context-mcp@latest"]
}
}
}This server implements multiple layers of security:
| Protection | Description |
|---|---|
| Access Control | Only explicitly allowed directories are accessible |
| Path Validation | All paths are validated before any filesystem operation |
| Symlink Protection | Symlinks that resolve outside allowed directories are blocked |
| Path Traversal Prevention | Attempts to escape via ../ are detected and blocked |
| Read-Only Operations | Server only performs read operations—no writes, deletes, or modifications |
| Safe Regex | Regular expressions are validated to prevent ReDoS attacks |
| Size Limits | Configurable limits prevent resource exhaustion |
┌─────────────────────────────────────────────────────────┐
│ MCP Client │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Filesystem Context MCP Server │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Path Validation Layer │ │
│ │ • Normalize paths │ │
│ │ • Check against allowed directories │ │
│ │ • Resolve and validate symlinks │ │
│ │ • Block traversal attempts │ │
│ └───────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Read-Only File Operations │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Allowed Directories Only │
│ /home/user/project ✅ │
│ /home/user/docs ✅ │
│ /etc/passwd ❌ (blocked) │
│ ../../../etc ❌ (blocked) │
└─────────────────────────────────────────────────────────┘
- Node.js >= 20.0.0
- npm
| Command | Description |
|---|---|
npm run build |
Compile TypeScript to JavaScript |
npm run dev |
Watch mode with tsx |
npm run start |
Run compiled server |
npm run test |
Run tests with Vitest |
npm run test:watch |
Run tests in watch mode |
npm run test:coverage |
Run tests with coverage report |
npm run lint |
Run ESLint |
npm run format |
Format code with Prettier |
npm run type-check |
TypeScript type checking |
npm run inspector |
Test with MCP Inspector |
src/
├── index.ts # Entry point, CLI argument parsing
├── server.ts # MCP server setup, roots protocol handling
├── config/
│ └── types.ts # Shared TypeScript types
├── lib/
│ ├── constants.ts # Configuration constants and limits
│ ├── errors.ts # Error handling utilities
│ ├── file-operations.ts# Core filesystem operations
│ ├── formatters.ts # Output formatting utilities
│ ├── fs-helpers.ts # Low-level filesystem helpers
│ ├── image-parsing.ts # Image dimension parsing
│ ├── path-utils.ts # Path manipulation utilities
│ └── path-validation.ts# Security: path validation layer
├── schemas/
│ ├── common.ts # Shared Zod schemas
│ ├── inputs.ts # Input validation schemas
│ ├── outputs.ts # Output validation schemas
│ ├── validators.ts # Custom validation functions
│ └── index.ts # Schema exports
├── tools/
│ ├── analyze-directory.ts
│ ├── directory-tree.ts
│ ├── get-file-info.ts
│ ├── list-allowed-dirs.ts
│ ├── list-directory.ts
│ ├── read-file.ts
│ ├── read-media-file.ts
│ ├── read-multiple-files.ts
│ ├── search-content.ts
│ ├── search-files.ts
│ └── index.ts # Tool registration
└── __tests__/ # Test files
npm run inspectorThis launches the MCP Inspector for interactive testing of all tools.
| Issue | Solution |
|---|---|
| "Access denied" error | Ensure the path is within an allowed directory. Use list_allowed_directories to check. |
| "Path does not exist" error | Verify the path exists. Use list_directory to explore available files. |
| "File too large" error | Use head or tail parameters for partial reading, or increase maxSize. |
| "Binary file" warning | Use read_media_file for binary files, or set skipBinary=false in content search. |
| Unexpected directory access | Server defaults to CWD if no args/roots provided. Pass explicit paths to restrict. |
| Symlink blocked | Symlinks that resolve outside allowed directories are blocked for security. |
| Regex timeout | Simplify the regex pattern or use isLiteral=true for literal string search. |
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Run tests and linting (
npm run lint && npm run test) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Use TypeScript with strict mode
- Follow ESLint configuration
- Use Prettier for formatting
- Write tests for new features