Add a Desktop MCP Server to Codex CLI
Codex CLI reads MCP servers from ~/.codex/config.toml. Here is the exact TOML block OpenOwl's installer writes, the two macOS permissions the server needs, and the errors you will hit on the way.
Loading...
Codex CLI reads MCP servers from ~/.codex/config.toml. Here is the exact TOML block OpenOwl's installer writes, the two macOS permissions the server needs, and the errors you will hit on the way.
Give Claude, Codex, or any AI assistant the ability to see your screen and automate any task. Runs locally on your Mac or PC.
Codex CLI reads its MCP server definitions from ~/.codex/config.toml, so setup means adding an [mcp_servers.owl] table with a command key and restarting the CLI. That one file is the whole mechanism, and it applies to every project on the machine. Four things decide whether it works: the exact block OpenOwl's installer writes, the two macOS permissions the server needs before any tool returns useful output, how you confirm the connection, and the failure modes waiting along the way.
One constraint up front: OpenOwl runs on macOS, version 12 or later, on Apple Silicon and Intel. The TOML syntax below applies to any MCP server you register with Codex, but the OpenOwl binary has no Windows or Linux build.
Codex CLI reads files, writes files, runs shell commands, and edits code in your repository, all inside the terminal sandbox. Screen access sits outside that boundary.
So Codex cannot see your screen. Ask it to verify a visual change and it will read the CSS, reason about what should render, and describe what it expects. Looking at the result is beyond what the base install can do.
An MCP server gives it that access. MCP defines a transport and a tool schema, so a server you register exposes a set of callable functions with typed arguments. OpenOwl registers 40 of them, covering screen capture, coordinate clicking, keyboard and mouse input, OCR text search, accessibility tree queries, window management, and batched action sequences.
| Capability | Codex CLI alone | Codex CLI with OpenOwl |
|---|---|---|
| Read and edit source files | Yes | Yes |
| Run shell commands | Yes | Yes |
| Capture the screen | No | screenshot |
| Click a control by its label | No | click_element, click_text |
| Type into a native app | No | type_text, send_keys |
| Read text baked into pixels | No | find_text (OCR) |
| Query the accessibility tree | No | list_elements, find_element |
| Enumerate open windows | No | list_windows |
| Run several actions in one call | No | batch_actions |
The path is ~/.codex/config.toml. Codex creates the ~/.codex/ directory on first run, but the config file may not exist until you create it. Make both if they are missing:
mkdir -p ~/.codex
touch ~/.codex/config.toml
Every Codex session on your machine shares this file. A server you add here loads for all projects, which suits desktop automation, because the screen is a machine-level resource.
The config block points at an executable named owl. If that binary is not on your PATH when Codex spawns the server, the entry does nothing. Install it first.
Homebrew:
brew install mihir-kanzariya/owl/owl
npm, which needs Node 16 or later:
npm install -g openowl
Next, save an API key. The free tier allows 50 tool calls per day and does not ask for a card. Create a key in the dashboard, then write it to disk:
mkdir -p ~/.openowl
echo 'owl-XXXX-XXXX-XXXX' > ~/.openowl/api.key
The server also reads OPENOWL_API_KEY from the environment. Prefer the key file when working with Codex, because Codex launches the server as a child process, and whether your shell exports reach that child depends on how the terminal session started. The file removes that ambiguity.
Now run the binary once by hand:
owl --version
The first launch creates a virtual environment at ~/.openowl/.venv and installs NumPy along with a few other runtime dependencies. Let it finish before you move on. Doing this before you wire up Codex matters, because otherwise that one-time delay surfaces as an apparent hang on your first tool call inside the CLI.
Append this to ~/.codex/config.toml:
[mcp_servers.owl]
command = "owl"
Those two lines are the same block OpenOwl's install script appends, so every install exercises this exact configuration.
Both halves of the table name matter. mcp_servers is the section Codex scans, and the segment after the dot (owl) becomes the namespace for the tools in your session. Name it something else and the tools still load, but prompts that reference owl by name will not match. Note the underscore: Codex uses mcp_servers, while Claude Desktop's JSON config uses the camelCase mcpServers. Copy the JSON key naming into TOML and you get a server that never loads, with no error message to point at the cause.
If owl does not resolve from Codex's PATH, use an absolute path instead. Find it first, then paste the result:
which owl
[mcp_servers.owl]
command = "/opt/homebrew/bin/owl"
Restart Codex CLI after saving. It reads the config at startup and will not pick up edits you make to a running session.
OpenOwl needs two permissions from System Settings under Privacy & Security. Neither is optional, and they serve different halves of the toolset.
| Permission | What breaks without it |
|---|---|
| Accessibility | Clicking, typing, key sends, and all accessibility tree queries |
| Screen Recording | Screenshots and window title reads |
The server checks both at startup and writes the result to stderr. A healthy start looks like this:
[OpenOwl] macOS permissions OK.
A missing grant looks like this:
[OpenOwl] macOS permissions needed: Accessibility and Screen Recording
[OpenOwl] Grant access in System Settings > Privacy & Security,
[OpenOwl] then restart the server for changes to take effect.
Two details trip people up here. First, macOS caches the permission decision for the lifetime of a process, so granting access while Codex is running changes nothing until you quit and reopen Codex. Restart after every grant. Second, the entry that appears in the permission list is often your terminal application rather than owl, because Codex spawns the server as a child of the terminal and macOS attributes the request to the parent binary. Grant access to whichever entry appears in the list, even when the name surprises you.
Start a Codex session and give it a task that succeeds only when the tools loaded:
Take a screenshot of my screen and tell me which application is in the foreground.
A working setup calls screenshot, gets an image back, and answers with the app name. A broken setup replies that it has no access to your screen, which means Codex never registered the server.
When that happens, work through three checks in order. First, run owl --version on its own to confirm the binary works. Then read the config block back:
grep -A1 'mcp_servers.owl' ~/.codex/config.toml
Last, watch stderr on startup for the preflight line. In almost every failed first run, one of those three is the cause.
Start with the workflow that closes the loop between code and rendered output. Codex writes a component, then looks at the result instead of assuming it.
Start your dev server, then prompt something like this:
The signup form at localhost:3000/signup has a validation message that renders too close to the input. Screenshot the page, find the validation text, and tell me the vertical gap between it and the field above it.
Codex calls screenshot to capture the page and find_text to locate the validation string through OCR, then reports coordinates it can check against your CSS. Because it can see the rendered result, its fix targets what the browser drew.
The same pattern extends to multi-step flows. Ask it to fill the form with type_text, submit with click_element, and call wait_for_change to block until the page settles before capturing the result. Run that sequence and you catch the bug class where the happy path renders as intended and the post-submit state does not.
Watch your call budget while you do this. Every tool invocation counts against the daily quota, and an interactive review loop burns through them fast. batch_actions helps: it takes a list of actions and executes them as a single call, so a fill-and-submit sequence costs one call instead of four. On the free tier's 50 calls per day, batching is what buys you several review passes.
| Symptom | Cause | Fix |
|---|---|---|
| Codex starts, no owl tools appear | You named the table mcpServers instead of mcp_servers | Use the TOML spelling with the underscore |
owl: command not found in the log | The binary sits outside the PATH Codex inherits | Set command to the absolute path from which owl |
[OpenOwl] ERROR: No API key found. | No key file and no environment variable | Write the key to ~/.openowl/api.key |
| Screenshots fail or return nothing usable | You have not granted Screen Recording | Grant it, then restart Codex |
| Clicks and keystrokes do nothing | You have not granted Accessibility | Grant it, then restart Codex |
| Permissions granted but still failing | You granted them while the process was running | Quit Codex and start it again |
| First tool call hangs for a while | The virtual environment bootstraps on first run | Run owl --version in a terminal and wait for it to finish |
| Daily limit message on a tool call | You used up the day's free calls | Wait for the reset or move to a paid tier |
| Works in Claude Code, fails in Codex | The two clients resolve PATH in different ways | Use an absolute command path in config.toml |
For a walkthrough with copy-paste commands for each client, see the OpenOwl quick setup guide. If your failure does not match anything in the table above, the OpenOwl help page covers less common cases and how to reach support.
At ~/.codex/config.toml. Codex creates the ~/.codex/ directory on first run, but you may need to create the config file yourself with touch ~/.codex/config.toml. Every Codex session on the machine uses it, whatever project you have open.
No. OpenOwl ships for macOS, version 12 or later, on Apple Silicon and Intel. Codex CLI itself runs on other platforms and the [mcp_servers.*] config syntax is identical there, so you can register other MCP servers, but the OpenOwl binary has no Windows or Linux build.
Three causes account for most of it. You may have spelled the table mcpServers instead of mcp_servers, which is valid TOML that Codex ignores. The owl binary may sit outside the PATH Codex inherits, which an absolute command path fixes. Or your session predates the edit.
50 per day. Each tool invocation counts as one call, so a screenshot followed by a click and a text lookup spends three. batch_actions runs a sequence of actions as a single call, which is the main way to stretch the quota during interactive work.
Yes. Each client spawns its own server process from its own config, and the two do not conflict. When both authenticate with the same API key, their usage counts against one daily quota, because OpenOwl tracks the limit per key.