Tool reference

44 tools, generated from the compiled schemas of macos-mcp v0.2.0. Every tool returns structured JSON in structuredContent; every failure carries a stable code and what an agent needs to recover — never prose alone. Tool names and argument shapes are public API: removals need a deprecation notice one minor version ahead (the ledger).

Toolsets and what they need

24 tools are on by default (ui, screen, system, files). The rest are gated to keep the default surface lean; switch them on with enable_toolset at runtime or --toolsets at launch. Grants attach to the app hosting the agent (your terminal or MCP client), and macos-mcp doctor names what is missing with the exact System Settings deep-link.

ToolsetToolsDefaultmacOS permission
ui
Accessibility-first UI automation
ui_snapshot, ui_click, ui_type, ui_scroll, ui_drag, ui_shortcut, app_launch, app_quit, window_manage, waitonAccessibility
screen
Screenshots, OCR and find-on-screen
screenshot, ocr, screen_findonScreen Recording
system
Shell, AppleScript recipes, clipboard, open, Spotlight
shell, applescript_recipes, applescript_run_recipe, applescript_eval, clipboard_read, clipboard_write, open, spotlight_searchonnone (Automation per app for recipes)
files
Trash and Finder tags — never a hard delete
trash, tagsonnone
apps
Calendar, Reminders, Contacts
calendar_list, calendar_search, reminders_list, reminders_search, contacts_searchgatedCalendars / Reminders / Contacts (prompted on first use)
intents
Shortcuts and App Intents
intents_list, intents_rungatednone (Shortcuts may prompt per shortcut)
web
Browser control: Apple's Safari MCP where available, AppleScript fallback elsewhere
web_navigate, web_current_tab, web_page_text, web_tabs, web_select_tabgatedAutomation for Safari (fallback) or Safari remote automation (Apple MCP)
ios
Control a mirrored iPhone in device points — read, tap, swipe, type, navigate
iphone_status, iphone_screenshot, iphone_find, iphone_tap, iphone_swipe, iphone_type, iphone_key, iphone_resumegatedScreen Recording + Accessibility
server
Meta
enable_toolsetonnone

The web toolset is either/or: where Apple's Safari MCP server mounts (Safari 27 / Safari Technology Preview with remote automation on) its 17 web_* tools are re-exported verbatim; otherwise the five AppleScript-backed tools below serve the same slot and every result says method: "fallback".

How the ui_* tools fit together

ui_snapshot maps an app's accessibility tree and stamps every element with an id of the form <pid>.<hash>. The other ui_* tools take that id — the pid rides in the handle, so they need no separate app argument, and an id from a relaunched app resolves to nothing rather than to the wrong element.

// 1. map the app
{"name": "ui_snapshot", "arguments": {"app": "TextEdit"}}
// → {"app": {...}, "node_count": 14, "root": {"children": [
//      {"id": "51728.965cab4f8d4d", "role": "AXButton",
//       "title": "Save", "actions": ["AXPress"], "frame": {...}}, ...]}}

// 2. act on what you found
{"name": "ui_click", "arguments": {"id": "51728.965cab4f8d4d"}}
// → {"ok": true, "method": "ax", "verified": false,
//    "element": {"id": "51728.965cab4f8d4d", "role": "AXButton", "title": "Save"}}

method says which path ran: ax used the element's own accessibility action — no window raised, no pointer moved, no focus stolen — and cgevent fell back to synthetic input. verified is true when the engine re-read the UI and saw the change it intended; a click has no general "did it land" signal, typing does.

ui — Accessibility-first UI automation (default)

ui_snapshot read-only

Accessibility snapshot of an app's UI: a tree of {id, role, title, value, frame, actions} with stable element ids the other ui_* tools take. Secure text fields are marked `secure` and their value is never read. Caps keep the payload small and every clip is reported in `truncated`. An app with no accessibility support returns a root with no children — use `screenshot` for those.

ArgumentTypeRequiredDefaultDescription
appstringnoBundle identifier or app name. Defaults to the frontmost app.
include_menu_barbooleannofalseInclude the app's menu bar, which is large and rarely needed.
max_childrenintegerno100Per-element sibling cap.
max_depthintegerno12Deepest level walked below the app element.
max_nodesintegerno2000Total node budget for the snapshot.
max_stringintegerno512Character cap for each title/value/help string.
pidintegernoTarget by process id instead of name.

