Prove it.

Benchmarks

On nine real phone jobs, mobile-mcp used 6.9x the tokens of phonedriver. Reading a screen takes 12.7 ms where adb takes 2,163.7 ms. This page has every number, what was thrown away, and the raw data.

6.9x

fewer tokens than mobile-mcp on nine real phone jobs, 10 November 2026

Nine jobs only a phone can do

The result of each job cannot be produced by adb, am, cmd, settings, content or a web search. A script checks each one from the phone's own data, never from the agent's report. All tools ran on the same emulator on 10 November 2026 with Claude Sonnet 5.5 through claude -p, one agent at a time: phonedriver three passes, the others two. The phonedriver build is cfa37b2; release 0.1.0 is that build plus Windows build and test fixes. Totals are sums of per-job medians.

Totals over nine jobs: tokens, tool calls, runs passed and time, per tool
ToolTokens, nine jobsTool callsRuns passedTime
phonedriver 0.1.0657,5429927 of 275.1 min
phone-harness 0.3.31,127,495104.514 of 1822.7 min
mobile-mcp 1.0.164,567,200276.517 of 188.7 min

adb ran only the two jobs where any route is allowed: 4 of 4 runs with 203,751 tokens. phonedriver's sum for the same two jobs is 92,737.

Each job

Bars are median tokens, drawn to scale within each job. After the bar: median tokens, runs passed, median tool calls and median wall time.

qa-signup

Test the Signup tab of a testbed app and report every bug with steps.

phonedriver122k3/323 calls50 s
mobile-mcp549k2/245 calls95 s
phone-harness306k1/220.5 calls477 s

The bugs exist only in how the screen behaves under use; no command exercises a form's validation path.

qa-shop

Test the Shop tab (quantity, totals, a discount code) and report bugs.

phonedriver93k3/315 calls41 s
mobile-mcp473k2/233.5 calls63.5 s
phone-harness136k2/213.5 calls242.5 s

Same: a stale total and a flat discount show only under use.

relay-code

Copy a random pickup code from the Relay tab into a new contact "Locker 7".

phonedriver49k3/38 calls31 s
mobile-mcp574k2/235.5 calls68 s
phone-harness161k0/214.5 calls190 s

The code exists only in a TextView and the app's private storage.

chrome-prefs

Make DuckDuckGo the default search engine and turn on "Always use secure connections".

phonedriver99k3/313 calls45 s
mobile-mcp1.1M1/252.5 calls99 s
phone-harness152k2/216.5 calls140.5 s

Chrome keeps both in a private Preferences file; no intent or setting reaches them.

clock-settings

Add Tokyo and London world clocks, set snooze to 15 min and silence after 5 min.

phonedriver96k3/313 calls39 s
mobile-mcp692k2/244 calls61.5 s
phone-harness80k2/29 calls84 s

These live in Clock's private preferences; the alarm intents set alarms and timers only.

messages-pin-archive

Pin one conversation, archive another, leave a third.

phonedriver45k3/36 calls21 s
mobile-mcp316k2/217.5 calls33.5 s
phone-harness42k2/25.5 calls37 s

Pin and archive are private Messages flags with no command.

photos-crop

Crop a photo to a square and save a copy, keeping the original.

phonedriver61k3/38 calls31 s
mobile-mcp637k2/233 calls60.5 s
phone-harness66k2/28 calls77 s

The Photos editor is the only crop tool; a shell has no image editor.

wakelock-restrict (mixed)

Find the app holding a wakelock and set its battery usage to Restricted.

phonedriver39k3/35 calls17 s
mobile-mcp123k2/27.5 calls20 s
phone-harness55k1/27.5 calls43.5 s
adb72k2/210 calls40 s

A shell finds the holder and Settings sets the restriction; any route allowed.

log-code (mixed)

Find a pickup code in the device log and put it in a contact "Locker 9".

phonedriver54k3/38 calls31 s
mobile-mcp151k2/28 calls22 s
phone-harness129k2/29.5 calls69 s
adb131k2/210.5 calls43 s

