Skip to content
Draft
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
14 changes: 12 additions & 2 deletions src/filesystem/__tests__/startup-validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ async function spawnServer(args: string[], timeoutMs = 2000): Promise<{ exitCode
let stderr = '';
proc.stderr?.on('data', (data) => {
stderr += data.toString();
if (stderr.includes('Secure MCP Filesystem Server running on stdio')) {
proc.kill('SIGTERM');
}
});

const timeout = setTimeout(() => {
Expand Down Expand Up @@ -66,8 +69,10 @@ describe('Startup Directory Validation', () => {
const result = await spawnServer([nonExistentDir, accessibleDir]);

// Should warn about inaccessible directory
expect(result.stderr).toContain('Warning: Cannot access directory');
expect(result.stderr).toContain('Warning: Skipping invalid allowed directory');
expect(result.stderr).toContain(nonExistentDir);
expect(result.stderr).toContain('ENOENT');
expect(result.stderr).toContain('no such file or directory');

// Should still start successfully
expect(result.stderr).toContain('Secure MCP Filesystem Server running on stdio');
Expand All @@ -82,6 +87,10 @@ describe('Startup Directory Validation', () => {
// Should exit with error
expect(result.exitCode).toBe(1);
expect(result.stderr).toContain('Error: None of the specified directories are accessible');
expect(result.stderr).toContain('Invalid allowed directories:');
expect(result.stderr).toContain(nonExistent1);
expect(result.stderr).toContain(nonExistent2);
expect(result.stderr).toContain('ENOENT');
});

it('should warn when path is not a directory', async () => {
Expand All @@ -91,7 +100,8 @@ describe('Startup Directory Validation', () => {
const result = await spawnServer([filePath, accessibleDir]);

// Should warn about non-directory
expect(result.stderr).toContain('Warning:');
expect(result.stderr).toContain('Warning: Skipping invalid allowed directory');
expect(result.stderr).toContain(filePath);
expect(result.stderr).toContain('not a directory');

// Should still start with the valid directory
Expand Down
26 changes: 24 additions & 2 deletions src/filesystem/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ import {
setAllowedDirectories,
} from './lib.js';

type InvalidAllowedDirectory = {
directory: string;
reason: string;
};

function formatStartupValidationError(error: unknown): string {
if (error instanceof Error) {
return error.message;
}

return String(error);
}

// Command line argument parsing
const args = process.argv.slice(2);
if (args.length === 0) {
Expand Down Expand Up @@ -68,22 +81,31 @@ let allowedDirectories = (await Promise.all(

// Filter to only accessible directories, warn about inaccessible ones
const accessibleDirectories: string[] = [];
const invalidDirectories: InvalidAllowedDirectory[] = [];
for (const dir of allowedDirectories) {
try {
const stats = await fs.stat(dir);
if (stats.isDirectory()) {
accessibleDirectories.push(dir);
} else {
console.error(`Warning: ${dir} is not a directory, skipping`);
const reason = "not a directory";
invalidDirectories.push({ directory: dir, reason });
console.error(`Warning: Skipping invalid allowed directory "${dir}": ${reason}`);
}
} catch (error) {
console.error(`Warning: Cannot access directory ${dir}, skipping`);
const reason = `cannot access: ${formatStartupValidationError(error)}`;
invalidDirectories.push({ directory: dir, reason });
console.error(`Warning: Skipping invalid allowed directory "${dir}": ${reason}`);
}
}

// Exit only if ALL paths are inaccessible (and some were specified)
if (accessibleDirectories.length === 0 && allowedDirectories.length > 0) {
console.error("Error: None of the specified directories are accessible");
console.error("Invalid allowed directories:");
for (const { directory, reason } of invalidDirectories) {
console.error(` - ${directory}: ${reason}`);
}
process.exit(1);
}

Expand Down
Loading