phonedriver
Control Android or iOS phones/emulators via 14 MCP tools to inspect and operate apps and screens.
List connected devices and installed apps; install, stop, clear data, uninstall, and launch apps or URLs.
Read the current screen as a numbered text tree, find matching elements, and take screenshots with tappable element marks.
Tap elements by @ref, text, or coordinates (including long press); type text securely; swipe, pinch, scroll, and press device keys.
Wait for screens or content to settle/appear/disappear, and run ordered batches of actions with a single diff result.
Read or set the clipboard and run device shell commands for system settings.
Works with Android emulators/phones via adb and iOS Simulator/iPhone via Xcode, with a guarded payment-action approval flow.
Enables AI agents to control Android phones and emulators, reading the screen as a numbered text list, tapping and typing, observing screen changes, taking screenshots, and requiring human approval before sensitive actions such as payments or account deletion.
Enables AI agents to control iPhones and the iOS Simulator, reading the screen as text, performing taps and input, observing UI changes, and taking screenshots for phone-based tasks.
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., "@phonedriver Open my app, sign up with a new account, and list any bugs with repro steps."
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.
phonedriver lets Claude Code, Codex, Cursor or any MCP client use an Android phone or an iPhone. It reads the screen as text, and it asks you before it taps Pay. The 6.9x is the token total on nine phone jobs.
Start
claude mcp add phonedriver -- npx -y phonedriver mcpOr install the binary with Homebrew and register that:
brew install hridaya423/tap/phonedriver
claude mcp add phonedriver -- phonedriver mcpOr install it as a plugin, which adds the server and its skill together. Use the plugin or mcp add, not both, or the tools are listed twice.
claude plugin marketplace add hridaya423/phonedriver && claude plugin install phonedriver@phonedriver # Claude Code
codex plugin marketplace add hridaya423/phonedriver && codex plugin add phonedriver@phonedriver # Codex
phonedriver is a standard MCP server over stdio, so any MCP client can run it. I test it with Claude Code. Setup for the others: Other clients.
Start an Android emulator, plug in an Android phone with USB debugging on, or boot the iOS Simulator. Then give Claude a job that only a phone can do:
Order me a large pepperoni pizza from the Domino's nearest Leicester Square, for collection.
Open my app, sign up with a new account, and tell me every bug you find on the way. Give me steps to reproduce each one.
Go through my app's onboarding in dark mode and in light mode, and show me every screen where the text is hard to read.
The first one is the demo above. phonedriver stops at Buy with GPay and asks you.
The first run downloads one binary for your machine (macOS, Linux or Windows) and caches it. phonedriver doctor checks adb, Xcode and each device and tells you what to fix. The shell and PowerShell installers and real iPhones: docs/reference.md.
Related MCP server: androir-mcp
Fewer tokens
Tokens are what an agent's usage limit is spent on. I ran nine jobs that only a phone can do, twice each, with Claude Sonnet 5.5 and three MCP servers. The jobs included setting Chrome's search engine, cropping a photo, pinning and archiving Messages threads, finding the app that keeps the phone awake, and testing a sign-up flow for planted bugs. A script checked the phone's own data afterwards, not the model's report.
This is my own test set. All three tools ran on the same Android emulator on 10 November 2026. Every run, the versions, the checks and what was thrown away are published in docs/bench/jobs.md. If you maintain one of these tools and think I set it up wrong, open an issue and I will rerun it.
What the model sees
Each screen arrives as a short numbered list. This is the Settings app on an Android emulator:
$ phonedriver snapshot
app com.android.settings (Settings) · keyboard hidden · settled in 0ms
@e1 button "Search Settings"
@e2 group scrollable↓
@e3 button "Google" "Services & preferences"
@e4 button "Network & internet" "Mobile, Wi‑Fi, hotspot"
@e5 button "Connected devices" "Bluetooth, pairing"
...
@e10 button "Display & touch" "Dark theme, font size, touch"The model taps by number. phonedriver waits until the screen stops changing, then replies. After tap @e10 the Display screen opens and the reply lists it, including @e24 switch "Dark theme" off. The next tap returns only what changed:
$ phonedriver tap @e24
ok tapped @e24 "Dark theme" at 353,532 · settled in 2034ms
~ @e23 button "Dark theme" "Will turn on when Bedtime starts" → "Will never turn off automatically"
~ @e24 switch "Dark theme" off → on+ marks a line that appeared, - one that left, ~ one that changed. The model doesn't need to poll or guess how long a screen takes to load. It takes a screenshot only when it needs to see a picture.
Speed
Measured on 11 November 2026 with phonedriver 0.1.1 on an Android 17 emulator, median of 50 runs, against adb shell uiautomator dump, screencap and input. The difference comes from a small helper process that phonedriver starts on the phone. It keeps the accessibility connection open, where adb starts a new process and writes XML each time. On a HONOR X8 phone (Android 13), reading the screen took 78.5 ms against 2,507 ms, and a screenshot took 16 ms against 1,006 ms. Method and raw data.
Asking before it pays
When a tap lands on a button labeled Pay, Buy now, Buy with GPay, Place order, Start subscription, Transfer or Delete account, phonedriver holds the tap and asks you in Claude Code's own prompt. The model never sees that question and can't answer it. In a client that can't show such a prompt, the tap waits and the model has to ask you in chat.
You choose Allow, Allow for this session, or Decline. If you want an agent to run without asking, you can say so in its instructions, and it passes autonomous: true; you can also turn the check off with PHONEDRIVER_GUARD=off.
The check reads button labels in English, Spanish, Portuguese, French, German, Italian, Dutch, Japanese, Chinese (Simplified), Korean and Hindi. It does not catch a payment button that is only an icon, or one labeled in another language. By default it asks about payments and deleting an account. To be asked about more, set PHONEDRIVER_ASK to any of send (Send, Reply, Post, Share), delete (Delete, Remove, Clear all) and install (Install, Uninstall), for example PHONEDRIVER_ASK=send,delete. The prompt says which kind of tap it is.
The model can't type plain text into a password field or a one-time code field. It gets an error that tells it to use secret, which keeps the text out of logs and traces.
Works with
Phone | You need |
Android emulator | adb |
Android phone or tablet | adb and USB debugging |
iOS Simulator | a Mac with Xcode |
iPhone or iPad | a Mac with Xcode and an Apple Development certificate (setup) |
On Android there is no app to install. phonedriver pushes its helper over adb when it connects. On iOS it builds a small XCTest runner on your Mac. For a real iPhone, plug it in and run phonedriver ios setup. It reads the signing team from your certificate, checks the phone, and builds, installs and starts the runner, so there is no team ID to look up.
The cable can come off after setup. For an iPhone, run phonedriver ios wifi on and unplug it; it stays in phonedriver devices while it shares a Wi-Fi network with the Mac. For an Android phone, phonedriver pair or phonedriver untether does the same (Android over Wi-Fi). When the phone is far away, on a tunnel or a mobile network, phonedriver sends a smaller screen stream and compressed screen trees. On a link shaped to 3G, the median tap on an emulator took 1.4 s, down from 2.8 s.
On Android 13 and later, the agent can work on a hidden second screen, so your own screen stays yours. Set PHONEDRIVER_DISPLAY=hidden in the MCP config, or pass --display hidden to the CLI. It has been tested on an emulator and on a HONOR X8 (Android 13). Typing into an app's own fields leaves no trace on your screen. Typing into a web page shows the system's clipboard note, and your clipboard is put back after.
The MCP server has 14 tools: devices, apps, snapshot, screenshot, tap, type, swipe, scroll, press, launch, wait, batch, clipboard and shell. snapshot, screenshot and wait are marked read-only, so a client can run them without a permission prompt. The phonedriver CLI has the same commands, for scripts and for agents that prefer a shell. A device you used from a terminal goes to your agent after 30 seconds idle.
One call can tap a whole path: tap {"target":"More options","then":["Settings","Dark theme"]}. Models tap one step at a time unless told otherwise, so give your agent the skill (phonedriver skill prints it), which tells it to send paths.
Limits
Tested on Android emulators (API 26 and 37), a HONOR X8 (Android 13), the iOS Simulator, an iPhone 13 Pro Max, an iPhone 14 Plus and an iPad Pro. Other Android phones use the same path. Bug reports from them are welcome.
A tap alone takes 29 to 39 ms on an Android emulator and 450 to 530 ms on a real iPhone. The 514 ms in the Speed chart is a tap, the wait until the screen stops changing, and the list of what changed.
The model can do anything on the phone you could do with your finger. For unattended runs, use an emulator or a spare phone.
phonedriver never enters a passcode and can't pass Face ID or a fingerprint check. On a locked phone a command stops and says the device is locked. On
main, phonedriver keeps an iPhone awake while an agent is using it; in 0.1.3, set Auto-Lock to Never before a long task. Screens that block screenshots, as some banking apps do, come out black in a screenshot, though their text can still be read.
Other clients
claude mcp add phonedriver -- npx -y phonedriver mcp # Claude Code
codex mcp add phonedriver -- npx -y phonedriver mcp # Codex
code --add-mcp '{"name":"phonedriver","command":"npx","args":["-y","phonedriver","mcp"]}' # VS CodeEvery other client takes the same entry in its MCP config file:
{
"mcpServers": {
"phonedriver": { "command": "npx", "args": ["-y", "phonedriver", "mcp"] }
}
}With more than one device connected, add "env": { "PHONEDRIVER_DEVICE": "android:emulator-5554" }. phonedriver devices lists the ids.
How it works
On Android, phonedriver starts a small Java helper on the phone over adb. The helper reads the accessibility tree through UiAutomation and streams the screen as H.264 video, which is how phonedriver knows when the screen has stopped changing. On iOS, an XCTest runner does the same job. A daemon on your computer keeps the connection open and remembers which @e number is which element, so a tap still lands after a list has moved. The MCP server and the CLI are thin clients of that daemon.
phonedriver sends no telemetry. After the first download from GitHub, it connects only to your device, and a test runs the whole tool in a sandbox that blocks every other connection. Design notes: architecture, protocol, decisions.
FAQ
How do I let Claude control my Android phone? Run claude mcp add phonedriver -- npx -y phonedriver mcp, turn on USB debugging, and plug the phone in.
Does it work with an iPhone? Yes, from a Mac with Xcode, on the Simulator or on an iPhone or iPad. phonedriver ios setup signs the runner with your Apple developer team, and phonedriver ios wifi on lets you unplug the cable.
Can my agent buy things with it? Not without your answer, unless you have told it to run autonomously. See Asking before it pays.
Can I use it at work? Yes. It is AGPL-3.0: you can use it freely. If you change it and offer it to others as a network service, you must publish your changes.
License
AGPL-3.0-only. See LICENSE. The Android helper includes files from scrcpy under Apache-2.0; see NOTICE.
The binary decodes H.264 with OpenH264 built from source. Cisco's patent license covers only the binaries Cisco distributes, so this build carries no H.264 patent license.
Available Tools
14 toolsappsB
List apps (name id), or action install (file in app), stop, clear (data) or uninstall.
Inputs: app, action.
batchB
Run steps in order, one diff back; stops at the first failure. A step is {"tool":"tap","target":"@e3"}: tap, type, swipe, scroll, press, launch or wait.
Inputs: steps (required).
clipboardC
Read the clipboard, or set it.
Inputs: set.
devicesA
List devices (id, model, OS, holder). With several, pass an id as device to the other tools. release gives up this session's hold.
Inputs: release.
launchA
Open an app (any part of its name, or its id) or a url (with app, opened in that app); returns the screen it shows, which is where the app was left unless restart. grant pre-allows permissions (camera, notifications).
Inputs: app, url, grant, restart.
pressC
Press home, back, recents, notifications, lock, volume-up, volume-down, enter or hide-keyboard.
Inputs: key (required).
screenshotA
Image of the screen, for what the snapshot lacks (canvas, map, photo). marks boxes tappable elements with their @ref.
Inputs: marks.
scrollB
Scroll toward more content, or until the text or @ref in until shows.
Inputs: direction (required), until.
shellA
Run a device shell command: exit code, output, screen change. Prefer it for settings: cmd uimode night yes, settings put system screen_brightness 20, am start -a android.intent.action.VIEW -d <url>. Simulator: simctl ui appearance dark. A physical iPhone has no shell.
Inputs: command (required).
snapshotB
The screen as lines like @e4 button "Save"; refs name elements for other tools. find lists matching lines (A|B for either). under-keyboard = hidden by the keyboard.
Inputs: find.
swipeA
A finger gesture: flip pages, dismiss. to drags from to a target; pinch (in, out) zooms. Use scroll for lists.
Inputs: to, from, pinch, direction.
tapA
Tap by @ref, text or x,y point (from a screenshot). long holds the press that many ms (context menus). The reply is the new screen: read it, and call tools one at a time (they share one screen).
Inputs: target (required), long.
typeA
Put text in a field (replaces its content) and report what it shows; empty text clears. secret for any password or code.
Inputs: text (required), into, secret, submit.
waitB
Wait for settled, a text (A|B for either) or @ref to appear, or gone:<text>.
Inputs: condition (required), timeout_ms.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 14 tools
Most tools have clearly distinct targets: observation (snapshot vs screenshot), gestures (tap, swipe, scroll, press) and input (type) are separated, and descriptions actively disambiguate hot spots like 'use scroll for lists' and 'screenshot for what the snapshot lacks'. Remaining overlap is real but modest: `batch` re-runs the same verbs as standalone tools, `shell` can launch apps the way `launch` does, and `tap` vs `press` require reading the descriptions to separate.
Every name is a single lowercase word with no casing or separator drift, which is a strong, predictable surface. The only inconsistency is grammatical: some names are nouns naming a resource (apps, devices, clipboard, snapshot, screenshot) while others are imperative verbs (launch, tap, type, wait), and `batch` is neither.
Fourteen tools sits squarely in the well-scoped range for mobile automation: device/app management, perception, gestures, text entry, waiting, and a batch combinator. Each tool maps to a distinct capability an agent needs and none feels like filler.
Coverage of the inspect-then-act loop is thorough: device discovery, app lifecycle (install/stop/clear/uninstall), launch with permissions, snapshot/screenshot perception, gestures, typing, waiting and batch composition, with `shell` as an escape hatch. Minor gaps remain — no permission revocation, no file push/pull, and `wait` only supports `gone:<text>` rather than `gone` for a ref — but agents can work around these via shell.
Maintenance
Related MCP Connectors
Give an AI agent an iPhone to see, tap and type on over MCP. Free early access (Simulator).
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
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.
Let AI agents control Android and iOS devices on Mobilerun Cloud: screen, input, apps, workflows.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceAn MCP server that lets AI agents control iOS and Android devices (tap, scroll, type, take screenshots, read UI trees, and run code). Works with multiple devices at the same time.94 npm54MIT
- AlicenseBqualityDmaintenanceAn MCP server that allows AI agents to drive real Android devices via adb, capturing screenshots, reading the live UI tree, and performing actions like tap, swipe, and type.135 npm1Apache 2.0
- AlicenseNot gradedqualityAmaintenanceAn MCP server that enables AI agents to control Android and iOS devices via natural language, using platform tools like adb and simctl.3,566 npm56Apache 2.0
- FlicenseNot gradedqualityCmaintenanceAn MCP server that lets AI agents drive real mobile-app automation on connected devices, enabling natural-language test tasks, test case generation, and app/build management for Android and iOS.-