From 328a284cfbd86fd444d823d97555798f3deb37f0 Mon Sep 17 00:00:00 2001 From: Will-hxw <1176843521@qq.com> Date: Thu, 23 Apr 2026 03:10:18 +0800 Subject: [PATCH 1/3] fix(filesystem): guard roots/list_changed against CLI directory override The roots/list_changed handler was unconditionally replacing allowedDirectories with MCP roots, even when CLI directories were set. Adding the same guard as oninitialized to prevent MCP roots from overriding CLI directories when both are present. --- src/filesystem/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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); From 1014ce80a269cce80db05b598bf88d5b1826404f Mon Sep 17 00:00:00 2001 From: Will-hxw <1176843521@qq.com> Date: Thu, 23 Apr 2026 07:07:44 +0800 Subject: [PATCH 2/3] fix(filesystem): normalize paths to NFC for Unicode consistency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit macOS and Linux filesystems use different Unicode normalizations: - macOS APFS stores filenames in NFD (decomposed) - Linux typically uses NFC (composed) This caused path validation to fail when: - A directory name contains characters like dakuten (シ/ジ) or non-breaking spaces (common in Japanese directory names on macOS) - Screenshot files created by macOS system dialogs - Any path where client-supplied NFC path doesn't match NFD-stored path The fix applies .normalize('NFC') to both the absolutePath and allowed directory paths after path.resolve.normalize(), ensuring consistent comparison regardless of the Unicode form used in the filesystem. Co-Authored-By: Claude Opus 4.7 --- src/filesystem/path-validation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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; } From 3e3d74ea6a66d652cce3e13bd5f10a428c571c0d Mon Sep 17 00:00:00 2001 From: Will-hxw Date: Fri, 24 Apr 2026 17:34:14 +0800 Subject: [PATCH 3/3] fix(filesystem): update test assertion for NFC normalization --- src/filesystem/__tests__/path-validation.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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', () => {