iphone-use
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| IPHONE_USE_MCP_TOOLS | No | Tool profile: all (full server), readonly (12 observation and registry tools), or a comma-separated allowlist. Use readonly for browser-only registry inspection. Device actions require a separately configured macOS daemon and iPhone. | all |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {} |
| resources | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| phone_app_notesA | Read or replace persistent notes for one bundle id about app navigation and quirks. Resolve bundle with phone_apps; without text, read the existing Markdown. With text, replace it entirely (not append), at most 16 KiB UTF-8; read first to preserve useful notes. An empty text clears the notes. The first screen read in an app also includes them in its registry block. Use phone_flow_draft for executable task replay, not these notes. Writes affect future sessions; never store passwords, codes or personal data. Errors do not prove a write was delivered: read back before retrying. |
| phone_appsA | Find an app's bundle id by name instead of guessing it. Read-only: it never touches the screen and takes no owner lease. Searches the phone's own app list, then a bundled catalog of popular apps (bundle ids verified against Apple's lookup API, plus Apple's built-in apps), then the App Store only when neither matched. Exact name/alias matches hide partial ones. Each candidate carries name, bundle_id, source (installed|catalog|apple), installed_verified, and publisher when known; installed_verified=false with installation_checked=false means unknown, not absent. Pick the candidate whose name and publisher fit, then launch it with launch_app bundle — or give launch_app |
| phone_capabilitiesA | Inspect supported UI operations and cached readiness before choosing an action. Unlike phone_status (device/setup/owner diagnostics), this names the backend's single-step, batch and perform vocabularies, observation support and accepted modes. Read-only: no device connection, wake or owner lease. supported is static; available is cached with blocked_by naming locked, released, reconnecting, human_handoff, owned_by_other or offline. ok:null is unknown, never proof of readiness. This describes UI control only, not app installation or other device management. If blocked, follow phone_status guidance instead of probing with actions. |
| phone_collect_listA | Read a scrolling list across pages in ONE call instead of scroll + read turns: reads the rows of one kind (default Cell) on screen, swipes one page, reads again, and stops when the exposed page repeats (duplicate_page), end_label is on screen, or max_pages (default 6, max 10) is reached. Rows are deduplicated on (kind, identifier, label, value): different exposed identifiers preserve identical-looking rows. Absent or reused identifiers with identical content may still collapse, so reconcile counts the user cares about. |
| phone_crashesA | Crash reports on the phone: without |
| phone_elementsA | The current screen as text — use this instead of screenshots (an order of magnitude cheaper). Rows carry kind, label, identifier, value, rect and state, plus a |
| phone_flow_draftA | Turn what you just did on the phone into a flow. The daemon records every action it applied in the current app (single actions and phone_run_steps batches, not flow runs); this returns that trail as a flow v1 document (snapshot taps become label/identifier locators, typed text becomes named inputs and is never included) plus a |
| phone_flow_infoA | Inspect an installed flow before phone_flow_run: returns metadata, declared inputs and step templates with placeholders, never runtime input values. Use an id from phone_flow_list, such as health/export-all: exactly two lowercase app/flow slugs, not a local file path. Check app, locale, verification and side effects before execution. Invalid or missing ids return an error without running or downloading anything; use phone_flow_update if the registry store is empty. To inspect a new local draft, validate its file instead of guessing a registry id. |
| phone_flow_listA | Find installed deterministic per-app flows before exploring a task: a matching phone_flow_run can replay it without model decisions or screenshots. Filters category, app and verified combine; app is a registry app directory or bundle id, not a display-name search. Compact rows report id, description, inputs, risk and compatibility with this phone's app version; detail=true returns full JSON. verified means a recorded hardware run, not a guarantee for every current version. Use phone_flow_info to inspect effects before running. An empty store needs phone_flow_update once; a filter with no matches is not proof that the whole store is empty. |
| phone_flow_publishA | Publish a working flow to the official registry through the user's GitHub CLI login: forks if needed, writes the flow and any new app.json, rebuilds index.json, pushes a branch and opens a public pull request. Requires confirm=true only after the user agrees to that publication. First create a draft with phone_flow_draft and prove it with phone_flow_run; use phone_flow_report to report an existing flow problem. Put device/iOS/date in verified_on; unverified flows open a draft PR. source is a local file or installed flow id; aliases are foreground-app labels by language. Returns id, repo, branch, head and pr_url; inspect the URL before retrying a failed publication. |
| phone_flow_reportA | File an issue on the official flow registry for an installed flow that failed (label changed, app updated, wrong locale). Uses the failure captured by the last phone_flow_run of that id — failed step, redacted daemon result, daemon version — so you only add a short note. Screen labels, typed text, and element lists are stripped. OUTWARD-FACING: requires confirm=true after the user agreed. Do not file for a phone that was simply locked/offline, or for a flow you ran on the wrong app/locale. |
| phone_flow_runA | Run one flow exactly once — an installed registry id, or a flow file path (*.json, e.g. a phone_flow_draft you are proving before publishing). A flow that declares |
| phone_flow_updateA | Mirror the official iphone-use flow registry (github.com/leeguooooo/iphone-use-flows) into the local store. Every file is checksum-verified and strictly validated before it is written; locally added flows are kept. Call once when phone_flow_list reports an empty store, or to pick up new flows. Network only; the phone is not touched. |
| phone_holdA | Keep the phone for a bounded human-in-the-loop pause inside the current user-requested task (the operator types a PIN, approves a prompt, fetches a code): the idle watchdog will not release it for |
| phone_home_layoutA | Read all home-screen pages, Dock, folder pages and widget stacks from the paired iPhone without opening folders, swiping, or taking screenshots. Optional page selects one zero-based page to reduce output. Requires a configured physical target reachable through usbmuxd. This metadata read does not require drivable=true or a running XCTest Runner; never reconnect or open Home solely for this read. Page indexes refer to persisted icon state, including hidden/Focus-filtered pages; they are not the visible page-control indexes. Zero-based row/col positions are derived from icon-state order and grid metrics; null positions or positions_complete=false mean unknown, never safe drag coordinates. Contains no pixel coordinates. An unreadable device returns an error rather than an empty home screen. Writing (action): backup saves the layout as a plist on the Mac; preview compares a plist with the phone by app identifier (added, removed, moved, widgets that would be lost); apply/restore replace the whole layout, only with confirm=true after the person agreed to the preview, and back up the replaced layout first. After a write read again: SpringBoard may bring App Library apps back over a few passes. Undo with action=restore. |
| phone_install_appA | Install an app build on the phone: |
| phone_instancesA | List installed local iPhone instances and their read-only status. This inventory is not a list of paired/connected devices. For recovery_owner=unconfigured, inspect xcrun devicectl list devices and run iphone-use doctor on the daemon host; ask which phone when ambiguous. Does not start, reconnect or claim a phone. Pass instance in any phone_* call to choose one; omitted instance preserves this MCP server's configured daemon. Missing/invalid instances never fall back to another phone. |
| phone_jev_runA | Hand ONE whole goal to a fast on-phone agent instead of driving it step by step: TypeSafe's Jev picks each next operation and target from the screen's element table (~1–2 s per step instead of a model turn), a small LLM types field values. Use it for a clear multi-step goal with no saved flow (check phone_flow_list first). It stops with status done / blocked / max_steps and the step history; it answers BLOCKED rather than send, pay, delete or share anything the goal did not explicitly ask for — still confirm such goals with the user first. Verify the end screen yourself. Needs TYPESAFE_API_KEY (or ~/.config/typesafe/key) and, for typing, TEXT_MODEL_API_KEY (or ~/.config/openrouter/key). |
| phone_keyA | Press a navigation or editing key in the foreground app, acting on its current focus. Use phone_type for literal text and phone_tap_element to focus a field first. Check phone_status.drivable and the focused control: delete/backspace can erase content, and return/enter may submit or send it. The schema lists accepted names; unsupported names fail before input is sent. observe defaults on and returns the settled screen change. On an unknown outcome, inspect the screen before another key; repeating a key can change or submit content twice. |
| phone_loginA | Log the app on screen in with the user's OWN account from their password vault (bitwarden-use on the Mac). The daemon reads the vault entry and types it into the phone, so no password or username passes through this conversation. Use it when an app shows its login page during a task the user asked for. The entry is matched to the app; when several match, the answer lists their names: ask the user, then pass |
| phone_logsA | Capture and read the paired phone's live system log for app debugging; use phone_crashes for saved crash reports instead. Start capture, reproduce the issue, then read (the default); stop releases the relay, while clear discards the in-memory buffer. Requires a reachable, paired device; connection/auth failures are errors, not an empty log. Capture self-stops after 15 minutes without reads. Filters apply to read results; pass the returned next cursor as since for incremental reads. Output lines are #seq time process[pid] level: message; error includes faults. Logs can contain private app data: collect only what the task needs. |
| phone_metricsA | Read task performance aggregates for this MCP client's X-Phone-Owner on the selected instance. Includes recorded HTTP-call counts, batch/flow usage, stale/unknown outcomes, losses and p50/p95 call latency. HTTP calls are not model turns or token usage; model_round_trips remains null without a complete declared trace. Read-only: no phone connection, Runner launch or control lease. Default latest 5 open and recent runs, maximum 20 per section and a shared 12 KiB row budget; omitted counts and truncated state are explicit. Never reconnect solely for metrics. Global cross-owner loss counters are excluded. |
| phone_read_valueA | Read one current element's AX value without tapping, focusing, waking or taking an owner lease. Give exactly one identifier or exact label from a fresh screen, optionally kind; ambiguity refuses. Use this when phone_elements marks value_truncated or the task needs full field text. Returns one bounded value in text with source/completeness metadata; complete means the AX attribute was not cut by this API, not that the app exposes its full document. A smaller max_utf16 returns truncated:true. Secure fields refuse; old runners without ipu:valueRead refuse rather than promote a preview. Owner/privacy changes withhold the value. |
| phone_reconnectA | Start/restart on-device automation for the configured phone. This occupies the phone and may require the operator to unlock it. Use only to continue a current user-requested phone operation or screen/UI read that cannot proceed; never reconnect for initialization, status/health checks, a completed task, or to keep the phone ready. Idle release is intentional. Require phone_status released/offline and recovery_owner=daemon; inspect hint/setup_blocked_on first. Do not reconnect while releasing/reconnecting or a blocker remains. Once these conditions are met, call once, then wait for the outcome: pass wait_secs (e.g. 180) to block until drivable=true, a blocker (setup_blocked_on), or the timeout, instead of polling phone_status; |
| phone_recordA | Record the iPhone screen to an MP4 on the daemon's Mac: start, perform the requested task, then stop to get path, duration and size. status checks the recording without starting one. Unlike phone_screenshot (one image) or phone_screen (live panel), this persists video under the instance state directory's runs/recordings. Requires a usable device video stream and a writable output directory. Auto-stops after 10 minutes or 200 MB; ended explains why. Video can contain private screens: record only the authorized task, stop when done, and do not treat the returned Mac path as an upload. |
| phone_release_ownerA | Release this session's phone lease when its task ends, so another session can drive immediately. Unlike phone_hold(secs=0), this gives up ownership rather than merely clearing a pause. It sends no phone input and returns ok with owner=null; releasing an already-unowned phone succeeds. A different session's active lease returns phone_owned and is never taken over. Authentication/transport failures are errors; check phone_status.owner before assuming the lease was released. |
| phone_run_endA | Close a live task opened with phone_run_start and return its metrics: HTTP calls (not model turns), batches, observed actions, flows, stale/unknown outcomes, failures and p50/p95 call time. Use the same run_id and session; a missing or already-closed run cannot produce a new live summary. Supply turn_ids only for a complete_trace declaration, including every model generation; otherwise model_round_trips remains unknown. This ends accounting only, not a video recording or the owner lease: use phone_record(stop) and phone_release_owner for those. |
| phone_run_startA | Open a task boundary for this session's daemon metrics; pair with phone_run_end, not phone_record (video). Later session calls count toward this run; this does not drive or reserve the phone. Optional: without it, idle gaps infer runs. Use a fresh run_id for each task; starting the same still-open id resumes that run rather than resetting its counts. complete_trace is valid only before its first counted call and commits you to supply every model turn id at run_end. Returns a start acknowledgement; report errors rather than claiming metrics were recorded. |
| phone_run_stepsA | THE DEFAULT WAY TO ACT: run a sequence of actions in ONE call and get the screen it ended on back in the same reply (observe, on by default) — one turn instead of one per tap. The daemon validates the whole sequence first, holds one control lock, and stops at the first failure. Use it as soon as a segment is understood; keep single-action tools for exploring an unknown screen. Step kinds: launch_app, open_url, tap_locator, tap_label, tap, type, key, shortcut, scroll, swipe, drag, longpress, multi_longpress (2–5 explicit normalized points held simultaneously), back, alert (system alerts; taps on them do not act), picker, perform (toggle a switch, adjust a slider: element+snapshot from phone_elements), wait_for, pause. Drag points can dwell at an edge while keeping one finger down. Guard every screen change with wait_for; never batch an irreversible step you have not verified. These step objects are exactly a saved flow's |
| phone_screenA | Open the live iPhone screen panel beside the chat, or reuse the one already open, in hosts that render MCP Apps: the person watches the phone while you work and can press Home or refresh. Read-only: takes no owner lease and wakes nothing. Returns a short status (phone, drivable, owner). Open it once when the person wants to watch; it never replaces phone_elements or phone_screenshot for your own observation. Hosts without MCP Apps show no panel. |
| phone_screen_frameA | Internal frame transport for the iphone-use MCP Apps panel; models use phone_screenshot for a still image and phone_elements for text. Returns structuredContent with status, frame (mime_type=image/png and base64 data), ok and error. A private hold returns frame=null; an empty or failed capture is reported rather than invented pixels. max_side is clamped to 200–1000; fresh bypasses the reusable frame. The panel calls this with visibility app; do not poll it from model turns or use it for health checks. |
| phone_screenshotA | Read one iPhone screen as an image/png content block when pixels matter; use phone_elements for text and semantic targets, or phone_screen to let the person watch a live MCP Apps panel. It reuses the captured settled frame after an observed action. max_side defaults to 1200; 0 requests full resolution. overlay=true draws interactive frames and their phone_elements indexes. Capture only for the current user task; private holds or offline devices can refuse it. Follow phone_status guidance instead of reconnecting for health checks. An image alone does not prove an action succeeded; check the target state or receipt. |
| phone_scrollA | Move the current viewport with one device-side swipe; use phone_scroll_find to locate a known label, or phone_collect_list to read multiple pages. x,y are a normalized anchor in the intended scrollable region. Avoid text fields, sliders and screen edges: a misplaced gesture can edit a control or navigate away. Positive dy reveals content below, negative above; dx reveals right/left. Travel is 1.5 times each delta in points, clamped to 15–75% of that screen dimension. Requires phone_status.drivable=true. observe defaults on and returns the settled screen change; inspect it before repeating a swipe. |
| phone_scroll_findA | Find one exact label, swiping at most max_swipes times (default 1, max 5) when it is not on screen. Stops AT ONCE — no further swipe — when the label is ambiguous (several on-screen matches: |
| phone_set_locationA | Simulate GPS for testing location-aware apps. Supply both lat (-90–90) and lon (-180–180), in finite decimal degrees, or clear=true without coordinates to restore real location; combining clear with coordinates is refused. Every app sees the simulated position until cleared; it is not a navigation or address lookup tool. Counts as one daemon action and requires authorized device control. Returns the applied location/clear status; on a transport failure the effect is unknown. Inspect the result before proceeding and clear the simulation when the test ends. |
| phone_shortcutA | Use Home (home), Spotlight (spotlight), physical volume buttons (volume_up, volume_down), or request a system screenshot in Photos (save_screenshot). Volume acknowledgement confirms only a button request, not the volume level; Simulator and older Runners without ipu:volumeButtons refuse before dispatch. save_screenshot is a mutation, unlike phone_screenshot which only returns image bytes. Its receipt means requested, not saved_verified, and requires ipu:systemCaptureRequest. If iOS opens a screenshot preview, inspect the system UI and choose Done/Save to Photos for that existing image; never take another screenshot to complete its save. Check Photos or its picker before claiming it saved. Never replay an uncertain capture or volume request. Navigation leaves the current app or changes its visible state. Use phone_key for field keys, or phone_run_steps with launch_app for a specific app. Requires phone_status.drivable=true. Names are trimmed and case-insensitive. switcher and other names fail before input is sent; the runner cannot synthesize the App Switcher gesture. observe defaults on and returns the settled screen change. Verify it before another action; repeating system navigation may lose the current context. |
| phone_statusA | Read the phone's status without taking control. |
| phone_tapA | Tap a pixel target using normalized x,y from 0–1, with (0,0) at top-left. Prefer phone_tap_element for an accessible control; use phone_screenshot to locate a pixel-only target. Requires phone_status.drivable=true and current screen evidence: a tap may send, buy, delete or toggle whatever occupies that point, so authorize its actual effect first. observe defaults on and returns the settled screen change. Read outcome/retry_safe; if delivery is unknown, inspect the current state or receipt before considering another tap. |
| phone_tap_elementA | Tap one element by its zero-based index and snapshot token from the SAME phone_elements response. The daemon re-reads the tree and refuses the action if the UI changed, so a stale element reference cannot silently tap a different control. Use identifier/kind/label/state fields to choose the index. Snapshot refs are ephemeral: never persist them in a flow. |
| phone_tap_labelA | Tap an iPhone UI element by an EXACT visible label (requires drivable=true in phone_status). Reads a fresh phone_elements snapshot, requires exactly one match, then performs a snapshot-bound tap. Zero or multiple matches return an error and send NO action. For duplicate labels, choose by identifier/kind/state from phone_elements and call phone_tap_element with that response's snapshot. |
| phone_typeA | Insert literal Unicode text into the currently focused iPhone field. Use phone_tap_element to focus the intended field first; use phone_key for a navigation or editing key. Requires phone_status.drivable=true. Text can replace a selection or reach the wrong field if focus changed. Long text is chunked. Optional budget_ms (6000..15000) can stop at a safe boundary; continue only with the returned continue_token, without text/clear/budget_ms. Tokens are single-use and scoped to the original field, owner, privacy, lifecycle and session. Unknown outcomes never issue a token. On partial failure inspect characters_confirmed and remaining_text; never resend the whole value blindly. observe defaults on and returns the settled screen change. Verify the resulting field before submitting; an unknown outcome is not evidence that nothing was typed. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| iphone-use-screen | Live view of the iPhone being driven, with Home and refresh buttons. |
TDQS
Scored across 41 tools
Most tools target clearly distinct capabilities, and the detailed descriptions explicitly differentiate close variants such as phone_tap, phone_tap_element, and phone_tap_label, or phone_screen, phone_screenshot, and phone_screen_frame. A few overlapping clusters remain—tap/scroll/observation/flow families—but their boundaries are largely well documented.
All tools use the predictable phone_ prefix and snake_case, making the set easy to scan. The pattern is mostly consistent, though it mixes verb-first names (phone_tap, phone_install_app) with noun-style read tools (phone_status, phone_elements) and one branded outlier (phone_jev_run).
With 41 tools, the surface is heavy for an agent to navigate, especially because several are niche administrative or internal tools. While the broad iPhone-automation domain justifies many capabilities, this count is well above a well-scoped set and increases selection cost.
The set covers a remarkably full lifecycle: app discovery, UI inspection, tapping, typing, scrolling, flows, installation, login, recording, diagnostics, location simulation, home layout, ownership, and metrics. Minor gaps exist—for example, no standalone direct launch/uninstall or clipboard/notification operations—but they are largely workable through phone_run_steps and related tools.