Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/filesystem/__tests__/startup-validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,32 @@ describe('Startup Directory Validation', () => {
expect(result.stderr).toContain('Error: None of the specified directories are accessible');
});

// Test for macOS /tmp -> /private/tmp symlink issue (GitHub issue #3253)
// Verifies that the startup code stores both the symlink path and its
// resolved target in allowedDirectories, so requests through either form work.
it('should start successfully with symlinked allowed directory', async () => {
const actualDir = path.join(testDir, 'actual-target');
await fs.mkdir(actualDir, { recursive: true });

const symlinkDir = path.join(testDir, 'symlink-dir');
try {
await fs.symlink(actualDir, symlinkDir);
} catch (error) {
// Skip on systems without symlink permissions (e.g., restricted Windows)
if ((error as NodeJS.ErrnoException).code === 'EPERM') {
return;
}
throw error;
}

// Start server with the symlink path (not the resolved path)
const result = await spawnServer([symlinkDir]);

// Server should start without errors
expect(result.stderr).toContain('Secure MCP Filesystem Server running on stdio');
expect(result.stderr).not.toContain('Error:');
});

it('should warn when path is not a directory', async () => {
const filePath = path.join(testDir, 'not-a-directory.txt');
await fs.writeFile(filePath, 'content');
Expand Down
7 changes: 5 additions & 2 deletions src/filesystem/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,11 @@ let allowedDirectories = (await Promise.all(
}
return [normalizedResolved];
} catch (error) {
// If we can't resolve (doesn't exist), use the normalized absolute path
// This allows configuring allowed dirs that will be created later
// Directory doesn't exist yet - store only the unresolved path.
// This allows configuring allowed dirs that will be created later.
// Note: if this path later appears as a symlink, only the unresolved
// form will be in allowedDirectories. Full symlink support requires
// the directory to exist at startup so both forms can be resolved.
return [normalizedOriginal];
}
})
Expand Down