1. 为什么 AI 写得出代码,却“看不见”你的 App
你可能已经习惯了让 AI 帮你补全函数、生成组件、甚至重构整个页面。但真正卡住流程的往往不是写代码,而是写完之后的验证环节:按钮点下去有没有反应?登录表单提交后跳没跳转?列表滑动到底部会不会触发加载更多?这些事 AI 之前基本插不上手,因为它拿不到界面状态,也做不了真实交互。
flutter-skill 想解决的就是这一段。它是一个基于 MCP(Model Context Protocol)的服务器,把 App 的 UI 树、点击、输入、滚动、截图、网络拦截等能力封装成 255 个工具,暴露给 Claude、Cursor、Windsurf 这类支持 MCP 的 AI 客户端。AI 不再只是“读代码猜结果”,而是能直接调用工具去操作界面、读取无障碍树、验证页面变化。
它覆盖 10 个平台:Flutter iOS/Android/Web、Electron、Tauri、KMP、React Native、.NET MAUI、Web CDP、iOS Native。对做跨端项目的团队来说,这意味着同一套 MCP 配置可以服务多个技术栈,不用为每个平台单独搭一套自动化框架。
适合谁用?三类人最直接:一是独立开发者,想让 AI 帮忙跑一遍冒烟测试但不想写测试代码;二是前端/移动端团队,已经在用 Cursor 或 Claude Code,希望把 UI 验证接进日常流程;三是做 AI Agent 的工程师,需要一个能“看见”界面的工具层。下面我从环境准备开始,一步步把这条链路跑通。
2. 前置准备:TaoToken 接入与 flutter-skill 安装
2.1 为什么需要 TaoToken
flutter-skill 本身只是工具层,真正驱动它的是背后的模型。你要让 AI 调用这 255 个工具,就得有一个稳定的模型入口。TaoToken 提供的就是这个入口:一个兼容 OpenAI 风格的 API 网关,支持 Claude、GPT 等模型,配置方式统一,适合在 MCP 客户端里直接填 Base URL 和 Key。
官网地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 端点是 https://taotoken.net/api 。注意 API 地址不带 UTM 参数,配置时直接写这个就行。
你需要先去控制台创建一个 API Key。打开 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,登录后在 API Keys 页面生成一个 Key,复制保存。这个 Key 后面会填到 MCP 配置里。
2.2 安装 flutter-skill
flutter-skill 提供三种安装方式,选你顺手的:
# 方式一:npm 全局安装 npm i -g flutter-skill-mcp # 方式二:Homebrew(macOS) brew tap nicholasmurray/tap brew install flutter-skill # 方式三:Dart 全局激活 dart pub global activate flutter_skill装完之后验证一下:
flutter-skill --version如果提示命令找不到,检查 npm 全局 bin 目录是否在 PATH 里。Homebrew 安装的话一般在 /opt/homebrew/bin 下。
2.3 浏览器场景零配置启动
如果你只是想先试试 Web 自动化,不需要改任何 App 代码。直接启动 serve 模式:
flutter-skill serve https://your-app.com --port=3000这条命令会启动一个本地 MCP 服务,通过 CDP(Chrome DevTools Protocol)连接浏览器。AI 客户端连上之后就能调用 snapshot() 拿到无障碍树,比截图省 99% 的 token。为什么省?因为无障碍树是文本结构,只包含可交互元素和语义信息,而截图是一整张图,模型要花大量 token 去“看”。
2.4 App 场景 30 秒集成
对原生 App 或 Flutter App,需要注入 Bridge SDK。flutter-skill 提供了自动检测:
flutter-skill init它会扫描你的项目结构,识别框架类型,然后把 Bridge SDK 注入到合适的位置。完成后跑一下 demo 验证:
flutter-skill demodemo 会启动一个示例页面,AI 可以通过 MCP 调用 tap、type、scroll 等工具操作它。这一步跑通,说明 Bridge 和 MCP Server 之间的 WebSocket JSON-RPC 2.0 通道是通的。
3. 可复制配置:MCP 客户端接入 flutter-skill
3.1 Claude Code 的 settings 配置
Claude Code 的 MCP 配置放在项目根目录的.claude/settings.json,或者用户级的~/.claude/settings.json。内容如下:
{ "mcpServers": { "flutter-skill": { "command": "flutter-skill", "args": ["mcp", "--port", "3000"], "env": { "TAOTOKEN_API_KEY": "sk-你的Key", "TAOTOKEN_BASE_URL": "https://taotoken.net/api", "TAOTOKEN_MODEL": "claude-sonnet-4-20250514" } } } }这里三个关键字段:Base URL 填 https://taotoken.net/api ,Key 填你在控制台生成的,Model ID 填你要用的模型。Claude Code 会读取这个配置,启动时自动拉起 flutter-skill 的 MCP 进程。
3.2 Cursor 的 MCP 配置
Cursor 的 MCP 配置在~/.cursor/mcp.json:
{ "mcpServers": { "flutter-skill": { "command": "flutter-skill", "args": ["mcp", "--port", "3000"], "env": { "TAOTOKEN_API_KEY": "sk-你的Key", "TAOTOKEN_BASE_URL": "https://taotoken.net/api", "TAOTOKEN_MODEL": "claude-sonnet-4-20250514" } } } }Cursor 重启后,在设置里能看到 flutter-skill 的工具列表。如果没出现,检查 command 路径是不是绝对路径,有时候 GUI 应用读不到 shell 的 PATH。
3.3 Cline / Roo Code 的配置
如果你用 Cline 或 Roo Code,配置格式类似,放在 VS Code 的 settings.json 里:
{ "cline.mcpServers": { "flutter-skill": { "command": "flutter-skill", "args": ["mcp", "--port", "3000"], "env": { "TAOTOKEN_API_KEY": "sk-你的Key", "TAOTOKEN_BASE_URL": "https://taotoken.net/api", "TAOTOKEN_MODEL": "claude-sonnet-4-20250514" } } } }3.4 Codex 的 auth.json 配置
如果你用 Codex CLI,配置在~/.codex/auth.json:
{ "api_key": "sk-你的Key", "base_url": "https://taotoken.net/api", "model": "claude-sonnet-4-20250514" }然后在 Codex 的 MCP 配置里引用 flutter-skill:
[mcp_servers.flutter-skill] command = "flutter-skill" args = ["mcp", "--port", "3000"]注意 TOML 格式和 JSON 的区别,别混用。Codex 读的是 TOML,写错了会静默失败。
3.5 配置检查清单
填完配置后,逐项确认:
| 检查项 | 正确值 | 常见错误 |
|---|---|---|
| Base URL | https://taotoken.net/api | 多写斜杠或漏写 /api |
| API Key | sk-开头 | 复制时带空格 |
| Model ID | 控制台显示的完整 ID | 简写或拼错 |
| command | flutter-skill 绝对路径 | 相对路径在 GUI 里失效 |
| port | 3000(与 serve 一致) | 端口冲突 |
4. 验证请求:让 AI 真正操作一次界面
4.1 启动服务并确认连接
先在一个终端启动 flutter-skill:
flutter-skill serve https://example.com --port=3000看到MCP server listening on port 3000就说明起来了。然后在 AI 客户端里发一条消息:
用 flutter-skill 的 snapshot 工具获取当前页面的无障碍树。
如果配置正确,AI 会调用 snapshot() 并返回一段文本,里面包含页面上的按钮、输入框、链接等元素。这就是 AI“看见”界面的方式。
4.2 调用 tap 工具点击按钮
假设 snapshot 返回里有一个按钮的 ref 是btn-login,你可以让 AI:
点击 ref 为 btn-login 的元素。
AI 会调用 tap 工具,参数是{"ref": "btn-login"}。执行后页面会跳转或触发登录逻辑。tap 操作的延迟在 1ms 左右,基本是即时反馈。
4.3 调用 type 工具输入文字
登录页通常有输入框。先 snapshot 拿到输入框的 ref,然后:
在 ref 为 input-email 的元素里输入 test@example.com。
AI 调用 type 工具,参数{"ref": "input-email", "text": "test@example.com"}。输入完成后可以再 snapshot 一次,确认值已经填进去。
4.4 调用 screenshot 截图验证
有些视觉变化无障碍树看不出来,比如颜色、布局错位。这时候用 screenshot:
对当前页面截图并保存为 login-result.png。
screenshot 的耗时约 31ms,比 snapshot 的 2ms 慢,但能拿到像素级结果。建议先用 snapshot 做结构验证,需要视觉确认时再截图。
4.5 完整验证流程示例
把上面几步串起来,一个登录流程的 AI 驱动测试是这样的:
1. snapshot() → 拿到登录页元素列表 2. type(ref="input-email", text="test@example.com") 3. type(ref="input-password", text="123456") 4. tap(ref="btn-login") 5. snapshot() → 确认跳转到首页 6. screenshot() → 保存结果截图整个过程你只需要用自然语言描述,AI 负责调用工具。656/664 的测试通过率(98.8%)就是这么跑出来的,零手写测试代码。
4.6 QR 码扫码登录自动化
v0.9.6 新增了两个工具:qr_login_start 和 qr_login_wait。适合需要扫码登录的场景。流程是:
1. AI 调用 qr_login_start → 自动检测页面二维码并截图 2. AI 把截图发到聊天工具 → 用户手机扫码 3. AI 调用 qr_login_wait → 检测 URL 变化、Cookie 变化、二维码消失 4. 登录成功,继续后续操作这个能力对测试需要登录态的页面特别有用,不用手动处理扫码环节。
5. 常见报错排查:401、local proxy failed、reading choices
5.1 401 Unauthorized
这是最常见的错误,说明 API Key 没被正确识别。排查顺序:
第一,检查 Key 有没有复制完整。去 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 重新复制一次,注意不要带前后空格。
第二,检查 Base URL 是不是 https://taotoken.net/api 。如果写成了 https://taotoken.net/api/ 带尾斜杠,有些客户端会拼出双斜杠导致 401。
第三,检查环境变量名。不同客户端读的变量名不一样,Claude Code 读TAOTOKEN_API_KEY,Cursor 可能读OPENAI_API_KEY。确认你的配置里变量名和客户端要求的一致。
5.2 local proxy failed
这个报错通常出现在 MCP 客户端启动 flutter-skill 进程时。原因是客户端找不到 flutter-skill 命令,或者端口被占用。
先确认命令路径:
which flutter-skill把输出的绝对路径填到配置的 command 字段。如果是 npm 全局安装,路径可能是/usr/local/bin/flutter-skill或~/.npm-global/bin/flutter-skill。
端口占用的话,换一个端口:
flutter-skill serve https://example.com --port=3001然后同步改配置里的 port。
5.3 reading choices 报错
这个错误一般出现在模型返回格式不符合预期时。MCP 协议要求工具调用返回结构化的 JSON,如果模型输出被截断或格式错乱,客户端解析就会报 reading choices。
解决办法:换一个上下文窗口更大的模型,或者在 prompt 里明确要求“只返回工具调用,不要额外解释”。另外检查 max_tokens 设置,太小会导致输出被截断。
5.4 OAuth 相关错误
如果你在配置里用了需要 OAuth 的模型入口,可能会遇到 token 过期。TaoToken 的 API Key 方式是静态 Key,不涉及 OAuth 刷新,所以用 Key 认证可以避开这类问题。确认你的配置里没有混入 OAuth 相关的字段。
5.5 工具调用无响应
AI 说“正在调用工具”但一直没结果。先看 flutter-skill 的终端输出,有没有收到请求。如果没收到,说明 MCP 连接没建立。检查客户端日志里的 MCP 连接状态。
如果收到了请求但没返回,可能是目标页面加载超时。给 serve 命令加一个超时参数:
flutter-skill serve https://example.com --port=3000 --timeout=300005.6 排查对照表
| 报错 | 根因 | 解决 |
|---|---|---|
| 401 | Key 错误或 Base URL 不对 | 重新复制 Key,确认 /api 结尾 |
| local proxy failed | 命令路径或端口问题 | 用绝对路径,换端口 |
| reading choices | 模型输出格式错乱 | 换模型,加 max_tokens |
| OAuth error | 认证方式混用 | 统一用 API Key |
| 无响应 | MCP 未连接或超时 | 查日志,加 timeout |
6. 把 AI 驱动测试接进日常流程
跑通一次验证之后,你可以把这套流程固化下来。我的做法是在项目里建一个mcp-test.md,把常用的测试指令写成模板:
## 登录流程测试 1. snapshot 获取登录页 2. 输入邮箱和密码 3. 点击登录 4. snapshot 确认跳转 5. screenshot 存档 ## 列表滑动测试 1. snapshot 获取列表 2. scroll 到底部 3. snapshot 确认加载更多每次让 AI 执行时直接引用这个文件,不用重复描述。对长期做编码和 Agent 的场景,可以考虑用 Coding Plan 来降低调用成本,地址是 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。
如果你更想先手动验证模型效果,可以打开模型对话页面 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite ,直接和模型交互,确认工具调用逻辑符合预期后再接进 MCP。
接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有完整的 API 参数说明和示例。Claude Code 的专项接入指南在 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude-code-anthropic&utm_campaign=rewrite ,如果你用 Claude Code 做主力开发工具,这篇值得先看。
最后提醒一个实际踩过的坑:flutter-skill 的 Bridge SDK 注入后,记得在 App 的 release 构建里排除掉,否则会带上调试通道。在pubspec.yaml里用dev_dependencies引入,或者用条件编译包一层。测试环境跑通之后,生产包不要包含 Bridge 代码。