Summary
On Windows, when the Filesystem MCP server is configured with paths on a mapped drive (e.g. Y:\k8s where Y: is mapped to \\192.168.xxx.xxx\nas), the configured paths are canonicalized to their UNC form at server startup. However, incoming tool-call paths using the original Y:\ form are not canonicalized to the same UNC form before the allow-list comparison, so they are rejected — even though they refer to the exact same directory on disk that was configured.
The net effect is that the configured allow-list (as shown in the UI) and the runtime allow-list (as reported by list_allowed_directories) disagree, and the user has no way to know which form to send without trial-and-error.
Environment
- OS: Windows (mapped drive
Y: → \\192.168.xxx.xxx\nas over SMB)
- Client: Claude Desktop (version: <fill in from Help → About>)
- Server:
@modelcontextprotocol/server-filesystem shipped as the Filesystem extension in Claude Desktop
- Drive mapping:
Y: is a persistent SMB mapping to a NAS share, available to the user account before Claude Desktop launches (dir Y:\ works in cmd at the time of repro)
Configuration
Claude Desktop Version:
1.6608.2 (ebf1a1) 2026-05-08T23:17:27.000Z
Filesystem Extension/Connector Version:
0.2.2
The Filesystem extension's Allowed Directories UI (screenshot attached) is configured with:
These entries are exactly as typed; the UI preserves the Y:\ form on save and on subsequent views.
Steps to Reproduce
- On Windows, map a drive letter (e.g.
Y:) to an SMB share (e.g. \\server\share) so that dir Y:\ works.
- Configure the Filesystem extension's Allowed Directories with one or more entries using the drive-letter form (e.g.
Y:\k8s).
- Start Claude Desktop and confirm the Filesystem extension is enabled.
- From a tool call, invoke
list_allowed_directories.
- From a tool call, invoke
list_directory with path = "Y:\k8s" (or any subpath).
Expected Behavior
list_allowed_directories returns the same form that was configured (Y:\k8s, etc.), or the input path is canonicalized to the same form as the stored allow-list before comparison.
list_directory with path = "Y:\k8s" succeeds, since it refers to a configured directory.
Actual Behavior
list_allowed_directories returns the UNC form, not the configured form:
Allowed directories:
\\192.168.xxx.xxx\nas\git
\\192.168.xxx.xxx\nas\wiki
\\192.168.xxx.xxx\nas\k8s
list_directory with path = "Y:\k8s" is rejected:
Access denied - path outside allowed directories:
Y:\k8s not in \\192.168.xxx.xxx\nas\git, \\192.168.xxx.xxx\nas\wiki, \\192.168.xxx.xxx\nas\k8s
The same call with path = "\\\\192.168.xxx.xxx\\nas\\k8s" succeeds. Both forms refer to the identical directory on disk.
Suspected Cause
Per the source summary at https://deepwiki.com/modelcontextprotocol/servers/2.2-filesystem-tools-reference, the configured directory list goes through expandHome() → absolute resolution → fs.realpath() → normalizePath() at startup (src/filesystem/index.ts ~lines 47–54). On Windows, fs.realpath() on a mapped drive returns the UNC target, which is why the stored allow-list is in UNC form.
The validatePath() function on incoming requests also resolves symlinks for security, but in this case the comparison evidently does not produce the same canonical form for the input as for the stored entries. Either:
validatePath() is not applying fs.realpath() to the input before the prefix check, or
- It is applying it, but on a path that doesn't yet exist on disk (e.g. for a
write_file to a new file under an allowed directory), and falling back to a non-canonicalized form, or
- The comparison is a string
startsWith against a canonicalized allow-list using a non-canonicalized input.
Why This Matters
From a user's perspective, the configured paths in the UI are the source of truth. There's no surfaced indication that the runtime has rewritten them, and no documented guidance to use UNC form for Windows users who have mapped drives. Workflows that have been stable for weeks can break silently if the server's startup-time canonicalization behavior changes.
It also means tool callers (LLMs, IDEs, scripts) that read list_allowed_directories to learn what's allowed will see UNC, send UNC, and work — but humans reading the same configuration in the UI will see Y:\ and reasonably expect that form to work.
Suggested Fix
In validatePath() (or wherever the prefix comparison happens), ensure the input path goes through the same canonicalization pipeline used at server startup before the allow-list check — fs.realpath() for paths that exist on disk, or for write operations, walk up to the nearest existing parent and canonicalize that, then re-append the trailing components. This preserves the existing security model (symlinks resolved, traversal attacks blocked) while making the allow-list check symmetric with the allow-list construction.
Alternatively (or additionally), preserve the original configured path strings alongside the canonicalized form, return the original form from list_allowed_directories, and check incoming paths against the canonical form after applying fs.realpath() to the input.
Workaround
For any Windows user with a mapped drive in their allow-list: send paths in UNC form (\\server\share\...) instead of drive-letter form (Y:\...) until this is fixed. This is awkward but works.
Attached

