Give your coding agent
eyes on macOS.
Plumshot lets AI agents capture and annotate native macOS windows, menus, sheets, and the menu bar — UI that has no DOM and no accessibility tree, so Playwright and browser tools can't see it. It runs locally next to your agent: no account, no token, no upload.
macOS 14 Sonoma or laterScreen Recording permissionNode ≥ 18100% on-device
What your agent hands back — a marked-up PNG, not a paragraph of coordinates.
Two directions
Screenshots become a conversation
The agent shows you
Your agent grabs a window, draws a red box and an arrow, numbers the problem, and hands back a marked-up PNG — instead of a paragraph of pixel coordinates. Small captures and every annotation come back inline as a base64 PNG, so a vision agent sees the result in a single call.
capture_window → annotate → show the human
You show the agent
Mark up a screenshot yourself — in Plumshot, Preview, anything — and your agent just reads the marks. Your circles, arrows, and notes are the instructions. No capture, no permission needed.
you annotate → agent Reads the PNG → agent acts
Three surfaces
Pick how you drive it
plumshot …Best when you're driving from a shell or a non-MCP script. Every capture prints JSON: { path, width, height, bbox }. The CLI is the signed Plumshot.app binary.
plumshot-mcpA zero-dependency stdio server (server.mjs, Node ≥ 18). Best inside Claude Code, Codex, or Cursor. Small images return inline as base64 PNG; large ones return a path.
/plugin install plumshotBest for the easiest install and built-in guidance on when to reach for Plumshot. One marketplace command brings the MCP server and the skill in together.
No hosted URL, no network, no API key — the inverse of every cloud MCP. Your captures never leave your Mac.
Quickstart
Give your agent eyes in 4 steps
The CLI is the signed app binary, so you build the app once, then wire any client to it.
Build the app
Builds the Plumshot scheme with Apple Development signing — so the Screen Recording grant sticks across rebuilds.
Put the CLI on PATH
Grant Screen Recording
System Settings ▸ Privacy & Security ▸ Screen Recording → enable Plumshot → relaunch. annotate needs no permission.
Verify
One more smoke test before wiring a client: capture a window and list targets.
Connect your agent
Wire it into your client
Jump to your client. Every block ends with the same verify ritual: run plumshot doctor.
Claude Code
The easy button — install the plugin (brings the MCP server and the skill together):
Or register the MCP server yourself — a .mcp.json at the repo root:
…or one line from the CLI:
Then run plumshot doctor (or the doctor tool) to confirm the CLI resolves and Screen Recording is granted.
OpenAI Codex
Add the server to ~/.codex/config.toml with an absolute path to server.mjs:
The env table is optional — drop it if your dev build resolves on its own.
Then run plumshot doctor (or the doctor tool) to confirm the CLI resolves and Screen Recording is granted.
Cursor
Add the server to .cursor/mcp.json (project) or ~/.cursor/mcp.json (global), absolute path:
Then run plumshot doctor (or the doctor tool) to confirm the CLI resolves and Screen Recording is granted.
Other MCP clients
Any MCP host launches the server over stdio — point it at:
For a non-default build, set PLUMSHOT_APP (path to the .app) or PLUMSHOT_BIN(full binary path) in the host's env block.
Permissions & privacy
Screen Recording is the only gate
Plumshot needs just one macOS permission — Screen Recording — for the capture tools. Not Accessibility (except scrolling auto-scroll), not Full Disk Access, and it makes no network requests.
- Captures are local files you choose to share — nothing is uploaded. No account, no token.
- annotate needs no permission at all — it composites onto a PNG you already have. Only the capture_* tools touch the screen.
- Images ≤ 5 MB come back inline as base64 PNG; larger ones return a path. capture_full and capture_scrolling always return a path by design.
- capture-scrolling's auto-scroll additionally needs Accessibility — run plumshot doctor to check, or pass --no-auto-scroll and scroll yourself.
- Redact before you share: cover secrets with a blur shape (style blackOut for a hard cover) before surfacing an image.
Tool catalog
The 9 MCP tools
All coordinates are top-left origin, image pixels. Every capture also prints a JSON envelope: { path, width, height, bbox }.
| Tool | Arguments | Returns |
|---|---|---|
| capture_region | x, y, w, h (required), display? (0) | Inline PNG + path |
| capture_window | app (required, substring), window? (title) | Inline PNG + path |
| capture_previous_region | — | Inline PNG + path · repeats last region |
| capture_full | display? (under cursor) | Path only (large) |
| capture_scrolling | x, y, w, h (required), display? (0), max_seconds? (30) | Path only · needs Accessibility |
| annotate | image (path), shapes (array or {shapes:[…]}), out? | Inline PNG + path · no permission |
| capture_text | input? (PNG path) | x, y, w, h | app + window?; keep_linebreaks? (false) | JSON { text, chars } · on-device OCR |
| list_targets | — | JSON { displays, windows } |
| doctor | — | Permissions + signing status |
Annotate from JSON
Marks are a declarative shape spec
No mouse, no GUI — your agent authors arrows, boxes, labels, and redactions as JSON.
Defaults: color #FF3B30 (red), lineWidth 3, text fontSize 28, number marker 36, blur pixelate. Pipe the spec via --shapes - (stdin) or --shapes file.json. On Retina, use the pixel numbers Plumshot prints — they are already device pixels.
Example prompts
What to actually say
The fastest way to feel the payoff — these map directly onto the two loops.
Loop A — the agent shows you
›Capture the Preferences window of MyApp and tell me if the Save button label is clipped.
›Screenshot the Settings sheet, draw a red box around the broken label with an arrow to it, number it 1, and show me.
›Take a scrolling capture of this list and check that the bottom rows render.
Loop B — you show the agent
›Read ~/shot.png — I drew arrows on what to fix. Do what they point at.
›Here's a screenshot I marked up; what does box 1 point at?
Priming
›Run plumshot doctor and list-targets, then capture the front window of MyApp.
Positioning
When to reach for Plumshot
Use Plumshot for
- Native AppKit/SwiftUI windows
- NSMenu and the menu bar
- Sheets, alerts, popovers
- Custom-drawn views & canvases
- Drag previews and traffic-light spacing
- Anything with no reliable accessibility tree
Reach for something else for
- Web UI → Playwright / a browser MCP (you get DOM, console, network)
- Pure text extraction → capture_text reads the words directly (on-device OCR) — cheaper than shipping pixels to a vision model
- Headless CI with no display or Screen Recording grant
- Sub-pixel diffing → capture, then diff with an image tool
Troubleshooting
Native-macOS failure modes
When in doubt, re-run plumshot doctor — it's the universal did-it-work check.
plumshot: command not found
Symlink the shim onto PATH: ln -sf "$PWD/scripts/plumshot" /usr/local/bin/plumshot.
doctor says Screen Recording NOT granted
Grant it and relaunch. Rebuilding with ad-hoc signing drops the grant — rebuild with Apple Development signing (scripts/run_dev.sh) so it sticks.
Captures are black / empty
The classic missing-Screen-Recording symptom even when the call "succeeds." Confirm with plumshot doctor; make sure the target is on a logged-in GUI session.
Could not find the Plumshot.app binary
Build the app, or set PLUMSHOT_APP / PLUMSHOT_BIN. The shim's error lists every path it tried.
capture-scrolling doesn't scroll
Auto-scroll needs Accessibility. Grant it, or pass --no-auto-scroll and scroll the target yourself.
node not found when the host launches the server
Node ≥ 18 must be on the host's PATH. With a trimmed environment, give an absolute path to node in the command field.
Self-ingest
Built for agents to read, too
Server-rendered, with machine-readable twins an agent can fetch directly.
Agent index
A curated, terse index of the three surfaces, the two loops, and instructions for when to reach for Plumshot — per the llms.txt convention.
/agents.mdMarkdown twin
This page as plain markdown — so an agent that fetches it gets parseable config and commands, not rendered HTML.
The Claude Code skill (plugins/plumshot/skills/plumshot/SKILL.md) ships the same "when to use Plumshot" guidance you can drop into an AGENTS.md or .cursor/rules.