Skip to main content
Glama
als0m3

ios-control-mcp

by als0m3

ios-control-mcp

Control a real iPhone from an MCP client on macOS. Capture screenshots, tap and swipe, type text, press buttons, and open apps through Apple's CoreDevice services, using pymobiledevice3.

The server runs locally over stdio. It uses on-demand screenshots rather than a continuous mirror, and requires no companion iOS app. USB is preferred; Wi-Fi is available after pairing and enabling wireless connections.

Status: beta. Device services can change between iOS releases. Automated tests cover protocol handling and mocked device behavior; they do not establish compatibility with every iPhone or iOS version. See the hardware validation checklist before relying on a new setup.

Requirements

  • macOS, including the built-in /usr/bin/sips image converter.

  • Python 3.10 or newer. CI targets Python 3.10, 3.12, and 3.13.

  • An iPhone with iOS 17 or newer, paired with this Mac, unlocked, and with Developer Mode enabled under Settings → Privacy & Security.

  • Working developer services on the device. Depending on iOS and the host setup, developer disk image preparation may be necessary; consult the upstream device setup documentation.

The iOS version is a transport prerequisite, not a promise that all tools work on every supported OS release. Linux and Windows device control are not supported by this project.

Related MCP server: iphone-mcp

Install

git clone https://github.com/als0m3/ios-control-mcp.git
cd ios-control-mcp
python3 -m venv .venv
.venv/bin/python -m pip install --upgrade pip
.venv/bin/python -m pip install .

Connect the iPhone with a cable, unlock it, and accept the trust prompt if shown. Enable Developer Mode and complete any restart or confirmation requested by iOS.

Connect an MCP client

Add a stdio server to your client's MCP configuration, using the absolute path to the installed executable:

{
  "mcpServers": {
    "ios-control": {
      "command": "/absolute/path/to/ios-control-mcp/.venv/bin/ios-control-mcp",
      "args": [],
      "env": {
        "IOS_CONTROL_TRANSPORT": "usb"
      }
    }
  }
}

Configuration formats differ between clients; some also require "type": "stdio". Restart or reload the client after editing its configuration. The equivalent module entry point is .venv/bin/python -m ios_control_mcp.

Start with iphone_status, then iphone_screenshot. The process waits for newline-delimited JSON-RPC input when launched directly in a terminal; it does not open a window or provide an interactive command prompt.

Tools

All coordinates are normalized, from 0.0 to 1.0: the top-left corner is (0, 0) and the center is (0.5, 0.5). Measure against a recent screenshot. Pixel coordinates, incorrect types, unknown arguments, and values outside the published limits are rejected before any device operation.

Tool

Arguments

Behavior

iphone_status

None

iOS version, build, model, available transports, tunnel state; omits the device name and UDID

iphone_screenshot

max_width: 200–1400, default 900

Returns a JPEG resized to the requested width; falls back to PNG if conversion fails

iphone_tap

Required x, y; hold_ms: 0–10000, default 120

Touch and release; contact lasts at least 100 ms

iphone_swipe

Required x, y, x1, y1; ms: 120–10000, default 300

Interpolated drag; requested duration excludes contact and flush delays

iphone_type

Required text: 1–1000 characters

Printable ASCII, tab, and newline using a US keyboard mapping; rejects unsupported characters before typing

iphone_button

Required name

home, lock, volume-up, volume-down, mute, or siri

iphone_open_app

Required app: 1–255 characters

Exact bundle ID, exact display name, or unique partial name; ambiguous matches are rejected

iphone_list_apps

Optional contains: up to 255 characters

Lists app names and bundle IDs; filter matches either field

iphone_wait

ms: 0–10000, default 500

Waits before the next operation

Example arguments:

{"x": 0.5, "y": 0.5}
{"x": 0.5, "y": 0.75, "x1": 0.5, "y1": 0.25, "ms": 400}
{"app": "com.apple.Preferences"}

