diff --git a/src/filesystem/__tests__/path-validation.test.ts b/src/filesystem/__tests__/path-validation.test.ts index 81ad247ee2..230e1dde9c 100644 --- a/src/filesystem/__tests__/path-validation.test.ts +++ b/src/filesystem/__tests__/path-validation.test.ts @@ -199,9 +199,9 @@ describe('Path Validation', () => { expect(isPathWithinAllowedDirectories('/home/user/café', allowed)).toBe(true); expect(isPathWithinAllowedDirectories('/home/user/café/file', allowed)).toBe(true); - // Different unicode representation won't match (not normalized) + // Different unicode representations now match after NFC normalization const decomposed = '/home/user/cafe\u0301'; // e + combining accent - expect(isPathWithinAllowedDirectories(decomposed, allowed)).toBe(false); + expect(isPathWithinAllowedDirectories(decomposed, allowed)).toBe(true); }); it('handles paths with spaces correctly', () => { diff --git a/src/filesystem/index.ts b/src/filesystem/index.ts index 7b67e63e58..998a35a7ae 100644 --- a/src/filesystem/index.ts +++ b/src/filesystem/index.ts @@ -717,7 +717,10 @@ async function updateAllowedDirectoriesFromRoots(requestedRoots: Root[]) { // Handles dynamic roots updates during runtime, when client sends "roots/list_changed" notification, server fetches the updated roots and replaces all allowed directories with the new roots. server.server.setNotificationHandler(RootsListChangedNotificationSchema, async () => { try { - // Request the updated roots list from the client + // Only update from MCP roots if no CLI directories were set + if (allowedDirectories.length > 0) { + return; + } const response = await server.server.listRoots(); if (response && 'roots' in response) { await updateAllowedDirectoriesFromRoots(response.roots); diff --git a/src/filesystem/path-validation.ts b/src/filesystem/path-validation.ts index 972e9c49d0..3c2adc8358 100644 --- a/src/filesystem/path-validation.ts +++ b/src/filesystem/path-validation.ts @@ -27,7 +27,7 @@ export function isPathWithinAllowedDirectories(absolutePath: string, allowedDire // Normalize the input path let normalizedPath: string; try { - normalizedPath = path.resolve(path.normalize(absolutePath)); + normalizedPath = path.resolve(path.normalize(absolutePath)).normalize("NFC"); } catch { return false; } @@ -51,7 +51,7 @@ export function isPathWithinAllowedDirectories(absolutePath: string, allowedDire // Normalize the allowed directory let normalizedDir: string; try { - normalizedDir = path.resolve(path.normalize(dir)); + normalizedDir = path.resolve(path.normalize(dir)).normalize("NFC"); } catch { return false; }