ui_click

Click an element by id. Uses the element's own accessibility action where it has one — which works without raising the window, moving the pointer, or stealing focus — and falls back to a synthetic click at the element's centre. The result reports which path ran as `method`. The synthetic fallback clicks whatever is topmost on screen, so a target covered by another app's window is refused with the covering app's name — raise the target window first (window_manage raise).

ArgumentTypeRequiredDefaultDescription
idstringyesElement id from a previous ui_snapshot (it carries the app's pid, so no separate app argument is needed). Re-snapshot if the UI has changed since.
buttonleft | rightno"left"Right-click prefers the element's AXShowMenu action.
countintegerno12 for a double-click. Anything above 1 uses synthetic events.

ui_type

Type text into an element by id. Sets the value through accessibility where the app accepts it, otherwise sends keystrokes, and in both cases reads the field back to confirm — a write that reported success but changed nothing comes back as an `action_unverified` error, never as success. Refuses secure text fields.

ArgumentTypeRequiredDefaultDescription
idstringyesElement id from a previous ui_snapshot (it carries the app's pid, so no separate app argument is needed). Re-snapshot if the UI has changed since.
textstringyesText to enter.
modereplace | appendno"replace"replace swaps the whole value; append inserts at the caret.

ui_scroll

Scroll an element by id. `page` uses the container's own accessibility scroll actions, which work without raising the window or moving the pointer; `reveal` brings an element into view; `pixels` sends wheel events. A request that would move nothing comes back as an error rather than a no-op reported as success.

ArgumentTypeRequiredDefaultDescription
idstringyesElement id from a previous ui_snapshot (it carries the app's pid, so no separate app argument is needed). Re-snapshot if the UI has changed since.
dxintegerno0Horizontal pixels; positive scrolls right.
dyintegerno0Vertical pixels; positive scrolls up, as on a trackpad.
modepixels | page | revealno"pixels"pixels sends wheel events; page performs the element's own scroll-by-page actions in the direction of dx/dy; reveal scrolls this element into view and ignores dx/dy.

ui_drag

Drag from one place to another with the pointer held down — reorder rows, move sliders, drop a file on a target. Each end is an element id (its centre) or a point in global screen coordinates (screen_find centers paste straight in). Drag effects are app-specific, so this is one of the few verbs whose result is honestly `verified: false`; re-snapshot to see what changed. A zero-length drag is refused.

ArgumentTypeRequiredDefaultDescription
buttonleft | rightno"left"
duration_msintegerno300Gesture duration; apps ignore teleporting drags.
fromobjectnoStart point in global screen coordinates (top-left origin).
from_idstringnoElement id from a previous ui_snapshot (it carries the app's pid, so no separate app argument is needed). Re-snapshot if the UI has changed since.
toobjectnoEnd point in global screen coordinates.
to_idstringnoElement id from a previous ui_snapshot (it carries the app's pid, so no separate app argument is needed). Re-snapshot if the UI has changed since.

ui_shortcut

Send a keyboard shortcut such as "cmd+s", "cmd+shift+p" or "f5". Key positions are resolved against the active keyboard layout, so this is correct on non-US layouts. Goes to the frontmost app unless an app or pid is given.

ArgumentTypeRequiredDefaultDescription
keysstringyesModifiers plus one key, '+'-separated: cmd, shift, alt/opt, ctrl, fn. The key is a single character or a name like return, tab, escape, space, delete, left, f1.
appstringnoBundle identifier or app name to receive the keys.
pidintegernoTarget by process id instead of name.

app_launch

Launch an app by name, bundle id, or absolute path, and wait until it reports ready. Launching an app that is already running re-opens or activates it rather than starting a second copy — the result says which happened. Does not steal focus unless `activate` is set: most automation here works without the app being frontmost.

ArgumentTypeRequiredDefaultDescription
appstringyesApp name ('TextEdit'), bundle id ('com.apple.TextEdit'), or absolute path to a .app bundle.
activatebooleannofalseBring the app to the front after launch.
wait_msintegerno15000How long to wait for the app to finish launching.

app_quit destructive — needs confirmation

Quit an app and confirm it actually exited. Graceful by default, which lets the app show its own save dialogs — if it is still running when the wait runs out, that comes back as an error naming the likely cause, never as success. `force` skips the dialogs and discards unsaved work.

ArgumentTypeRequiredDefaultDescription
appstringnoBundle identifier or app name.
confirm_tokenstringnoOne-shot token from a prior confirmation_required response; authorizes this destructive call under the standard profile.
forcebooleannofalseForce-quit: no save dialogs, unsaved work is lost.
pidintegernoTarget by process id instead of name.
wait_msintegerno10000How long to wait for the process to exit.

window_manage

Move, resize, minimize, unminimize, raise, or close a window — each verified by reading the window state back. A write the app accepted but ignored is an error, and a resize the app clamped reports the frame the window actually took. Target a window by element id (a node with role AXWindow in ui_snapshot), or by app plus an optional title substring; the default is the frontmost app's focused window. The result carries window.id — the handle for the window actually acted on. For several actions on ONE window, pass that id back rather than re-targeting by app: resolution follows focus, and minimize moves it, so an app with more than one window can otherwise hand the next call a different window. A window id stays valid for as long as the window exists — minimizing it or opening other windows does not repoint it — and once the window is gone the id resolves to a typed error, never to another window.

ArgumentTypeRequiredDefaultDescription
actionmove | resize | minimize | unminimize | raise | closeyesclose presses the window's close button, so an app with unsaved changes may answer with a save sheet — the result reports whether the window is still present.
appstringnoBundle identifier or app name. Defaults to the frontmost app.
heightnumbernoresize: new height, in points.
idstringnoElement id from a previous ui_snapshot (it carries the app's pid, so no separate app argument is needed). Re-snapshot if the UI has changed since.
pidintegernoTarget by process id instead of name.
titlestringnoPick the window whose title contains this, case-insensitively.
widthnumbernoresize: new width, in points.
xnumbernomove: new left edge, in screen points.
ynumbernomove: new top edge, in screen points.

wait read-only

Wait a fixed time, or poll until a condition holds: an app running or gone, an element id resolving or not. Prefer a condition over a blind sleep — it returns the moment the condition is true, and a condition that never comes true is a `timeout` error rather than a guess.

ArgumentTypeRequiredDefaultDescription
appstringnoApp name or bundle id, for the app_* conditions.
idstringnoElement id, for the element_* conditions.
msintegernoFixed sleep in milliseconds, when no condition is given.
pidintegernoTarget by process id instead of name.
timeout_msintegerno10000Give up on the condition after this long.
untilapp_running | app_gone | element_exists | element_gonenoCondition to poll for instead of sleeping blind.

screen — Screenshots, OCR and find-on-screen (default)

screenshot read-only

Capture a display, a window, or a rect as PNG (returned as image content; structuredContent carries dimensions and the captured region in screen points). Downscaled so the longest edge fits max_dimension.

ArgumentTypeRequiredDefaultDescription
appstringnoCapture this app's window instead of a display (bundle identifier or name). With several windows open, the frontmost on-screen match wins — narrow with window_title, or re-target exactly with the window_id every result reports.
displayintegernoDisplay index, 0 = main. The default when no window or rect is given.
max_dimensionintegerno1568Longest-edge pixel cap; never upscales.
rectobjectnoCrop in global screen points, top-left origin — an element frame from ui_snapshot pastes straight in. Ignored when a window is targeted.
window_idintegernoExact window from a previous result — the precise handle.
window_titlestringnoCase-insensitive title substring to pick a window; combines with app.

ocr read-only

Read the text on a display, in a window, or inside a rect (Vision, accurate mode). Returns lines with confidence and bounding boxes in global screen points — the same space ui_snapshot frames use, so a box's center is clickable as-is. For finding one known string, screen_find is cheaper to read.

ArgumentTypeRequiredDefaultDescription
appstringnoCapture this app's window instead of a display (bundle identifier or name). With several windows open, the frontmost on-screen match wins — narrow with window_title, or re-target exactly with the window_id every result reports.
displayintegernoDisplay index, 0 = main. The default when no window or rect is given.
max_dimensionintegernoLongest-edge pixel cap. Default is native resolution — downscaling costs recognition accuracy.
rectobjectnoCrop in global screen points, top-left origin — an element frame from ui_snapshot pastes straight in. Ignored when a window is targeted.
window_idintegernoExact window from a previous result — the precise handle.
window_titlestringnoCase-insensitive title substring to pick a window; combines with app.

screen_find read-only

Find text on screen and get clickable coordinates: OCR plus fuzzy matching, tolerant of small misreads. The bridge for apps without accessibility support — matches come back best-first with center points in global screen coordinates. No match above the threshold is an element_not_found error carrying the nearest text actually seen.

ArgumentTypeRequiredDefaultDescription
textstringyesThe text to look for — a word, label, or short phrase.
appstringnoCapture this app's window instead of a display (bundle identifier or name). With several windows open, the frontmost on-screen match wins — narrow with window_title, or re-target exactly with the window_id every result reports.
displayintegernoDisplay index, 0 = main. The default when no window or rect is given.
max_dimensionintegernoLongest-edge pixel cap. Default is native resolution — downscaling costs recognition accuracy.
rectobjectnoCrop in global screen points, top-left origin — an element frame from ui_snapshot pastes straight in. Ignored when a window is targeted.
thresholdnumberno0.8Minimum match score; 1.0 demands an exact (case-insensitive) hit.
window_idintegernoExact window from a previous result — the precise handle.
window_titlestringnoCase-insensitive title substring to pick a window; combines with app.

system — Shell, AppleScript recipes, clipboard, open, Spotlight (default)

shell

Run a shell command with /bin/zsh -c. Returns exit_code and combined stdout+stderr (capped, `truncated` flags the cut). Times out with a typed error; long jobs should raise timeout_seconds (max 600).

ArgumentTypeRequiredDefaultDescription
commandstringyesCommand line passed to /bin/zsh -c.
cwdstringnoWorking directory (default: server's cwd).
envobjectnoExtra environment variables, merged over the server's.
timeout_secondsnumberno30

applescript_recipes read-only

The catalog of vetted AppleScript recipes: id, description, and the parameters each one takes. Recipes are the standard way to script apps — parameterized, injection-safe, and allowed where raw applescript_eval is not.

Takes no arguments.

applescript_run_recipe

Run a recipe from the catalog by id with typed params. Script failures come back as ok:false with the script's own error message (and a fix hint for missing Automation permission) — like a non-zero shell exit, not a protocol error. Scripting another app prompts for Automation permission on first use.

ArgumentTypeRequiredDefaultDescription
recipestringyesRecipe id from applescript_recipes.
paramsobjectnoRecipe parameters by name; types per the recipe's schema.
timeout_secondsnumberno30

applescript_eval destructive — needs confirmation

Evaluate raw AppleScript or JXA source. Gated to the `full` permission profile — under `standard`, use applescript_run_recipe. Same result contract as recipes: script failures are ok:false with the script's message, not protocol errors.

ArgumentTypeRequiredDefaultDescription
sourcestringyesThe script source to evaluate.
confirm_tokenstringnoOne-shot token from a prior confirmation_required response; authorizes this destructive call under the standard profile.
languageapplescript | jxano"applescript"
timeout_secondsnumberno30

clipboard_read read-only

Read the clipboard with type awareness. Default picks the richest of string → png → file_url; pass `type` to demand one (an absent type is an element_not_found listing what the clipboard does hold). Images come back as PNG image content regardless of how the source app put them there.

ArgumentTypeRequiredDefaultDescription
typestring | png | file_urlnoDemand a specific representation instead of the default order.

clipboard_write

Replace the clipboard with exactly one of: text, a base64 PNG, or file paths. The write is read back from the pasteboard before reporting success.

ArgumentTypeRequiredDefaultDescription
file_pathsarraynoAbsolute paths to place as file references.
png_base64stringnoBase64-encoded image data, placed as PNG.
textstringnoPlain text to place.

open

Open a file or URL with its default app, or a specific app by bundle id / .app path. Reports the app that actually took it (name + pid). To reveal a file in Finder instead of opening it, use the finder_reveal recipe.

ArgumentTypeRequiredDefaultDescription
targetstringyesAbsolute file path (or ~/…), or a URL with a scheme.
appstringnoBundle id or absolute .app path to open the target with.

files — Trash and Finder tags — never a hard delete (default)

trash destructive — needs confirmation

Move a file or folder to the Trash — recoverable by design; this server never hard-deletes. Verified: the item is gone from its place and present in the Trash, and the result carries the trash path for undo.

ArgumentTypeRequiredDefaultDescription
pathstringyesAbsolute (or ~/…) path of the file or folder.
confirm_tokenstringnoOne-shot token from a prior confirmation_required response; authorizes this destructive call under the standard profile.

tags

Read a file's Finder tags; pass `set` to replace them (empty array clears). Writes are read back before reporting success.

ArgumentTypeRequiredDefaultDescription
pathstringyesAbsolute (or ~/…) path of the file or folder.
setarraynoReplace all tags with these; omit to only read.

apps — Calendar, Reminders, Contacts (gated)

calendar_list read-only

The user's event calendars: id and title.

Takes no arguments.

reminders_list read-only

Reminders, incomplete by default, optionally narrowed to one list. First use may prompt for Reminders access.

ArgumentTypeRequiredDefaultDescription
include_completedbooleannofalse
limitintegerno100
liststringnoExact reminder-list name (case-insensitive).

intents — Shortcuts and App Intents (gated)

intents_list read-only

List the shortcuts installed on this Mac (name + identifier). These are the App Intents apps have donated; run one with intents_run. Needs a logged-in GUI session — the Shortcuts helper is unavailable from a bare SSH shell.

Takes no arguments.

intents_run

Run a shortcut by name or identifier and return its output. Optional `input` is passed to the shortcut as text. Effects are the shortcut's own; this reports what it returned, empty when it produces nothing.

ArgumentTypeRequiredDefaultDescription
namestringyesShortcut name or identifier from intents_list.
inputstringnoOptional text input handed to the shortcut.

web — Browser control: Apple's Safari MCP where available, AppleScript fallback elsewhere (gated)

web_navigate

Open an http(s) URL in Safari (AppleScript fallback — used when Apple's Safari MCP isn't available). Launches Safari if needed, then polls until the page settles and VERIFIES the front tab actually shows it; a redirect is reported, not hidden. Fails typed if the tab never moved.

ArgumentTypeRequiredDefaultDescription
urlstringyesAbsolute http(s) URL. javascript: and file: are refused.
timeout_secondsnumberno10How long to wait for the page to settle.

web_current_tab read-only

URL and title of Safari's frontmost tab (AppleScript fallback). Refuses rather than launching Safari when it isn't running.

Takes no arguments.

web_page_text read-only

Rendered text of Safari's frontmost tab (AppleScript fallback) — the page as read, not its HTML. Capped by max_chars with `truncated` flagging the cut. Needs no developer settings; scripts and markup are not returned.

ArgumentTypeRequiredDefaultDescription
max_charsintegerno20000

web_tabs read-only

Every tab of Safari's front window with its 1-based index, URL, title and whether it is current (AppleScript fallback). The indices are what web_select_tab takes.

Takes no arguments.

web_select_tab

Make a tab of Safari's front window current, by the 1-based index from web_tabs (AppleScript fallback). Reads the current tab back and fails typed if the window ignored the switch.

ArgumentTypeRequiredDefaultDescription
indexintegeryes1-based tab index from web_tabs.

ios — Control a mirrored iPhone in device points — read, tap, swipe, type, navigate (gated)

iphone_status read-only

Is a mirrored iPhone readable right now, and if not, what to do about it. Never fails: it reports one of ready, screen_recording_missing, app_not_installed, app_not_running, window_offscreen, no_phone_content, blocked, not_connected, needs_resume or capture_timed_out, each with the observation behind it and the single next action. Call this first, and call it after any other iphone_ tool returns refused. **The three states that come from the window's own accessibility tree** — blocked (a modal sheet is over the phone; its message and buttons are in `overlay`), not_connected (the window offers Sign In / Connect) and needs_resume (the session paused: an AXButton 'Resume' beside 'Connection Paused'; `overlay.control` names the control as observed and `overlay.signals` says which markers fired, and iphone_resume presses it) — are the ones pixels cannot see: an alert still renders something phone-shaped, so a screenshot would call it ready. **Device identification is reported with its quality.** `phone.device_match.quality` is `matched` when at least one profile fits the window's shape, `nearest` when **none** does and the reported profile is only the closest one, and `pinned` when you passed `device`. `device_candidates` is empty in the `nearest` case, which is the honest answer rather than a guess dressed as a match; taps still land correctly because the mapping is proportional, but the reported point size is approximate until you pin one. Note that a locked-and-far-away phone, an unfinished setup screen, a phone being used in your hand, and a region where iPhone Mirroring is unavailable all look identical from outside the app — they share the no_phone_content state, whose fix lists all four. This tool is focus-free, so it never reports focus_unavailable; only the tools that act on the phone can see that one.

ArgumentTypeRequiredDefaultDescription
devicestringnoPin the device profile (e.g. "iphone-16-pro") instead of inferring it from the mirrored window's shape. iphone_status lists the ids that fit. Inference is proportional and lands taps correctly either way; pin this when you need device points to be exact.

iphone_screenshot read-only

Capture the mirrored iPhone's screen as PNG — cropped to the phone itself, never the surrounding window or its letterbox bars. structuredContent carries the device's logical size in points, the scale it is being shown at, and the device profiles that fit its shape. Read-only and focus-free: it takes nothing from the app you are in.

ArgumentTypeRequiredDefaultDescription
devicestringnoPin the device profile (e.g. "iphone-16-pro") instead of inferring it from the mirrored window's shape. iphone_status lists the ids that fit. Inference is proportional and lands taps correctly either way; pin this when you need device points to be exact.
max_dimensionintegerno1568Longest-edge pixel cap; never upscales.

iphone_find read-only

Find text on the mirrored iPhone and get coordinates in the phone's own space: OCR plus fuzzy matching, tolerant of small misreads. Matches come back best-first with a center in **device points** — the frame of reference that survives a window resize, a different Mac, and a transcript replayed tomorrow — alongside the global screen point that currently corresponds to it. Text outside the phone (window chrome, a notification lying over it) is dropped rather than returned. No match above the threshold is an element_not_found error carrying the nearest text actually seen. There is no accessibility tree behind iPhone Mirroring — the content is an opaque video surface — so this is the primary way to locate anything on the phone.

ArgumentTypeRequiredDefaultDescription
textstringyesThe string to look for. Case- and diacritic-insensitive.
devicestringnoPin the device profile (e.g. "iphone-16-pro") instead of inferring it from the mirrored window's shape. iphone_status lists the ids that fit. Inference is proportional and lands taps correctly either way; pin this when you need device points to be exact.
thresholdnumberno0.8Minimum fuzzy score, 0–1. Lower it for noisy or stylised text.

iphone_tap destructive — needs confirmation

Tap the mirrored iPhone at a point in **device points** — the space iphone_find returns, which survives a window resize. Captures the phone before and after and reports whether the screen actually changed: a tap that changed nothing is an action_unverified error, never a false success. A change proves something happened, not that this tap caused it — a notification arriving would also move pixels — so treat it as evidence, and read the screen back with iphone_screenshot when it matters. Takes keyboard and mouse focus for the length of the call: macOS sends synthetic input to the frontmost app only, so this cannot run alongside other ui_ automation on this Mac and will interrupt whoever is at the keyboard.

ArgumentTypeRequiredDefaultDescription
xnumberyesDevice-point x, from iphone_find's device_center.
ynumberyesDevice-point y.
confirm_tokenstringnoOne-shot token from a prior confirmation_required response; authorizes this destructive call under the standard profile.
countintegerno12 for a double-tap (zoom, text selection).
devicestringnoPin the device profile (e.g. "iphone-16-pro") instead of inferring it from the mirrored window's shape. iphone_status lists the ids that fit. Inference is proportional and lands taps correctly either way; pin this when you need device points to be exact.
min_changenumbernoFraction of the phone's screen that must look different for the action to count as verified, 0–1. Compared on a coarse grid so video compression noise does not register. Raise it on a screen with live content (a video, an animated wallpaper) that changes on its own.
settle_msintegernoHow long to keep watching for the screen to change, in milliseconds. Polled, not slept: a landed action returns as soon as it shows. Raise it for a slow network or a long animation; the phone arrives as a video stream, so nothing here is instant.

iphone_swipe destructive — needs confirmation

Swipe or scroll the mirrored iPhone between two points in **device points**. `drag` mode presses, moves and releases — a finger on the glass, which is what dismisses a sheet or pulls to refresh. `scroll` mode sends wheel events instead, which is what Apple's own iPhone Mirroring documentation points at for moving a list, and is often the one that works when a drag does not. Verified the same way as iphone_tap: no change on screen is an action_unverified error rather than a reported success. Takes focus for the length of the call.

ArgumentTypeRequiredDefaultDescription
fromobjectyesWhere the finger goes down, in device points.
toobjectyesWhere it lifts.
confirm_tokenstringnoOne-shot token from a prior confirmation_required response; authorizes this destructive call under the standard profile.
devicestringnoPin the device profile (e.g. "iphone-16-pro") instead of inferring it from the mirrored window's shape. iphone_status lists the ids that fit. Inference is proportional and lands taps correctly either way; pin this when you need device points to be exact.
duration_msintegerno300How long the drag takes. Short is a flick, long is a deliberate drag; iOS reads the difference. Ignored in scroll mode.
min_changenumbernoFraction of the phone's screen that must look different for the action to count as verified, 0–1. Compared on a coarse grid so video compression noise does not register. Raise it on a screen with live content (a video, an animated wallpaper) that changes on its own.
modedrag | scrollno"drag"drag = a finger on the glass; scroll = wheel events.
settle_msintegernoHow long to keep watching for the screen to change, in milliseconds. Polled, not slept: a landed action returns as soon as it shows. Raise it for a slow network or a long animation; the phone arrives as a video stream, so nothing here is instant.

iphone_type destructive — needs confirmation

Type text into whatever has keyboard focus on the mirrored iPhone. **The result is honestly unverified and says so**: iOS hides the on-screen keyboard while Mirroring is active and the mirrored content has no accessibility tree, so there is nothing on this Mac that can read the field back. The body carries verification.method = "none" — branch on that, not on ok — plus whether the screen changed at all, which is weak evidence the keystrokes arrived somewhere. To confirm the text landed, call iphone_screenshot and look. Tap the field first: this types wherever focus already is, and if nothing has focus the keystrokes are discarded silently. Known Apple defect, mitigated but not fixed here: iPhone Mirroring intermittently latches a modifier and returns text in alternating case — this tool releases every modifier first and never sends Shift, and reports the risk rather than claiming immunity.

ArgumentTypeRequiredDefaultDescription
textstringyesThe text to send. Sent as Unicode, so emoji and non-Latin scripts work.
clear_modifiersbooleannotrueRelease every modifier before typing, the mitigation for the alternating-case defect. Turn it off only if it interferes.
confirm_tokenstringnoOne-shot token from a prior confirmation_required response; authorizes this destructive call under the standard profile.
devicestringnoPin the device profile (e.g. "iphone-16-pro") instead of inferring it from the mirrored window's shape. iphone_status lists the ids that fit. Inference is proportional and lands taps correctly either way; pin this when you need device points to be exact.
settle_msintegernoHow long to keep watching for the screen to change, in milliseconds. Polled, not slept: a landed action returns as soon as it shows. Raise it for a slow network or a long animation; the phone arrives as a video stream, so nothing here is instant.

iphone_key destructive — needs confirmation

Press one of iPhone Mirroring's navigation keys. **home, app_switcher and spotlight send Apple's documented Command-1 / Command-2 / Command-3**, each measured on a real iPhone. home and app_switcher additionally have named buttons in the window's own toolbar (AXButton 'Home Screen' and AXButton 'App Switcher', both taking AXPress) and those are pressed when they are there — but the toolbar auto-hides and a hidden toolbar exposes no children at all, so in a normal session they are not, and the shortcut is the ordinary path rather than a degraded one. The result says which ran: `method` is "ax_press" or "shortcut", and `ax_press` reports whether the control was available and whether it was used. The other two are not shortcuts either — back sends iOS's left-edge swipe gesture, because iPhone Mirroring documents no back key; lock **ends the mirroring session** (it closes the window), because Mirroring only ever runs against an iPhone that is already locked and nearby, so there is no phone to lock from here. Verified like every other mutating verb — the screen has to change, and for lock the session has to actually end — so a shortcut that Apple changes in a future macOS surfaces as action_unverified rather than as a silent no-op. Takes focus for the length of the call.

ArgumentTypeRequiredDefaultDescription
keyhome | app_switcher | spotlight | back | lockyesWhich navigation key to press.
confirm_tokenstringnoOne-shot token from a prior confirmation_required response; authorizes this destructive call under the standard profile.
devicestringnoPin the device profile (e.g. "iphone-16-pro") instead of inferring it from the mirrored window's shape. iphone_status lists the ids that fit. Inference is proportional and lands taps correctly either way; pin this when you need device points to be exact.
min_changenumbernoFraction of the phone's screen that must look different for the action to count as verified, 0–1. Compared on a coarse grid so video compression noise does not register. Raise it on a screen with live content (a video, an animated wallpaper) that changes on its own.
settle_msintegernoHow long to keep watching for the screen to change, in milliseconds. Polled, not slept: a landed action returns as soon as it shows. Raise it for a slow network or a long animation; the phone arrives as a video stream, so nothing here is instant.

iphone_resume destructive — needs confirmation

Press the `Resume` control iPhone Mirroring shows when it has paused itself, then report what the window did about it. Mirroring suspends the session after inactivity and shows an AXButton 'Resume' beside 'Connection Paused'; iphone_status reports that as `needs_resume`. **Branch on `outcome`, not on ok** — ok only means the control was pressed. `outcome` is "resumed" when the paused markers are gone, or "authentication_required" when the window is still paused or a macOS authentication prompt has taken the screen. Both really happen: a measured resume needed no password at all, and the same Mac asks for one on other occasions, so neither is assumed. **This tool never answers that prompt.** Passwords and Touch ID are a hard line here, and SecurityAgent cannot be scripted in any case; when authentication is needed a person does it, and the result says so. Verification is the window's own accessibility tree changing state, never the press's return value — the AX click that resumed a real session reported verified:false and was right to. A tree that cannot be read back at all is action_unverified. If no pause control can be identified, this refuses and lists every control it did see rather than pressing a guess.

ArgumentTypeRequiredDefaultDescription
confirm_tokenstringnoOne-shot token from a prior confirmation_required response; authorizes this destructive call under the standard profile.
settle_msintegernoHow long to keep re-reading the window's accessibility tree for the paused markers to clear, in milliseconds. Polled, not slept. When the budget runs out with the window still paused, that is the authentication_required answer, not a failure.

server — Meta (default)

enable_toolset

Enable a gated toolset — its tools join the tool list at once and a list_changed notification follows. Toolsets: ui (enabled, 10 tools, needs accessibility); screen (enabled, 3 tools, needs screen_recording); system (enabled, 8 tools, no TCC grants); apps (enabled, 5 tools, needs calendars, reminders, contacts); files (enabled, 2 tools, no TCC grants); intents (enabled, 2 tools, no TCC grants); ios (enabled, 8 tools, needs screen_recording, accessibility); web (enabled, 5 tools, needs automation).

ArgumentTypeRequiredDefaultDescription
toolsetstringyesToolset name to enable.

Error codes

Every failure carries one of these codes. They are wire-stable: renames need a deprecation entry.

CodeMeansWhat to do
permission_missingA required TCC grant is missing.fix holds the exact System Settings deep-link; grant it and restart the client.
element_not_foundAn id, app or key did not resolve.nearest_matches lists what is there — often ids visible right now. Re-snapshot and retry.
invalid_argumentA value failed semantic validation the schema can't express — unknown recipe param, enum violation, type mismatch.param names the offender; expected says what would have been accepted. Fix the argument, don't retry blind.
refusedA hard line. The server will not do this.reason says why, alternative points at the legitimate route. Do not retry.
action_unverifiedIt ran, reported success, and nothing changed.expected vs actual show the gap. Re-snapshot: the UI is not where you thought.
timeoutThe operation outlived its budget.Retry with a longer timeout, or check the app is responding.
confirmation_requiredA destructive action needs a confirm token.Re-issue with the token once the human has approved.
upstream_unavailableA mounted upstream (Safari/Xcode MCP) is down.enable_hint names the setting to switch on.
machine_unreachableFleet: the target machine is not answering.Check last_seen; the agent may be offline.
host_at_capacityFleet: the host is at its macOS-VM ceiling (2 per Apple Silicon host — a kernel quota).Schedule the clone on another host; more RAM will not help.
lease_heldFleet: another client holds the machine.expires says when it frees.

Safety properties worth knowing