A swipe beginning at the bottom edge can trigger the Home gesture. For scrolling inside an app, start at y < 0.9. Keyboard layout, orientation, lock state, and system dialogs can affect input. Newline may submit a form; tab may move focus.

Opening an app does not request termination of an already running instance. Allow time for launch animations, then take another screenshot to verify the result. A successful tool response confirms the request completed, not that the screen reached the intended state.

Device selection and configuration

Environment variable

Meaning

IOS_CONTROL_UDID

Select one specific device. If it is absent, the call fails instead of controlling another device.

IOS_CONTROL_TRANSPORT

usb or wifi. If unset, prefer USB and use the network when USB is unavailable.

IOS_CONTROL_TIMEOUT

Base device-call timeout in seconds, from 1 to 300; default 60. Typing and swiping add a bounded duration allowance.

With multiple devices attached, set IOS_CONTROL_UDID. With one device, the server remembers its selection until restart. Keep actual UDIDs in local client configuration, not in committed files or public issue reports.

Requests run sequentially. Screenshots and app listing can reconnect and retry once after a connection failure. Input and app launching are never automatically replayed. A timeout or connection error may mean an action partially completed; inspect the screen before retrying it. Client cancellation notifications are not processed during an in-flight call; configured timeouts bound device operations.

Wi-Fi

With only the intended iPhone attached by USB, enable wireless connections:

.venv/bin/python -m pymobiledevice3 lockdown wifi-connections on

Keep the Mac and iPhone on the same trusted network. Unset IOS_CONTROL_TRANSPORT for USB preference with network fallback, or set it to wifi to require a network connection. The phone must remain unlocked and reachable. To disable wireless connections, reconnect over USB and run the command with off instead of on.

Privacy and security

This server can read the visible screen and send input to the foreground app. Only connect trusted MCP clients and keep their tool approval controls enabled for consequential actions. Screenshots can include messages, credentials, and other private content; the MCP client may send tool results to its model provider.

The server has no telemetry or HTTP listener. It sends tool results over local stdio and communicates with the paired device. Image conversion uses temporary files removed when conversion finishes normally; abrupt process termination can leave files behind. The dependency manages pairing records and may download developer support assets. Client logs and dependency diagnostics can contain private information. See SECURITY.md before sharing a report.

Troubleshooting

Symptom

What to check

No iPhone found

Cable, unlock state, trust pairing, and Developer Mode. For Wi-Fi, check wireless pairing and the network.

Selected device unavailable

Check IOS_CONTROL_UDID and transport. Restart the server to deliberately select a different phone.

Multiple devices found

Set IOS_CONTROL_UDID explicitly.

Developer service or tunnel error

Confirm developer services are ready, check upstream setup requirements, and restart the client after fixing the connection.

Input completes but nothing changes

Confirm focus, normalized coordinates, keyboard layout, and absence of a blocking system dialog.

Typing rejects text

Use printable ASCII, tab, or newline. Unsupported text is rejected as a whole.

App name is ambiguous

Use the exact bundle ID returned by iphone_list_apps.

Device action times out

Inspect the current screen before retrying; the action may already have partly completed.

Client cannot start the server

Check its absolute executable path and environment variables. Inspect stderr locally; redact before sharing.

Development

See CONTRIBUTING.md for setup and checks, architecture for the transport design, and CHANGELOG.md for behavior changes.

License

The code in this repository is licensed under the MIT License. Dependencies retain their own licenses. In particular, pymobiledevice3 is GPL-3.0-or-later; the MIT license here does not relicense that dependency. Review third-party license obligations when redistributing a combined package.

This is an independent project, not affiliated with or endorsed by Apple.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables MCP clients to control macOS via accessibility and screen recording, providing tools to list apps, observe UI, click, type, press keys, and scroll.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables MCP hosts to observe and operate macOS apps by reading accessible controls, entering text, clicking, scrolling, capturing screenshots, and performing on-device OCR, with background app control and explicit human authorization.
    3
    MIT