Summary
On an iOS Simulator, sequential mobile-mcp commands that go through the on-device devicekit agent (screenshot, list_elements, click/tap, swipe) exhibit pathologically variable latency — most calls stall for ~20–30 s, a few return sub-second, and some hang indefinitely (observed up to multiple minutes in real use). Because the MCP server has no client-side timeout and resolves the device synchronously, a single stalled command makes the entire MCP server appear frozen to the agent/host (Claude Code, etc.) for the duration.
This is not the large-accessibility-tree timeout from #288 — it reproduces with a bare screenshot on the iOS Home screen (a trivial tree), and it persists after every environmental fix we could think of (full list below).
Environment
|
|
| mobile-mcp |
0.0.59 (latest) |
| bundled mobilecli |
0.3.79 (also tested 0.3.83 — no change) |
| Device |
iPhone 16 Pro Simulator, iOS 18.6 |
| Xcode |
26.5 (17F42) |
| macOS |
26.5.1 (Apple Silicon, arm64) |
| Node |
v25.9.0 |
Symptom (user-facing)
mobile_list_available_devices (no device arg, pure simctl) — always instant.
mobile_save_screenshot / mobile_take_screenshot / mobile_list_elements_on_screen / mobile_click_on_screen_at_coordinates (all take a device arg → go through getRobotFromDevice → mobilecli → devicekit agent) — routinely stall 20–30 s; intermittently hang indefinitely.
The instant-vs-stall split by whether the tool touches the agent isolates the problem to the mobilecli + devicekit-agent path, not the MCP transport.
Reproduction (isolated to the bundled mobilecli, below the MCP layer)
Boot a single iPhone 16 Pro / iOS 18.6 simulator, then loop screenshots directly against the bundled binary:
BIN=node_modules/mobilecli/bin/mobilecli-darwin-arm64
UDID=<booted iPhone 16 Pro, iOS 18.6>
"$BIN" agent install --device "$UDID"
for i in $(seq 1 10); do
/usr/bin/time "$BIN" screenshot --device "$UDID" --format png --output /tmp/s_$i.png
sleep 3
done
Observed (fresh agent install; 45 s hard cap per call; on the Home screen):
agent install exit=0
call 1: OK 28643 ms
call 2: OK 943 ms
call 3: OK 2846 ms
call 4: OK 29095 ms
call 5: OK 7644 ms
call 6: OK 24263 ms
call 7: OK 20141 ms
call 8: OK 10060 ms
call 9: OK 392 ms
call 10: OK 390 ms
Latency swings from 0.39 s to 29 s on identical, trivial calls. With a tighter cap (25–30 s), the long calls present as outright hangs (and earlier runs with a 60 s cap produced genuine >60 s hangs; one real-world mobile_save_screenshot hung ~17 minutes before being interrupted). In several runs forcibly killing a stalled call appeared to "free" the next one, which is what produced an apparent OK/HUNG alternation at fixed timeouts.
Diagnosis / hypothesis
The ~25–30 s clustering looks like an internal connection/read timeout firing on a stale agent connection: each successful command seems to leave the devicekit agent holding a connection, so the next command's first attempt blocks until a ~25–30 s timeout elapses, then reconnects and succeeds. Occasionally the recovery itself stalls, yielding the multi-minute hangs.
Two things turn this from "slow" into "freezes the whole server":
- No client-side timeout on the
mobilecli calls — a stall is unbounded.
- In
server.js getRobotFromDevice, mobilecli.agentStatus / agentInstall use synchronous execFileSync, so a stall blocks the MCP server's event loop entirely (every queued tool call waits).
Ruled out (so this isn't mistaken for a local/setup problem)
Related
Suggested directions
- Reuse or cleanly tear down the devicekit-agent connection per command so the next command doesn't block on a stale-connection timeout (the apparent ~25–30 s stall).
- Add a client-side timeout to
mobilecli invocations in mobile-mcp so a stalled command fails fast instead of wedging the whole MCP server.
- Make device resolution async (avoid synchronous
execFileSync in getRobotFromDevice) so one stall can't block the event loop for other tools.
Happy to run diagnostics or test a patch against this setup — the repro is consistent here.
Summary
On an iOS Simulator, sequential mobile-mcp commands that go through the on-device
devicekitagent (screenshot,list_elements,click/tap, swipe) exhibit pathologically variable latency — most calls stall for ~20–30 s, a few return sub-second, and some hang indefinitely (observed up to multiple minutes in real use). Because the MCP server has no client-side timeout and resolves the device synchronously, a single stalled command makes the entire MCP server appear frozen to the agent/host (Claude Code, etc.) for the duration.This is not the large-accessibility-tree timeout from #288 — it reproduces with a bare
screenshoton the iOS Home screen (a trivial tree), and it persists after every environmental fix we could think of (full list below).Environment
0.0.59(latest)0.3.79(also tested0.3.83— no change)Symptom (user-facing)
mobile_list_available_devices(nodevicearg, puresimctl) — always instant.mobile_save_screenshot/mobile_take_screenshot/mobile_list_elements_on_screen/mobile_click_on_screen_at_coordinates(all take adevicearg → go throughgetRobotFromDevice→mobilecli→ devicekit agent) — routinely stall 20–30 s; intermittently hang indefinitely.The instant-vs-stall split by whether the tool touches the agent isolates the problem to the
mobilecli+ devicekit-agent path, not the MCP transport.Reproduction (isolated to the bundled
mobilecli, below the MCP layer)Boot a single iPhone 16 Pro / iOS 18.6 simulator, then loop screenshots directly against the bundled binary:
Observed (fresh agent install; 45 s hard cap per call; on the Home screen):
Latency swings from 0.39 s to 29 s on identical, trivial calls. With a tighter cap (25–30 s), the long calls present as outright hangs (and earlier runs with a 60 s cap produced genuine >60 s hangs; one real-world
mobile_save_screenshothung ~17 minutes before being interrupted). In several runs forcibly killing a stalled call appeared to "free" the next one, which is what produced an apparent OK/HUNG alternation at fixed timeouts.Diagnosis / hypothesis
The ~25–30 s clustering looks like an internal connection/read timeout firing on a stale agent connection: each successful command seems to leave the devicekit agent holding a connection, so the next command's first attempt blocks until a ~25–30 s timeout elapses, then reconnects and succeeds. Occasionally the recovery itself stalls, yielding the multi-minute hangs.
Two things turn this from "slow" into "freezes the whole server":
mobileclicalls — a stall is unbounded.server.jsgetRobotFromDevice,mobilecli.agentStatus/agentInstalluse synchronousexecFileSync, so a stall blocks the MCP server's event loop entirely (every queued tool call waits).Ruled out (so this isn't mistaken for a local/setup problem)
mobilecli+ devicekit, not WDA; installing/running WDA on 8100 changes nothing.simctl eraseto factory: no change.simctl boot: no change.0.3.79vs0.3.83: no change.screenshoton the Home screen; element count is tiny.Related
click/list_elements(iOS sim)". Same family, but that report attributes it to large accessibility trees; this one reproduces on a trivial Home-screenscreenshot, so the root cause appears broader (agent connection handling), not tree size.Suggested directions
mobilecliinvocations in mobile-mcp so a stalled command fails fast instead of wedging the whole MCP server.execFileSyncingetRobotFromDevice) so one stall can't block the event loop for other tools.Happy to run diagnostics or test a patch against this setup — the repro is consistent here.