List, read, and search files in a directory via MCP
How do I use the @modelcontextprotocol/server-filesystem MCP server to list directory contents, read files, search by glob, and get a recursive directory tree — all within an allowed directory sandbox?
Recipe: Filesystem operations via @modelcontextprotocol/server-filesystem
Launch
npx -y @modelcontextprotocol/server-filesystem /path/to/allowed/dirThe server sandboxes all operations to the directories you list as CLI args. On macOS, /tmp symlinks to /private/tmp — use the canonical path in both the CLI arg and tool calls, or you'll get "Access denied - path outside allowed directories".
Server info
- name:
secure-filesystem-server - version:
0.2.0 - protocol:
2024-11-05
Available tools (14 total)
read_text_file, read_file (deprecated), read_media_file, read_multiple_files, write_file, edit_file, create_directory, list_directory, list_directory_with_sizes, directory_tree, move_file, search_files, get_file_info, list_allowed_directories
Key tool schemas
list_directory — { path: string } → text with [FILE] / [DIR] prefixed entries, one per line.
read_text_file — { path: string, head?: number, tail?: number } → file contents as text. Supports head/tail for partial reads.
search_files — { path: string, pattern: string, excludePatterns?: string[] } → glob search, e.g. **/*.js. Returns full paths.
directory_tree — { path: string, excludePatterns?: string[] } → recursive JSON tree with name, type, and children for dirs.
Gotcha: macOS /tmp symlink
If you pass /tmp/mydir as the allowed dir, the server resolves it to /private/tmp/mydir. But tool calls with /tmp/mydir will fail because the path doesn't literally match /private/tmp/mydir. Always use the canonical path.
Runs
All 4 tool calls (listdirectory, readtextfile, searchfiles, directory_tree) succeeded on first try after fixing the canonical path.
{ "server": "@modelcontextprotocol/server-filesystem", "version": "0.2.0", "transport": "stdio", "launch": "npx -y @modelcontextprotocol/server-filesystem /private/tmp/pathfinder-test", "traces": [ { "tool": "list_directory", "request": { "name": "list_directory", "arguments": { "path": "/private/tmp/pathfinder-test" } }, "response": { "content": [ { "type": "text", "text": "[FILE] package.json [FILE] readme.txt [DIR] src" } ], "structuredContent": { "content": "[FILE] package.json [FILE] readme.txt [DIR] src" } }, "success": true }, { "tool": "read_text_file", "request": { "name": "read_text_file", "arguments": { "path": "/private/tmp/pathfinder-test/readme.txt" } }, "response": { "content": [ { "type": "text", "text": "Hello from pathfinder test " } ], "structuredContent": { "content": "Hello from pathfinder test " } }, "success": true }, { "tool": "search_files", "request": { "name": "search_files", "arguments": { "path": "/private/tmp/pathfinder-test", "pattern": "**/*.js" } }, "response": { "content": [ { "type": "text", "text": "/private/tmp/pathfinder-test/src/index.js" } ], "structuredContent": { "content": "/private/tmp/pathfinder-test/src/index.js" } }, "success": true }, { "tool": "directory_tree", "request": { "name": "directory_tree", "arguments": { "path": "/private/tmp/pathfinder-test" } }, "response": { "structuredContent": { "content": "[ {"name":"package.json","type":"file"}, {"name":"readme.txt","type":"file"}, {"name":"src","type":"directory","children":[{"name":"index.js","type":"file"}]} ]" } }, "success": true } ], "gotcha": "On macOS, /tmp symlinks to /private/tmp. The allowed-dir arg resolves to canonical path, but tool call paths must match literally. Always use /private/tmp." }