From c078e282ed1ce91fc4d244c9284bcbae45873264 Mon Sep 17 00:00:00 2001 From: butubb <1422726308@qq.com> Date: Fri, 4 Sep 2026 15:46:14 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=96=B0=E5=A2=9E=20MCP=20=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E6=89=8B=E5=86=8C=EF=BC=88doc/MCP.md=EF=BC=89?= =?UTF-8?q?=E2=80=94=E2=80=9419=20=E4=B8=AA=20de=5F*=20=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E5=85=A8=E6=B8=85=E5=8D=95=EF=BC=88=E5=8F=82=E6=95=B0/?= =?UTF-8?q?=E7=94=A8=E9=80=94/=E6=8E=A8=E8=8D=90=E7=94=A8=E6=B3=95?= =?UTF-8?q?=EF=BC=89=E3=80=81=E5=9D=90=E6=A0=87=E7=A9=BA=E9=97=B4=E4=B8=8E?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=90=B8=E9=99=84=E7=BA=A6=E5=AE=9A=E3=80=81?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F=E8=A1=A8?= =?UTF-8?q?=E3=80=81=E6=8E=A8=E8=8D=90=E6=93=8D=E4=BD=9C=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=EF=BC=88=E6=96=87=E5=AD=97=E8=AF=AD=E4=B9=89=E7=82=B9=E5=87=BB?= =?UTF-8?q?=E4=BC=98=E5=85=88=E2=86=92=E5=85=83=E7=B4=A0=E2=86=92=E5=9D=90?= =?UTF-8?q?=E6=A0=87=E5=85=9C=E5=BA=95=EF=BC=89=E3=80=81=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E4=B8=8E=E5=AE=A1=E8=AE=A1=E8=AF=B4=E6=98=8E=E3=80=81fastmcp?= =?UTF-8?q?=20=E5=AE=A2=E6=88=B7=E7=AB=AF=E7=A4=BA=E4=BE=8B=EF=BC=9B?= =?UTF-8?q?=E4=B8=8E=20MCP=5FDESIGN.md=20=E8=AE=BE=E8=AE=A1=E7=A8=BF?= =?UTF-8?q?=E4=BA=92=E9=93=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/MCP.md | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 doc/MCP.md diff --git a/doc/MCP.md b/doc/MCP.md new file mode 100644 index 0000000..a44fde7 --- /dev/null +++ b/doc/MCP.md @@ -0,0 +1,147 @@ +# MCP 手机控制 Server(`mcp_server/`) + +多模态 AI(DeepSeek/Claude 等)通过 [MCP(Model Context Protocol)](https://modelcontextprotocol.io) 实时操作 Android 手机的统一出口。AI 控制台(`mcp_agent/`)与外部 MCP 客户端都经它控制设备池中的手机。 + +> 本文档 = 使用手册(工具清单/用法)。架构与演进设计见 [MCP_DESIGN.md](MCP_DESIGN.md)。 + +## 架构 + +``` +AI 控制台 / 外部 MCP 客户端 + │ Streamable HTTP + ▼ +MCP Server (:8033, mcp_server/mcp_server.py) ← 19 个 de_* 工具 + │ 平台 HTTP API(登录 + CSRF) + ▼ +auto_control 平台 (:18050) ← 设备池/任务/看屏 + │ adb / uiautomator2 / uiautodev / OCR + ▼ +Android 设备(IP:5555) +``` + +- 轻量通道直连(adb monkey 开 App、u2 输入、本地 OCR)不绕平台,省时省 token +- 坐标换算、UI 吸附、剪贴板注入等细节全部在 Server 层消化,模型只需给意图 + +## 运行与配置 + +服务跑在 220 的 `python-app` 容器内(`scripts/start.sh` 自动拉起,端口 8033)。 +生产访问方式:`http://192.168.20.220:8033/mcp`;本机调试 `python3 -m mcp_server.mcp_server`。 + +| 环境变量 | 默认 | 说明 | +|---|---|---| +| `MCP_PLATFORM_URL` | `http://127.0.0.1:18050` | 平台地址(Agent 与平台同机时用本机) | +| `MCP_PLATFORM_USER` / `MCP_PLATFORM_PASS` | `admin` / 空 | 平台登录账号 | +| `MCP_ALLOW_WRITE` | `0` | **写门控**:=1 才允许点击/输入/开关 App 等写操作(只读工具不受限) | +| `MCP_ALLOWED_SERIALS` | 空=不限 | 设备白名单(逗号分隔),非空时只允许列出的 serial | +| `MCP_HTTP_HOST` / `MCP_HTTP_PORT` | `0.0.0.0` / `8033` | 监听地址 | +| `MCP_SCREENSHOT_WIDTH` | `540` | 截图返回宽度上限(px),模型看到的图即该坐标系 | +| `MCP_JPEG_QUALITY` | `70` | 截图 JPEG 质量 | +| `MCP_AUDIT_FILE` | `/var/log/mcp/audit.log` | 审计日志(每次工具调用一行) | +| `MCP_PLATFORM_TIMEOUT` | `30` | 平台请求超时(秒) | + +生产(220 容器)已设 `MCP_ALLOW_WRITE=1`;`MCP_ALLOWED_SERIALS` 未设(= 平台设备池内可操作)。 + +## 坐标空间(重要约定) + +- `de_screenshot` 返回 ≤540px 宽的 JPEG(display 空间),并附 `native_size`(设备原生分辨率) +- `de_tap` / `de_swipe` 的坐标一律使用 **de_screenshot 返回图像的坐标系**,Server 按比例换算为原生坐标 +- **先截图、后点击**:Server 需要最近一次截图才能建立坐标空间(未截图就点击会报「请先执行 de_screenshot」) +- `de_tap` 带**自动吸附**:点击点若落在某个可点击元素内,实际会点该元素中心——坐标只需大致对准,偏十几像素也能点准;点空白处则按原坐标 +- 返回的 `snapped/label` 可核对吸附结果(snapped=true 表示已吸到元素,label 为该元素文案) + +## 工具清单(19 个) + +### 设备与状态 + +| 工具 | 用途 | +|---|---| +| `de_list_devices` | 列出可控制设备:serial / 型号 / 在线 / 任务状态 / 前台 App。**开局第一步** | +| `de_foreground_app(serial)` | 当前前台 App 包名(dumpsys,MIUI 焦点为空时自动兜底) | + +### 观察屏幕(感知) + +| 工具 | 用途 | +|---|---| +| `de_screenshot(serial)` | 截图并返回图像(≤540px JPEG)+ 尺寸 + 亮/熄屏状态。多模态模型直接看图 | +| `de_ui_tree(serial, limit=150)` | 当前界面元素树(text/id/desc/class/bounds)。**可点击元素排前**;limit 1-300 控制条数防 token 膨胀。用于确认界面上有什么 | +| `de_ocr(serial)` | OCR 识别当前屏幕文字(UI 树没有的图片/WebView 文字也能识别),返回 [{text, score}] | + +### 点击与滑动(操作) + +| 工具 | 用途 | +|---|---| +| `de_tap_text(serial, text)` | **按屏幕文字点击**(推荐):给一个屏幕上可见的文字(子串匹配)即找到并点其中心。原生控件走 UI 树,WebView/图片文字自动 OCR 兜底。找不到返回明确错误 | +| `de_tap_element(serial, by, value, index=1)` | 按元素点击:`by` = text / id / desc(精确)或 text_contains / desc_contains(模糊)。多命中用 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 | + +### 输入与剪贴板 + +| 工具 | 用途 | +|---|---| +| `de_type_text(serial, text)` | 向当前界面输入框输入文字(支持中文,直设 EditText 不依赖剪贴板/粘贴) | +| `de_set_clipboard(serial, text)` | 写入设备剪贴板(ClipInject 通道注入 + 读回验证) | +| `de_read_clipboard(serial)` | 读取设备当前剪贴板内容 | + +### App 管理(轻量 adb 直连,不建 u2 会话) + +| 工具 | 用途 | +|---|---| +| `de_open_app(serial, package)` | 打开 App(adb monkey 直启,无需知道 activity——最快的打开路径) | +| `de_stop_app(serial, package)` | 强制停止 App(am force-stop) | +| `de_list_apps(serial, keyword="")` | 列出第三方已装应用(pm list packages -3),keyword 可过滤(如 "douyin") | +| `de_sleep(serial)` | 熄屏(**运行中任务会中断,慎用**) | +| `de_wake(serial)` | 亮屏并解锁(熄屏时先调它再截图) | + +### 平台联动 + +| 工具 | 用途 | +|---|---| +| `de_list_tasks()` | 列出平台任务计划(名称/类型/启用/调度),了解已自动化的工作 | + +## 推荐使用模式(操作手机的正确姿势) + +1. **`de_list_devices`** 确认目标设备在线 +2. **`de_screenshot`** 看图理解当前界面(图像会在下一次模型回合送达) +3. **点击定位优先级**(从高到低): + - 目标有可见文字 → **`de_tap_text`**(一次调用完成「找到并点击」,最可靠) + - 文字有歧义/多候选 → `de_ui_tree` 确认后 `de_tap_element`(text_contains 模糊匹配) + - 纯图形目标 → `de_tap` 坐标(**无需精算**,Server 自动吸附到可点元素中心) +4. 输入文字:先点中输入框(de_tap_text / de_tap),再 `de_type_text` +5. 每次关键操作后 `de_screenshot` 验证:界面变化 = 成功;无变化 = 未命中,换 de_tap_text / de_tap_element 重新定位,**不要重复点同一坐标** +6. 完成/失败时用中文总结:做了什么、当前状态、注意事项 +7. 效率约束:界面未变不重复截图/点同位置;连续 6 步无进展停止并总结 + +## 安全与审计 + +- **写门控**:`MCP_ALLOW_WRITE=0`(默认)时点击/输入/开关 App 全部拒绝,只读工具可用 +- **设备白名单**:`MCP_ALLOWED_SERIALS` 非空时限制可操作的 serial +- **审计日志**:每次调用记录 `{ts, tool, serial, args, result}` 到 `MCP_AUDIT_FILE`(220 上 `/tmp/mcp_audit.log`) +- 操作对象限定平台设备池;`adb kill-server` / `adb disconnect` 属项目红线,任何工具不触碰 + +## 客户端接入示例 + +```python +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()) +``` + +## AI 控制台(内置 Agent) + +Web 端「AI 控制台」Tab 内建的 Agent(`mcp_agent/`,OpenAI 兼容协议:DeepSeek 等)也通过本 Server 的同一批工具跑任务: + +- 流式输出 + 每步 MCP 工具调用实时展示(含截图缩略) +- 多轮会话记忆(同会话上下文保留,新建会话清空) +- **自进化经验记忆**:一轮成功操作会被提炼成「配方」存入 `agent_experience` 表,下次相似任务自动注入参考(命中/写入均有 🧠 提示卡) +- 模型与 API Key 在 AI 控制台右上角 ⚙ 配置,存平台 `app_meta`