Skip to main content
Glama
osamabinabsar

codex-cua-mcp

codex-cua-mcp

Collaborative Windows computer use for any MCP agent harness — no cloud, no account, no usage limits.

Windows-MCP and similar tools take over your PC: while the agent drives the mouse and keyboard, you can't. This server does the opposite. Every action names a target window and is delivered to that window only, without raising it, so you keep your focus, your mouse, and your keyboard while the agent works alongside you.

It is backed by the local Codex computer-use helper — a Windows binary that ships with the Codex app. No model runs through it, no request leaves your machine, and there is nothing to be rate-limited by. The model is whatever your harness already talks to: Claude Code, OpenCode, Hermes, Codex, Gemini CLI, Cursor — anything that speaks MCP.

"No cloud" describes this server: it makes no network calls of its own. The accessibility tree and the screenshot it returns go to your harness, which passes them to your model provider — so what leaves your machine is whatever your model already sees.

It also has a second backend that needs no OpenAI binary at all: the MIT-licensed trycua/cua driver. Same sixteen tools, same names, same arguments — a harness config does not change when you switch. See Two backends.


Why this is different from Windows-MCP

Windows-MCP

codex-cua-mcp

Focus during agent action

Taken from you (low-level input hooks block you)

Left alone — actions are routed to the target window

Mouse

Moves the real cursor

Stays where you left it

Screenshots of occluded windows

No

Yes (Windows.Graphics.Capture)

Cloud / usage limits

No, but no model either

No — the model is your harness's

Per-app consent

App-level

App-level, surfaced as an MCP elicitation you answer

The collaboration is not a wrapper trick. It is a property of how the helper delivers input: per-window UI Automation actuation and coordinate-routed background injection, instead of a global cursor-and-keyboard takeover.


Related MCP server: guarded-computer-use-mcp

Install

One machine

git clone https://github.com/osamabinabsar/windows-cua
cd windows-cua
powershell -ExecutionPolicy Bypass -File .\scripts\install.ps1 -Harness claude

Windows ships with a Restricted execution policy, which blocks local .ps1 files. The -ExecutionPolicy Bypass above is scoped to that one command. If your organisation manages the policy, ask it to allow signed local scripts instead.

The installer creates a virtualenv, installs the package, registers the server with your harness, and verifies it can find the helper. It is idempotent — run it again after a git pull and it will update in place.

-Harness accepts claude, codex, opencode, or all. -Backend accepts codex (the default) or cua.

To install on another PC, clone the repo there and run the same script. See Deploying to other PCs. The one thing that does not copy across is the helper binary: it comes from the Codex app's runtime, not from this repo.

Manually

uv pip installs into a virtualenv, so make one first:

uv venv
uv pip install -e .

Or run it straight from the checkout, without a separate install step — uv makes a virtualenv for it and installs the package into that:

uv run --with "mcp>=2" python -m codex_cua_mcp.server

Requirements

  • Windows 10 or 11 with UI Automation. (The code checks sys.platform, not the Windows version — it runs on either, but UI Automation behaves differently on older builds.)

  • Python 3.11+, or uv, which will fetch one. uv is what the installer uses.

  • Git, to clone the repo. There is no PyPI package yet.

  • For the codex backend: the Codex app (or a standalone helper package), which supplies codex-computer-use.exe; and the codex CLI, used for the approval handshake.

    The Codex CLI on its own is not enough. npm i -g @openai/codex ships the CLI but not the helper. The helper arrives with the Codex desktop app, which installs a runtime the server searches:

    %LOCALAPPDATA%\OpenAI\Codex\runtimes\cua_node\*\bin\node_modules\@oai\cua\bin\windows\codex-computer-use.exe
    %LOCALAPPDATA%\OpenAI\Codex\runtimes\cua_node\*\bin\node_modules\@oai\sky\bin\windows\codex-computer-use.exe

    A current install carries both package names, and the server searches both. Override with CODEX_COMPUTER_USE_BIN if yours lives elsewhere.

    If you would rather not depend on an OpenAI binary at all, use the cua backend below — it needs neither the app nor an account.

Verify the setup:

.venv\Scripts\codex-cua-mcp.exe --check

