How it reads the screen is the design decision that matters: text, structured elements and full XML are three separate tools, so checking that a button moved costs a snapshot_ui token and a detect_ui_change poll rather than a whole hierarchy dump. Settle the execution mode before you connect anything you care about — unrestricted is the default and grants full shell access, so set ADB_EXECUTION_MODE=restricted with an allowlist on a real device.
An MCP server that drives a connected Android device or emulator through ADB, with tools shaped to return small structured responses rather than raw XML dumps.
- Three deliberately separate ways to read a screen: get_screen_text for what is written on it, get_screen_elements for tappable or interactive elements with bounds and XPath, and get_ui_hierarchy for the full XML when nothing smaller will do
- Input: tap at pixel coordinates, swipe from one point to another over a duration, type into the focused field with spaces encoded for you, and send raw Android keycodes
- PNG screenshots returned with width, height and base64 data
- Change detection that does not re-read the screen — snapshot_ui returns a short token, detect_ui_change polls until the content differs or the timeout passes
- Device handling: list what ADB can see with serials and models, launch an app by package/activity component, and check identity, privilege level or hardware profile before starting
- Raw ADB commands when no tool fits, parsed with shlex and never handed to a system shell
- An optional app_manager pack adding nine tools: list_packages, get_app_info, install_app, uninstall_app, pull_apk, manage_permission, launch_app_extra, manage_app and inject_intent
Python 3.11+, ADB installed on the host, and a device or emulator connected. It runs over stdio with uvx agent-droid-bridge; ADB_PATH points at the binary when adb is not on PATH. The security posture is set by environment variable: ADB_EXECUTION_MODE is unrestricted by default (all shell commands, with an optional ADB_SHELL_DENYLIST) or restricted (only what ADB_SHELL_ALLOWLIST names), ADB_ALLOW_SHELL=false blocks shell commands outright, and ADB_DENIED_TOOLS hides named tools at startup. ADB_EXTRA_TOOL_PACKS=app_manager loads the app-management pack, and MCP_LOG_ENABLED with MCP_LOG_DIR records every tool call and ADB command to JSONL.
One command — uvx agent-droid-bridge
