什么是 Filesystem MCP?
让 LLM 通过 MCP Server 与本地 filesystem 交互,可读取、浏览并处理文件与目录。
README
Filesystem MCP Server
Overview
Filesystem-MCP is a Model Context Protocol server that lets AI assistants read and write files within explicitly allowed directories. Sensitive file patterns (.env, *.pem, *id_rsa*) are blocked by default. It exposes filesystem tools, resources, and prompts over stdio or Streamable HTTP transport.
| Aspect | Details |
|---|---|
| Status | Active (see npm badge for the current version) |
| Language | TypeScript (strict) |
| Runtime | Node.js >= 24 |
| Package | npm |
| License | MIT |
Features
| Feature | Description |
|---|---|
| Path guarding | Every path is validated against allowed roots; .env, *.pem, *id_rsa* and similar patterns are denied |
| Filesystem tools | Navigate, inspect, read, and write across all major file operations |
| Batch operations | Most tools accept path, paths[], or files[] for parallel execution |
| Dual transport | stdio by default; --port enables Streamable HTTP |
| File subscriptions | Resource subscriptions push change notifications when watched files update |
| Regex safety | RE2 in all search tools: linear-time matching, so no pattern can ReDoS the server |
Built with
| Layer | Technology |
|---|---|
| Protocol | MCP SDK v2 (@modelcontextprotocol/server) |
| Runtime | Node.js >= 24 · TypeScript 6 · ESM |
| Transport | stdio (default) · Streamable HTTP (--port) |
| Regex | RE2 (re2-wasm) — linear time, no lookahead/lookbehind/backreferences |
| Container | Docker alpine · multi-stage build · non-root user |
Table of Contents
Quick start
[!NOTE] Requires Node.js ≥ 24.
Prerequisites
| Requirement | Version / Notes |
|---|---|
| Node.js | ≥ 24 |
| npm | Bundled with Node.js |
| Docker | Optional — for container use |
Install via npx
npx -y @j0hanz/filesystem-mcp /path/to/allowed/dir
Or install globally:
npm install -g @j0hanz/filesystem-mcp
filesystem-mcp /path/to/allowed/dir
Install via Docker
docker run -i --rm \
-v /path/to/project:/workspace:ro \
ghcr.io/j0hanz/filesystem-mcp:latest \
--read-only /workspace
Configure in VS Code
Add to .vscode/mcp.json:
{
"servers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@j0hanz/filesystem-mcp@latest", "/path/to/project"]
}
}
}
Or install via CLI:
code --add-mcp '{"name":"filesystem","command":"npx","args":["-y","@j0hanz/filesystem-mcp@latest","/path/to/project"]}'
Configure in Visual Studio
Add to .vs\mcp.json in your solution directory, or %USERPROFILE%\.mcp.json for a global configuration:
{
"servers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@j0hanz/filesystem-mcp@latest", "/path/to/project"]
}
}
}
Configure in Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@j0hanz/filesystem-mcp@latest", "/path/to/project"]
}
}
}
Install in Cursor
Add to .cursor/mcp.json in your project root (project-scoped), or ~/.cursor/mcp.json for a global configuration:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@j0hanz/filesystem-mcp@latest", "/path/to/project"]
}
}
}
Docker configuration
VS Code (.vscode/mcp.json) and Visual Studio (.vs\mcp.json):
{
"servers": {
"filesystem": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-v",
"/path/to/project:/workspace",
"ghcr.io/j0hanz/filesystem-mcp:latest",
"/workspace"
]
}
}
}
Claude Desktop (claude_desktop_config.json) and Cursor (mcp.json):
{
"mcpServers": {
"filesystem": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-v",
"/path/to/project:/workspace",
"ghcr.io/j0hanz/filesystem-mcp:latest",
"/workspace"
]
}
}
}
[!NOTE] For least privilege, use both controls:
:romakes the container mount read-only at the operating-system boundary, while the server's--read-onlyflag removes mutating tools (create,edit,move,delete,patch,replace_text) fromtools/list.
Usage
Tools
All tools are scoped to the configured roots. Call list_roots first to discover what is allowed.
Navigate
| Tool | Description |
|---|---|
list_roots | List allowed workspace roots. Call this first — all other tools scope to these. |
list | List directory contents. Returns entries (dirs-first, alphabetical) and an ASCII tree. |
find_files | Find files by glob pattern (e.g. **/*.ts). Returns matching files with metadata. |
Inspect
| Tool | Description |
|---|---|
stat | Get file/directory metadata: size, modified time, permissions, MIME type, token estimate. |
search_text | Search file contents for text (grep-like). Returns matching lines with context. |
diff | Compare two files and return a unified diff with added/removed line counts. |
Read
| Tool | Description |
|---|---|
read | Read a text file. Supports head/tail and line ranges. Accepts paths[] for batches. |
Write
| Tool | Description |
|---|---|
create | Create one or more files, overwriting existing content and creating parent directories as needed. |
edit | Apply sequential literal string replacements to one or more files (max 5 per call). |
move | Move, rename, or copy (copy: true) one or more files/directories to explicit destinations. |
delete | Permanently delete one or more files or directories. This action is irreversible. |
replace_text | Bulk search-and-replace across files matching a glob pattern. |
patch | Apply a single-file unified diff and write the result. |
Resources
| URI | Description |
|---|---|
internal://instructions | Server navigation guide — tools overview, constraints, and error recovery. |
filesystem-mcp://file/{+path} | Read a workspace file. Subscribe to receive push notifications on change. |
filesystem-mcp://result/{id} | Ephemeral cached tool output. Expires after ~60 seconds, eviction, or server restart. |
Prompts
| Prompt | Description |
|---|---|
get-help | Return usage instructions, optionally filtered to a specific section. |
Project structure
filesystem-mcp/
├── __tests__/ Test suites
├── scripts/ Build and task utilities
├── src/
│ ├── core/ Path guarding, filesystem abstraction, concurrency, observability
│ ├── tools/ Tool definitions and registration
│ ├── index.ts Process entrypoint and transport selection
│ ├── server.ts Server factory and registrar composition
│ ├── transport/ stdio and Streamable HTTP transport setup
│ ├── prompts.ts Prompt definitions and registration
│ └── resources.ts Resource definitions and registration
└── Dockerfile Multi-stage alpine build, non-root user
Runtime composition flows from src/index.ts to src/transport.ts, then to
src/server.ts, the registrars, and finally src/core/. Each registrar owns
the narrow dependency contract it consumes.
| Path | Purpose |
|---|---|
src/core/path.ts | PathGuard — validates every path against allowed roots |
src/core/fs.ts | GuardedFileSystem — guarded filesystem facade |
src/tools/define.ts | Tool registration and execution framework |
src/tools/batch.ts | Batch helpers (runOverPaths, normalizeBatchItems) |
src/server.ts | Builds shared dependencies and invokes the three registrars |
src/transport.ts | Owns stdio and Streamable HTTP setup around the server factory |
Configuration
The server starts with allowed directories from explicit startup configuration:
- Positional directories passed to
filesystem-mcp. - Environment variable
FS_ALLOWED_DIRS(separated by:on POSIX or;on Windows). - Current working directory when
--allow-cwdis enabled.
Legacy MCP connections may additionally seed roots through the deprecated
roots/list flow. Modern 2026-07-28 connections do not automatically send
workspace roots. They can add access after startup by calling a tool with a
concrete path and approving the elicitation-backed grant. list_roots reports
the roots already configured or accepted; it cannot discover an unknown
workspace by itself.
Recommended global recipes
VS Code / Cursor / Claude Code (primary recipe)
Configure the project directory explicitly:
Add to your global or project-scoped configuration:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@j0hanz/filesystem-mcp@latest", "/path/to/project"]
}
}
}
Claude Desktop (fallback recipe via environment variable)
Claude Desktop and similar clients don't support the MCP Roots protocol. Use the FS_ALLOWED_DIRS environment variable to configure allowed folders.
Add to your claude_desktop_config.json:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@j0hanz/filesystem-mcp@latest"],
"env": {
"FS_ALLOWED_DIRS": "/path/to/project1:/path/to/project2"
}
}
}
}
(On Windows, separate directories with a semicolon ; instead of a colon :).
Advanced / per-project positional arguments
You can also restrict access to specific directories by passing positional arguments directly:
# Start with explicit positional paths
filesystem-mcp /path/to/project1 /path/to/project2
Configuration reference
CLI flags
| Flag | Default | Purpose |
|---|---|---|
[dirs...] | — | One or more allowed root directories (positional) |
--allow-cwd | false | Also allow the current working directory as a root |
--walk-cwd | false | Walk up from CWD to find a project root; implies --allow-cwd |
--allow-missing-roots | false | Start even if configured allowed directories do not exist |
--port <n> | — | Enable Streamable HTTP transport on the given port (env: FS_PORT) |
--http-host <host> | — | HTTP server bind address (env: FS_HTTP_HOST) |
--api-key <key> | — | Require this API key on HTTP requests (env: FS_API_KEY) |
--read-only | false | Disable write tools: create, edit, delete, move, patch, replace_text |
--safe | false | Alias for --read-only |
--deny <pattern> | — | Block paths matching this pattern; repeatable |
--allow-sensitive | false | Allow access to sensitive system paths (env: FS_ALLOW_SENSITIVE) |
--root-boundary <path> | — | Require all allowed roots to fall under this path (env: FS_ROOT_BOUNDARY) |
--max-file-size <bytes> | — | Maximum file size for reads in bytes (env: FS_MAX_FILE_SIZE) |
--log-level <level> | info | RFC 5424 log level, debug through emergency (env: FS_LOG_LEVEL) |
--print-config | false | Print the active configuration and exit (use --json for machine-readable output) |
--json | false | Output --print-config as JSON |
Environment variables
All boolean variables accept true or 1 to enable and false, 0, or
unset to disable; any other value logs a warning and reads as disabled.
Flags take precedence when both are set.
| Variable | Purpose |
|---|---|
FS_ALLOWED_DIRS | Colon-separated (POSIX) or semicolon-separated (Windows) list of directories to allow. |
FS_ROOT_BOUNDARY | Path prefix all allowed roots must fall under (mirrors --root-boundary). |
FS_ALLOW_CWD_WALK | Walk up from CWD to find a project root (mirrors --walk-cwd). |
FS_ALLOW_MISSING_ROOTS | Start even if configured directories do not exist (mirrors --allow-missing-roots). |
FS_ALLOW_SENSITIVE | Allow access to sensitive system paths (mirrors --allow-sensitive). |
FS_DENYLIST | Comma-separated list of paths or patterns to block (mirrors --deny). |
FS_MAX_FILE_SIZE | Maximum file size for reads in bytes (mirrors --max-file-size). |
FS_LOG_LEVEL | RFC 5424 log level: debug, info, notice, warn/warning, error, critical, alert, or emergency (mirrors --log-level). |
FS_PORT | Start the Streamable HTTP transport on this port; unset = stdio (mirrors --port). |
FS_HTTP_HOST | HTTP server bind address (mirrors --http-host). |
FS_API_KEY | API key required on HTTP requests (mirrors --api-key). |
FS_TRUST_PROXY | Express trust proxy setting: hop count or expression. Unset = do not trust X-Forwarded-*. |
FS_ALLOWED_HOSTS | Comma-separated Host header values to accept (HTTP transport). |
FS_ALLOWED_ORIGINS | Comma-separated origin hostnames for CORS. |
FS_ALLOW_UNRESTRICTED_HOSTS | Bind a wildcard host with no Host validation (accepts the risk). |
FS_PUBLIC_URL | Resource identifier URL for RFC 9728 discovery. |
FS_RATE_LIMIT_RPM | Per-client-IP requests/minute (default 120 with API-key authentication, 6,000 for keyless loopback; range 1–100000). |
FS_MAX_REQUEST_BYTES | Max HTTP request body bytes (default 4194304, 1024–268435456). |
FS_KEEPALIVE_TIMEOUT_MS | HTTP keep-alive timeout in ms; set above any fronting proxy's idle timeout (default 5000, 1000–600000). |
FS_MAX_WATCHERS | Max concurrent file watchers (default 256, 1–4096). |
FS_MAX_INLINE_MATCHES | Max inline content matches per search (default 50, 1–10000). |
FS_MAX_READ_MANY_BYTES | Max total bytes across a batched read (default 524288, 10240–104857600). |
FS_SEARCH_TIMEOUT_MS | Search timeout in ms (default 5000, 100–60000). |
NO_COLOR | Any value disables ANSI color output. |
FS_REQUEST_STATE_KEY | HMAC key sealing input_required requestState across retry rounds. Optional for stdio and single-instance HTTP (random per boot if unset); mandatory and shared across every fleet instance (UTF-8, >=32 bytes). |
Multi-instance HTTP deployments
Each instance delivers subscriptions/listen change events
(resources/updated, tools/list_changed, etc.) on an in-process bus by
default. Behind a load balancer with more than one instance, a listener on
instance A will not see an event published on instance B. Explicit fleet mode
therefore refuses to boot without a shared event bus.
To fan events out across instances, implement the SDK's ServerEventBus
interface (two methods: publish/subscribe) over whatever pub/sub you
already run, then pass it to filesystem-mcp's programmatic HTTP entry:
import type { ServerEvent, ServerEventBus } from '@modelcontextprotocol/server';
import { startHttpServer } from '@j0hanz/filesystem-mcp/transport';
import Redis from 'ioredis';
// any pub/sub client works the same way
class RedisServerEventBus implements ServerEventBus {
private readonly listeners = new Set<(event: ServerEvent) => void>();
private readonly pub = new Redis(process.env['REDIS_URL']);
private readonly sub = new Redis(process.env['REDIS_URL']);
constructor() {
void this.sub.subscribe('fs-mcp-events');
this.sub.on('message', (_channel, message) => {
const event = JSON.parse(message) as ServerEvent;
for (const listener of this.listeners) listener(event);
});
}
publish(event: ServerEvent): void {
void this.pub.publish('fs-mcp-events', JSON.stringify(event));
}
subscribe(listener: (event: ServerEvent) => void): () => void {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
}
const eventBus = new RedisServerEventBus();
const apiKey = process.env['FS_API_KEY'];
if (!apiKey) throw new Error('FS_API_KEY is required for a multi-instance HTTP deployment');
await startHttpServer(
3000,
{ cliAllowedDirs: ['/workspace'] },
{ apiKey, eventBus, deploymentMode: 'fleet' },
);
This project ships no bus adapter and no pub/sub dependency. A single
in-process instance (the common case) needs nothing extra and is the CLI's
default. Load-balanced deployments must use the programmatic API with
deploymentMode: 'fleet'.
Examples
# Allow current working directory
filesystem-mcp --allow-cwd
# HTTP transport on port 3000
filesystem-mcp --port 3000
Scripts
| Mode | Command | Description |
|---|---|---|
| Full check | node scripts/tasks.mjs | Run build, type check, lint, format, knip, and tests |
| Auto-fix + check | node scripts/tasks.mjs fix | Auto-fix formatting/linting and run the full check |
| Static only | node scripts/tasks.mjs --quick | Run static analysis without tests |
| Tests only | node scripts/tasks.mjs test | Run tests; accepts native node --test options |
Security
[!IMPORTANT] Report vulnerabilities privately via GitHub Security Advisories. Do not open public issues for security reports.
| Topic | Detail |
|---|---|
| Path traversal | Every path is resolved and validated against allowed roots before any operation |
| Sensitive files | .env, *.pem, *id_rsa*, and similar patterns are denied by default |
| Regex safety | RE2 cannot backtrack, so a hostile pattern cannot hang the server (ReDoS) |
| Container | Runs as non-root mcp user; bind mounts control what is exposed |
Contributing
- Fork the repository.
- Create a feature branch:
git checkout -b feat/your-feature. - Commit your changes with a clear message.
- Run
node scripts/tasks.mjsto confirm tests, types, lint, formatting, and knip all pass. - Open a pull request.
License
Released under the MIT License. See LICENSE for details.
常见问题
Filesystem MCP 是什么?
让 LLM 通过 MCP Server 与本地 filesystem 交互,可读取、浏览并处理文件与目录。
相关 Skills
MCP构建
by anthropics
聚焦高质量 MCP Server 开发,覆盖协议研究、工具设计、错误处理与传输选型,适合用 FastMCP 或 MCP SDK 对接外部 API、封装服务能力。
✎ 想让 LLM 稳定调用外部 API,就用 MCP构建:从 Python 到 Node 都有成熟指引,帮你更快做出高质量 MCP 服务器。
Slack动图
by anthropics
面向Slack的动图制作Skill,内置emoji/消息GIF的尺寸、帧率和色彩约束、校验与优化流程,适合把创意或上传图片快速做成可直接发送的Slack动画。
✎ 帮你快速做出适配 Slack 的动图,内置约束规则和校验工具,少踩上传与播放坑,做表情包和演示都更省心。
接口测试套件
by alirezarezvani
扫描 Next.js、Express、FastAPI、Django REST 的 API 路由,自动生成覆盖鉴权、参数校验、错误码、分页、上传与限流场景的 Vitest 或 Pytest 测试套件。
✎ 帮你把API与集成测试自动化跑顺,减少回归漏测;能力全面,尤其适合复杂接口场景的QA团队。
相关 MCP Server
Slack 消息
编辑精选by Anthropic
Slack 是让 AI 助手直接读写你的 Slack 频道和消息的 MCP 服务器。
✎ 这个服务器解决了团队协作中需要 AI 实时获取 Slack 信息的痛点,特别适合开发团队让 Claude 帮忙汇总频道讨论或发送通知。不过,它目前只是参考实现,文档有限,不建议在生产环境直接使用——更适合开发者学习 MCP 如何集成第三方服务。
by netdata
io.github.netdata/mcp-server 是让 AI 助手实时监控服务器指标和日志的 MCP 服务器。
✎ 这个工具解决了运维人员需要手动检查系统状态的痛点,最适合 DevOps 团队让 Claude 自动分析性能数据。不过,它依赖 NetData 的现有部署,如果你没用过这个监控平台,得先花时间配置。
by d4vinci
Scrapling MCP Server 是专为现代网页设计的智能爬虫工具,支持绕过 Cloudflare 等反爬机制。
✎ 这个工具解决了爬取动态网页和反爬网站时的头疼问题,特别适合需要批量采集电商价格或新闻数据的开发者。不过,它依赖外部浏览器引擎,资源消耗较大,不适合轻量级任务。