codex-cua-mcp is on your PATH only while the virtualenv is activated; the full path works from anywhere. --check reports which binaries it found — it is a discovery check, not a test of the whole chain, so read a clean report as "the pieces are present", not "the server works".

The MIT backend (no OpenAI binary)

If you would rather not depend on a proprietary binary, install the trycua/cua driver instead and run the server with --backend cua:

codex-cua-mcp --backend cua --check

The driver is MIT-licensed and Rust; it needs no Codex CLI and no OpenAI account. See Two backends.


Register with your harness

The installer does this for you (powershell -ExecutionPolicy Bypass -File .\scripts\install.ps1 -Harness claude). To do it by hand:

The examples below start the default codex backend. If you installed the MIT driver instead, add --backend cua to the server's arguments — otherwise you silently get the OpenAI binary whichever one you meant. WINDOWS_CUA_BACKEND=cua in the server's environment does the same thing.

Claude Code

claude mcp add --scope user codex-cua -- \
  C:\path\to\windows-cua\.venv\Scripts\python.exe -m codex_cua_mcp.server

Or as JSON (~/.claude.json / .mcp.json):

{
  "mcpServers": {
    "codex-cua": {
      "command": "C:\\path\\to\\windows-cua\\.venv\\Scripts\\python.exe",
      "args": ["-m", "codex_cua_mcp.server"],
      "env": {}
    }
  }
}

OpenCode

~/.config/opencode/opencode.json:

{
  "mcp": {
    "codex-cua": {
      "type": "local",
      "command": ["C:\\path\\to\\windows-cua\\.venv\\Scripts\\python.exe", "-m", "codex_cua_mcp.server"],
      "enabled": true
    }
  }
}

Codex CLI

~/.codex/config.toml:

[mcp_servers.codex-cua]
command = 'C:\path\to\windows-cua\.venv\Scripts\python.exe'
args = ['-m', 'codex_cua_mcp.server']

Any other MCP client

The server speaks MCP over stdio. Point any client at:

C:\path\to\windows-cua\.venv\Scripts\python.exe -m codex_cua_mcp.server

If you did not use the installer and have no virtualenv, the equivalent is:

uv run --project <path> python -m codex_cua_mcp.server

Uninstalling

powershell -ExecutionPolicy Bypass -File .\scripts\install.ps1 -Uninstall

That removes the codex-cua entries from ~/.claude.json, ~/.codex/config.toml and ~/.config/opencode/opencode.json — and nothing else. The rest of each file is edited in place, byte for byte, so your other settings and their formatting survive. The virtualenv, the repository and every other setting are left alone.

It is safe to run twice, and safe to run when you never installed anything. If it cannot remove one of the entries it says which file and leaves that file unchanged rather than risking it, and exits non-zero.


Taking the computer back

One chord stops the agent, from outside the harness, at any moment:

Ctrl + Alt + Shift + Backspace

Press it once and the agent loses the computer. Press it again and it gets it back. No panel, no tray icon, no prompt to answer.

One press stops the action in flight, takes the on-screen banner down, revokes every approval the agent had been given, and leaves the server running so it can be resumed. While it is held, every computer-use call fails with a message telling the agent to stop and wait for you.

The chord is armed automatically whenever the server starts. Change it with --takeover-key "Ctrl+Alt+Shift+Pause", or switch it off with --no-takeover.

Full documentation →


The banner

While the Codex helper is driving, it draws a banner on screen. Both sentences in it are yours to set, in a JSON file the helper already reads — no binary is patched:

%USERPROFILE%\.codex\computer-use\config.json
{
  "accentColor": "#339cff",
  "direction": "ltr",
  "locale": "en-GB",
  "strings": {
    "usingComputer": "AI is controlling this computer",
    "escToCancel": "Press Ctrl+Alt+Shift+Backspace to take over"
  }
}
python scripts\set-overlay-text.py --show
python scripts\set-overlay-text.py \
    --using "AI is controlling this computer" \
    --hint "Press Ctrl+Alt+Shift+Backspace to take over"

The helper reads it when it starts an overlay, so restart the MCP server to see the change. One rule: escToCancel is a label. It does not make a key work — point it at a key that actually stops the agent.

Full documentation →


Tools

Tool

Purpose

list_apps

Installed apps and the windows each owns. Start here.

list_windows

Every open, targetable window.

get_window

Rehydrate a window by id after a lost binding.

get_window_state

Accessibility tree (with element indexes) and/or screenshot.

read_accessibility_tree

Just the tree text — cheaper than a full state read.

click

Click at window-relative coordinates.

click_element

Click an element by index, via UI Automation. Prefer this.

type_text

Type literal text. Pass element_index to say where it goes.

press_key

Key or chord (Return, Control_L+a, …). Also takes element_index.

scroll

Scroll from a window-relative point.

drag

Drag between two window-relative points.

set_value

Replace an editable element's value by index. Empty clears it.

perform_secondary_action

Expand, Collapse, Raise, Scroll Up, …

launch_app

Launch an app by id or .exe path.

activate_window

Brings a window to the front. The one tool that takes focus on purpose.

status

Which binaries are in use, what is approved, helper log.

Every action except activate_window leaves your foreground window, your focus, and your mouse cursor exactly where they were — with one exception. On the cua backend, a window that refuses background input makes the driver ask for delivery_mode: "foreground", which it performs as a brief focus swap that restores your previous window afterwards. The server never fronts a window on a guess.


Two backends

The server speaks to one of two drivers. Both present the same sixteen tools, so your harness config does not change when you switch.

codex (default)

cua

Driver

codex-computer-use.exe, ships with the Codex app

cua-driver.exe, from trycua/cua

Licence

Proprietary (OpenAI)

MIT

Needs the Codex app

Yes

No

Needs an OpenAI account

No

No

Approval

Per-app MCP elicitation you answer

The driver's own permission model

Element handles

Per-observation indexes

Stable element_tokens

Input delivery

Per-window UI Automation

Explicit delivery_mode, background first

Switch with a flag:

python -m codex_cua_mcp.server --backend cua

or the WINDOWS_CUA_BACKEND=cua environment variable. --driver (or CUA_DRIVER_BIN) points at a specific cua-driver.exe if the auto-detection misses it.

The two differ in three places

Both backends are honest about where they are not equivalent:

  1. perform_secondary_action. The Codex helper has a real accessibility-action tool (Expand, Collapse, Toggle, Invoke). The cua driver does not — its closest is invoke_menu, which walks an application menu path. So on the cua backend, File>Save As goes to invoke_menu, a plain action name like Invoke is honoured by clicking the element, and anything it cannot express raises rather than silently doing nothing.

  2. Background delivery. Some window classes (dialogs, for instance) refuse background input. The cua driver detects that, says so, and asks for delivery_mode: "foreground" — which it performs as a brief focus swap that restores your previous foreground window after. The client takes that escalation; it never fronts a window on a guess.

  3. Text delivery. The Codex helper's type_text goes to whatever holds focus, so it needs an element_index to be reliable. The cua driver routes keystrokes to the target window itself, so it works without one — but pass element_index when you mean a specific control.


Where the text goes

On the codex backend, type_text and press_key send keystrokes to the control that holds focus in the target window. If nothing holds focus there, the characters are lost and the call still reports success. So always pass element_index:

type_text(text="hello", element_index=3)   # clicks element 3, then types
set_value(element_index=3, value="hello")   # no focus needed — the reliable one

set_value with an empty value clears the field. On Windows, click needs coordinates and click_element is the element-targeted path.

The cua driver routes keystrokes to the target window itself, so type_text works there without an element_index — but pass one anyway when you mean a specific control.


Approval model

The helper gates every app behind per-app consent. This server surfaces that as an MCP elicitation: your harness shows you a prompt, you answer, and the answer decides.

  • The approved-app key is written to the wire only after you accept.

  • Approvals persist for the session.

  • list_apps, list_windows, get_window, and status never prompt — they are read-only.

If your harness does not support elicitation, the action fails with a clear error naming the app, rather than proceeding unapproved.

To skip prompts entirely (not recommended):

python -m codex_cua_mcp.server --auto-approve

Workflow that works

Element indexes are only valid for the observation that produced them, and coordinates are only valid for the screenshot they came from. Observe, act once, re-observe.

1. list_apps              -> find the app, get its window id
2. get_window_state       -> read the tree, note element indexes
3. click_element(index=…) -> exactly one action
4. get_window_state       -> verify what changed

