Codex can't see your screen? Appshots, MCP servers, and the macOS fix
7 min readMilind Soni
You ask Codex to fix a layout bug and it asks you to describe the layout. You tell it to "look at my screen" and it explains, politely, that it can't. Both are working as designed — Codex has good vision, but no eyes of its own.
Codex never captures your screen on its own. Every pixel the model sees arrives one of three ways: you attach an image yourself (paste or the
-iflag), you press the Appshots hotkey in the Codex macOS app, or an MCP server takes the screenshot and returns it as a tool result.
This post is the troubleshooting map for all three, plus the bugs you'll hit on the way.
Is there a way for the model to actually see my screen?
Codex accepts images, and has for a while. In the CLI you attach them explicitly with the --image / -i flag — codex -i error.png "why is this button clipped?" — with multiple files as a comma-separated list. Inside the interactive TUI you can paste from the clipboard or drag a file in. Since v0.115.0 (March 2026), attached images render at full resolution instead of compressed previews, which matters when the bug is a 2-pixel misalignment.
What Codex does not have is autonomous capture. There's no built-in tool that screenshots your desktop mid-task — unlike Claude Code, which shipped built-in computer use for macOS as a research preview in March 2026. On the Codex side, getting the screen to the model stayed manual until May 2026, when the Codex app for macOS got a hotkey for it.
What is Appshots and how do I use it?
Appshots shipped in the Codex macOS app's May 21, 2026 update. Press both Command keys at once (left ⌘ + right ⌘ — rebindable in settings) and the frontmost window is attached to your Codex thread.
Per the docs, an appshot contains two things:
- An image of the visible window.
- Available text from that window — "including visible text and text the app makes available outside the visible scroll area," read over the macOS Accessibility API.
That second part is the clever bit. Codex doesn't just get pixels; it gets the full text of the document, email, or error log, including what's scrolled out of view. Sending structure alongside pixels is empirically the right call — OSWorld ablations measured accessibility tree + screenshot at 69.2–71.8% relative task success versus 56.4% for a screenshot-only Set-of-Mark setup.
Appshots needs two permissions: Screen & System Audio Recording (for the image) and Accessibility (for the text). Grant both or you get half a capture.
Where Appshots stops
Appshots is genuinely useful, and it has hard edges. All of these are either documented or straightforwardly observable:
- One window, frontmost only. No background windows, no multi-window context, no "the terminal and the browser." If the thing you're debugging spans two apps, that's two appshots and two round trips.
- App-only. It's a feature of the Codex desktop app. The CLI and TUI have no Appshots hotkey; there you're still on
-iand paste. - macOS-only. The Windows Codex app doesn't have it — the feature leans on the macOS Accessibility and Screen Recording stack.
- Web apps can go text-blind. The docs note that for Google Docs, Gmail, Sheets, and Slides, Codex "sometimes only captures the visible screenshot" without off-screen text; OpenAI's workaround is installing the matching plugin.
- No pointing. You send the whole window. There's no way to indicate which button, row, or misrendered div you mean — no gesture, no voice, no element identity.
- iOS Simulator: only if frontmost, only as pixels. Bring the Simulator forward and Appshots will capture it like any window, but you get a flat image — not the simulated app's accessibility elements or your
accessibilityIdentifiers.
For everything past those edges, you want an MCP server.
How do I add a screenshot MCP server to Codex?
Codex configures MCP servers in ~/.codex/config.toml (see the MCP docs). A local stdio server:
[mcp_servers.screenshot]
command = "npx"
args = ["-y", "your-screenshot-mcp-server"]
Or from the command line:
codex mcp add screenshot -- npx -y your-screenshot-mcp-server
Remote and HTTP servers take a url, with bearer_token_env_var and http_headers for auth:
[mcp_servers.capture]
url = "http://127.0.0.1:19741/mcp"
http_headers = { "Authorization" = "Bearer YOUR_TOKEN" }
Two facts worth knowing before you wire one up. First, the MCP spec allows tool results to carry base64 images, not just text — and Codex now honors that. It didn't always: issue #4819 documented Codex swallowing MCP image results as inert base64 the model couldn't see, fixed by PR #5600. If your Codex is ancient and screenshots come back as <image content> the model ignores, upgrade.
Second, if you're on a build whose HTTP support won't send the headers your server needs, the standard workaround is a stdio bridge: a small local process Codex launches over stdio that forwards requests to the HTTP server with the auth header attached. Most local capture tools ship one.
For a comparison of the actual servers — Peekaboo, Screenpipe, and friends — see our screenshot MCP server roundup.
Image paste isn't working: known bugs and fixes
The Codex TUI's clipboard handling has a real bug trail. The fixes, in order of likelihood:
- On macOS, press Ctrl+V, not Cmd+V. Cmd+V pastes text fine but silently does nothing for images; Ctrl+V attaches them. This inconsistency is tracked across #3397, #10523, and #24322.
- Capture straight to clipboard. Cmd+Ctrl+Shift+4 puts a region screenshot on the macOS clipboard; then Ctrl+V in Codex. No file dance.
- Paste dead entirely? Some sessions lose both Ctrl+V and right-click paste (#18645). Fall back to a path:
codex -i /path/to/shot.png "...", or drag the file onto the composer. - Windows Terminal + WSL: clipboard images don't cross the WSL boundary (#9999). Save to a file inside the WSL filesystem and use
-i.
Claude Code has its own, different paste quirks — we covered those in pasting screenshots into Claude Code.
The full spatial-context setup for Codex
Full disclosure: we build SupaMaus Lite, a macOS menu-bar app that exists precisely because "one flat window capture" wasn't enough context. It runs a local, token-authenticated MCP server on 127.0.0.1:19741 — no API keys, everything on-device.
The workflow: hold Control+Option and move the mouse over the thing you mean. That draws a highlight trail, and releasing saves an annotated screenshot plus the accessibility element under your gesture — role, name, pixel bounds — the full window text (including scrolled-away content), and a local voice transcript so "this button here" resolves to an actual button. Because Codex renders MCP image results post-#5600, the capture lands in your thread with pixels and structure intact. It also resolves elements inside the iOS Simulator, down to your own accessibilityIdentifier — the gap Appshots leaves open.
And when MCP itself is the broken link, every capture mirrors to ~/.supamaus/latest/ (capture.png, context.md, manifest.json); pasting that path into any composer works even in sessions where clipboard paste is dead. Setup for both Claude Code and Codex is one command: agents/install.sh writes the MCP config and an agent skill for each.
FAQ
Does Codex support screenshots?
Yes — as input. Attach files with codex -i shot.png, paste into the TUI (Ctrl+V on macOS), press the Appshots hotkey in the macOS app, or let an MCP server return images as tool results. Codex cannot take a screenshot of your screen by itself.
What is Appshots? A Codex macOS app feature (May 2026): press both Command keys and the frontmost window is sent to your thread as an image plus its accessible text, including content scrolled out of view.
Can Codex see the iOS Simulator?
Only if you show it. Appshots captures the Simulator as a flat image when it's the frontmost window. For element-level context — including accessibilityIdentifiers from your own app — you need an MCP server that reads the Simulator's bridged accessibility tree, like SupaMaus Lite.
How do I add an MCP server to Codex?
codex mcp add <name> -- <command> for stdio servers, or edit ~/.codex/config.toml directly; HTTP servers take a url with optional bearer_token_env_var and http_headers.