MCP Spotlight: Anthropic Filesystem MCP Server — Sandboxed Read/Write, Allow-Listed Directories, and the Reference Architecture for File Access
Server: @modelcontextprotocol/server-filesystem by Anthropic
License: MIT · Status: Reference implementation · Transport: stdio
Tools: ~14 (read, write, edit, move, search, directory ops)
Security model: Explicit --allowed-directory allow-list · Per-call path validation · Symlink resolution check
GitHub: github.com/modelcontextprotocol/servers/tree/main/src/filesystem
MCP Tracker: glama.ai/mcp/servers/modelcontextprotocol/filesystem
Every coding agent needs to read, write, and edit files. The "give the agent full disk access" approach is reckless. The "give the agent nothing" approach blocks every useful workflow. The "give the agent a sandbox and let it escape later" approach is the worst of both worlds.
Anthropic's official Filesystem MCP Server is the reference implementation for safe file access in the MCP ecosystem. It enforces a strict directory allow-list at server startup, validates every path against the allow-list on every call, resolves symlinks before access, and refuses any operation outside the sandbox. It ships with ~14 tools covering the full file lifecycle: read, write, edit, move, copy, delete, search, directory operations, and metadata.
This is the server every other "filesystem-like" MCP server should be compared against. It's also the architectural template for any local-resource MCP server where the safety model is "the agent can only touch what we explicitly allow."
The Security Model: Allow-Listed Directories
The defining feature of the Filesystem MCP Server is the per-invocation directory allow-list. When you start the server, you pass one or more --allowed-directory arguments. Those directories — and only those directories — are accessible:
# Allow only the workspace directory
mcp-server-filesystem --allowed-directory /home/user/projects/myapp
# Allow multiple directories
mcp-server-filesystem \
--allowed-directory /home/user/projects/myapp \
--allowed-directory /home/user/.config/myapp
# Try to access /etc/passwd → rejected
Every tool call validates the path against the allow-list after symlink resolution. This prevents the common bypass:
/home/user/projects/myapp/safe → /etc/passwd # symlink
The server resolves safe, sees it points to /etc/passwd, sees /etc/passwd is not in the allow-list, and rejects the call. Symlink escape attempts are caught at the boundary.
For multi-tenant or multi-project setups, the allow-list enforces strict isolation. One MCP server per workspace, each with its own allow-list, each with its own audit trail.
The Tools: 14 Operations
The Filesystem MCP Server exposes the full file lifecycle:
Read operations
| Tool | What It Does |
|---|---|
read_file | Read a file, optionally with offset and length |
read_multiple_files | Read several files in one call (efficient batching) |
read_text_file | Read a text file with explicit encoding handling |
read_image_file | Read an image file (returns base64 for vision-capable agents) |
list_directory | List directory contents with metadata |
list_directory_with_sizes | List with file sizes |
directory_tree | Recursive tree view of a directory |
search_files | Glob-based file search within allow-list |
get_file_info | File metadata (size, mtime, permissions, MIME) |
Write operations
| Tool | What It Does |
|---|---|
write_file | Create or overwrite a file |
edit_file | Find-and-replace edit (safe for partial updates) |
create_directory | Create a directory (with parent dirs) |
move_file | Move/rename a file (within allow-list) |
Destructive operations
| Tool | What It Does |
|---|---|
delete_file | Delete a file (with allow-list validation) |
The edit_file tool is the underrated gem. Instead of overwriting a whole file (write_file), edit_file does a structured find-and-replace. The agent says "change line 42 from const PORT = 3000 to const PORT = 8080" and the server does exactly that, with diff validation. Safer than full-file overwrites, faster than reading-then-writing.
The read_multiple_files Pattern
A common agent inefficiency: calling read_file 10 times in a row to gather context. Each call is a roundtrip, each response is parsed separately. The read_multiple_files tool batches the operation:
read_multiple_files([
"/home/user/projects/myapp/package.json",
"/home/user/projects/myapp/tsconfig.json",
"/home/user/projects/myapp/src/index.ts",
"/home/user/projects/myapp/src/config.ts"
])
One tool call. One response. The agent gets all four files in context. Lower latency, lower token overhead, lower audit-trail noise.
For agents doing code review, this is the standard pattern: pull the relevant files in one batch, analyze the cross-file relationships, surface the findings. The Filesystem MCP Server is built for this loop.
The edit_file Pattern: Surgical Edits
The edit_file tool is the right primitive for code changes:
{
"path": "/home/user/projects/myapp/src/index.ts",
"edits": [
{
"oldText": "const PORT = 3000",
"newText": "const PORT = process.env.PORT || 8080"
}
]
}
The server validates that oldText exists exactly once in the file. If it appears 0 times, the edit fails (the agent's context is stale). If it appears multiple times, the edit fails (ambiguous). The agent can only make changes it can uniquely identify.
For HITL workflows, this is the safety layer that complements Facio's destructive-operation gating. Even if the agent is compromised, the edits it makes are constrained to text it can find exactly once in the file.
For multi-edit operations:
{
"path": "src/index.ts",
"edits": [
{ "oldText": "old A", "newText": "new A" },
{ "oldText": "old B", "newText": "new B" },
{ "oldText": "old C", "newText": "new C" }
]
}
One tool call, multiple atomic edits, single audit-trail entry. The agent can refactor across the file in one operation without the inconsistency risk of multiple sequential edits.
The search_files Tool: Glob Within the Sandbox
The search_files tool does glob-based search inside the allow-list:
search_files(pattern="**/*.test.ts", path="/home/user/projects/myapp/src")
search_files(pattern="**/package.json", path="/home/user/projects/myapp")
search_files(pattern="src/**/*.config.{js,ts}", path="/home/user/projects/myapp")
The result is a list of matching paths. The agent can then read_multiple_files on the matches, getting a batched context for analysis. Glob search + batched read is the standard code-analysis workflow.
For large codebases, the glob pattern keeps the search scope tight. The agent can't accidentally walk the entire filesystem because every search is bounded by the allow-list.
Facio Integration
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"--allowed-directory",
"/data/facio-1/.facio/workspace/projects/myapp"
]
}
}
}
Facio's audit trail captures every file operation with the tool, the path (validated against the allow-list), the diff (for edits), and the size/checksum (for reads/writes). For a regulated team (SOC2, ISO 27001), this is the complete file-access record: "Agent read 4 files at 14:32 UTC, edited src/index.ts to add a PORT env var, wrote 2 new test files."
For HITL workflows, the tool annotations map to gate requirements:
| Tool | Severity | Suggested Gate |
|---|---|---|
read_*, list_*, search_files, get_file_info | Read | None — autonomous |
read_image_file | Read (large blob) | Soft confirm for >10MB |
write_file (new file) | Write, idempotent | Soft confirm for files >100KB |
write_file (overwrite existing) | Write, destructive in effect | Hard confirm if the existing file >10KB |
edit_file | Write, idempotent | Soft confirm (review the diff) |
move_file, create_directory | Write, contextual | Soft confirm |
delete_file | Write, destructive | Hard confirm + reason required (irreversible) |
The write_file tool deserves special attention — overwriting an existing file is a destructive operation in effect. Facio's destructive-hint annotation should require explicit human approval for any write_file where the target exists and is non-trivial in size.
For multi-workspace setups (one project per team, per customer, per environment), the pattern is one MCP server per workspace, each with its own --allowed-directory, each with its own audit trail, each with its own HITL gating. The agent switches context per workspace, the audit trail is per-workspace, and the destructive gating is per-workspace.
The environment variable + credential injection pattern is also worth highlighting: instead of passing the directory directly, you can pass ${credentials.WORKSPACE_PATH} and resolve the path at startup. This lets the same MCP server config work across multiple workspaces by swapping the credential.
Quickstart
# 1. Install the Filesystem MCP Server
npm install -g @modelcontextprotocol/server-filesystem
# 2. Configure with allowed directory
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"--allowed-directory",
"/data/facio-1/.facio/workspace/projects/myapp"
]
}
}
}
# 3. First prompts
# "List the top-level files in my workspace"
# "Read package.json, tsconfig.json, and src/index.ts"
# "Add a PORT env var to src/index.ts with default 8080"
# "Find all .test.ts files in src/ and tell me the count"
# "Create a new file src/utils/logger.ts with a basic Winston setup"
Use Cases
Code review: "Read all .ts files in src/ that were modified in the last 7 days, summarize the changes, flag any that look suspicious." Batch read + analysis + report.
Refactoring: "Find every file that imports the deprecated request library and replace it with fetch. Show me the diff before applying." Glob search → batch read → edit_file per file.
Project bootstrapping: "Create the initial directory structure for a Next.js 15 app with the App Router, TypeScript, and Tailwind. Show me the tree before creating." Plan → confirm → batch directory + file creation.
Config audit: "List all .env* files and check that no secrets are committed. Surface anything sensitive." Glob → read → secret detection → report.
Documentation generation: "For each .ts file in src/, generate a JSDoc comment block. Apply the edits and show me the diff." Batch read → JSDoc synthesis → batch edit.
File watching: "Watch the src/ directory for changes, and for any new .ts file, run the linter and report issues." The Filesystem MCP doesn't watch, but combined with a cron job + list_directory, the agent can do periodic audits.
Backup and archive: "Find all files in the workspace older than 1 year, list them with sizes, and create a tar archive." Glob → metadata → tar via move_file or external command.
Cleanup: "Find all node_modules directories and report their total size. Don't delete them — just give me the report." Glob → size aggregation → summary.
File integrity: "Generate SHA-256 checksums for all source files. Save to a checksums.txt file." Batch read → hash → write_file.
Multi-file edit: "Rename the function getUserById to findUserById across the entire codebase. Update imports too." Search → batch edit → verify.
The Reference Architecture for Local-Resource MCP Servers
The Filesystem MCP Server is more than just one server. It's the architectural template for any MCP server that exposes local resources. The pattern is:
- Allow-list at startup — explicit list of resources the server can access
- Per-call validation — every operation validates against the allow-list
- Symlink resolution — resolve symlinks before validation to prevent escape
- Atomic operations — multi-edit and batch-read as first-class tools
- Structured audit hooks — every operation has clear inputs, outputs, and metadata
- MIT license — reference implementation that forks can extend
The same pattern applies to:
- Database MCP servers — allow-list of databases, per-query role validation
- Docker MCP servers — allow-list of containers/images, per-call capability check
- Kubernetes MCP servers — allow-list of namespaces, per-call RBAC check
- Shell MCP servers — allow-list of commands, per-call argument validation
- Git MCP servers — allow-list of repositories, per-call ref validation
Every "agent can do X locally" MCP server should follow the Filesystem MCP Server's allow-list pattern. The agent never gets implicit access to everything; it gets explicit access to what the operator allow-listed.
Bottom Line
The Filesystem MCP Server is the reference implementation for safe file access in the MCP ecosystem. ~14 tools, MIT-licensed, maintained by Anthropic, and built on the allow-list + symlink-resolution security model that every local-resource MCP server should copy.
For any agent that needs to read, write, or edit files — code review, refactoring, project bootstrapping, documentation generation, config audit — this is the bridge. The agent works in a strict sandbox, every operation is validated, every access is logged, every destructive operation can be gated by Facio.
For the broader MCP ecosystem, the Filesystem MCP Server is the design template for any local-resource MCP server. The allow-list is the boundary. The validation is the safety net. The audit trail is the evidence. Every agent-driven file operation should be this disciplined.
npx -y @modelcontextprotocol/server-filesystem --allowed-directory /path/to/workspace and your agent has sandboxed file access.
MCP Spotlight is a series covering servers that give AI agents real capabilities. Every server is evaluated for security design, ecosystem fit, and integration fit with Facio's HITL-first agent runtime.