MCP Tools (Linux)
Reference for MCP tools Cua Driver exposes on Linux
This reference describes the Linux native tool registry. Other platforms: macOS MCP tools, Windows MCP tools. See MCP tool notes for shared guidance.
cua-driver exposes 60 MCP tools through a single stdio server (cua-driver mcp). Every tool is also callable from the shell as cua-driver <name> '<JSON-args>'.
Tool names are snake_case. Responses are MCP CallTool.Result envelopes: a text content block prefixed with a ✅ summary (or the error reason on failure), plus optional image or structured-content blocks on tools that produce them. See the CLI reference for CLI-specific options like --socket and --screenshot-out-file.
For the cross-cutting parameter contract (shared parameters, required-parameter rules, platform-specific parameters) and the action response shape, see MCP tool notes.
Tool names here match the CLI form exactly. cua-driver list_apps and the MCP list_apps tool run the same code path.
Runtime ownership. On Windows and Linux, bare cua-driver mcp owns its SDK runtime directly and shuts it down on stdin EOF. On macOS it proxies to the installed CuaDriver.app daemon so AX and Screen Recording grants retain the app-bundle identity. Passing --socket selects an explicit daemon/service endpoint on every platform. See the process model for the full lifecycle and wrapper-author guidance.
Inspection tools#
list_apps#
List Linux apps — both currently running and installed-but-not-running — with per-app state flags:
- running: is a process for this app live? (pid is 0 when false)
- active: reserved (Linux X11/Wayland focus model differs from frontmost-app); always false.
- kind:
"desktop"for XDG.desktoplauncher entries. - launch_path: the launcher command from
Exec=(field codes stripped). Pass tolaunch_app(launch_path=...). - bundle_id: the XDG "desktop file id" — the
.desktopfile's path relative to its XDGapplications/root with the.desktopsuffix stripped and path separators replaced with-(e.g.kde4/konqbrowser.desktop→kde4-konqbrowser). - last_used: RFC3339 mtime of the
.desktopfile, when readable.
Running apps come from /proc. Installed apps come from XDG Desktop Entry files in $XDG_DATA_HOME/applications and each $XDG_DATA_DIRS entry's applications/ subdir. Entries with NoDisplay=true or Hidden=true are filtered. A .desktop file whose launcher matches a running process (by basename) is merged into a single entry with running: true.
Use this for "is X installed?" as well as "is X running?". For per-window state — visibility, geometry, titles — call list_windows instead.
Arguments: none.
list_windows#
List top-level windows. Each record includes z_index (integer or null; higher values are closer to the front; null means stacking order is unavailable and callers must not infer one). To select a frontmost candidate, take the maximum integer z_index; if every value is null, use an explicit fallback instead of relying on array order.
Arguments:
on_screen_only(boolean, optional): When true, filter to visible windows only. Default false.pid(integer, optional)
get_window_state#
Walk a running app's AT-SPI tree and return BOTH a structured elements array (preferred) AND a Markdown rendering of the same tree (back-compat). Every actionable element is tagged with [element_index N] in the markdown and as element_index in the structured array.
PREFERRED CONSUMERS read structuredContent.elements (one entry per indexed row with element_index, role, label, value, enabled, selected, actions (names of AT-SPI actions exposed by the element, omitted when empty), frame: {x,y,w,h} when AT-SPI reports usable bounds, parent_index, depth). The markdown tree_markdown stays available and unchanged in shape for existing text-parsing callers — but new fields will only be added to the structured side. Set query to project BOTH representations to matching rows plus their ancestor chain while preserving original indices. total_element_count reports the complete snapshot and returned_element_count reports the projection.
Always returns BOTH the element tree AND a screenshot — ground on both and cross-check (the tree lies on some surfaces). Choose the modality at ACTION time: an element ax action (element_index/element_token → accessibility rung) or an element px action (x,y → pixel rung off this screenshot). capture_mode is deprecated and ignored. On Wayland, where output capture cannot prove the requested surface's identity, the truthful tree is returned without a screenshot and screenshot_error.code is surface_identity_unproven.
The mirror image: pass include_accessibility_tree:false to SKIP the AT-SPI walk entirely and return just the screenshot plus window metadata (window_bounds, app_name, window_title) — the capture-only path for a live window preview / picture-in-picture. Setting BOTH include_accessibility_tree:false and include_screenshot:false is an error. Optional max_dimension caps the returned screenshot's long edge in pixels for a cheap thumbnail.
Optional max_elements / max_depth bound the AT-SPI walk to mitigate context-window blow-up on Electron / large web apps that produce 10k+ element trees. When applied, BOTH the markdown and the structured elements are truncated identically. Omit both for current default behaviour.
Arguments:
capture_mode(string, optional): DEPRECATED and ignored. get_window_state always returns BOTH the element tree and a screenshot — ground on both. The modality is chosen at action time by how you address the target: an element ax action (element_index/element_token) or an element px action (x,y). Any value (including the old "som"/"screenshot" aliases) is accepted but has no effect.include_accessibility_tree(boolean, optional): Default true — walk the AT-SPI tree and returnelements+tree_markdownalongside the screenshot. Set false to SKIP the AT-SPI walk entirely and return just the screenshot plus window metadata (window_bounds, app_name, window_title) — the capture-only path for a live window preview / picture-in-picture. Mirrors include_screenshot. Setting BOTH include_accessibility_tree:false AND include_screenshot:false is an error (nothing to return).include_screenshot(boolean, optional): Default true — returns a grounding screenshot alongside the tree. Set false to skip the grab and return tree only (the cheap path for re-indexing before an element ax action).max_depth(integer, optional): Cap on the AT-SPI tree walk depth. Omit for the default (uncapped). Lower for deeply nested apps. range: 1–unboundedmax_dimension(integer, optional): Optional cap on the returned screenshot's long edge, in pixels (aspect ratio preserved) — the cheap path for a small preview. Applied on top of the configured max_image_dimension ceiling; the tighter wins. Omit for the configured default. range: 1–unboundedmax_elements(integer, optional): Cap on total AT-SPI nodes walked. Omit for the default (5 000). Lower for huge web/Electron trees. range: 1–unboundedpid(integer, required)query(string, optional): Optional case-insensitive substring. Projects both tree_markdown and structured elements to matches plus ancestors while preserving original indices. Compare total_element_count with returned_element_count.screenshot_out_file(string, optional): When set, write the PNG to this file path (~ expanded) instead of embedding base64 in the response. The structured output carries screenshot_file_path instead.session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.window_id(integer, required): Native window identifier from list_windows.
{"pid":844,"window_id":10725}get_accessibility_tree#
Return a lightweight snapshot of the desktop: running processes and on-screen visible X11 windows with their bounds and owner pid.
For the full AT-SPI subtree of a single window (with interactive element indices you can click by), use get_window_state instead — this is a fast discovery read.
Arguments: none.
get_desktop_state#
Capture the full display in the desktop action coordinate frame. Use the returned PNG directly as the coordinate source for actions whose target is {kind:"desktop",display_id:"primary"}. No AT-SPI walk.
Arguments:
screenshot_out_file(string, optional): Write PNG here instead of base64.session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.
get_screen_size#
Return the logical size of the main display in points plus its backing scale factor. Agents click in points; Retina displays have scale_factor 2.0. Requires no TCC permissions.
Arguments:
session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.
get_cursor_position#
Return the current mouse cursor position in screen points (origin top-left).
Arguments:
session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.
get_config#
Return current cua-driver-rs configuration.
Arguments: none.
get_recording_state#
Report the current trajectory recorder state: whether recording is enabled, the output directory (when enabled), and the 1-based counter for the next turn folder that will be written. Counter increments on every recorded action tool call and resets to 1 each time recording is (re-)enabled.
Pure read-only.
Arguments: none.
get_agent_cursor_state#
Return the session cursor's theme, semantic playback, position, visibility, and motion.
Arguments:
session(string, required)
{"session":"example"}Action tools#
launch_app#
Launch a Linux app in the background. Provide launch_path (preferred — round-trip the value from list_apps), name (tried as a direct command, then matched against installed .desktop applications, then handed to xdg-open if it is a URL or existing file path), bundle_id (ignored on Linux), or urls (list of URLs to open). Resolution precedence: launch_path > name > bundle_id. Errors when the name resolves to nothing launchable.
Arguments:
additional_arguments(array of string, optional): Extra command-line arguments passed to the launched process.bundle_id(string, optional): Ignored on Linux (macOS/Windows concept).launch_path(string, optional): Round-trip thelaunch_pathreturned bylist_apps— the Exec= command from the .desktop file with XDG field codes already stripped. Highest precedence on Linux; spawned directly via the system shell.name(string, optional): App name or command to launch. Tried as a direct command first, then matched against installed .desktop applications (exact display name, desktop-file id, or Exec basename; else an unambiguous display-name substring).urls(array of string, optional): URLs to open via xdg-open.
kill_app#
Force-terminate a process by pid (kill -9 equivalent on Linux). Use as escalation when the cooperative close path failed to make the process exit. Unsaved state is lost — prefer the cooperative path first.
Arguments:
pid(integer, required): PID of the process to terminate.
{"pid":844}bring_to_front#
Persistently activate a window so subsequent input lands on it. This deliberately breaks the no-foreground contract and is not part of the normal input ladder. For an ordinary background_unavailable response, retry only the refused action with delivery_mode:"foreground"; the input tool performs its own activate, act, and restore sequence. Use bring_to_front only for a focus-proxy surface that must remain foreground across multiple calls, such as a remote desktop session, or when repeated action-scoped activation prevents the remote surface from accepting input. X11: EWMH _NET_ACTIVE_WINDOW activation (the wmctrl -a equivalent, with proper timestamp handling to beat focus-stealing prevention). Wayland: activates through a target-addressable compositor adapter (wlroots foreign-toplevel or the GNOME Shell helper) and refuses when the compositor offers no safe adapter. Matches the macOS / Windows bring_to_front rung.
Arguments:
pid(integer, required)window_id(integer, optional): X11 window id (xid) to activate. If omitted, the first window ofpidis used.
{"pid":844}set_window_frame#
Set one exact top-level window's frame in the desktop-coordinate space reported by list_windows and verify the resulting geometry through an independent readback.
Arguments:
height(number, required): range: 1–unboundedpid(integer, required): range: 1–unboundedsession(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.width(number, required): range: 1–unboundedwindow_id(integer, required): range: 1–unboundedx(number, required)y(number, required)
{"height":1,"pid":844,"width":1,"window_id":10725,"x":100,"y":200}click#
Click against a target pid. Prefer element_index over pixel coordinates — element_index works on backgrounded / hidden windows, surfaces a stable handle, and tells you what you're clicking via the cached AT-SPI element's role + label. Reach for x, y only when the target is a canvas / custom-drawn surface that doesn't appear in the AT-SPI tree.
Provide either (window_id + x/y) or (pid + element_index). Routes via XSendEvent (no focus steal). element_index cache is scoped per (pid, window_id) and is replaced by the next get_window_state of the same window — re-snapshot every turn before clicking.
After a zoom call, pass from_zoom=true to auto-translate zoom-image coords back to full-window space.
button: "left" (default), "right", or "middle". Defaults to left so the field is fully back-compat. X11: routes through XSendEvent ButtonPress/Release with the matching button code. Native Wayland: only left-button is supported via the virtual-pointer protocol — right/middle return an error rather than silently degrading to left. modifier holds ctrl/shift/alt/super for the click on X11. Native Wayland refuses modified pointer clicks until its input protocol can carry keyboard modifier state.
Arguments:
button(string, optional): Mouse button. Default: "left" (legacy back-compat). X11: routed via ButtonPress/Release with the matching evdev code. Native Wayland: only left-button is supported via the virtual-pointer protocol; right/middle return an error.count(integer, optional)cursor_id(string, optional): Optional multi-cursor instance id. Default: 'default'.delivery_mode(string, optional): Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus — Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window — a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. default:"background"element_index(integer, optional): Element index from get_window_state. Requires the matchingsnapshot_idalongside it. Preferelement_token, which carries both values.element_token(string, optional): Opaque per-snapshot element handle fromstructuredContent.elements[].element_token. If element_index, snapshot_id, or window_id are also supplied they must agree. Returns an explicit stale error once a newer snapshot supersedes it.from_zoom(boolean, optional): Set true after a zoom call to auto-translate zoom-image pixel coordinates back to full-window space.modifier(array of string, optional): Modifier keys held during the action: cmd, shift, option/alt, ctrl.pid(integer, optional)scope(string, optional): default:"window"session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.snapshot_id(string, optional): Snapshot handle from get_window_state. Required when targeting by element_index; stale snapshots fail closed.target(window target or desktop target, optional): Exact capture/input target selected independently for each action.
display_id="primary" is the portable desktop target in this release.
Platforms that cannot address another display reject it explicitly rather
than silently changing coordinate spaces.
window_id(integer, optional)x(number, optional)y(number, optional)
double_click#
Double-click at (x,y) or an element_index (AT-SPI bounds) via XSendEvent. No focus steal. Provide either (window_id + x/y) or (pid + element_index). After a zoom call, pass from_zoom=true to auto-translate zoom-image coords.
Arguments:
cursor_id(string, optional): Optional multi-cursor instance id. Default: 'default'.delivery_mode(string, optional): Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus — Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window — a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. default:"background"element_index(integer, optional): Element index from get_window_state. Requires the matchingsnapshot_idalongside it. Preferelement_token, which carries both values.element_token(string, optional): Opaque per-snapshot element handle fromstructuredContent.elements[].element_token. If element_index, snapshot_id, or window_id are also supplied they must agree. Returns an explicit stale error once a newer snapshot supersedes it.from_zoom(boolean, optional): Set true after a zoom call to auto-translate zoom-image pixel coordinates back to full-window space.pid(integer, required)session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.snapshot_id(string, optional): Snapshot handle from get_window_state. Required when targeting by element_index; stale snapshots fail closed.window_id(integer, optional)x(number, optional)y(number, optional)
{"pid":844}right_click#
Right-click at (x,y) or an element_index (AT-SPI bounds) via XSendEvent. No focus steal. Provide either (window_id + x/y) or (pid + element_index). After a zoom call, pass from_zoom=true to auto-translate zoom-image coords.
Arguments:
cursor_id(string, optional): Optional multi-cursor instance id. Default: 'default'.delivery_mode(string, optional): Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus — Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window — a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. default:"background"element_index(integer, optional): Element index from get_window_state. Requires the matchingsnapshot_idalongside it. Preferelement_token, which carries both values.element_token(string, optional): Opaque per-snapshot element handle fromstructuredContent.elements[].element_token. If element_index, snapshot_id, or window_id are also supplied they must agree. Returns an explicit stale error once a newer snapshot supersedes it.from_zoom(boolean, optional): Set true after a zoom call to auto-translate zoom-image pixel coordinates back to full-window space.modifier(array of string, optional): Modifier keys held during the action: cmd, shift, option/alt, ctrl.pid(integer, required)session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.snapshot_id(string, optional): Snapshot handle from get_window_state. Required when targeting by element_index; stale snapshots fail closed.window_id(integer, optional)x(number, optional)y(number, optional)
{"pid":844}drag#
Press-drag-release gesture from (from_x, from_y) to (to_x, to_y) in window-local screenshot pixels via XSendEvent (ButtonPress + MotionNotify × steps + ButtonRelease). duration_ms (default 500), steps (default 20). No focus steal.
Arguments:
button(string, optional): Mouse button. Default "left".cursor_id(string, optional): Optional multi-cursor instance id. Default: 'default'.delivery_mode(string, optional): Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus — Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window — a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. default:"background"duration_ms(integer, optional): Total drag duration. Default: 500. range: 0–10000from_x(number, required)from_y(number, required)from_zoom(boolean, optional)modifier(array of string, optional): Modifier keys held during the action: cmd, shift, option/alt, ctrl.pid(integer, optional)scope(string, optional): default:"window"session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.steps(integer, optional): Intermediate MotionNotify events. Default: 20. range: 1–200target(window target or desktop target, optional): Exact capture/input target selected independently for each action.
display_id="primary" is the portable desktop target in this release.
Platforms that cannot address another display reject it explicitly rather
than silently changing coordinate spaces.
to_x(number, required)to_y(number, required)window_id(integer, optional): Target window XID. Required.
{"from_x":100,"from_y":200,"to_x":100,"to_y":200}type_text#
Type text to a window via XSendEvent (KeyPress/KeyRelease). No focus steal.
Arguments:
delivery_mode(string, optional): Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus — Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window — a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. default:"background"element_index(integer, optional): Element index from get_window_state. Requires the matchingsnapshot_idalongside it. Preferelement_token, which carries both values.element_token(string, optional): Opaque per-snapshot element handle fromstructuredContent.elements[].element_token. If element_index, snapshot_id, or window_id are also supplied they must agree. Returns an explicit stale error once a newer snapshot supersedes it.pid(integer, optional)scope(string, optional): default:"window"session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.snapshot_id(string, optional): Snapshot handle from get_window_state. Required when targeting by element_index; stale snapshots fail closed.target(window target or desktop target, optional): Exact capture/input target selected independently for each action.
display_id="primary" is the portable desktop target in this release.
Platforms that cannot address another display reject it explicitly rather
than silently changing coordinate spaces.
text(string, required)window_id(integer, optional)x(number, optional): Screenshot-pixel X of the field to type into — the element px action form. Pass x,y (no element_index) and the tool pixel-clicks there to establish real renderer focus, then types. Use for Chromium/Electron inputs the AX path can't reach. Read straight off the get_window_state PNG, same convention as click.y(number, optional): Screenshot-pixel Y of the field (see x).
{"text":"hello"}press_key#
Press a key via XSendEvent to a window. No focus steal.
Arguments:
delivery_mode(string, optional): Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus — Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window — a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. default:"background"element_index(integer, optional): Element index from get_window_state. Requires the matchingsnapshot_idalongside it. Preferelement_token, which carries both values.element_token(string, optional): Opaque per-snapshot element handle fromstructuredContent.elements[].element_token. If element_index, snapshot_id, or window_id are also supplied they must agree. Returns an explicit stale error once a newer snapshot supersedes it.key(string, required)modifiers(array of string, optional)pid(integer, optional)scope(string, optional): default:"window"session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.snapshot_id(string, optional): Snapshot handle from get_window_state. Required when targeting by element_index; stale snapshots fail closed.target(window target or desktop target, optional): Exact capture/input target selected independently for each action.
display_id="primary" is the portable desktop target in this release.
Platforms that cannot address another display reject it explicitly rather
than silently changing coordinate spaces.
window_id(integer, optional)x(number, optional): Screenshot-pixel X — the element px action form: pixel-click there to focus, then send the key. Use when the key must go to a Chromium/Electron surface the AX path can't focus. Pass with y, no element_index.y(number, optional): Screenshot-pixel Y (see x).
{"key":"return"}hotkey#
Press a combination of keys simultaneously, e.g. ["ctrl","c"] for Copy. Sent via XSendEvent directly to the target pid; target does NOT need to be frontmost.
Arguments:
delivery_mode(string, optional): Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus — Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window — a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. default:"background"element_index(integer, optional): Element index from get_window_state. Requires the matchingsnapshot_idalongside it. Preferelement_token, which carries both values.element_token(string, optional): Opaque per-snapshot element handle fromstructuredContent.elements[].element_token. If element_index, snapshot_id, or window_id are also supplied they must agree. Returns an explicit stale error once a newer snapshot supersedes it.keys(array of string, required): Modifier(s) + one non-modifier key, e.g. ["ctrl","c"]. items: 2–unboundedpid(integer, optional)scope(string, optional): default:"window"session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.snapshot_id(string, optional): Snapshot handle from get_window_state. Required when targeting by element_index; stale snapshots fail closed.target(window target or desktop target, optional): Exact capture/input target selected independently for each action.
display_id="primary" is the portable desktop target in this release.
Platforms that cannot address another display reject it explicitly rather
than silently changing coordinate spaces.
window_id(integer, optional)x(number, optional): Screenshot-pixel X — the element px action form: pixel-click there to focus, then send the combo (so e.g. Ctrl+V pastes into that field). Pass with y. Use for Chromium/Electron surfaces the background combo can't reach.y(number, optional): Screenshot-pixel Y (see x).
{"keys":["cmd","c"]}set_value#
Set value of an AT-SPI element via SetValue action.
Arguments:
element_index(integer, optional): Element index from get_window_state. Requires the matchingsnapshot_idalongside it. Preferelement_token, which carries both values.element_token(string, optional): Opaque per-snapshot element handle fromstructuredContent.elements[].element_token. If element_index, snapshot_id, or window_id are also supplied they must agree. Returns an explicit stale error once a newer snapshot supersedes it.pid(integer, required)session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.snapshot_id(string, optional): Snapshot handle from get_window_state. Required when targeting by element_index; stale snapshots fail closed.value(string, required)window_id(integer, optional): Required when element_index is used; optional when element_token is supplied (the token carries it).
{"pid":844,"value":"42"}scroll#
Scroll the target pid's focused region via XSendEvent Button4/5. direction required; by defaults to line, amount defaults to 3.
Arguments:
amount(integer, optional): range: 1–50by(string, optional)cursor_id(string, optional): Optional multi-cursor instance id. Default: 'default'.delivery_mode(string, optional): Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus — Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window — a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. default:"background"direction(string, required)element_index(integer, optional): Element index from get_window_state. Requires the matchingsnapshot_idalongside it. Preferelement_token, which carries both values.element_token(string, optional): Opaque per-snapshot element handle fromstructuredContent.elements[].element_token. If element_index, snapshot_id, or window_id are also supplied they must agree. Returns an explicit stale error once a newer snapshot supersedes it.pid(integer, optional)scope(string, optional): default:"window"session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.snapshot_id(string, optional): Snapshot handle from get_window_state. Required when targeting by element_index; stale snapshots fail closed.target(window target or desktop target, optional): Exact capture/input target selected independently for each action.
display_id="primary" is the portable desktop target in this release.
Platforms that cannot address another display reject it explicitly rather
than silently changing coordinate spaces.
window_id(integer, optional)x(number, optional): Window-local screenshot-pixel X of the scroll target. Pass with y and without element_index.y(number, optional): Window-local screenshot-pixel Y of the scroll target. Pass with x and without element_index.
{"direction":"up"}move_cursor#
Move the synthetic agent cursor without changing the user's pointer. Only an explicit scope=desktop request moves the real OS pointer in get_desktop_state coordinates.
Arguments:
cursor_id(string, optional)scope(string, optional): default:"window"session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.target(window target or desktop target, optional): Preferred per-call target. New callers should set this field.x(number, required)y(number, required)
{"x":100,"y":200}zoom#
Capture a cropped JPEG of a window region (x1,y1)–(x2,y2) in screenshot pixels, with 20% padding. Output is at most 500 px wide.
After a zoom, pass from_zoom=true to click/type_text to auto-translate coordinates back to full-window space.
Arguments:
pid(integer, optional): Target pid — required for from_zoom click/type translation.window_id(integer, required)x1(number, required)x2(number, required)y1(number, required)y2(number, required)
{"window_id":10725,"x1":100,"x2":100,"y1":200,"y2":200}Browser tools#
page#
Legacy browser compatibility tool. Prefer get_browser_state and the typed browser_* tools for exact targeting, endpoint ownership, and consent. Read-only get_text and query_dom remain available by default. Mutating actions require the daemon operator to set CUA_DRIVER_ENABLE_LEGACY_PAGE_MUTATIONS=1 before daemon startup (restart the daemon after changing it); this escape hatch does not provide the typed browser surface's exact binding or existing-profile grant guarantees. Supports Chrome, Brave, Edge, Safari (via AppleScript on macOS), Electron apps (via CDP), Chromium/Firefox on Windows (via UIA for read; CDP for execute_javascript when --remote-debugging-port is set), and WKWebView/Tauri/AT-SPI fallbacks.
Actions:
- execute_javascript: Run JS and return the result.
- get_text: Extract visible text from the page.
- query_dom: Find elements matching a CSS selector.
- click_element: Click a CSS-selected element AND animate the agent cursor to its on-screen center first (so the user sees what the agent is doing). Prefer over
execute_javascript('el.click()')whenever you want visible cursor feedback. - insert_text: Insert
textat whatever currently holds DOM focus in one native operation (CDP Input.insertText) — no synthesized key events, but more durable than a one-shot execute_javascript write since rich-text editors already have to treat it like an IME commit. Try this before type_keystrokes on a contenteditable that discarded an execute_javascript write. Click/focus the target field first. - type_keystrokes: Type
textvia real per-character keystroke events into whatever currently holds DOM focus. Slower than insert_text but the most durable rung — use it when insert_text also gets discarded, or the editor's own keydown/keyup handlers need to see real keys. Click/focus the target field first. - enable_javascript_apple_events: macOS-only — patch the browser's Preferences to allow JS from Apple Events (Chrome/Brave/Edge, requires user confirmation and a browser restart).
Arguments:
action(string, required): Action to perform.attributes(array of string, optional): Element attributes to include in query_dom results.bundle_id(string, optional): Bundle ID of the browser. Required for enable_javascript_apple_events (macOS only).cdp_port(integer, optional): Optional, for execute_javascript/insert_text/type_keystrokes: use this exact CDP port instead of auto-discovering one from pid. Needed when the port was opened via the browser's own remote-debugging toggle rather than a launch-time flag, since that path may not answer the auto-discovery probe. range: 1–65535css_selector(string, optional): CSS selector for query_dom (e.g. 'a', 'button', 'input', 'h1'-'h6', 'p', 'img', 'select', '*').javascript(string, optional): JavaScript to execute. Required for execute_javascript.pid(integer, optional): Target process ID.selector(string, optional): CSS selector for click_element (e.g. 'button.submit', '#login a').target_url_contains(string, optional): Optional, for execute_javascript/insert_text/type_keystrokes: require exactly one browser tab whose URL contains this substring. Use this on a multi-tab browser — there's no built-in link between window_id and which tab a CDP call reaches.text(string, optional): Text to insert or type. Required for insert_text and type_keystrokes. The target field must already have DOM focus (click/focus it first).user_has_confirmed_enabling(boolean, optional): Must be true to proceed with enable_javascript_apple_events. This will quit and relaunch the browser.window_id(integer, optional): Target window ID from list_windows.
{"action":"execute_javascript"}Clipboard tools#
clipboard_read#
List available system clipboard types and optionally return privacy-sensitive plain text. Clipboard content is never retained in telemetry.
Arguments:
include_text(boolean, optional): Return plain-text clipboard content in addition to the available types. Clipboard content is privacy-sensitive and is never retained in telemetry. default:falsesession(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.
clipboard_write#
Replace the system clipboard with exactly one value: plain text, an image from an absolute local path, or a file URL from an absolute local path. Returns the available types for read-back before paste.
Arguments:
file_path(string, optional): Absolute path to a local file to place on the clipboard as a file URL.image_path(string, optional): Absolute path to a local image to place on the clipboard.session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.text(string, optional): Plain text to place on the clipboard.
Recording tools#
start_recording#
Start trajectory recording. Every subsequent action-tool invocation (click, right_click, scroll, type_text, press_key, hotkey, set_value) writes a turn folder under output_dir:
before_state.json/after_state.json— application AX/UIA/AT-SPI state immediately before and after the action.before.png/after.png— target-window screenshots immediately before and after the action.evidence.json— capture status and a stable classification when an expected artifact could not be captured.app_state.json— post-action AX/UIA snapshot for the target pid.screenshot.png— compatibility alias ofafter.png.action.json— tool name, full input arguments, result summary, result-error flag, pid, click point (when applicable), ISO-8601 timestamp.click.png— for dispatched click-family actions only,before.pngwith a red marker at the click point. A call refused before target resolution is explicitly not applicable instead.
Turn folders are named turn-00001/, turn-00002/, etc. Turn numbering restarts at 1 each time recording is (re-)started.
Video is off by default. Pass record_video: true to also capture the main display to <output_dir>/recording.mp4 (H.264 / 30 fps) for the lifetime of the session. The recording is torn down automatically when the MCP client disconnects.
macOS uses native ScreenCaptureKit (daemon-owned SCStream + SCRecordingOutput) so video inherits the daemon's Screen Recording grant — no extra TCC prompt, no ffmpeg subprocess. Requires macOS 15.0+.
Windows + Linux use an ffmpeg subprocess (gdigrab / x11grab + libx264). Requires ffmpeg on PATH (winget install Gyan.FFmpeg / apt install ffmpeg); when ffmpeg is missing or fails on startup the per-turn capture (screenshots + action.json) still runs and the session's last_error field carries the diagnostic.
State persists for the life of the daemon; a restart resets to disabled with no on-disk state. Call stop_recording to disable + finalize the mp4.
Arguments:
output_dir(string, required): Absolute or ~-rooted directory where turn folders and (when enabled) the video file are written.record_video(boolean, optional): Capture the main display to <output_dir>/recording.mp4. Default: false. Set to true to also capture the main display to recording.mp4 (otherwise only the per-turn screenshots + JSON are recorded). On macOS this uses native ScreenCaptureKit (no extra TCC prompt, macOS 15.0+); on Windows + Linux it requires ffmpeg on PATH.
{"output_dir":"~/cua-trajectories/demo1"}stop_recording#
Stop trajectory recording. Disables further per-turn capture and, when video was enabled, gracefully terminates the ffmpeg subprocess so the mp4's moov atom is finalized (the file is playable). Calling stop on an already-stopped session is a no-op. The response carries last_video_path pointing at the finalized mp4 (when video was on).
A manual stop_recording is unconditional — it stops whatever recording is active regardless of which session started it. Ownership-scoped teardown (so one client disconnecting can't stop a recording a later client started) is handled by the registry's session_end lifecycle hook, not by this tool.
Arguments: none.
replay_trajectory#
Replay a recorded trajectory by re-invoking every turn's tool call in lexical order. dir must point at a directory previously written by start_recording. Each turn-NNNNN/ is parsed for action.json, and the recorded tool is called with its recorded arguments via the same dispatch path an MCP / CLI call uses.
Caveats:
- Element-indexed actions (
click({pid, element_index})etc.) will fail because element indices are per-snapshot and don't survive across sessions. Pixel clicks (click({pid, x, y})) and all keyboard tools replay cleanly. Failures are reported but don't stop replay unlessstop_on_erroris true. get_window_stateand other read-only tools are NOT currently recorded, so replays do not re-populate the per-(pid, window_id) element cache.- If recording is ENABLED while replay runs, the replay itself is recorded into the currently configured output directory. That's deliberate: recording a replay against a new build and diffing the two trajectories is the regression-test workflow.
Arguments:
delay_ms(integer, optional): Milliseconds to sleep between turns, for human-observable pacing. Default 500. range: 0–10000dir(string, required): Trajectory directory previously written bystart_recording. Absolute or ~-rooted.stop_on_error(boolean, optional): Stop replay on the first tool-call error. Default true — set false to best-effort through the full trajectory.
{"dir":"~/cua-trajectories/demo1"}Configuration tools#
set_config#
Update cua-driver-rs configuration. capture_mode / max_image_dimension take effect immediately.
Two input shapes (both accepted, matching Windows/Swift):
- {key, value} (preferred):
{"key": "max_image_dimension", "value": 800}— single leaf write. - Legacy per-field:
{"capture_mode": "som", "max_image_dimension": 0}.
The experimental_pip keys persist to ~/.cua-driver/config.json and apply on next daemon restart (the PiP backend is initialised once at startup; Linux ships only the trait stub today — see issue #1729).
Arguments:
capture_mode(string, optional): Legacy per-field shape. Default capture mode for get_window_state. ("som"/"screenshot" still decode as deprecated aliases.)experimental_pip(boolean, optional): Enable the experimental PiP preview window (applies next restart; Linux backend stubbed).experimental_pip_geometry(string, optional): PiP window size + optional position inWxHorWxH+X+Yform.key(string, optional): Name of a single config field to write ({key, value} shape). Pair withvalue.max_image_dimension(integer, optional): Legacy per-field shape. Max dimension for screenshot resizing (0 = no limit).value(unknown, optional): New value forkey. JSON type depends on the key.
start_session#
Optionally create or return a lifecycle session before acting. For multi-call work, prefer a short public session label and repeat it on every call that accepts it; an omitted value uses the authenticated transport lease's implicit session instead. This tool is optional because an ordinary action can create or reuse a named run directly. Use it to set the initial cursor theme before acting or to revive a public name after it has ended; ordinary actions never revive ended names. capture_scope is deprecated compatibility input; new callers select window or desktop modality per action. Idempotent.
Arguments:
capture_scope(string, optional): Deprecated compatibility policy. New callers select window or desktop modality on each action instead of storing it on the session.cursor_theme(object or null, optional): Optional initial cursor theme. The host applies it before the cursor is first made visible, avoiding a flash of the default theme.session(string, optional): Optional stable public label for this run (e.g. "research-run-1"). When omitted, the authenticated transport lease's implicit session is created or returned.
end_session#
End one visible lifecycle session and run its cursor, recording, configuration, and other cleanup hooks exactly once. Omit session to end the authenticated transport's implicit session. Idempotent.
Arguments:
session(string, optional): Optional public label to end. When omitted, end the caller's attached implicit session.
set_agent_cursor_enabled#
Show or hide the agent cursor owned by a session.
Arguments:
enabled(boolean, required)session(string, required)
{"enabled":false,"session":"example"}set_agent_cursor_motion#
Configure only movement physics and visibility timing for a session cursor.
Arguments:
arc_flow(number or null, optional)arc_size(number or null, optional)dwell_after_click_ms(number or null, optional)end_handle(number or null, optional)glide_duration_ms(number or null, optional)idle_hide_ms(number or null, optional)session(string, required)spring(number or null, optional)start_handle(number or null, optional)turn_radius(number or null, optional)
{"session":"example"}Maintenance tools#
check_permissions#
Check required permissions for cua-driver-rs on Linux.
Arguments: none.
health_report#
Single-call end-to-end driver diagnostics. Designed to let downstream consumers ship one stable call instead of stitching together check_permissions, doctor, version, bundle attribution, and platform capability status. On macOS, prompt-capable direct capture is deliberately skipped; use cua-driver permissions grant to verify it explicitly. cua-driver owns the health model; consumers stay thin.
Input — all optional:
{
"include": ["<check_name>", ...], // run only these
"skip": ["<check_name>", ...] // skip these
}
If both are given, include wins.
Canonical check names: macOS : binary_version, platform_supported, session_active, bundle_identity, tcc_accessibility, tcc_screen_recording, ax_capability, screen_capture_capability Windows: binary_version, platform_supported, session_active, ax_capability (via UIA), screen_capture_capability (via DXGI) Linux : binary_version, platform_supported, session_active, ax_capability (via AT-SPI), screen_capture_capability (via X11)
Output — stable contract, schema_version="1": { "schema_version": "1", "platform": "darwin" | "win32" | "linux", "driver_version": "<semver>", "overall": "ok" | "degraded" | "failed", "checks": [ { "name": "<one of the canonical names above>", "status": "pass" | "fail" | "skip", "message": "<one-line summary, always present>", "hint": "<remediation step, present when status=fail>", "data": { /* check-specific structured fields */ } }, ... ] }
overall rules:
ok— every non-skipped check passesdegraded— at least one non-core check fails (binary is still usable)failed— any core check fails (binary_version, platform_supported, session_active)
Stability: schema_version="1" is the contract. Future breaking changes will be "2". Adding new check names under the same schema_version is non-breaking; consumers must tolerate unknown check names.
Arguments:
include(array of string, optional): Only run these checks (canonical names). Wins overskip.skip(array of string, optional): Skip these checks (canonical names). Ignored whenincludeis set.
check_for_update#
Check the saved stable/nightly Cua Driver channel for a release on GitHub. Returns current and selected channels, current and latest versions, an update_available boolean, the install one-liner, and the release notes URL. Read-only — never installs. Mirror of cua-driver check-update --json.
Arguments: none.
install_ffmpeg#
Install the ffmpeg binary used by start_recording's video capture (Linux/Windows; macOS records natively and needs no ffmpeg). Two-step and confirmed: called without confirm it only REPORTS the exact install command for this platform's package manager; pass confirm: true to actually run it. No-op if ffmpeg is already on PATH. ffmpeg is run as a separate process, never linked into the driver.
Arguments:
confirm(boolean, optional): Run the install command. Without it, only the planned command is reported.
Other tools#
verify_state#
Deterministically verify bounded predicates against one exact window. The driver evaluates structured window/accessibility state and may return the final screenshot as uninterpreted visual evidence for a multimodal caller. Predicate results are satisfied, unsatisfied, or unknown; unknown never implies success. Accessibility projections are conservative: absence remains unknown unless the observed search domain is proven exhaustive.
Arguments:
expect(array of object, required): One to eight predicates, combined with logical AND. items: 1–8include_screenshot(boolean or null, optional): Return the final window screenshot as image content for a multimodal caller. The driver does not interpret that image.pid(integer, required): Exact process whose window may be observed. range: 1–unboundedsession(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. This field never selects capture modality or authorization.stable_samples(integer, optional): Consecutive satisfied samples required before returning success. default:2; range: 1–5timeout_ms(integer, optional): Bounded wait. Zero performs one sample. default:5000; range: 0–10000window_id(integer, required): Exact native window identifier.
{"expect":[{"window":{"exists":true}}],"pid":844,"window_id":10725}invoke_menu#
Resolve an exact application-menu path one live native level at a time and invoke its final item through accessibility APIs. Missing, ambiguous, disabled, or structurally mismatched segments fail closed; this tool never falls back to pixels.
Arguments:
path(array of string, required): items: 1–16pid(integer, required): range: 1–unboundedsession(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session.window_id(integer, required): range: 1–unbounded
{"path":["example"],"pid":844,"window_id":10725}mouse_button_down#
Press and hold a mouse button at (x,y) via background X11 delivery. Does not release the button; pair with mouse_drag / mouse_button_up. Returns the current held-button state.
Arguments:
button(string, optional): Mouse button. Default "left".cursor_id(string, optional): Optional multi-cursor instance id. Default: 'default'.from_zoom(boolean, optional): Set true after a zoom call to auto-translate zoom-image pixel coordinates back to full-window space.pid(integer, required)session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. When both are present, session takes precedence over cursor_id.window_id(integer, required)x(number, required)y(number, required)
{"pid":844,"window_id":10725,"x":100,"y":200}mouse_drag#
Move a previously-held mouse button to a new point via background X11 delivery. Requires an active mouse_button_down state; does not release the button. Returns the updated held-button state.
Arguments:
cursor_id(string, optional): Optional multi-cursor instance id. Default: 'default'.duration_ms(integer, optional): Total drag duration. Default: 500. range: 0–10000from_zoom(boolean, optional): Set true after a zoom call to auto-translate zoom-image pixel coordinates back to full-window space.pid(integer, optional)session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. When both are present, session takes precedence over cursor_id.steps(integer, optional): Intermediate MotionNotify events. Default: 20. range: 1–200window_id(integer, optional)x(number, required)y(number, required)
{"x":100,"y":200}mouse_button_up#
Release a previously-held mouse button via background X11 delivery. If x/y are omitted, releases at the last held position. Returns the current held-button state.
Arguments:
cursor_id(string, optional): Optional multi-cursor instance id. Default: 'default'.from_zoom(boolean, optional): Set true after a zoom call to auto-translate zoom-image pixel coordinates back to full-window space.pid(integer, optional)session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. When both are present, session takes precedence over cursor_id.window_id(integer, optional)x(number, optional)y(number, optional)
parallel_mouse_drag#
Run multiple mouse drag gestures concurrently via Linux MPX/XI2 virtual master pointers. Each drag item runs on its own session-scoped master pointer (true same-window concurrent draws on X11). Each item presses once, glides continuously through its whole path, and releases once — one smooth held drag, not a chain of clicks. A path is given either as a straight segment (from_x/from_y → to_x/to_y) or as a function fn = y(x) sampled over [x_from, x_to] in window-local pixels (e.g. fn:"x" is a diagonal, fn:"300+120*sin(x/40)" a sine wave). Functions support + - * / ^, sin/cos/tan, sqrt, abs, exp, ln, pi, e.
Arguments:
drags(array of object, required): items: 2–unbounded
{"drags":["example"]}set_agent_cursor_theme#
Select an already-installed cursor theme for a session.
Arguments:
reduced_motion(string, optional): default:"auto"session(string, required)theme_id(string, required)
{"session":"example","theme_id":"example"}get_browser_state#
Read-only browser inspection. Mode 1 (bind): pass pid + window_id of a native browser window to classify it, correlate it to a CDP target (exact-or-refuse), and mint a session-scoped target id plus tab ids. Mode 2 (snapshot): pass target_id + tab_id. The dom_refs_v1 compatibility format returns composed DOM refs. semantic_v2 joins accessibility, DOM, layout, and viewport state; ranks visible content before retained/offscreen state; and returns a semantic outline, typed action refs, content refs, scoped reads, and opaque continuation. Never performs setup — a missing endpoint is a structured browser_requires_setup refusal pointing at browser_prepare.
Arguments:
continuation(string, optional): Opaque continuation minted by an earlier semantic_v2 response.include_screenshot(boolean, optional): Capture the exact tab viewport as PNG through CDP without selecting the tab or foregrounding its native window. The request refuses if capture cannot be completed. default:falsepid(integer, optional): Native browser process id (bind mode).query(string, optional): Read-only semantic match over role, accessible name, and visible text.scope_ref(string, optional): Current semantic/content ref whose subtree should be observed.session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. Browser targets, tabs, and refs belong to the resolved lifecycle session.snapshot_format(string, optional): Versioned snapshot contract. dom_refs_v1 remains the compatibility default.tab_id(string, optional): Opaque tab id from get_browser_state (session-scoped).target_id(string, optional): Opaque browser target id minted by get_browser_state (session-scoped; never a CDP id).window_id(integer, optional): Native window id owned by pid (bind mode).
browser_prepare#
Explicitly prepare an owned DevTools endpoint for a browser. pid is required for an existing process or existing-profile attachment, and optional only for allow_launch=true with an isolated profile. Existing endpoints are detected without side effects. Acting setup for an isolated profile follows the runtime permission mode and optional capability manifest. It requires allow_launch=true, launches a separate browser, and never copies, modifies, or terminates the requested user profile. Without pid, only a platform-attested system Chrome/Edge installation (or a root-owned package payload on Linux) is eligible; redirects and user-controlled locations fail closed. Existing-profile attachment is explicit and follows the runtime's immutable permission mode: standard requires an explicit --grant existing-profile launch grant or an embedding authorization host, bounded requires a launch-approved exact resource manifest, and unrestricted requires explicit trusted startup risk acceptance. Ordinary MCP transport approval never proves profile authorization. On proven platforms, an authorized request also permits one bounded exact-window setup: open the recognized browser product's fixed remote-debugging page, toggle its uniquely matched per-instance checkbox, prove the PID-owned loopback endpoint, and close the temporary tab. Every visible effect is reported; ambiguity is refused.
Arguments:
allow_launch(boolean, optional): Allow a separate driver-owned isolated Chromium process to be launched (default false).pid(integer, optional): Browser process id to prepare. Required except for a driver-owned isolated_new/isolated_named launch with allow_launch=true.profile(object, optional)session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. Browser targets, tabs, and refs belong to the resolved lifecycle session.strategy(object, optional)window_id(integer, optional): Exact native window approval anchor; required for strategy.kind=existing_profile.
browser_navigate#
Navigate one tab of an exactly-bound browser target to a new URL (http/https/about only). Refused for heuristic bindings. Navigation invalidates all p<snapshot>:<index> refs for the tab.
Arguments:
session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. Browser targets, tabs, and refs belong to the resolved lifecycle session.tab_id(string, required): Opaque tab id from get_browser_state (session-scoped).target_id(string, required): Opaque browser target id minted by get_browser_state (session-scoped; never a CDP id).url(string, required): Destination URL (http:, https:, or about:).
{"tab_id":"example","target_id":"example","url":"example"}browser_click#
Click a page element (by ref) or viewport coordinates in an exactly-bound tab. Default route is trusted hardware-like input (Input.dispatchMouseEvent), and refuses where that route cannot preserve standalone-browser background posture. input_route="dom_event" (synthetic el.click(), ref required) is used only when explicitly requested; it proves dispatch, not control activation, because trust-gated controls may ignore synthetic events. Refused for heuristic bindings.
Arguments:
input_route(string, optional): "trusted" (default): Input.dispatchMouseEvent. It refuses rather than foregrounding a standalone browser. "dom_event": synthetic full-background DOM click, only when explicitly requested. Dispatch does not prove the control activated; refresh page state and verify the expected postcondition.ref(string, optional): Page element ref in the p<snapshot>:<index> namespace from get_browser_state. Refs are invalidated by navigation and by newer snapshots of the same tab.session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. Browser targets, tabs, and refs belong to the resolved lifecycle session.tab_id(string, required): Opaque tab id from get_browser_state (session-scoped).target_id(string, required): Opaque browser target id minted by get_browser_state (session-scoped; never a CDP id).x(number, optional): Viewport x (CSS px) — alternative to ref.y(number, optional): Viewport y (CSS px) — alternative to ref.
{"tab_id":"example","target_id":"example"}browser_type#
Type text into an exactly-bound tab via the Input domain. mode="insert_text" (default) uses Input.insertText; mode="keystrokes" dispatches per-character key events. Both insert at the caret, so typing into a field that already holds text appends to it; pass replace=true to set the field instead, or to clear it by typing an empty string. Pass a ref to an editable element from the latest snapshot. A ref is required; heuristic bindings are refused.
Arguments:
mode(string, optional): insert_text (default): bulk Input.insertText. keystrokes: per-character Input.dispatchKeyEvent.ref(string, required): Page element ref in the p<snapshot>:<index> namespace from get_browser_state. Refs are invalidated by navigation and by newer snapshots of the same tab.replace(boolean, optional): false (default): insert at the caret, appending to whatever the field already holds. true: select the element's whole content first so the text replaces it — with an empty text this clears the field. Replacement goes through the selection, so beforeinput/input still fire and framework state stays consistent.session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. Browser targets, tabs, and refs belong to the resolved lifecycle session.tab_id(string, required): Opaque tab id from get_browser_state (session-scoped).target_id(string, required): Opaque browser target id minted by get_browser_state (session-scoped; never a CDP id).text(string, required): Text to type.
{"ref":"example","tab_id":"example","target_id":"example","text":"hello"}browser_dialog#
Inspect or resolve a page-owned JavaScript alert, confirm, prompt, or beforeunload dialog on one exactly-bound tab. This never handles browser permission UI, extension UI, native dialogs, or file pickers. Inspect returns an opaque dialog_id; accept/dismiss require that exact current id. Resolution defaults to background delivery; Linux callers must explicitly request foreground delivery because Chromium's native modal cannot be resolved there without changing foreground posture.
Arguments:
action(string, required)delivery_mode(string, optional): Requested foreground posture for accept/dismiss. Linux Chromium requires foreground; inspect is read-only. default:"background"dialog_id(string, optional): Opaque current dialog generation returned by action=inspect.prompt_text(string, optional): Sensitive response text, valid only when accepting a prompt dialog.session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. Browser targets, tabs, and refs belong to the resolved lifecycle session.tab_id(string, required): Opaque tab id from get_browser_state (session-scoped).target_id(string, required): Opaque browser target id minted by get_browser_state (session-scoped; never a CDP id).
{"action":"inspect","tab_id":"example","target_id":"example"}browser_set_input_files#
Assign one or more explicit absolute local files to an exact live <input type=file> ref through CDP. This bypasses native file pickers, rejects symlinks and non-regular files, and never returns local paths.
Arguments:
files(array of string, required): items: 1–32ref(string, required): Page element ref in the p<snapshot>:<index> namespace from get_browser_state. Refs are invalidated by navigation and by newer snapshots of the same tab.session(string, optional): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. Browser targets, tabs, and refs belong to the resolved lifecycle session.tab_id(string, required): Opaque tab id from get_browser_state (session-scoped).target_id(string, required): Opaque browser target id minted by get_browser_state (session-scoped; never a CDP id).
{"files":["example"],"ref":"example","tab_id":"example","target_id":"example"}browser_download#
Trigger one download through an exact live browser ref and save it inside an explicitly approved directory. Requires MCP-host destructive-tool approval, refuses ambiguous or stale capabilities, and never returns the source URL, filename, or destination path.
Arguments:
destination_root(string, required): Absolute, existing, canonical directory approved to receive the download.ref(string, required): Live page ref whose activation initiates the download.session(string, required): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. This tool requires the label that owns its browser target, tab, and refs.tab_id(string, required): Opaque exact tab id from get_browser_state.target_id(string, required): Opaque exact browser target id from get_browser_state.
{"destination_root":"example","ref":"example","session":"example","tab_id":"example","target_id":"example"}browser_pointer#
Perform hover, right-click, double-click, scroll, or drag in an exactly-bound browser tab. Semantic refs must declare pointer for hover, right-click, double-click, and drag; scroll accepts a scroll or pointer capability. The trusted route uses CDP Input events and refuses if standalone background posture cannot be preserved. The explicit dom_event route requires a page ref and synthesizes full-background DOM events. Never activates or brings a tab to the foreground.
Arguments:
action(string, required)delta_x(number, optional): Horizontal scroll delta in CSS pixels.delta_y(number, optional): Vertical scroll delta in CSS pixels.destination_ref(string, optional): Drag destination page ref in the exact same frame.input_route(string, optional): default:"trusted"ref(string, optional): Origin page ref. Alternative to x/y.session(string, required): For multi-call work, prefer a short public session label and repeat it on every call that accepts it. This tool requires the label that owns its browser target, tab, and refs.tab_id(string, required): Opaque tab id minted by get_browser_state.target_id(string, required): Opaque target id minted by get_browser_state.to_x(number, optional): Drag destination viewport x in CSS pixels.to_y(number, optional): Drag destination viewport y in CSS pixels.x(number, optional): Origin viewport x in CSS pixels.y(number, optional): Origin viewport y in CSS pixels.
{"action":"hover","session":"example","tab_id":"example","target_id":"example"}escalate_session#
Deprecated compatibility tool for legacy capture-scope sessions. New callers select window or desktop modality on each action. No deescalate_session tool exists.
Arguments:
detail(string, optional): Optional bounded diagnostic detail. Never use secrets or page content.reason(string, required)session(string, required)
{"reason":"ax_tree_pixel_mismatch","session":"example"}get_session#
Read content-free lifecycle, cursor, recording, and idle status for one session visible to this authenticated transport. Omit session to inspect its implicit session.
Arguments:
session(string, optional): Optional public label. When omitted, inspect the caller's attached implicit session.
list_sessions#
List content-free lifecycle summaries attached to this authenticated transport lease. It does not enumerate other callers' sessions.
Arguments:
cursor(string, optional): Opaque continuation cursor returned by a previous call.limit(integer or null, optional): Maximum number of content-free summaries to return (default 50, max 100). Ordinary agent transports are scoped to their own lease. range: 0–unbounded
get_session_state#
Deprecated compatibility alias that reads a live legacy session's capture policy. Use get_session for lifecycle state.
Arguments:
session(string, optional): Optional public label. When omitted, inspect the caller's attached implicit session.