-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Description
Describe the bug
Since my last update of the Fileserver MCP I see basically every first call to the MCP fail with "Error: Access denied - path outside allowed directories". Which is really annoying because it means many tool calls instead of the first one succeeding.
To Reproduce
Steps to reproduce the behavior:
- configure the fileserver MCP with one path that doesn't include the execution dir of the server (or c:\windows\system32 apparently)
- Have Claude try to open a relative path directly in his root path, e.g.
{
path
:make-automation-audit-20250807/message.log
} - see Error instead of success
Error: Access denied - path outside allowed directories: C:\Windows\system32\make-automation-audit-20250807\message.log not in C:\Users\Stefan Polenz\Documents\claude
Expected behavior
Until recently giving the current project dir to Claude would allow it to open it right away.
Logs
from mcp-server-Filesystem.log
2025-08-11T17:57:20.223Z [Filesystem] [info] Message from server: {"result":{"content":[{"type":"text","text":"Error: Access denied - path outside allowed directories: C:\Windows\system32\make-automation-audit-20250807\message.log not in C:\Users\Stefan Polenz\Documents\claude"}],"isError":true},"jsonrpc":"2.0","id":15} { metadata: undefined }
Additional context
Working with Claude on the issue we are fairly confident that the issue is that in
servers\src\filesystem\index.ts
in lines 77+, so
servers/src/filesystem/index.ts
Line 77 in aad86ee
// Security utilities |
sync function validatePath(requestedPath: string): Promise {
const expandedPath = expandHome(requestedPath);
const absolute = path.isAbsolute(expandedPath)
? path.resolve(expandedPath)
: path.resolve(process.cwd(), expandedPath);
causes issues as the current working dir at the time of calling the functions isn't in any of the permitted paths.
A possible solution (that I can't test as I can't seem to see building information):
sync function validatePath(requestedPath: string): Promise {
const expandedPath = expandHome(requestedPath);
const absolute = path.isAbsolute(expandedPath)
? path.resolve(expandedPath)
: (() => {
// Try to resolve relative path against all allowed directories
for (const allowedDir of allowedDirectories) {
const candidate = path.resolve(allowedDir, expandedPath);
// Check if the resulting path lies within an allowed directory
if (allowedDirectories.some(dir => candidate.startsWith(dir))) {
return candidate;
}
}
// Fallback: use first allowed directory as base
return path.resolve(allowedDirectories[0], expandedPath);
})();
I could try to push this change - but seeing as I can't test currently I think this bug ticket might be a better option.