Skip to content
Computer Use

User guide

Connect your agent, locally or remotely

Start with the installation prompt, or follow the separate CLI, MCP and remote connection steps below. Both macOS and Windows use the same pairing and safety controls

Choose a connection

The Skill explains what to do; CLI and MCP provide ways to call the local service

Skill is guidance, not a connection

The Skill teaches an agent to discover or install the App, pair and operate it. Installing a Skill does not configure MCP. The App runs on the computer whose windows you want to control

The same three interfaces on both platforms

macOS and Windows both provide CLI, stdio MCP and optional loopback HTTP MCP. CLI saves screenshots as local files for the agent to open; MCP returns images directly to the agent

Your model can run in the cloud while its command tools run on your computer. That still uses a local connection; a cloud-only agent with no access to your computer needs a reachable remote connection

Pick the flow that fits your agent

For an agent that can execute local commands, start with the App’s installation prompt and CLI. Keep an existing working MCP connection. For another computer, use HTTP MCP over an SSH tunnel

These are setup recommendations, not an automatic selection mechanism. Computer Use does not choose an interface for your agent or retry an uncertain action through another one

Quick start with the installation prompt

Use this flow unless you specifically need manual configuration or another computer

1. Install the App and copy the prompt

Install the correct package from the download page, then open Computer Use. In “Connect”, choose “Copy Installation Prompt” and paste it into your agent. The App includes its own CLI runtime; you do not need global Node or a repository clone

The agent follows the Skill installation mechanism it supports. Keep existing Skill customizations and use the same profile throughout; examples in this guide use default

2. Complete permissions and approve pairing

On macOS, grant Accessibility and Screen Recording to Computer Use in system settings. On Windows, keep the App in your signed-in interactive desktop and review any system launch warning yourself. Approve the client in the local App when the first pairing request appears

Reuse an existing authorized profile. Pairing, operating-system permission and the agent’s own tool approvals are separate controls. A new window or app does not require another pairing when the client already has all-app access

3. Verify one real observation

Open a harmless test window and copy the read-only task below to your agent. Ask it to check the connection, list windows, open a background session, show a screenshot and close the session without clicking, typing or changing anything

Success means the agent actually opens and shows the image, not just that a command exits successfully. Screenshots are sent to the agent you chose; avoid a window containing information you do not want it to receive

A safe first task
Use computer-use to check the connection, list authorized windows, open a session for the test window, capture and describe its screenshot, then close the session. Do not click or type during this task

Local CLI

Run commands on the computer with Computer Use installed. CLI does not require MCP configuration

macOS: find the installed CLI

Use the local path hint in the App’s installation prompt, or discover standard installation locations with the example below. It sets cu; if it finds multiple copies, select the intended complete installation instead of guessing. Keep this terminal open for the following commands

Read --help and instructions at the start of each new agent conversation, then run preflight --profile default. These commands do not require pairing or start the service. An offline credential marked unverified is not proof that access is valid

macOS · Terminal
cuApp=''
for candidate in '/Applications/Computer Use.app' "$HOME/Applications/Computer Use.app"; do
  if [ -d "$candidate" ]; then
    if [ -n "$cuApp" ]; then
      printf '%s\n' 'Multiple installations: choose the intended App path' >&2
      cuApp=''
      break
    fi
    cuApp="$candidate"
  fi
done
# For multiple or custom installations, set cuApp to the intended absolute path
cu() {
  [ -n "$cuApp" ] || { printf '%s\n' 'Set cuApp first' >&2; return 1; }
  cuResources="$cuApp/Contents/Resources"
  [ "$(/usr/bin/plutil -extract CFBundleIdentifier raw -o - "$cuApp/Contents/Info.plist")" = com.starroy.computeruse ] || return 1
  for file in "$cuApp/Contents/MacOS/ComputerUseHost" "$cuResources/bin/node" "$cuResources/bin/computer-use" "$cuResources/bin/cua-driver"; do
    [ -x "$file" ] || { printf 'Missing executable: %s\n' "$file" >&2; return 1; }
  done
  for file in runtime/cli.js runtime/host.js package.json build-receipt.json skills/computer-use/SKILL.md; do
    [ -f "$cuResources/$file" ] || { printf 'Missing file: %s\n' "$file" >&2; return 1; }
  done
  [ -d "$cuResources/node_modules" ] || return 1
  "$cuResources/bin/computer-use" "$@"
}
cu --help && cu instructions && cu preflight --profile default

