DEV Community

Auten
Auten

Posted on

Why your computer-use agent silently does nothing on macOS

You wire a computer-use MCP server into Claude Code, Cursor or Codex, ask it to click a button, and the tool call comes back "ok". Nothing on screen moves. Or the screenshot it reasons over is just your wallpaper.

Nine times out of ten this is not the model and not the MCP server. It is macOS privacy permissions (TCC), and the frustrating part is that most of the failures are silent. This is the checklist we wish we had on day one.

Two permissions, two different failures

A screen-driving agent needs two separate grants in System Settings > Privacy & Security:

Accessibility lets a process read other apps' UI trees (buttons, fields, labels) and post synthetic mouse and keyboard events.

  • Without it, Accessibility API calls fail with kAXErrorAPIDisabled (-25211). Many tools swallow that and return an empty element list.
  • Synthetic events posted with CGEventPost are dropped without any error. The click "succeeds" and nothing happens.

Screen Recording lets a process capture other apps' pixels.

  • Without it, a capture typically shows the desktop and menu bar, but not the contents of other apps' windows. A vision model then confidently describes an empty desktop.
  • Less obvious: window titles of other apps (kCGWindowName from CGWindowListCopyWindowInfo) are withheld too, so "find the window called Invoice" fails even if you never take a screenshot.

If your agent can list elements but its screenshots look empty, you are missing Screen Recording. If it can see everything but clicks do nothing, you are missing Accessibility.

The grant usually belongs to the app hosting your agent

This is the part that costs people an afternoon. macOS attributes a permission request to the "responsible" app, which for a command-line tool is usually the GUI app that launched it, not the binary itself.

  • MCP server started by Claude Code in Terminal or iTerm: the grant Terminal (or iTerm) has is what counts.
  • Started by Cursor, VS Code or a desktop chat client: that app's grant counts.
  • Started by launchd (a LaunchAgent): the process itself is responsible, so the grant has to be on the exact binary in ProgramArguments.

So "I gave node Accessibility and it still fails" is normal: node was never the responsible process. Grant the host app, restart it fully (quit, not just close the window), and try again. Permission changes are not picked up by already running processes.

The flip side is worth saying plainly: granting Accessibility to Terminal means everything you run from Terminal can drive your Mac. If that bothers you, run the agent from a dedicated terminal app you use only for it.

Check what your agent actually has

Run this from the same app that launches your agent, so it inherits the same responsible process:

cat > /tmp/tcc-check.swift <<'EOF'
import ApplicationServices
import CoreGraphics
print("Accessibility:   ", AXIsProcessTrusted())
print("Screen Recording:", CGPreflightScreenCaptureAccess())
EOF
swift /tmp/tcc-check.swift
Enter fullscreen mode Exit fullscreen mode

Both functions only check, they never show a prompt. false on either line explains most "it does nothing" reports. (Needs the Xcode command line tools for swift.)

To watch macOS decide in real time while your agent tries to act:

log stream --debug --predicate 'subsystem == "com.apple.TCC"'
Enter fullscreen mode Exit fullscreen mode

The log shows which process TCC evaluated and whether it was allowed, which settles the "who is responsible" question without guessing.

The toggle is on, and it still fails

Three causes we keep running into:

  1. The binary changed. A grant is tied to the code signature. For an ad-hoc signed binary that means its exact hash, so every rebuild, or a package-manager upgrade that replaces the binary, silently invalidates the grant while the switch in Settings still shows "on". Fix: remove the entry with the minus button and add it again, or sign your own tools with a stable identity (a free Apple Development certificate is enough).
  2. Stale entries. Reset one service for one app and let it ask again:
   tccutil reset Accessibility com.apple.Terminal
   tccutil reset ScreenCapture com.apple.Terminal
Enter fullscreen mode Exit fullscreen mode

Use the bundle id of whichever app hosts your agent.

  1. A pending system prompt. Recent macOS versions periodically ask you to reconfirm screen access for some apps. An unattended agent cannot answer that dialog, so if captures suddenly go blank after weeks of working, look at the screen for a waiting prompt first.

A short pre-flight before you blame the model

  • Which app launches the agent? That app needs both grants.
  • Fully quit and reopen it after granting.
  • Run the Swift check from that app: both true?
  • Screenshots empty -> Screen Recording. Clicks ignored -> Accessibility.
  • Toggle on but failing after an update -> remove and re-add the entry.

Why we care

We build Auten, an MCP server that gives Claude Code, Codex, Cursor or any MCP client hands on a real screen: your computer and your Android phone (beta). On macOS, its installer runs a permissions setup step and auten status shows what is connected and what is missing, because silent permission failures were the first thing that broke for us too.

If you are hitting a TCC case not covered here, describe it in the comments. We collect these.

Top comments (0)