Pure Python MCP server for mobile automation — Android via uiautomator2, iOS via pymobiledevice3
Project description
pymobile-mcp
Pure Python MCP server for mobile automation.
- Android:
uiautomator2+adbutils - iOS: pure
pymobiledevice3userspace tunnel + WebDriverAgent (no go-ios, no root)
Public contract: fixed upstream mobile-mcp@c5d7d27 — 23 tools by default, 26 with MOBILEFLEET_ENABLE=1.
mobile_get_page_source is not public; fleet runtime uses the documented approved exception when no Python fleet provider is installed.
Current release: 0.3.1 · Changelog · Live regression checklist
Features
- One stdio MCP server for Android + iOS
- Upstream-compatible natural-language/image results and exact Actionable vs
isError=truefailures - Live smoke scripts that exit
blocked(code 2) when devices are missing — never fake pass - Destructive actions (install/uninstall) gated by explicit env flags
Requirements
| Platform | Need |
|---|---|
| Host | Python ≥ 3.10, pip |
| Android | adb in PATH, authorized device or emulator |
| iOS | macOS recommended, paired iPhone/iPad, Developer Mode, mounted DDI, installed WDA runner (default com.byte.WebDriverAgentRunner.xctrunner) |
[!NOTE] iOS real-device screen recording returns the approved upstream-compatible Actionable exception on current pure-userspace RSD (iOS 26.5.2). iOS Simulator uses
simctl recordVideo+ WDA and is not covered by that exception.
Install
From a git checkout (development)
git clone https://github.com/ByteTrue/pymobile-mcp.git
cd pymobile-mcp
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .
pymobile-mcp --help
Dev install (tests)
pip install -e ".[dev]"
python -m pytest -q
From PyPI
pip install pymobile-mcp==0.3.1
From GitHub (no local clone of tooling)
pip install "git+https://github.com/ByteTrue/pymobile-mcp.git@v0.3.1"
[!TIP] Prefer a venv. System Python +
pip installoften fights with Homebrew/PEP 668.
Quick start
- Connect an Android emulator/device (
adb devices) and/or a paired iPhone. - For iOS, ensure your WDA runner is installed (Xcode once); pymobile-mcp launches it over userspace tunnel.
- Run the server:
pymobile-mcp run
# equivalent:
python -m pymobile_mcp.cli run
- Point an MCP client at the process (stdio).
MCP client config
Installed entrypoint (pymobile-mcp on PATH):
{
"mcpServers": {
"pymobile-mcp": {
"command": "pymobile-mcp",
"args": ["run"]
}
}
}
Checkout / venv (absolute paths are more reliable for GUI clients):
{
"mcpServers": {
"pymobile-mcp": {
"command": "/ABS/PATH/pymobile-mcp/.venv/bin/python",
"args": ["-m", "pymobile_mcp.cli", "run"],
"cwd": "/ABS/PATH/pymobile-mcp",
"env": {
"NO_PROXY": "*",
"PYTHONPATH": "src"
}
}
}
}
[!IMPORTANT] For iOS USB, unset or bypass HTTP proxies (
NO_PROXY=*or clearhttp_proxy/https_proxy). Proxy env often breaks usbmux / userspace tunnel.
First tools to try
After the client connects:
mobile_list_available_devices— confirm Android/iOS idsmobile_get_screen_size/mobile_take_screenshotmobile_list_elements_on_screen— UI tree (no raw page source tool)mobile_list_apps→mobile_launch_appwith a known package/bundle id
Capability matrix (23 default tools; 26 with fleet env)
| Status | Meaning |
|---|---|
| supported | Implemented and covered by automated contract tests; this label is not a claim of current-device live attestation |
| approved exception | Explicit, reviewed black-box difference with stable upstream-compatible text |
Current live evidence passes on Android physical, Android emulator, iOS Simulator, and iOS real device. Android recording and iOS Simulator recording both passed live; iOS real-device recording remains the approved exception.
| Tool | Android | iOS |
|---|---|---|
| mobile_list_available_devices | supported | supported |
| mobile_list_apps | supported | supported |
| mobile_launch_app | supported | supported |
| mobile_terminate_app | supported | supported |
| mobile_install_app | supported (destructive) | supported (destructive, gated) |
| mobile_uninstall_app | supported (destructive) | supported (destructive, gated) |
| mobile_get_screen_size | supported | supported |
| mobile_click_on_screen_at_coordinates | supported | supported |
| mobile_double_tap_on_screen | supported | supported |
| mobile_long_press_on_screen_at_coordinates | supported | supported |
| mobile_list_elements_on_screen | supported | supported (WDA source internal) |
| mobile_press_button | supported | supported (HOME/VOLUME_*; BACK unsupported) |
| mobile_open_url | supported (http/https default) | supported (device must be unlocked) |
| mobile_swipe_on_screen | supported | supported |
| mobile_type_keys | supported | supported |
| mobile_save_screenshot | supported | supported |
| mobile_take_screenshot | supported | supported |
| mobile_set_orientation | supported | supported |
| mobile_get_orientation | supported | supported |
| mobile_start_screen_recording | supported; live passed | simulator supported/live passed; real device approved exception |
| mobile_stop_screen_recording | supported; live passed | simulator supported/live passed; real device approved exception |
| mobile_list_crashes | supported (dropbox) | supported (crash reports) |
| mobile_get_crash | supported (dropbox) | supported (crash reports) |
Environment variables
| Env | Purpose |
|---|---|
MOBILEMCP_ALLOW_UNSAFE_URLS=1 |
allow non-http(s) schemes for mobile_open_url |
MOBILEFLEET_ENABLE=1 |
expose the upstream fleet tool set (26 tools); runtime returns approved exception without a Python fleet provider |
PYMOBILE_MCP_ANDROID_DROPBOX_ALL=1 |
include non-crash dropbox tags (strictmode/boot/…) |
PYMOBILE_MCP_ANDROID_DEVICE |
pin Android serial for smokes |
PYMOBILE_MCP_ANDROID_ACTIONS=1 |
enable tap/type interactions in Android UI smoke |
PYMOBILE_MCP_ANDROID_TAP=x,y |
override Android tap point |
PYMOBILE_MCP_ANDROID_APK + PYMOBILE_MCP_ANDROID_PACKAGE + PYMOBILE_MCP_ANDROID_DESTRUCTIVE=1 |
allow install/uninstall smoke |
PYMOBILE_MCP_IOS_DEVICE |
pin iOS UDID |
PYMOBILE_MCP_IOS_ACTIONS=1 |
enable interaction steps in iOS core smoke |
PYMOBILE_MCP_WDA_XCTRUNNER |
WDA runner bundle id (default com.byte.WebDriverAgentRunner.xctrunner) |
PYMOBILE_MCP_WDA_PORT |
WDA port used by the in-process userspace RSD service client (default 8100; not a remote WDA URL) |
PYMOBILE_MCP_IOS_IPA + PYMOBILE_MCP_IOS_DESTRUCTIVE=1 |
allow iOS install/uninstall smoke |
NO_PROXY=* |
recommended for iOS USB / usbmux |
Live smoke
Scripts print JSON with status: passed|failed|blocked.
Exit 2 + blocked = missing device/env, not a product pass.
export NO_PROXY='*'
PATH=.venv/bin:$PATH
# Android
python tests/android_live_smoke.py
PYMOBILE_MCP_ANDROID_ACTIONS=1 python tests/android_live_smoke.py
python tests/android_app_system_live_smoke.py
python tests/android_recording_crash_live_smoke.py
# iOS (unlock phone for open_url)
python tests/ios_pmd3_wda_live_smoke.py
PYMOBILE_MCP_IOS_ACTIONS=1 python tests/ios_pmd3_wda_live_smoke.py
python tests/ios_system_helpers_live_smoke.py
python tests/ios_app_lifecycle_live_smoke.py
python tests/crash_tools_live_smoke.py
python tests/ios_app_recording_crash_live_smoke.py # real recording uses approved exception
Full dual-device gate: docs/regression-checklist.md.
Architecture (short)
MCP client --stdio--> pymobile-mcp server
│
├─ tools/registry + specs (23 default / 26 fleet contract)
├─ tools/* handlers
└─ drivers/
├─ android.py (adbutils / uiautomator2)
├─ ios.py (UserspaceRsdTunnel + WDA)
└─ ios_simulator.py (simctl + WDA)
Development
pip install -e ".[dev]"
python -m pytest -q
CI runs unit tests on Python 3.11 and 3.12 (workflow). Device live smokes stay local.
Known limits
- Android crashes come from
dumpsys dropbox --print, filtered to crash/ANR/tombstone-like tags by default - iOS real-device recording remains an approved exception; Simulator recording requires a booted CoreSimulator + WDA
- Recording state is process-local (
ActiveRecording); no cross-process resume - Custom URL schemes need
MOBILEMCP_ALLOW_UNSAFE_URLS=1 - Screenshot/recording host paths must resolve under cwd or system temp
- No go-ios/mobilecli runtime fallback by design
Troubleshooting
| Symptom | What to check |
|---|---|
| iOS device list empty | Cable/trust, pymobiledevice3 usbmux list, proxies (NO_PROXY=*) |
| iOS driver tunnel errors | Developer Mode, pymobiledevice3 mounter auto-mount, only one userspace tunnel process |
open_url fails on iPhone |
Unlock the device (passcode lock blocks Safari/WDA URL open) |
| Android no devices | adb devices authorized; kill stale adb if offline |
| Install/uninstall smoke skipped | Set package/apk/ipa and *_DESTRUCTIVE=1 |
| CI green but live red | Expected — CI is unit-only; use regression checklist |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pymobile_mcp-0.3.1.tar.gz.
File metadata
- Download URL: pymobile_mcp-0.3.1.tar.gz
- Upload date:
- Size: 68.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b5ff7603854262bd7a6925311f9cb92f4b48346b820fb5ee9db849c78359b88
|
|
| MD5 |
2e494ab590a534c15b819ca4374518eb
|
|
| BLAKE2b-256 |
880d61e5d99bbcb2324f943fdbbca06505a2d9bf57ce85c69094c5e474c3c8a7
|
Provenance
The following attestation bundles were made for pymobile_mcp-0.3.1.tar.gz:
Publisher:
publish.yml on ByteTrue/pymobile-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymobile_mcp-0.3.1.tar.gz -
Subject digest:
2b5ff7603854262bd7a6925311f9cb92f4b48346b820fb5ee9db849c78359b88 - Sigstore transparency entry: 2205287090
- Sigstore integration time:
-
Permalink:
ByteTrue/pymobile-mcp@ef2ea1c422ea52476c4dee1244b92694ab8af8d9 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/ByteTrue
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ef2ea1c422ea52476c4dee1244b92694ab8af8d9 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pymobile_mcp-0.3.1-py3-none-any.whl.
File metadata
- Download URL: pymobile_mcp-0.3.1-py3-none-any.whl
- Upload date:
- Size: 52.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec798dbcb89e3db1100935b4b7fd8d8270c23c3da84fd8cc21ad41394ee4fbc0
|
|
| MD5 |
ee99e0a537ca024e3b2388efc0819d45
|
|
| BLAKE2b-256 |
c8faca02e8b8870109a2b4ad189848ea2a4ab9d2e1b491647bef3087b266127a
|
Provenance
The following attestation bundles were made for pymobile_mcp-0.3.1-py3-none-any.whl:
Publisher:
publish.yml on ByteTrue/pymobile-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymobile_mcp-0.3.1-py3-none-any.whl -
Subject digest:
ec798dbcb89e3db1100935b4b7fd8d8270c23c3da84fd8cc21ad41394ee4fbc0 - Sigstore transparency entry: 2205287101
- Sigstore integration time:
-
Permalink:
ByteTrue/pymobile-mcp@ef2ea1c422ea52476c4dee1244b92694ab8af8d9 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/ByteTrue
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ef2ea1c422ea52476c4dee1244b92694ab8af8d9 -
Trigger Event:
push
-
Statement type: