android-build-mcp
# android-build-mcp
[](https://github.com/jjs03111/android-build-mcp/actions/workflows/ci.yml)
[](LICENSE)
An [MCP](https://modelcontextprotocol.io) server that gives a coding agent hands and eyes on an Android device.
Your agent can already shell out to `gradlew` and `adb`. The problem is what that costs: a failed build dumps four thousand lines of task progress into the context window, tap coordinates get guessed off a screenshot, and a JDK/Gradle mismatch sends it bisecting application code for an hour over an error that had nothing to do with the app.
This is the build-debug loop made cheap. 19 tools covering build, test, install, launch, screenshot, UI inspection, input, logs, and toolchain diagnosis — each one shaped to return the smallest thing that answers the question.
```
You: "The start button doesn't do anything on the tablet. Fix it."
Agent: android_doctor -> toolchain OK, Gradle 8.7 / JDK 17 compatible
android_build -> assembleDebug + install + launch
android_set_rotation -> landscape
android_dump_ui -> Button "start" at (1580, 890), clickable=false
android_logcat -> IllegalStateException in onMeasure
...reads the layout, edits it, rebuilds, taps the button, confirms
```
## Install
Requires Node 18+, the Android SDK platform-tools, and a JDK (Android Studio's bundled one is found automatically).
Not on npm yet — install from source:
```bash
git clone https://github.com/jjs03111/android-build-mcp
cd android-build-mcp
npm install && npm run build
```
**Claude Code**
```bash
claude mcp add android -- node /absolute/path/to/android-build-mcp/dist/index.js
```
**Any MCP client** — add to the client's server config:
```json
{
"mcpServers": {
"android": {
"command": "node",
"args": ["/absolute/path/to/android-build-mcp/dist/index.js"]
}
}
}
```
No configuration needed if you have a standard Android Studio install. Run `android_doctor` to confirm.
## What this does beyond wrapping adb
**It knows why builds fail.** JDK/Gradle incompatibility is checked *before* the build starts, so you get "JDK 23 is newer than Gradle 8.7 supports (max 22)" instead of `Unsupported class file major version 67` after a two-minute wait. `SDK location not found`, signature mismatches on install, and INSTALL_FAILED codes all come back with the actual remedy attached.
**It doesn't flood the context.** A failed Gradle build is thousands of lines, nearly all of it task progress. The failure summary keeps the compiler diagnostics and the `FAILURE:` block and drops the rest — typically a 50%+ reduction, all of it noise. Logcat filters to your app's pid. `android_dump_ui` returns text and coordinates instead of a megabyte of screenshot when what you needed was the label on a button.
**It recognises failures it has seen before.** Every failure path — Gradle, install, crash logs — runs its output past a set of known Android failure modes whose symptoms point nowhere near their causes. When one matches, the fix arrives attached to the error, not in a document somebody has to remember to consult:
- Gradle refusing to run on a too-new JDK (`Unsupported class file major version`)
- `SDK location not found` on a fresh clone, because `local.properties` is gitignored
- OEM battery management killing foreground services within seconds on Samsung and Xiaomi
- `adb shell input text` silently dropping every non-ASCII character
- `GridLayout` cells collapsing when weights meet `wrap_content`
- Package visibility filtering returning empty lists on Android 11+
- Signature mismatches on install, and stale package references after an external uninstall
`android_pitfalls` exposes the same set for browsing and searching. An agent that hits one of these otherwise starts bisecting application code that was never the problem.
**It finds your toolchain.** adb via `ANDROID_HOME`, the platform's standard SDK locations, then `PATH`. The JDK via Android Studio's bundled JBR — chosen because it is version-matched to AGP, unlike whatever is on `PATH`. Gradle is launched through the wrapper's main class rather than `gradlew.bat`, which both sidesteps Node's refusal to spawn `.bat` files and guarantees the validated JDK is the one that runs.
## Tools
**Device**
| Tool | Purpose |
|---|---|
| `android_list_devices` | Connected devices with Android version, screen size, density, rotation |
| `android_connect_wifi` | Promote a USB device to wireless adb, or reconnect to a known address |
**Build**
| Tool | Purpose |
|---|---|
| `android_build` | Gradle assemble, optionally install and launch in one call |
| `android_test` | Run unit or instrumented tests, reporting which ones failed |
| `android_install` | Install an APK |
| `android_launch` | Start an app, optionally cold |
| `android_clear_data` | Wipe an app's data without uninstalling it |
| `android_uninstall` | Remove an app |
**Inspect**
| Tool | Purpose |
|---|---|
| `android_screenshot` | PNG of the screen, inline or to a file |
| `android_dump_ui` | View hierarchy as text, with tap coordinates for every element |
| `android_logcat` | Logs filtered by package, tag, priority, or the crash buffer |
| `android_shell` | Arbitrary `adb shell` command |
**Interact**
| Tool | Purpose |
|---|---|
| `android_tap` | Tap or long-press |
| `android_swipe` | Swipe, scroll, drag |
| `android_input_text` | Type into the focused field |
| `android_key_event` | Back, Home, Enter, arrows, volume |
| `android_set_rotation` | Force an orientation, or restore auto-rotate |
**Diagnose**
| Tool | Purpose |
|---|---|
| `android_doctor` | Check adb, SDK, JDK, devices, and JDK/Gradle compatibility |
| `android_pitfalls` | Look up known failure modes by error text, topic, or keyword |
## How this compares
**vs. letting an agent run `gradlew` and `adb` through a shell tool.** It already can, and when a build succeeds the difference is small. The difference shows up on failure and at scale: a failed build here returns the compiler errors instead of several thousand lines of task progress, a JDK/Gradle mismatch is caught before the build rather than two minutes into it, logs come back scoped to your app's process, and `android_dump_ui` hands over exact tap coordinates instead of the agent estimating them off a screenshot. Shell output is also unstructured, so every result has to be re-read by the model.
**vs. [mobile-mcp](https://github.com/mobile-next/mobile-mcp) and other device-control servers.** Those cover device automation — tap, swipe, screenshot, element inspection — and mobile-mcp covers iOS too, which this does not. The overlap is real, and if driving a device is all you need, they are the more established choice. What they do not cover is the build side: compiling the project, resolving the application id out of Gradle, installing, running tests, and explaining why the toolchain refused. That is what this server is for. Testing an app you did not build? Prefer theirs. Writing the app? This closes the edit-run-read cycle.
**vs. Android Studio.** Not a competitor. This exists so an agent can do the parts of the loop that do not need a person watching. Keep the IDE open.
## Configuration
Everything is auto-detected. Override only if you need to:
| Variable | Purpose |
|---|---|
| `ANDROID_HOME` / `ANDROID_SDK_ROOT` | Android SDK root |
| `ANDROID_MCP_ADB` | Path to the adb binary |
| `ANDROID_MCP_JAVA_HOME` | JDK to build with |
## Security
`android_shell` runs arbitrary commands on the connected device, and the build tools execute the target project's Gradle wrapper — which is code from that project. Point this at repositories you trust, the same way you would before opening one in an IDE. Nothing is sent anywhere: every tool talks only to the local adb server and the local filesystem.
`android_clear_data` and `android_uninstall` destroy app data irrecoverably; both are marked with `destructiveHint` so clients that gate destructive tools can prompt before running them.
## Known limitations
- `android_input_text` cannot type non-ASCII characters, nor the literal sequence `%s`. Both are limitations of Android's `input text` command: it is ASCII-only, and it decodes `%s` to a space with no escape available (even `%%s` decodes to `% `). The tool rejects both cases with an explanation rather than silently mangling the text. Use an adb-driven IME (ADBKeyBoard) for CJK.
- `android_dump_ui` cannot see inside `WebView` content; uiautomator only exposes the WebView node itself. Use `android_screenshot` for WebView-based UIs.
- `android_dump_ui` cannot see past a locked screen either — you get the keyguard, not the app.
- Wireless adb requires the host and the device to be on the same LAN. A VPN interface on the host does not bridge to the device's network, and routers with AP isolation block it outright. `android_connect_wifi` detects the subnet mismatch case and says so rather than reporting a bare timeout.
- Release builds require the project's own signing config.
- Developed and manually verified on Windows against a physical device (Galaxy A16, Android 16) and an emulator (API 35). macOS and Linux run the unit tests and a server smoke check in CI, and Linux additionally runs the full end-to-end suite on an emulator — but neither has been driven by hand, so rough edges in toolchain discovery are likelier there. Reports welcome.
## Development
```bash
npm install
npm run build
npm test # unit tests
npm run typecheck
```
`npm test` needs no device. The end-to-end suite does — start an emulator or plug in a phone, then:
```bash
node test/e2e/run.mjs
```
It prefers an emulator when one is running, so it will not rotate the screen or inject input on a phone you happen to have plugged in.
## Contributing
The most useful contribution is a pitfall. If some Android failure cost you an afternoon because the symptom pointed nowhere near the cause, add it to `src/services/pitfalls.ts`: the symptom as you observed it, the actual cause, the fix, and — if the error has a recognisable signature — a regex so it fires automatically at the moment of failure instead of waiting to be looked up.
Bug reports that include the output of `android_doctor` are much faster to act on.
## License
MIT
TDQS
Scored across 19 tools
Each tool has a clearly distinct purpose: device enumeration, Wi-Fi connection, build, test, app lifecycle (install/launch/clear/uninstall), UI interaction (screenshot/dump/tap/swipe/input/key/rotation), and diagnostics (logcat/shell/doctor). Even overlapping actions like android_build with install=true are explicitly separated from android_install and android_launch, so an agent can reliably pick the right tool.
All tools share the 'android_' prefix and snake_case, but the second part mixes styles: some are verb_noun (list_devices, clear_data, set_rotation), some are single verbs (build, test, install, tap), and some are nouns (pitfalls, logcat, shell, doctor). This is readable and predictable in terms of prefix, but the verb/noun pattern is not consistent across the set.
With 19 tools, the count is slightly above the ideal 3–15 range, but each tool covers a distinct aspect of Android development (device management, build/test, app lifecycle, UI automation, diagnostics). The number feels appropriate for the broad scope, and none are redundant, so it earns a slightly-over-but-reasonable rating.
The tool surface covers the full lifecycle: build, test, install, launch, uninstall, clear data; UI interaction (tap, swipe, input, key events, rotation, screenshot, UI dump); device discovery and Wi-Fi connection; logcat with crash analysis; arbitrary shell access; and a toolchain doctor. There are no obvious dead ends, and the shell tool acts as an escape hatch for anything not explicitly covered.