内容
# flutter-device-mcp
[模型上下文协议](https://modelcontextprotocol.io) 服务器和捆绑的 CLI(`flutter-device`),可在 iOS、Android、Tizen、tvOS 和 webOS 上构建、部署、启动和驱动 Flutter 应用。其最主要的行为是 **pty 包装启动,可靠地捕获 Dart VM 服务 `ws://…/ws` URI**,因此外部驱动程序(例如第三方 [Marionette](https://pub.dev/packages/marionette_mcp) MCP)可以连接到实时应用并点击、输入、滚动、截图和读取日志。将您的 AI 客户端指向 MCP 服务器,或将人类/脚本指向 CLI:两者都是薄薄的面向单一共享命令核心的接口,因此每个防护措施的行为都相同,无论谁调用。
## 支持矩阵
各行代表平台,各列代表功能。 ✅ 完整 · ⚠️ 部分/实验性 ·
❌ 不支持。
| 平台 | 部署 + VM URI | 热重载/重启 | 生命周期(后台/前台/杀死) | 截图 | 记录 | 系统提示 | TV 输入(按键/指针) |
|----------|:---------------:|:------------------:|:----------------------:|:----------:|:------:|:-------------:|:----------------------:|
| **iOS** | ✅ | ✅ | ✅ | ✅ ¹ | ✅ ² | ✅ ³ | ❌ ⁴ |
| **Android** | ✅ | ✅ | ✅ | ✅ | ✅ ⁵ | ❌ | ❌ ⁴ |
| **Tizen** (三星) | ✅ ⁶ | ✅ | ❌ | ❌ ⁷ | ❌ ⁷ | ❌ | ✅ ⁸ |
| **tvOS** (Apple TV) | ✅ ⁹ | ✅ | ✅ | ⚠️ ¹⁰ | ❌ | ❌ | ❌ ¹¹ |
| **webOS** (LG) | ⚠️ ¹² | ⚠️ | ❌ | ❌ | ❌ | ❌ | ⚠️ ¹³ |
**iOS 是最成熟、测试最好的路径;Android 得到良好的支持。** 两者都使用标准的 Flutter / `adb` / `xcrun` 工具。**Tizen、tvOS 和 webOS 依赖于社区工具链,处于实验阶段 — 欢迎在设备上验证和贡献。**
<sub>
¹ 模拟器原生;物理设备需要 `pymobiledevice3`。
² 模拟器生成 mp4;物理捕获是断断续续的(~1–3 fps)截图爆发。
³ 仅限模拟器,通过 `idb` 访问辅助功能树。
⁴ 不是 TV/焦点平台 — 改用 Marionette 通过 VM 服务驱动应用。
⁵ 通过 `adb` 原生 mp4。
⁶ 通过 `flutter-tizen run`(捕获 VM URI 的启动);构建/安装在 `flutter-tizen` 上重新实现,仍需要设备验证。
⁷ 三星设备上禁用 `sdb shell` — 使用驱动程序通过 VM 服务的截图。
⁸ 通过三星远程频道使用 D-pad / 指针。
⁹ 需要设备上的 **配置文件/发布** 构建;调试构建会发生段错误。
¹⁰ 模拟器截图;设备捕获未连接。
¹¹ Siri-remote / 焦点驱动 — 通过 Marionette 驱动。
¹² 打包需要用户提供的 `preBuild` 钩子;没有标准的 `flutter-webos build`。
¹³ 输入通过 `ssap` 连接,但目前设备受限。
</sub>
## 为什么
- **VM 服务 URI 实际到达您。** 以通常的方式启动 Flutter 应用会将 `ws://…/ws` URI 埋在继承的标准输入/输出中,编排器无法获取。`flutter-device` 通过 pty 启动应用,将进程放到后台,并在其结果中返回 URI — 直接交给像 Marionette 这样的驱动程序。它还保持启动活动,以便热重载/重启在同一守护进程上运行,而不是缓慢的重新构建和重新部署循环。
- **一脑两面。** MCP 服务器和 CLI 是薄薄的翻译层,位于单个共享命令核心之上。每个防护措施 — **一次部署(在安装前杀死陈旧驱动程序)**、**磁盘满恢复(卸载 + 重试安装一次,遇到 `ENOSPC`)**、**陈旧设备 pin 自我修复** 和 **VM URI 捕获** — 都存在于核心中,并由 AI 代理调用工具或人类运行命令时以相同的方式运行。
## 安装
需要 **Node.js 18+**。
无需安装即可运行:
```bash
npx flutter-device-mcp # MCP 服务器(通过标准输入/输出讲话 MCP)
npx flutter-device info # CLI
```
或全局安装:
```bash
npm i -g flutter-device-mcp
flutter-device info
```
### 从源代码运行
`scripts/run.mjs` 是一个 **自构建启动器**:在第一次运行时,它会安装依赖项并编译 `dist/`(如果丢失或过时,则进行内容哈希,因此分支切换会触发重建),然后通过标准输入/输出执行服务器。新的代码仓库可以直接使用,无需手动构建步骤:
```bash
node scripts/run.mjs
```
## 连接您的 AI 客户端
在客户端配置中将 `flutter-device-mcp` 注册为 MCP 服务器。对于 Claude Code 或 Cursor,请添加一个服务器条目(键 `flutter`):
```json
{
"mcpServers": {
"flutter": {
"command": "npx",
"args": ["-y", "flutter-device-mcp"]
}
}
}
```
要从源代码检出运行,请将其指向自构建启动器:
```json
{
"mcpServers": {
"flutter": {
"command": "node",
"args": ["/绝对路径/到/flutter-device-mcp/scripts/run.mjs"]
}
}
}
```
**在编辑配置后重启客户端**,使其加载工具。
## 零配置
通常无需配置。`flutter-device`:
- **通过向上搜索到最近的 `pubspec.yaml` 来查找您的应用**。
- **通过 `.fvmrc`(或 `.fvm/` 别针)检测 fvm**,并在存在时使用 `fvm flutter`,以便设备发现与您的项目固定的 SDK 匹配。
- **从原生项目文件中尽力派生应用/包 ID**,用于卸载和生命周期操作。
运行 `flutter-device info` 以查看 **具有来源的完全解析配置** — `appDir` 及其来源、`defaultPlatform` 及其来源,以及配置文件路径(如果有):
```bash
flutter-device info
```
## 配置
每个设置都按照以下优先级解析(最高优先级获胜),按设置应用:
1. 显式 **标志 / MCP 参数**(`--app-dir`、`--platform`,...),
2. **环境变量**,
3. **`flutter-device.config.json` 文件**(从当前工作目录向上搜索),
4. **从 Flutter 项目本身派生**。
如果以上方法都无法得出应用目录,则工具会报错并告诉您如何设置一个。
### 环境变量
所有变量都使用前缀 `FLUTTER_DEVICE_`(`<PLATFORM>` 是 `IOS`、`ANDROID`、`TIZEN`、`TVOS`、`WEBOS` 之一):
| 变量 | 目的 |
|----------|---------|
| `FLUTTER_DEVICE_APP_DIR` | Flutter 应用路径(覆盖向上搜索) |
| `FLUTTER_DEVICE_CONFIG` | 配置文件的显式路径 |
| `FLUTTER_DEVICE_DEFAULT_PLATFORM` | 调用省略 `platform` 时使用的平台 |
| `FLUTTER_DEVICE_<PLATFORM>_DEVICE` | 平台的设备 pin(ID / 名称 / 地址) |
| `FLUTTER_DEVICE_<PLATFORM>_APP_ID` | 平台的应用 / 包 ID |
| `FLUTTER_DEVICE_ANDROID_LAUNCH_MODE` | `debug` \| `profile` \| `release`(默认 Android 模式) |
| `FLUTTER_DEVICE_TIZEN_PROFILE` | `flutter-tizen build` 的 Tizen 设备配置文件 |
| `FLUTTER_DEVICE_TIZEN_SDK_PATH` | Tizen SDK 数据目录(包含 `platforms/` 的文件夹),用于 rootstrap 预检查;否则为 `$TIZEN_SDK` / 常见位置 |
| `FLUTTER_DEVICE_TIZEN_API_VERSION` | 要针对的 Tizen API 版本(例如 `8.0`);否则从 `tizen/tizen-manifest.xml` 读取 |
| `FLUTTER_DEVICE_TIZEN_SECURITY_PROFILE` | 用于签署 TPK 的安全配置文件名称(`-s`);否则为活动配置文件 |
| `FLUTTER_DEVICE_TVOS_FLUTTER_BIN` | 包含 `flutter-tvos` 二进制文件的目录(添加到 PATH) |
| `FLUTTER_DEVICE_IDB_PATH` | `idb` 二进制文件路径(iOS 系统提示) |
| `FLUTTER_DEVICE_PYMOBILEDEVICE3` | `pymobiledevice3` 路径(iOS 物理捕获) |
| `FLUTTER_DEVICE_FFMPEG` | `ffmpeg` 路径(录制 / gif 编码) |
`APPLE_TV_DEVICE` 也被视为 tvOS 设备 pin 的后备,因此您已经为其他 Apple TV 工具设置的数值在没有重复前缀的情况下被拾取。
### `flutter-device.config.json`
一个签入的配置文件将团队默认值放在一个地方。每个字段都是可选的。`platforms` 下的每个平台都接受 `device`、`appId` 和 `preBuild`(一个在构建前按顺序运行的 shell 命令数组,从 `appDir` 开始 — **应用特定步骤的无分叉扩展点**,例如编译原生引擎);以及 `tizenProfile` / `securityProfile` / `sdk`(Tizen)、`androidLaunchMode`(Android)和 `flutterTvosBinDir`(tvOS)。
**Tizen 注意事项。** Tizen TV 通过网络访问,因此 `sdb` 不会列出它,直到连接 — 将 `device` 设置为 TV 的 IP 地址,工具 **自动运行 `sdb connect <ip>:26101` ** 在第一次使用时(无需手动 `setup`)。在构建之前,它运行一个 **rootstrap 预检查**:`flutter-tizen build tpk` 需要一个 Tizen SDK 设备 rootstrap,与应用的 `tizen-manifest.xml` `api-version` 匹配,当它丢失时,工具会快速失败并提供准确的指导(通过 VS Code *Tizen: 包管理器* 安装该 SDK 版本,或将 `sdk.apiVersion` 设置为您已安装的版本)而不是深入的构建错误。`flutter-device info --platform tizen` 显示 rootstrap 状态,并作为 Tizen SDK 医生。
```json
{
"appDir": "./",
"defaultPlatform": "ios",
"platforms": {
"ios": {
"device": "我的 iPhone",
"appId": "com.example.myapp"
},
"android": {
"appId": "com.example.myapp",
"androidLaunchMode": "profile"
},
"tizen": {
"device": "192.168.1.42",
"tizenProfile": "tv",
"securityProfile": "my-dev-profile",
"sdk": {
"dataPath": "~/tizen-studio/data",
"apiVersion": "8.0"
},
"preBuild": [
"cargo build --release --manifest-path native/engine/Cargo.toml",
"cp native/engine/target/release/libengine.so tizen/lib/"
]
},
"tvos": {
"flutterTvosBinDir": "/opt/flutter-tvos/bin"
},
"webos": {
"device": "lg-tv",
"preBuild": ["./tools/package-webos.sh"]
}
}
}
```
## CLI 用法
```
flutter-device <命令> [--platform <ios|android|tizen|tvos|webos>] [--app-dir <目录>] [标志]
```
命令:
| 命令 | 执行操作 |
|---------|--------------|
| `info` | 设备 + 环境状态和解析配置(带来源) |
| `setup` | 准备设备进行开发( `--device-ip <主机>` ) |
| `build` | 构建应用包( `--profile`、`--debug`、`--target`、`--install`、`--run` ) |
| `deploy` | 安装 + 启动,捕获 VM 服务 URI(**关键命令**) |
| `uninstall` | 从设备中删除应用 |
| `kill-stale` | 杀死占用设备锁定的陈旧启动/驱动程序进程 |
| `terminate` | 强制退出应用(移动设备) |
| `background` | 将应用发送到后台而不杀死它(移动设备) |
| `foreground` | 将应用带到前台(移动设备) |
| `hot-reload` | 应用运行时进行真正的热重载 |
| `hot-restart` | 应用运行时进行热重启(重新运行 `main()` ) |
| `screenshot` | 捕获屏幕到 PNG 文件( `--output-path`、`--include-base64` ) |
| `record` | 记录有限的屏幕剪辑( `--duration-s`、`--fps`、`--format mp4\|gif` ) |
| `set-input-mode` | 选择输入平面( `--mode dpad\|pointer` )— TV |
| `key` | 发送远程/导航键( `--key UP\|DOWN\|ENTER\|…` )— TV |
| `pointer` | 驱动指针( `--action move\|click\|scroll`、`--x --y --dy` )— TV |
| `system-prompt` | 检测/点击 OS 对话框( `--action detect\|tap\|dismiss` )— iOS |
每个命令都打印 JSON 结果并以非零退出,脚本可以对其进行判断。
示例:
```bash
# 部署到 iOS 并打印捕获的 VM 服务 URI
flutter-device deploy --platform ios
# 查看完全解析的配置和设备状态
flutter-device info
# 在 Tizen 上重新安装 + 重启现有的调试构建
flutter-device deploy --platform tizen --debug
# 从正在运行的 Android 应用中记录 10 秒剪辑
flutter-device record --platform android --duration-s 10 --output-path ./demo.mp4
```
## MCP 工具
服务器提供 **17 种工具**。每个工具都接受可选的 `platform` 参数(`ios` | `android` | `tizen` | `tvos` | `webos`);省略它以使用您的配置中的默认平台。
| 工具 | 摘要 |
|------|---------|
| `flutter_info` | 设备 + 环境状态和解析配置与来源 |
| `flutter_setup` | 准备设备进行开发 |
| `flutter_build` | 构建应用包 |
| `flutter_deploy` | **通过 pty 安装 + 启动并返回捕获的 `ws://…/ws` VM 服务 URI** 以交给驱动程序。运行防护措施:首先杀死陈旧驱动程序,从 `ENOSPC` 恢复,记录启动以进行热重载/重启,并探测构建是否可由 Marionette 驱动 |
| `flutter_uninstall` | 从设备中删除应用 |
| `flutter_kill_stale` | 杀死占用设备锁定的陈旧启动/驱动程序进程 |
| `flutter_hot_reload` | 应用运行时进行真正的热重载 |
| `flutter_hot_restart` | 应用运行时进行热重启(重新运行 `main()` ) |
| `flutter_screenshot` | 捕获屏幕 |
| `flutter_record` | 记录有限的屏幕剪辑 |
| `flutter_terminate` | 强制退出应用(移动设备) |
| `flutter_background` | 将应用放到后台而不杀死它(移动设备) |
| `flutter_foreground` | 将应用带到前台(移动设备) |
| `flutter_set_input_mode` | 选择输入平面(dpad/指针)— TV |
| `flutter_key` | 发送远程/导航键 — TV |
| `flutter_pointer` | 驱动指针(移动/点击/滚动)— TV |
| `flutter_system_prompt` | 检测/点击 OS 级对话框 — iOS |
## 每平台的先决条件
- **iOS** — 带有 Xcode 命令行工具(`xcrun devicectl` / `simctl`)和 `flutter` 的 macOS。可选:`idb` 用于系统提示处理;`pymobiledevice3` + `ffmpeg` 用于物理设备的捕获。
- **Android** — `adb` 和 `flutter` 在 PATH 上。
- **Tizen** — `flutter-tizen`、`sdb` 和处于 **开发者模式** 的 Tizen 设备。
- **tvOS** — `flutter-tvos` 工具链和 Xcode。
- **webOS** — `ares` SDK。
## 开发
```bash
npm install
npm run build # tsc → dist/
npm test # jest
npm run inspector # 在 dist/index.js 上启动 MCP Inspector
```
## 许可证
MIT © SupposedlySam