Windows: use the bundled runtime

Open PowerShell as the desktop user. Use the local path hint in the App’s installation prompt, or discover the current-user installation as below; for a portable package, use its complete extracted directory. The example sets cuNode and cuCli; keep this PowerShell window open

Read --help and instructions, then inspect preflight --profile default. Do not copy cli.js out of its package or substitute a global Node installation

Windows · PowerShell
$ErrorActionPreference = 'Stop'
$cuRoot = Join-Path $env:LOCALAPPDATA 'Programs\Computer Use'
# For a portable/custom installation, replace cuRoot with its complete package directory
foreach ($file in @('ComputerUse.WindowsHost.exe', 'bin\node.exe', 'bin\cua-driver.exe', 'runtime\cli.js', 'runtime\host.js', 'package.json', 'build-receipt.json', 'skills\computer-use\SKILL.md')) {
  if (-not (Test-Path -LiteralPath (Join-Path $cuRoot $file) -PathType Leaf)) {
    throw "Incomplete package: $file"
  }
}
if (-not (Test-Path -LiteralPath (Join-Path $cuRoot 'node_modules') -PathType Container)) {
  throw 'Incomplete package: node_modules'
}
$cuNode = Join-Path $cuRoot 'bin\node.exe'
$cuCli = Join-Path $cuRoot 'runtime\cli.js'
& $cuNode $cuCli --help
if ($LASTEXITCODE -ne 0) { throw 'CLI help failed' }
& $cuNode $cuCli instructions
if ($LASTEXITCODE -ne 0) { throw 'Read the installed version compatibility instructions' }
& $cuNode $cuCli preflight --profile default

Pair only if the selected profile is missing

If preflight reports missing and no known revocation explains it, request first pairing and approve it in the local App. If the profile already exists, reuse it; invalid, unauthorized or an offline unverified state calls for diagnosis, not deleting credentials and pairing again

The examples request the default all-desktop-app scope. Review that scope before approving it; isolated-browser access is separate. Replace default consistently if you use another profile

macOS · Terminal
# Only when preflight reports missing credentials and there is no known revocation
cu pair --profile default --name 'My Agent'
Windows · PowerShell
# Only when preflight reports missing credentials and there is no known revocation
& $cuNode $cuCli pair --profile default --name 'My Agent'

Check, list windows, observe, then close

Run doctor and targets, choose the returned target ID for your test window, then call session_open in background mode. Use its returned session ID for observe and session_close; replace every placeholder with the actual returned value

Open the screenshot file returned by observe with the agent’s image-reading tool before closing the session. No click or typing is required for this check. Use schema for the installed version’s exact method parameters

macOS · Terminal
# Run each command separately, inspect its result, then replace the next placeholder
cu doctor --profile default
cu targets --profile default
cu session_open --profile default --json '{"targetId":"TARGET_ID","mode":"background"}'
cu observe --profile default --json '{"sessionId":"SESSION_ID"}'
# Read the screenshot file returned by observe, then close this session
cu session_close --profile default --json '{"sessionId":"SESSION_ID"}'
Windows · PowerShell
# Run each step separately and inspect the result
& $cuNode $cuCli doctor --profile default
& $cuNode $cuCli targets --profile default

# File input avoids native-command JSON quoting differences in PowerShell 5.1 and 7
$cuRequest = Join-Path ([IO.Path]::GetTempPath()) ("cu-observe-" + [Guid]::NewGuid().ToString() + '.json')
$utf8 = New-Object Text.UTF8Encoding($false)
[IO.File]::WriteAllText($cuRequest, '{"targetId":"TARGET_ID","mode":"background"}', $utf8)
& $cuNode $cuCli session_open --profile default --input $cuRequest

# Replace SESSION_ID with the returned sessionId
[IO.File]::WriteAllText($cuRequest, '{"sessionId":"SESSION_ID"}', $utf8)
& $cuNode $cuCli observe --profile default --input $cuRequest
# Read the screenshot file returned by observe, then close this session
& $cuNode $cuCli session_close --profile default --input $cuRequest
Remove-Item -LiteralPath $cuRequest

Local stdio MCP

Use this when your agent supports MCP and can launch a process on the same computer

