ai-mobile-tester
ai-mobile-tester
An MCP server that lets AI assistants (Claude Code, Cursor, Windsurf, …) write and run Android UI tests by natural language — for native and WebView/hybrid apps — over ADB. No Appium, no Chromedriver. You describe a test in plain English; the assistant captures the screen, authors a durable YAML flow, then replays it deterministically with self-healing locators and an HTML report.
How it works
- Observe — the assistant captures a compact, token-frugal snapshot of the screen —
observe_ui(native) orobserve_webview(WebView) — with stable selectors and[ref]handles. - Author — it writes a YAML flow (tap / input / assert / scroll steps) from those selectors. You keep the flow file.
- Validate —
validate_flowlints the flow offline (schema, fragile selectors, undefined variables) — no device needed. - Run —
run_flowexecutes it deterministically on the device (no AI per run), self-heals drifted native locators, and writes an HTML report.
The YAML flow is the durable artifact: author once with the assistant, then replay forever (CI, daily, …) at zero AI cost and with no drift.
Prerequisites
- Node.js 18+
- Android SDK Platform Tools —
adbin your PATH (download) - An Android device with USB debugging enabled, or a running emulator
- For WebView / hybrid apps: the app's WebView must be built with
WebView.setWebContentsDebuggingEnabled(true)(debug builds usually have it)
Install & set up
npm install -g ai-mobile-tester
npx ai-mobile-tester init
The wizard checks adb, registers the MCP server with Claude Code (via claude mcp add, writing ~/.claude.json) and Claude Desktop (its claude_desktop_config.json, if installed), and installs the /run-test slash command. Restart Claude Code / Claude Desktop and the tools are available.
Manual / advanced MCP configuration
init is the easy path, but you can register the server by hand for any MCP client. The server runs on stdio via the serve subcommand:
{
"mcpServers": {
"ai-mobile-tester": { "command": "npx", "args": ["-y", "ai-mobile-tester", "serve"] },
},
}
For Claude Code you can also run claude mcp add --scope user ai-mobile-tester -- ai-mobile-tester serve. For the most stable setup, install globally (npm i -g ai-mobile-tester) and point at ai-mobile-tester serve (or the absolute node <prefix>/dist/index.js that init writes) — the npx form re-resolves from a cache that npm can garbage-collect.
Example — a native flow
appId: com.example.app
env:
TEST_USER: "you@example.com"
TEST_PASS: "your-password"
---
- launchApp
- tapOn: { id: login_button }
- inputText: { into: { id: email }, text: "${TEST_USER}" }
- inputText: { into: { id: password }, text: "${TEST_PASS}" }
- tapOn: "Sign in"
- assertVisible: "Welcome"
Example — a WebView flow
appId: com.example.shop
env:
TEST_USER: "you@example.com"
TEST_PASS: "your-password"
---
- launchApp
- switchContext: "WEBVIEW_com.example.shop@shop.example.com"
- assertVisible: { css: "#email" }
- inputText: { into: { css: "#email" }, text: "${TEST_USER}" }
- tapOn: { css: '[data-testid="signin"]' }
- assertVisible: { css: ".order-summary" }
- switchContext: NATIVE_APP
Validate offline, then run it:
/run-test path/to/flow.yaml
…or just ask the assistant to run it.
Run in CI (no AI)
Once a flow exists, run it headlessly — no Claude, no MCP, just adb and a device:
ai-mobile-tester run flow.yaml --junit results.xml # exit 0 pass · 1 fail · 2 could-not-run
ai-mobile-tester validate flow.yaml # offline pre-check
Portable to any CI (GitHub Actions, MacStadium, GitLab, …). See docs/ci-runner.md.
Documentation
- Authoring UI tests — the full workflow — start here.
- YAML flow format reference — every command and selector.
- Testing WebView / hybrid apps — drive WebViews by CSS selector.
- Compose testability — make Jetpack Compose elements addressable with
testTags. - Running flows in CI — exit codes, secrets, JUnit, a portable recipe.
MCP tools
Flow engine:
| Tool | Description |
|---|---|
observe_ui |
Compact, token-frugal snapshot of the native screen (actionable elements get a [ref]) |
observe_webview |
Lists the app's WebView pages and returns a compact DOM snapshot of the richest, by CSS selector |
webview_tap |
Tap a WebView element by css/text, then return the updated DOM (interactive observe→act→observe) |
webview_input |
Type into a WebView field by css/text, then return the updated DOM |
input_text |
Type into a field (optionally focus it by selector first) |
validate_flow |
Statically validate a YAML flow (schema + lints), no device needed |
run_flow |
Run a validated YAML flow deterministically; self-heals native locators; writes an HTML report |
check_testability |
Report Compose testTag coverage so elements are addressable |
Device & app: list_devices, device_info, connect_device, launch_app, install_apk, uninstall_app, clear_app_data, force_stop.
Interaction: tap, tap_xy, tap_element, long_press, swipe, scroll_down, scroll_up, type_text, press_key.
Observation & assertions: take_screenshot, dump_ui, find_element, get_current_activity, is_element_visible, assert_visible, assert_not_visible, assert_text, wait_for_element, wait_for_text, wait_for_activity.
Development
git clone <your-fork> ai-mobile-tester && cd ai-mobile-tester
npm install
npm run build
npm test # the test suite
npm run lint && npm run format:check
Roadmap
- Phase 1 (current): Android (native + WebView) via ADB, distributed as an npm package.
- Phase 2: iOS (
xcrun simctl+idb), web (Playwright). - Phase 3: cloud device farms (Firebase Test Lab, BrowserStack), CI/CD integration.
- Phase 4: SaaS — web dashboard, team collaboration, history & analytics.
License
MIT