ui¶
Retrieving the current Android UI hierarchy (uiautomator dump) as inline
XML, and searching it by structured criteria (find_ui_elements,
wait_for_ui_element) — no temporary device-file location for the caller to
know or manage, no XML parsing on the caller's side. Input actions aren't
implemented here (see the input module for touch injection).
adb_automation_mcp.modules.ui.tools
¶
Module-level, statically-introspectable tool functions for the ui module.
Kept as plain top-level functions, never closures, so that documentation tooling and the registry meta-test can both introspect them directly.
dump_ui_hierarchy(ctx: Context, serial: str) -> UiHierarchyDumpResult
async
¶
Retrieve the device's current UI hierarchy: uiautomator dump.
uiautomator dump only writes its result to a file on the device, so
this dumps to a temporary path under /data/local/tmp, reads the XML
back inline over adb shell (no host filesystem write, no local_root
needed — the caller never has to know or manage the device-side path),
and always removes the temporary file afterward. To search the
hierarchy without handling XML yourself, use find_ui_elements; this
module doesn't inject any input actions (see the input module for that).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serial
|
str
|
The target device's adb serial (see list_connected_devices). |
required |
Returns:
| Type | Description |
|---|---|
UiHierarchyDumpResult
|
The serial, the full hierarchy xml (the raw |
Error handling
An unknown serial or unresponsive adb binary raises
DEVICE_NOT_FOUND/ADB_UNAVAILABLE. uiautomator dump itself failing
to run (e.g. the uiautomator binary is missing on this build)
raises UIAUTOMATOR_FAILED. The command running but finding no
inspectable window content right now (screen off, locked, or
otherwise no accessible root node) raises
UI_HIERARCHY_UNAVAILABLE — distinct from an empty-but-successful
hierarchy, which is returned as data with node_count=0, not raised.
A permission rejection raises PERMISSION_DENIED; the dumped temp
file vanishing before it could be read back raises
REMOTE_FILE_NOT_FOUND; any other failure raises a generic
BACKEND_ERROR.
Example
Called with serial="emulator-5554". A typical response:
{
"status": "success",
"message": "Dumped UI hierarchy (2 nodes) from emulator-5554.",
"data": {
"serial": "emulator-5554",
"xml": "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><hierarchy rotation=\"0\">...</hierarchy>",
"node_count": 2,
"success": true,
"output": "UI hierarchy dumped to: /data/local/tmp/adb_automation_mcp_ui_dump_....xml\n"
},
"error": null
}
Source code in src/adb_automation_mcp/modules/ui/tools.py
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | |
find_ui_elements(ctx: Context, serial: str, text: str | None = None, text_contains: str | None = None, resource_id: str | None = None, content_desc: str | None = None, class_name: str | None = None, package: str | None = None, clickable: bool | None = None, enabled: bool | None = None, limit: int = 50) -> UiElementMatchResult
async
¶
Search the current UI hierarchy for nodes matching structured criteria.
Captures the hierarchy the same way dump_ui_hierarchy does, then
filters <node> elements in Python and returns each match flattened to
the attributes callers act on (text, resource_id, class, bounds + the
bounds' center point, and the usual boolean state flags). At least one
criterion must be given; all supplied criteria are combined with AND.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serial
|
str
|
The target device's adb serial (see list_connected_devices). |
required |
text
|
str | None
|
Match nodes whose |
None
|
text_contains
|
str | None
|
Match nodes whose |
None
|
resource_id
|
str | None
|
Match nodes by resource-id — either the full "package:id/name" or just the bare "name" (which also matches a Compose testTag surfaced as a prefix-less resource-id). |
None
|
content_desc
|
str | None
|
Match nodes whose |
None
|
class_name
|
str | None
|
Match nodes by class — either the fully-qualified name ("android.widget.Button") or just the final segment ("Button"). |
None
|
package
|
str | None
|
Match nodes whose |
None
|
clickable
|
bool | None
|
Match nodes whose |
None
|
enabled
|
bool | None
|
Match nodes whose |
None
|
limit
|
int
|
Maximum number of matched elements to return (1-500, default 50). match_count still reports the true total; truncated says whether the list was cut. |
50
|
Returns:
| Type | Description |
|---|---|
UiElementMatchResult
|
The serial, match_count (total matches), returned_count (how many are in elements), truncated, and elements — each an object with text / resource_id / class_name / package / content_desc, the boolean flags (clickable, enabled, focused, checkable, checked, selected, scrollable, long_clickable, password), and bounds ({left, top, right, bottom, center_x, center_y} or null). Zero matches is a normal success result. |
Error handling
No criteria at all, or a limit outside 1-500, is rejected before any device round-trip (INVALID_ARGUMENT). An unknown serial or unresponsive adb binary raises DEVICE_NOT_FOUND/ADB_UNAVAILABLE; uiautomator failing raises UIAUTOMATOR_FAILED; no inspectable window content right now raises UI_HIERARCHY_UNAVAILABLE. A malformed / truncated hierarchy is treated as zero matches, not a crash. Other failures raise PERMISSION_DENIED / REMOTE_FILE_NOT_FOUND / BACKEND_ERROR as for dump_ui_hierarchy.
Example
Called with serial="emulator-5554", text="Phone". A typical response:
{
"status": "success",
"message": "1 UI element(s) matched on emulator-5554.",
"data": {
"serial": "emulator-5554",
"match_count": 1,
"returned_count": 1,
"truncated": false,
"elements": [
{
"text": "Phone",
"resource_id": "com.android.launcher3:id/icon",
"class_name": "android.widget.TextView",
"package": "com.android.launcher3",
"content_desc": "Phone",
"clickable": true,
"enabled": true,
"focused": false,
"checkable": false,
"checked": false,
"selected": false,
"scrollable": false,
"long_clickable": true,
"password": false,
"bounds": {"left": 100, "top": 200, "right": 300, "bottom": 400, "center_x": 200, "center_y": 300}
}
]
},
"error": null
}
Source code in src/adb_automation_mcp/modules/ui/tools.py
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | |
wait_for_ui_element(ctx: Context, serial: str, condition: WaitCondition = 'present', timeout_s: float = 10.0, poll_interval_s: float = 1.0, text: str | None = None, text_contains: str | None = None, resource_id: str | None = None, content_desc: str | None = None, class_name: str | None = None, package: str | None = None, clickable: bool | None = None, enabled: bool | None = None) -> UiWaitResult
async
¶
Poll the UI hierarchy until an element appears or disappears.
Re-captures the hierarchy every poll_interval_s and re-checks the same
structured criteria as find_ui_elements, until the condition holds or
timeout_s elapses. Replaces blind sleeps in automation. The loop is
always bounded — timeout_s is capped at 120s and poll_interval_s at
30s. At least one criterion must be given (AND-combined).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serial
|
str
|
The target device's adb serial (see list_connected_devices). |
required |
condition
|
WaitCondition
|
"present" (default) waits until at least one node matches; "absent" waits until no node matches. |
'present'
|
timeout_s
|
float
|
Maximum seconds to keep polling (0 exclusive to 120). Default 10. |
10.0
|
poll_interval_s
|
float
|
Seconds between hierarchy captures (0.1 to 30). Default 1. |
1.0
|
text
|
str | None
|
Match nodes whose |
None
|
text_contains
|
str | None
|
Match nodes whose |
None
|
resource_id
|
str | None
|
Match by full "package:id/name" or bare "name". |
None
|
content_desc
|
str | None
|
Match nodes whose |
None
|
class_name
|
str | None
|
Match by fully-qualified class or final segment. |
None
|
package
|
str | None
|
Match nodes whose |
None
|
clickable
|
bool | None
|
Match nodes whose |
None
|
enabled
|
bool | None
|
Match nodes whose |
None
|
Returns:
| Type | Description |
|---|---|
UiWaitResult
|
The serial, condition, satisfied (always True on return), match_count and elements at the moment the condition held (for "absent" that's 0 / []), waited_s (roughly how long the loop ran), and poll_count (how many captures it took). |
Error handling
No criteria, an out-of-range timeout_s / poll_interval_s, or a bad condition is rejected before any device round-trip (INVALID_ARGUMENT). The condition never holding within timeout_s raises TIMEOUT (retryable), with the last seen match count in its details. An unknown serial / adb failure raises DEVICE_NOT_FOUND/ADB_UNAVAILABLE; uiautomator or hierarchy failures raise UIAUTOMATOR_FAILED / UI_HIERARCHY_UNAVAILABLE / PERMISSION_DENIED / BACKEND_ERROR as for dump_ui_hierarchy.
Example
Called with serial="emulator-5554", text="Success", timeout_s=5. A typical response:
{
"status": "success",
"message": "UI condition 'present' satisfied on emulator-5554 after 1.2s (2 check(s)).",
"data": {
"serial": "emulator-5554",
"condition": "present",
"satisfied": true,
"match_count": 1,
"waited_s": 1.2,
"poll_count": 2,
"elements": [{"text": "Success", "resource_id": "", "class_name": "android.widget.TextView", "package": "com.example.app", "content_desc": "", "clickable": false, "enabled": true, "focused": false, "checkable": false, "checked": false, "selected": false, "scrollable": false, "long_clickable": false, "password": false, "bounds": null}]
},
"error": null
}
Source code in src/adb_automation_mcp/modules/ui/tools.py
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 | |