ios-agent について
ios-agent は XCTest で iOS シミュレーターや実機の iPhone を操作します。デバイス操作やアプリのフロー確認が必要な iOS 開発者やエージェント開発者向けで、各操作後の画面を返し、送信や支払いなどには承認を求めます。
主な機能
- 01シミュレーターと実機の iPhone を操作
- 02操作ごとに画面状態を返却
- 03要素参照でコントロールを特定
- 04リスクのある操作に事前承認
- 05ターミナル、Python ライブラリ、MCP に対応
活用シーン
- 01実行中アプリの変更を検証
- 02エージェントで iOS 設定を変更
- 03アプリ内フローをテスト
- 04エージェントが見る画面を手動確認
MCP 対応
31 個のツール、5 個のリソース、ios_operator プロンプトを備えた MCP サーバーを提供します。stdio または HTTP で MCP クライアントから接続できます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
uvx ios-mcpIOS_MCP_POLICY__CONFIRM_DESTRUCTIVEAsk before an action that sends, deletes, or pays. Leave it on for a device carrying real accounts.IOS_MCP_POLICY__ENABLEDThe policy gate as a whole: approval, app blocklist, redaction, audit.IOS_MCP_WDA__BUNDLE_IDBundle id of the WebDriverAgent runner. Free Apple IDs cannot sign com.facebook.*, so device builds need their own.IOS_MCP_SIMULATOR__SHOW_WINDOWBring Simulator.app to the front when booting, so a person can watch the agent drive. Set false in CI.ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
31掲載時のツール一覧 · 2 週間前に更新
このツールにパラメーターはありません。
詳細
ios-agent
AI エージェントに iPhone を使わせましょう。実行するすべての手順を確認します。
ios-agent を使うと、AI エージェントはケーブルまたは Wi-Fi 経由で、iOS Simulator や実際の iPhone を操作できます。すべての操作で、その結果の画面が返されるため、エージェントはタップが反応しなかったときや、入力したテキストが反映されなかったときに判断できます。画面の情報量は数万トークンではなく、数百トークンです。送信、支払い、削除、または他の人への連絡を行う操作は、必ず事前に確認を求めます。Apple の公開 API のみを使用するため、Xcode をアップデートしても動作しなくなることはありません。ターミナル、Claude Code、任意の MCP クライアントから、または Python ライブラリとして利用できます。

