feat: MCP 工具补全(9→18)——de_open_app(adb monkey 直启,省 token)/de_stop_app/de_foreground_app/de_type_text(中文直接输入)/de_set_clipboard/de_sleep/de_ocr(屏幕文字识别)/de_list_apps/de_list_tasks;direct_ops 直连封装(adb/u2/OCR/剪贴板轻量通道)

This commit is contained in:
2026-09-04 13:48:55 +08:00
parent 1560a13d86
commit fab7e2b11a
3 changed files with 298 additions and 0 deletions
+150
View File
@@ -273,6 +273,156 @@ def de_press_key(serial: str, key: str) -> dict:
return _ok({"action": "press_key", "serial": serial, "key": key})
# ================== L2 扩展工具(轻量通道:adb/u2 直连,省 token) ==================
@mcp.tool()
def de_open_app(serial: str, package: str) -> dict:
"""打开 App(adb monkey 直启,最快路径)。package 为应用包名,如 com.ss.android.ugc.aweme。"""
try:
_check_write()
serial = _check_serial(serial)
if not package:
raise PlatformError("invalid_param", "缺少包名")
from mcp_server import direct_ops
direct_ops.open_app(serial, package)
except PlatformError as e:
return _err(e)
except Exception as e:
return _err(PlatformError("platform_unavailable", f"打开失败: {str(e)[:100]}"))
audit.audit("de_open_app", serial, package, "ok")
return _ok({"action": "open_app", "package": package})
@mcp.tool()
def de_stop_app(serial: str, package: str) -> dict:
"""强制停止 App(am force-stop)。"""
try:
_check_write()
serial = _check_serial(serial)
if not package:
raise PlatformError("invalid_param", "缺少包名")
from mcp_server import direct_ops
direct_ops.stop_app(serial, package)
except PlatformError as e:
return _err(e)
except Exception as e:
return _err(PlatformError("platform_unavailable", f"停止失败: {str(e)[:100]}"))
audit.audit("de_stop_app", serial, package, "ok")
return _ok({"action": "stop_app", "package": package})
@mcp.tool()
def de_foreground_app(serial: str) -> dict:
"""查询设备当前前台运行的 App 包名(轻量 dumpsys,不打扰设备)。"""
try:
serial = _check_serial(serial)
from mcp_server import direct_ops
pkg = direct_ops.foreground_app(serial)
except PlatformError as e:
return _err(e)
except Exception as e:
return _err(PlatformError("platform_unavailable", f"查询失败: {str(e)[:100]}"))
audit.audit("de_foreground_app", serial, "", pkg or "未知")
return _ok({"foreground_app": pkg or ""})
@mcp.tool()
def de_type_text(serial: str, text: str) -> dict:
"""向设备当前输入框输入文字(支持中文,直接 set_text 不依赖剪贴板)。"""
try:
_check_write()
serial = _check_serial(serial)
if not text:
raise PlatformError("invalid_param", "内容为空")
from mcp_server import direct_ops
ok, msg = direct_ops.type_text(serial, text)
if not ok:
raise PlatformError("device_offline", msg)
except PlatformError as e:
return _err(e)
except Exception as e:
return _err(PlatformError("platform_unavailable", f"输入失败: {str(e)[:100]}"))
audit.audit("de_type_text", serial, f"{text[:30]}...", "ok")
return _ok({"action": "type_text"})
@mcp.tool()
def de_set_clipboard(serial: str, text: str) -> dict:
"""写入设备剪贴板(ClipInject 通道,读回验证)。"""
try:
_check_write()
serial = _check_serial(serial)
if not text:
raise PlatformError("invalid_param", "内容为空")
from mcp_server import direct_ops
ok, msg = direct_ops.set_clipboard(serial, text)
if not ok:
raise PlatformError("device_offline", msg)
except PlatformError as e:
return _err(e)
except Exception as e:
return _err(PlatformError("platform_unavailable", f"注入失败: {str(e)[:100]}"))
audit.audit("de_set_clipboard", serial, f"{text[:30]}...", "ok")
return _ok({"action": "set_clipboard"})
@mcp.tool()
def de_sleep(serial: str) -> dict:
"""熄灭设备屏幕(运行中任务会中断,慎用)。"""
try:
_check_write()
serial = _check_serial(serial)
platform().sleep(serial)
except PlatformError as e:
return _err(e)
audit.audit("de_sleep", serial, "", "ok")
return _ok({"action": "sleep"})
@mcp.tool()
def de_ocr(serial: str) -> dict:
"""OCR 识别当前屏幕文字(图片/画布/WebView 里 UI 树没有的文字也能识别)。
返回 [{text, score}]——搜屏幕关键词后可配合 de_tap_element/de_tap 操作。
"""
try:
serial = _check_serial(serial)
from mcp_server import direct_ops
results = direct_ops.ocr(serial)
except PlatformError as e:
return _err(e)
except Exception as e:
return _err(PlatformError("platform_unavailable", f"OCR 失败: {str(e)[:100]}"))
slim = [{"text": r["text"], "score": round(r["score"], 2)} for r in results]
audit.audit("de_ocr", serial, "", f"{len(slim)} 条")
return _ok({"count": len(slim), "texts": slim[:100]})
@mcp.tool()
def de_list_apps(serial: str, keyword: str = "") -> dict:
"""列出设备第三方已装应用包名(可关键词过滤,如 keyword='douyin' 找抖音)。"""
try:
serial = _check_serial(serial)
from mcp_server import direct_ops
pkgs = direct_ops.list_apps(serial, keyword)
except PlatformError as e:
return _err(e)
except Exception as e:
return _err(PlatformError("platform_unavailable", f"查询失败: {str(e)[:100]}"))
audit.audit("de_list_apps", serial, keyword or "", f"{len(pkgs)} 个")
return _ok({"count": len(pkgs), "packages": pkgs[:200]})
@mcp.tool()
def de_list_tasks() -> dict:
"""列出平台任务计划(名称/启用状态/调度),供了解可自动化的任务。"""
try:
tasks = platform().list_tasks()
except PlatformError as e:
return _err(e)
audit.audit("de_list_tasks", "", "", f"{len(tasks)} 个")
return _ok({"tasks": tasks})
def main():
_log.info("MCP mobile-control 启动: allow_write=%s port=%s",
S.allow_write, S.http_port)