The code is only in logcat; the contact is made in the Contacts UI; any route allowed.

Where phonedriver does not win

  • phonedriver had the lowest median wall time on 8 of the nine jobs. Not on these: mobile-mcp on log-code (22 s against 31 s). Wall time includes the model's own thinking, so it moves with the day.
  • These are two or three runs per cell on one emulator. They describe what happened and are not rates. Against its own 7 November runs phonedriver used 657,542 tokens where it had used 977,780, but the emulator was not the same one, so part of that is the device.

What each tool failed at, the versions, and every run that was thrown away and why are in docs/bench/jobs.md. The method is in Benchmarking AI agents on jobs only a phone can do, and the comparison write-up in phonedriver vs mobile-mcp vs phone-harness vs adb on real phone jobs. If you maintain one of these tools and think I set it up wrong, open an issue and I will rerun it.

Speed against adb and phone-harness

p50 of 50 runs after 20 warm-ups, on an Android 17 emulator (API 37, headless, software rendering). Everything ran on the emulator's own host with the three tools interleaved. phonedriver 0.1.1, 11 November 2026. Host CPU load was 31% to 80% around the rows, from other work on the machine. Bars are drawn to scale within each operation; the tick is p95.

Tap, back to back

phonedriver38.5 msp95 175.6
adb170.5 msp95 205.1
phone-harness162.8 msp95 434.9

4.4x faster than adb (95% CI 3.0 to 5.2), 4.2x faster than phone-harness (3.0 to 5.1).

Tap, 1.5 s apart

phonedriver28.7 msp95 42.2
adb69.4 msp95 89.5
phone-harness220.4 msp95 248.1

2.4x faster than adb (95% CI 2.3 to 2.6), 7.7x faster than phone-harness (7.2 to 8.3).

Screenshot

phonedriver9.1 msp95 11.1
adb291.2 msp95 763.7
phone-harness348.3 msp95 715.0

32x faster than adb (95% CI 30.9 to 33.2), 38x faster than phone-harness (36.9 to 39.8).

UI tree, Settings

phonedriver51.0 msp95 146.3
adb2,215.7 msp95 3,054.8
phone-harness2,393.8 msp95 3,017.7

43x faster than adb (95% CI 41.1 to 48.0), 47x faster than phone-harness (44.3 to 51.7).

UI tree, 1,000-row list

phonedriver12.7 msp95 20.0
adb2,163.7 msp95 2,204.5
phone-harness2,362.5 msp95 2,398.1

170x faster than adb (95% CI 162.0 to 177.4), 186x faster than phone-harness (176.8 to 193.4).

Type 100 characters

phonedriver158.3 msp95 426.6
adb3,638.4 msp95 9,234.2
phone-harness3,928.7 msp95 10,064.9

23x faster than adb (95% CI 19.7 to 25.1), 25x faster than phone-harness (21.2 to 26.9).

Tap, then what changed

phonedriver514.0 msp95 612.0
adb2,477.2 msp95 5,460.4
phone-harnessno equivalent

4.8x faster than adb (95% CI 4.7 to 5.2).

Every operation, fastest gain first

How many times faster phonedriver is, then each tool's median time with its slowest 1 in 20 (p95) under it.

OperationFaster than adbphonedriveradbphone-harnessFaster than phone-harness
UI tree, 1,000-row list170x162.0 to 177.412.7 msp95 20.02,163.7 msp95 2,204.52,362.5 msp95 2,398.1186x176.8 to 193.4
UI tree, Settings43x41.1 to 48.051.0 msp95 146.32,215.7 msp95 3,054.82,393.8 msp95 3,017.747x44.3 to 51.7
Screenshot32x30.9 to 33.29.1 msp95 11.1291.2 msp95 763.7348.3 msp95 715.038x36.9 to 39.8
Type 100 characters23x19.7 to 25.1158.3 msp95 426.63,638.4 msp95 9,234.23,928.7 msp95 10,064.925x21.2 to 26.9
Tap, then what changed4.8x4.7 to 5.2514.0 msp95 612.02,477.2 msp95 5,460.4not supportedn/a
Tap, back to back4.4x3.0 to 5.238.5 msp95 175.6170.5 msp95 205.1162.8 msp95 434.94.2x3.0 to 5.1
Tap, 1.5 s apart2.4x2.3 to 2.628.7 msp95 42.269.4 msp95 89.5220.4 msp95 248.17.7x7.2 to 8.3

