Expo Android MCP
MCP server for Android emulator automation via ADB.
Open source Open in the app JSON README (API)
About
MCP server for Android emulator automation via ADB.
Details
- Kind
- MCP servers
- Topic
- No topic detected
- Publisher
- frndchagas
- Origin
- official
- Category
- ferramentas
- Transport
- local
- Version
- 0.5.2
- Stars
- 4
- Open pull requests
- 1
- Last push
- 2026-09-08T01:24:59Z
- Repository state
- ativo
- Language
- TypeScript
- License
- MIT
- Added
- 2026-08-29 03:02:48
- Updated
- 2026-08-29 03:02:48
- Origin id
io.github.frndchagas/expo-android
README
# expo-android
[](https://www.npmjs.com/package/@fndchagas/expo-android)
[](https://www.npmjs.com/package/@fndchagas/expo-android)
[](LICENSE)
[](package.json)
[](https://www.typescriptlang.org/)
[](https://github.com/frndchagas/expo-android/actions/workflows/ci.yml)
MCP server for Android emulator automation via ADB.
## Requirements
- Node 18+
- Android SDK platform-tools (adb) available
- Android emulator or device connected
Verify adb:
```bash
adb devices
```
## Install
**Claude Desktop, one-click:** download [`expo-android.mcpb`](https://github.com/frndchagas/expo-android/releases/latest/download/expo-android.mcpb) from the latest release and drag it into **Settings → Extensions**. You can leave both fields empty — adb is auto-detected and the only connected device is used by default.
**Via npm:**
```bash
npm install -g @fndchagas/expo-android
# or
npx -y @fndchagas/expo-android
```
## Quickstart
1. Start an emulator or connect a device.
2. Run `doctor` to validate adb + device selection.
3. Use `inspect`, `tapElement`, `inputText`, etc.
Example:
```ts
await client.callTool({ name: 'expo-android.doctor', arguments: {} });
await client.callTool({
name: 'expo-android.inspect',
arguments: { onlyInteractive: true, maxElements: 200 },
});
```
## Use with Claude Code CLI
```bash
claude mcp add expo-android \
--env ADB_PATH="$HOME/Library/Android/sdk/platform-tools/adb" \
--env ADB_SERIAL="auto" \
-- npx -y @fndchagas/expo-android
```
## Use with OpenAI Codex CLI
```bash
codex mcp add expo-android \
--env ADB_PATH="$HOME/Library/Android/sdk/platform-tools/adb" \
--env ADB_SERIAL="auto" \
-- npx -y @fndchagas/expo-android
```
Or edit `~/.codex/config.toml`:
```toml
[mcp_servers.expo-android]
command = "npx"
args = ["-y", "@fndchagas/expo-android"]
env = { ADB_PATH = "/Users/you/Library/Android/sdk/platform-tools/adb", ADB_SERIAL = "emulator-5554" }
```
Serial selection priority:
`serial` param (per tool call) → `setDevice` override → `ADB_SERIAL` env → auto (if only one device).
## Environment variables
| Variable | Default | Description |
| --- | --- | --- |
| `ADB_PATH` | `adb` | Path to adb executable |
| `ADB_SERIAL` | optional | Device serial to target (`auto` to clear and auto-detect) |
| `ADB_TIMEOUT_MS` | `15000` | Timeout for adb commands |
| `ADB_MAX_BUFFER_MB` | `10` | Max output buffer size |
| `ADB_DEBUG` | `0` | Log adb diagnostics to stderr |
| `MCP_TRANSPORT` | `stdio` | Transport: `stdio`, `http`, or `both` |
| `PORT` | `7332` | HTTP port when using http/both |
## Troubleshooting
### adb not found (spawn adb ENOENT)
The server starts even when adb is missing — tools return the `ADB executable not found`
error until adb becomes reachable (run `doctor` to diagnose). To fix it, set `ADB_PATH`
or export an SDK path:
```bash
export ADB_PATH="$HOME/Library/Android/sdk/platform-tools/adb"
# or
export ANDROID_HOME="$HOME/Library/Android/sdk"
```
If multiple devices are connected, set `ADB_SERIAL` to the target device.
You can also run `setDevice` at runtime:
```ts
await client.callTool({
name: 'expo-android.setDevice',
arguments: { serial: 'emulator-5554' },
});
```
If you update PATH or SDK variables, restart the MCP process so it can pick up
the new environment.
## Tests
```bash
npm run build
npm test
```
## Tools
Tool names are plain identifiers (e.g. `tap`); your MCP client prefixes them with the server name you registered.
- `devices` — list connected devices and emulators.
- `doctor` — validate adb availability and show connected devices.
- `setDevice` — override the active device serial for this MCP process.
- `inspect` — UI dump parsed into elements with a summary (screenshot optional).
- `screenshot` — capture a screenshot only (base64 or file path).
- `findElement` — return elements that match search criteria.
- `tapElement` — find an element and tap its center.
- `waitForElement` — wait until an element appears (optionally with state checks).
- `assertElement` — verify element existence and state.
- `tap` — tap at x/y coordinates.
- `swipe` — swipe between coordinates.
- `longPress` — press and hold at coordinates.
- `inputText` — type text in the focused field.
- `keyEvent` — send Android key events (e.g., BACK, HOME).
- `openApp` — launch an app by package name.
- `listPackages` — list installed package names.
- `installExpoGo` — download the pinned Expo Go APK and install it via `adb install -r` (the `url` override only accepts official `github.com/expo/expo-go-releases` URLs).
Every tool declares MCP annotations (`readOnlyHint`/`destructiveHint`), so clients can auto-approve inspection tools and gate the ones that drive the device.
## Search criteria
These tools accept flexible search inputs: `findElement`, `tapElement`,
`waitForElement`, `assertElement`.
Common fields:
- `text`, `textContains`
- `contentDesc`, `contentDescContains`
- `resourceId`, `resourceIdContains`
- `class`
- `normalizeWhitespace`, `caseInsensitive`
## MCP usage examples
### Inspect
```ts
const result = await client.callTool({
name: 'expo-android.inspect',
arguments: { onlyInteractive: true, includeScreenshot: false, maxElements: 200 },
});
```
Inspect options:
- `includeScreenshot` (default: `false`)
- `screenshotMode`: `base64` or `path`
- `screenshotPath`: optional file path when using `path`
- `maxElements`: limit elements returned
- `includeElements`: return elements or summary only
### Doctor
```ts
await client.callTool({
name: 'expo-android.doctor',
arguments: {},
});
```
### Override serial per call
```ts
await client.callTool({
name: 'expo-android.tapElement',
arguments: { text: 'Search', serial: 'emulator-5554' },
});
```
### Tap element
```ts
await client.callTool({
name: 'expo-android.tapElement',
arguments: { text: 'Private account' },
});
```
### Wait + assert
```ts
await client.callTool({
name: 'expo-android.waitForElement',
arguments: { text: 'Save', timeout: 10000, shouldBeClickable: true },
});
await client.callTool({
name: 'expo-android.assertElement',
arguments: { text: 'Private account', shouldBeChecked: true },
});
```