MUA Driver
A mobile-use driver for agents. It controls Android and iOS devices and exposes them over MCP, a JSON CLI, and a small Python SDK. It runs no model of its own: an agent brings the decisions, MUA carries them out and reports what actually happened on the device.
- Android devices and emulators through
adb, with an optional Accessibility Helper for the thingsadbcannot do - iOS Simulators through
xcrun simctlandidb, physical devices throughxcrun devicectland WebDriverAgent - MCP over stdio or authenticated HTTP, with a capability-filtered tool list
Install
python3 -m pip install -e '.[dev]'
Host tools: Android platform-tools (MUA resolves MUA_ADB_PATH, then
ANDROID_SDK_ROOT/ANDROID_HOME, then PATH); Xcode command line tools with
xcrun for iOS Simulators; Facebook idb for iOS touch and text input; Xcode
15+ with Developer Mode and a signed, running WDA for physical iOS devices.
Quick start
mua-driver call list_devices
mua-driver call screenshot --device emulator-5554 --output /tmp/screen.png
mua-driver call tap_element --device emulator-5554 --platform android \
--using text --value "Continue"
mua-driver mcp # MCP server over stdio
from mua_driver import MuaDriver
driver = MuaDriver.create()
driver.tap_element(device_id="emulator-5554", platform="android",
using="text", value="Continue")
mua-driver call --help lists every tool. docs/ has the details.
Tool coverage
| Capability | Tools | Android | iOS Simulator | iOS device |
|---|---|---|---|---|
| Discovery | list_devices, list_apps, is_app_installed |
yes | yes | yes |
| Visual/UI | observe, hierarchy reads, resolve_target/tap_target, semantic element actions, waits, assertions |
yes | yes | yes |
| Action safety | preflight target validation, semantic-first targeting, postcondition verification, bounded retry | yes | yes | yes |
| Sequencing | batch_commands with per-step trace and optional trailing observation |
yes | yes | yes |
| Input | tap, drag/fling (velocity-aware)/pinch, element drag, W3C actions, text, keyboard | yes | yes | yes |
| Platform input | key, back, shell / pinch, shake |
yes | yes | partial |
| App lifecycle | launch_app, stop_app, install_app, uninstall_app |
yes | yes | yes |
| App state | foreground state, restart, metadata, explicit reset | yes | yes | yes |
| Navigation | open_url, custom deep links |
yes | yes | yes |
| Device state | permissions, location, orientation, appearance | yes | partial | partial |
| Files and diagnostics | container files, logs, crash logs, bugreport, video, battery | yes | yes | yes |
| Recording | start/stop/cancel screen recording | yes | yes | yes |
| Sync | wait_for_idle, screen-change and activity waits |
yes | yes | yes |
| Alerts | get_alert, handle_alert |
yes | yes | yes |
| Environment | IME selection, locale/timezone, wifi/airplane mode, volume | yes | partial | unsupported |
| Pasteboard | get_clipboard, set_clipboard with restore, paste with restore |
helper | yes | yes |
| Foldables | get_posture, set_posture, get_display_segments, locate_point |
yes | unsupported | unsupported |
| Audit and trace | get_audit_log, start_trace…export_trace, replay_trace |
yes | yes | yes |
| Automation context | list_contexts, get_context — which context is active |
no | native only | setup |
What MUA does differently
Actions are verified by their effect. Every semantic action re-resolves its
target on a live hierarchy before dispatch, declares what it expects
(screen_change, element_present, text_value, activity, …), waits for
that outcome, and reports verified or a structured failure with a bounded
retry. A tool that reports success has either dispatched a real action or told
you it degraded. See docs/action-safety.md.
Gestures are motion, not two events. adb shell input swipe delivers a
press and a release with no movement between them on many devices. MUA ships its
own Accessibility Helper and prefers it for drags, flings and pinches, so a
ScrollView actually scrolls and a pinch actually produces a scale event. See
docs/android-helper.md.
Platform limits are named, not hidden. A capability the platform does not
expose is reported unsupported_by_platform with a reason. The verification
suite records the same way: every tool ends as verified, requires_setup, or
unsupported — never a silent skip.
Compared to other mobile drivers
Read from each project's own README at the time of writing. Not stated means the project does not document the capability; it is not a claim that it is missing.
| MUA Driver | mobile-use | ARTEMIS | mobile-mcp | |
|---|---|---|---|---|
| Ships | driver only, no model | full agent | agent + test framework | MCP server |
| Android device + emulator | yes | yes | yes | yes |
| iOS simulator | yes | yes | no, on roadmap | yes |
| iOS physical device | yes (WDA) | not stated | no, on roadmap | yes |
| Interfaces | MCP (stdio, HTTP), CLI, Python SDK | agent library, MCP | MCP, CLI, Python SDK, web console | MCP, TS CLI |
| Own on-device helper | yes (android/self_apk) |
no | yes (helper APK) | no |
| Verifies the effect of an action | every semantic action | not stated | checker, per-profile verification levels | not stated |
| Re-validates target before dispatch | every semantic action | not stated | safety net (XML, then pixel) | not stated |
| Finding an element | semantic, then coordinate | accessibility tree; weak on games | index, coordinate, visual (OCR + vision model) | accessibility tree first, coordinates second |
| Multi-touch | pinch, fling, element drag | not stated | not stated | swipe |
| Unicode text input | yes (helper) | not stated | not stated | text entry |
| Clipboard | read, replace, restore | not stated | not stated | read, replace |
| Crash logs | list, get, clear | not stated | logcat collection | list, get |
| Foldable posture | model and actuate | not stated | not stated | fold/unfold |
The comparison is about shape, not depth: all four expose an MCP tool surface, and all four can find an element semantically before falling back to coordinates. What differs is in this repository's column — it ships an on-device helper, and it verifies the effect of an action rather than reporting that a command was accepted.
Where MUA is behind: ARTEMIS runs a vision model as a third fallback for Canvas and Compose interfaces. MUA runs no model of any kind, so a target it cannot find semantically is reached by coordinate or not at all — see docs/targeting.md.
Verification
Both device surfaces are measured, not asserted. A suite drives the real driver
against a live device and records an explicit verdict per tool, asserting against
what the test app recorded receiving — event counts, pointer contact time, peak
velocity, pinch span, selected range — rather than against a returned ok.
MUA_ANDROID_TEST_DEVICE=emulator-5554 python -m pytest tests/verification -q
MUA_IOS_SIMULATOR_UDID=6F7241FF-... python -m pytest tests/verification_ios -q
Android: 123 tests in about 2¾ minutes, 163 verdicts across 129 tools — 161
verified, 1 requires_setup (clipboard read, which Android 10+ grants only to the
focused app), 1 unsupported (get_screen_state, iOS-only in this surface).
iOS: the same seven modules against a booted Simulator — 141 tests, about eleven
minutes, 159 verdicts across 122 tools, with every tool the schema lets a caller
reach on iOS covered. 125 verified; the other 34 are platform limits the Simulator
cannot lift, each recorded with the measurement behind it and a typed refusal from
the driver rather than an ok.
Neither run has a failing or unrun tool, and a coverage gate fails either one if a tool lacks a verdict or fails one.
DRIVER_VERIFICATION.md has the method, both results and
the defects they found — among them an iOS tap path that passed floats to a
command that parses integers, which had broken every element-addressed tool on
that platform; a selector table in the iOS backend that resolved content-desc
nowhere, which made scroll_element_into_view scroll a screen looking for an
element the hierarchy already held; an Android set_permission that returned ok
for a grant the platform had silently declined to make; and an Android
handle_alert("accept") that returned ok after pressing Deny, because the two
dialog buttons were held in a set and sets do not keep an order.
Boundaries
- Android defaults to platform tools (
adb shell input,adb exec-out, package manager); the accessibility helper is opt-in and never installed or enabled without being asked. - iOS Simulator discovery, screenshots, launch and termination use
simctl; gesture, text and accessibility operations useidb. Physical iOS usesdevicectlfor discovery and diagnostics, WDA for interaction. - The MCP server is stdio JSON-RPC with
initialize,tools/list, andtools/call.
Documentation
| Document | Covers |
|---|---|
| docs/cli.md | CLI usage, action sequences with batch_commands |
| docs/mcp.md | MCP registration, capability-filtered tool list, HTTP transport |
| docs/action-safety.md | preflight, postconditions, bounded retry |
| docs/targeting.md | semantic-first targeting and coordinate fallback |
| docs/python-sdk.md | embedding the driver |
| docs/android-helper.md | the accessibility helper, backend selection, shell policy |
| docs/ios.md | physical iOS devices, WDA, per-target capabilities |
| docs/device-primitives.md | screen, orientation, buttons, clipboard |
| docs/crashes.md | crash resource model |
| docs/foldables.md | foldable posture model |
| docs/traces.md | action traces and replay |
Metadata
Release files for mua-driver 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| mua_driver-0.3.0.tar.gz | 435.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mua_driver-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 614.0 kB
Release files / mua_driver-0.3.0.tar.gz
| Download URL | mua_driver-0.3.0.tar.gz |
|---|---|
| Size | 435.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6c47761facfd5854d216564eeac0a91f98f2aa443d23ff6ef3b8e82e62a3ddd6
|
|
BLAKE2b-256 checksum How to use checksums |
931c7ba462e5f3fdba192892ac67dd8acc32bc1214747924106cfba88e03f879
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / mua_driver-0.3.0-py3-none-any.whl
| Download URL | mua_driver-0.3.0-py3-none-any.whl |
|---|---|
| Size | 178.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0075584cc748e07b054c93dfd744a2579fdc5ffcf09521930dd3b17d51d4f0e1
|
|
BLAKE2b-256 checksum How to use checksums |
4e767e1519674a28dec224ec18c6919aaa7b25eb4e575ace12c1adea20832a9d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Security for mua-driver 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.