modelcontextprotocol/servers · error · Error

Access denied - path outside allowed directories: ${absolute

Error message

Access denied - path outside allowed directories: ${absolute} not in ${allowedDirectories.join(', ')}

What it means

Thrown by `validatePath` in the filesystem server when the normalized requested path does not lie within any allowed directory. This is the primary security boundary: every file operation runs through `validatePath`, which checks the resolved absolute path before any I/O. Symlink resolution is then applied separately (error 17).

Source

Thrown at src/filesystem/lib.ts:110

  
  // If no valid resolution found, use the first allowed directory as base
  // This provides a consistent fallback behavior
  return path.resolve(allowedDirectories[0], relativePath);
}

// Security & Validation Functions
export async function validatePath(requestedPath: string): Promise<string> {
  const expandedPath = expandHome(requestedPath);
  const absolute = path.isAbsolute(expandedPath)
    ? path.resolve(expandedPath)
    : resolveRelativePathAgainstAllowedDirectories(expandedPath);

  const normalizedRequested = normalizePath(absolute);

  // Security: Check if path is within allowed directories before any file operations
  const isAllowed = isPathWithinAllowedDirectories(normalizedRequested, allowedDirectories);
  if (!isAllowed) {
    throw new Error(`Access denied - path outside allowed directories: ${absolute} not in ${allowedDirectories.join(', ')}`);
  }

  // Security: Handle symlinks by checking their real path to prevent symlink attacks
  // This prevents attackers from creating symlinks that point outside allowed directories
  try {
    const realPath = await fs.realpath(absolute);
    const normalizedReal = normalizePath(realPath);
    if (!isPathWithinAllowedDirectories(normalizedReal, allowedDirectories)) {
      throw new Error(`Access denied - symlink target outside allowed directories: ${realPath} not in ${allowedDirectories.join(', ')}`);
    }
    return realPath;
  } catch (error) {
    // Security: For new files that don't exist yet, verify parent directory
    // This ensures we can't create files in unauthorized locations
    if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
      const parentDir = path.dirname(absolute);
      try {
        const realParentPath = await fs.realpath(parentDir);

View on GitHub (pinned to 76d64c822f)

Solutions

  1. Request a path inside one of the allowed directories shown in the error message.
  2. Add the needed directory to the server's allowed directories (CLI args or client roots).
  3. Avoid `..` segments; resolve and normalize the path client-side before sending.

Example fix

# before (only /home/me/projects allowed)
read_text_file({ path: '/etc/passwd' })
# after
read_text_file({ path: '/home/me/projects/notes.txt' })
Defensive patterns

Strategy: validation

Validate before calling

import path from 'node:path';
function isWithinAllowed(p: string, allowed: string[]): boolean {
  const a = path.resolve(p);
  return allowed.some(d => a === path.resolve(d) || a.startsWith(path.resolve(d) + path.sep));
}
if (!isWithinAllowed(args.path, allowedDirs)) { /* surface error */ }

Try / catch

try {
  await readFile({ path });
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Access denied - path outside')) {
    // path resolves outside allowlist; choose an allowed path or extend allowlist
  }
}

Prevention

When it happens

Trigger: Any filesystem tool call (`read_text_file`, `write_file`, `list_directory`, etc.) whose `path` resolves outside all configured allowed directories — e.g. requesting `/etc/passwd` when only `/home/me/projects` is allowed, or a relative path that escapes via `..`.

Common situations: Clients passing absolute paths outside the allowlist, `..` traversal in relative paths, home shorthand (`~/`) expanding outside the allowlist, or a misconfigured allowlist that omits the needed directory.

Understand the failure class

Related errors


AI-assisted analysis of modelcontextprotocol/servers@76d64c822f (2026-08-12). Data as JSON: /api/errors/3b6858d8b566c5a5. Report an issue: GitHub.