Skip to main content
Glama

droidmcp

Give any AI assistant hands on your Android device.

droidmcp is a Model Context Protocol server that exposes a connected Android device — real hardware or an emulator — as a set of tools an LLM can actually use: read the screen, tap elements by their label, type, swipe, launch apps and inspect the system.

Zero dependencies. Pure standard library. Python 3.9+.

┌──────────────┐   MCP/JSON-RPC   ┌───────────┐   adb / su   ┌─────────────┐
│ Claude / any │ ───────────────► │ droidmcp  │ ───────────► │ Android     │
│ MCP client   │ ◄─────────────── │  (stdio)  │ ◄─────────── │ device      │
└──────────────┘                  └───────────┘              └─────────────┘

Why

Automating a phone from an LLM is usually done by pasting screenshots into the prompt and hoping the model guesses the right coordinates. That breaks the moment the screen changes, the keyboard opens, or the device rotates.

droidmcp takes the opposite approach: read the real layout, act on real elements. Every interaction targets a uiautomator node with known bounds, so taps land where the model intended instead of three buttons away.

# Instead of guessing: "the search icon is around x=980, y=270"
tap_text(text="搜索")     # resolved from the live UI tree every time

Two things make it practical:

  • Any transport. Use adb over USB/wireless, or — on a rooted phone, in Termux, or with Shizuku/Sui — drive the device through a local root shell with no cable at all.

  • Token-efficient screens. A raw uiautomator dump of a busy home screen is 167 nodes; screen_dump reports the 15 that a model can actually act on.


Related MCP server: autoglm-mcp-server

Install

pip install .            # or: pipx install .

You also need adb on your PATH (skip this if you use the root backend):

Platform

Command

Debian/Ubuntu

sudo apt install android-tools-adb

macOS

brew install android-platform-tools

Arch

sudo pacman -S android-tools

Alpine

sudo apk add android-tools

Windows

platform-tools

Termux

pkg install android-tools

Verify everything works:

droidmcp --selftest
# droidmcp 0.1.0
# backend: adb → /usr/bin/adb
# ✓ R58M12345 (Pixel 7, device)
# ✓ screen: 1080x2400
# ready

Two backends

adb (default)

Requires USB debugging or wireless debugging. Nothing to configure:

droidmcp --selftest

Point at a non-standard binary with ADB_PATH=/opt/platform-tools/adb.

root — no cable required

If the device is rooted, or you have Shizuku/Sui running, droidmcp can drive it through a local root shell instead of adb. This is the difference between "needs a laptop and a cable" and "runs from Termux on the phone itself".

# Rooted device / Magisk / KernelSU
DROIDMCP_BACKEND=root droidmcp

# Shizuku or Sui (rish is the Shizuku shell)
DROIDMCP_BACKEND=root DROIDMCP_ROOT_CMD=rish droidmcp

# Any sudo-capable Linux chroot / proot
DROIDMCP_BACKEND=root DROIDMCP_ROOT_CMD=sudo droidmcp

Verify:

DROIDMCP_BACKEND=root DROIDMCP_ROOT_CMD=rish droidmcp --selftest
# droidmcp 0.1.0
# backend: root → rish
# ✓ local (M2012K11AC, device)
# ✓ screen: 1080x2400
# ready

Variable

Values

Meaning

DROIDMCP_BACKEND

adb (default), root

Which transport to use

DROIDMCP_ROOT_CMD

su (default), rish, sudo, …

How to elevate for the root backend

ADB_PATH

path

Non-standard adb binary


Quick start

1. Enable debugging on the device

adb backend:

  1. Settings → About phone → tap Build number seven times.

  2. Settings → System → Developer options → enable USB debugging.

  3. Plug it in and accept the RSA fingerprint prompt.

  4. adb devices should list it as device (not unauthorized).

root backend: nothing to enable — just make sure su or rish works.

2. Register with your MCP client

Claude Desktop — claude_desktop_config.json:

{
  "mcpServers": {
    "android": {
      "command": "droidmcp",
      "env": { "DROIDMCP_BACKEND": "adb" }
    }
  }
}

Claude Code:

claude mcp add android -- droidmcp

Any other MCP-over-stdio client works the same way: run droidmcp and talk JSON-RPC on stdin/stdout.

3. Ask for something

"Open Settings, find the Wi-Fi entry, tell me which network is connected, and screenshot it."

The model calls launch_app → screen_dump → tap_text → screen_dump → screenshot, reading the real UI at every step.


Tools

Tool

What it does

list_devices

Devices visible to the active backend, plus which backend is live

device_info

Model, Android version, ABI, screen size, focused activity

battery

Level, charging state, temperature

storage

Free/used bytes on /data

list_packages

Installed packages (third-party only by default)

screen_dump

The live UI tree as flat JSON with tap coordinates

screenshot

PNG saved locally, plus pixel dimensions

tap

Tap absolute coordinates

tap_text

Find by text / content-desc / resource-id and tap its centre

swipe

Drag between two points

scroll

Scroll a direction by a fraction of the screen

type_text

Type into the focused field, safely escaped

press_key

home / back / enter / power / volume / …

launch_app

Start an app by package name

force_stop

Kill an app

open_url

Open a URL in the matching app

shell

Escape hatch for arbitrary shell commands

logcat

Tail logs, optionally filtered (*:E for errors)

Why screen_dump matters

uiautomator dump returns a deeply nested XML tree that wastes most of a context window on layout containers. droidmcp flattens it, drops zero-area nodes, and keeps only elements a model can act on — anything with a label, that is clickable, or that scrolls.

Measured on a Xiaomi home screen: 167 raw nodes → 15 reported elements.

{
  "node_count": 167,
  "elements": [
    {"i": 19, "text": "", "desc": "系统应用", "id": "",
     "cls": "RelativeLayout", "at": {"x": 287, "y": 442},
     "clickable": true, "scrollable": false},
    {"i": 11, "text": "", "desc": "", "id": "com.miui.home:id/workspace",
     "cls": "ScreenView", "at": {"x": 540, "y": 1160},
     "clickable": false, "scrollable": true}
  ]
}

If a screen renders entirely into unlabelled views, the filter relaxes rather than returning nothing.


CLI

droidmcp --selftest              # check the backend and device connectivity
droidmcp --list-tools            # dump the MCP tool schema as JSON
droidmcp --call device_info      # invoke one tool, for debugging
droidmcp --call tap --args '{"x": 540, "y": 1200}'

Verified against a real device

The tool layer was exercised end-to-end on a Xiaomi M2012K11AC (Android 13, SDK 33, 1080×2400) over a Shizuku/Sui root shell — no USB cable:

Check

Result

--selftest

✓ local (M2012K11AC, device), ✓ screen: 1080x2400

device_info

Xiaomi / M2012K11AC / Android 13 / arm64-v8a / 1080x2400

battery

42%, charging, 45.2 °C

storage

114.1 GB total, 47.9% used

screen_dump

167 nodes → 15 actable elements

tap_text("不同意")

resolved to (301, 2250), tapped, screen advanced

screenshot

1080×2400 PNG, 167 KB, correctly rendered


Running the tests

python -m unittest discover -s tests -v

The suite runs with no device and no adb — every subprocess call is monkeypatched. The parsing, escaping, backend and protocol layers are pure functions, so they're tested directly:

Ran 43 tests in 0.03s
OK

Coverage highlights:

  • uiautomator XML flattening, bounds→centre math, zero-area rejection

  • input text escaping (spaces, quotes, %s, unicode) round-tripped through a shell

  • dumpsys parsers for battery/storage/packages/focused activity

  • both backends' argv construction (adb -s <serial> …, su -c …, rish …)

  • JSON-RPC: initialize, tools/list, tools/call, errors, notifications, parse errors, and the stdio loop


Design notes

  • Standard library only. The official mcp SDK pulls in pydantic, anyio and a Python-version floor. This server speaks the same wire protocol in ~150 lines so it installs anywhere Python runs — including Termux on the phone itself.

  • Flat node list, not a tree. Models reason about "the node labelled Wi-Fi" far more reliably than about nested FrameLayouts.

  • Coordinates come from bounds, never from a screenshot. Screenshot pixels and input coordinates diverge on scaled/notched displays; uiautomator bounds are already in input space.

  • input text escaping. On-device input treats %s as a space and evaluates the argument in a shell, so both the space encoding and quote escaping have to be right. Regression tests cover a literal %s in user text and embedded apostrophes.

  • Screenshots are base64 over the root backend. Raw PNG bytes do not survive rish or su -c transports; capturing to a device temp file and reading it back as base64 does.

  • Commands may fail harmlessly. grep exits 1 when it finds nothing, so backends return a result instead of raising and callers opt in to raise_for_status().


