Skip to main content
Glama

computer-use

CI

A model-agnostic, vision-first Computer Use runtime for macOS (macOS 14+).

External agents (Claude Code, Pi, OpenCode, Codex CLI, or any model) drive the desktop through a small JSON-RPC surface: capture screenshots, act on them with clicks/keys/typing, and stay safe behind session locking, stale-frame protection, and trace redaction. The runtime has no model dependency — it never calls an LLM. The loop is always: agent observes → agent decides → runtime executes.

+----------------+   JSON-RPC 2.0    +-------------------+   line-JSON   +---------+
| agent (Pi,     | <===============> | cu-daemon         | <===========> | cubridge|
| OpenCode, ...) |   ~/.computer-use | sessions, locking, |  Unix pipe   | Swift:  |
| via SDK / MCP  |   /runtime.sock   | stale-frame, trace |              | SCK     |
+----------------+                   +-------------------+              | capture |
                                     | cu-runtime · cu-driver-macos      +---------+

What's inside

Component

Where

Purpose

cu CLI

crates/cu-cli

daemon lifecycle, session, observe/act, traces

Daemon

crates/cu-daemon

JSON-RPC 2.0 over a Unix socket (current-user only)

Runtime

crates/cu-runtime

sessions, control lock, action queue, stabilizer, pause/resume/takeover/stop

macOS driver

crates/cu-driver-macos

capture, mouse, keyboard, displays, clipboard, permissions

Swift bridge

crates/cu-driver-macos/swift

ScreenCaptureKit + clipboard + AX (the only Swift in the project)

Trace recorder

crates/cu-trace

session JSONL traces with redaction

TypeScript SDK

packages/sdk-typescript

ComputerUseClient for Node agents

MCP Server

packages/mcp-server

7 tools (observe/act/inspect/session/cancel/trace) as image content blocks

Pi Extension

packages/pi-extension

4 tools with real image content blocks + 8 slash commands, abort + lifecycle

OpenCode adapter

packages/opencode-adapter

companion CLI (cu-opencode) + official MCP config for OpenCode

Inspector

apps/cu-inspector

minimal local dashboard (http://127.0.0.1:8420)

Related MCP server: Daimon

Quick start

# 1. build
cargo build --release

# 2. grant permissions once (see docs/permissions.md):
#    System Settings → Privacy & Security → Screen Recording → add cubridge

# 3. start the daemon
cu daemon start

# 4. drive it
cu doctor
cu observe --include-image --image-out /tmp/screen.jpg   # first observe auto-creates a session
cu move 500 400
cu click 500 400
cu type "hello"            # text is redacted in traces
cu session stop            # only a client holding the session's control token may stop it

Sessions are created on first use. The first observe/act from any client auto-starts a session when none is active (the CLI resolves the active session first and only starts when the daemon reports SESSION_NOT_FOUND). The daemon records who started it — every client sends its identity (client_id / client_name / client_instance_id) with session start, and session status returns the owner. Access control is capability-based, not identity-based: only a client holding the session's control token can stop or take over the session; mutating operations require the control token, and sensitive reads require the observation token (a second client trying to use the session without one gets CONTROL_LOCKED under the default policy — see the Pi extension's COMPUTER_USE_EXISTING_SESSION_POLICY).

Type actions are redacted by default: traces record text_redacted: true and a character count, never the text itself. To log full text (e.g. a development environment you trust), run the daemon with dev mode on — see Trace redaction.

Validation Focus

The current round is the Pointer Isolation closing phase: no new architecture, no new benchmark tasks, no new product capabilities. The identified implementation issues (Swift crop leak, crop-size math, observe/inspect target refresh, human-interrupt telemetry, physical-fallback races, drag/scroll cancellation, double-click semantics) are fixed and tested; the priority is real macOS acceptance of these exact behaviors, in order:

  1. Pointer Isolation — agent clicks/moves never move the user's system cursor (DirectPositionEvent), the ghost cursor is excluded from captures, and the physical fallback preserves and restores the user's cursor. See docs/pointer-isolation.md.

  2. Click Accuracy — ≥32px target hit rate on the Browser and Native target boards (benchmarks/target-boards).

  3. Human Interrupt — Human Always Wins: a real hardware event stops the agent immediately, the cursor is never yanked back, and the P0-4 KPIs (event_detection_latency_ms, human_to_takeover_ms, human_to_input_stop_ms) are real measured numbers.

  4. Window Isolation — captures are scoped to the session target window (including windows wider than max_width and windows that moved), with zero stale or cross-app captures.

  5. Keyboard Safety — the strict focus guard re-checks bundle + pid + window live before every key/type/clipboard event; nothing is ever sent to an unfocused app.

