iPhone MCP
README.md
# iPhone MCP
[English](README.en.md) · 简体中文
运行在电脑上的 MCP 服务,通过 Wi-Fi(默认)或 USB、CoreDevice 隧道和 RemoteXPC 开发者服务直接控制 iPhone。手机无需安装或后台运行 Actell,无需手机端 MCP、VPN、ESP32 或 WebDriverAgent。
当前面向 **macOS、iOS 27+、Wi-Fi/USB、正向竖屏触控**。已在拔掉 USB 后验证截图、点击、滑动、中文输入、Bonjour 发现及 TCP 中断后重连;长期稳定性仍待验收。不把协议请求成功视为界面操作成功。
## 安装
需要 [uv](https://docs.astral.sh/uv/)、Python 3.13。`uv sync` 会按需安装 Python 并创建隔离环境。
```sh
git clone https://github.com/the-eric-kwok/iphone-mcp.git
cd iphone-mcp
uv sync --frozen
uv run --frozen iphone-mcp --help
uv run --frozen iphone-mcp devices
```
手机准备:
1. 用数据线连接 Mac,解锁手机,在 Finder 或 Xcode 中完成信任配对。
2. 在手机设置中启用开发者模式。
3. 让匹配版本的 Xcode 完成设备准备,挂载 Personalized Developer Disk Image(DDI)。本项目不下载、挂载 DDI,不更改开发者模式或信任关系。
4. 手机和 Mac 连接同一局域网,开启系统无线连接,然后拔掉数据线。关闭其他正在控制同一手机的任务,保持手机可操作。
```sh
# 首次准备:先通过 USB 完成信任,再显式开启系统无线连接。
uv run --frozen iphone-mcp devices --transport usb
uv run --frozen iphone-mcp prepare-wifi --udid <UDID>
# 拔掉数据线后验证 Wi-Fi。doctor 只连接和截图,随后释放,不发送输入或保存截图。
uv run --frozen iphone-mcp devices --transport wifi --udid <UDID>
uv run --frozen iphone-mcp doctor --udid <UDID> --transport wifi
# Bonjour 不可用时,可直接指定手机 IP。
uv run --frozen iphone-mcp doctor --udid <UDID> --host <手机IP>
```
`prepare-wifi` 只通过 USB 为指定的已配对设备设置系统 `EnableWifiConnections`,并读取确认;普通发现和连接不会自动修改此设置。准备完成后,手机仅运行系统服务,无需常驻 App。系统更新、重启或 DDI 状态变化后,可能需要重新在 Xcode 中准备开发者服务。
无线发现使用 macOS 系统 Bonjour,仅返回电脑已有身份能够认证的设备。macOS 的系统配对记录不能直接枚举时,传入已知 `--udid` 可以让系统查询对应记录,连接不依赖数据线。发现为空也需检查本地网络权限、Bonjour、局域网隔离和手机解锁状态。指定 `--host` 可跳过服务发现;`.local` 名称仍由系统 Bonjour 解析,连接始终校验所选 UDID 和配对身份。为避免误选插线虚拟网络,Wi-Fi 模式过滤链路本地、回环及组播地址,要求可用的局域网 IPv4 或非链路本地 IPv6 地址。
Wi-Fi 是默认连接方式;USB 必须显式传入 `--transport usb`。Wi-Fi 失败会报错,不自动回退 USB。
## MCP 接入
使用 stdio,MCP 客户端负责启动进程,不需要手动常驻运行另一个 HTTP 服务。
```json
{
"mcpServers": {
"iphone": {
"command": "/opt/homebrew/bin/uv",
"args": [
"--directory", "/absolute/path/to/iphone-mcp",
"run", "--frozen", "iphone-mcp", "serve"
]
}
}
}
```
配置模板见 [examples/mcp.json](examples/mcp.json)。请替换 `uv` 和项目的绝对路径;`/absolute/path/to/iphone-mcp` 是占位路径。项目安装过程不会自动修改任何 MCP 客户端配置。
## 工具
| 工具 | 用途 |
| --- | --- |
| `list_devices` | 通过 `transport=all/wifi/usb` 发现设备,可按 `udid` 过滤;返回 `items` 和部分发现错误 `errors` |
| `connect_device` | 指定 `udid`,默认 `transport=wifi`;可指定 `host`,或显式选 `usb` |
| `device_status` | 读取本地会话状态,不隐式重连 |
| `screenshot` | 返回新鲜 JPEG、尺寸、方向和 `frame_id` |
| `tap` | 根据截图像素坐标点击 |
| `drag` | 根据截图像素坐标拖动,时长 0.1~5 秒 |
| `home` | 返回主屏幕 |
| `list_apps` | 按名称或 Bundle ID 搜索应用 |
| `launch_app` | 启动或激活应用,不终止已有进程 |
| `type_text` | 设置手机剪贴板后粘贴,支持完整 Unicode 文本 |
| `wait` | 等待 0~10 秒,不阻塞停止操作 |
| `stop`、`disconnect_device` | 取消动作、作废排队请求、释放当前会话 |
`list_devices()` 每次调用刷新两种连接;`list_devices(transport="wifi")` 或 `list_devices(transport="usb")` 可单独查询,`udid` 可缩小范围。同一设备有两种可用路线时保留两条记录,使用 UDID 关联,避免掩盖实际连接方式。发现只读取设备身份,不启动控制会话;当前控制会话通过 `device_status` 查询。
例如:`connect_device(udid="…", transport="wifi", host="192.168.1.20")`;省略 `host` 时通过 Bonjour 寻找设备。状态中的 `transport` 和 `host` 表示实际选用的连接。
推荐顺序:`list_devices` → `connect_device` → `screenshot` → `tap`/`drag`/`type_text` → `screenshot`。
坐标属于**返回图片**,不是原始屏幕尺寸。默认截图最长边为 1600 像素,可以用 `max_size` 调整。输入必须携带最近的 `frame_id`;新截图、输入、停止、断线或超过 60 秒都会使旧引用失效。方向变化会拒绝坐标输入,首版不猜测横屏映射。截图引用用于绑定坐标,不能保证屏幕内容在两次调用之间未改变。
`type_text` 会替换手机剪贴板,不读取或保存原剪贴板,不自动回车;系统粘贴确认需要用户处理。返回 `dispatched` 只说明协议发送完成,必须重新截图确认实际效果。
## 架构
```text
MCP 客户端
└─ stdio MCP 服务
└─ Controller:串行操作、截图引用、取消与不重放
└─ CoreDeviceBackend
├─ Wi-Fi TCP Lockdown/USB Lockdown 配对身份
├─ DesktopTunnel:CoreDeviceProxy、进程内隧道、RSD
├─ ScreenCaptureService:按需获取 PNG,再压缩为 JPEG
├─ DisplayService:输入所需媒体会话、UDP 接收、RTCP 反馈
├─ UniversalHID/Indigo:触控、Home、粘贴按键
└─ AppService/PasteboardService
```
常驻复用隧道和服务连接,输入第一次调用时才启动媒体通道。截图使用独立截图服务,不实现视频解码或返回旧视频缓存;这有利于首版验证和降低空闲解码开销,截图吞吐与延迟尚需真机测量。
每个 MCP 进程只连接一台设备,这是上游进程内网络栈的限制。同一设备的不同进程通过文件锁互斥;切换设备、连接方式或主机须先断开。锁不会约束 Xcode、Actell 或其他外部控制器。
停止只处理自己的触控、按键和媒体标识,不调用全局 `stopAll`。操作失败或超时时关闭当前会话,不自动重连并重放动作。输入已经发出但结果不明时,需要用户或 Agent 在新会话截图核对。物理断线时无法保证设备收到释放报告;本地清理与设备端实际释放须区分。
服务不监听 TCP 端口,不存储截图、输入文本或配对记录;依赖库使用电脑已有的配对身份。stdout 只承载 MCP,诊断写入 stderr。具备此 stdio 连接权限的客户端可以调用所有工具,没有额外逐次确认层。
## 验证
```sh
uv run --frozen ruff check .
uv run --frozen pytest -q
uv build
```
测试覆盖系统 Bonjour 的 TXT 解析与取消清理、无线身份匹配、禁止隐式 USB 回退、取消期间资源释放、无线断线唤醒、连接复用、坐标缩放、截图失效、方向变化、并发输入、取消、停止、未知结果不重放、按键释放、媒体定向停止、设备锁,以及真实子进程的 MCP 初始化、工具发现、参数校验和退出。
真机验收步骤见 [docs/ACCEPTANCE.md](docs/ACCEPTANCE.md)。尚待验证持续媒体反馈、方向边界、输入期间的断线、实际关闭无线网络及设备端释放边界。Windows、Linux、横屏触控、自动 DDI 准备和视频流截图不在首版支持范围内。
## 代码与来源
| 文件 | 职责 |
| --- | --- |
| `src/iphone_mcp/bonjour.py` | macOS 系统 DNS-SD 浏览、服务解析、TXT 与资源清理 |
| `src/iphone_mcp/connection.py` | USB/Wi-Fi 发现、Lockdown、指定通道的 CoreDevice 隧道、显式无线准备 |
| `src/iphone_mcp/backend.py` | CoreDevice 服务、媒体反馈与资源释放 |
| `src/iphone_mcp/controller.py` | 会话、串行调度、超时、取消、截图引用 |
| `src/iphone_mcp/model.py` | 图片压缩、坐标转换、输入验证 |
| `src/iphone_mcp/server.py` | MCP 工具与标准图片结果 |
| `src/iphone_mcp/__main__.py` | 启动、设备枚举与只读诊断 |
采用 GPL-3.0-only,第三方来源见 [THIRD_PARTY.md](THIRD_PARTY.md)。协议能力依赖非公开开发者服务及匹配的 DDI,固定依赖版本与 `uv.lock` 一同维护。升级系统或依赖后应重新验收。
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues