Desktop Commander MCP Allowed Directories vs Terminal Commands: Understanding Access Control Differences

allowedDirectories in Desktop Commander MCP restricts filesystem operations directly, while terminal commands are controlled separately via blockedCommands with filesystem paths validated later by the same allowed directories check.

Desktop Commander MCP implements a two-layer security model that distinguishes between where files can be accessed and what commands can be executed. This design gives administrators granular control over filesystem boundaries without limiting terminal flexibility. The primary keyword—allowed directories restrictions for file operations vs terminal commands—reflects this architectural separation.

How Allowed Directories Restrict Filesystem Operations

All filesystem operations in Desktop Commander MCP enforce allowedDirectories at the point of access. This includes reading, writing, moving, copying, and listing files through the filesystem tool.

The Enforcement Logic in filesystem.ts

In src/tools/filesystem.ts (lines 172-210), every filesystem path undergoes validation before any operation proceeds:

// src/tools/filesystem.ts
const allowedDirectories = await getAllowedDirs();
if (allowedDirectories.includes('/') || allowedDirectories.length === 0) {
  // unrestricted – allow any path
}
const isAllowed = allowedDirectories.some(dir => {
  const normDir = normalizePath(dir);
  // exact match
  if (normPath === normDir) return true;
  // sub‑directory match
  return normPath.startsWith(normDir + '/');
});
if (!isAllowed) {
  throw new Error(`Path not allowed: ${requestedPath}`);
}

Key behaviors:

  • If allowedDirectories contains "/" or is empty, all paths are permitted
  • Otherwise, the requested path must exactly match or be a subdirectory of an allowed directory
  • The check uses normalized paths with trailing-slash-aware matching to prevent traversal attacks

This validation runs for every file-touching operation regardless of whether the request originates from an API call, UI interaction, or tool invocation.

How Terminal Commands Bypass Allowed Directories (Initially)

Terminal commands follow a completely different validation path. In src/command-manager.ts (lines 29-52), commands are checked against blockedCommands only—allowedDirectories is not consulted:

// src/command-manager.ts
const blocked = config.blockedCommands || [];
const baseCmd = this.getBaseCommand(command);
if (blocked.includes(baseCmd)) {
  return false; // command blocked
}
return true; // command allowed (file paths will be checked later)

This means:

  • A command like cp /etc/passwd /tmp/backup is allowed to execute if cp is not in blockedCommands
  • The file paths within the command (/etc/passwd, /tmp/backup) are only validated when the filesystem layer processes them

The Critical Interaction Layer

When a terminal command actually performs file operations, those operations flow through the same filesystem.ts validation shown above. A command can start but will fail if it attempts to access forbidden paths. This creates two possible outcomes:

Scenario Result
Command has no file arguments (e.g., ls, date) Executes freely if not blocked
Command has file arguments in allowed directories Executes and succeeds
Command has file arguments outside allowed directories Command starts but filesystem operations throw "Path not allowed" errors

Configuration Structure

Both restrictions are defined in the same configuration but operate independently:

{
  "allowedDirectories": ["/home/user/documents", "/var/www"],
  "blockedCommands": ["rm", "shutdown", "reboot"]
}

allowedDirectories — array of absolute paths defining filesystem boundaries blockedCommands — array of command names that cannot be executed regardless of directory context

File-Level Responsibilities

File Security Function
src/config-field-definitions.ts Defines configuration schema for both fields
src/tools/filesystem.ts Enforces allowedDirectories on every file operation
src/command-manager.ts Validates terminal commands against blockedCommands only
src/server.ts Documents configuration fields in API descriptions

Practical Security Implications

This separation enables useful security patterns that would be impossible with a unified model:

  • Permit terminal exploration while restricting file damage: Allow ls / to browse the filesystem tree, but block writes outside approved directories
  • Block dangerous commands globally: Prevent rm, mkfs, or dd from executing even within allowed directories
  • Allow safe commands anywhere: Permit grep, awk, or cat to process files without directory restrictions, trusting that write operations will still be gated

The blockedCommands check is pre-execution—it prevents process spawn. The allowedDirectories check is per-operation—it gates actual file descriptor access.

Summary

  • allowedDirectories restricts filesystem operations at the point of path access in src/tools/filesystem.ts
  • blockedCommands restricts terminal command execution during parsing in src/command-manager.ts
  • Terminal commands are validated against blockedCommands, not allowedDirectories, but any file paths they use are later checked by the filesystem layer
  • Empty or root-only allowedDirectories grants full filesystem access; empty blockedCommands allows all command names

Frequently Asked Questions

Does allowedDirectories prevent terminal commands from running in restricted directories?

No. allowedDirectories does not block command execution based on working directory. A terminal command can start in any directory, but any file operations it performs will be rejected if they target paths outside the allowed list.

What happens if a terminal command tries to access a blocked file path?

The command process launches, but the specific filesystem operation fails with a "Path not allowed" error thrown from src/tools/filesystem.ts. The command may continue executing if it handles errors, or may terminate depending on its implementation.

Can blockedCommands include full command paths like /usr/bin/rm?

The getBaseCommand() method in src/command-manager.ts extracts the base command name from the full command string, so /usr/bin/rm would be evaluated as rm. The blocked list should contain bare command names without paths.

Why are these restrictions separated instead of unified?

Separating command execution control from filesystem access control allows flexible security policies. Administrators can permit read-only exploration of the filesystem tree while strictly limiting where changes can be written, or block destructive commands globally without restricting directory navigation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →