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 mua-driver
That is the whole requirement list. The driver has no runtime dependencies — it
shells out to adb, simctl and idb rather than importing a platform
library — so an install pulls nothing else in and cannot break under one.
Screenshots come back full size over MCP. One optional extra adds downscaling:
python3 -m pip install 'mua-driver[image]' # pillow
The optional Android accessibility helper is not bundled in the wheel; see
docs/android-helper.md
for building it and pointing MUA_ANDROID_SELF_APK at the APK.
From a checkout, which is what you want only if you are changing the driver or running the device suites against local edits:
git clone https://github.com/slm/mua.git && cd mua
python3 -m pip install -e '.[dev]'
Published as 0.3.1, Development Status :: 3 - Alpha. See
ROADMAP.md for what is measured
and what is not — 30 iOS tools do not have a clean verdict (23 never verified,
7 partly), and the physical-device backend has never been run at all. Pin the
version if you need the surface to stay still.
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.
Registering the MCP server
opencode mcp add mua --global -- mua-driver mcp # every project
opencode mcp add mua -- mua-driver mcp # this project only
opencode mcp list
The tool list is filtered by what the connected device can actually do, so the
same server offers fewer tools against a plain emulator than against a Simulator
with idb.
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. The
docs/ directory 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
for the helper and the backend selection.
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 of the 159 verdicts are verified; the other 34 are
requires_setup or unsupported. At tool level, 30 tools do not have a clean
verdict — 13 Simulator limits, 6 managed-WDA capabilities that need a physical
device, 4 that need WebDriverAgent, and 7 that work in part — 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.
Thirty tools on iOS do not have a clean verdict, and they are not one problem: thirteen are limits of the Simulator that will never lift, six are the managed-WDA family that has never run on a device at all, four need WebDriverAgent, and seven work in part. Android has no such split — every tool on it is verified against the run. ROADMAP.md has each group with the measurement behind it.
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 |
|---|---|
| ROADMAP.md | what the driver does not yet do, why, and in what order |
| 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 |
Contributing
Development setup and the device suites are in CONTRIBUTING.md. Security reports go through SECURITY.md, not the public issue tracker. Release history is in CHANGELOG.md.
Metadata
Release files for mua-driver 0.3.1
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.1.tar.gz | 444.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mua_driver-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 623.7 kB
Release files / mua_driver-0.3.1.tar.gz
| Download URL | mua_driver-0.3.1.tar.gz |
|---|---|
| Size | 444.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
11e16ad8cbf5c0b78bb84b342a04c1f4f45cea9d43a451f101c6f37294879a0f
|
|
BLAKE2b-256 checksum How to use checksums |
5c5380a86a9f8ccee64a38365517cd86e7725859bbbba47451122d98bdadcdd4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / mua_driver-0.3.1-py3-none-any.whl
| Download URL | mua_driver-0.3.1-py3-none-any.whl |
|---|---|
| Size | 179.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3e0998ff73c5bd6ac70f07b11c3aebd7b54f476d599763c3108db8687e668133
|
|
BLAKE2b-256 checksum How to use checksums |
d084e2a08d99280ccc6fc9356358105cdd7c0517d4e234e3f0c97a2687e23a25
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for mua-driver 0.3.1
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.