Skip to main content
Glama

android-mcp

MCP server that lets an agent use the Android app you are developing, on a local emulator or a real device, the way a person does: look at the screen, tap, swipe, type, press keys, wait for things to appear and read logcat. It drives the devices through adb, so every device Android Studio sees works here too.

Only Android Studio dev builds

The server only works with apps installed by Run in Android Studio. A package qualifies when all of these hold (read from dumpsys package):

  • DEBUGGABLE: a debug build.

  • TEST_ONLY: Android Studio injects android:testOnly="true" into builds started from the IDE, and such APKs can only be installed with adb install -t, never through the Play Store or the package installer. A ./gradlew installDebug from the command line doesn't set it, so those installs are refused as well.

  • initiatingPackageName=com.android.shell (Android 11+): installed over adb.

Enforcement:

  • launch_app, stop_app, clear_app_data, open_url and logcat only accept such a package.

  • Screen tools (get_ui, screenshot, wait_for, tap, long_press, swipe, scroll, type_text, press_key) check the foreground activity first and refuse while any other app is in front. The exception is the system permission dialog, so runtime permission prompts of the dev app can be answered.

  • When an action leaves the app (e.g. press_key home), the response says so instead of showing the screen.

  • There is no install tool: install via Android Studio.

Related MCP server: AndroidBuildMCP

On-device agent (fast UI reads)

The UI tree is read through Appium's UiAutomator2 server (appium-uiautomator2-server, Apache-2.0), which stays running on the device and keeps its accessibility connection open:

  • A UI read takes ~0.4 s instead of 2.5–3 s with uiautomator dump, and it does not wait for the screen to become idle.

  • Starting it costs ~2 s once per session. It is installed automatically the first time (two APKs: io.appium.uiautomator2.server and io.appium.uiautomator2.server.test). They are not dev builds, so the dev-app restriction never lets the tools drive them.

  • Compose test tags are exposed as id= (mapTestTagToResourceId).

  • If the agent can't be started, the tree falls back to uiautomator dump. Set ANDROID_MCP_AGENT=0 in the server's environment to always use the dump.

  • Only one process can hold the accessibility connection, so the server is stopped before falling back to a dump, and other UiAutomator tools (Appium, Maestro) can't run against the device at the same time.

  • Remove it from a device with adb uninstall io.appium.uiautomator2.server.test && adb uninstall io.appium.uiautomator2.server.

The APKs come from the npm dependency at install time; this repo does not redistribute them.

Setup

Requirements: Node 20+, the Android SDK platform-tools (adb) and, for AVDs, the emulator. The SDK is found via $ANDROID_HOME, $ANDROID_SDK_ROOT, ~/Android/Sdk, ~/Library/Android/sdk or PATH.

npm install
npm run build
claude mcp add --scope user android -- node "$PWD/dist/index.js"

During development npm run dev (tsx) runs the sources directly; rebuild and run /mcp → reconnect in Claude Code after changes.

Real devices

  • USB: enable Developer options → USB debugging, plug in, accept the RSA prompt.

  • Wi-Fi (Android 11+): Developer options → Wireless debugging → Pair device with pairing code, then adb pair <ip>:<pair-port> and adb connect <ip>:<port>.

When several devices are connected, every tool needs serial (from list_devices).

Tools

Area

Tools

Devices

list_devices, start_emulator, get_screen_info, set_orientation

Apps

list_dev_apps, current_app, launch_app, stop_app, clear_app_data, open_url

Observe

get_ui, read_all, screenshot, wait_for

Diagnose

logcat, list_crashes, get_crash

Act

tap, long_press, swipe, scroll, type_text, press_key

  • One coordinate space. Screenshots are downscaled so the long edge is at most 768 px (image tokens grow with the pixel count). get_ui reports element centres in the same space, and every x/y input uses it, so a point read off a screenshot can be tapped as is. screenshot with region returns a sharper crop for reading small text; a crop's pixels are not screen coordinates.

  • Stable refs. Every element has a [ref] that stays the same across calls as long as the element keeps its identity (class, id, label). Refs start at a random base per server process, so refs from before a restart fail instead of hitting another element.

  • Act + observe in one call. Action tools wait settle_ms (300 ms default) and then report, by default (observe: "changes"), only what changed since the agent last saw the screen: added (+), changed (~) and gone (-) elements, or "No visible change". A new screen comes back in full. ui, screenshot, both and none are available too.

  • read_all scrolls a list or form to its end and returns all entries in one call, instead of one call (and one model turn) per scroll.

  • Targets. tap/long_press/type_text take index (a [ref]), text (text or content description, substring unless exact), id (resource id / Compose test tag), or x+y. Refs are looked up in a fresh read, so they always tap the element's current position.

  • get_ui output lists only visible elements that carry text, a description, a leaf id or an interaction flag. A clickable container without its own text shows its children's text as ~"…", and those children are folded into it. all: true shows every node.

  • No tree? If neither the agent nor uiautomator dump delivers a tree, the tools say so and get_ui returns a screenshot instead: continue with x/y taps.

  • logcat filters by the app's uid, so lines of a crashed earlier process are still included. By default each call only returns lines that are new since the previous call with the same filters.

  • Crash notices. Every screen tool and launch_app/open_url check the app for new crashes, ANRs and native crashes after acting (~75 ms) and put a ⚠ … crashed: <exception> at <frame> notice with the get_crash id in front of the result, or of the error if the action failed. Crashes that happen between two calls are reported with the next call; each crash is reported once.

  • Animations off. On first use of a device the server sets the window, transition and animator duration scales to 0, so steps are faster and the UI is settled when it is read. The original values are restored when the server exits (also on SIGINT/SIGTERM and when the client closes stdin) and kept in $TMPDIR/android-mcp-animations-<serial>.json meanwhile, so a server that was killed hard doesn't make the next one treat 0 as original. If a server was killed hard (kill -9), the next server restores the device when it exits. Set ANDROID_MCP_KEEP_ANIMATIONS=1 to leave the settings alone.

  • list_crashes / get_crash read the system DropBox (dumpsys dropbox): Java crashes, ANRs and native crashes of the app, newest first, each with exception and top frame; get_crash returns the full report with the complete stack trace. Unlike logcat, these reports survive the process and log rotation. since_minutes limits the list to recent crashes.

  • start_emulator waits up to ~40 s per call (below the MCP request timeout); call it again to keep waiting. Emulator output goes to $TMPDIR/android-mcp-emulator-<avd>.log.

Token usage

Each tool call is one model turn, and every turn re-reads the whole conversation. Long UI sessions inside a big coding conversation get expensive because of that, not because of the tool output. Run UI testing in a fresh session or in a subagent with a small context: a ready-made Claude Code subagent definition (android-tester, Sonnet, android tools only) can live in ~/.claude/agents/android-tester.md.

Typical loop

  1. Install the app with Run in Android Studio, then list_dev_apps.

  2. launch_app → read the returned UI tree.

  3. tap / type_text / scroll and read the new state from each response; wait_for text after navigation or loading instead of sleeping.

  4. list_crashes to see whether the app crashed, get_crash for the stack trace, and logcat with level: "E" for errors that didn't crash it.

Limitations

  • type_text supports printable ASCII only (adb shell input text). Use press_key enter for new lines.

  • No multi-touch gestures (pinch). Zoom maps with double-tap or their zoom controls.

  • Compose test tags appear as id= only if the app sets Modifier.semantics { testTagsAsResourceId = true } at its root (worth enabling in debug builds); otherwise target Compose elements by text or content description.

  • Screenshots take ~1 s (screencap) and cost far more tokens than the tree; ask for them only where the tree is not enough.

Available Tools

23 tools
clear_app_dataA

Delete all data of an app (like a fresh install). Also stops it.

Inputs: package (required), serial.

current_appC

Package and activity currently in the foreground.

Inputs: serial.

get_crashB

Full report of one crash from list_crashes: header (process, version, time) and the complete stack trace.

Inputs: id (required), package (required), serial, max_lines.

get_screen_infoC

Screen size, density, coordinate scale and the app in the foreground.

Inputs: serial.

get_uiA

The visible UI as a list of elements: [ref] (stable across calls), text, desc, id, centre, size, flags. Target elements by ref (index), text or id. Falls back to a screenshot when no tree is available.

Inputs: all, serial.

launch_appC

Launch an allowed dev app by package (its launcher activity), or a specific activity of it.

Inputs: package (required), serial, observe, activity, +1 more.

list_crashesB

List recorded crashes, ANRs and native crashes of a dev app, newest first, with exception and top frame. Reports survive the process and logcat rotation.

Inputs: package (required), limit, types, serial, +1 more.

list_dev_appsA

List the apps this server may use. Only debug builds installed from Android Studio (Run) are allowed: debuggable + testOnly + installed via adb.

Inputs: serial.

list_devicesA

List connected adb devices (emulators and physical) and available AVDs.

Inputs: no named parameters.

logcatA

Read the log of an allowed dev app (including lines of crashed earlier processes). By default returns only lines that are new since the previous call with the same filters.

Inputs: package (required), grep, level, serial, +2 more.

long_pressB

Long-press an element (by index, text or id) or a screen point (x, y).

Inputs: x, y, id, text, +7 more.

open_urlB

Open a URL / deep link in an allowed dev app with an ACTION_VIEW intent.

Inputs: url (required), package (required), serial, observe, +1 more.

press_keyB

Press a key: back, home, enter, del, backspace, forward_del, tab, escape, app_switch, menu, search, power, wakeup, volume_up, volume_down, dpad_up, dpad_down, dpad_left, dpad_right, dpad_center, move_home, move_end, any KEYCODE_* name, or a key code number.

Inputs: key (required), serial, observe, settle_ms.

read_allA

Read a whole scrollable list or form in one call: scrolls it to the end and returns every entry it contains, in order. Much cheaper than scrolling step by step. Afterwards the list stays at the end.

Inputs: index, serial, direction, max_scrolls.

screenshotA

Take a screenshot (costs far more tokens than get_ui; use it for visuals the tree lacks). Its pixel coordinates are the screen coordinates every other tool uses. With region, returns a sharper crop for reading small text; a crop's pixels are NOT screen coordinates.

Inputs: region, serial.

scrollA

Scroll the content in a direction (down = reveal content below). Scrolls the given element (index), otherwise the largest scrollable element on screen (the screen centre only when no UI tree is available).

Inputs: direction (required), index, amount, serial, +2 more.

set_orientationA

Lock the screen to portrait or landscape, or re-enable auto-rotation.

Inputs: orientation (required), serial, observe, settle_ms.

start_emulatorA

Start an Android emulator (AVD) and wait until it has booted. Returns immediately if it is already running.

Inputs: avd (required), headless, cold_boot.

stop_appC

Force-stop an app.

Inputs: package (required), serial.

swipeA

Swipe/drag from one screen point to another. A slow swipe (duration >= 1000) acts like a drag.

Inputs: to_x (required), to_y (required), from_x (required), from_y (required), +4 more.

tapC

Tap an element (by index, text or id) or a screen point (x, y).

Inputs: x, y, id, text, +6 more.

type_textA

Type text into the focused field. Optionally tap a target first to focus it, clear it, and press enter afterwards.

Inputs: value (required), x, y, id, +9 more.

wait_forC

Wait until an element with the given text/id appears (or, with gone=true, disappears).

Inputs: id, gone, text, exact, +3 more.

Tool Schema Changelog

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

Loading tool schema changelog…

    TDQS

    B3.3/5.0

    Scored across 23 tools

    Disambiguation4/5

    Tools are well separated by resource and action: interaction (tap/long_press/swipe/scroll/type_text/press_key), capture (screenshot/get_ui), and diagnostics (logcat/list_crashes/get_crash) are clearly differentiated, and descriptions explicitly resolve the screenshot-vs-get_ui and read_all-vs-scroll boundaries. The only mild overlap is get_screen_info vs current_app, both of which report the foreground app, and get_ui vs read_all for reading content.

    Naming Consistency4/5

    Most tools follow a predictable verb_noun pattern (open_url, get_ui, list_devices, start_emulator, launch_app, clear_app_data, press_key). A few deviate to bare nouns/actions (screenshot, logcat, tap, swipe, current_app), which is readable but not fully uniform.

    Tool Count4/5

    At 23 tools it sits at the high end, but the surface is cleanly partitioned across device lifecycle, app lifecycle, UI interaction, capture and diagnostics, so each tool earns its place. It is slightly heavy but not bloated or redundant.

    Completeness4/5

    The set covers a full Android automation lifecycle: device/emulator management, app listing/launch/stop/clear-data, rich UI interaction, capture, and crash/log diagnostics, with sensible helpers like wait_for and read_all. Minor gaps remain (no install/uninstall or file push-pull, no multi-touch/pinch or double-tap gestures), but they are workaroundable.

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      Enables an agent to inspect and interact with Android emulators or physical devices via ADB, capturing UI snapshots, tapping nodes, typing text, and reading app logs.
      10
      MIT
    • A
      license
      A
      quality
      B
      maintenance
      Drives an Android emulator or a physical device over adb. The agent screenshots, reads the UI hierarchy with true device-pixel coordinates, taps and types, manages app lifecycle, reads logcat, and runs Gradle builds and tests.
      81
      18
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables AI assistants and IDEs to drive real Android devices and emulators for cross-app UI automation, testing, and diagnostics, including collecting Logcat output and screenshots.
      Apache 2.0