On this page
Quick answer · Install · First run · API · Capabilities · WebMCP · Security · FAQ
Short answer.
OpenCode Computer Use is an OpenCode plugin that registers a local MCP server named cua_repl. The model receives two tools: js for persistent JavaScript and js_reset for a clean JavaScript session. Native operating-system permissions still control desktop access.
- Plugin
- The package loaded by OpenCode. It manages registration and lifecycle.
- MCP server
- The local stdio server named
cua_repl. - Model tools
jsandjs_reset, not raw desktop tools.- Native runtime
open-computer-use, running separately with OS accessibility and capture permissions.- Supported hosts
- OpenCode 1 and OpenCode2 on Linux, macOS, and Windows where the native runtime and permissions are available.
Install once. Keep the session.
This repository is a standalone OpenCode plugin. It contains the plugin entrypoint, the local MCP server, and the native runtime integration. The plugin registers cua_repl; the model sees only js and js_reset.
git clone [email protected]:rashidtvmr/opencode-computer-use.git
cd opencode-computer-use
npm installFor a published-package install, use:
npm install @frontendxlab/opencode-computer-use
# pnpm install @frontendxlab/opencode-computer-use also worksThe package postinstall step detects OpenCode 1 versus OpenCode2 and local versus global installation. It edits JSONC without discarding comments, avoids duplicate entries, and prints both manual formats when the choice is ambiguous. Run it explicitly with npx opencode-computer-use-setup, or add --local, --global, --v1, or --v2 when you want to choose explicitly.
Recent pnpm releases can require explicit lifecycle-script approval. If pnpm reports ERR_PNPM_IGNORED_BUILDS, run pnpm approve-builds @frontendxlab/opencode-computer-use once, then rerun pnpm install. If scripts are disabled, run pnpm exec opencode-computer-use-setup manually. CI environments skip setup automatically.
Desktop onboarding is part of installation from v0.1.5. When package lifecycle scripts are allowed, postinstall first configures OpenCode and then runs opencode-computer-use-permissions --install. On macOS it runs the native doctor and opens the Open Computer Use permission onboarding when Accessibility or Screen Recording is missing; Apple still requires you to approve the protected toggles. On GNOME Linux it best-effort enables toolkit accessibility before the AT-SPI/session check. Windows validates that UI Automation is running in an interactive signed-in desktop session. Set OPENCODE_COMPUTER_USE_SKIP_PERMISSIONS=1 to opt out.
Add the checkout to the V1 plugin list. An absolute path avoids ambiguity when OpenCode starts from another directory.
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
["@frontendxlab/opencode-computer-use", { "backend": "auto" }]
]
}Restart OpenCode or reopen the TUI, then verify the dynamically registered server:
opencode mcp listUse the V2 plugins shape when installing the checkout with OpenCode2.
{
"$schema": "https://opencode.ai/config.json",
"plugins": [
{
"package": "@frontendxlab/opencode-computer-use",
"options": { "backend": "auto" }
}
]
}Confirm that cua_repl is connected before starting a desktop task.
cua_repl. That server then exposes the callable tools cua_repl.js and cua_repl.js_reset.Start with discovery.
Use the first call to see what the native runtime can actually see. Do not guess an app name or assume that a screenshot is available.
var apps = await cua.listApps({ emit: false });
var chrome = await cua.getApp("Google Chrome");
await chrome.getAXState({ textLimit: 12000 });The generated snippet is intentionally small. Bindings persist between calls, so use var when a later call may need to reassign the same name.
One surface. Three layers.
- OpenCode plugin
Loads the local package and registers the MCP server without exposing raw desktop tools. - cua_repl MCP server
Provides a persistent JavaScript evaluation surface, output bounds, request validation, and worker isolation. - Native runtime
Runsopen-computer-useas a separate child process. OS accessibility, screen capture, and input permissions remain there.
The Codex-shaped API.
The model receives a small async API inside persistent JavaScript:
- cua.getState(options?)List the current desktop apps and emit bounded output.
- cua.listApps(options?)Return structured app records without emitting by default.
- cua.getApp(nameOrPid)Bind an app and emit its initial accessibility state.
- app.getAXState(options?)Read a fresh semantic tree before choosing an element index.
- app.getScreenshot(options?)Return local image bytes and optionally emit them with nodeRepl.
- app.click(target, options?)Use an element index first, or a bounded coordinate when necessary.
- app.typeText(text)Send literal text through the native runtime.
- app.pressKey(key)Send a named key or supported modifier chord.
- app.performSecondaryAction(index, action)Invoke an advertised accessibility action such as a context action.
Use nodeRepl.write(value) for extra text and await nodeRepl.emitImage(bytesOrDataUrl) for local images. Both are bounded before crossing the worker boundary.
What it can — and cannot — do.
This is the complete model-facing computer-use surface of the current cua_repl adapter, plus its intentional exclusions. A green check means the adapter/native runtime exposes that path; it does not mean every application or desktop session will accept it.
| Capability | macOS | Linux | Windows | What that means |
|---|---|---|---|---|
| Observe & understand | ||||
| List visible applicationscua.listApps() | Discovers apps visible to the native accessibility runtime in the signed-in desktop session. | |||
| Read desktop / app inventorycua.getState() | Returns the current structured app inventory with bounded output. | |||
| Bind an existing applicationcua.getApp(nameOrID) | Binds an already running or visible app. It does not launch a new application. | |||
| Read accessibility / UI treeapp.getAXState() | macOS uses Accessibility, Linux AT-SPI, and Windows UI Automation. Some Wayland/app combinations expose incomplete trees. | |||
| Capture an app screenshotapp.getScreenshot() | Depends on capture permission plus the active compositor/capture backend. | |||
| Read state + screenshot togetherapp.getAXStateAndScreenshot() | Combines semantic state and image capture; state can still be useful when capture is unavailable. | |||
| Keep variables and app bindingspersistent js session | Top-level variables and app bindings survive across model turns until js_reset or a timeout reset. | |||
| Emit text and images to the modelnodeRepl.write / emitImage | Text and image output are bounded before crossing the worker/MCP boundary. | |||
| Interact with an app | ||||
| Click a semantic elementapp.click(elementIndex) | Preferred input path. Requires a current actionable accessibility element. | |||
| Click a coordinateapp.click([x, y]) | Use when semantic state is insufficient. System/global-pointer fallback is separately gated. | |||
| Type literal textapp.typeText(text) | Requires a writable/focused target and an input path accepted by the host desktop. | |||
| Press keys / supported chordsapp.pressKey(key) | Host input restrictions, focus, and compositor behavior still apply. | |||
| Scroll an accessibility elementapp.scroll(index, direction, pages) | Semantic element scrolling is exposed. Coordinate-only scrolling is intentionally rejected. | |||
| Drag between coordinatesapp.drag([x1,y1], [x2,y2]) | Coordinate/input-backend dependent; verify the resulting UI state rather than assuming success. | |||
| Set an accessibility valueapp.setValue(index, value) | Only works when the target element/backend advertises a writable accessibility value. | |||
| Run a secondary accessibility actionapp.performSecondaryAction() | Only actions advertised by the accessibility element/backend can be invoked. | |||
| Use global pointer fallbackallowGlobalPointerFallbacks | Off by default. Must be explicitly enabled because it can move the real pointer and change focus. | |||
| Session, safety & recovery | ||||
| Run a read-only desktop sessionsafetyMode: read-only | Mutating CUA methods are blocked by the adapter before native execution. | |||
| Allow / deny specific applicationsappAccess | Exact case-insensitive app name/ID rules are enforced before binding and actions. | |||
| Reset JavaScript bindingsjs_reset | Clears REPL variables and bindings without closing apps or resetting their native state. | |||
| Recover from CPU-bound JavaScriptworker timeout + restart | A wedged JS evaluation is terminated in its worker and the JavaScript session is restarted. | |||
| Cancel timed-out native requestsnotifications/cancelled | Cancellation is best-effort. After an uncertain mutation, observe state before acting again. | |||
| Clean up at session endnotifications/turn-ended | Shutdown sends best-effort lifecycle cleanup before the native child is closed. | |||
| Re-emit CUA usage guidancecua.rewriteDocumentation() | Reprints the bounded API guidance inside the persistent JavaScript session. | |||
| Use safe JavaScript timerssetTimeout / clearTimeout | Timers exist inside the restricted session and are cancelled when the active evaluation ends or resets. | |||
| Intentionally unavailable through cua_repl | ||||
| Launch a new applicationno model-facing method | cua.getApp() binds an existing/visible app; the adapter exposes no launch method. |
|||
| Quit / force-close an applicationno model-facing method | No quit, terminate, or force-kill application API is exposed to the model. | |||
| Move / resize native windowsno model-facing method | The current public CUA facade has no window-management methods. | |||
| Coordinate-only scrollingapp.scroll([x,y], ...) | The adapter explicitly rejects coordinate scroll; bind a semantic element instead. | |||
| Read / write clipboard directlyno clipboard API | There is no direct clipboard API. Normal app keyboard shortcuts may still work when the app/OS accepts them. | |||
| Use browser DOM / CDP directlyno DOM or CDP API | Browser windows are controlled like desktop apps through accessibility/screenshot state, not page DOM or Chrome DevTools Protocol. | |||
| Record screen video or audioscreenshot only | The model-facing surface can request still screenshots; it does not expose continuous screen, camera, microphone, or audio recording. | |||
| Run arbitrary shell commandsrun_shell / exec | Deliberately omitted from the model-facing CUA API. | |||
| Read / write filesystem from JSfs / require / process | Node built-ins, require, process, and filesystem access are removed from the sandbox. | |||
| Make network requests from JSfetch / sockets | fetch and arbitrary networking are unavailable inside the model-facing VM. |
|||
| Use dynamic imports / code generationimport() / eval / Function | Dynamic imports, string code generation, WebAssembly, and host-realm escapes are disabled. | |||
| Control lock / login / secure desktopOS-owned surfaces | Lock/login screens, Windows UAC secure desktop, and other OS-owned consent surfaces are outside the supported session. | |||
| Bypass operating-system permissionsAccessibility / AT-SPI / UIA / capture | The plugin never elevates itself or bypasses native permission boundaries. | |||
| Automate password managersnative safety denial | The native runtime keeps its password-manager safety denial; secret stores are intentionally out of scope. | |||
WebMCP, when available.
The documentation page includes a small, read-only WebMCP enhancement based on the current document.modelContext.registerTool() draft. Browsers without the preview API continue normally; no desktop-control or mutating tool is exposed through the page.
const tools = await document.modelContext.getTools();
// Preview-only discovery: search_docs, get_install_config, get_api_referencePreview boundary: WebMCP support is experimental and requires a supporting browser, a secure context, and the tools Permissions Policy. Cloudflare Browser Run lab sessions can be used to test discovery. This page does not claim stable or universal browser support.
Make risky capabilities explicit.
{
"backend": "auto",
"serverName": "cua_repl",
"safetyMode": "full",
"appAccess": {
"default": "allow",
"allow": [],
"deny": []
},
"timeoutMs": 30000,
"maxTextChars": 200000,
"maxImageBytes": 2097152,
"allowGlobalPointerFallbacks": false
}- backend
auto,native, or a fixedcustomcommand. - serverNameRename the generated MCP server when a project needs isolation.
- safetyMode
fullorread-only. Read-only blocks mutating CUA methods and changes MCP annotations. - appAccessOptional exact-name and ID allow or deny rules, evaluated before native access.
- timeoutMsBounds JavaScript evaluation. A timeout resets the worker session.
- maxTextCharsAggregate text budget for one tool result.
- maxImageBytesAggregate image budget for one tool result.
- allowGlobalPointerFallbacksOff by default. Enable only when system-level pointer movement is explicitly intended.
Observe, act, verify.
- Read
getAXState()before selecting an element. - Prefer a fresh element index over a remembered coordinate.
- Batch deterministic actions and the next state read in one
jscall. - After navigation, dialogs, resizing, or rerendering, read state again.
- Use
js_resetwhen bindings need to be discarded, not when an app needs restarting.
var app = await cua.getApp("Example App");
await app.getAXState();
var state = await app.click(12);
await app.getAXState();
nodeRepl.write(state);Know what the host can do.
| Target | Packaged runtime | Live status here |
|---|---|---|
| Linux x64 | Included | Read-only smoke passed |
| Linux arm64 | Included | Needs arm64 host |
| macOS arm64 and x64 | Included | Needs live permissions |
| Windows arm64 and x64 | Included | Needs UIA validation |
macOS generally needs Accessibility and Screen Recording. Windows needs UI Automation in the interactive desktop session. Linux needs AT-SPI plus the native runtime's input and capture services. A secure desktop, lock screen, UAC surface, or missing permission is not bypassed.
Installation walks you through desktop access.
The v0.1.5+ npm/pnpm install path runs the same desktop prerequisite helper automatically. It checks the current OS, runs the native doctor, and gives platform-specific guidance. The helper can enable non-protected settings such as GNOME toolkit accessibility, but it never bypasses protected OS consent. On macOS, the native onboarding opens the correct System Settings panes and waits for you to approve Open Computer Use.app — not Terminal or OpenCode.
Manual permission and verification commands.
macOS 14+ requires Accessibility and Screen Recording for Open Computer Use.app. The first command is the same checker used by installation.
# Run/re-run our guided installer check
npx opencode-computer-use-permissions --strict
# Native status + onboarding UI
open-computer-use doctor
# Open the exact System Settings panes manually if needed
open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"
open "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture"
# Verify after approving both toggles
open-computer-use doctor
open-computer-use call list_appsIf doctor still reports a missing permission after you enable it, close/reopen the affected app or Open Computer Use and run the check again. Do not use tccutil as a grant command: it can reset consent, not silently approve it.
Linux has no universal permission toggle. The requirement is a signed-in graphical desktop with AT-SPI2/D-Bus. GNOME also exposes a toolkit-accessibility setting that our install helper enables best-effort.
# Run/re-run our guided installer check
npx opencode-computer-use-permissions --strict
# Native runtime/session notes
open-computer-use doctor
# GNOME only: manually enable and verify toolkit accessibility
gsettings set org.gnome.desktop.interface toolkit-accessibility true
gsettings get org.gnome.desktop.interface toolkit-accessibility
# Inspect the session the runtime inherited
printf "desktop=%s session=%s\n" "$XDG_CURRENT_DESKTOP" "$XDG_SESSION_TYPE"
test -n "$DBUS_SESSION_BUS_ADDRESS" && echo "D-Bus session available" || echo "D-Bus session not exported; runtime will try discovery"
# Verify real desktop visibility
open-computer-use call list_appsOn Wayland, AT-SPI semantic actions can work while screenshots or coordinate input remain compositor-dependent. If a workflow needs reliable coordinate injection, X11 may behave more predictably; otherwise prefer element-index actions and verify state after every mutation.
Windows UI Automation does not have a separate privacy permission switch. The important requirement is that OpenCode/Open Computer Use runs in the signed-in user desktop rather than a Windows service or session 0.
# PowerShell
npx opencode-computer-use-permissions --strict
open-computer-use doctor
# Confirm this shell belongs to an interactive user session
Write-Host "SESSIONNAME=$env:SESSIONNAME"
(Get-Process -Id $PID).SessionId
# Verify UI Automation can see desktop applications
open-computer-use call list_appsA normal process cannot automate the UAC secure desktop. Elevated target applications can also be restricted from a lower-integrity automation process. Prefer ordinary user-session apps and keep OpenCode at the same integrity level as the target when automation is required.
How to turn conditional actions into reliable ones.
- Fix the host firstRun the guided permission check and make sure the native runtime can actually list the target app before debugging clicks or typing.
- Prefer semantic actionsUse a fresh accessibility element index for click, scroll, set-value, and secondary actions. This is more portable than remembered coordinates.
- Observe after mutationsRead a new accessibility state after navigation, dialogs, rerenders, or an uncertain timeout. Never assume a reported input event changed the UI.
- Use screenshots as a fallbackWhen the accessibility tree is incomplete, a screenshot can recover visual context if the OS capture backend permits it.
- Opt into pointer fallback only when needed
allowGlobalPointerFallbackscan recover coordinate-only cases, but it may move the real pointer or change focus and stays off by default. - Choose the right Linux sessionAT-SPI works best when the process can reach the logged-in desktop bus. Wayland may restrict capture/input; semantic actions are the preferred path.
Small surface. Strong boundaries.
- No arbitrary shellThe model-facing API does not expose
run_shellor a host command escape. - Restricted VMFilesystem, process, fetch, dynamic import, code generation, and host built-ins are blocked.
- Worker isolationCPU-bound JavaScript can be terminated without wedging the MCP server.
- Read-only modeAn opt-in safety profile blocks clicks, keys, text, scroll, drags, and secondary actions before native execution.
- App policyExact-name and ID allow or deny rules provide adapter-level defense in depth.
- Bounded transportInput frames, output text, images, content items, app bindings, and native responses are bounded.
- Lifecycle cleanupTimeouts send native cancellation, and shutdown sends a best-effort turn-ended notification.
- Approval preservingConsequential actions still pass through OpenCode and OS permission boundaries.
- Safe defaultsGlobal pointer fallback is disabled until explicitly enabled.
Read SECURITY.md before using the session against banking, password managers, administrator tools, or unattended workloads.
When the session is quiet.
Start with the same three checks on every platform. They separate plugin registration problems from native desktop permission/session problems before you retry an action.
opencode mcp list
npx opencode-computer-use-permissions --strict
open-computer-use call list_appsPermission failures by operating system.
doctor says Accessibility is missing
Likely cause: Open Computer Use.app has not been approved in Privacy & Security → Accessibility, or the permission was granted to Terminal/OpenCode instead of the app-agent that performs the real automation.
Fix: run open-computer-use doctor, use the onboarding UI, or open the Accessibility pane directly. Enable Open Computer Use, then restart the Open Computer Use app-agent/OpenCode and rerun doctor.
open-computer-use doctor
open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"
open-computer-use doctorScreenshots are missing or screenRecording=missing
Likely cause: Screen Recording / Screen & System Audio Recording has not been granted to Open Computer Use.app, or the already-running app-agent has not picked up the new TCC grant yet.
Fix: approve Open Computer Use in the Screen Recording pane, then quit/reopen the app-agent or restart OpenCode before checking again. Accessibility text can still work while screenshots are unavailable.
open "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture"
open-computer-use doctor
open-computer-use call list_appsSystem Settings shows permission granted, but onboarding still says Allow
Likely cause: stale TCC/app-agent state, an old installed copy, or a development build with a different signing identity/path.
Fix: first restart Open Computer Use and rerun doctor. Confirm you approved the current Open Computer Use.app, not Open Computer Use (Dev) or Terminal. As a last-resort reset, remove only this app's stored consent and let doctor request it again. These commands reset permission; they do not grant it.
tccutil reset Accessibility com.ifuryst.opencomputeruse
tccutil reset ScreenCapture com.ifuryst.opencomputeruse
open-computer-use doctorThe native binary will not start even after granting permissions
Likely cause: macOS is older than the native runtime's minimum supported version. Permission changes cannot fix binary compatibility.
Fix: check the OS version. The current macOS runtime requires macOS 14 or newer.
sw_vers -productVersion
open-computer-use --versionlist_apps is empty or AT-SPI cannot see desktop apps
Likely cause: the command is running outside the signed-in graphical session, toolkit accessibility is disabled, or the process cannot reach the user's D-Bus/AT-SPI session.
Fix: run OpenCode from the logged-in desktop user, not a pure SSH shell, service, CI job, or unrelated user session. On GNOME, enable toolkit accessibility and verify the session variables. The native runtime also tries to discover missing session variables automatically.
gsettings set org.gnome.desktop.interface toolkit-accessibility true
gsettings get org.gnome.desktop.interface toolkit-accessibility
printf "desktop=%s session=%s\n" "$XDG_CURRENT_DESKTOP" "$XDG_SESSION_TYPE"
printf "runtime=%s\n" "$XDG_RUNTIME_DIR"
test -n "$DBUS_SESSION_BUS_ADDRESS" && echo "D-Bus session available"
open-computer-use doctor
open-computer-use call list_appsScreenshot is black, omitted, or unavailable on Wayland
Likely cause: the compositor blocks the runtime's best-effort capture path. This is not an AT-SPI permission toggle and there is no universal Linux command that grants unrestricted screen capture.
Fix: continue with accessibility text/semantic actions when possible. If the workflow requires reliable coordinate/screenshot automation, test the same workflow in an X11 session or a compositor/environment whose capture path works for the runtime.
Click/type/drag reports success but the application does not change
Likely cause: AT-SPI exposed a pseudo-window or incomplete action tree, or Wayland rejected synthetic coordinate/key input. A native event being accepted does not prove the application consumed it.
Fix: take a fresh state, prefer element-index accessibility actions, setValue, and advertised secondary actions over coordinates. Verify state after every mutation. If only coordinate synthesis is failing, compare the same workflow under X11 before treating it as an adapter bug.
Error mentions Accessible.is_text or is_editable_text
Likely cause: an older Linux native runtime is using PyGObject helpers that are not available on that distribution.
Fix: update/reinstall the current native runtime. The current package line detects AT-SPI Text/EditableText through standard interfaces.
npm install [email protected]
open-computer-use --version
open-computer-use call list_appslist_apps is empty even though doctor runs
Likely cause: Open Computer Use is running in a service, session 0, detached SSH context, or another non-interactive session. UI Automation only sees windows in the signed-in interactive desktop.
Fix: start OpenCode/Open Computer Use from the user's normal desktop session and confirm the current PowerShell process has a user session ID.
open-computer-use doctor
Write-Host "SESSIONNAME=$env:SESSIONNAME"
(Get-Process -Id $PID).SessionId
open-computer-use call list_appsThe target app is elevated and clicks/typing fail
Likely cause: Windows integrity-level boundaries can block a normal process from driving a higher-integrity application.
Fix: prefer running the target as a normal user. If automation genuinely requires an elevated target, run the automation host at the same integrity level only for that explicit task and keep the elevated session narrow.
A UAC prompt, lock screen, or sign-in screen cannot be controlled
Cause: these are Windows secure-desktop / OS-owned surfaces. This is an intentional security boundary, not a missing UI Automation permission.
Fix: approve or dismiss the UAC prompt manually and return to the normal user desktop. The plugin will not attempt to bypass the secure desktop.
App is found but an action does nothing
Likely cause: the app does not expose the expected UI Automation pattern or ignores the Win32 message fallback while backgrounded/hidden.
Fix: make sure the target has a visible, non-minimized top-level window, refresh the accessibility state, and prefer semantic element actions over coordinates. Verify the resulting state instead of trusting the input call alone.
General failures.
cua_repl is not listed
Check that the plugin path/package is in the active OpenCode generation's configuration, restart the TUI/service, and run opencode mcp list. Do not register the same plugin and MCP server twice.
Node cannot be found
Set OPENCODE_NODE to an absolute Node executable before starting OpenCode, or make node available on the server PATH.
The app is not found
Run open-computer-use call list_apps, use the exact returned app name or ID, and confirm the app already has a visible, non-minimized window in the same graphical session.
Element index stopped working after navigation
Element indexes belong to a particular accessibility snapshot. Fetch a fresh state after navigation, dialogs, rerenders, or focus changes before acting again.
Global pointer fallback is rejected
This is the expected safety gate. Enable allowGlobalPointerFallbacks only for an explicit diagnostic session where moving the real pointer and changing focus are acceptable.
Frequently asked questions.
Is OpenCode Computer Use a plugin, an MCP server, or both?
Both layers are present. The OpenCode plugin loads the local package and registers an MCP server named cua_repl. That server exposes only js and js_reset to the model.
What do js and js_reset do?
js evaluates JavaScript in the persistent session, so variables and app bindings survive between calls. js_reset discards those bindings but does not reset native application state.
Which operating systems are supported?
Optional native runtime packages target Linux, macOS, and Windows on x64 and arm64. CI is configured to test the adapter on all three operating systems; live capabilities still depend on OS permissions, the desktop session, and the target app, and macOS/Windows native desktop behavior requires on-host validation.
Does the plugin bypass operating-system permissions?
No. It cannot control a lock screen, login screen, or UAC secure desktop. OpenCode approvals and operating-system permissions remain the enforcement layer.
Does the model get raw desktop tools or an arbitrary shell?
No. The model sees only js and js_reset, not raw desktop tools or run_shell. The restricted JavaScript session also blocks filesystem, process, fetch, dynamic import, and code-generation access.
How do I verify the installation?
Restart OpenCode or reopen the TUI, run opencode mcp list, and confirm that cua_repl is connected before starting a desktop task.