1 つの目標を、最初から最後まで 2.5 倍速で実行します。エージェントは Maps へのディープリンクで車の所要時間を確認し、次にタップして徒歩と公共交通機関の情報を表示し、スクロールして詳細を読み取ります。4 回の操作、1 回の観察、2,767 デバイストークン、実時間で 49.8 秒です。ターミナルにはエージェント自身の操作記録が表示され、エージェントが操作しているのは iOS Simulator です。
uv sync && uv run ios-agent quickstart
quickstart はツールチェーンを確認し、手間に見合う簡単な修復方法を提示します。WebDriverAgent が見つからない場合はビルドし(約 20 秒、初回のみ)、手動モードを起動します。手動モードではデバイスを自分で操作でき、API キーは必要ありません。エージェント自体を使う場合は、次のようにします。
uv run ios-agent "turn on bold text"
目次
- できること · 機能 · 対象ユーザー
- 要件 · セットアップ
- ターミナルアプリ · MCP 経由で独自のエージェントを接続する
- モデルに表示される内容 · 安全性
- 実機での測定結果
- この設計にした理由 · 公開 API のみを使う理由
- トレース · 開発 · コントリビューション
できること
| 目的 | 始め方 |
|---|---|
| エージェントに目標を与えて、その作業を見守る | uv run ios-agent "turn on bold text"。手順はクイックスタートで説明しています |
| 自分のアプリに加えた変更が動作するか確認する | docs/check-your-app.md |
| Claude Code、Cursor、または任意の MCP クライアントにデバイスを操作させる | ios-mcp serve と、.mcp.jsonへのエントリー 1 つ |
| ケーブルまたは Wi-Fi 経由で自分の iPhone に接続して実行する | docs/real-device-setup.mdに従い、その後 uv run ios-agent --pick "..." を実行します |
| API キーなしでデバイスを自分で操作し、エージェントに見える内容を確認する | uv run ios-agent manual |
| プロトコルを介さずに、その上に独自のエージェントを構築する | await run_goal(session, "...") または IosSession を直接使います。docs/library.mdを参照してください |
よくある目標としては、実行中のアプリで直前の変更が動作するかの確認、設定の変更、API のないアプリからの回答の読み取り、テストスイートに渡せないスマートフォンでの一連の操作などがあります。 docs/use-cases.mdには、現在できることと、それぞれの根拠、まだサポートされていないことが記載されています。
機能
操作がうまくいかなかったことを検知します
スマートフォン向けエージェントツールで最もよくある不満は、何も起きていないのに操作が成功したと報告されることです。このツールでは:
- すべての操作が、
screen_changedとともに操作後の画面を返します。 そのため、タップしても何も起きなかったことがすぐに分かります。実機の iPhone でも、シミュレーターだけでなく、本当に何も起きなかった場合はscreen_changed=Falseを返します。 - 入力したテキストはフィールドから読み返します。 テキストが入力されなかった場合、入力済みと報告するのではなく、フィールドに表示されている内容とともに
ok: falseを返します。 - エージェントが指定するのは要素名で、座標ではありません。
e2のような ref を渡すと、画面が動いた場合でもホストが6段階の照合を通じて同一の要素を再特定し、ref が別の要素を指すようになっていれば操作を拒否します。 - スイッチは切り替えるのではなく、状態を指定します。 スイッチがすでにオンのときに
onを指定しても、オフにはなりません。 - 安全に再試行できます。 冪等性キーにより、エージェントフレームワークがステップを再実行しても、Send を二度タップすることはありません。
ケーブルまたは Wi-Fi 経由で実機の iPhone でも動作します
- iOS 26.6 を搭載した iPhone 17 Pro Max で、USB と Wi-Fi の両方を使い、シミュレーターと同じ操作数で動作することを確認済みです。
ios-agent doctorはツールチェーン、ランタイム、トンネル、ランナーの署名の有効期限を確認し、それぞれの問題に対する解決策を提示します。- デバイスランナーは、SIGKILL を含め、起動元のプロセスとともに停止するため、クラッシュした実行がスマートフォンを占有し続けることはありません。
ios-mcp resetで、その他の残留物も削除できます。 - デバイス選択画面では実機が自動選択されず、
/deviceでセッション中にデバイスを切り替えられます。
トークンを節約できます
- 画面はアクセシビリティ XML ではなく、コンパクトなダイジェストとして返されます。実際のサードパーティ製アプリ画面では生のノード251個を12個の要素に、13種類のゴールデンフローでは1ステップあたり50~474トークンに圧縮します。
- 次の画面が似ている場合、操作結果として変更箇所だけを返します。
- 300行の連絡先リストは、438トークンの1回の観測で返されます。
ios_findはダイジェストから漏れた要素をフルツリーから検索するため、圧縮によって操作対象が完全に見えなくなることはありません。- 同梱のエージェントが画面を確認するのは1回の実行につき1回です。すべての操作がすでに操作後の画面を返すためです。
リスクのある操作の前に確認を求めます
- Send、Pay、Buy、Delete、Confirm、Sign Out、および他の人に関わる操作(Like、Follow、Share、Message)を実行する前に承認が必要です。確認できる相手がいない場合、操作は拒否されます。
- パスワードは Mac のキーチェーンから取得し、そのままデバイスに送信します。プロンプトや監査ログに入ることはなく、入力後はセッションが返すすべての画面で、パスワードが
[secret]に置き換えられます。 - カード番号とメールアドレスは、クライアントが画面を確認する前にマスキングされ、すべての操作がエクスポート可能な監査ログに記録されます。
Xcode が更新されても使い続けられます
すべての操作は、Apple が UI テスト向けに提供するフレームワークである XCTest を経由します。プライベートフレームワークを使った高速な経路は実測のうえ採用を見送りました。それらの経路を使う複数のツールは、Xcode 27 で動作しなくなりました。詳しくはApple の公開 API のみを使う理由をご覧ください。
3つの利用方法に対応し、あらゆるモデルを使えます
- ターミナルアプリ。モデルの推論を読み取っている画面と並べてストリーミング表示し、操作数、トークン数、コストの増加も画面上で確認できます。
- MCP サーバー:31個のツール、5個のリソース、
ios_operatorプロンプトを、stdio または HTTP 経由で利用できます。ツールリファレンスをご覧ください。 - Python ライブラリの
IosSession。サーバーとエージェントの両方がこのライブラリを使うため、独自のエージェントではプロトコルを省略できます。 - Anthropic、OpenAI、Azure OpenAI、Gemini、Vertex AI、Bedrock、Groq、Mistral、またはローカルの Ollama モデルを、2つの環境変数で切り替えられます。
実測済みで、数値を再現できます
エージェントの実行39回すべてが成功し、手書きのオラクルが必要とするアクションの1.14倍で、合計1.50ドルでした。ゴールデンフローではトークン数、所要時間、各要素の検出方法を記録し、記録された理由なく無料の系列が変化するとCIが失敗します。実機での測定をご覧ください。
対応していないこと
- Androidには対応していません。他のプラットフォーム、ビルド、プロファイリングにも対応していません。
- **FlutterのキャンバスとWebViewには読み取り可能なツリーがありません。**ダイジェストでその旨を伝え、空の画面を返す代わりにスクリーンショットを示します。ADR 0007をご覧ください。
- 承認ゲートは、画面に埋め込まれた指示への防御策ではありません。ADR 0013をご覧ください。
- 実機のiPhoneには署名済みランナーが必要で、無料のApple IDのプロファイルは7日間有効です。
対象ユーザー
- Claude Code、Cursor、または任意のMCPクライアントからiOSを操作する必要がある、エージェントを構築する人。
- シミュレーターまたは自分のiPhoneでコーディングエージェントに変更を確認させたい、iOS開発者。
- エージェントエンジニアリングを学ぶ人。評価ハーネスと意思決定記録では、測定した内容と、数値に基づいて採用しなかった案を確認できます。
Android、React Nativeのプロファイリング、Xcodeビルド、または多数のデバイスでの並列実行が必要な場合は、別のツールが適しています。ドキュメントサイトに率直な比較があります。
要件
| 対象 | 必要なもの |
|---|---|
| シミュレーター | macOS、Xcode 16.3以降、iOSランタイム、Python 3.12以降 |
| 実機のiPhone | 上記に加えて、go-ios、デベロッパーモード、署名ID。docs/real-device-setup.mdに従ってください。シミュレーターより手順が多く、バグのように見えても問題のない手順がいくつかあります |
Xcodeにはシミュレーターランタイムが含まれていません。xcrun simctl list runtimesの結果が空の場合は、xcodebuild -downloadPlatform iOSを実行するとランタイムがダウンロードされます(約8 GB)。ランタイムはインストール済みでもシミュレーターが作成されていない場合は、ios-agentが作成を提案します。この作業は約1秒で完了します。
セットアップ
uv sync
./scripts/prepare_wda.sh simulator # builds WebDriverAgent, once
uv run ios-agent doctor # says exactly what is still missing
問題が起きたとき、最初に実行するのがdoctorです。ツールチェーン、シミュレーターランタイム、トンネル、WebDriverAgentの署名の有効期限、モデルを確認し、接続エラーとして後から表面化させるのではなく、失敗ごとに対処方法を返します。
アプリはデバイスに触れる前に同じ確認を実行するため、セットアップされていないマシンには、シミュレーターの起動を待たせることなく、約1秒でその旨を知らせます。
ターミナルアプリ
uv run ios-agent # open it, decide later
uv run ios-agent "turn on bold text" # give it a goal
uv run ios-agent --pick "turn wi-fi off" # choose the device from a list
uv run ios-agent manual # drive it by hand, no model needed
uv run ios-agent devices # what is reachable

