1. 移动端自动化的真正痛点在哪里
做过移动端自动化的朋友应该都有体会,这个领域最让人头疼的从来不是写脚本本身,而是"连接"这件事。你写了一堆 Appium 脚本,跑起来发现设备掉了;你搭好了 WebDriverAgent,结果签名过期了;你想让 AI 帮你操作一下手机,发现根本没有一个统一的接口能让模型"看见"屏幕上的元素。mobile-mcp 这个项目,本质上就是在解决这个"最后一公里"的问题——它把 iOS 和 Android 设备的操作能力,通过 MCP(Model Context Protocol)协议暴露出来,让任何支持 MCP 的 AI 客户端都能直接操控真机。
说白了,mobile-mcp 就是一个"翻译层"。左边是 iOS 的 XCUITest 和 Android 的 ADB/UIAutomator,右边是 MCP 协议定义的标准工具调用接口。中间这层翻译做好了,Claude、Cursor、各种 AI Agent 框架就能像调用本地函数一样去点击手机屏幕、滑动列表、输入文字、截屏分析。这跟 playwright-mcp 让 AI 操控浏览器的思路是一脉相承的,只不过战场从浏览器换到了移动端。
这个项目适合谁?三类人值得重点关注:一是做移动端自动化测试的工程师,你们可以用它来快速搭建 AI 驱动的测试流程;二是搞 AI Agent 开发的开发者,你们需要一个可靠的移动端操作接口;三是对 MCP 协议感兴趣、想找一个完整实战项目来学习的技术爱好者。不管你是哪一类,理解 mobile-mcp 的架构设计和实操细节,都能帮你少走很多弯路。
2. mobile-mcp 的架构设计与核心思路拆解
2.1 为什么是 MCP 而不是自定义 API
很多人第一反应是:我直接写个 HTTP 接口不就行了,为什么要用 MCP?这个问题我在刚开始接触时也想过。答案其实很简单——生态。MCP 是 Anthropic 推动的一个开放协议标准,它的核心价值在于"一次实现,到处调用"。你按照 MCP 协议暴露了工具,那么所有支持 MCP 的客户端(Claude Desktop、Cursor、Windsurf、各种 Agent 框架)都能直接接入,不需要你为每个客户端单独写适配层。
对比一下传统方案:如果你自己定义 REST API,那 Claude 要接你的 API 得写一套适配,Cursor 要接又得写一套,每个客户端的工具描述格式还不一样。MCP 把这些标准化了——工具名称、参数 schema、返回值格式,全部统一。这就像 USB-C 接口的意义,不是它比专用接口性能更好,而是它让所有设备都能用同一根线。
另一个关键考量是上下文管理。MCP 协议天然支持流式返回和资源管理,AI 模型可以在一次对话中连续调用多个工具,根据上一步的结果决定下一步操作。这对于移动端自动化来说太重要了——你不可能预先知道每个步骤的结果,必须让模型根据屏幕反馈动态决策。
2.2 iOS 和 Android 两条技术路线的取舍
mobile-mcp 在底层实现上,iOS 和 Android 走的是完全不同的技术路线,这个设计决策值得仔细说说。
iOS 这边,核心依赖是WebDriverAgent(简称 WDA)。WDA 是 Facebook 开源的一个测试框架,它利用 XCUITest 框架在设备上运行一个 HTTP 服务器,接收外部指令并转化为 XCUITest 操作。mobile-mcp 通过 WDA 的 REST API 来操控 iOS 设备。为什么不用 libimobiledevice 那套?因为 libimobiledevice 只能做设备级别的操作(安装应用、获取设备信息),没法做 UI 级别的精细控制。你要点击屏幕上某个特定按钮,必须走 XCUITest 这条路。
Android 这边就简单多了,直接走ADB + UIAutomator。ADB 负责设备连接和基础操作(安装、启动、截屏),UIAutomator 负责 UI 元素定位和操作。Android 的开放性是它的优势——你不需要像 iOS 那样折腾签名和开发者模式,插上 USB 打开调试就能用。但 Android 的碎片化也是真实存在的问题,不同厂商的 ROM 对 UIAutomator 的支持程度参差不齐,这个后面会详细讲。
这里有个容易踩的坑:很多人以为 iOS 自动化必须用 Mac,其实 mobile-mcp 的服务端确实需要 macOS 来运行 WDA,但 MCP 客户端可以在任何机器上。也就是说你可以在 Windows 上写代码,通过局域网连接到 Mac 上的 mobile-mcp 服务。
2.3 工具集设计:暴露哪些能力给 AI
mobile-mcp 暴露给 AI 的工具集设计得相当克制,没有追求大而全,而是聚焦在最核心的操作上。我梳理了一下主要工具类别:
| 工具类别 | 具体功能 | 对应底层实现 |
|---|---|---|
| 设备管理 | 列出设备、获取设备信息 | iOS: ideviceinfo / Android: adb devices |
| 应用管理 | 安装、卸载、启动、停止应用 | iOS: WDA API / Android: adb shell am |
| UI 交互 | 点击、长按、滑动、输入文字 | iOS: WDA / Android: UIAutomator |
| 元素定位 | 获取 UI 树、查找元素 | iOS: WDA source / Android: dump hierarchy |
| 屏幕操作 | 截屏、录屏 | iOS: WDA screenshot / Android: adb screencap |
| 系统操作 | 返回、Home、音量、旋转 | iOS: WDA / Android: adb shell input |
这个设计的好处是,每个工具都有明确的语义,AI 模型很容易理解什么时候该用哪个工具。比如模型看到"点击登录按钮"这个任务,它会先调用"获取 UI 树"找到按钮的坐标或选择器,然后调用"点击"工具执行操作。这个流程跟人类操作手机的逻辑是一致的。
2.4 通信层:stdio 还是 SSE
MCP 协议支持多种传输方式,mobile-mcp 主要用的是stdio(标准输入输出)。这意味着 MCP 客户端会启动 mobile-mcp 作为一个子进程,通过标准输入输出进行通信。这种方式的优点是简单、无需网络配置、安全性好(不暴露端口)。缺点是不支持远程调用——你的 MCP 客户端和 mobile-mcp 必须在同一台机器上。
如果你需要远程使用,可以考虑 SSE(Server-Sent Events)模式,mobile-mcp 也支持。但这种模式下你需要自己处理网络暴露带来的安全问题。我的建议是,除非有明确的远程需求,否则优先用 stdio 模式,省心。
3. 环境搭建与核心细节实操
3.1 iOS 环境:从零到能跑通
iOS 这边的环境搭建是最容易劝退人的环节,我把完整流程和踩坑点都列出来。
第一步:安装 Xcode 和命令行工具
从 App Store 安装 Xcode,然后执行:
xcode-select --install sudo xcode-select -s /Applications/Xcode.app/Contents/Developer验证安装:xcodebuild -version能输出版本号就说明没问题。
第二步:部署 WebDriverAgent
这一步是最容易出问题的。WDA 需要签名才能在真机上运行。你需要一个 Apple Developer 账号(免费的也行,但免费账号签名 7 天过期,需要重新签)。
# 克隆 WDA 仓库 git clone https://github.com/appium/WebDriverAgent.git cd WebDriverAgent # 用 Xcode 打开项目 open WebDriverAgent.xcodeproj在 Xcode 中,你需要做几件事:
- 选择 WebDriverAgentRunner target
- 在 Signing & Capabilities 中设置你的 Team
- 修改 Bundle Identifier 为一个唯一的字符串(比如 com.yourname.wda)
- 选择你的真机设备作为 target
然后按 Cmd+U 运行测试,WDA 会被安装到设备上并启动。如果一切正常,你会在 Xcode 控制台看到设备 IP 和端口号(通常是 8100)。
第三步:端口转发
WDA 运行在设备的 8100 端口上,你需要把它转发到 Mac 本地:
# 用 iproxy(来自 libimobiledevice) iproxy 8100 8100 # 或者用 tidevice(国内开发者常用的工具) tidevice relay 8100 8100验证:curl http://localhost:8100/status应该返回 JSON 格式的设备状态信息。
第四步:安装 mobile-mcp
# 如果用 npm npm install -g mobile-mcp # 或者从源码构建 git clone https://github.com/mobile-next/mobile-mcp.git cd mobile-mcp npm install npm run build第五步:配置 MCP 客户端
以 Claude Desktop 为例,编辑配置文件(macOS 路径是~/Library/Application Support/Claude/claude_desktop_config.json):
{ "mcpServers": { "mobile-mcp": { "command": "node", "args": ["/path/to/mobile-mcp/dist/index.js"], "env": { "DEVICE_TYPE": "ios" } } } }重启 Claude Desktop,你应该能在工具列表里看到 mobile-mcp 提供的工具。
3.2 Android 环境:相对简单但仍有坑
Android 的环境搭建比 iOS 简单不少,但也不是没有坑。
第一步:安装 Android SDK 和 ADB
如果你已经装了 Android Studio,ADB 通常在~/Library/Android/sdk/platform-tools/(macOS)或%LOCALAPPDATA%\Android\Sdk\platform-tools\(Windows)。把这个路径加到系统 PATH 里。
# 验证 adb version第二步:开启设备调试
在 Android 设备上:
- 进入设置 → 关于手机 → 连续点击"版本号"7 次,开启开发者模式
- 返回设置 → 开发者选项 → 开启"USB 调试"
- 用 USB 连接电脑,设备上会弹出授权对话框,勾选"始终允许"
验证:adb devices应该能看到你的设备。
第三步:安装 UIAutomator 服务
mobile-mcp 需要 UIAutomator 来获取 UI 层级信息。有些设备自带,有些需要手动安装:
# 检查是否已安装 adb shell pm list packages | grep uiautomator # 如果没有,安装 Android SDK 自带的版本 adb install /path/to/android-sdk/tools/lib/uiautomator.jar实际上,现代 Android 设备通常不需要单独安装 UIAutomator,adb shell uiautomator dump命令可以直接使用。如果遇到权限问题,试试adb root(仅限模拟器或已 root 的设备)。
第四步:配置 mobile-mcp
跟 iOS 类似,只是在环境变量里指定 Android:
{ "mcpServers": { "mobile-mcp": { "command": "node", "args": ["/path/to/mobile-mcp/dist/index.js"], "env": { "DEVICE_TYPE": "android", "ANDROID_HOME": "/Users/yourname/Library/Android/sdk" } } } }3.3 设备连接方式的选择
USB 连接和无线连接各有优劣,我整理了一个对比:
| 连接方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| USB | 稳定、延迟低、无需额外配置 | 线缆限制、设备数量受限 | 开发调试、单设备测试 |
| 无线(ADB) | 自由移动、支持多设备 | 需要初始 USB 配对、网络不稳定时掉线 | 演示、多设备并行 |
| 无线(iOS) | 同上 | WDA 需要额外配置、稳定性较差 | 特定场景 |
Android 无线调试的配置(需要 Android 11+):
# 先用 USB 连接,开启无线调试 adb tcpip 5555 # 查看设备 IP adb shell ip addr show wlan0 # 断开 USB,无线连接 adb connect 192.168.1.100:5555iOS 无线调试就麻烦多了,需要 WDA 支持,而且稳定性一直是个问题。我的建议是 iOS 尽量用 USB,Android 可以尝试无线。
3.4 实操心得:那些文档里不会写的东西
关于 iOS 签名:免费开发者账号签名的 WDA 只有 7 天有效期,过期后需要重新用 Xcode 运行一次。如果你要长期使用,建议花 99 美元买个开发者账号,签名有效期一年。另外,用xcodebuild命令行签名比用 Xcode GUI 更稳定,不容易出现"无法验证"的问题。
关于 Android 碎片化:小米、华为、OPPO 等厂商的 ROM 对 UIAutomator 的支持有差异。我遇到过小米设备上uiautomator dump返回空文件的情况,解决方案是关闭 MIUI 优化(开发者选项里)或者改用adb shell dumpsys window来获取窗口信息。华为设备则可能需要在开发者选项里额外开启"仅充电模式下允许 ADB 调试"。
关于端口冲突:WDA 默认用 8100 端口,如果你同时跑多个 iOS 设备,需要给每个设备分配不同端口。iproxy 8101 8100这样转发,然后在 mobile-mcp 配置里指定端口。
4. 完整实操流程:让 AI 帮你操作手机
4.1 一个完整的自动化场景
假设我们要让 AI 完成一个任务:打开微信,找到某个联系人,发送一条消息。这个场景涵盖了启动应用、UI 定位、文本输入、点击操作等核心环节。
第一步:确认设备连接
在 MCP 客户端里,先调用list_devices工具。返回结果应该包含你的设备信息:
{ "devices": [ { "id": "ABCD1234-5678-90EF-GHIJ-KLMNOPQRSTUV", "name": "iPhone 15 Pro", "platform": "ios", "status": "connected" } ] }如果设备列表为空,检查 USB 连接、WDA 是否运行、端口转发是否正常。
第二步:启动目标应用
调用launch_app工具,传入微信的 Bundle ID(iOS 是com.tencent.xin,Android 是com.tencent.mm):
{ "bundleId": "com.tencent.xin" }这一步底层执行的是xcrun simctl launch或adb shell am start。启动后建议等 2-3 秒,让应用完全加载。
第三步:获取 UI 层级
调用get_ui_hierarchy工具,返回的是一棵 XML 树(iOS)或 JSON 树(Android)。这棵树包含了屏幕上所有可见元素的属性:类型、文本、坐标、是否可点击等。
<XCUIElementTypeApplication name="微信"> <XCUIElementTypeWindow> <XCUIElementTypeOther> <XCUIElementTypeButton name="搜索" x="320" y="88" width="44" height="44"/> <XCUIElementTypeTable> <XCUIElementTypeCell name="文件传输助手" x="0" y="200" width="390" height="72"/> </XCUIElementTypeTable> </XCUIElementTypeOther> </XCUIElementTypeWindow> </XCUIElementTypeApplication>AI 模型会解析这棵树,找到目标元素。比如要点击"搜索"按钮,它会提取出坐标 (320, 88) 或者用 name 属性来定位。
第四步:执行点击操作
调用tap工具:
{ "x": 342, "y": 110 }注意坐标是元素中心点,不是左上角。计算方式:x + width/2, y + height/2。
第五步:输入文字
点击搜索框后,调用type_text工具:
{ "text": "文件传输助手" }iOS 上这个操作通过 WDA 的/wda/keys接口实现,Android 上通过adb shell input text实现。注意中文输入在 Android 上可能有问题,因为adb shell input text不支持 Unicode。解决方案是用 ADBKeyBoard 这个输入法,或者用adb shell am broadcast发送广播。
第六步:验证结果
再次调用get_ui_hierarchy,确认搜索结果出现了目标联系人。然后点击联系人,进入聊天界面,输入消息内容,点击发送。
整个流程下来,AI 模型会调用 8-10 次工具,每次调用之间它会根据返回结果决定下一步。这就是 MCP 协议的价值——它让 AI 有了"感知-决策-执行"的闭环能力。
4.2 参数计算与选择过程
在实际操作中,有几个参数需要特别注意:
截图压缩率:mobile-mcp 返回的截图默认是 PNG 格式,一张 1080P 的截图可能有 2-3MB。如果频繁截屏,会消耗大量 token。建议在配置里开启 JPEG 压缩,质量设为 70-80,这样截图大小能降到 200-300KB,对 AI 识别影响不大。
UI 树深度:有些应用的 UI 层级非常深(比如嵌套了十几层 ViewGroup),完整的 UI 树可能有几千行。mobile-mcp 支持设置最大深度,建议设为 10-15 层,既能覆盖大部分元素,又不会让 token 爆炸。
操作间隔:AI 连续调用工具时,如果间隔太短,设备可能来不及响应。建议在 mobile-mcp 配置里设置ACTION_DELAY为 500-1000ms。这个值太小会导致操作丢失,太大又会影响效率。
超时设置:获取 UI 树和截屏操作可能耗时较长,特别是在低端设备上。建议把超时设为 10-15 秒,避免因为超时导致整个流程失败。
4.3 实操现场记录:一次完整的调试过程
让我记录一次真实的调试过程,展示遇到问题时的排查思路。
目标:让 AI 在 Android 设备上打开设置,进入"关于手机",读取 Android 版本号。
第一次尝试:AI 调用launch_app启动设置应用,然后调用get_ui_hierarchy。返回的 UI 树是空的。
排查:手动执行adb shell uiautomator dump /sdcard/ui.xml,然后adb pull /sdcard/ui.xml,发现文件内容是空的。这说明 UIAutomator 服务没有正常工作。
解决:检查发现是设备的 MIUI 优化导致的。在开发者选项里关闭"MIUI 优化",重启设备,问题解决。
第二次尝试:UI 树正常返回了,但 AI 找不到"关于手机"这个选项。查看 UI 树发现,这个选项在 ScrollView 里,当前屏幕没有显示出来。
解决:AI 需要先执行滑动操作。调用swipe工具,从屏幕底部向上滑动。滑动后再获取 UI 树,就能找到目标元素了。
第三次尝试:找到了"关于手机"元素,点击后进入新页面。但 AI 读取到的版本号是"13",而实际版本是"13.0.5"。
排查:查看 UI 树发现,版本号被拆分成了两个 TextView,AI 只读取了第一个。
解决:调整 AI 的提示词,让它读取完整的 UI 树并合并相邻的文本节点。或者在 mobile-mcp 层面做处理,把相邻的文本节点合并。
这个过程展示了移动端自动化的典型挑战:环境问题、UI 动态性、数据解析。每一个环节都可能出问题,需要耐心排查。
5. 常见问题与排查技巧实录
5.1 连接类问题速查表
| 问题现象 | 可能原因 | 排查步骤 | 解决方案 |
|---|---|---|---|
| 设备列表为空 | USB 连接问题 | adb devices或idevice_id -l | 换线、换端口、重启 ADB 服务 |
| WDA 启动失败 | 签名问题 | 查看 Xcode 控制台报错 | 重新签名、检查 Bundle ID 唯一性 |
| 端口转发失败 | iproxy 未运行 | lsof -i :8100 | 启动 iproxy 或 tidevice |
| 设备显示 unauthorized | 未授权调试 | 设备屏幕上的授权对话框 | 勾选"始终允许"、撤销授权重试 |
| 连接频繁断开 | 无线信号不稳定 | 检查网络延迟 | 改用 USB 连接 |
5.2 UI 操作类问题
元素定位不到:最常见的原因是 UI 树没有及时更新。应用界面切换后,需要等待一段时间再获取 UI 树。建议在每次操作后加 1-2 秒延迟。另外,有些元素是动态生成的(比如列表项),需要先滑动到可见区域才能定位。
点击无效:可能是坐标计算错误,或者元素被遮挡。iOS 上可以用 WDA 的/wda/element/{id}/click接口直接点击元素,比坐标点击更可靠。Android 上可以用adb shell input tap但要注意坐标是相对于屏幕的,不是相对于应用的。
输入中文乱码:Android 的adb shell input text不支持 Unicode。解决方案有三个:一是安装 ADBKeyBoard 输入法,通过广播发送中文;二是用adb shell am broadcast -a ADB_INPUT_TEXT --es msg '中文';三是用 UIAutomator 的setText方法(需要写 Java 代码)。
滑动不灵敏:adb shell input swipe的持续时间参数很关键。默认是 300ms,对于长距离滑动可能太快。建议设为 500-800ms。iOS 上 WDA 的/wda/dragfromtoforduration接口也需要调整 duration 参数。
5.3 性能与稳定性问题
截图太慢:iOS 上 WDA 截屏走的是 XCUITest 的 API,本身就比较慢。优化方法是降低截图频率,只在必要时截屏。Android 上adb exec-out screencap -p比adb shell screencap快很多,因为避免了文件写入和读取。
UI 树太大:前面提到过,设置最大深度。另外可以过滤掉不可见元素和装饰性元素,只保留可交互的元素。mobile-mcp 支持配置过滤规则。
内存泄漏:长时间运行后,WDA 或 UIAutomator 服务可能内存泄漏。建议定期重启服务,或者设置自动重启策略。iOS 上可以用xcodebuild test-without-building重新启动 WDA。
5.4 独家避坑技巧
技巧一:用 accessibility id 而不是坐标。坐标点击最大的问题是屏幕分辨率变化或 UI 布局调整后就失效了。accessibility id 是元素的唯一标识,更稳定。在 iOS 上对应accessibilityIdentifier,Android 上对应content-desc或resource-id。
技巧二:处理弹窗和权限对话框。应用启动时经常弹出权限请求或更新提示,这些弹窗会遮挡目标元素。建议在启动应用后先检查是否有弹窗,有的话先处理掉。可以写一个通用的弹窗处理函数,检测常见的弹窗类型并自动关闭。
技巧三:用录屏辅助调试。当自动化流程失败时,光看日志很难定位问题。建议开启录屏,回放操作过程。iOS 上用xcrun simctl io booted recordVideo,Android 上用adb shell screenrecord。
技巧四:建立元素库。对于频繁操作的元素,建立一个元素库,记录它们的定位方式。这样 AI 不需要每次都解析完整的 UI 树,直接查库就行,能大幅提升效率。
技巧五:处理 WebView 和 Hybrid 应用。很多应用的核心页面是 WebView,UIAutomator 和 XCUITest 对 WebView 的支持有限。解决方案是切换到 WebView 上下文(Android 上用adb shell cat /proc/net/unix | grep webview找到 socket),或者用图像识别的方式定位元素。
6. 进阶玩法与扩展思路
6.1 结合视觉模型做图像识别
mobile-mcp 返回的截图可以直接喂给视觉模型(比如 GPT-4V、Claude 3 Vision),让模型"看懂"屏幕内容。这在处理游戏、Canvas 绘制、自定义控件等场景下特别有用,因为这些场景下 UI 树拿不到有效信息。
具体做法是:截屏 → 传给视觉模型 → 模型返回目标元素的坐标 → 调用 tap 工具点击。这个流程比传统的图像模板匹配灵活得多,不需要预先准备模板图片。
6.2 多设备并行操作
mobile-mcp 支持同时连接多个设备。你可以让 AI 同时在 iOS 和 Android 上执行相同的操作,对比结果。这在兼容性测试中非常有用。
配置方式是在 MCP 客户端里启动多个 mobile-mcp 实例,每个实例连接不同的设备。或者用 mobile-mcp 的多设备模式,在工具调用时指定设备 ID。
6.3 与 CI/CD 流水线集成
把 mobile-mcp 集成到 CI/CD 里,可以实现自动化的冒烟测试。每次代码提交后,自动在真机上跑一遍核心流程,发现问题及时报警。
集成方式有两种:一是用 MCP 客户端(比如 Claude API)作为执行引擎,在 CI 脚本里调用;二是直接用 mobile-mcp 的底层 API,绕过 MCP 协议。前者更灵活,后者更稳定。
6.4 自定义工具扩展
mobile-mcp 的工具集是可以扩展的。如果你有特殊需求,比如需要操作蓝牙、NFC、传感器等,可以自己写工具实现,然后注册到 mobile-mcp 里。
扩展方式是继承 mobile-mcp 的 Tool 基类,实现execute方法,然后在工具注册表里添加。具体可以参考项目源码里的src/tools/目录。
7. 我个人在实际操作中的体会
折腾 mobile-mcp 这段时间,最大的感受是:移动端自动化的核心难点从来不是技术本身,而是环境的不可控性。同样的脚本,在你的机器上跑得好好的,换一台设备就可能各种报错。iOS 的签名、Android 的碎片化、网络的不稳定,每一个都是潜在的坑。
我的建议是,如果你要长期使用 mobile-mcp,一定要做好三件事:第一,建立完善的环境检查流程,每次使用前先跑一遍健康检查;第二,准备好备用设备,主力设备出问题时能快速切换;第三,记录所有踩过的坑和解决方案,形成自己的知识库。
另外,不要指望 AI 能 100% 正确地完成所有操作。移动端 UI 的动态性太强了,AI 也会有判断失误的时候。实际使用中,我通常会让 AI 处理 80% 的常规操作,剩下 20% 的复杂场景还是人工介入。这样既能享受 AI 带来的效率提升,又能保证关键流程的可靠性。
最后分享一个小技巧:如果你在 iOS 上遇到 WDA 频繁掉线的问题,试试用tidevice代替iproxy做端口转发。tidevice 是国内开发者写的一个工具,对 iOS 设备的兼容性更好,而且支持自动重连。安装方式很简单:pip install tidevice,然后用tidevice relay 8100 8100就能替代 iproxy 了。这个工具还支持查看设备日志、安装应用等功能,比 libimobiledevice 那套工具链好用不少。