The range under each ratio is its 95% confidence interval.

On a real phone

A smaller pass on a HONOR X8 (Android 13) over USB, on 9 November 2026. 30 samples per phonedriver row and 20 per adb row; the two tools never ran at once.

Operationphonedriver p50adb p50Ratio
Read the screen78.5 ms2,506.6 ms32x
Screenshot16.4 ms1,005.7 ms61x

The screen read was 461 bytes against 25,334 bytes of XML. On the phone's main Settings page, uiautomator dump failed with "could not get idle state" after 11.5 s, because the page never goes idle, while phonedriver read it in 110 ms. Typing and taps were not measured on the phone.

How to read it

  • The bare tap is the closest row. Host to reply, phonedriver takes 38.5 ms when taps come back to back (4.4 times faster than adb) and 28.7 ms when they are 1.5 s apart (2.4 times). The tap misses its own 15 ms budget (decision D14), and typing 100 characters misses its 150 ms budget (158.3 ms).
  • adb returns raw XML and a tap that returns before the app reacts. phonedriver returns a pruned, numbered tree and waits for the app. Different outputs for the same job. For "tap, then what changed", the adb row is a tap, one uiautomator dump and a diff in the caller.
  • adb input text and phone-harness cannot type Unicode. phonedriver typed 100 mixed-script characters (ASCII, accents, CJK, Arabic, emoji) in 225.0 ms.
  • Fresh daemon to first screen read: phonedriver 1,620.5 ms, phone-harness 2,999.4 ms (new Python process), adb 2,488.0 ms (one dump; adb has no cold start, so this is for scale only).
  • phone-harness's helpers ran in process, which spares it a Python start per call and favours it.
  • No iOS device is in this run. On the iOS 26.5 Simulator a tap takes 357 ms against 80 ms for the Simulator's own tool, because XCTest takes about 240 ms to confirm the event (D15). On an iPhone 14 Plus and an iPad Pro (M5) it takes 534 and 454 ms.

The earlier 153x figure came from a quieter, single-tool run and is not current. The full method, machine state and the history of the number are in the repo.

An earlier token run: three small tasks

Before the nine jobs, a smaller run: Claude Sonnet 5.5 through claude -p, each tool the only tool, three tasks checked from device state (the alarm database, the testbed event log, the answer text). Tokens are input, output, cache creation and cache read. Device: a 2560x1600 tablet emulator.

Task (checked from device state)phonedriver, 2 to 3 runs, median tokensphone-harness, 3 runs, median tokens
alarm-gym: 7:30 AM weekday alarm "Gym" in Clock110,419 (19.5 calls)220,723 (19 calls)
wiki-birthdate: Chrome, Wikipedia, Grace Hopper's birth date57,753 (7 calls)53,403 (7 calls)
testbed-web-form: fill and submit a web form50,185 (7 calls)56,249 (6 calls)
Sum of the three medians218,357330,375

On the sum of medians phonedriver used 34% fewer tokens than phone-harness (218,357 against 330,375). It is ahead on the longest task, level on the form and 8% behind on the Wikipedia task, where individual runs ranged from 26,147 to 64,522 tokens. Raw adb finished alarm-gym in 2 calls with a SET_ALARM intent (18,607 tokens), a shortcut the task allows, and failed the web form, a WebView with no accessibility nodes. mobile-mcp failed every run of this earlier setup for an environment reason (its helper could not reach a remote adb server), so it is not compared here; the nine jobs above gave it its own adb server.

The fixed cost per turn is also measured: the first turn of a one-word claude -p costs 5,606 tokens with phonedriver's 14 MCP tools and 5,214 with phone-harness. All three rounds, the fixes between them and the caveats.

Raw data and reproduction