
Security News
pnpm 11.5 Adds Support for Recognizing npm Staged Publishes
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.
@j0hanz/filesystem-context-mcp
Advanced tools
🔍 Read-only MCP server for secure filesystem exploration, searching, and analysis
A secure, read-only MCP server for filesystem scanning, searching, and analysis with comprehensive security validation.
| Task | Tool |
|---|---|
| Explore project structure | list_directory |
| Find files or directories | search_files |
| Search for code patterns/text | search_content |
| Read source code | read_file |
| Batch read multiple files | read_multiple_files |
| Get file metadata (size, dates) | get_file_info |
| Batch get file metadata | get_multiple_file_info |
| Check available directories | list_allowed_directories |
Allow the current working directory explicitly:
npx -y @j0hanz/filesystem-context-mcp@latest --allow-cwd
Or pass explicit directories:
npx -y @j0hanz/filesystem-context-mcp@latest /path/to/project /path/to/docs
If your MCP client supports the Roots protocol, you can omit directory arguments and let the client provide allowed directories. Otherwise, pass explicit directories or use --allow-cwd.
Add to .vscode/mcp.json:
{
"servers": {
"filesystem-context": {
"command": "npx",
"args": [
"-y",
"@j0hanz/filesystem-context-mcp@latest",
"${workspaceFolder}"
]
}
}
}
npx -y @j0hanz/filesystem-context-mcp@latest /path/to/dir1 /path/to/dir2
npm install -g @j0hanz/filesystem-context-mcp
filesystem-context-mcp /path/to/your/project
git 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/project
Access is always restricted to explicitly allowed directories.
--allow-cwd optionally adds the current working directory.--allow-cwd are provided, only roots inside those baseline directories are accepted.Notes:
C:path are rejected. Use C:\path or C:/path.CON, NUL) are blocked.All configuration is optional. Values are integers unless noted. Sizes are in bytes, timeouts in milliseconds.
| Variable | Default | Range | Description |
|---|---|---|---|
UV_THREADPOOL_SIZE | (unset) | 1-1024 | libuv threadpool size. If set, caps parallelism. |
FILESYSTEM_CONTEXT_CONCURRENCY | Auto (2x cores, cap 50) | 1-100 | Parallel file operations. Further capped by UV_THREADPOOL_SIZE |
FILESYSTEM_CONTEXT_SEARCH_WORKERS | 0 (disabled) | 0-32 | Worker-thread offload for search_content (uses one worker per search) |
FILESYSTEM_CONTEXT_GLOB_ENGINE | auto | n/a | Glob engine: auto, fast-glob, or node/node:fs |
MAX_FILE_SIZE | 10MB | 1MB-100MB | Max text file size (read_file, read_multiple_files) |
MAX_SEARCH_SIZE | 1MB | 100KB-10MB | Max file size for content search (search_content) |
DEFAULT_DEPTH | 10 | 1-100 | Default max depth (list_directory, search_files) |
DEFAULT_RESULTS | 100 | 10-10000 | Default max results (search_files, search_content) |
DEFAULT_LIST_MAX_ENTRIES | 10000 | 100-100000 | Default max entries (list_directory) |
DEFAULT_SEARCH_MAX_FILES | 20000 | 100-100000 | Default max files scanned (search_files, search_content) |
DEFAULT_SEARCH_TIMEOUT | 30000 | 100-3600000 | Default operation timeout (list_directory, search_files, search_content) |
Note: FILESYSTEM_CONTEXT_GLOB_ENGINE is a string. auto uses Node's glob when options allow, otherwise it falls back to fast-glob.
See CONFIGURATION.md for profiles and examples.
All tools return both human-readable text and structured JSON. Structured
responses include ok, error (with code, message, path, suggestion),
and effectiveOptions/summary fields where applicable.
list_allowed_directoriesList all directories that this server can access.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| (none) | - | - | - | - |
Returns: Allowed directory paths plus access status (accessible/readable), count, and a configuration hint in structured output.
list_directoryList contents of a directory with optional recursion.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path | string | Yes | - | Directory path to list |
recursive | boolean | No | false | List subdirectories recursively |
includeHidden | boolean | No | false | Include hidden files and directories |
excludePatterns | string[] | No | [] | Glob patterns to exclude |
pattern | string | No | - | Glob pattern to include (relative, no ..) |
maxDepth | number | No | 10 | Maximum depth for recursive listing (0-100) |
maxEntries | number | No | 10000 | Maximum entries to return (1-100000) |
timeoutMs | number | No | 30000 | Timeout in milliseconds |
sortBy | string | No | name | Sort by: name, size, modified, type |
includeSymlinkTargets | boolean | No | false | Include symlink target paths (symlinks are not followed) |
Returns: Entries with name, relativePath, type, extension, size, modified time,
and symlink target. Structured output includes effectiveOptions and a
summary (totals, maxDepthReached, truncated/stoppedReason, entriesScanned,
entriesVisible, skippedInaccessible, symlinksNotFollowed).
search_filesSearch for paths (files and directories) using glob patterns.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path | string | Yes | - | Base directory to search from |
pattern | string | Yes | - | Glob pattern (e.g., **/*.ts, src/**/*.js) |
excludePatterns | string[] | No | built-in exclude list | Glob patterns to exclude |
maxResults | number | No | 100 | Maximum matches to return (1-10000) |
sortBy | string | No | path | Sort by: name, size, modified, path |
maxDepth | number | No | 10 | Maximum directory depth to search (0-100) |
maxFilesScanned | number | No | 20000 | Maximum files to scan before stopping |
timeoutMs | number | No | 30000 | Timeout in milliseconds |
baseNameMatch | boolean | No | false | Match patterns without slashes against basenames |
skipSymlinks | boolean | No | true | Must remain true; symlink traversal is disabled for security |
includeHidden | boolean | No | false | Include hidden files and directories |
Returns: Matching paths with relative path, type, size, and modified date.
Structured output includes effectiveOptions and a summary (matched,
filesScanned, truncated/stoppedReason, skippedInaccessible).
Directories may appear as type other in structured output.
read_fileRead the contents of a text file.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path | string | Yes | - | File path to read |
encoding | string | No | utf-8 | File encoding (utf-8, utf8, ascii, base64, hex, latin1) |
maxSize | number | No | 10MB | Maximum file size in bytes (capped by MAX_FILE_SIZE) |
skipBinary | boolean | No | true | Reject likely-binary files |
lineStart | number | No | - | Start line (1-indexed) for range reading |
lineEnd | number | No | - | End line (inclusive) for range reading |
head | number | No | - | Read only first N lines |
tail | number | No | - | Read only last N lines |
Notes:
head, tail, and lineStart/lineEnd are mutually exclusive.lineStart and lineEnd must be provided together.Returns: File content plus structured metadata (readMode, linesRead, totalLines, hasMoreLines, truncated, effectiveOptions).
read_multiple_filesRead multiple files in parallel.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
paths | string[] | Yes | - | Array of file paths (max 100) |
encoding | string | No | utf-8 | File encoding |
maxSize | number | No | 10MB | Maximum size per file in bytes (capped by MAX_FILE_SIZE) |
maxTotalSize | number | No | 100MB | Maximum total size across all files (max 1GB) |
head | number | No | - | Read only first N lines of each file |
tail | number | No | - | Read only last N lines of each file |
lineStart | number | No | - | Start line (1-indexed) for each file |
lineEnd | number | No | - | End line (inclusive) for each file |
Notes:
lineStart and lineEnd must be provided together.head, tail, and lineStart/lineEnd are mutually exclusive.read_file with skipBinary=true for checks.Returns: Per-file content or error, plus structured summary and effectiveOptions.
get_file_infoGet detailed metadata about a file or directory.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path | string | Yes | - | Path to file or directory |
Returns: name, path, type, size, created/modified/accessed timestamps, permissions, isHidden, MIME type, and symlink target (if applicable).
get_multiple_file_infoGet metadata for multiple files/directories in parallel.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
paths | string[] | Yes | - | Array of paths to query (max 100) |
includeMimeType | boolean | No | true | Include MIME type detection |
Returns: Array of file info with individual success/error status, plus summary (total, succeeded, failed, totalSize).
search_contentSearch for text content within files using regular expressions.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path | string | Yes | - | Base directory to search in |
pattern | string | Yes | - | Regex pattern to search for |
filePattern | string | No | **/* | Glob pattern to filter files |
excludePatterns | string[] | No | built-in exclude list | Glob patterns to exclude |
caseSensitive | boolean | No | false | Case-sensitive search |
maxResults | number | No | 100 | Maximum number of results |
maxFileSize | number | No | 1MB | Maximum file size to scan (default from MAX_SEARCH_SIZE) |
maxFilesScanned | number | No | 20000 | Maximum files to scan before stopping |
timeoutMs | number | No | 30000 | Timeout in milliseconds |
skipBinary | boolean | No | true | Skip likely-binary files |
includeHidden | boolean | No | false | Include hidden files and directories |
contextLines | number | No | 0 | Lines of context before/after match (0-10) |
wholeWord | boolean | No | false | Match whole words only |
isLiteral | boolean | No | false | Treat pattern as literal string (escape regex chars) |
baseNameMatch | boolean | No | false | Match file patterns without slashes against basenames |
caseSensitiveFileMatch | boolean | No | true | Case-sensitive filename matching |
Returns: Matching lines with file path, line number, content, and optional
context. Structured output includes effectiveOptions and a summary
(filesScanned/filesMatched, totalMatches, truncated/stoppedReason,
skippedTooLarge/skippedBinary/skippedInaccessible,
linesSkippedDueToRegexTimeout).
Matched line content is trimmed to 200 characters.
Built-in exclude list includes common dependency/build/output directories and
files: node_modules, dist, build, coverage, .git, .vscode, .idea,
.DS_Store, .next, .nuxt, .output, .svelte-kit, .cache, .yarn,
jspm_packages, bower_components, out, tmp, .temp,
npm-debug.log, yarn-debug.log, yarn-error.log, Thumbs.db. Pass
excludePatterns: [] to disable it.
| Code | Meaning |
|---|---|
E_ACCESS_DENIED | Path outside allowed roots |
E_NOT_FOUND | Path does not exist |
E_NOT_FILE | Expected file, got directory |
E_NOT_DIRECTORY | Expected directory, got file |
E_TOO_LARGE | File exceeds size limits |
E_TIMEOUT | Operation timed out |
E_INVALID_PATTERN | Invalid glob/regex pattern |
E_INVALID_INPUT | Invalid argument(s) |
E_PERMISSION_DENIED | OS-level permission denied |
E_SYMLINK_NOT_ALLOWED | Symlink escapes allowed roots |
E_UNKNOWN | Unexpected error |
Add to .vscode/mcp.json (recommended) or .vscode/settings.json:
{
"servers": {
"filesystem-context": {
"command": "npx",
"args": [
"-y",
"@j0hanz/filesystem-context-mcp@latest",
"${workspaceFolder}"
]
}
}
}
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",
"C:\\path\\to\\project"
]
}
}
}
If your client supports MCP Roots, you can omit the path. Otherwise, pass a path or --allow-cwd.
Add to Cursor's MCP configuration:
{
"mcpServers": {
"filesystem-context": {
"command": "npx",
"args": [
"-y",
"@j0hanz/filesystem-context-mcp@latest",
"${workspaceFolder}"
]
}
}
}
Add to ~/.codex/config.toml:
[mcp_servers.filesystem-context]
command = "npx"
args = ["-y", "@j0hanz/filesystem-context-mcp@latest", "/path/to/your/project"]
If your client supports MCP Roots, you can omit the path. Otherwise, pass a path or --allow-cwd.
Add to Windsurf's MCP configuration:
{
"mcpServers": {
"filesystem-context": {
"command": "npx",
"args": [
"-y",
"@j0hanz/filesystem-context-mcp@latest",
"${workspaceFolder}"
]
}
}
}
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 | No writes, deletes, or modifications |
| Safe regex | Regex validation with RE2 prevents ReDoS |
| Size limits | Configurable limits prevent resource exhaustion |
| 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 (node --test with tsx/esm) |
npm run test:watch | Run tests in watch mode (node --test --watch) |
npm run test:coverage | Run tests with coverage (node --test --experimental-test-coverage) |
npm run test:node | Run node-tests (isolated checks) |
npm run lint | Run ESLint |
npm run format | Format code with Prettier |
npm run type-check | TypeScript type checking |
npm run bench | Run benchmarks |
npm run inspector | Test with MCP Inspector |
src/
index.ts # CLI entry point
server.ts # MCP server wiring and roots handling
instructions.md # Tool usage instructions (bundled in dist)
config/ # Shared types and formatting helpers
lib/ # Core logic and filesystem operations
schemas/ # Zod input/output schemas
tools/ # MCP tool registration
__tests__/ # node:test + tsx tests
node-tests/ # Isolated Node.js checks
| Issue | Solution |
|---|---|
| "Access denied" error | Ensure the path is within an allowed directory. Use list_allowed_directories to check. |
| "Path does not exist" | Verify the path exists. Use list_directory to explore available files. |
| "File too large" | Use head/tail or increase maxSize. |
| "Binary file" warning | Set skipBinary=false in read_file to read as text. |
| No directories available | Pass explicit paths, use --allow-cwd, or ensure the client provides MCP Roots. |
| Symlink blocked | Symlinks that resolve outside allowed directories are blocked. |
| Invalid regex/pattern | Simplify the regex or set isLiteral=true for exact matches. |
Contributions are welcome! Please follow these steps:
git checkout -b feature/amazing-feature)npm run lint && npm run test)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)FAQs
🔍 Read-only MCP server for secure filesystem exploration, searching, and analysis
We found that @j0hanz/filesystem-context-mcp demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.

Security News
Federal audit finds NIST lacked a plan to clear the NVD backlog, wasted funds on duplicate work, and delayed use of CISA data.

Research
/Security News
A mini Shai-Hulud campaign compromised Red Hat Cloud Services npm packages to steal developer and CI/CD secrets during installation.