iphone-use について
iphone-use は、独自の XCTest ランナーで実機 iPhone を操作するスキルです。Mac の画面やカーソルには触れず、アプリ操作、保存済みフローの実行、操作の検証を支援します。
主な機能
- 01XCTest による実機 iPhone の操作
- 02保存済みフローと Jev の端末タスク実行
- 03UI 操作の一括実行と結果検証
- 04画面要素とホーム画面レイアウトの確認
- 05端末の占有状態と再接続の管理
活用シーン
- 01iPhone アプリで複数手順のタスクを自動化
- 02アプリの状態確認と表示中のコントロール操作
- 03繰り返し使うフローの再利用や作成
MCP 対応
`phone_*` ツールを備えた MCP サーバーが付属し、端末の読み取りや操作、フロー実行、セッション管理を行います。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ leeguooooo/ iphone-use --skill iphone-useインストール後、Claude Code で/iphone-useを入力して読み込まれたか確認します。
詳細
iphone-use — 実機の iPhone を操作
iphone-use デーモンは、独自の XCTest デバイスランナーを通じて実機の iPhone を操作します。このランナーは v0.14.0 で WebDriverAgent に置き換わりました。iPhone Mirroring は使用しません。HTTP インターフェースは WebDriverAgent と互換性があります(/wda/... ルート)。Mac の画面やカーソルには一切触れません。以下の HTTP API または同梱の MCP サーバー(phone_* ツール)を使用してください。どちらも同じ手順で操作できます。詳細についてはリファレンスをご覧ください。curl -s -H "$AUTH" "$HOST/agent/reference" で、実行中のデーモンに対応する内容を取得できます(以下の各セクション名は見出しに対応しています。また、docs/agent-reference.md もご覧ください)。
HOST="${IPHONE_USE_URL:-http://127.0.0.1:44321}"
AUTH="Authorization: Bearer $IPHONE_USE_TOKEN" # daemon password or IPHONE_USE_AGENT_TOKEN
MUTATION="X-Phone-Control: 1" # required on every state-changing POST
OWNER="X-Phone-Owner: <your-session-name>" # MCP sends this for you
curl -s -H "$AUTH" "$HOST/agent/status" # probe first; on failure stop and report
ループ
-
確認します。 ユーザーが電話を指定した場合、または複数の電話がインストールされている場合は、まず
phone_instancesを使用し、その後、すべての MCP 読み取り/操作でそのinstanceを渡します。instanceを省略すると、設定済みデーモンのみが報告され、すべての電話は報告されません。GET /agent/status/phone_status。画面の読み取りと UI 入力にはdrivable:trueが必要です。それ以外の場合はhintを報告し、これらの操作を中止します。 ペアリング済みのメタデータ読み取り(phone_home_layout、phone_apps)とキャッシュ済みの機能検出には、 Runner の実行は必要ありません。再接続したりホーム画面を開いたりせずに使用し、 プライベート保留による拒否があれば従います。再接続(phone_reconnect/POST /agent/mode {"mode":"agent"})は、 ユーザーの現在のタスクで電話が必要な場合に限り、一度だけ行います。ヘルスチェックのためには行いません。 Runner を起動するたびに、オペレーターがパスコードを入力しなければならない場合があります。ownerが別のセッションであれば、操作しないでください。→ 参考:電話の状態と復旧 -
まず保存済みのフローを探します。 アプリに入ると、
launch_appの応答、 または新しく入ったアプリでの最初の/agent/elements読み取りに、そのアプリの保存済みフローを一覧表示するregistryブロックが含まれます。タスクに合うフローがあれば、phone_flow_runで実行します。数十回の呼び出しが1回で済み、スクリーンショットも不要です。phone_flow_list/iphone-use-mcp flow list --app <bundle>ですべてを表示できます。 実行前にriskとcompatを確認してください。→ 参考:フロー"$HOME/Applications/iPhoneUse.app/Contents/MacOS/iphone-use-mcp" flow run system/spotlight-search --input query=Health -
フローがない場合は、明確な目標を Jev に渡します。
phone_jev_run/iphone-use-mcp jev run --goal "…" [--app <bundle>]は、高速なオンデバイスエージェントを実行します(TypeSafe の Jev が各ステップを選択し、1ステップ約1~2秒で、モデルのターンはありません)。ステップ履歴とともにdone/blockedを返します。送信、支払い、削除を行う目標は、先にユーザーの確認を取ってください。最後の画面は自分で確認します。 -
それ以外の場合は、タップではなくまとめて操作します。 呼び出しのたびに1ターンを丸ごと消費し、 電話の処理には約0.5秒かかります。
launch_appでアプリを開き、GET /agent/elementsを一度読み取ります。その後、すでに把握している操作をすべて1つのバッチとして送信します(POST /agent/actions/phone_run_steps、最大24ステップ。画面が変わるたびにwait_forを指定)。observe:true(MCP のデフォルト)を指定すると、バッチ終了時の画面が応答に含まれるため、読み取りを追加せずに次のバッチを判断できます。バッチの間に1分以上考える場合は、phone_holdを使うと Runner が解放されません。単独のタップ(要素+スナップショット、一意のラベル、ロケーター)は、まだ読み取っていない画面を調べるためのものです。3回連続すると、デーモンがその旨を知らせます(batch_hint)。 -
各ステップを後続条件と照合して確認します。 MCP の操作ツールはデフォルトで画面を確認し(HTTP:
?return=delta)、同じ呼び出しで確定した変更を返します。確定後の画面もキャプチャされ、phone_screenshotを呼び出せばすぐに取得できます。テキストだけでは不十分な場合に限り、画面を確認してください。ok:trueは操作が送信されたことを意味するだけで、何かが達成されたという意味ではありません。 → 参考:結果の読み取り -
フローとして保存するか尋ねます。 応答に
flow_suggestionが含まれている場合、またはフローで対応できない複数ステップのタスクを完了した場合は、フローとして保存するかユーザーに尋ねてください。同意があった場合に限り、ドラフトを取得し(phone_flow_draft(save_as=…)/flow draft --out …/GET /agent/flow/draft)、todoリストを修正して検証し、ファイルを一度実行します。公開すると公開 PR が作成されるため、phone_flow_publish(confirm=true)の前にもう一度確認してください。→ 参考:フローの保存
タスクが完了したら、電話を解放します:phone_release_owner /
POST /agent/owner {"release":true}。応答に agent_focus が含まれていた場合、セッション中は電話が「おやすみモード」になっています。そのことをユーザーに伝えてください。解放すると、おやすみモードは解除されます。
基本操作
| 内容 | HTTP /agent/input type | MCP | Flow / バッチステップ kind |
|---|---|---|---|
| アプリのバンドル ID を検索する | GET /agent/apps?query=微信 | phone_apps | — |
| アプリを開く | launch_app bundle(組み込みアプリの場合は name、または app:任意のアプリの正確な名前。候補が複数ある場合は拒否されます) | phone_run_steps ステップ | launch_app(bundle または app) |
| ホームページ、Dock、フォルダを読み取る | GET /agent/home-layout?page=0(すべて表示する場合は page を省略) | phone_home_layout(page?) | — |
| 画面を読み取る | GET /agent/elements | phone_elements | wait_for(application、present、absent) |
| 要素をタップする | tap + element + snapshot | phone_tap_element | tap_locator(永続的) |
| 一意のラベルをタップする | tap + label | phone_tap_label | tap_label |
| ロケーターでタップする | tap_locator + locator | phone_run_steps ステップ | tap_locator |
| 座標をタップする | tap + x,y | phone_tap | tap |
| テキストを入力する | text(フォーカス中のフィールドに入力。最大 20000 文字。text_partially_typed の場合は clear:false を指定して remaining_text のみ送信) | phone_type | type(input は実行時の値を指定) |
| キー | key return… | phone_key | key |
| ホーム/Spotlight | shortcut | phone_shortcut | shortcut |
| スクロール | scroll dy(80 ≈ 画面の 15%、400 ≈ 75%) | phone_scroll(x、y、dx、dy が必須) | scroll(MCP バッチでは x、y、dx、dy が必須) |
| システムアラート | alert button / action | phone_run_steps ステップ | alert |
| アプリからログインを求められた場合 | POST /agent/login(iphone-use auth login --bwu) | phone_login | — |
| スイッチ/スライダー | perform toggle / adjust | phone_run_steps の perform ステップ(要素 + スナップショット) | — |
エッジでホバーする必要があるドラッグでは、drag ステップで x1/y1/x2/y2 の代わりに
points: [{x, y, dwell_ms}, …] を使用できます。現在の画面から 2~16 個のポイントを指定します。
各滞留中も同じ指を押し続けます。duration_ms はセグメントごとの移動時間で、保持・移動・滞留の合計は 10 秒以下です。
Flow の下書きにはパス全体が保持されます。次の操作に進む前に、終了画面を確認してください。
スワイプ、ドラッグ、ピッカー、set_value、スクロールの各バリエーションを含むすべての操作は、リファレンスに記載されています:操作カタログ。
HTTP /agent/input では、省略されたスクロールアンカーと dx にデフォルト値が設定されます。
MCP の phone_scroll と phone_run_steps のスクロールステップでは、4 つのフィールドすべてが必須です。
現在の画面で、スクロール対象領域内の適切な位置に正規化された x/y を指定してください。
その画面で確認した中央アンカーの場合、MCP バッチの形式は次のとおりです。
{"steps":[{"kind":"scroll","x":0.5,"y":0.5,"dx":0.0,"dy":80.0}]}
curl -s -H "$AUTH" -H "$MUTATION" -H "$OWNER" -X POST "$HOST/agent/input" \
-d '{"type":"launch_app","bundle":"com.apple.Health"}' # response may carry `registry`
curl -s -m 40 -H "$AUTH" "$HOST/agent/elements" # give it ≥40s
curl -s -H "$AUTH" -H "$MUTATION" -H "$OWNER" -X POST "$HOST/agent/input?return=delta" \
-d '{"type":"tap","element":3,"snapshot":"<from that read>"}'
curl -s -H "$AUTH" -H "$MUTATION" -H "$OWNER" -X POST "$HOST/agent/actions" -d '{"steps":[
{"kind":"action","action":{"type":"tap_locator","locator":{"label":"资料","kind":"Button"}}},
{"kind":"wait_for","expect":{"present":[{"label":"导出所有健康数据"}]},"timeout_ms":8000}]}'
しないこと → 代わりにすること
| しないこと | 代わりにすること |
|---|---|
| スクリーンショットから推測した座標をタップする | phone_elements を読み取り、要素とスナップショット、またはラベルでタップする |
| 各ステップの後にスクリーンショットを撮る | observe:true / ?return=delta で変更を確認する |
| 次のステップが見えているのに、呼び出しごとに1回タップする | wait_for を使って、phone_run_steps / /agent/actions でまとめて実行する |
秒数を固定して sleep する | present / absent / application を指定して wait_for する |
| システムアラートのボタンをタップする | alert アクション(button または action)を使う |
| スイッチやスライダーをタップする | perform の toggle / adjust を使う |
outcome_unknown または retry_safe:false の後に再送する | まず画面を読み取る。すでにスマートフォン側で操作が実行されている場合があります |
no_progress となったアクションを繰り返す | 画面を読み直してから、別のコントロールを使うか wait_for する |
| 状態確認のために再接続する | phone_status を使い、タスクでスマートフォンが必要な場合にのみ再接続する |
| フォーカスを確認せずに入力する | フォアグラウンドアプリとフォーカスされているフィールドを確認してから入力する |
| 見つからなかったラベルを繰り返す | エラーが提示する「もしかして」のラベルを使うか、読み直す |
タスクのメトリクス:iphone-use metrics / GET /agent/metrics が数えるのは、モデルのターン数ではなく HTTP 呼び出し数です。1つのタスクを正確に数えるには、phone_run_start / phone_run_end で囲みます(HTTP:POST /agent/run)。GET /agent/elements?scope=app(または interactive、focused、changed&since=…)は、同じ読み取り結果のより小さなビューを返します。?image=auto は、ツリーが使えない場合にのみスクリーンショットを追加します。MCP の画面テキストには、最大200行、行テキストは最大32 KiBが含まれます。省略された行があると表示された場合は、query / limit を指定して phone_elements の範囲を絞り込みます。対象が省略されていても、その対象が存在しないとは証明されず、切り詰められたリストも完全であるとは証明されません。表示された #N はスナップショットとともに使用してください。これは元の全ツリーでのインデックスです。画面外または描画されていない行のうち、5ラベルの要約に含まれないものを調べるには、include_hidden: true を指定して phone_elements を呼び出します(テキスト量の上限に収めるため、query / limit と組み合わせてください)。これらの行には hidden と表示されます。操作する前に、スクロールして表示し、もう一度読み取ってください。
→ 参照:タスクのメトリクス、実行、アドバイス
ホーム画面の整理:フォルダーを開いたりページをスワイプしたりする前に、phone_home_layout を使用してください。保存されたページ、Dock、フォルダーの内容、ウィジェットスタックを、アイコンを移動せずに読み取ります。ページ/行/列のインデックスは0始まりです。ページには非表示のページやFocusフィルターが適用されたページも含まれるため、ページのインデックスは表示中のページコントロールの番号とは異なります。位置はアイコンの順序とグリッドの指標から導出されます。サイズが不明な場合、位置は null となり、positions_complete:false になります。これらはピクセル座標ではなく、レイアウトを復元してよいことを意味するものでもありません。ドロップ後に読み直し、ジェスチャーにはライブ要素を使用してください。
フローは、保存されたこれらのステップにすぎません
{
"version": 1,
"name": "New note",
"app": "com.apple.mobilenotes",
"risk": "navigation",
"locale": "zh-CN",
"inputs": { "body": { "type": "string", "description": "note text" } },
"steps": [
{ "kind": "launch_app", "bundle": "com.apple.mobilenotes" },
{ "kind": "wait_for", "expect": { "application": "备忘录" }, "timeout_ms": 15000 },
{ "kind": "tap_locator", "locator": { "label": "新建备忘录", "kind": "Button" } },
{ "kind": "type", "input": "body" }
]
}
フィールドの全リスト、互換性の値、公開とレポートの手順については、参照:フロー をご覧ください。
厳守事項
retry_safe:falseまたはoutcome_unknownの場合は再送しないでください。 すでにスマートフォンで操作が実行されている可能性があります。まず画面を読み取り、テキストの再入力、送信、支払い、削除を確認せずに行わないでください。失敗したフローにも同じルールが適用されます。risk: side_effectおよび破壊的なタップには、対象と入力を明示したユーザーの許可(confirm=true/--confirm)が必要です。 支払い画面や2FA画面を無人で操作しないでください。- テキストは、フォーカスがあるフィールドに入力されます。 誰かがチャット中の場合、入力した言葉はその人のメッセージに入ります。まず前面のアプリとフォーカスのあるフィールドを確認してください。チャットアプリでは Return で送信され、入力テキスト内の
\nも Return になります。ユーザーが改行を希望していない限り、1行だけ送信してください。→ 参考:チャットアプリ:複数行のメッセージ - システムアラートには
alertアクションが必要です。 アラートのボタンをタップすると ACK されますが、多くの場合、何も起こりません。スイッチにはperform toggleが必要です。どちらの場合も、新しいvalueを確認してください。 - スマートフォン1台につきセッションは1つです。
X-Phone-Ownerを送信してください。409 phone_ownedが返された場合は待機し、他のセッションが放棄されたとユーザーが明示しない限り、引き継がないでください。完了したら、リースを解放してください。 - ログインには
phone_login/iphone-use auth login --bwuのみを使用してください。 デーモン内でユーザー自身の保管庫エントリーを入力します。パスワードやコードを自分で尋ねたり、入力したり、繰り返したりしないでください。新規登録、パスワード変更、支払いも行わないでください。Face ID、PIN、コード、またはログインphone_loginでログインを完了できない場合は、停止し、phone_hold(secs, private=true)を取得して、その間に画面が読み取られないようにします。ホストの質問ツールを使ってユーザーに尋ね(ツールがない場合、または現在利用できない場合はプレーンテキストで尋ねます)、返答を待ってからホールドを解除し、画面を読み直してください。→ 参考:本人がサインインする必要がある場合 - 保存や公開を決めるのはユーザーです。 フローを保存する前に確認し、公開またはイシューを登録する前にも再度確認してください。いずれもユーザーの GitHub アカウントを使用します。
タスクで必要なファイルは AirDrop 経由で Mac に転送され、~/Downloads に保存されます(参考:スマートフォンから Mac にファイルを転送する)。
画面が変わった場合(app_changed、バナー、スマートフォンの操作など)は、停止して画面を読み直してから、以前の手順を続けてください。スキルの説明とライブ API の内容が異なる場合は、スキルが古くなっている可能性があります。参考:アップグレード。iphone-use 自体に不具合や分かりにくい点がある場合は、イシューの登録を提案してください(参考:イシューの登録)。
複数指での長押しと復旧可能な観察
同時に長押しするには、phone_run_steps で
{"kind":"multi_longpress","points":[{"x":0.3,"y":0.5},{"x":0.5,"y":0.5},{"x":0.7,"y":0.5}],"duration_ms":1500} を使用してください。
2~5個の異なる正規化座標で指の本数を指定します。長押し時間は1~10000 ms(既定値は1000)です。HTTP アクションの本文では type:"multi_longpress" を使用し、保存済みフローでは kind を使用します。通常の longpress に fingers:3 を指定してはいけません。アプリ固有のジェスチャー認識は実機で確認する必要があります。
Jev は、HTTP 504 runner_source_timeout が発生した場合に限り、サーバー指定の1~60秒の retry_after_secs 後に、観察を1回だけ再試行します。直前のアクションを再実行することはありません。アクション後の観察から復旧できない場合は、status:"blocked"、history、last_successful_action を調べてから、次の操作に進む前に読み直してください。
複数のインストール済みスマートフォン
読み取り専用のローカルインスタンス一覧には phone_instances を使用してください。他の各 phone_* 呼び出しでは instance(例:i13、default)を渡し、そのデーモンを指定します。読み取りとアクションで同じ値を使い続けてください。instance を省略すると、MCP サーバーの起動時設定が使用されます。対象が変更されることはなく、利用できないインスタンスが別のスマートフォンにフォールバックすることもありません。インスタンスを切り替えるためにトークンを読み取ったり、HTTP リクエストを手書きしたりしないでください。検出によって再接続が行われることはありません。通常の所有権、プライバシー、ロック、スマートフォン終了/人への引き継ぎのルールは引き続き適用されます。実行時の選択では、インストール済みのローカル LaunchAgent がサポートされます。シミュレーターのみのレコードは別に扱われます。