Skip to content

Design decisions

Raw WebDriverAgent page source for a 200-row list runs to roughly 37,000 tokens. Re-reading that after every tap exhausts a context window in a handful of steps. Four decisions follow from that, and they are the whole design:

Perception is budget-aware 251 raw nodes to 12 elements on a real third-party screen; 50 to 472 tokens per step
Actions return the screen they produced halves round-trips, and returns a delta when the screen is similar
Resolution runs on the host six tiers, so a retry costs zero model tokens where a round-trip costs a whole turn
The gate asks before acting, not after so the answer still means something

Everything else in the repository is downstream of those.

Everything here reaches the device through XCTest, the framework Apple ships for UI testing, by way of WebDriverAgent. Two routes that looked faster were measured and turned down:

route what it measured
Xcode’s Accessibility Inspector service, which needs no signed runner reads by walking focus one element at a time, 60 to 80 ms each: Settings root in 3.3 to 4.4s against WebDriverAgent’s 3.7s, with no frames, scrolling the screen as it reads ADR 0016
CoreSimulator’s private accessibility framework, the route idb and AXe take 10 to 23% faster than WebDriverAgent against a 50% bar, and on Xcode 27 it starts an XCTest session to bootstrap anyway ADR 0017

Neither saving was worth the exposure, and the exposure is not hypothetical. Xcode 27 replaced Simulator.app with Device Hub and moved SimulatorKit.framework, and tools that reach into those private pieces broke in public:

  • MobileBuildMCP, then named XcodeBuildMCP, #453: its bundled AXe looked for SimulatorKit where it used to be.
  • Argent #406: booting a device failed once Device Hub replaced Simulator.app, and #465: its simulator server could not load SimulatorKit on macOS 27.
  • MobileBuildMCP #535: keyboard tools silently do nothing, because System Events cannot attach to Device Hub.

The same rename reached this project in one place: open -a Simulator, used only to show the simulator’s window, stopped working. It now opens Device Hub by bundle id. The automation path did not change: everything it touches is an interface Apple documents and carries from one release to the next.

Public is not painless. WebDriverAgent tracks Xcode closely, so the build is pinned and rebuilt by scripts/prepare_wda.sh rather than followed blindly. A phone needs a signed runner whose provisioning profile lasts seven days on a free Apple ID, and a Wi-Fi launch still goes through xcodebuild. ios-mcp doctor checks the toolchain, the runner build, the devices and the tunnel, and says what to fix.

Why the automation runs on a host, not on the phone

Section titled “Why the automation runs on a host, not on the phone”

An iOS app cannot automate other apps on the device it runs on. The sandbox blocks cross-process access, and the Accessibility API is unavailable to sandboxed apps even with user consent. XCUIAutomation only executes inside an XCTest runner started by testmanagerd, which is driven from a host. So the engine has to live on a Mac, which is why this project has no iOS app.