Reuse pairing and generate configuration

First complete the CLI discovery and profile checks above. In the App, open “Connect” → “Advanced Connection”, select the same profile, and copy the configuration for your agent. HTTP does not need to be enabled for stdio MCP

Alternatively use config stdio for generic JSON or config codex for Codex configuration. Generated stdio settings contain absolute launch paths and the profile name, not the pairing token

macOS configuration commands

Run these in the terminal where cu was set. Add the generated server entry to the format supported by your agent, preserving its other entries

macOS · Terminal
cu config stdio --profile default
# For an Agent that uses Codex TOML, run this instead:
# cu config codex --profile default

Windows configuration commands

Run these in the PowerShell window where cuNode and cuCli were set. Keep the generated absolute paths and argument boundaries; do not paste a shell command into a field that expects an executable path

Windows · PowerShell
& $cuNode $cuCli config stdio --profile default
# For an Agent that uses Codex TOML, run this instead:
# & $cuNode $cuCli config codex --profile default

Reload the agent and verify its tools

Reload the agent’s MCP configuration or restart the agent, then check that the server exposes tools. Ask it to run doctor → targets → session_open → observe → session_close on the test window. MCP returns the screenshot as an image

preflight, instructions and recover are CLI helpers, not MCP tools. An agent using MCP may still need local command access for maintenance; a pure MCP client cannot assume these helpers are available

Another computer: HTTP MCP over SSH

The target computer runs the App and its windows; the controller computer runs your agent and the SSH tunnel

Target computer

Keep the desktop signed in and unlocked, run Computer Use, approve permissions and pairing, prepare SSH and enable HTTP MCP

Computer running the agent

Prepare the SSH client and key, verify the target, open the tunnel, import the private HTTP configuration and check a screenshot

Both computers: check the prerequisites

Use the same LAN or an existing trusted network where SSH is reachable. This guide does not set up public port forwarding, tunnelling services or unattended access. Use the same target account for SSH and the App so its profile and private files match. Keep that user signed in to an unlocked interactive desktop, with the App running and its system permissions approved

Both platforms expose HTTP MCP, but interface availability is not an acceptance-test claim: the documented cross-computer test controls Windows from a Mac; remote control of a Mac has not received the same desktop validation

Target Mac: enable SSH for the intended user

Open System Settings → General → Sharing → Remote Login, turn it on and allow only the account you intend to use. Note the displayed login name and address. Do not enable full disk access for remote users for this setup; SSH permission does not grant Computer Use access

Follow Apple’s Remote Login instructions if your system settings differ

Target Windows: install and start OpenSSH Server

In an administrator PowerShell, inspect the optional OpenSSH Server component, install it only if absent, and start sshd. Check the existing inbound SSH rule rather than adding duplicate rules; restrict its source addresses to the controller or trusted network. Do not open the MCP port in the firewall

Automatic service startup is optional and is a separate persistent change. See Microsoft’s OpenSSH setup; the second example enables automatic startup only if you choose it

Windows · PowerShell
$ErrorActionPreference = 'Stop'
$cuSsh = Get-WindowsCapability -Online -Name 'OpenSSH.Server~~~~0.0.1.0'
if ($cuSsh.State -ne 'Installed') {
  Add-WindowsCapability -Online -Name 'OpenSSH.Server~~~~0.0.1.0'
  Set-Service -Name sshd -StartupType Manual
}
Start-Service sshd
Get-Service sshd
Get-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' |
  Select-Object Name, Enabled, Direction, Action, Profile
Get-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' | Get-NetFirewallPortFilter
Windows · PowerShell
Set-Service -Name sshd -StartupType Automatic

Controller: check SSH and prepare a key

macOS includes an SSH client. On Windows, check ssh -V; if missing, add the OpenSSH Client optional feature in Windows settings. Generate a dedicated passphrase-protected key, or reuse an appropriate existing key without overwriting it

Only transfer the .pub public key to the target through a trusted channel. The private key stays on the controller. The examples use a dedicated key path; cancel if a key already exists there rather than replacing it

macOS · Terminal
command -v ssh ssh-keygen sftp
cuKey="$HOME/.ssh/id_ed25519_computer_use"
if [ -e "$cuKey" ] || [ -e "$cuKey.pub" ]; then
  printf '%s\n' 'Key already exists: reuse the intended pair; do not overwrite it'
else
  install -d -m 700 "$HOME/.ssh"
  ssh-keygen -t ed25519 -f "$cuKey" -C 'computer-use'
fi
# Copy only this public key to the target computer
cat "$cuKey.pub"
Windows · PowerShell
$ErrorActionPreference = 'Stop'
Get-Command ssh.exe, ssh-keygen.exe, sftp.exe -ErrorAction Stop
$cuKey = Join-Path $env:USERPROFILE '.ssh\id_ed25519_computer_use'
if ((Test-Path -LiteralPath $cuKey) -or (Test-Path -LiteralPath "$cuKey.pub")) {
  Write-Output 'Key already exists: reuse the intended pair; do not overwrite it'
} else {
  New-Item -ItemType Directory -Path (Split-Path $cuKey) -Force | Out-Null
  ssh-keygen.exe -t ed25519 -f $cuKey -C computer-use
  if ($LASTEXITCODE -ne 0) { throw 'Key generation failed' }
}
# Copy only this public key to the target computer
Get-Content -LiteralPath "$cuKey.pub"

Target Mac: authorize the public key

Sign in locally as the target SSH user. Add the controller’s public key as a new line in ~/.ssh/authorized_keys, preserving existing keys. The directory belongs to that user with permission 700; the file uses 600. Do not paste the private key

macOS · Terminal
cuPublicKey='<PUBLIC_KEY_LINE>'
case "$cuPublicKey" in
  'ssh-ed25519 '*)
    install -d -m 700 "$HOME/.ssh"
    cuAuthorized="$HOME/.ssh/authorized_keys"
    if [ -L "$cuAuthorized" ]; then
      printf '%s\n' 'Review the existing authorized_keys symlink before continuing' >&2
    else
      (umask 077; touch "$cuAuthorized")
      grep -Fqx -- "$cuPublicKey" "$cuAuthorized" || printf '\n%s\n' "$cuPublicKey" >> "$cuAuthorized"
      chmod 600 "$cuAuthorized"
    fi
    ;;
  *) printf '%s\n' 'Replace PUBLIC_KEY_LINE with the complete public key' >&2 ;;
esac

Target Windows: use the correct key file and ACL

For a standard account, add the public key to that user’s %USERPROFILE%\.ssh\authorized_keys; the example restricts the file to that account, Administrators and SYSTEM. Check that its directory belongs to the user and cannot be written by other standard accounts. For an administrator account, default OpenSSH instead uses %ProgramData%\ssh\administrators_authorized_keys; manage it from administrator PowerShell

Append without replacing existing keys. The administrator key file is shared across administrator accounts and must be accessible only to Administrators and SYSTEM. To limit a key to one administrator, review a deliberate server configuration instead of treating this shared file as per-user authorization. Use the matching example and Microsoft’s key-management instructions; a customized sshd_config can change the expected location