Real-machine acceptance (sections A–D + double-click) and honest NOT VERIFIED statuses are recorded in docs/acceptance-manual.md (Round 7 results) and the round's closing report in docs/round7-acceptance-report.md.

The four tools (any agent)

Tool

Purpose

computer_observe

Capture the screen → frame_id + image + metadata

computer_act

Execute actions on a frame (click, move, type, key, scroll, drag, wait)

computer_inspect

Crop a region of a stored frame (vision detail, no DOM/XPath/OCR)

computer_session

Start / status / pause / resume / takeover / release / stop

Plus trace inspection (trace_list, trace_get, trace_export, trace_replay) and runtime introspection (health, permissions, displays, pointer, active-application).

Everything the runtime enforces — frame staleness, coordinates in bounds, pause, takeover, session state, the control lock — is enforced server-side, not by the client, so every adapter gets the same guarantees.

Security model

  • Socket: Unix domain socket at ~/.computer-use/runtime.sock, mode 0700 — only your user can connect.

  • Sessions: one active session at a time (control lock). Auto-creation is an adapter convenience (SDK/CLI/MCP/Pi resolve status first and start only on SESSION_NOT_FOUND) — the raw computer.observe / computer.act methods never create a session. The creator is recorded as the session's owner for diagnostics; access is capability-based — only a client holding the session's control token may stop or take it over, and a client without a valid token is refused with CONTROL_LOCKED. Every observe/act carries a session_id. Actions on a stale, paused, taken-over, or stopped session are rejected with a specific error code.

  • Capability tokens: session start returns a session's two tokens exactly once (each 256-bit CSPRNG): an observation_token for sensitive reads and a control_token for mutating operations (which also opens reads). Knowing a session ID grants no observation or control permission — the daemon verifies SHA-256 hashes of presented tokens and never repeats them after start. status never re-issues them, and stop or a daemon restart invalidates them. The CLI persists session credentials to files with mode 0600; the SDK keeps them in memory only.

  • Existing sessions default to reject: a client that finds a session it does not own must not silently attach. The SDK's ensureSession offers explicit, token-bearing opt-ins only: read_only requires the foreign session's observation token (attachReadOnly(sessionId, observationToken) first) and attach_with_token requires its control token — a session id alone grants nothing. Adapters expose no token-less policy: the Pi extension's COMPUTER_USE_EXISTING_SESSION_POLICY is reject only (the pre-0.3 read_only/attach values print a deprecation warning and behave like reject).

  • Daemon admin token: runtime.shutdown requires a per-install admin token (256-bit CSPRNG, persisted 0600 at daemon startup) — only the daemon manager (CLI / LaunchAgent) holds it; a corrupt store refuses startup rather than leaving the daemon unstoppable.

Capability matrix

Operation

Session ID alone

Observation token

Control token

Admin token

status

OBSERVATION_TOKEN_REQUIRED

observe / inspect

OBSERVATION_TOKEN_REQUIRED

trace.list / trace.summaries / trace.get / trace.export / trace.replay

OBSERVATION_TOKEN_REQUIRED (session-scoped: the token must belong to the addressed session)

✅ *

✅ *

trace.admin_list (cross-session listing)

DAEMON_ADMIN_TOKEN_REQUIRED

act / cancel / pause / resume / takeover / release / stop

CONTROL_TOKEN_REQUIRED

runtime.shutdown

DAEMON_ADMIN_TOKEN_REQUIRED

* the session's own observation/control token, for the addressed session only — a token from session A can never read session B's trace.

The control token includes observation permission (it verifies for reads too); the observation token never grants mutation. Token errors are deliberately non-descriptive (INVALID_* never says which token was wrong).

  • Stale frames: acting on anything but the session's current frame is rejected (STALE_FRAME) under the default strict policy; the visual_match policy (env COMPUTER_USE_STALE_POLICY) additionally allows an older frame whose content still matches the live screen. Live visual comparison + app-change + age backstop always run on top.

  • Bounds: actions outside the display are rejected (OUT_OF_BOUNDS).

  • Redaction: type records { text_redacted: true, character_count } in traces; full text only under an explicit opt-in. Clipboard contents are never recorded, and no capability token ever appears in a trace.

  • Takeover: a human can grab the mouse at any time; the session flips to user_takeover and the runtime refuses further actions. resume cannot bypass it — the agent must release first (USER_TAKEOVER_ACTIVE).

  • See docs/protocol.md for the full error table and docs/permissions.md for the permission gotchas (including the "rebuild cubridge → re-grant Screen Recording" one).

Trace redaction

Default: on. cu daemon start runs with redaction. To record full typed text in traces (development only):

COMPUTER_USE_TRACE_DEV_MODE=1 cu daemon start

