j0hanz/fs-context-mcp-server
If you are the rightful owner of fs-context-mcp-server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
A secure Model Context Protocol (MCP) server designed for filesystem scanning, searching, and analysis, built with TypeScript and the official MCP SDK.
FS Context MCP Server
A read-only MCP server that provides AI assistants with secure filesystem access for exploring, searching, and reading files within approved directories.
One-Click Install
Overview
This server enables AI assistants to navigate your filesystem through a set of read-only tools:
- Explore directory structures with
lsandtree - Find files using glob patterns with
find - Search file contents with
grep - Read files with options for previews, line ranges, and batch operations
- Access file metadata through
statandstat_many
All operations are restricted to explicitly approved directories, with no write or modification capabilities.
Features
Directory Operations
- List directory contents with
ls - Render directory trees with configurable depth using
tree - Find files by glob patterns with
find
File Operations
- Read single files with optional line ranges or head preview
- Batch read up to 100 files in a single operation
- Get file metadata (size, timestamps, permissions) with
statandstat_many
Search
- Content search across files using
grep - Respects
.gitignorepatterns and common ignore directories - Configurable search timeout and worker threads
Security
- Read-only operations only
- Access restricted to explicitly approved directories
- Path traversal protection (blocks
..and symlink escapes) - RE2-based regex engine prevents ReDoS attacks
When to Use
| Task | Tool |
|---|---|
| Explore project structure | ls |
| Render a directory tree | tree |
| Find files | find |
| Search for code patterns/text | grep |
| Read source code | read |
| Batch read multiple files | read_many |
| Get file metadata (size, dates) | stat |
| Batch get file metadata | stat_many |
| Check available directories | roots |
Quick Start
NPX (Recommended)
For current directory:
npx -y @j0hanz/fs-context-mcp@latest --allow-cwd
For specific projects:
npx -y @j0hanz/fs-context-mcp@latest /path/to/project /path/to/docs
Note: If your MCP client supports the Roots protocol, you can omit directory arguments—the client will provide them automatically.
VS Code
Add to .vscode/mcp.json:
{
"servers": {
"fs-context": {
"command": "npx",
"args": ["-y", "@j0hanz/fs-context-mcp@latest", "${workspaceFolder}"]
}
}
}
Installation
NPX
Run without installation:
npx -y @j0hanz/fs-context-mcp@latest /path/to/dir1 /path/to/dir2
Global Installation
For permanent setup across all projects:
npm install -g @j0hanz/fs-context-mcp
fs-context-mcp /path/to/your/project
From Source
For contributors or custom builds:
git clone https://github.com/j0hanz/fs-context-mcp-server.git
cd fs-context-mcp-server
npm install
npm run build
node dist/index.js /path/to/your/project
Directory Access and Resolution
Access is always restricted to explicitly allowed directories.
- CLI directories are validated and added first (if provided).
--allow-cwdoptionally adds the current working directory.- MCP Roots from the client are used next:
- If CLI and/or
--allow-cwdare provided, only roots inside those baseline directories are accepted. - If no baseline is provided, roots become the allowed directories.
- If CLI and/or
- If nothing is configured and the client provides no roots, the server starts with no accessible directories and logs a warning until roots are provided.
Notes:
- Windows drive-relative paths like
C:pathare rejected. UseC:\pathorC:/path. - Reserved Windows device names (e.g.,
CON,NUL) are blocked.
Configuration
All configuration is optional. Sizes in bytes, timeouts in milliseconds.
Environment Variables
| Variable | Default | Description |
|---|---|---|
MAX_FILE_SIZE | 10MB | Max file size for read operations (range: 1MB-100MB) |
MAX_READ_MANY_TOTAL_SIZE | 512KB | Max combined size for read_many (range: 10KB-100MB) |
MAX_SEARCH_SIZE | 1MB | Max file size for content search (range: 100KB-10MB) |
DEFAULT_SEARCH_TIMEOUT | 30000 | Timeout for search/list operations (range: 100-3600000ms) |
FS_CONTEXT_SEARCH_WORKERS | min(cpu cores, 8) | Search worker threads (range: 0-16; 0 disables) |
See for examples and CLI usage.
Resources
This server exposes standard MCP resources to provide static documentation and handle large content efficiently.
| Resource URI | Description |
|---|---|
internal://instructions | Returns the detailed usage instructions (Markdown) for this server. |
fs-context://result/{id} | Access to large file content or search results that were truncated in tool outputs. |
Note on Large Outputs:
Tools like read, read_many, and grep automatically cache content exceeding value limits (default 20k chars). In these cases, the tool returns a preview and a resource_link (URI) that can be read by the client to retrieve the full content.
Tools
All tools return both human-readable text and structured JSON. Structured
responses include ok, optional error (with code, message, path,
suggestion), plus the tool-specific fields documented below.
roots
List all directories that this server can access.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| (none) | - | - | - | - |
Returns: Allowed directory paths. Structured output includes ok and
directories.
ls
List the immediate contents of a directory (non-recursive). Omit path to use
the first allowed root.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path | string | No | first root | Directory path to list (omit to use first root) |
includeHidden | boolean | No | false | Include hidden files and directories |
Returns: Entries with name, relativePath, type, size, and modified time.
Structured output includes ok, path, entries, and totalEntries.
find
Search for files using glob patterns. Omit path to search from the first
allowed root. By default, find excludes common dependency/build directories
(node_modules, dist, .git, etc.); set includeIgnored: true to include ignored
directories and disable built-in excludes.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path | string | No | first root | Base directory to search from (omit to use first root) |
pattern | string | Yes | - | Glob pattern (e.g., **/*.ts, src/**/*.js) |
includeIgnored | boolean | No | false | Include ignored dirs and disable built-in excludes |
maxResults | number | No | 100 | Maximum matches to return (1-10000) |
Notes:
- When
includeIgnored=false, results also respect a root.gitignorefile (if present under the basepath).
Returns: Matching paths (relative) with size and modified date. Structured
output includes ok, results, totalMatches, and truncated.
tree
Render a directory tree (bounded recursion). Omit path to use the first
allowed root.
path(string, optional; default:first root): Base directory to rendermaxDepth(number, optional; default:5): Maximum recursion depth (0 = just the root)maxEntries(number, optional; default:1000): Maximum number of entries before truncatingincludeHidden(boolean, optional; default:false): Include hidden files/directoriesincludeIgnored(boolean, optional; default:false): Include ignored dirs and disable built-in +.gitignorefiltering
Notes:
- When
includeIgnored=false, the tree respects both built-in ignore rules (e.g.,node_modules,dist,.git) and a root.gitignorefile (if present).
Returns: ASCII tree output plus a structured JSON tree (ok, root, tree,
ascii, truncated, totalEntries).
read
Read the contents of a text file.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path | string | Yes | - | File path to read |
head | number | No | - | Read only first N lines |
startLine | number | No | - | 1-based start line (inclusive) |
endLine | number | No | - | 1-based end line (inclusive) |
Notes:
- Reads are UTF-8 text only; binary files are rejected.
- Full reads are capped by
MAX_FILE_SIZE(default 10MB). Whenheadis set, output stops at the line limit or size budget, whichever comes first. headcannot be combined withstartLine/endLine.- If the content exceeds a size limit (default 20k chars), the tool returns a
resource_linkinstead of inline content.
Returns: File content plus structured metadata (ok, path, content,
truncated, totalLines, and range metadata when applicable).
read_many
Read multiple files in parallel.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
paths | string[] | Yes | - | Array of file paths (max 100) |
head | number | No | - | Read only first N lines of each file |
startLine | number | No | - | 1-based start line (inclusive) per file |
endLine | number | No | - | 1-based end line (inclusive) per file |
Notes:
- Reads files as UTF-8 text; binary files are not filtered. Max size per file
is capped by
MAX_FILE_SIZE(default 10MB). - Total read budget across all files is capped by
MAX_READ_MANY_TOTAL_SIZE. - No binary detection is performed; use
readfor single-file safety checks. headcannot be combined withstartLine/endLine.- If any file content exceeds the inline limit, it is returned as a
resource_link.
Returns: Per-file content or error, plus structured summary (total,
succeeded, failed).
stat
Get 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, timestamps (created/modified/accessed),
permissions, hidden status, MIME type (for files), and symlink target (if
applicable). Structured results may include tokenEstimate (rule of thumb:
ceil(size/4)).
stat_many
Get metadata for multiple files/directories in parallel.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
paths | string[] | Yes | - | Array of paths to query (max 100) |
Returns: Array of file info with individual success/error status, plus summary (total, succeeded, failed).
grep
Search for text content within files.
- Omit
pathto search from the first allowed root. - Pass a file path in
pathto search only that file.
pattern is treated as a literal string and matched case-insensitively.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path | string | No | first root | Base directory or file path to search in |
pattern | string | Yes | - | Text pattern to search for |
includeHidden | boolean | No | false | Include hidden files and directories |
Example (search a single file):
{ "path": "src/transform.ts", "pattern": "TODO" }
Returns: Matching lines with file path, line number, content, and optional context.
Notes:
grepskips binary files by default.- Very large files are skipped based on
MAX_SEARCH_SIZE(default 1MB). “No matches” is not proof the text is absent from skipped files.
Note: the grep tool currently exposes only path, pattern, and
includeHidden. Context fields are omitted unless enabled internally.
Structured output includes ok, matches, totalMatches, and truncated.
Matched line content is trimmed to 200 characters.
Built-in exclude list: grep skips 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.
Error Codes
| 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 |
Client Configuration
VS Code
Add to .vscode/mcp.json (recommended) or .vscode/settings.json:
{
"servers": {
"fs-context": {
"command": "npx",
"args": ["-y", "@j0hanz/fs-context-mcp@latest", "${workspaceFolder}"]
}
}
}
Claude Desktop
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"fs-context": {
"command": "npx",
"args": ["-y", "@j0hanz/fs-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.
Cursor
Add to Cursor's MCP configuration:
{
"mcpServers": {
"fs-context": {
"command": "npx",
"args": ["-y", "@j0hanz/fs-context-mcp@latest", "${workspaceFolder}"]
}
}
}
Codex
Add to ~/.codex/config.toml:
[mcp_servers.fs-context]
command = "npx"
args = ["-y", "@j0hanz/fs-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.
Windsurf
Add to Windsurf's MCP configuration:
{
"mcpServers": {
"fs-context": {
"command": "npx",
"args": ["-y", "@j0hanz/fs-context-mcp@latest", "${workspaceFolder}"]
}
}
}
Security Details
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 |
Development
Prerequisites
- Node.js >= 20.0.0
- npm
Scripts
| 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 inspector | Test with MCP Inspector |
Project Structure
src/
index.ts # CLI entry point
server.ts # MCP server wiring and roots handling
tools.ts # MCP tool registration + response helpers
schemas.ts # Zod input/output schemas
config.ts # Shared types and formatting helpers
instructions.md # Tool usage instructions (bundled in dist)
lib/ # Core logic and filesystem operations
__tests__/ # node:test + tsx tests
node-tests/ # Additional Node.js checks
docs/ # Static docs assets
dist/ # Build output (generated)
Troubleshooting
| Issue | Solution |
|---|---|
| "Access denied" error | Ensure the path is within an allowed directory. Use roots to check. |
| "Path does not exist" | Verify the path exists. Use ls to explore available files. |
| "File too large" | Use head or increase MAX_FILE_SIZE. |
| "Binary file" warning | read only supports UTF-8 text and rejects binary files. |
| No directories available | Pass explicit paths, use --allow-cwd, or ensure the client provides Roots. |
| Symlink blocked | Symlinks that resolve outside allowed directories are blocked. |
| Invalid pattern | Simplify the pattern (note: grep treats pattern as literal text). |
Contributing
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Run format, lint, type-check, build, and tests (
npm run format && npm run lint && npm run type-check && npm run build && 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
Code Style
- Use TypeScript with strict mode
- Follow ESLint configuration
- Use Prettier for formatting
- Write tests for new features