- Screenshot: Allowed Directories settings showing the configured `Y:\` entries (UI source of truth, contradicting the runtime UNC behavior).
(Bug report written with assistance from Claude)
Summary
On Windows, when the Filesystem MCP server is configured with paths on a mapped drive (e.g.
Y:\k8swhereY:is mapped to\\192.168.xxx.xxx\nas), the configured paths are canonicalized to their UNC form at server startup. However, incoming tool-call paths using the originalY:\form are not canonicalized to the same UNC form before the allow-list comparison, so they are rejected — even though they refer to the exact same directory on disk that was configured.The net effect is that the configured allow-list (as shown in the UI) and the runtime allow-list (as reported by
list_allowed_directories) disagree, and the user has no way to know which form to send without trial-and-error.Environment
Y:→\\192.168.xxx.xxx\nasover SMB)@modelcontextprotocol/server-filesystemshipped as the Filesystem extension in Claude DesktopY:is a persistent SMB mapping to a NAS share, available to the user account before Claude Desktop launches (dir Y:\works incmdat the time of repro)Configuration
Claude Desktop Version:
1.6608.2 (ebf1a1) 2026-05-08T23:17:27.000Z
Filesystem Extension/Connector Version:
0.2.2
The Filesystem extension's Allowed Directories UI (screenshot attached) is configured with:
Y:\gitY:\wikiY:\k8sThese entries are exactly as typed; the UI preserves the
Y:\form on save and on subsequent views.Steps to Reproduce
Y:) to an SMB share (e.g.\\server\share) so thatdir Y:\works.Y:\k8s).list_allowed_directories.list_directorywithpath = "Y:\k8s"(or any subpath).Expected Behavior
list_allowed_directoriesreturns the same form that was configured (Y:\k8s, etc.), or the input path is canonicalized to the same form as the stored allow-list before comparison.list_directorywithpath = "Y:\k8s"succeeds, since it refers to a configured directory.Actual Behavior
list_allowed_directoriesreturns the UNC form, not the configured form:list_directorywithpath = "Y:\k8s"is rejected:The same call with
path = "\\\\192.168.xxx.xxx\\nas\\k8s"succeeds. Both forms refer to the identical directory on disk.Suspected Cause
Per the source summary at https://deepwiki.com/modelcontextprotocol/servers/2.2-filesystem-tools-reference, the configured directory list goes through
expandHome()→ absolute resolution →fs.realpath()→normalizePath()at startup (src/filesystem/index.ts~lines 47–54). On Windows,fs.realpath()on a mapped drive returns the UNC target, which is why the stored allow-list is in UNC form.The
validatePath()function on incoming requests also resolves symlinks for security, but in this case the comparison evidently does not produce the same canonical form for the input as for the stored entries. Either:validatePath()is not applyingfs.realpath()to the input before the prefix check, orwrite_fileto a new file under an allowed directory), and falling back to a non-canonicalized form, orstartsWithagainst a canonicalized allow-list using a non-canonicalized input.Why This Matters
From a user's perspective, the configured paths in the UI are the source of truth. There's no surfaced indication that the runtime has rewritten them, and no documented guidance to use UNC form for Windows users who have mapped drives. Workflows that have been stable for weeks can break silently if the server's startup-time canonicalization behavior changes.
It also means tool callers (LLMs, IDEs, scripts) that read
list_allowed_directoriesto learn what's allowed will see UNC, send UNC, and work — but humans reading the same configuration in the UI will seeY:\and reasonably expect that form to work.Suggested Fix
In
validatePath()(or wherever the prefix comparison happens), ensure the input path goes through the same canonicalization pipeline used at server startup before the allow-list check —fs.realpath()for paths that exist on disk, or for write operations, walk up to the nearest existing parent and canonicalize that, then re-append the trailing components. This preserves the existing security model (symlinks resolved, traversal attacks blocked) while making the allow-list check symmetric with the allow-list construction.Alternatively (or additionally), preserve the original configured path strings alongside the canonicalized form, return the original form from
list_allowed_directories, and check incoming paths against the canonical form after applyingfs.realpath()to the input.Workaround
For any Windows user with a mapped drive in their allow-list: send paths in UNC form (
\\server\share\...) instead of drive-letter form (Y:\...) until this is fixed. This is awkward but works.Attached
(Bug report written with assistance from Claude)