What an agent with a shell can actually do
Claude Code, Codex, and Cursor all run shell commands on your Mac as your user. That is what makes them useful for npm test and xcodebuild. It is also what makes “clean up my disk” a bad prompt: the shell command that frees space is rm -rf, and an agent will find something to point it at. ~/Library/Developer/Xcode/DerivedData if you are lucky, ~/Library/Developer/Xcode/Archives if you are not. Both look like build output; only one holds the dSYMs you need to read crash reports.
Processes are the same. kill -9 $(lsof -t -i:3000) is a reasonable shell answer to “port 3000 is busy”, and it kills whatever has the port at that instant, including the server you restarted a second earlier.
Each agent ships controls for this. Use them, then notice what they cannot express.
Limit it in the agent itself
Settings as of 2026-10-20, from each vendor’s documentation. Check the linked pages before relying on exact names.
Claude Code has allow, ask, and deny rules in settings.json (~/.claude/settings.json, .claude/settings.json, .claude/settings.local.json). Deny wins over ask, which wins over allow:
A Bash rule matches the command text, not the effect: the docs say Bash(rm *) in deny does not catch the same program by path or inside sh -c. A separate sandbox isolates filesystem and network. bypassPermissions mode turns prompts off and is documented for containers and VMs.
Codex sets sandbox_mode (read-only, workspace-write, danger-full-access) and approval_policy (on-request asks before leaving the sandbox, never does not ask) in ~/.codex/config.toml. On macOS the sandbox is the system’s Seatbelt framework. --dangerously-bypass-approvals-and-sandbox removes both and is labelled elevated risk.
Cursor has Run Modes under Settings > Agents > Approvals & Execution: Auto-review (allowlisted calls run, other shell commands run in the sandbox when possible, the rest goes to a classifier), Allowlist, and Run Everything (no sandbox, no classifier). The macOS sandbox is Seatbelt via sandbox-exec, network blocked by default and opened by sandbox.json; permissions.json holds plain-English allow_instructions and block_instructions.
These controls share three limits: they are per tool, so you maintain them three times; they decide by command text or sandbox boundary, not by what a path means on a Mac; and once a command is allowed, it runs without you seeing the file list first.
Give it tools instead of a shell
The alternative is to stop asking the agent to type rm and give it a tool whose job is “list what can be cleaned, with a risk label, and clean what I approve”. MCP (Model Context Protocol) is how all three agents load such tools: a local server exposes named functions, the agent calls them, the server decides what to do.
The difference is where the safety lives. With a shell it is a pattern over the command text. With a tool it is the tool’s own rule: this path is protected, this process belongs to macOS, this action is permanent and needs a click from the owner. rm is not in the agent’s vocabulary for this job.
What Room Service lets an agent see, do, and must ask for
Room Service ships an MCP server built that way. It is off by default. When on, it listens on a Unix socket in your home folder, checks that the connecting process belongs to your user, and nothing is reachable from the network. On an unknown agent’s first tool call, Room Service shows which executable is asking and waits for your Allow; a Deny holds for five minutes.
Tools are grouped into scopes, each with a switch in Settings > Agents. Reading covers storage with a risk label per row, projects, ports, build watchers, Git Sweep, Health Check, the Ops Feed, applications, simulators, and recipes. Acting covers clean_items under the Clean screen’s rules (Trash or Quarantine, dry_run: true first), stop_port, stop_watcher, quit_process, app and startup-item changes, and recipes. Sensitive project details and permanent actions are off by default; a permanent action waits for your Allow in a floating panel for up to two minutes, and Return does not confirm it.
Some rules are structural. Agent stops send SIGTERM only; force_quit_process is a separate permanent action. stop_port reads who holds the port right before signalling and stops nothing if the owner changed from the PID the agent passed. Agents are refused for macOS processes, Room Service’s own, and their own. Developer Vault has no scope, so no setting can expose it. Every agent action and every Allow or Deny lands in the Ops Feed with the agent’s name.
Setting it up for Claude Code, Codex, Cursor
In Room Service, open Settings > Agents and turn on “Allow agents to connect”.
Click “Add to Claude Code” (or Codex, or Cursor). For Claude Code that runs:
claude mcp add --scope user room-service -- /usr/local/bin/roomservice mcp
Codex gets a [mcp_servers.room-service] table in ~/.codex/config.toml, Cursor an entry in ~/.cursor/mcp.json.
Restart the agent and ask something concrete: “What is taking space in Xcode, and what is safe to remove?” The first tool call brings up the Allow prompt.
Keep your shell rules. Scoped tools cover disk, ports, processes, and projects; file edits and test runs still go through the agent’s own permission model.
Reading over MCP is free, except the Projects tools, and so are dry runs. Actions that need Pro in the app need Pro over MCP.
Related
MCP Server: every scope, the approval panel, and the full tool list