Windows · PowerShell
$ErrorActionPreference = 'Stop'
$cuPublicKey = '<PUBLIC_KEY_LINE>'
if (-not $cuPublicKey.StartsWith('ssh-ed25519 ')) { throw 'Replace PUBLIC_KEY_LINE with the complete public key' }
$cuAuthorized = Join-Path $env:USERPROFILE '.ssh\authorized_keys'
New-Item -ItemType Directory -Path (Split-Path $cuAuthorized) -Force | Out-Null
if ((Test-Path -LiteralPath $cuAuthorized) -and ((Get-Item -LiteralPath $cuAuthorized).Attributes -band [IO.FileAttributes]::ReparsePoint)) {
  throw 'Review the existing authorized_keys link before continuing'
}
if (-not (Test-Path -LiteralPath $cuAuthorized) -or -not (Select-String -LiteralPath $cuAuthorized -Pattern $cuPublicKey -SimpleMatch -Quiet)) {
  Add-Content -LiteralPath $cuAuthorized -Value ("`r`n" + $cuPublicKey) -Encoding ascii
}
$cuSid = [Security.Principal.WindowsIdentity]::GetCurrent().User
$cuAcl = New-Object Security.AccessControl.FileSecurity
$cuAcl.SetOwner($cuSid)
$cuAcl.SetAccessRuleProtection($true, $false)
foreach ($sid in @($cuSid, [Security.Principal.SecurityIdentifier]'S-1-5-18', [Security.Principal.SecurityIdentifier]'S-1-5-32-544')) {
  $cuAcl.AddAccessRule((New-Object Security.AccessControl.FileSystemAccessRule($sid, 'FullControl', 'Allow')))
}
Set-Acl -LiteralPath $cuAuthorized -AclObject $cuAcl
Windows · PowerShell
$ErrorActionPreference = 'Stop'
$cuPublicKey = '<PUBLIC_KEY_LINE>'
if (-not $cuPublicKey.StartsWith('ssh-ed25519 ')) { throw 'Replace PUBLIC_KEY_LINE with the complete public key' }
$cuAuthorized = Join-Path $env:ProgramData 'ssh\administrators_authorized_keys'
if ((Test-Path -LiteralPath $cuAuthorized) -and ((Get-Item -LiteralPath $cuAuthorized).Attributes -band [IO.FileAttributes]::ReparsePoint)) {
  throw 'Review the existing administrators_authorized_keys link before continuing'
}
if (-not (Test-Path -LiteralPath $cuAuthorized) -or -not (Select-String -LiteralPath $cuAuthorized -Pattern $cuPublicKey -SimpleMatch -Quiet)) {
  Add-Content -LiteralPath $cuAuthorized -Value ("`r`n" + $cuPublicKey) -Encoding ascii
}
$cuAdmins = [Security.Principal.SecurityIdentifier]'S-1-5-32-544'
$cuAcl = New-Object Security.AccessControl.FileSecurity
$cuAcl.SetOwner($cuAdmins)
$cuAcl.SetAccessRuleProtection($true, $false)
foreach ($sid in @($cuAdmins, [Security.Principal.SecurityIdentifier]'S-1-5-18')) {
  $cuAcl.AddAccessRule((New-Object Security.AccessControl.FileSystemAccessRule($sid, 'FullControl', 'Allow')))
}
Set-Acl -LiteralPath $cuAuthorized -AclObject $cuAcl

Target: read the SSH host fingerprint locally

Read the target’s host-key fingerprint on the target computer itself and record it through a trusted channel. Compare it with the fingerprint shown on the controller’s first SSH connection; accept only after it matches. A changed fingerprint needs investigation, not disabling host-key checking

macOS · Terminal
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
Windows · PowerShell
ssh-keygen.exe -lf "$env:ProgramData\ssh\ssh_host_ed25519_key.pub"

Controller: test SSH login before forwarding

Replace the user and address placeholders with the target’s actual login. Use the dedicated key, verify the fingerprint and confirm you reach the intended account. Exit that remote shell before starting the tunnel; a successful SSH login does not start Computer Use or approve pairing

macOS · Terminal
ssh -i "$HOME/.ssh/id_ed25519_computer_use" -o IdentitiesOnly=yes -o HostKeyAlgorithms=ssh-ed25519 '<REMOTE_USER>@<REMOTE_HOST>'
# Compare the offered SHA256 fingerprint with the target computer before accepting
# After verifying the login, type exit to return to the Agent computer
Windows · PowerShell
ssh.exe -i "$env:USERPROFILE\.ssh\id_ed25519_computer_use" -o IdentitiesOnly=yes -o HostKeyAlgorithms=ssh-ed25519 '<REMOTE_USER>@<REMOTE_HOST>'
# Compare the offered SHA256 fingerprint with the target computer before accepting
# After verifying the login, type exit to return to the Agent computer

Controller: open the loopback tunnel

Keep this command running in a separate terminal. Bind only 127.0.0.1:47631 and forward it to the target’s same port. The Host validates that address and port, so changing only the local port is not a supported workaround

If the port is busy, identify and stop your old tunnel or disable the controller’s local HTTP MCP before retrying. Do not kill an unrelated process, use -g, bind 0.0.0.0 or expose the MCP port directly

macOS · Terminal
ssh -i "$HOME/.ssh/id_ed25519_computer_use" \
  -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes \
  -o HostKeyAlgorithms=ssh-ed25519 -o ExitOnForwardFailure=yes \
  -o ServerAliveInterval=15 -o ServerAliveCountMax=3 -N \
  -L 127.0.0.1:47631:127.0.0.1:47631 '<REMOTE_USER>@<REMOTE_HOST>'
