一、指纹匹配自动认领(可选,默认关)
- 发现设置新增「指纹匹配自动认领」勾选(app_meta: discovery_auto_claim,默认 0)
- 打开后:扫描发现某设备指纹与池中已有记录一致(同一台换了 IP)→ 自动迁移记录到新地址
并同步分组/任务引用,零点击;关闭时维持"识别自动 + 人工点一次确认"
- 默认关的原因:认领会改写分组/任务引用(数据结构变动),交人工确认更稳妥
- 扫描结果与状态行会显示本轮自动认领了几台
二、设备名称在界面上呈现(凡选择/展示设备处都显示名称)
- 新增前端 helper `devText(name, serial)`(base.js):有名称→「名称 · serial」
- 监控页设备表:名称加粗为主、地址作副行(未命名显示橙色提醒);任务概况的覆盖设备
chip 也优先显示名称(tooltip 保留完整地址)
- AI 控制台:目标设备下拉、实时画面设备下拉、目标/运行中提示都带名称(serial→name 映射)
- 任务编辑器「指定设备」下拉、分组编辑的设备勾选列表:带名称
- 后端 `/api/devices` 新增 `items`([{serial,name,model}],`devices` 保持兼容);
`/api/agent/devices` 增加 `name` 字段
- MCP `de_list_devices` 返回 `name`,并在工具说明与 Agent 系统提示里要求"汇报用名称、
调工具用 serial"
文档:API.md(items/name/auto_claim + §6.2.1 名称呈现表)、MCP.md(工具返回)
自测(全通过):自动认领端到端(开开关→扫描→自动迁址 + 名称保留 + 分组/任务引用同步 +
待连接池清理 + 开关默认关且可持久化);名称显示浏览器验证(监控页/覆盖设备 chip/AI 目标与
观看下拉/任务编辑器/分组弹窗/发现设置开关);设备指纹与人工认领回归
11 KiB
MCP 手机控制手册(MCP.md)
多模态 AI(DeepSeek / Claude 等)通过 MCP(Model Context Protocol) 操作 Android 手机的统一出口。平台自带的「AI 控制台」(mcp_agent/)与外部 MCP 客户端使用的都是这一批工具。
本文 = 使用手册(工具清单 / 用法 / 接入)。设计取舍与演进见 MCP_DESIGN.md;AI 控制台的会话/经验/动作机制见 AI_CONSOLE.md。
1. 架构与边界
AI 控制台 / 外部 MCP 客户端
│ Streamable HTTP(http://<host>:8033/mcp)
▼
MCP Server(mcp_server/mcp_server.py) ← 19 个 de_* 工具
│ 平台 HTTP API(登录 + CSRF) 或 轻量 adb/u2 直连
▼
auto_control 平台(:18050) ← 设备池 / 任务 / 看屏
│ adb / uiautomator2 / uiautodev / OCR
▼
Android 设备(IP:5555)
- 轻量通道直连(adb monkey 开 App、u2 输入、本地 OCR)不绕平台,省时间省 token
- 坐标换算、UI 吸附、剪贴板注入等细节全部在 Server 层消化,模型只给"意图"
- 只暴露设备层能力:平台级的任务增改/分组/设备池/APK/备份等 REST 只给 Web 前端用,未做成 MCP 工具(补齐规划见 AI_TASK_GEN.md §9)
2. 运行与配置
生产跑在 220 的 python-app 容器内(scripts/start.sh 自动拉起),端点 http://192.168.20.220:8033/mcp。
# 本机手动启动
MCP_ALLOW_WRITE=1 MCP_PLATFORM_USER=admin MCP_PLATFORM_PASS=<平台密码> \
MCP_AUDIT_FILE=data/mcp_audit.log python -m mcp_server.mcp_server
| 环境变量 | 默认 | 说明 |
|---|---|---|
MCP_PLATFORM_URL |
http://127.0.0.1:18050 |
平台地址 |
MCP_PLATFORM_USER / MCP_PLATFORM_PASS |
admin / 空(start.sh 兜底 admin123) |
平台登录凭据。改过 admin 密码必须同步,否则登录失败 |
MCP_ALLOW_WRITE |
0(start.sh 内强制 1) |
写门控:=1 才允许点击/输入/开关 App(只读工具不受限) |
MCP_ALLOWED_SERIALS |
空 | 设备白名单(逗号分隔)。非空 = 只允许列出的 serial;为空时只校验 serial 非空,不按平台设备池过滤(语义缺口见 backlog) |
MCP_HTTP_HOST / MCP_HTTP_PORT |
0.0.0.0 / 8033 |
监听 |
MCP_SCREENSHOT_WIDTH |
540 |
截图返回宽度上限(模型看到的坐标系) |
MCP_JPEG_QUALITY |
70 |
截图 JPEG 质量 |
MCP_AUDIT_FILE |
/var/log/mcp/audit.log(容器兜底 /tmp/mcp_audit.log) |
审计日志路径 |
MCP_PLATFORM_TIMEOUT |
30 |
平台请求超时(秒) |
MCP_ENABLED |
1 |
只被 scripts/start.sh 消费:=0 则不后台拉起 MCP |
mcp_server/config.py只读进程环境变量,不读.env;scripts/supervise.sh不拉起 MCP(容器场景由start.sh负责)。
3. 坐标空间(重要)
de_screenshot返回 ≤540px 宽的 JPEG(display 空间),同时给出native_size(设备原生分辨率)与screen_statede_tap/de_swipe的坐标一律用de_screenshot返回图像的坐标系,Server 按比例换算成原生坐标- 必须先截图再点击:Server 需要最近一次截图来建立坐标空间,否则报
invalid_param「请先对该设备执行 de_screenshot」 de_tap带自动吸附:落点若在某个可点击元素内,实际点该元素中心 → 坐标只需大致对准;返回snapped/label便于核对
4. 工具清单(19 个)
统一返回约定:
{"ok": true, "data": { … }}
{"ok": false, "error": {"code": "device_busy", "message": "设备正在执行任务…"}}
错误码:invalid_param · device_not_allowed · write_disabled · device_busy · platform_unavailable · device_offline · text_not_found。
4.1 设备与状态(只读)
| 工具 | 参数 | 返回 / 说明 |
|---|---|---|
de_list_devices |
— | [{serial, name, model, online, task_job, worker_status, foreground_app}]。开局第一步;name 是设备在平台里的名称(身份),向用户汇报时用名称(同型号多台靠它区分),调工具仍用 serial |
de_foreground_app |
serial |
{foreground_app} 当前前台包名(dumpsys,MIUI 焦点为空时兜底) |
de_list_tasks |
— | 平台任务计划 [{id,name,task_type,enabled,schedule}](只读) |
4.2 观察屏幕(只读)
| 工具 | 参数 | 返回 / 说明 |
|---|---|---|
de_screenshot |
serial |
{image:{type:"image",data:<base64>,mimeType:"image/jpeg"}, width, height, native_size, screen_state}。多模态模型直接看图 |
de_ui_tree |
serial, limit(150,1-300) |
{count, elements:[{text,id,desc,class,clickable,bounds}]},可点击元素排前;limit 控 token |
de_ocr |
serial |
{count, texts:[{text,score}]}(≤100 条)。UI 树拿不到的图片/WebView 文字用它 |
de_read_clipboard |
serial |
{clipboard} |
de_list_apps |
serial, keyword("") |
{count, packages} 第三方已装包名(pm list packages -3,≤200) |
4.3 点击与滑动(写操作)
| 工具 | 参数 | 说明 |
|---|---|---|
de_tap_text |
serial, text(≤100 字符) |
推荐:按屏幕可见文字点击。原生控件走 UI 树,WebView/图片文字自动 OCR 兜底;找不到 → text_not_found |
de_tap_element |
serial, by(text/id/desc/text_contains/desc_contains), value, index(1) |
按元素属性点击,无需坐标;多命中用 index |
de_tap |
serial, x, y |
坐标点击(截图坐标系 + 自动吸附)。纯图形目标才用 |
de_swipe |
serial, x1,y1,x2,y2, duration(0.2) |
滑动(长按 = 同点起止 + duration≥1) |
de_press_key |
serial, key |
back / home / recent / menu / power / volume_up / volume_down / enter / delete / search / camera |
4.4 输入与剪贴板(写操作)
| 工具 | 参数 | 说明 |
|---|---|---|
de_type_text |
serial, text |
向当前输入框输入(支持中文,直设 EditText,不依赖剪贴板) |
de_set_clipboard |
serial, text |
写入设备剪贴板(ClipInject 通道 + 读回验证) |
4.5 App 管理(写操作,走 adb 直连不建 u2 会话)
| 工具 | 参数 | 说明 |
|---|---|---|
de_open_app |
serial, package |
adb monkey 直启(最快路径,无需知道 activity) |
de_stop_app |
serial, package |
am force-stop |
4.6 亮屏 / 熄屏(写操作)
| 工具 | 参数 | 说明 |
|---|---|---|
de_wake |
serial |
亮屏并解锁(熄屏时先调它再截图) |
de_sleep |
serial |
熄屏(会中断正在运行的任务,慎用) |
后两组分别经
direct_ops(adb)与平台POST /api/device/screen_all实现,不在 MCP 进程内建 u2 会话。
4.7 写操作门控(三连)
所有写工具在执行前依次检查:
_check_write()——MCP_ALLOW_WRITE=1?否则write_disabled_check_serial()—— serial 非空 / 在白名单内?否则device_not_allowed_ensure_device_free()—— 设备worker_status不是running/connecting?否则device_busy(AI 不与任务抢设备)
设备状态查询有 5 秒缓存;查询本身异常时放行(不阻塞),由平台侧兜底。
5. 推荐使用模式
de_list_devices确认目标设备在线de_screenshot看图理解当前界面(图像会在下一回合送达模型)- 点击定位优先级:
- 目标有可见文字 →
de_tap_text(一次调用完成"找到并点击",最可靠) - 文字有歧义/多候选 →
de_ui_tree确认后用de_tap_element(可用text_contains模糊匹配) - 纯图形目标 →
de_tap坐标(无需精算,会自动吸附)
- 目标有可见文字 →
- 输入文字:先点中输入框,再
de_type_text - 每步验证:关键操作后再
de_screenshot——界面变了 = 成功;没变 = 未命中 → 换de_tap_text/de_tap_element重新定位,不要重复点同一坐标 - 收尾:用中文总结做了什么、当前状态、注意事项
- 效率:界面未变时不重复截图/点击;连续 6 步无进展就停止并总结
6. 安全与审计
| 机制 | 说明 |
|---|---|
| 写门控 | MCP_ALLOW_WRITE=0(默认)时所有写操作被拒,只读可用 |
| 任务互斥 | 写操作前检查设备是否在跑任务,忙则 device_busy |
| 平台会话 | platform_client 内部登录(MCP_PLATFORM_USER/PASS)、会话失效自动重登、POST 自动带 X-CSRF-Token;平台凭据不暴露给客户端 |
| 白名单 | MCP_ALLOWED_SERIALS 非空时限制可操作 serial |
| 审计 | 每次调用(含只读)写一行 JSON 到 MCP_AUDIT_FILE:{ts, tool, serial, args 摘要, result 摘要} |
| 红线 | 任何工具都不执行 adb kill-server / adb disconnect |
| 端点鉴权 | ⚠️ MCP HTTP 端点自身没有 token/账号校验,只做出站登录 → 必须靠网络隔离(同机/内网),不要直接暴露公网 |
7. 客户端接入示例
import asyncio
from fastmcp import Client
async def main():
async with Client("http://192.168.20.220:8033/mcp", timeout=30) as c:
devs = await c.call_tool("de_list_devices", {})
serial = devs.data["data"][0]["serial"] # 第一台设备
shot = await c.call_tool("de_screenshot", {"serial": serial})
img = shot.data["data"]["image"] # base64 JPEG(给多模态模型看)
await c.call_tool("de_tap_text", {"serial": serial, "text": "搜索"})
asyncio.run(main())
给外部数字员工的使用约定与红线,另见 staffdeck/KNOWLEDGE_BASE.md。
8. AI 控制台(内置 Agent)
Web 的「AI 控制台」Tab 内建 Agent(mcp_agent/,OpenAI 兼容模型)用的就是本 Server 的同一批工具:
- 流式输出 + 每步 MCP 调用实时展示(含截图缩略)
- 多轮会话(同会话保留上下文)
- 自进化记忆:经验库(任务配方)+ 动作库(命名动作),相似任务自动注入参考
- 模型与 Key 在控制台右上角 ⚙ 配置(存平台
app_meta)
依赖提示:AI 控制台依赖本 MCP Server(默认
http://127.0.0.1:8033/mcp)。未启动时平台会把 SDK 的含糊报错映射成明确文案「MCP server(8033) 不可达 …」。
维护约定:新增 / 修改 / 删除任何一个 MCP 工具或相关配置,必须同步更新本文与 MCP_DESIGN.md(doc 同步红线)。