
Desktop automation agents — tools that drive the UI through Accessibility APIs, capture the screen, or synthesise keyboard and mouse input — hit macOS's TCC framework the moment they start. TCC (Transparency, Consent, and Control) gates Accessibility, Screen Recording, Full Disk Access, microphone, camera, and input monitoring. Without the right grants, the agent hangs, returns empty frames, or fails silently.
This guide maps which services each capability needs, how to grant and verify them on a hosted Mac, and how to handle updates that invalidate the grants.
Start by confirming the architecture of your agent binary:
file /path/to/your-agent-binary
# Universal: Mach-O universal binary with 2 architectures: [x86_64 arm64]
# Intel only: Mach-O 64-bit executable x86_64
# Apple silicon only: Mach-O 64-bit executable arm64
If the agent depends on Xcode 27, visionOS, or any tool that explicitly requires Apple silicon, choose a physical M-series plan. Agents that are arm64-agnostic — Electron apps, Python/Node scripts, Intel-compiled utilities — fit the $29 virtual Intel macOS.
Setting TCC up correctly for a brand-new agent type is the kind of work covered by the Managed Build Mac ($249/month): one M-series Mac, CI setup, backups, monitoring, and 30 minutes of monthly support.
macOS checks Accessibility trust and Screen Recording access when a process first requests the service. Granting permission after the process is already running does not promote the running process; you must restart it after flipping the toggle.
tccutil, built into macOS, is the supported command for resetting TCC entries. After a reset, macOS prompts again the next time the app asks for the service:
# Reset Accessibility for one app (by bundle ID):
tccutil reset Accessibility com.example.myagent
# Reset Screen Recording for all apps (service name is ScreenCapture):
tccutil reset ScreenCapture
If your agent is a bare binary with no bundle, macOS uses the binary path as its TCC identifier. Change the path and the agent is, for TCC's purposes, a new application. To see denials in real time:
log show --last 5m \
--predicate 'subsystem == "com.apple.TCC" AND eventMessage CONTAINS "denied"' \
--info
A denial in this log confirms the symptom is a missing permission, not a bug in the agent.
Grant only the services your agent actually uses. Extra permissions are a security liability and add tccutil reset calls to every deploy.
| Agent capability | Required TCC service | System Settings path |
|---|---|---|
| Synthetic keyboard or mouse events | Accessibility | Privacy & Security › Accessibility |
| AXUIElement queries (read UI text, state, hierarchy) | Accessibility | Privacy & Security › Accessibility |
| Screenshots, pixel reads, screen capture | Screen Recording | Privacy & Security › Screen & System Audio Recording |
| Reading other apps' window titles | Screen Recording | Privacy & Security › Screen & System Audio Recording |
| Reading files outside the app's sandbox | Full Disk Access | Privacy & Security › Full Disk Access |
| Microphone input | Microphone | Privacy & Security › Microphone |
| Camera input | Camera | Privacy & Security › Camera |
| Global keyboard hooks | Input Monitoring | Privacy & Security › Input Monitoring |
Start with Accessibility and Screen Recording only. Add the rest after you see a specific denial in the TCC log.
# App bundle:
defaults read /Applications/MyAgent.app/Contents/Info CFBundleIdentifier
# Output example: com.example.myagent
# Bare binary: no bundle ID — TCC keys on the full executable path.
For a shell script that execs a Python or Node interpreter, the TCC identifier is the interpreter (/usr/bin/python3, /usr/local/bin/node), not the script. Prefer bundling the agent as a signed .app so the identifier stays stable across releases.
.app, and add it.For launchd agents that start at login, run launchctl bootout (or launchctl unload on older systems), grant the permission, then launchctl bootstrap/load to pick up the new trust state.
Same path: System Settings › Privacy & Security › Screen & System Audio Recording. Screen Recording cannot be pre-granted by a script on a single machine — it requires an interactive toggle. In an MDM-managed fleet, a PPPC configuration profile can pre-approve specific bundle IDs.
Run these after granting and before you put the agent on a schedule:
# Screen Recording check: a real capture produces a non-trivial PNG.
screencapture -x /tmp/tcc_check.png
python3 - <<'PY'
import os
sz = os.path.getsize('/tmp/tcc_check.png')
print('Screen Recording OK' if sz > 5000 else f'DENIED or empty ({sz} bytes)')
PY
rm -f /tmp/tcc_check.png
# Accessibility check: list foreground apps via System Events.
osascript -e \
'tell application "System Events" to return name of every process whose background only is false'
# Returns a list if Accessibility is granted; errors with
# "osascript is not allowed assistive access" if denied.
A black or near-empty PNG means Screen Recording is denied. An "assistive access" error on the osascript call means Accessibility is denied — most often because the agent binary path on disk does not match the entry in System Settings.
When you ship a new agent build, macOS can invalidate TCC grants if the code signature or path changes. Build the reset into the deploy script so the next launch asks fresh:
#!/usr/bin/env bash
# deploy.sh — run after staging the new agent build at /tmp/MyAgent-new.app
set -euo pipefail
AGENT_ID="com.example.myagent"
AGENT_APP="/Applications/MyAgent.app"
PLIST="$HOME/Library/LaunchAgents/com.example.myagent.plist"
echo "Stopping agent..."
launchctl bootout "gui/$(id -u)/${AGENT_ID}" 2>/dev/null || true
echo "Resetting TCC entries..."
tccutil reset Accessibility "$AGENT_ID"
tccutil reset ScreenCapture "$AGENT_ID"
echo "Installing new build..."
rm -rf "$AGENT_APP"
cp -R /tmp/MyAgent-new.app "$AGENT_APP"
echo "Launching for first-run prompts..."
open "$AGENT_APP"
# Grant Accessibility and Screen Recording when prompted, then:
# launchctl bootstrap gui/$(id -u) "$PLIST"
| Symptom | Likely cause | Fix |
|---|---|---|
| Agent stalls at startup with no visible UI | TCC prompt rendered in a session you cannot see | Grant the permissions once interactively via the browser console desktop, then start headlessly |
| Accessibility works, screen capture returns black frames | Screen Recording missing | Toggle Screen & System Audio Recording for the agent |
| Permissions disappear after a release | Code signature or binary path changed | Include tccutil reset in the deploy script and re-grant |
| Child process denied although parent is allowed | Different signing identity | Grant TCC to the child binary directly |
osascript returns "not allowed assistive access" | Accessibility denied for the invoking shell | Grant Accessibility to Terminal (or your automation host) |
For more than a handful of Macs, PPPC profiles delivered by MDM can pre-approve Accessibility for specific bundle IDs and stop per-machine clicking. Mention fleet size when requesting an enterprise quote.
TCC setup is one-time work per agent build. It adds no ongoing runtime cost.
| Plan | Fit for desktop agents |
|---|---|
| $29/month virtual Intel macOS | x86_64 or universal agents; grant Accessibility and Screen Recording via the browser console desktop session |
| Physical M-series from $95/month | Required for arm64-only binaries and tooling that needs Apple silicon; exact chip/RAM/disk is confirmed in the quote |
| Managed Build Mac $249/month | One M-series Mac, CI setup, backups, monitoring, 30 minutes of monthly support — useful for the first TCC bring-up |
| Managed Mac Team $999/month | Two M-series Macs, two CI runners, team setup, backups, monitoring, one hour of monthly support |
Input Monitoring (global keyboard hooks) is treated more strictly than Accessibility. Confirm the requirement before locking in a plan.
The virtual Intel macOS is reached through the browser console desktop; inbound public SSH to that VM is not currently offered, so TCC prompts are handled in that session. Outbound connections from the VM to your CI service run normally.
file against the agent to confirm architecture.screencapture and osascript checks above.tccutil reset Accessibility and tccutil reset ScreenCapture steps to the deploy script.Configure your plan and start →
Related guides: Idempotent jobs for Mac agents · Retry budget for Mac agents · Self-hosted GitHub Actions runner on Mac