Skip to main content
Glama

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 mcp

Or install the binary with Homebrew and register that:

brew install hridaya423/tap/phonedriver
claude mcp add phonedriver -- phonedriver mcp

Or 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 Code

Every 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 tools
appsB

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

    A3.5/5.0

    Scored across 14 tools

    Disambiguation4/5

    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.

    Naming Consistency4/5

    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.

    Tool Count5/5

    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.

    Completeness4/5

    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

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      C
      maintenance
      An 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 npm
      54
      MIT
    • A
      license
      B
      quality
      D
      maintenance
      An 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.
      13
      5 npm
      1
      Apache 2.0
    • F
      license
      Not graded
      quality
      C
      maintenance
      An 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.
      -