Cua DriverGuideGetting Started

Installation

Install Cua Driver on your Mac

Install Cua Driver with a single command:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/trycua/cua/main/libs/cua-driver/scripts/install.sh)"

The installer drops CuaDriver.app into /Applications and symlinks the binary at ~/.local/bin/cua-driver. The bundle is signed under com.trycua.driver, so TCC grants survive every release.

The install runs without sudo: /Applications is user-writable on personal Macs, and the CLI symlink lives under $HOME. If ~/.local/bin is not on your PATH, the installer detects your shell (zsh / bash / fish) and appends an export PATH=... line to the right rc file. Reload with source ~/.zshrc (or restart your terminal).

Power users: override the install path with --bin-dir (or CUA_DRIVER_BIN_DIR). Use --no-modify-path if you'd rather edit your shell rc yourself.

/bin/bash -c "$(curl -fsSL .../install.sh)" -- --bin-dir=/usr/local/bin    # legacy system path (needs sudo)
/bin/bash -c "$(curl -fsSL .../install.sh)" -- --no-modify-path            # don't touch shell rc

Upgrading from v0.0.x? The new installer puts the CLI at ~/.local/bin/cua-driver. Any existing /usr/local/bin/cua-driver symlink is left in place so existing MCP client configs (Claude Code, Codex, etc.) keep working. Re-register them at your leisure to use the new path.

Verify it worked

cua-driver --version
# cua-driver 0.1.0

cua-driver --help
# OVERVIEW: macOS Accessibility-driven computer-use agent — MCP stdio server.

Grant TCC permissions

Cua Driver needs two permissions:

  • Accessibility — to walk AX trees and dispatch AXUIElementPerformAction.
  • Screen Recording — to capture per-window screenshots via ScreenCaptureKit.

Start the daemon first so TCC attributes the subsequent requests to CuaDriver.app rather than to whatever shell parent launched the CLI:

open -n -g -a CuaDriver --args serve

Then trigger the prompts:

cua-driver check_permissions

macOS raises the Accessibility and Screen Recording system dialogs. Grant both, then re-run the command to confirm:

cua-driver check_permissions
# ✅ Accessibility: granted.
# ✅ Screen Recording: granted.

check_permissions reports the TCC status of the calling process. Inside an IDE terminal (Claude Code, Cursor, VS Code, Conductor) the shell inherits the IDE's TCC responsibility chain, so results can read "NOT granted" even when you've granted both to CuaDriver.app. The daemon-first recipe above sidesteps this because the CLI forwards through the daemon, which runs under CuaDriver.app's bundle id.

If a grant still reads NOT granted after granting in the dialog, open System Settings → Privacy & Security, find CuaDriver.app under Accessibility and Screen Recording, and flip the toggle.

Requirements

  • macOS 14 (Sonoma) or later
  • Apple Silicon (M1/M2/M3/M4) or Intel Mac
  • 50 MB free disk space for the app bundle

Run the daemon

Most workflows benefit from a persistent daemon. Element-indexed workflows require one: the per-pid element cache lives in-process, so one-shot CLI invocations lose it between calls.

# Start the daemon in the background.
open -n -g -a CuaDriver --args serve

# Confirm it's up.
cua-driver status
# cua-driver daemon is running
#   socket: /Users/you/Library/Caches/cua-driver/cua-driver.sock
#   pid: 12345

# Stop it cleanly when done.
cua-driver stop

open -n -g -a CuaDriver --args serve is the recommended form because LaunchServices attributes the process to CuaDriver.app's bundle id, which is what the user actually granted TCC against. cua-driver serve & also works and auto-relaunches itself via open when it detects the wrong TCC context.

Register with an MCP client (optional)

The MCP server is one of two ways to use cua-driver — skip this section if you only want the CLI (cua-driver list_apps, etc.). You can register both later, MCP and CLI work side-by-side.

Cua Driver speaks MCP over stdio. Use cua-driver mcp-config --client <name> to print the right command for your client, or copy one of the snippets below directly:

Clients with a CLI add command

# Claude Code (add --scope project|global as needed)
claude mcp add --transport stdio cua-driver -- ~/.local/bin/cua-driver mcp

# Codex (OpenAI)
codex mcp add cua-driver -- ~/.local/bin/cua-driver mcp

# OpenClaw
cua-driver mcp-config --client openclaw | sh

