peekaboo mcp
mcp runs Peekaboo as a Model Context Protocol server. peekaboo mcp defaults to serve, so you can launch the server without specifying a subcommand.
#Subcommands
| Name | Purpose | Key options |
|---|---|---|
serve | Run Peekaboo’s MCP server over stdio. | --transport stdio (default); global --bridge-socket <path> attaches to an existing Bridge host. HTTP/SSE names and --port are reserved for future support and currently fail with an actionable error. |
#Implementation notes
serveinstantiatesPeekabooMCPServerand maps the transport string toPeekabooCore.TransportType. Stdio is the default for Claude Code integrations.- Public MCP servers are always background-only. Foreground actions, shared desktop input, browser connection setup,
- Direct-text
pasteis admitted only with an exact generation-pinned app/PID/window authorization and a canonical - HTTP/SSE server transports are reserved but not implemented. Selecting either fails before daemon startup and emits a structured error in JSON mode.
- The MCP process owns its stdio lifecycle and never hosts a Bridge listener. Support stays process-local by default;
- If an unrelated legacy Bridge is still a possible ScreenCaptureKit owner, startup and non-capturing tools remain
- The native tool catalog includes bounded
capturefor live screen/window/region recording or video ingest. It writes retained frames,contact.png,metadata.json, and optional MP4 output, so use tool allow/deny filters when exposing MCP to untrusted clients. - UI automation tools include action-first additions:
set_valuedirectly mutates a settable accessibility value, andactioninvokes a named accessibility action on an element fromsee. verify_statereplaces fixed sleeps with bounded native polling. It resolves an app or PID to one exact window, evaluates 1–8 AND predicates for window existence/bounds or exact AX element existence/value/enabled/selected state every 100 ms, and reportssatisfied,unsatisfied, or conservativeunknownafter at most 10 seconds. Explicit PIDs and app-name selectors are pinned to the first resolved PID/process-start generation for the whole invocation; relaunch, PID reuse, and selector drift areunknown. Exact-window ownership is rechecked on every sample and before an optional screenshot, whose capture metadata must confirm the same PID and window ID. A directly read value matching a unique exact AX identifier can satisfy anelement_valuepredicate when unrelated AX siblings are unreadable; missing, mismatched, non-identifier, or ambiguous partial-tree evidence remainsunknown. A WindowServer miss is corroborated with a complete app-scoped window inventory before Peekaboo reports absence, preserving minimized AX windows. Ownership ambiguity, partial enumeration, or identity changes areunknown. It never focuses or replays actions.clickpreserves element IDs and queries when forwarding to automation, so action-first policy can use accessibility actions before synthetic fallback.
and ambient browser auto-connect fail before dispatch. Establish an exact browser connection separately with peekaboo browser connect --foreground; MCP browser calls can then reuse its signed live receipt.
background result. Targetless, foreground, current-clipboard, and binary paste are refused before dispatch. The nested agent tool likewise retains immutable background-only authority and never exposes Shell.
an explicit --bridge-socket <path> uses that existing Bridge host and skips the embedded daemon.
available through an explicitly selected current host, but pixel-producing calls fail before dispatch with the exact owner diagnostic. That capture refusal is immutable for the connection; after fixing the owner, start a fresh MCP process before retrying capture.
#Examples
# Start the Peekaboo MCP server (defaults to stdio)
peekaboo mcp
# Explicit transport selection
peekaboo mcp serve --transport stdio
# Route MCP tools through an existing Bridge host
peekaboo mcp serve --bridge-socket "$HOME/Library/Application Support/Peekaboo/bridge.sock"
An MCP client can wait for stable native state without interrupting the user:
{
"app": "TextEdit",
"predicates": [
{
"kind": "element_value",
"selector": { "identifier": "document-content" },
"expected_value": "Ready"
},
{ "kind": "window_exists", "expected": true }
],
"stable_samples": 2,
"timeout_ms": 5000
}
#Troubleshooting
- Verify Screen Recording + Accessibility permissions (
peekaboo permissions status). - Confirm your process with
peekaboo app list, its exact window withpeekaboo window list, and current UI withpeekaboo seebefore rerunning. - Re-run with
--jsonor--verboseto surface detailed errors.