Computer use for macOS

Give any agent real hands on a Mac

Proctor lets any model or harness read what's on screen and drive it; click, type, check what the app actually drew. It works on background windows, without taking over your machine. The same primitives make it a proper Mac test harness.

Runs locked or unattended Accessibility + Apple Events plane ScreenCaptureKit with freshness MCP, stdio or HTTP
01 / Any agent

Computer use for any agent

Any MCP host, any harness. Proctor hands the model on the other end a real Mac to work: the accessibility tree to read, the pointer and keyboard to drive, the screen to check what happened. It isn't a script. It's hands.

02 / Any test

A test harness that measures itself

The same tools, plus the discipline testing needs. Settle before you assert, replay to tell flaky from broken, measure a build against its mock with real numbers. You get a result you can trust, and the gaps stated instead of hidden.

What it does

Primitives that hold up under a real run

Every tool returns evidence, not a promise. Here is what that looks like.

Runs in the background

It works on windows you're not looking at

Proctor drives through the accessibility and Apple Events planes. They don't need a window in front, or even on screen, so it reaches background and occluded windows and other Spaces without stealing your focus. You keep working; it keeps going.

Ledger · background
window Ledger · Preferences Space 2 · occluded
toggle Round up spare change
button Run reconciliation
Proctor
focus unchanged · no window raised
Runs while locked

It keeps going when the Mac is locked

The planes Proctor drives through don't need the screen, so a run carries on while the Mac is locked and you're away from it. When a task genuinely needs the machine unlocked, an optional login-path capability opens a short turn bounded by a timeout, does the work, and relocks; the password prompt stays as the fallback, so nobody gets locked out.

proctor_unlock · turn
screen locked · you're away
agent still running · driving through AX + Apple Events
turn  authorized · ttl 90s // a crashed caller can't leave it open
unlock right  evaluated → granted
on finish  relock · password prompt stays as fallback
gated: login-path plugin, armed disclosed in the methodology
Capture

A screenshot you can trust, or a flag

Every window capture carries its freshness: the frame status, the dirty rects, the content rect. A stale frame gets flagged, not handed back as if it were current. A green run never rests on a picture of the old state.

capture · window 0x3F2
SCFrameStatus.complete
status  complete
dirtyRects  [ {0,0,480,300} ]
contentRect  {0,0,480,300} · scale 2.0
trustworthy  true
Read the real layout

Geometry, and the style behind it

The accessibility tree gives you roles, values, and frames. For an app you own, an in-process reflector adds the resolved colours, fonts, corner radius, constraints, and the CALayer model and presentation values, with a render revision. Those are the numbers that check a build against a mock and stand behind a real assertion.

snapshot + reflector
AXWindow 1200×760
AXGroup toolbar
AXButton "New"
AXButton "Save" · {24,690,96,28}
AXTable 12 rows
Reflector · rev 118
fill#F1552A
labelSF Pro · 600 · 13pt
radius7.0
layer.model{24,690,96,28}
layer.pres{24,690,96,28}
opacity1.0
The tools

One tool per decision, actions batched

Attach to an app, snapshot a pruned tree with stable ids and since-revision diffs, find by predicate, act on a batch of steps. A six-step flow is one call; each step settles and reports its outcome, a post-state hash, and what changed.

proctor_act
act(// one call, six steps, each settles
) → 6 ok · state 0x9c…21
the full surface
attachsnapshotfind actcapturewait assertflowstability inspectdoctor
actuation the AX + Apple Events way, so Secure Event Input doesn't block it where synthetic keystrokes would be dropped.
Settle, then assert

No bare sleeps, no guessed waits

Settle is a conjunction: quiet frames, and no relevant accessibility notifications, and the app's own idle signal when it has one, and a timeout. Determinism is measured by replay, so a race is filed as flaky, not as a bug.

settle · step 3
frames quiet3 / 3
ax notificationsnone
app idle signalidle
within timeout412ms
settledreason: allSignalsQuiet
Permissions

Granted once, to a stable identity

Proctor installs as its own signed background agent, so macOS attributes the grant to Proctor itself, not to whatever tool is driving it. Grant it once, drag it into the list, and it keeps working when you change the model or the harness.

responsible process
Proctor.appDeveloper ID · launchd user agent · stable identity
Accessibility granted → Proctor
Screen Recording granted → Proctor
driver today
Claude → shim → Proctor
swap the driver tomorrow; grants stay put
Remote

Drive it from another machine, on your terms

There's a built-in MCP server over HTTP, with optional bearer-token auth. Local by default; you open it up only when you mean to.

proctor serve --remote
$ proctor serve --remote --token $PROCTOR_MCP_TOKEN
listening · 127.0.0.1:8787 · POST /mcp · GET /health
# from your harness
POST /mcp
Authorization: Bearer ••••••••
Origin: exact-match, or refused
local by default tunnel when you mean to
For testing

Built to run a real test campaign

Driving an app is the easy half. Proctor carries the parts a test actually needs: a way to know a step finished, a way to tell a flake from a defect, and a report that admits what it didn't cover.

accessibility tree
layer geometry
captured pixels · disagreement = a finding
01Exploratory sweep to map the app before you script a thing.
02Acceptance criteria turned into executable flows.
03State-matrix coverage: light and dark, text sizes, displays.
04Accessibility audit against the platform's own rules.
05Fidelity checking: the build measured against its mock, not eyeballed.
06Determinism and flake measured by replay, with the first divergence named.
07Honest coverage; skipped is never counted as passed.
08The observer effect disclosed, because setting the accessibility flag changes what you're testing.
09Tri-observer disagreement as a defect oracle: tree, layout and pixels disagreeing about one instant is a finding, not noise.
10Given a suite, every case is traced to the flow and the assertion that verifies it.

Works with any MCP host and any harness. If your model can call a tool, it can drive a Mac.

Claude DesktopClaude CodeCursorYour own harnessstdio or HTTP

Give your agent a Mac to work

Install the agent, point your model at it, and let it read and drive a real app.

Get started