Description
When running server-filesystem on macOS with /tmp specified as an allowed directory, file operations using /tmp/... paths are incorrectly rejected as being outside allowed directories.
Root Cause
On macOS, /tmp is a symlink to /private/tmp:
$ ls -la /tmp
lrwxr-xr-x@ 1 root wheel 11 Nov 30 2024 /tmp -> private/tmp
The server appears to resolve paths to their canonical (real) form before checking against allowed directories. When a user specifies /tmp as allowed and passes /tmp/foo.txt, the server resolves it to /private/tmp/foo.txt, which doesn't match the allowed path /tmp.
Reproduction Steps
-
Start server-filesystem with /tmp as allowed directory:
npx -y @modelcontextprotocol/server-filesystem /tmp
-
Call read_file with a path under /tmp:
{"path": "/tmp/test.txt"}
-
Observe rejection error indicating path is outside allowed directories
Expected Behavior
Either:
- The server should resolve allowed directories to their canonical paths during initialization, OR
- The server should compare paths consistently (both resolved or both as-specified)
When /tmp is specified as allowed, files under /tmp/... should be accessible.
Actual Behavior
Files under /tmp/ are rejected as being outside allowed directories because the path resolves to /private/tmp/... internally.
Environment
- macOS (any version with /tmp -> /private/tmp symlink)
- @modelcontextprotocol/server-filesystem (current npm version)
Suggested Fix
In the path validation logic, resolve allowed directories to their canonical paths at initialization time using fs.realpathSync(), so that /tmp becomes /private/tmp in the allow list. This ensures consistent comparison with resolved request paths.
Discovery
This bug was discovered using Bellwether, a structural drift detection and behavioral testing tool for MCP servers. Bellwether automatically explores MCP server capabilities and identifies edge cases through systematic testing.
Description
When running
server-filesystemon macOS with/tmpspecified as an allowed directory, file operations using/tmp/...paths are incorrectly rejected as being outside allowed directories.Root Cause
On macOS,
/tmpis a symlink to/private/tmp:The server appears to resolve paths to their canonical (real) form before checking against allowed directories. When a user specifies
/tmpas allowed and passes/tmp/foo.txt, the server resolves it to/private/tmp/foo.txt, which doesn't match the allowed path/tmp.Reproduction Steps
Start server-filesystem with
/tmpas allowed directory:Call
read_filewith a path under/tmp:{"path": "/tmp/test.txt"}Observe rejection error indicating path is outside allowed directories
Expected Behavior
Either:
When
/tmpis specified as allowed, files under/tmp/...should be accessible.Actual Behavior
Files under
/tmp/are rejected as being outside allowed directories because the path resolves to/private/tmp/...internally.Environment
Suggested Fix
In the path validation logic, resolve allowed directories to their canonical paths at initialization time using
fs.realpathSync(), so that/tmpbecomes/private/tmpin the allow list. This ensures consistent comparison with resolved request paths.Discovery
This bug was discovered using Bellwether, a structural drift detection and behavioral testing tool for MCP servers. Bellwether automatically explores MCP server capabilities and identifies edge cases through systematic testing.