Skip to content

Security

Access TypeScopeDetails
FilesystemRead/WriteLimited to allowed directories (CLI args or MCP Roots)
NetworkNoneStdio transport only, no outbound connections
SystemNoneNo process execution, no system calls

Every file operation goes through path validation:

  1. The path is resolved to an absolute path.
  2. Symlinks are resolved to their real target.
  3. The resolved path is checked against the allow-list.
  4. Path traversal attempts (../) are blocked.

If any check fails, the operation is rejected before touching the filesystem.

On Windows, WSL-style paths like /mnt/c/Users/foo are automatically converted to C:\Users\foo before validation. This prevents false “access denied” errors when tools are called from WSL environments.

On Windows, path comparison against the allow-list is case-insensitive. This fixes scenarios where c:\users\foo and C:\Users\foo refer to the same directory but would fail strict string comparison.

The following attack vectors are tested and blocked:

VectorExampleResult
Path traversal../../etc/passwdBlocked
WSL path injection/mnt/c/Windows/System32Converted + validated
Win32 namespace\\?\C:\secretBlocked
Device paths//./COM1Blocked
Forward-slash bypassC:/Windows/System32Normalized + validated
Cross-dir exfiltrationcopy_file / move_file to outside allowed dirsBlocked (destination validated)

Allowed directories are passed as command-line arguments at startup:

Terminal window
mcp-filesystem-go-light /home/user/projects /tmp/scratch

The server declares the roots capability. If the MCP client provides roots via roots/list, those directories are merged into the allow-list on the first tool call. This allows clients to dynamically configure access without restarting the server.

The server can start with no CLI arguments and rely entirely on roots from the client:

Terminal window
mcp-filesystem-go-light # roots will be provided by the client

Only the resulting merged list of directories (and their subdirectories) is accessible. Everything else is denied.

write_file uses an atomic write pattern:

  1. Content is written to a temporary file in the same directory.
  2. os.Rename() atomically replaces the target.
  3. On any error, the temporary file is removed.

This prevents data loss from partial writes and eliminates race conditions. write_file_safe and chunked_write use the same pattern.

Symlinks are followed and the real target path is validated. If a symlink points outside the allowed directories, the operation is rejected.

A sliding-window rate limiter restricts operations to 60 calls per minute. This prevents runaway loops from consuming resources.

If you find a security issue, email security@scopweb.dev. Do not open a public issue.