Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/content/docs/how-to-guides/driver/connect-your-agent.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ Register the stdio server:
openclaw mcp set cua-driver '{"command":"cua-driver","args":["mcp"]}'
```

This registers the driver as a normal gateway-spawned MCP server; on macOS it does **not** inherit OpenClaw.app's permission grants. For that, the app process must spawn `cua-driver --embedded` directly; see [Embedding](/reference/cua-driver/embedding).

Verify: restart OpenClaw and confirm `cua-driver` is available in the MCP server list.

## OpenCode
Expand Down
4 changes: 4 additions & 0 deletions docs/content/docs/reference/cua-driver/cli-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,15 @@ On macOS, shell-spawned MCP processes can auto-launch and proxy through a CuaDri
| Name | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| `--socket` | String | — | Override the daemon socket or named-pipe path used by the proxy fallback. |
| `--host-bundle-id` | String | — | Advisory host bundle id label echoed in check_permissions output (embedded mode). |

**Flags:**

| Name | Description |
| ---- | ----------- |
| `--no-daemon-relaunch` | Stay in-process instead of proxying through a daemon. |
| `--claude-code-computer-use-compat` | Expose the Claude Code computer-use compatibility screenshot surface. |
| `--embedded` | Run embedded inside a host app: inherit the host's TCC grants, never prompt or relaunch. Also CUA_DRIVER_EMBEDDED=1. |

### `cua-driver serve`

Expand All @@ -91,12 +93,14 @@ The daemon owns per-process state such as element-index caches, recording state,
| ---- | ---- | ------- | ----------- |
| `--socket` | String | — | Override the daemon socket or named-pipe path. |
| `--pid-file` | String | — | Override the pid-file path on Unix targets. |
| `--host-bundle-id` | String | — | Advisory host bundle id label echoed in check_permissions output (embedded mode). |

**Flags:**

| Name | Description |
| ---- | ----------- |
| `--no-permissions-gate` | Skip the macOS first-launch permissions gate. |
| `--embedded` | Run embedded inside a host app: inherit the host's TCC grants, never prompt or relaunch. Also CUA_DRIVER_EMBEDDED=1. |

### `cua-driver stop`

Expand Down
80 changes: 80 additions & 0 deletions docs/content/docs/reference/cua-driver/embedding.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
title: Embedding
description: Run cua-driver as a direct child of your host app instead of handing off to a standalone daemon.
---

Embedding runs `cua-driver` as a direct child of your host app instead of relaunching or proxying through a standalone daemon. On macOS, this also lets the driver inherit the host app's Accessibility and Screen Recording grants, so users only approve your app. On Windows and Linux, embedded mode keeps driver execution inside the host process tree and avoids daemon handoff.

A complete macOS reference host and demo live in the repo at `libs/cua-driver/rust/examples/embedded-host-macos`.

## Launch embedded

Set embedded mode on the driver process your host spawns:

```sh
CUA_DRIVER_EMBEDDED=1 \
CUA_DRIVER_HOST_BUNDLE_ID=com.yourco.yourapp \
cua-driver mcp
```

Or use the equivalent flags:

```sh
cua-driver mcp --embedded --host-bundle-id com.yourco.yourapp
```

Only the exact value `CUA_DRIVER_EMBEDDED=1` enables embedded mode. The host bundle id is an advisory label echoed in `check_permissions`; trust still comes from macOS's responsibility chain.

## Host requirements

- Spawn `cua-driver` directly from your app, for example with `Process` / `NSTask`, `posix_spawn`, or `fork` / `exec`.
- Speak MCP over the child's stdin/stdout.
- On macOS, do not launch the driver with `open(1)` or `NSWorkspace.open`; LaunchServices makes the launched app its own responsible process and breaks permission inheritance.
- On macOS, request Accessibility and Screen Recording from the host app with `AXIsProcessTrustedWithOptions` and `CGRequestScreenCaptureAccess`.

If macOS grants are added after the driver child has started, restart the child so TCC is re-queried with a fresh per-process cache.

## App + gateway architectures

`--embedded` does not transfer a GUI app's grants to the driver; it only keeps the driver inside its spawner's macOS responsibility chain. If a separate gateway, daemon, or Node process spawns MCP servers, registering `cua-driver mcp --embedded` there makes the driver inherit the gateway's identity, not the app's. Spawn the driver from the app process, or bridge MCP from the gateway to an app-spawned child.

```text
Wrong (inherits the gateway's identity): Right:

gateway / node daemon YourApp.app
└─ cua-driver --embedded └─ cua-driver --embedded
```

## What changes

| Behavior | Standalone | Embedded |
| --- | --- | --- |
| Process model | May proxy through a daemon | Direct child / in-process path |
| Daemon relaunch | May proxy through app daemon | Disabled |
| macOS TCC identity | `com.trycua.driver` or caller | Host app |
| macOS permission prompts | Driver may prompt | Driver never prompts |
| macOS Settings entries | CuaDriver | Host app only |
| `check_permissions` attribution | `driver-daemon` or `caller` | `host` on macOS embedded runs |

Driver tools, screenshots, AX tree reads, background input, and the agent cursor overlay otherwise behave the same.

## macOS permission check

Call the `check_permissions` MCP tool after starting the embedded driver. On macOS, embedded mode ignores prompt requests and should return `source.attribution: "host"`:

```json
{
"accessibility": true,
"screen_recording": true,
"screen_recording_capturable": true,
"source": {
"attribution": "host",
"host_bundle_id": "com.yourco.yourapp",
"embedded": true
}
}
```

If `source.attribution` is not `host` on macOS, embedded mode is not active for the process handling your MCP calls. Check that `CUA_DRIVER_EMBEDDED=1` is passed to the child, that the child was spawned directly, and that you are not accidentally talking to an old standalone daemon.

`source.attribution: "host"` means the driver process is running in embedded mode; it does not prove that your GUI app is the responsible process. If a gateway, daemon, or Node process spawned the child, the reported grant state still belongs to that spawner. Spawn `cua-driver` from the app process that owns the macOS grants, or bridge MCP to an app-spawned child.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: The macOS-only `cua-driver permissions` command for inspecting and

## `cua-driver permissions` (macOS)

Inspect or request the macOS TCC grants the driver needs (Accessibility and Screen Recording).
Inspect or request the macOS TCC grants the driver needs (Accessibility and Screen Recording). Embedded-mode hosts do not use `cua-driver permissions grant`; the host app requests these grants itself, as described in [Embedding](/reference/cua-driver/embedding).

```bash
cua-driver permissions status # report grant status; read-only, no prompt
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/reference/cua-driver/meta.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{ "title": "Cua Driver", "pages": ["cli-reference", "macos-permissions", "mcp-tools", "mcp-tool-notes", "contracts", "limits", "modality-test-suite"] }
{ "title": "Cua Driver", "pages": ["cli-reference", "macos-permissions", "embedding", "mcp-tools", "mcp-tool-notes", "contracts", "limits", "modality-test-suite"] }
Loading
Loading