モデルの推論を届き次第ストリーミング表示し、モデルが読み取っているダイジェストを横に表示します。また、アクション数、観測数、デバイストークン数、コストが増えていく様子を画面に表示します。
/ | コマンドメニュー。入力に応じて絞り込みます |
/device · ctrl+o | セッション中にiPhoneまたはシミュレーターを切り替えます |
esc | 次のステップで停止し、完全なレポートを表示します。もう一度押すと中止します |
ctrl+r · ctrl+s | 画面を再読み込み · 監査記録を保存 |
| ドラッグして選択 | キー操作なしで選択範囲をクリップボードにコピーします |
--inline | プロンプトの下にある短い領域で実行します |
--no-tui | パイプ向けのプレーンな行表示 |
**manualモードではAPIキーは不要です。**エージェントの9つの動詞を手動で操作でき、これまで適用したことのないアプリの認識をデバッグする最速の方法です。
フロントエンドには、議論ではなくアサーションで検証されるルールが1つあります。実行の監視によってコストが変わってはなりません。tests/tui/test_cost.pyでは、同じタスクをラップありとなしで実行し、すべてのカウンターが等しいことを比較します。
モデルの選択
プロバイダーは設定であり、依存関係ではありません。ループは
LangChain の init_chat_model を通じて構築されるため、切り替えに必要なのは
環境変数 2 つと追加の依存関係です。
uv sync --extra openai
IOS_AGENT_PROVIDER=openai IOS_AGENT_MODEL=gpt-6.1-sol uv run ios-agent "..."
Anthropic、OpenAI、Azure OpenAI、Gemini、Vertex AI、Bedrock、Groq、Mistral、および ローカルの Ollama モデルがすべてサポートされています。agent/README.md をご覧ください。
MCP 経由で独自のエージェントを接続する
31 個のツールと 5 個のリソースを、stdio または HTTP 経由で利用できます。
クローンせずに PyPI から利用できます。WebDriverAgent を一度ビルドして
~/Library/Application Support/ios-mcp/wda に配置し、任意のクライアントから uvx を指定します。
uvx ios-mcp prepare-wda simulator # about 20 seconds, once
uvx ios-mcp doctor # says what, if anything, is missing
{ "mcpServers": { "ios": { "command": "uvx", "args": ["ios-mcp"] } } }
クローンから利用する場合、以降の README ではこの方法を前提とします。すべてのクライアントで、同じ方法で起動します:uv run --directory <path to this clone> ios-mcp serve。
絶対パスを使用してください。デスクトップアプリはプロジェクトディレクトリから起動されません。
クローン内のビルドは vendor/wda から見つかります。
Claude Code。 このリポジトリ内では、.mcp.json を通じてすでに設定されています。別の場所から利用する場合:
claude mcp add ios -s user -- uv run --directory /path/to/ios-agent ios-mcp serve
Cursor(~/.cursor/mcp.json またはプロジェクト内の .cursor/mcp.json)と
Claude Desktop(~/Library/Application Support/Claude/claude_desktop_config.json)では、同じブロックを使用します:
{
"mcpServers": {
"ios": {
"command": "uv",
"args": ["run", "--directory", "/path/to/ios-agent", "ios-mcp", "serve"]
}
}
}
VS Code(.vscode/mcp.json)では、キー名が servers になります:
{
"servers": {
"ios": {
"type": "stdio",
"command": "uv",
"args": ["run", "--directory", "/path/to/ios-agent", "ios-mcp", "serve"]
}
}
}
MCP のエリシテーションに対応しているクライアントは、承認ゲートで停止される操作の前に、直接ユーザーに確認します。対応していないクライアントでは、再試行時に渡すための署名とともに判断が返されます。承認とシークレットをご覧ください。
あとは、実行したいことを自然言語で依頼するだけです。サーバーには ios_operator
プロンプトが付属しており、観察/操作/検証のループを説明するため、クライアント側で一から考える必要はありません。stdio ではなく HTTP を使用するクライアントの場合、
ios-mcp serve --transport http --port 8765 はこのマシン上だけでサービスを提供します。認証機能はないため、各リクエストの送信元を確認し、--allow-remote が指定されない限り、別のアドレスにはバインドしません。脅威モデルをご覧ください。
または、プロトコルを使わずにライブラリをインポートできます:
outcome = await run_goal(session, "turn on bold text")
モデルが見るもの
screen: com.apple.Preferences / "Display & Text Size" fp=3872280e
e1 button "Accessibility" id=BackButton @(38,84)
e2 switch "Bold Text" =0 id=ENHANCE_TEXT_LEGIBILITY @(336,161)
e3 button "Larger Text, Off" id=LARGER_TEXT @(190,216)
実機シミュレーターで 13 個のゴールデンフローを測定した結果、各フローの平均はステップあたり 50~474 トークンでした。そのうち 1 つは、他のツールではタイムアウトが報告される、実際の 300 行のリストです。Contacts に 300 人を登録した状態で、UIKit は画面上の行だけを実体化するため、1 回の観察では 166 個の生ノードが返され、1.35 秒で 438 トークンでした。
エージェントは e2 をアクションに渡します。XPath を記述することも、座標を推測することもありません。画面の移動によって参照が古くなった場合でも、ホストは失敗するのではなく、識別情報を使って同じ要素を再度見つけます。
安全性
誰かの実際のスマートフォンを自動操作することは、テスト自動化ではありません。以下はデフォルトで有効です。
- 送信、支払い、購入、削除、確認、サインアウトに該当する操作には、MCP のエリシテーション、または外部のヒューマン・イン・ザ・ループ層が応答できる
action_requires_approvalエラーを通じて、実行前に承認が必要です。承認は1回の操作に限られます。「送信」を承認しても「削除」は承認されません。 - 他の人に届く操作を行うボタンを押す場合も同様です。「いいね」、フォロー、コメント、返信、共有、招待、メッセージ、投稿が該当します。タップを取り消しても、通知は取り消されません。docs/adr/0014をご覧ください。
- 承認者がいない場合、実行は無人で行われ、ゲートが確認するすべての操作が拒否されます。回答できない質問は同意ではないためです。
ios_type_secretはホストのキーチェーンから値を読み取り、デバイスへ直接送信します。プロンプトや監査証跡には一切記録されず、入力後に返されるすべての画面で[secret]としてマスキングされます。パスワードフィールドを使用してください。スクリーンショットはマスキングされません。4111 1111 1111 1111のように区切られた形式を含むカード番号とメールアドレスは、セッション内で秘匿化されます。そのため、MCP サーバー、同梱のエージェント、ターミナルアプリはいずれも秘匿化された画面を受け取ります。- 失敗が繰り返された場合、またはループが検出された場合、セッションは停止します。
- デバイス選択画面で物理的なスマートフォンがあらかじめ選択されることはありません。物理端末に接続するには、必ずキーを押す必要があります。
これらの対策で保護できないものについては、SAFETY.mdと脅威モデルをご覧ください。すべてのデフォルト値は、IOS_MCP_*環境変数、.env、または任意のios-mcp.tomlで設定できます。優先順位はこの順です。全項目の一覧を確認するには、.env.exampleを.envにコピーしてください。
実機での測定結果
評価ハーネスはエージェントより先に作成されました。これが、これらの数値が存在する唯一の理由です。直近の全項目測定は、gpt-5.6-solで13タスクを各3回実行したものです。
| 成功 | 39/39 |
| 観測 | 41。オラクルの下限39に対して |
| アクション | 123。手書きのオラクルの1.14倍 |
| モデルのターン数 | 216 |
| 障害 | 認識6、ポリシー6、モデル0 |
| 拒否、使用不能な実行 | 0、0 |
| コスト | 6分39秒で$1.50 |
コストは、入力100万トークンあたり$4、出力100万トークンあたり$20で算出しています。最初は$1.87として公開されましたが、これはモデルの横に設定された価格を無視するハーネスが、Claude Opusの料金で計算したためです。トークン数と各条件間の比率は、どれも影響を受けていません。
その後、テストスイートは19タスクに増え、そのうち4つではエージェントが読む画面に指示を埋め込んでいます。docs/adr/0015のための実行では、同じモデルで各タスクを3回実行し、オラクルの1.30倍のアクション数で57回中55回成功しました。コストは$2.61で、埋め込まれた指示には12回の試行すべてで従いませんでした。この実行で記録したのは成功数、アクション数、コストで、観測数や障害数は記録していません。そのため、これらについては上の表を参照してください。
実行あたりの観測数は、全タスクを通じた差が2以内で、おおむね1回です。Apple製ではないアプリの2タスクも含まれます。これが下限です。各アクションの応答には、そのアクションによって生成された画面がすでに含まれているため、この下限が保たれます。
ios_findによって、エージェントがそこから作られたダイジェストだけでなく、生のアクセシビリティツリーも読めるようになるまでは、アクション数はオラクルの1.25倍でした。改善は、これを呼び出す2つのタスクだけで生じています。ターン数は43から27、アクション数は10から3になりました。一方、呼び出さない11タスクでは、ターン数は195から189、アクション数は125から120でした。ただし、無料ではありません。10個目の動詞を追加すると、使用されるかどうかにかかわらず、すべての実行のすべてのターンでプロンプトが約190トークン増えます。詳細はdocs/adr/0011をご覧ください。11個目の動詞がないのはそのためです。
モデルのターン数も測定しています。これが残された唯一の軸だったためです。複数のアクションを1つのターンにまとめても、デバイス上の作業はまったく変わりません。そうするよう促しても、モデルは一度もまとめませんでした。そのため、docs/adr/0010ではこの案を退け、実行されていたはずの経路を制限するガードを維持しています。
実機の iOS で検証済み。物理 iPhone も含みます
Tier 1 はスクリプト化されたインプロセスデバイスに対して実行されるため、その数値は偽物についての主張です。3 つすべての Tier で同じ目標「Bold Text をオンにする」を実行した結果は次のとおりです。
| アクション | 観測 | ダイジェスト | |
|---|---|---|---|
| スクリプト化された偽物 | 3 | 1 | 該当なし |
| iOS 27.0 シミュレーター | 4 | 1 | 166 個の生ノード → 15 個の要素、272 トークン |
| iPhone、iOS 26.6、Wi-Fi | 3 | 1 | 140 個の生ノード → 15 個の要素、243 トークン |
設計の根拠となる数値は、すべての Tier で観測が 1 回であることです。iPhone では 48.6 秒かかりましたが、シミュレーターでは数秒でした。シミュレーターの行は、26.5 ランタイムの削除後に iOS 27.0 で再測定したものです。アクションが 1 回多いのは、モデルがより長い経路を選んだためであり、その Tier にない機能が原因ではありません。デバイスランナーのプロビジョニングプロファイルの有効期限が切れているため、iPhone の行は引き続き 26.6 と表示されています。そのため、現在は Tier 3 を再実行できません。
スイッチの変更は、その場所に移動して value="1" を読み取り、エージェントの申告とは独立して確認したうえで、元に戻しました。
最も重要なのは、実機で実際に何もしなかった場合も、screen_changed=False が報告されることです。 物理デバイスで、安定した状態のスナップショット間でフィンガープリントが変化していたなら、すべてのシミュレーターと偽物のテストが成功し続ける一方で、検証ステップはハードウェア上ではひそかに機能しなくなっていたでしょう。
ハードウェアの使用は、device マーカーと IOS_MCP_ALLOW_DEVICE=1 の両方を指定する必要があります。ハードウェアが接続されているからといって、その設定を変更することに同意したことにはならないためです。
このような設計にした理由
200 行のリストに対する WebDriverAgent の生のページソースは、約 37,000 トークン に達します。タップのたびにそれを再読み込みすると、わずか数ステップでコンテキストウィンドウを使い果たします。そこから 4 つの判断が導かれます。これらが設計のすべてです。
| 認識は予算を考慮します | 実際のサードパーティ製画面では 251 個の生ノードを 12 個の要素に削減し、1 ステップあたり 50~472 トークンです |
| アクションは、その結果の画面を返します | ラウンドトリップを半減させ、画面が類似している場合は 差分 を返します |
| 解決処理はホスト上で実行します | 6 つの Tier があり、再試行にかかるモデルのトークン数はゼロですが、ラウンドトリップには 1 ターン分まるごとかかります |
| ゲートは実行後ではなく、実行前に確認します | そのため、返答に意味が保たれます |
リポジトリ内のその他のものは、すべてこれらを前提にしています。
Apple の公開 API のみを使用する理由
ここにあるすべての機能は、Apple が UI テスト用に提供しているフレームワーク XCTest を、WebDriverAgent 経由で使ってデバイスにアクセスします。より高速に見えた 2 つの経路を計測しましたが、採用しませんでした。
| 経路 | 計測内容 | |
|---|---|---|
| 署名済みランナーが不要な、Xcode の Accessibility Inspector サービス | フォーカスを一度に 1 要素ずつ移動して読み取るため、1 要素あたり 60~80 ms かかります。Settings のルートでは 3.3~4.4 秒で、WebDriverAgent の 3.7 秒に対して大差はなく、フレームも取得できず、読み取り中は画面がスクロールします | ADR 0016 |
| CoreSimulator の非公開アクセシビリティフレームワーク。idb と AXe が使う経路 | WebDriverAgent より 10~23% 高速ですが、目標とする 50% には届きません。また、Xcode 27 では、起動時の準備に結局 XCTest セッションが開始されます | ADR 0017 |
どちらも、そのわずかな高速化に見合うリスクはありませんでした。そのリスクは仮説ではありません。Xcode 27 では Simulator.app が Device Hub に置き換えられ、SimulatorKit.framework が移動したため、非公開の部品にアクセスするツールが実際に動かなくなりました。
- MobileBuildMCP(当時は XcodeBuildMCP)#453:同梱の AXe が、以前の場所にある SimulatorKit を探していました。
- Argent #406:Device Hub が Simulator.app に置き換わるとデバイスの起動に失敗し、#465:macOS 27 でシミュレーターサーバーが SimulatorKit を読み込めませんでした。
- MobileBuildMCP #535:System Events が Device Hub に接続できないため、キーボードツールが何もせずに終了します。
同じ名称変更の影響は、このプロジェクトにも 1 か所ありました。シミュレーターのウィンドウを表示するためだけに使っていた open -a Simulator が動かなくなりました。現在はバンドル ID で Device Hub を開きます。自動化の経路に変更はありません。アクセスするのはすべて、Apple が文書化し、リリースをまたいで維持しているインターフェースです。
公開 API を使えば問題がなくなるわけではありません。WebDriverAgent は Xcode に密接に追従するため、無条件に追従するのではなく、ビルドを固定し、scripts/prepare_wda.sh で再ビルドします。iPhone には署名済みランナーが必要です。無料の Apple ID ではプロビジョニングプロファイルの有効期間は 7 日間で、Wi-Fi 経由の起動にも xcodebuild を使います。ios-mcp doctor はツールチェーン、ランナービルド、デバイス、トンネルを確認し、修正が必要な項目を提示します。
自動化をスマートフォンではなくホストで実行する理由
iOS アプリは、実行中のデバイス上にある他のアプリを自動化できません。サンドボックスによりプロセス間アクセスがブロックされ、ユーザーが許可しても、サンドボックス内のアプリでは Accessibility API を利用できません。XCUIAutomation は testmanagerd が起動する XCTest ランナー内でのみ実行され、そのランナーはホストから操作されます。そのため、エンジンは Mac 上で動作させる必要があり、このプロジェクトには iOS アプリがありません。
トレース
すべてのエージェント実行は、OpenTelemetry のスパンとして記録できます。実行全体、各グラフノード、モデル呼び出し、ツール呼び出しごとにスパンが作成され、タスク ID、ステップ、ツール名、入出力トークン数、コスト、解像度の段階、検証結果が記録されます。デフォルトでは無効で、無効時には何も構築されません。
uv sync --extra tracing
IOS_AGENT_TRACING=otel uv run ios-agent "turn on bold text" # any OTLP backend
IOS_AGENT_TRACING=langsmith uv run ios-agent "turn on bold text" # needs LANGSMITH_API_KEY
otel は標準の OTEL_EXPORTER_OTLP_* 環境変数を読み取るため、Phoenix、Langfuse、またはコレクターを 1 つのエンドポイントで接続できます。Phoenix でローカルにトレースを表示するには、次のようにします。
uvx --from arize-phoenix phoenix serve # UI and OTLP on http://localhost:6006
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:6006 IOS_AGENT_TRACING=otel \
uv run ios-agent "turn on bold text"
スパンに記録されるのは件数と名前であり、プロンプト、画面、返信は記録されません。また、すべてのスパンは出力時にセッションのリダクターを通過するため、ios_type_secret で入力した値がトレースに含まれることはありません。テストでは値を設定し、エクスポートされたスパンを読み戻して確認します。エージェントタスク 22 件をトレース無効・有効の両方でリプレイして計測したところ、トークン数に変化はなく、スパンあたり 0.06 ms 追加されました。docs/adr/0023 を参照してください。また、uv run pytest tests/evals/agent --trace-agent otel を実行すると、評価の実行をトレースし、各トレース ID を結果とともに記録できます。
開発
uv run pytest tests/unit # 856 tests, no device, no model
uv run pytest tests/tui # 234 tests, the terminal front end
uv run pytest tests/integration # 18 simulator + 3 device tests
uv run pytest tests/evals -s # golden flows, with cost per flow
uv run ruff check . && uv run mypy ios_mcp agent/ios_agent tui/ios_tui
評価スイートは品質ゲートです。フローごとにトークン数、実時間、アクション数、解決ティアの分布を報告します。exact から text-fuzzy へずれていくことは、フローが不安定になり始める兆候です。エージェントのタスクではさらに、手書きのオラクルに必要なアクション数である アクション下限 を宣言し、そのオラクルに対して検証することで、目標だけに終わることを防ぎます。また、同じルートで完璧なバッチ処理を行った場合に必要となるモデル呼び出し回数である ターン下限 もあります。ターン下限は宣言するのではなく導出します。batch.simulate_turns はオラクルが実際に行った処理をたどり、バッチガードが停止する箇所で分割するため、横に書かれた数値ではなく、ガードそのものを測定します。失敗の原因も特定します。レポートでは、単に何かが失敗したと示すのではなく、デバイス、認識処理、モデル、ポリシーゲートのどれが原因だったかを示します。
これらの数値は、測定した実行ごとに1行をコミットする tests/evals/history.jsonl に時系列で保存されます。CI では、コストのかからない唯一のシリーズ(スクリプト化されたデバイスに対するオラクル)を実行し、新しい行とともにコミットされないまま数値が変化した場合は失敗します。手動で実行したスライスも同じファイルに記録します。
python scripts/eval_trend.py show --suite agent-oracle
python scripts/eval_trend.py append .artifacts/evals/agent-s5.json --suite agent-model
検証対象となる数値は、モデルもネットワークも時計も関与しない固定ルートのカウント値なので、範囲ではなく完全一致を要求します。docs/adr/0009 を参照してください。
uv run python scripts/tui_screenshot.py # render the front end to .artifacts
テストスイートに合格しても、ターミナルアプリの見た目については何もわかりません。このスクリプトは、アサーションでは検出できなかった表示バグを8件検出しました。
1つの uv ワークスペースに3つのディストリビューションがあり、依存関係は一方向にのみ向いています。ARCHITECTURE.md を参照してください。
ios-tui terminal front end depends on ios-agent, ios-mcp
ios-agent goal-directed agent depends on ios-mcp
ios-mcp library + MCP server depends on neither
貢献
Issue とプルリクエストを歓迎します。CONTRIBUTING.md では、セットアップ、開発サイクル、スタイル上の慣習ではなく重要な5つの規約について説明しています。CI では Linux と macOS で ruff、mypy、およびオフラインテスト1061件を実行します。
ライセンス
MIT。LICENSE を参照してください。
mcp-name: io.github.emazaheri/ios-agent
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。