Cua Docs

Process model

How Cua Driver maps one tool surface onto CLI, MCP, and daemon-backed processes.

Cua Driver has several process shapes, but they are adapters around one driver surface: list windows, read accessibility, capture the screen, click, type, record, configure, and report state. The process model places that surface in the operating-system context that can perform it.

All Cua Driver tool execution happens in a long-lived cua-driver serve daemon. MCP stdio and one-shot CLI calls are client adapters: they send requests to the daemon and never execute tools locally.

Three process roles#

The MCP stdio proxy is client-owned. The parent process starts cua-driver mcp, keeps stdin and stdout connected, and sends MCP tool calls over that transport. The proxy forwards every call to the daemon.

The daemon shape is machine-owned. A single cua-driver serve process listens on a local IPC endpoint, such as a Unix socket or named pipe, and keeps driver state in memory while it lives.

The one-shot CLI adapter is call-owned. cua-driver call <tool> connects to the daemon, prints one result, and exits. If the daemon is unavailable, the command fails instead of executing the tool in the CLI process.

These are transport roles around one daemon-owned implementation.

Why a daemon proxy exists#

The daemon-proxy pattern separates the process that speaks to the caller from the process that has the right desktop authority. The proxy handles the client protocol. The daemon performs the GUI work. That matters when the caller can start a shell process but cannot correctly operate the user's desktop.

On macOS, the root issue is TCC, Transparency Consent and Control. Accessibility and Screen Recording grants are attached to a specific app identity, represented by a bundle ID. Grants to CuaDriver.app do not automatically cover every subprocess named cua-driver.

Supported macOS launch modes#

Use one of two supported daemon identities:

  • Standalone: grant permissions to the installed CuaDriver.app and launch its daemon through LaunchServices. See macOS permissions.
  • Embedded: have the macOS app that owns the grants spawn cua-driver serve --embedded directly, then connect an MCP proxy to its socket. See Embedding.

A raw daemon launched outside CuaDriver.app without embedded mode has no stable TCC identity and is unsupported. Do not grant permissions to arbitrary binary paths or use that configuration in production.

If an IDE terminal starts cua-driver directly, macOS attributes that subprocess to the terminal app's bundle, not to CuaDriver.app. The binary is right, but the privacy identity is wrong.

The daemon path fixes the attribution. The daemon is launched through LaunchServices with open -n -g -a CuaDriver, so macOS treats it as part of CuaDriver.app. The MCP stdio process remains where the assistant spawned it, but becomes a thin proxy: it forwards tool calls to the daemon over a Unix socket and returns the daemon's responses. There are two processes, but one tool surface.

Windows has the same shape for a different reason#

On Windows, the daemon solves a session problem rather than a bundle-identity problem. When cua-driver is reached through SSH, the SSH-side process typically lands in Session 0, the non-interactive service session. Session 0 is not the logged-in user's GUI desktop, so it cannot see or operate those windows.

The daemon belongs in the interactive user session instead. It may be kept there by platform autostart machinery such as a Scheduled Task. An SSH-side client can then proxy requests to it. The shape is the same as on macOS: a caller-side process speaks the protocol, while a daemon-side process owns desktop access. The root cause is different.

Session identity and shared state#

A daemon drives one physical machine. Multiple MCP clients can connect at the same time, but they still share the same screen, keyboard, pointer, accessibility tree, and recording machinery. Session identity does not make concurrent control independent. Two agents clicking at once still contend for the same desktop.

Session identity solves a narrower state problem. When a proxy starts, it mints a session identity and stamps forwarded calls with it. The daemon uses that identity to scope mutable state to one client lifetime. Recording ownership, per-session config overrides, and the agent-cursor overlay are all keyed by session.

Four agent sessions share one Windows desktop

Four Hermes agents operate four app windows. Each session has its own cursor overlay, while all four sessions still share the same desktop.

View the original post on X

Cleanup follows the proxy connection, not a final message. The proxy keeps a long-lived control connection open to the daemon. When the proxy exits, even from an ungraceful kill, the kernel closes that connection. The daemon sees EOF and removes state owned by that session. This prevents stale recordings, cursors, and temporary config overrides.

Lifetimes and memory#

The proxy and one-shot CLI processes may come and go, but the daemon owns element-index caches, active recordings, configuration, policy, and cursor state. This makes the execution identity and permission-policy boundary stable across client reconnects.

If the daemon disappears, clients fail closed. They do not construct a fresh tool registry or continue with partial state.