Phone MCP
Enables remote control of Android phones through an installed APK, allowing agents to list devices, capture screenshots, inspect the UI tree, open URLs/deep links, and perform taps, swipes, and text input without ADB, root, or an emulator.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Phone MCPtake a screenshot of the current screen"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
phone-mcp
Remote-control an Android phone from an AI agent — no ADB, no root, no emulator. Install one APK on a real phone, point it at your server, and an MCP client (Claude, etc.) can list devices, take screenshots, open links/deep-links, read the UI tree, and drive taps/swipes/typing.
┌──────────────┐ MCP (stdio) ┌─────────────────────┐ WebSocket ┌──────────────────┐
│ AI agent │ ◄─────────────► │ broker server │ ◄────────────► │ Android phone │
│ (MCP client)│ │ (Bun + WebSocket) │ cmd + result │ (this APK) │
└──────────────┘ └─────────────────────┘ └──────────────────┘A single token (from an env var) guards both sides: the MCP authenticates to the
server with it, and the phone connects to the server with it. See
ARCHITECTURE.md for the full design.
Monorepo layout
packages/
shared/ # wire protocol + shared TypeScript types
server/ # Bun broker: device WebSocket + token-guarded agent HTTP API
mcp/ # MCP server (stdio) that drives the broker on behalf of an agent
android/ # the Kotlin APK (foreground service, accessibility, media projection)
Dockerfile # hosts the broker server
.github/ # CI (typecheck + smoke APK) and Release (semver -> APK + GHCR)Runtime & package manager: Bun. TypeScript throughout.
Related MCP server: phone-mcp-server
1. Run the server
The server is the broker both the phone and the MCP talk to. Host it anywhere the phone can reach over the internet (a VPS, a tunnel, etc.).
cp .env.example .env
# edit .env — at minimum set a long random PHONE_MCP_TOKEN:
# openssl rand -hex 32
bun install
PHONE_MCP_TOKEN=$(grep PHONE_MCP_TOKEN .env | cut -d= -f2) bun run serverOr with Docker:
docker compose up -d # reads PHONE_MCP_TOKEN from .env
# or
docker build -t phone-mcp-server .
docker run -p 8080:8080 -e PHONE_MCP_TOKEN=... phone-mcp-serverHealth check: curl http://localhost:8080/health.
Put the server behind HTTPS (a reverse proxy / tunnel) so the phone can use
wss://and the token isn't sent in clear text.
2. Install the APK on a phone
Grab
phone-mcp-agent-vX.Y.Z.apkfrom the repo's Releases and sideload it.Open the app, enter your server URL (e.g.
https://phone.example.com) and the token, tap Connect.Grant the permissions the setup screen lists:
Accessibility — needed for UI tree + gestures. On Android 13+ sideloaded apps are blocked from Accessibility until you allow it manually: if you hit a “Restricted setting” dialog, go to Settings → Apps → Phone MCP Agent → ⋮ → Allow restricted settings, then enable it under Accessibility.
Screen capture — the system consent dialog (per-session on Android 15+).
Battery optimization — disable it so the connection survives the screen turning off (important on Xiaomi/Oppo/Vivo/Samsung).
When connected, the phone appears in the broker's device registry.
3. Wire up the MCP
The MCP runs locally next to your agent (stdio) and talks to the broker over HTTP.
// e.g. claude_desktop_config.json / your MCP client config
{
"mcpServers": {
"phone": {
"command": "bun",
"args": ["run", "/absolute/path/to/phone-mcp/packages/mcp/src/index.ts"],
"env": {
"PHONE_MCP_SERVER_URL": "https://phone.example.com",
"PHONE_MCP_TOKEN": "the-same-token-as-the-server"
}
}
}
}Tools exposed to the agent
Tool | What it does |
| List connected phones + status |
| Status / hardware info for one device |
| Preferred. Compact text view of the screen: one line per element with a ref like |
| Image of the screen, only for visual content (video, photos, canvas). Defaults 540px / JPEG q60 (typically < 60 KB) |
| Open a URL / deep link via Intent (preferred for links/profiles) |
| By |
| By |
| Type into the focused field, or into |
| Gestures |
| back / home / recents / notifications |
| Wake + unlock (swipe/none lock) / turn the screen off. Rarely needed — see below |
| Raw JSON accessibility tree (large; prefer |
Action tools return one line plus a hint (now: <app> — "<screen title>" (screen changed)),
so you rarely need a snapshot after every step; pass hint: false to skip it. A ref that no longer
matches the screen returns a readable stale ref error: take a new snapshot. The compact snapshot,
refs and hints are implemented in the HTTP /mcp endpoint (the stdio package has the new screenshot
defaults only).
If exactly one phone is online, the deviceId argument is optional.
Screen off & locked phones
The phone does not need its screen on all the time. The connection stays up with the
screen off (disable battery optimization for the app), and every screen tool wakes the
phone on demand: the screen turns on, a swipe/none lock is dismissed, the agent works,
and after 30 s without commands the screen is locked again (only if the agent woke it —
it never locks a phone someone is using). Change the delay with
wake_screen { idleLockSec } (0 = never), or end a task with lock_screen.
A PIN / pattern / biometric lock cannot be unlocked remotely — that is an Android guarantee, and this project does not try to work around it. Screen tools then fail with a clear "unlock it on the device" error instead of returning a black screenshot. For an unattended automation phone, set Screen lock to None or Swipe — and only do that on a dedicated device, since anyone holding it gets full access. If it lives on a charger, turn on the phone's charge limit / optimized charging to protect the battery.
4. Releases & versioning
Versioning is deliberately boring: the root package.json version is the single
source of truth.
Every push to
mainchecks whether the tagv<version>already exists.New version → the Release workflow builds the APK, tags
v<version>, publishes a GitHub Release with the APK attached, and pushes the server image toghcr.io/<owner>/<repo>.Already tagged → nothing happens.
To cut a release, bump and merge to main:
bun run bump patch # or: minor | major | 1.4.2
git commit -am "release: v$(node -p "require('./package.json').version")"
git push # onto main (or via PR)versionCode for the APK is derived deterministically from the semver
(major*1_000_000 + minor*1_000 + patch), so it always increases.
Signing the APK (optional)
By default the release APK is debug-signed so it installs with zero setup. For a stable signature across releases (so users can update in place), add these repo secrets and the workflow will use them automatically:
ANDROID_KEYSTORE_BASE64, ANDROID_KEYSTORE_PASSWORD, ANDROID_KEY_ALIAS,
ANDROID_KEY_PASSWORD.
Developing
bun install
bun run typecheck # all three TS packages
bun run test # snapshot builder + MCP tool tests
bun run server:dev # broker with --watchThe Android app opens as a standard Android Studio project in android/.
Limits & gotchas
FLAG_SECUREscreens (banking, DRM video) show black in screenshots — normal.MediaProjection consent is per-session on Android 15+; re-grant if it stops.
UI tree is for navigation; media content (e.g. video feeds) isn't in it — use screenshots + vision for "what's on screen".
One phone = one session; parallel work needs more phones.
A PIN/pattern lock blocks remote control until someone unlocks the phone (see Screen off & locked phones).
See ARCHITECTURE.md for the complete rationale.
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Control real Android and iOS devices with LLM agents — tap, swipe, type, automate flows.
Cloud Android phones for AI agents: create a phone, read the screen, tap, type, install APKs, park.
A real Android phone in the cloud for MCP agents: observe the screen, act, reset to a saved state.
Melaya is a remote MCP server. It gives an assistant hands on your own Android phone and browser: it reads the screen through the accessibility tree, then taps, types and navigates inside the apps and sites you allow-list, with no per-app API. It also builds, schedules and runs agent pipelines across 6k+ connected tools. OAuth 2.1, nothing to install.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables full Android control from any AI agent via 7 MCP tools, including screen capture, touch interaction, app management, and system control.MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to control Android phones via MCP and HTTP. Supports screen capture, taps, swipes, text input, and app management.6AGPL 3.0
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to control Android devices via ADB, providing tools for screen capture, tap/swipe/input, app management, and diagnostics. Works locally over USB or WiFi and integrates with MCP clients like Claude and Cursor.1MIT
- AlicenseCqualityBmaintenanceEnables AI agents to control Android phones, emulators, redroid containers, iOS devices, and cloud-hosted devices through a perceive-act-verify loop of screen reading, taps, gestures, typing, and app launching. Exposes 138 tools for driving screens, elements, and files over adb, Portal APIs, and ios-portal without running ARM-only code on the device.9415 npm1MIT