Windows · PowerShell
ssh.exe -i "$env:USERPROFILE\.ssh\id_ed25519_computer_use" `
  -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes `
  -o HostKeyAlgorithms=ssh-ed25519 -o ExitOnForwardFailure=yes `
  -o ServerAliveInterval=15 -o ServerAliveCountMax=3 -N `
  -L 127.0.0.1:47631:127.0.0.1:47631 '<REMOTE_USER>@<REMOTE_HOST>'

Target: enable HTTP and export private settings

On the target, reuse the CLI discovery variables and the intended existing profile, or complete first local pairing only if missing and not known to be revoked. Open “Connect” → “Advanced Connection” in the App and enable local HTTP MCP. It defaults to off each time the App or service restarts

Export a new HTTP configuration file using the matching command. Unlike stdio settings, this JSON contains a Bearer credential. Windows requires the App’s private data directory. The exporter refuses to overwrite an existing file; review that file or choose a new filename

macOS · Terminal
install -d -m 700 "$HOME/.config/computer-use"
cu config http --profile default --out "$HOME/.config/computer-use/remote-http.json"
# The CLI refuses to overwrite an existing file; do not print its contents
Windows · PowerShell
& $cuNode $cuCli config http --profile default --out "$env:LOCALAPPDATA\Computer Use\remote-http.json"
# Export only into the Host private data directory; existing files are not overwritten
# Do not print the file contents

Controller: transfer the file and connect the agent

Use SSH/SFTP to retrieve the exported JSON into a new private local directory. Replace the remote-path placeholder with the absolute path returned by the export command, using forward slashes for a Windows target and keeping the whole remote argument quoted. The examples restrict the downloaded file to your account

Import its URL and authorization header into an agent that supports HTTP MCP and private credential storage. Keep the URL http://127.0.0.1:47631/mcp; the tunnel reaches the target. Do not paste the credential into an agent conversation, a web page, a repository or a shared folder. No global Node installation is required for this direct HTTP connection

Reload the agent’s MCP connection. Keep the private configuration while it is needed; after confirming the transfer, remove only the extra exported copy on the target if desired, not the App’s pairing credentials

macOS · Terminal
cuRemoteDir="$(mktemp -d "$HOME/.computer-use-remote.XXXXXX")"
# Replace REMOTE_CONFIG_PATH with the absolute path returned by config http
# The exported filename is remote-http.json; keep the entire remote argument quoted
(
  umask 077
  cd "$cuRemoteDir" &&
  sftp -i "$HOME/.ssh/id_ed25519_computer_use" \
    -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes \
    -o HostKeyAlgorithms=ssh-ed25519 \
    '<REMOTE_USER>@<REMOTE_HOST>:<REMOTE_CONFIG_PATH>' &&
  chmod 600 remote-http.json
)
printf 'Private configuration directory: %s\n' "$cuRemoteDir"
Windows · PowerShell
$ErrorActionPreference = 'Stop'
$cuRemoteDir = Join-Path $env:USERPROFILE ('.computer-use-remote-' + [Guid]::NewGuid().ToString())
New-Item -ItemType Directory -Path $cuRemoteDir | Out-Null
$cuSid = [Security.Principal.WindowsIdentity]::GetCurrent().User
$cuAcl = New-Object Security.AccessControl.DirectorySecurity
$cuAcl.SetOwner($cuSid)
$cuAcl.SetAccessRuleProtection($true, $false)
$cuAcl.AddAccessRule((New-Object Security.AccessControl.FileSystemAccessRule($cuSid, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow')))
Set-Acl -LiteralPath $cuRemoteDir -AclObject $cuAcl
# Replace REMOTE_CONFIG_PATH with the absolute exported path, using / separators
Push-Location -LiteralPath $cuRemoteDir
try {
  sftp.exe -i "$env:USERPROFILE\.ssh\id_ed25519_computer_use" `
    -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes `
    -o HostKeyAlgorithms=ssh-ed25519 `
    '<REMOTE_USER>@<REMOTE_HOST>:<REMOTE_CONFIG_PATH>'
  if ($LASTEXITCODE -ne 0) { throw 'SFTP failed; do not use the partial file' }
} finally {
  Pop-Location
}
Write-Output "Private configuration directory: $cuRemoteDir"

Controller: verify the target through MCP

Run the same read-only sequence: doctor → targets → session_open → observe → session_close. Confirm the window and screenshot belong to the target computer, not the controller

Remote HTTP cannot start the Host or invoke local recover. If the service is unavailable, the target user must check the App and its state; do not bypass a pause, emergency stop or revoked grant by starting another copy

Both computers: reconnect or finish safely

If the tunnel disconnects, preserve any action’s original requestId. Reconnect, query action_status and observe again; do not resend an action whose result is uncertain. If the App or service restarted, the target user must enable HTTP again

When finished, close sessions and press Ctrl+C in the tunnel terminal. Disable HTTP in the target App. To stop SSH access too, turn off Remote Login on macOS or stop sshd on Windows; if you enabled automatic startup, disable it deliberately. Remove only the dedicated public-key entry if you no longer want that key authorized

Windows · PowerShell
Stop-Service sshd
Set-Service -Name sshd -StartupType Manual

Troubleshooting and recovery

Keep the exact error and the existing profile. Changing the connection does not change its authorization

CLI missing or MCP launch path invalid

Check the complete installation and the local path hint in the App’s installation prompt. After moving the App or portable directory, regenerate stdio configuration because its paths are absolute. If an older App lacks instructions or preflight, use the installed Skill’s compatibility guidance and review an upgrade; a missing command does not mean the App is absent

Profile rejected or a window is missing

Use the same profile in CLI commands, MCP configuration and diagnostics. Inspect doctor.authorization, capabilities.authorization or online preflight.profile.authorization; an absent field means the scope is unknown. targets omits windows outside the client’s grant

For an existing desktop client, the user can choose “Clients” → “Allow all apps…” in the App and confirm locally. This preserves the token, profile and MCP configuration and leaves isolated-browser permission unchanged. Browser-only clients need separate local review; an older App without this control needs an upgrade or local review, not automatic re-pairing

Also check that the app has a visible window in the signed-in desktop and that system permissions are granted. New windows do not need individual approval under an existing all-app grant

SSH or HTTP cannot connect

For SSH, check the target address, allowed account, SSH service, firewall rule, key-file location and ACL. Never dismiss a changed host fingerprint without checking the target. For HTTP, check that the target App is running, its HTTP switch is enabled and the tunnel owns the controller’s port 47631

A 401 response calls for checking the selected profile and whether the client was revoked. Missing system permissions and a locked or non-interactive desktop need attention on the target. Do not expose MCP through the firewall to work around the tunnel

Recover a service fault once, locally

Read the installed instructions and preflight first. For a failed Host, or a connection lost after an unknown desktop action with no user stop, an agent may make one recover attempt using the same complete package and existing profile. The examples are local CLI commands, not remote MCP tools

Recovery cannot clear pause or emergency stop, restore revoked access, supply system permissions or re-pair. Those need local user action. On success, query any original action, open a new background session and observe; do not reuse old sessions, snapshots or elements. If recovery fails or the next step faults again, report it rather than looping

macOS · Terminal
cu preflight --profile default
# Only for a recoverable service fault; inspect preflight before running once
cu recover --profile default
# If an action is uncertain, preserve its original requestId and query it after recovery
cu action_status --profile default --json '{"requestId":"ORIGINAL_REQUEST_ID"}'
# Open a new session and observe; never replay the uncertain action
Windows · PowerShell
& $cuNode $cuCli preflight --profile default
# Only for a recoverable service fault; inspect preflight before running once
& $cuNode $cuCli recover --profile default
# If an action is uncertain, preserve its original requestId and query it after recovery
$cuStatusInput = Join-Path ([IO.Path]::GetTempPath()) ("cu-status-" + [Guid]::NewGuid().ToString() + '.json')
[IO.File]::WriteAllText($cuStatusInput, '{"requestId":"ORIGINAL_REQUEST_ID"}', (New-Object Text.UTF8Encoding($false)))
& $cuNode $cuCli action_status --profile default --input $cuStatusInput
Remove-Item -LiteralPath $cuStatusInput
# Open a new session and observe; never replay the uncertain action

An uncertain result must never be replayed

unknown means input delivery is uncertain. Keep the original requestId, query action_status and inspect a fresh observation after reconnection. Do not retry through CLI instead of MCP, invent a new request ID, or repeat an equivalent click or keystroke

executed with an unconfirmed effect needs observation, not a service restart. An unchanged screen does not prove input was never delivered; if you cannot establish whether a consequential action happened, ask the user to resolve it