Each trace entry keeps redaction: { text_redacted, character_count } so you can audit what happened without exposing secrets.

Trace recording policy (COMPUTER_USE_TRACE_MODE): best_effort (default — a trace write failure degrades the trace and computer.act reports trace: {degraded: true, warnings}), required (session start / act fail if the trace cannot be recorded), or disabled (no recorder).

Layout

~/.computer-use/
├── runtime.sock        # JSON-RPC socket (0700)
├── bin/cubridge        # compiled Swift bridge
├── frames/             # captured frames (per session, named s_<id>_<n>.jpg)
├── traces/             # s_<id>.jsonl session traces
└── daemon.log

Experimental / Frozen Task Benchmark

STATUS: EXPERIMENTAL / FROZEN. The 30-task cu-bench suite is frozen: its tasks, runner, fixtures, and schema are kept, but no new tasks or evaluators are added. It remains a reference for regression comparisons — it is not the current validation focus (see Validation Focus).

A repeatable macOS desktop task benchmark (cu-bench, 30 tasks across TextEdit / Finder / System Settings / Calculator / Safari / cross-app) runs a real model host against the real desktop and judges each task only through declarative evaluators — see benchmarks/README.md. Results are never hand-edited; traces are read with the observation token and failure categories come from the trace events alone.

node benchmarks/runner/cu-bench.mjs list               # the 30 tasks
node benchmarks/runner/cu-bench.mjs run --suite smoke  # 10-task smoke suite
node benchmarks/runner/cu-bench.mjs report             # summary/failures/metrics

Per-session forensics without a browser:

cu trace analyze <session-id>            # metrics + failure category + timeline
cu trace analyze <session-id> --json     # full structured analysis

Tests

cargo test --workspace                    # Rust: core, driver, runtime, daemon protocol, ownership matrix, trace analysis
cargo test -p cu-daemon --test integration -- --ignored   # live security-matrix test
pnpm install && pnpm -r build && pnpm -r test             # SDK / Pi / OpenCode adapter / MCP suites
pnpm run ci                               # the full TypeScript gate in one command:
                                          #   check:protocol → build → typecheck → lint → test
                                          #   (a *repo* script; not `pnpm ci`, which is the
                                          #   lockfile-only install command)
pnpm scan:secrets                         # gitleaks over the whole repo
./scripts/smoke.sh                        # strict smoke: exit 0 = all green, 1 = any gate failed,
                                          # 2 = usage error — every gate is judged by exit code
                                          # only (no grep-guessing); --fast skips the slow
                                          # artifact gates (npm tarballs + release checksums),
                                          # --self-test proves a failing gate fails the run

Real-environment acceptance (needs a logged-in GUI session, Screen Recording + Accessibility permissions, daemon running, no active session):

node scripts/pi-host-acceptance.mjs       # Pi extension, real code, real daemon/screen — 32 checks
node scripts/opencode-mcp-acceptance.mjs  # real computer-use-mcp binary over stdio, real daemon/screen — 17 checks
node scripts/ownership-scenario-a.mjs     # ownership: MCP-owned session vs. the Pi extension — 6 checks

See docs/acceptance-manual.md for the full manual checklists (Pi 20 steps, OpenCode 14 steps, ownership A/B/C) and the results recorded during the round-2 through round-5 acceptance runs. Round 6's real-host Pi/OpenCode run is recorded as NOT VERIFIED (this session has no interactive WindowServer — cu observe times out at the ScreenCaptureKit bridge), with the daemon-level trace verification that was possible live documented in the same place.

Documentation

License

MIT (see LICENSE).

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Standalone MCP server that gives AI agents full GUI control over macOS — screenshots, mouse, keyboard, apps, clipboard, and multi-display — with zero private dependencies.
    18
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    A local daemon for macOS that gives any MCP-capable AI client eyes, hands, and a face — screen capture, accessibility tree, mouse/keyboard actions, and an overlay — with a built-in security ceiling.
    1
    AGPL 3.0
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server that enables AI to fully control macOS — mouse, keyboard, terminal, screenshots, window management, UI element detection, and provides AI-optimized information reporting.
    32
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    Provides a local MCP bridge for AI assistants to control a Mac by observing screen state and performing actions like mouse movement, clicking, typing, and opening URLs.
    12
    MIT

View all related MCP servers

Related MCP Connectors

  • Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.

  • Let ChatGPT, Claude & Cursor use your Mac: email, calendar, iMessage, Teams, files. Local, free.

  • A paid remote MCP for AI agent browser MCP session, built to return verdicts, receipts, usage logs,

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/taotao135791-bit/oc-computer-use'

If you have feedback or need assistance with the MCP directory API, please join our Discord server