Clients configured via a config file

Cursor, OpenCode, and Hermes all configure MCP servers via files. Use mcp-config to print the exact snippet, paste it into the right path:

# Cursor — paste into ~/.cursor/mcp.json (or .cursor/mcp.json for project scope)
cua-driver mcp-config --client cursor | pbcopy

# OpenCode (sst/opencode) — paste into opencode.json at the project root
cua-driver mcp-config --client opencode | pbcopy

# Hermes (NousResearch) — paste into ~/.hermes/config.yaml,
# then run `/reload-mcp` inside Hermes
cua-driver mcp-config --client hermes | pbcopy

For any other client that accepts the standard mcpServers JSON shape:

cua-driver mcp-config | pbcopy

Pi (badlogic/pi-mono) does not support MCP natively. Use cua-driver as a plain CLI from inside Pi instead — cua-driver list_apps, cua-driver click '{...}', etc. — which is exactly the shape Pi is built around. Run cua-driver mcp-config --client pi for the full guidance, or see the Quickstart for CLI usage.

The client spawns cua-driver mcp on demand once registered.

Agent skills (auto-wired)

The bundle ships an Anthropic-format SKILL.md pack at /Applications/CuaDriver.app/Contents/Resources/Skills/cua-driver/. The installer detects the agents you have and creates symlinks pointing them at the bundle so the skill auto-loads:

AgentSkills directoryAuto-wired by installer
Claude Code~/.claude/skills/cua-driverwhen ~/.claude/skills/ exists
Codex~/.agents/skills/cua-driverwhen ~/.codex/ exists
OpenClaw~/.openclaw/skills/cua-driverwhen ~/.openclaw/ exists
OpenCode~/.config/opencode/skills/cua-driverwhen ~/.config/opencode/ exists

The installer creates the skills/ subdirectory if missing, then drops the symlink. Symlinks survive auto-updates because /Applications/CuaDriver.app is replaced atomically — the link target stays valid across releases.

OpenCode also reads ~/.claude/skills/ natively, so users who have both Claude Code and OpenCode get the skill via the Claude path automatically.

Agents without a SKILL.md auto-loader

Cursor, Hermes, and Pi each use a single-file system prompt (different format) instead of a skills directory. We don't auto-wire these because:

  • Cursor (~/.cursor/rules/*.mdc): rules expect a different frontmatter shape (description/globs/alwaysApply), not Anthropic's name/description. Symlinking the SKILL.md as .mdc works but the frontmatter renders as a noisy comment block.
  • Hermes (~/.hermes/SOUL.md) and Pi (~/.pi/agent/SYSTEM.md): replace the entire system prompt — symlinking would clobber any custom prompt you've set.

For these, paste the skill content manually into whatever instructions file the agent uses:

cat /Applications/CuaDriver.app/Contents/Resources/Skills/cua-driver/SKILL.md | pbcopy

Uninstall

# Stop the daemon if running.
cua-driver stop 2>/dev/null

# Remove the app and the CLI symlink.
rm -rf /Applications/CuaDriver.app
rm -f ~/.local/bin/cua-driver
# legacy install path (only present on installs from before v0.1.0)
sudo rm -f /usr/local/bin/cua-driver 2>/dev/null || true

# Optional: remove config + telemetry state.
rm -rf ~/.cua-driver
rm -rf ~/Library/Application\ Support/Cua\ Driver
rm -rf ~/Library/Caches/cua-driver

# Optional: remove legacy LaunchAgent from older installs (≤ v0.0.5).
launchctl unload ~/Library/LaunchAgents/com.trycua.cua_driver_updater.plist 2>/dev/null
rm -f ~/Library/LaunchAgents/com.trycua.cua_driver_updater.plist

Troubleshooting

cua-driver: command not found~/.local/bin isn't on your PATH. Add it and reload (see the first-time install callout above).

Permissions dialogs reappear after every launch — macOS is attributing the process to a different bundle id than the one you granted. Run cua-driver diagnose and paste the output when filing an issue; it reports cdhash, team id, and which bundle TCC is checking against.

Daemon won't start — another daemon may already be bound to the socket. Check with cua-driver status and stop it with cua-driver stop. For stale lock files after a crash, the daemon's own probe detects those and proceeds.

Ready to drive an app? Head to the Quickstart.

Was this page helpful?