Problem: AI Agents Writing Files Outside Your Project Directory
You've connected an AI assistant to your filesystem via MCP and it writes a config file to ~/ instead of ./project. Or worse — it overwrites something in a directory you never intended to expose.
The MCP Filesystem Server has a solid sandbox model, but the default setup lets you shoot yourself in the foot if you don't pin the allowed directories correctly.
You'll learn:
- How to restrict the server to specific allowed directories (read-write and read-only)
- The difference between
read_file,write_file, and destructive tools — and which to expose - How to wire it up in Claude Desktop, VS Code, and Docker with safe mounts
Time: 15 min | Difficulty: Intermediate
Why This Happens
The MCP Filesystem Server grants access to any directory you pass as a command-line argument. If you pass /Users/you instead of /Users/you/projects/myapp, the AI can touch your entire home directory — dotfiles, SSH keys, everything.
There's no write-permission flag per directory at the CLI level in the official Node.js server (@modelcontextprotocol/server-filesystem). Read-only enforcement requires either the Docker ro mount flag or a wrapper config.
Symptoms of a misconfigured setup:
- AI creates files in
~or/tmpinstead of your project root move_filesilently succeeds on paths you didn't intend to exposedelete_filewithrecursive: truedeletes a parent directory
How Directory Access Control Works
Before touching any config, understand the access flow:
Claude Desktop / VS Code
│
▼
MCP Client (roots protocol)
│ roots/list_changed notification
▼
Filesystem Server
│ validates every path against allowedDirs
▼
Allowed Directory (e.g. /projects/myapp)
│
├── read_file ← read-only, safe
├── list_directory ← read-only, safe
├── write_file ← destructive if path escapes
├── delete_file ← destructive
└── move_file ← destructive
The server resolves symlinks and checks that the real path starts with an allowed directory prefix. Path traversal like ../../etc/passwd is caught here. But a too-broad allowed directory silently passes.
Solution
Step 1: Install the Official MCP Filesystem Server
# Verify Node.js >= 18
node --version
# Run directly via npx — no global install needed
npx -y @modelcontextprotocol/server-filesystem /path/to/your/project
Expected output:
MCP Filesystem Server running on stdio
Allowed directories: [ '/path/to/your/project' ]
If it fails:
Error: No allowed directories→ You must pass at least one directory as an argument; the server will not start without oneError: ENOENT→ The path doesn't exist; create the directory first withmkdir -p
Step 2: Configure Claude Desktop with a Scoped Directory
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/you/projects/myapp"
]
}
}
}
Restart Claude Desktop after saving. The server only sees /Users/you/projects/myapp and its subdirectories — nothing outside.
Multiple directories (e.g., project + a read-only docs folder):
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/you/projects/myapp",
"/Users/you/docs"
]
}
}
}
Both paths are read-write at the server level. To enforce read-only on /docs, use Docker (Step 4).
Step 3: Configure VS Code (Workspace Method)
Create .vscode/mcp.json in your workspace root:
{
"servers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"${workspaceFolder}"
]
}
}
}
${workspaceFolder} expands to the workspace root at runtime — no hardcoded paths. Commit this file to share the config with your team.
Open the Command Palette → MCP: List Servers to confirm the server is running.
Step 4: Enforce Read-Only Directories with Docker
The Docker image supports the ro mount flag to make specific directories genuinely read-only at the OS level — the server cannot write there even if the AI requests it.
docker run -i --rm \
--mount type=bind,src=/Users/you/projects/myapp,dst=/projects/myapp \
--mount type=bind,src=/Users/you/docs,dst=/projects/docs,ro \
mcp/filesystem \
/projects/myapp \
/projects/docs
The ro flag on the docs mount means any write_file or delete_file call targeting /projects/docs will fail at the OS level with a permission error — not just a soft server refusal.
Claude Desktop config for the Docker version:
{
"mcpServers": {
"filesystem": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"--mount", "type=bind,src=/Users/you/projects/myapp,dst=/projects/myapp",
"--mount", "type=bind,src=/Users/you/docs,dst=/projects/docs,ro",
"mcp/filesystem",
"/projects/myapp",
"/projects/docs"
]
}
}
}
Step 5: Know Which Tools Are Destructive
The server exposes these tools. Understand what each can do before letting an agent loose:
| Tool | Destructive? | Notes |
|---|---|---|
read_file | No | Safe; errors on binary files |
read_multiple_files | No | Batches reads |
list_directory | No | Metadata only |
directory_tree | No | Recursive listing |
get_file_info | No | Size, mtime, permissions |
list_allowed_directories | No | Returns the configured roots |
search_files | No | Glob pattern match |
write_file | Yes | Overwrites without warning |
create_directory | Low | Creates parent dirs |
move_file | Yes | Fails silently if dest exists |
delete_file | Yes | recursive: true is very destructive |
edit_file | Yes | In-place find-replace |
For agents that only need to read project files, pass the directory as a ro Docker mount and rely on the fact that write tools will simply fail at the OS level.
Verification
Ask Claude Desktop (after restarting):
List the files in my project directory.
You should see a directory listing limited to the path you configured — not your entire home folder.
Then test the boundary:
Try to read /etc/passwd
You should see an error like Access denied - path outside allowed directories. If you see file contents, your allowed directory is set too broadly.
Check the server is using the right paths by asking:
What directories are you allowed to access?
The response comes from the list_allowed_directories tool and shows exactly what the server sees.
What You Learned
- Pass the narrowest possible directory path — project root, not home directory
roDocker mounts are the only way to enforce read-only at the OS level; the server's own tooling doesn't have a per-directory write flagdelete_filewithrecursive: trueis the highest-risk tool — consider whether your agent actually needs it- VS Code's
${workspaceFolder}variable keeps configs portable across machines
Limitation: The official Node.js server has no built-in audit log. If you need a record of what the AI read or wrote, run it behind a proxy or use a fork with logging enabled (e.g., gabrielmaialva33/mcp-filesystem supports JSON config with log file and metrics).
Tested on @modelcontextprotocol/server-filesystem 2025.x, Node.js 22 LTS, Claude Desktop 0.9.x, macOS Sequoia and Ubuntu 24.04