mobila
by mamayevets
README.md
# mobila
> One command → a real Android phone, controlled by Claude / ChatGPT /
> Cursor / any MCP client.
`mobila` boots a local Android emulator, installs apps for it, and exposes
the whole thing as an MCP server that an AI client can drive — tap, type,
swipe, screenshot, install — in plain English. Optionally publishes the MCP
server on a stable public URL so the same phone is reachable from a remote
ChatGPT Custom GPT or Claude Code on another machine.
Built on top of [`mobilerun`](https://github.com/droidrun/mobilerun)
(vendored under [`vendor/mobilerun`](vendor/mobilerun); MIT,
[© Niels Schmidt](vendor/mobilerun/UPSTREAM.md)) plus a small layer of
glue — recipes, install helpers, MCP transport, persistent ngrok tunnel.
Status: macOS works end-to-end. Linux + Windows packaging — next release.
---
## Install (macOS)
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/mamayevets/mobila/main/scripts/install/mac.sh)
```
…or from a cloned repo:
```bash
git clone https://github.com/mamayevets/mobila && cd mobila
bash scripts/install/mac.sh
```
The bootstrap installs: Xcode Command-Line Tools, Homebrew, `uv`, and the
`mobila` package itself as a [`uv tool`](https://docs.astral.sh/uv/guides/tools/).
## First run
```bash
mobila setup # Android SDK + scrcpy + AVD creation (~5 min, one-time)
mobila # boots the phone + opens scrcpy mirror window
```
## Daily use
| Command | What it does |
|---|---|
| `mobila` | Boot the phone + open scrcpy |
| `mobila stop` | Tear down everything |
| `mobila view` | Open scrcpy on a phone that's already running |
| `mobila status` | What's running right now |
| `mobila logs` | Tail the emulator log |
| `mobila shell` | ADB shell into the phone |
| `mobila install <X>` | Install an app (preset / package id / URL / .apk file) |
### Installing apps without a Google account
```bash
mobila install telegram # presets: telegram, whatsapp, signal,
# instagram, tiktok, youtube, gmail,
# chrome, firefox, fdroid, aurora, spotify
mobila install com.spotify.music # any package id (resolved via apkeep)
mobila install ~/Downloads/foo.apk # local file
mobila install https://example.com/foo.apk
```
Presets and package-id installs need `brew install apkeep`.
---
## AI control: `mobila serve`
`mobila serve` exposes the phone as an [MCP](https://modelcontextprotocol.io)
server so AI clients can drive it. Three modes:
| Mode | Command | For |
|---|---|---|
| **stdio** (default) | `mobila serve` | Claude Desktop (launches us as a child process) |
| **HTTP, local** | `mobila serve --http` | Claude Code, Cursor on the same machine |
| **HTTP, public** | `mobila serve --tunnel` | ChatGPT Custom GPT, Claude Code on another machine, remote demos |
`--http` and `--tunnel` are protected by a Bearer token stored at
`~/.mobila/token` (chmod 600). The token survives restarts; rotate with
`mobila serve --rotate-token`.
### Public access in one wizard
```bash
mobila tunnel setup # one-time: paste ngrok authtoken + dev domain
mobila serve --tunnel # → https://<your-dev-domain>.ngrok-free.dev
```
We use ngrok's free-tier dev domain (one persistent
`*.ngrok-free.dev` host per account) so the public URL stays the same
across reboots, restarts, even reinstalls — register it once in your AI
client and it keeps working. Setup walks you through both pastes:
```
─────────────────────────────────────────────────────────────
mobila tunnel setup
─────────────────────────────────────────────────────────────
✓ ngrok installed
Step 1/2 — ngrok authtoken
Opening https://dashboard.ngrok.com/get-started/setup/macos…
Paste your authtoken: ***
✓ authtoken saved
Step 2/2 — your assigned dev domain
Your dev domain: cute-name-xyz.ngrok-free.dev
✓ domain saved
Done. Run: mobila serve --tunnel
Your public URL is always: https://cute-name-xyz.ngrok-free.dev
```
### Tools the AI sees
- `task_submit(prompt)` — natural-language task → delegated to mobilerun
- `task_logs / task_status / task_attach / task_kill` — track that task
- `phone_screenshot()` → PNG
- `phone_install(target)` — preset / package id / URL / file
- `phone_status()` — is the emulator alive?
- `dev_reload()` — hot-reload the server (useful while iterating)
Wire it into Claude Code:
```bash
claude mcp add --transport http mobila https://<your-dev-domain>.ngrok-free.dev \
--header "Authorization: Bearer $(mobila serve --show-token)"
```
…or into Claude Desktop / Cursor via JSON (`mobila serve --tunnel`
prints the snippet on startup).
---
## Tuning for your Mac
Override emulator resources via env vars:
```bash
AVD_RAM_MB=2048 AVD_CORES=4 mobila # 16 GB Mac
AVD_RAM_MB=1024 AVD_CORES=2 mobila # 8 GB Mac (default)
```
## Platforms
- ✅ macOS (Apple Silicon + Intel)
- ⏳ Linux (via redroid) — next release
- ⏳ Windows (via WSL2 + redroid) — next release
## License
[MIT](LICENSE). Vendored `mobilerun` is also MIT — see
[`vendor/mobilerun/UPSTREAM.md`](vendor/mobilerun/UPSTREAM.md) for the
pinned upstream version and our patches.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing