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
allowedDirectoriescontains "/" 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/backupis allowed to execute ifcpis not inblockedCommands - 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, orddfrom executing even within allowed directories - Allow safe commands anywhere: Permit
grep,awk, orcatto 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
allowedDirectoriesrestricts filesystem operations at the point of path access insrc/tools/filesystem.tsblockedCommandsrestricts terminal command execution during parsing insrc/command-manager.ts- Terminal commands are validated against
blockedCommands, notallowedDirectories, but any file paths they use are later checked by the filesystem layer - Empty or root-only
allowedDirectoriesgrants full filesystem access; emptyblockedCommandsallows 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →