MCP server for Android device automation via ADB, providing tools for app management, UI interaction, file transfer, input simulation, and device inspection.
AndroidMCP demonstrates solid tool design with consistent naming (verb_noun pattern), comprehensive parameter descriptions, and well-structured schemas. All 30 tools follow a clear pattern with explicit InputSchema definitions. However, output schemas are not formally documented, responses are inferred from code (e.g., ToolCallResult.Json/Text). Tool descriptions average ~150 chars, meeting the 10 - 1024 baseline. Parameter descriptions are present and specific (e.g., 'Android device serial number or transportId'). Error handling is minimal, most tools return success/failure booleans without recovery guidance. No tool annotations (readOnlyHint, destructiveHint, idempotentHint) are present despite clear risk classifications in the metadata. Security is reasonable (no secrets in params), but permission gating and audit trails are absent.
Reset an app to a fresh-install state via `pm clear`. Wipes preferences, databases, caches, and revokes runtime permissions. The app stays installed.
Remove every non-home task from the recents overview via `am stack remove`. Skips `type=home` so the launcher stays alive. Returns the list of removed task ids.
Single-tap at (x, y) in screen pixel coordinates (origin: top-left). Coordinates come from dump_hierarchy bounds — use the centre.
Detailed device profile: build / display / cpu / gpu / sensors / cameras / network / sim / treble / features / properties. Returns a single JSON document from the agent's /device-info endpoint.
Dump the active window's accessibility tree. Default `format=lean` returns compact JSON (5–12× fewer tokens than XML); pass `format=xml` for the raw agent output. `interactive_only=true` further drops pure layout containers. If you already know what to tap, prefer `find_element` — single-element results are tiny.
Output schemas not formally documented. Tools return ToolCallResult.Json/Text but response structure is inferred from code, not declared in tool metadata. LLMs cannot plan downstream calls without knowing what fields to expect.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint) despite clear risk classifications in metadata. Tools like clear_app_data (DESTRUCTIVE) and stop_app (WRITE) lack explicit hints to guide agent behavior and prevent accidental misuse.
Error handling is minimal. Most tools return success/failure booleans without recovery guidance. E.g., launch_app returns {ok: false, output: '...'} but does not suggest next steps if the app is not found or already running.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 63 | 2026-07-28+ | v2 |
Find UI elements matching a selector in the current hierarchy. Returns all matches (single dump_hierarchy + match — no scrolling). Use scroll_until_visible if the element may be off-screen.
Single-package summary: versionName, versionCode, install paths, enabled state, target SDK. Faster than list_packages when you know the package up front.
Return the foreground activity component (package/Activity). Reads `dumpsys activity activities | grep ResumedActivity`.
Type literal text into the focused field via `adb shell input text`. ASCII only — for non-ASCII (CJK, emoji), use the clipboard route in a follow-up shell call.
Send a single Android keycode via `adb shell input keyevent`. Accepts symbolic names (BACK, HOME, MENU, ENTER, VOLUME_UP, VOLUME_DOWN, POWER, etc.) or numeric KeyEvent codes.
Launch an app by package name. Uses `monkey -p <pkg> 1` which resolves the launcher activity automatically — works whether or not you know the entry component.
Enumerate Android devices currently visible to adb. Returns serial, state, model, product and transportId for each. Always call this before any other tool to discover the device serial to pass. If two devices share the same serial, pass the per-device transportId (unique) as the 'serial' argument to target one unambiguously.
List installed packages with metadata (label, version, signature, install times, system flag) via the on-device agent. Set user_only=true to skip system apps.
Long-press at (x, y). Implemented as a zero-distance `input swipe` with the requested duration (Android's `input` has no native long-press).
Send BACK keyevent — go back one navigation step. Convenience wrapper.
Send HOME keyevent — return to launcher. Convenience wrapper around `key_event` with KEYCODE_HOME.
Send APP_SWITCH keyevent — open the recent-tasks overview. Convenience wrapper.
Download a file from the device to the host filesystem. Useful for collecting logs, screenshots, app data exports.
Upload a file from the host filesystem to the device. Useful for deploying configs, test fixtures, screenshots back to the device. Use install_apk for APKs.
Return the device's current display size in pixels (Override size if set, otherwise Physical size).
Capture the current screen via the on-device agent (UiAutomation API — much faster than `adb shell screencap`). Returns a PNG image content block.
Fling repeatedly in `direction` until the UI hierarchy stops changing (boundary reached) or max_steps is hit. Use this to jump to the top/bottom/leftmost/rightmost of a scrollable view in one call.
Scroll in `direction` until a selector matches, or timeout/max_scrolls is hit. Returns the matched element or error. Polls every 200ms; use this to find off-screen elements.
Enable or disable a package via `pm enable` / `pm disable-user`. Disabled packages stay installed but become invisible to launchers and intent resolution.
Force-stop a package via `am force-stop`. Tears down all processes and pending alarms for the app.
Swipe from (x1,y1) to (x2,y2) over `duration_ms` (default 300). Use ~150ms for fling, ~600ms for slow scroll.
One-shot directional swipe based on screen size — caller doesn't compute coordinates. `up` means content scrolls up (finger moves up, revealing content below).
Find an element by selector, then tap its center in one round-trip. Errors if the selector matches zero or more than one element (use index=N to disambiguate).
Block until a selector matches at least one element, or the timeout elapses. Polls the hierarchy every 200 ms. Returns the first match.
Wake the screen and dismiss the keyguard. Sends KEYCODE_WAKEUP then `wm dismiss-keyguard`. Idempotent if already unlocked. Does NOT enter a PIN/pattern/password — secure locks just surface the password prompt. Call this first when scripting starts cold.
No permission gating or audit trail. Tools that modify device state (clear_app_data, set_package_enabled, push_file) do not verify caller authority or log who invoked them. Compliance and incident response are unsupported.
Selector parameter in find_element, tap_element, wait_for_element, scroll_until_visible is typed as 'object' with no schema constraints. LLMs cannot infer valid selector structure (e.g., text, resource-id, class, index). Add a formal SelectorSchema definition or enum examples.