iphone-mcp
Enables control and automation of a physical iPhone device, including app management, UI navigation, and system interactions through Apple's XCTest/XCUITest framework.
Allows reading, drafting, and sending iMessages via integration with Apple's Messages database and UI automation, enabling AI to manage conversations on the device.
Provides direct UI automation for iOS devices, allowing AI agents to tap, swipe, type, and interact with apps on a physical iPhone using accessibility tree snapshots.
Click on "Install 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., "@iphone-mcpRead my latest iMessage"
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.
iphone-mcp
Control a real, physical iPhone from an MCP client (Claude Code, Claude Desktop, or any MCP host). Not a simulator, not iPhone Mirroring — the actual device, driven through Apple's own UI-automation channel.
Read your messages · reply to people · reorganise your home screen · open apps
navigate any app · fill in forms · scroll, tap, swipe, dragWhy this works at all
iOS gives exactly one sanctioned way to inject touch events into a physical device: XCTest / XCUITest, running as a UI-test bundle on the phone. That is what WebDriverAgent is — an XCTest runner that exposes an HTTP server on the device.
Everything else in the iOS tooling world (devicectl, pymobiledevice3,
libimobiledevice, idb) does device management — install apps, launch them, take
screenshots, read logs — but cannot tap.
MCP client
│ stdio (MCP)
▼
iphone-mcp ─────────────────┬──────────────────────────┐
│ HTTP :8100 │ devicectl │ sqlite (read-only)
▼ ▼ ▼
iproxy (usbmux over USB) app inventory ~/Library/Messages/chat.db
│
▼
WebDriverAgentRunner (XCTest) on the iPhone
│
▼
iOS · SpringBoard · any appRelated MCP server: rpcclient-mcp
The design decision that makes it usable
The obvious approach — screenshot the phone and let the model guess pixel coordinates — is where phone agents fail in practice. This server does what works for browser automation instead: it turns the accessibility tree into a compact outline with stable refs, and the model acts on refs.
The numbers make the case. One iPhone home screen serialises to:
raw XCUIElement JSON | this server's snapshot | |
size | 764 KB | ~1.2 KB |
nodes | 524 | 21 |
tokens | ~190,000 | ~400 |
and the output is directly actionable:
app: com.apple.MobileSMS | screen: 440x956pt | snapshot #3 | 72 elements
[e1] NavigationBar "Messages" @220,90
[e2] Button "Edit" @52,84
[e9] Cell "Keka, Unread, i don't have a good headshot for my linkedin, Pinned" @220,185
[e12] TextField "iMessage" value="" @220,905
[e13] Button "Send" @410,905The model calls ui_tap({ref: "e13"}). It never sees a coordinate. Screenshots stay
available via ui_screenshot for when the tree isn't enough (canvas UI, games, visual
confirmation).
Requirements
macOS with Xcode 15+ (26 recommended) and the command line tools
An iPhone running iOS 17+ (developed and tested against iOS 26.5)
An Apple Developer account — a free personal team works, but certificates expire after 7 days and WDA must be rebuilt. A paid account with a wildcard profile is far less friction.
brew install libimobiledevice(providesiproxyandidevice_id)Node 22+ (uses the built-in
node:sqlite)
On the phone: Settings → Privacy & Security → Developer Mode → on, then trust this Mac when prompted.
Setup
git clone https://github.com/teddyoweh/iphone-mcp.git
cd iphone-mcp
npm install
npm run build
bash scripts/fetch-wda.sh # clone WebDriverAgent into vendor/
bash scripts/wda-up.sh # build, sign, install, launch, wait for readywda-up.sh is idempotent — it exits immediately if WDA is already answering on :8100.
Re-run it after a reboot, a cable reconnect, or if iOS jetsams the runner.
It auto-detects your Apple Developer Team ID from your installed provisioning profiles. Override it if that guesses wrong:
export IPHONE_MCP_TEAM_ID=XXXXXXXXXXRegister with your MCP client
Claude Code:
claude mcp add iphone -- node /absolute/path/to/iphone-mcp/dist/index.jsOr in claude_desktop_config.json / .mcp.json:
{
"mcpServers": {
"iphone": {
"command": "node",
"args": ["/absolute/path/to/iphone-mcp/dist/index.js"],
"env": {
"IPHONE_MCP_PASSCODE": "123456"
}
}
}
}Then ask it to iphone_status — that health-checks the whole chain.
Environment variables
variable | default | meaning |
| — | Device passcode, typed on the on-screen keypad to unlock. Prefer this over passing it in a tool call so it never lands in conversation logs. |
| auto | Device UDID, if more than one is attached. |
|
| Where WebDriverAgent is reachable. |
| auto | Apple Developer Team ID for signing WDA. |
|
| Set |
Permissions
Full Disk Access for whatever process runs the server (Terminal, Claude, …) — needed to read
chat.dband Contacts. System Settings → Privacy & Security → Full Disk Access, then restart the app.Messages tools degrade gracefully without it; everything else still works.
Tools
Observing
tool | what it does |
| Health check: WDA, iOS version, screen, lock state, foreground app, Messages DB. |
| The primary tool. Compact ref-addressed outline of the screen. Supports |
| PNG of the screen, downscaled. For when the tree isn't enough. |
Acting
tool | what it does |
| Tap an element by ref. Optional double-tap. |
| Escape hatch: tap absolute coordinates in points. |
| Type into a field (optionally tapping it first), with |
| Swipe/scroll in a direction, optionally scoped to a scrollable element. |
| Press and hold — opens context menus, enters home screen edit mode. |
| Press, hold, drag one element onto another. |
System
tool | what it does |
| Launch, terminate, enumerate apps by name or bundle id. |
| Any URL scheme. Often the most reliable navigation: |
|
|
| Lock, or wake and unlock (types the passcode on the keypad). |
Messages
tool | what it does |
| Recent conversations with names, previews, unread counts. |
| Full history of one conversation, by contact name / number / email. |
| Compose a reply and hold it. Sends nothing. |
| Actually send a draft. Requires |
Home screen
tool | what it does |
| Walk every page; report apps, folders, widgets, badges and the dock. |
| Remove an icon from the home screen. App stays installed. Experimental in bulk — see Status. |
| Drag an icon to a page/row/column. Best effort, verified. Experimental. |
| Open the page editor, where whole pages can be hidden. |
Notes from building this
Things that cost real debugging time, documented so they don't cost yours.
Points vs pixels. Every coordinate WDA accepts or returns is in points
(440 × 956 on a 17 Pro Max). Screenshots come back in pixels (1320 × 2868, 3×). Mixing
them is a silent 3× error. Only screenshot() is allowed to know pixels exist.
85% of your messages aren't in message.text. On current macOS that column is NULL for
most rows and the content lives in attributedBody, an NSArchiver typedstream blob. On
the machine this was built on: 193,479 of 226,918 rows. A reader that only checks .text
silently loses most of the history. The decoder in src/messages.ts was validated at
400/400 exact matches against rows that happen to have both fields populated.
message.date overflows a JS number. It's nanoseconds since 2001 — e.g.
806973797577999872, well past Number.MAX_SAFE_INTEGER. node:sqlite throws rather than
silently rounding, so those queries must opt into BigInt.
Auto-lock is the number one cause of mysterious failures. When the phone locks itself, iOS revokes the XCTest process's UI-testing authorization, and WDA then fails every call with errors that never mention locking:
Failed to get system application: Not authorized for performing UI testing actions
stale element reference: ... Application 'local.pid.0' is not present
Failed to resolve query: Application local.pid.0 is not runningA batch that starts fine and then fails on every subsequent step has almost certainly just
hit the auto-lock timer — chasing the "stale element" message instead leads nowhere. Every
tool here detects this class of error, unlocks, and retries once, which is why
IPHONE_MCP_PASSCODE matters for anything long-running.
pressButton("home") does not wake a Face ID phone. The display stays asleep and every
screenshot comes back solid black. /wda/unlock does wake it.
The lock screen tells you the passcode length. The passcode field's accessibility value
reads "4 of 6 values entered" — so a wrong-length passcode can be rejected before
submitting a guaranteed-failed attempt, and stale digits can be cleared first.
Home screen pages render lazily. Off-screen page containers exist in the tree, but SpringBoard doesn't populate their icons, so mapping requires actually swiping. There's no shortcut.
Double-tap needs a gap. Two taps with no delay aren't read as a double-tap; ~130 ms between them is.
Safety
Sending messages is gated.
messages_drafthas no side effect;messages_sendis a separate tool that requires the draft id and an exact copy of the text. The intended flow is: draft → show the user → send only on approval.home_remove_appnever deletes an app. iOS puts "Delete App" (destroys data) directly next to "Remove from Home Screen" (keeps it in App Library) in the same menu. The matcher for that sheet is exact and anchored; if it can't find an unambiguous "Remove from Home Screen", it taps Cancel and reports rather than guessing.Your passcode belongs in the env, not in tool arguments.
This drives a real phone that sends real messages to real people. Treat it that way.
Status
Verified working against a physical iPhone 17 Pro Max on iOS 26.5:
ui_snapshot,ui_screenshot, and the whole ref-addressed tap/type/swipe loopapp_open/app_list/open_url/press_buttonpasscode unlock, including rejecting a wrong-length passcode before submitting it
messages_threads/messages_read— contact names, group participants, unread counts,attributedBodydecodinghome_mapacross all pages
Experimental — do not trust in bulk: the home screen mutation tools
(home_remove_app, home_move_app, home_page_editor). A single removal works, but
running many in sequence has been observed to destabilise WebDriverAgent itself until the
runner is relaunched. Go a few at a time and verify with home_map.
Limitations
USB only in this configuration. WDA over Wi-Fi works but is slower and flakier.
Face ID cannot be triggered programmatically — passcode entry is the only unlock path.
Cross-page icon drags are unreliable.
home_move_appverifies and tells you the truth rather than claiming success.Removing icons doesn't reflow icons backward across pages — iOS never does that — so decluttering reduces icons per page more than it reduces page count. Use
home_page_editorto hide pages outright.home_maptakes ~4 s per page. Some MCP clients default to a 60 s tool timeout; raise it or pass a smallermax_pages.Free Apple developer accounts need WDA rebuilt weekly.
Development
npm run build # compile
npm run watch # compile on change
npm run wda # bring WebDriverAgent upvendor/WebDriverAgent, .wda-build/, and dist/ are gitignored.
License
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityDmaintenanceEnables iPhone automation through Appium integration, allowing users to control apps, capture screenshots, interact with UI elements, and perform touch operations via natural language commands.Last updated132MIT
- Alicense-qualityCmaintenanceMCP server for iOS automation via rpcclient, enabling AI agents to connect to iOS RPC servers and perform actions like launching apps, clicking, typing, and gesture control.Last updated2MIT
- Alicense-qualityDmaintenanceEnables AI agents to control real iPhones and simulators on macOS, allowing for UI interaction, testing, and automation.Last updated4385MIT
- AlicenseAqualityCmaintenanceAn MCP server that lets an AI agent drive the iOS Simulator in a loop, enabling tapping, typing, swiping, reading the screen via the accessibility tree, and verifying app state.Last updated14MIT
Related MCP Connectors
Push notifications for AI agents - send instant iPhone notifications from any MCP client.
Access Kernel's cloud-based browsers and app actions via MCP (remote HTTP + OAuth).
Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/teddyoweh/iphone-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server