Summary
@modelcontextprotocol/server-filesystem v0.2.0 (secure-filesystem-server) crashes
on startup if any allowed directory in argv does not exist. The crash is caused
by Promise.all(dirs.map(fs.stat)) rejecting on the first ENOENT, which terminates
the entire startup with no fallback and no actionable stderr captured by hosts that
don't surface child-process stderr (e.g. Claude Desktop on Windows MSIX, see linked
report).
Reproduction
node dist/index.js "C:\exists" "C:\does-not-exist"
Output:
Error accessing directory C:\does-not-exist: Error: ENOENT: no such file or directory,
stat 'C:\does-not-exist'
at async Object.stat (node:internal/fs/promises:1038:18)
at async file:///.../dist/index.js:43:23
at async Promise.all (index 1)
at async file:///.../dist/index.js:41:1
Process exits 1. No partial startup, no warning, no continuation with valid dirs.
Impact
When run under a host that does not pipe child-process stderr to the user (Claude
Desktop on the MSIX/Microsoft Store build of Windows is one such host), the user sees
only "Server transport closed unexpectedly" in the host log with no further detail.
Diagnosing the missing-directory cause requires running node dist/index.js manually
outside the host — a step most users won't think to take.
In my case the single offending entry was C:\NetPhantom 8 QS (a directory I had
removed). The other 8 allowed directories were valid, but the entire server died.
Expected behavior
Either:
- Skip missing directories with a clear warning to stderr and start with the
valid ones. This matches how most "allowlist" tools behave and is the friendliest
to users whose allowed_directories list drifts.
- Fail hard with a clear stderr message naming the offending directory, then
process.exit(1). This preserves strict-validation semantics but at least makes
the error survive process.on('uncaughtException')/host stderr capture.
Either way, replacing Promise.all with Promise.allSettled and inspecting each
result allows the server to report all missing/invalid directories in one pass
instead of dying on the first.
Suggested patch
Around dist/index.js:41-43 (the startup validation block), something like:
const results = await Promise.allSettled(
allowedDirectories.map(async (dir) => {
const stat = await fs.stat(dir);
if (!stat.isDirectory()) {
throw new Error(`Not a directory: ${dir}`);
}
return dir;
}),
);
const valid: string[] = [];
const errors: string[] = [];
results.forEach((r, i) => {
if (r.status === 'fulfilled') valid.push(r.value);
else errors.push(`${allowedDirectories[i]}: ${(r.reason as Error).message}`);
});
if (errors.length > 0) {
console.error(`[server-filesystem] Skipping ${errors.length} invalid director(y/ies):`);
errors.forEach((e) => console.error(` - ${e}`));
}
if (valid.length === 0) {
console.error('[server-filesystem] No valid allowed directories; exiting.');
process.exit(1);
}
// proceed with `valid`
Environment
@modelcontextprotocol/server-filesystem v0.2.0
- Node.js: bundled with Claude Desktop (Windows MSIX build) + tested with system Node
- OS: Windows 11 Pro Canary build, MSIX-installed Claude Desktop v1.8555.2.0
- Server name reported in initialize:
secure-filesystem-server v0.2.0
Summary
@modelcontextprotocol/server-filesystemv0.2.0 (secure-filesystem-server) crasheson startup if any allowed directory in
argvdoes not exist. The crash is causedby
Promise.all(dirs.map(fs.stat))rejecting on the first ENOENT, which terminatesthe entire startup with no fallback and no actionable stderr captured by hosts that
don't surface child-process stderr (e.g. Claude Desktop on Windows MSIX, see linked
report).
Reproduction
Output:
Error accessing directory C:\does-not-exist: Error: ENOENT: no such file or directory,
stat 'C:\does-not-exist'
at async Object.stat (node:internal/fs/promises:1038:18)
at async file:///.../dist/index.js:43:23
at async Promise.all (index 1)
at async file:///.../dist/index.js:41:1
Process exits 1. No partial startup, no warning, no continuation with valid dirs.
Impact
When run under a host that does not pipe child-process stderr to the user (Claude
Desktop on the MSIX/Microsoft Store build of Windows is one such host), the user sees
only "Server transport closed unexpectedly" in the host log with no further detail.
Diagnosing the missing-directory cause requires running
node dist/index.jsmanuallyoutside the host — a step most users won't think to take.
In my case the single offending entry was
C:\NetPhantom 8 QS(a directory I hadremoved). The other 8 allowed directories were valid, but the entire server died.
Expected behavior
Either:
valid ones. This matches how most "allowlist" tools behave and is the friendliest
to users whose allowed_directories list drifts.
process.exit(1). This preserves strict-validation semantics but at least makesthe error survive
process.on('uncaughtException')/host stderr capture.Either way, replacing
Promise.allwithPromise.allSettledand inspecting eachresult allows the server to report all missing/invalid directories in one pass
instead of dying on the first.
Suggested patch
Around
dist/index.js:41-43(the startup validation block), something like:Environment
@modelcontextprotocol/server-filesystemv0.2.0secure-filesystem-serverv0.2.0