Limitations

  • uiautomator sees the accessibility tree, so canvas-rendered content (some games, Flutter/Unity surfaces) appears as a single opaque node. Use screenshot plus coordinate taps there.

  • A device showing a secure (DRM) surface returns a black screenshot — that's the platform, not the server.

  • The root backend drives one local device; device is accepted for API symmetry but ignored.

  • uiautomator dump fails while the screen is animating. A failed dump returns an actionable error rather than an empty screen — retry once the UI settles.

  • This is not an on-device automation app: it needs debugging enabled (adb) or a root shell (root backend).

License

MIT

Available Tools

18 tools
batteryC

Battery level, charging status and temperature.

Inputs: device.

device_infoB

Model, Android version, screen size and the currently focused activity.

Inputs: device.

force_stopC

Force-stop a running app by package name.

Inputs: package (required), device.

launch_appB

Launch an app by package name, optionally specifying an activity.

Inputs: package (required), device, activity.

list_devicesA

List Android devices visible to adb, including emulators.

Inputs: no named parameters.

list_packagesB

Installed package names. Set third_party_only=false to include system apps.

Inputs: device, third_party_only.

logcatC

Tail recent logcat lines. filter_spec uses adb syntax such as '*:E' for errors only.

Inputs: lines, device, filter_spec.

open_urlC

Open a URL in the device's default browser or matching app.

Inputs: url (required), device.

press_keyB

Press a hardware key: home, back, enter, menu, power, volume_up, volume_down, recent, wake, sleep, tab, delete, search, notifications. Raw KEYCODE_* names are also accepted.

Inputs: key (required), device.

screen_dumpA

Read the current screen as structured JSON: every node with text, content description, resource id, tap coordinates and whether it is clickable or scrollable. Use this before tapping anything so you act on real element positions instead of guessing.

Inputs: limit, device.

screenshotA

Capture the screen and save it to a local PNG path. Returns the path plus the pixel dimensions so coordinates can be scaled correctly.

Inputs: path, device.

scrollB

Scroll the dominant axis of the screen. Use direction 'down' to reveal content further down the page. Coordinates are derived from the real screen size, so this works on any resolution.

Inputs: amount, device, direction.

shellA

Escape hatch: run an arbitrary adb shell command and return stdout. Use for anything not covered by the dedicated tools (e.g. 'settings get secure enabled_accessibility_services').

Inputs: command (required), device.

storageC

Free/used storage for the /data partition.

Inputs: device.

swipeC

Swipe/drag from (x1,y1) to (x2,y2). Increase duration_ms for slow drags.

Inputs: x1 (required), x2 (required), y1 (required), y2 (required), +2 more.

tapB

Tap the screen at absolute pixel coordinates.

Inputs: x (required), y (required), device.

tap_textA

Find an element by its visible text, content description or resource id and tap its centre. This is the robust alternative to guessing coordinates. Runs a fresh screen dump unless the previous one is reused.

Inputs: text (required), nth, exact, device.

type_textA

Type into the currently focused text field. Tap the field first. Spaces and shell metacharacters are escaped automatically.

Inputs: text (required), device.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.5/5.0

    Scored across 18 tools

    Disambiguation5/5

    Each tool has a distinct purpose: listing devices/info/battery/storage/packages, UI inspection via screen_dump vs screenshot, input actions tap/tap_text/swipe/scroll/type_text/press_key, and app lifecycle tools. shell and logcat are clearly positioned as an escape hatch and a specialized log tool, so there is no meaningful ambiguity.

    Naming Consistency4/5

    All names use snake_case consistently, with no disruptive camelCase or mixed casing. However, the set mixes verb_noun patterns (list_devices, launch_app, force_stop) with bare nouns (battery, storage, screenshot, shell, logcat), so it is slightly less predictable than a pure verb_noun convention.

    Tool Count4/5

    18 tools is slightly above the ideal 3-15 range, but each tool covers a distinct Android/adb capability needed for device inspection and UI automation. There is little redundancy, so the count is reasonable rather than bloated.

    Completeness4/5

    The surface covers device discovery, system state inspection, package listing, UI dumping, screenshots, input automation, app launch/stop, URL opening, and shell/logcat escape hatches. Dedicated install/uninstall, file transfer, and app-data operations are missing, but the shell tool provides a practical workaround for most gaps.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      B
      quality
      D
      maintenance
      Enables AI assistants to interact with Android devices and emulators via ADB, providing tools for screenshots, UI inspection, touch and text input, app management, and device control.
      42
      200 npm
      22
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      Enables AI agents to control, inspect, and automate Android devices, Waydroid containers, and AVD emulators over ADB. Provides tools for screenshots, UI-hierarchy text-based tapping, gestures, key presses, text input, app management, and raw shell commands.
      12
      MIT