MCP server for Android accessibility automation via AccessibilityService
droid-mcp demonstrates solid tool design with consistent naming (verb_noun pattern), comprehensive parameter descriptions, and explicit input schemas across all 15 tools. Tool annotations (destructiveHint) are present. However, output schemas are not formally documented in the visible code, responses are described narratively rather than as structured JSON schemas. Error handling uses short-form codes (accessibility_not_enabled, node_not_found, gesture_failed) with some recovery guidance, but lacks systematic categorization (retryable vs fatal). Parameter constraints are well-described (e.g., max_nodes 1-2000, timeout_ms 100-60000) but not enforced via JSON Schema enums where applicable (e.g., direction: forward|backward, action: back|home|recents). Tool composition is strong, each tool has a single responsibility, and output fields chain well (e.g., find_node returns view_id for use in click_node). Descriptions average ~150 chars, meeting the 10-1024 baseline. No secrets exposed as parameters.
Perform ACTION_CLICK on a node matched by the selector.
Find a node by text / contentDescription / view-id / class and ACTION_CLICK it in one call. Composition of find_node + click_node. Errors with `node_not_found` when the match doesn't appear.
Search the active window's UI tree for nodes matching any combination of text, view-id, class name, or package. Returns up to `limit` matches with the same shape as `query_screen` entries.
Dispatch a touch gesture on the screen via AccessibilityService.dispatchGesture. The gesture is described as a single ordered sequence of points and an optional duration; use this for swipes, drags, or tap-by-coordinate when no node matches. Single-stroke only — cannot express multi-finger gestures like pinch.
Return the foreground package name plus the root node's class (typically the foreground activity's view-root class). Cheap; safe to poll.
Dispatch a system-wide AccessibilityService global action: back, home, recents, notifications, quick_settings, power_dialog, lock_screen, screenshot.
Output schemas not formally documented. Tools describe results narratively (e.g., 'Returns nodes ranked by usefulness') but do not expose structured JSON Schema for response fields. LLMs cannot reliably extract fields or plan downstream calls without explicit output schemas.
Enum constraints not enforced via JSON Schema. Parameters like 'direction' (forward|backward), 'action' (back|home|recents|...), and 'match_kind' (text|desc|id|class) are described as free-form strings rather than JSON Schema enums. This invites hallucinated values and requires runtime validation.
Error handling lacks systematic categorization. Tools return short-form codes (accessibility_not_enabled, node_not_found, gesture_failed) but do not classify errors as retryable, user-fixable, or fatal. LLMs cannot determine whether to retry, ask the user, or abort.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 67 | 2026-07-28+ | v2 |
Perform ACTION_LONG_CLICK on a node matched by the selector.
Dispatch a long-press gesture at the given screen coordinates. Holds for `duration_ms` (default 800ms).
Dump the active window's AccessibilityNodeInfo tree as a flat list of nodes with text / bounds / class / view-id / action capabilities. **Returns nodes ranked by usefulness:** clickable > has-text > scrollable > rest. When truncated to `max_nodes`, the highest-ranked nodes are kept — token-bound callers can trust the order. Password-flagged nodes report `is_password = true` with text / content_description masked.
Scroll a scrollable node forward or backward.
Repeatedly swipe in `direction` (reading semantics: 'down' reveals content below, 'up' reveals content above, 'left'/'right' likewise) until `match` appears in the active window's UI tree or `max_scrolls` exhausts. Returns the matched node's selector fields on hit.
Replace the text of an editable node via ACTION_SET_TEXT. Works for most native EditText fields; some apps disable accessibility text-set, in which case use the IME tools instead.
Capture the screen via AccessibilityService.takeScreenshot — no MediaProjection consent prompt required. Returns a base64-encoded PNG (default) or JPEG. Requires API 30+. Returns a specific error when the foreground window has FLAG_SECURE set or the screenshot rate-limit was hit.
Dispatch a single-tap gesture at the given screen coordinates via the accessibility service. Use when no node matches the selector or for raw coord-based input.
Poll the active window's UI tree until a node matching `text` appears or `timeout_ms` expires. Returns the matched node's projection on success.
Destructive operations lack confirmation step. Tools like click_node, set_node_text, and gesture modify device state but do not support dry-run or confirmation. Agents cannot preview side effects before committing.
Transport mechanism unknown. Repository does not clearly document whether the server uses HTTP, SSE, STDIO, or another transport. This blocks assessment of remote accessibility and hosted client compatibility.