Layout

src/codex_cua_mcp/
  transport.py     JSON-RPC over the helper's stdio; approval handshake
  client.py        the sky Windows API, typed (Codex backend)
  cua_backend.py   a small MCP client for the trycua/cua driver
  cua_client.py    the same sky API, over the cua driver
  server.py        MCP surface + elicitation-based approval
  takeover.py      the take-over chord, and the gate it closes
  overlay.py       the helper's banner text
scripts/           protocol probes and a smoke test
tests/             the test suite; CI runs it on every push

Every script in there is indexed in scripts/README.md, with what it touches and what it does not.


Checking the drivers

Codex ships a new helper build now and then, and the helper's undocumented behaviours are the fragile part of this project. When it updates, re-check that the workarounds still hold:

.venv\Scripts\python.exe scripts\probe_helpers.py

It runs eight checks against the live helper and reports pass/fail for each. The target is Character Map, which cannot hold a user document, so running it cannot damage your work.

The cua backend has its own probe, with the same shape:

.venv\Scripts\python.exe scripts\probe_cua_backend.py

Twenty-four checks against the live cua-driver.exe. Run it after updating the driver — the driver moves faster than the helper, and its error messages are specific enough to tell you exactly what changed.

See the architecture doc for what the three workarounds are and why each is needed.


Troubleshooting

Symptom

Where to look

ModuleNotFoundError: No module named 'codex_cua_mcp.takeover' on a fresh clone

You are running an old checkout against new code, or the clone is missing files. Re-clone, or git pull.

Could not find codex-computer-use.exe

The Codex CLI is not enough — the helper ships with the Codex app. See Requirements. Or use --backend cua.

failed to launch codex app-server: program not found

The helper could not find the codex CLI. Set CODEX_CLI_PATH to its full path, or put codex on PATH.

codex-cua-mcp is not recognised

The virtualenv is not activated. Use .venv\Scripts\codex-cua-mcp.exe.

The mouse pointer disappears over parts of the screen (Explorer nav pane, VS Code file tree, Windows Search list, chat messages) — the item still highlights, but the arrow is gone

The invisible cursor

The agent did not stop when you pressed the chord

status reports the take-over mode. polling means another app owns the chord and the fallback is in use; none means it is switched off. See Taking the computer back.

The invisible cursor is a Windows 11 / display-driver compositing fault, not this server and not the MCP tooling. The doc records what was measured on the machine, what was ruled out, the three candidate mechanisms, and the by-eye tests that separate them — plus scripts/fix-mouse-cursor.ps1, which reports and (with Administrator, and a full power cycle) applies the MPO overrides.

More

Doc

What it is for

Using it in other harnesses

Pointing Claude Code, OpenCode, Codex CLI, Cursor, Gemini CLI or anything else at this server, and telling an agent it exists.

Deploying to other PCs

Installing on a second or third machine, and what does not copy across.

For new users

The short version, if you have never set up an MCP server before.

Taking the computer back

The take-over chord, and the banner text.

Architecture

How the pieces fit together, and the three undocumented helper behaviours this project works around.

Alternatives

The other projects in this space, and how they differ.

Licence

MIT. See LICENSE.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server for driving any Windows app through five layers including OCR, UI Automation, and direct OS operations. Enables AI agents to control Windows desktop and OS cursor-free, even on background/locked windows.
    51 npm
    2
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables agentic control of Windows via MCP tools for screenshots, mouse, keyboard, UI Automation, OCR, windows, and clipboard, while risky actions require real-human approval through a policy-gated dialog with audit logging.
    5
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Gives AI agents full control of a Windows computer over MCP through 63 native tools — screenshots and UI trees for vision, mouse/keyboard input, Chrome control via CDP, plus files, terminal, processes, and SSH/SFTP. The agent can look at the screen, click, type, launch apps, run commands, and verify each step with a fresh screenshot.
    45
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to see and operate Windows applications through MCP, including windows, screenshots, mouse, keyboard, scrolling, UI Automation, clipboard, and freehand or geometric drawing. Supports supervised creative workflows with native Win32 input, screenshots, allow-listed apps, and an idle safety guard.
    MIT