| name | phone |
| description | Use when the user asks to text or message someone from their iPhone, read or check something on their phone, or control their iPhone — e.g. "text my dad ...", "send this to Mom", "summarize this and text it to X", "what's on my phone", "open <app> on my phone", "organize my home screen", "what apps do I have installed". |
Drive the user's iPhone (phone-claude)
The user's real iPhone is connected to this PC over USB and fully controllable.
Harness lives at C:\Projects\phone-claude. Works from any project directory.
Run
cd C:/Projects/phone-claude && ./phone-harness.cmd <<'PY'
send_message("Mom", "hi")
PY
Bare phone-harness is NOT on the bash PATH — always use ./phone-harness.cmd
from the repo root. Helpers are pre-imported; print() output comes back to you.
REQUIRED SUB-SKILL: read phone-gotchas before your first tap. It holds the
coordinate traps, the batching tool, and what the harness genuinely cannot do —
none of which you can learn by looking at the screen.
Helpers
| Helper | Use |
|---|
send_message(contact, text) | full Messages send; contact = conversation name in the Messages list |
read_messages(contact, limit=20) | read a thread back: [{text, from_me}, ...], oldest first |
ocr() | all visible text with center coords (real UI tree, exact). Elements are {"text","x","y","type","rect"} — the key is text, and full= is MCP-only |
find_text(t) / tap_text(t) | locate / tap by visible text |
wait_for_text(t, timeout=10) | poll until text appears; returns the element (with x/y) |
tap(x, y) / swipe(x1,y1,x2,y2,secs) / scroll("down") | raw gestures, units = points |
long_press(x, y, seconds=1.0) | context menus and other hidden affordances |
act([{"tool":n,"args":{...}}, ...]) | several tools in ONE round trip; screenshot excluded |
type_text(t) | type into the focused field (tap the field first) |
open_app("messages") | friendly name or bundle id |
current_app() / wait_for_app(bundle_id) | frontmost app / wait until one is |
screenshot("out.png") / press_home() / wait_stable() / unlock() | utilities |
Not in the table, and you will want them:
- There is no
drag(). Moving Home Screen icons means jiggle mode plus a
hand-built client()._pointer_actions gesture, and it fails silently if you
skip jiggle mode. Recipe and the page-hiding flow are in phone-gotchas.
Same-page drags and folder-creation (drop icon A on icon B) are verified;
cross-page drags are not and degrade into plain swipes that look like
progress.
- "Organise my Home Screen" is not a quick job. ~160 icons is hours of drags
and can strand half-sorted. Hiding pages via the
PageIndicator editor is
~10 taps, reversible, and does most of the work — read the costing section in
phone-gotchas before you promise anything.
- Full installed-app inventory:
ios apps --list (go-ios, on PATH as
ios.EXE). Instant, and far cheaper than sweeping Home Screen pages.
Rules
- Sending to real people: send exactly what the user asked. If you composed the
content yourself (a summary, a draft), include the sent text verbatim in your reply.
Ask first only when the recipient or content is genuinely ambiguous.
- Messages compose field is labeled "Message", NOT "iMessage" — old message
bubbles carry "iMessage" in their labels; searching for it taps the wrong thing.
send_message/read_messages handle Messages resuming mid-thread, find the
thread via Messages SEARCH (type the name, tap the Conversations result), and
verify the opened thread's header before acting — a wrong or unverifiable
match raises instead of guessing.
- MCP alternative: the
sidetap MCP server is registered at user scope on
this machine — new sessions get all helpers as native mcp__sidetap__*
tools (no Python piping). Prefer those tools when they are available.
- Anything fails to connect →
./phone-harness.cmd doctor from the repo root.
Never guess at connection problems. Common: free-Apple-ID signing expires every
7 days → phone-harness fix-input, then the USER clicks Start in Sideloadly.
- Phone must be unlocked for bring-up;
unlock() types PHONE_PASSCODE from .env if set.
- Human-watchable live viewer: http://127.0.0.1:8770 (start with
python launch.py).