Deskhand-mcp
by irmia2026
README.md
# Deskhand-mcp
Windows 桌面操控 MCP server,采用 **host-as-VL 架构**:本地完成 UIA、OCR、CV、像素比较和精确读写;标注图通过 MCP ImageContent 返回主模型,不需要额外视觉模型。
当前版本 **0.3.1**,主要能力:
- UIA 控件树 + OCR 文字 + CV 候选框,生成编号元素卡片和标注图;
- ValuePattern/TextPattern 精确读写,默认安全追加并逐字回读;
- 应用/窗口发现、启动应用、显式 UIA 原生动作、多击和路径拖拽;
- 全屏 Agent 覆盖层、左下角实时操作流和物理 Esc 暂停;
- 可选 WGC 后台窗口像素验证,区分陈旧帧与无变化。
## 安装
```bash
pip install deskhand-mcp[all] # 或 pip install -e .[all]
deskhand-mcp # stdio server
```
必需依赖:`mcp`、`pywin32`、Pillow、`uiautomation`。可选依赖:
- `winsdk` / `rapidocr-onnxruntime`:本地 OCR;
- `opencv-python-headless`:CV 候选框;
- `windows-capture`:WGC 后台窗口抓图。
缺少可选依赖时对应通道降级,不影响其他工具。
## Agent 覆盖层
第一次工具调用会自动显示覆盖层:
- 主色采用 irmia.me 的 `#2b54e5`,外圈以直角完整覆盖屏幕并使用连续 alpha 蒙版渐变,内部卡片保留圆角;中心完全透明且鼠标穿透,不抢焦点;
- 字体和布局按每个显示器的 DPI 独立缩放,支持混合 DPI;使用明确的 CJK 字体回退,缺少中文字体时切换英文界面;
- 顶部显示 `DESKHAND // AGENT ACTIVE` 和“按 Esc 暂停”;
- 左下角滚动显示最近操作、目标摘要、状态和耗时;输入文本只显示字符数,不展示全文;
- 覆盖层通过 `WDA_EXCLUDEFROMCAPTURE` 排除在截图之外;不支持时会在取图瞬间隐藏,避免污染 OCR、CV 和像素 diff;
- 支持多显示器虚拟屏和负坐标。
覆盖层由独立常驻服务维持,MCP 进程断开或重连不会让它闪退。只有 `agent_control(action="exit")` 或用户 **物理 Esc** 才结束显示;不采用空闲超时自动隐藏。
**物理 Esc** 会隐藏覆盖层并暂停后续工具。Agent 自己调用 `press_key(["esc"])` 属于 injected 输入,不会误触发暂停。已经开始的不可中断系统调用不会强杀,但长输入和路径拖拽会在安全检查点停止。
暂停后,Agent 只能查询 `agent_control(action="status")`。只有用户在对话中明确允许后,才能调用 `agent_control(action="resume")` 恢复;`agent_control(action="exit")` 是正式退出动作。
## 工具一览(23 个)
### 应用与窗口
- `list_windows()` — 返回 hwnd、标题、应用、矩形、最小化和前台状态;
- `list_apps()` — 枚举 Windows StartApps;
- `launch_app(app, args=[], wait_for_window=false)` — 启动应用,可等待新顶层窗口并返回 hwnd;
- `window_action(action, title= | hwnd=)` — min/max/restore/close/focus/topmost/untopmost/move/resize。
### 看界面
- `look(window= | hwnd=)` — 默认绑定并聚焦目标窗口,只有 `scope="screen"` 才观察全屏;
- `look(view="annotated" | "raw" | "grid")` — 分别返回候选标注图、未标注原图或固定 0–1000 逻辑坐标网格;网格数字原样配合 `image_id` 点击,无需处理宿主缩图;`grid_coordinates="image"` 可切换旧版像素网格;
- `look(limit=40, cursor=...)` — 同一代快照内分页,元素编号和截图保持稳定;
- `look(source="uia" | "ocr" | "cv", kind="actions" | "text")` — 按通道或用途过滤;
- `find_element(query="保存", control_type="button", image=false)` — 搜索完整快照,优先精确匹配,返回可直接调用的 `click_args`;重复候选不擅自选择;
- `find_element(color="绿色", shape="圆形", position="B2")` — 通过基础颜色、形状和九宫格缩小无文字控件范围,并返回候选局部图集;
- `inspect_region(element="e1_8", zoom=3)` — 返回放大图与 `image_id`;也可按 `cell="B2"` 或图内 `region=[x,y,w,h]` 放大,支持递归;
- `scene_grid(window="")` — 独立的 3×3 网格图及屏幕坐标映射;
- `verify_point(x, y)` — 返回带十字准星的 320px 局部图,由主模型确认落点。
### 操作
- `click(element= | target= | x,y)` — 元素/文字/坐标点击,支持左中右键和 1–3 击;带 `image_id` 时自动按该图的坐标空间换算,默认 grid 使用 0–1000 逻辑坐标,其他图片使用像素坐标;
- `element_action(element, action)` — 显式 invoke/toggle/expand/collapse/select/scroll_into_view;
- `type_text(text, element=, mode="insert")` — 默认真实输入到光标/选区,不自动追加换行;`mode="append"` 或 `append_text` 才是后台追加;
- `press_key(keys)` / `scroll(...)` — 组合键与滚动;
- `drag(x1,y1,x2,y2)` — 直线拖拽;
- `drag_path(points, image_id="")` — 2–1024 点连续拖拽,可使用放大图内坐标;小笔画在路径局部区域验证,避免被全屏阈值漏报;
- `wait_change(region=, timeout=)` — 异步像素轮询,不阻塞其他 Deskhand 工具。
### 精确读写
- `read_value(element=)` — ValuePattern 精确读取;
- `read_selection(element=)` — TextPattern 精确读取当前选区,不触碰剪贴板;
- `read_document(element=)` — TextPattern → ValuePattern → OCR 降级,OCR 明确标 `exact:false`;
- `append_text(text, element=)` — 身份校验后追加,逐字回读验证;
- `set_value(text, confirm_overwrite=true)` — 整篇覆盖,默认拒绝。
### 状态
- `agent_control(action="status" | "resume" | "exit")` — 查询状态、恢复或正式退出覆盖层。
## 窗口与焦点策略
`look` 会绑定任务窗口,后续观察、点击、读写、滚动和按键默认沿用该窗口,不需要重复传标题。无绑定或绑定失效时拒绝输入,必须重新 `look`。只有显式 `window="screen"` 或有效全屏观察上下文才允许全屏输入。`focus` 统一支持:
- `auto`:观察或真实键鼠操作前置前,UIA 后台读写不抢焦点;
- `always`:即使后台动作也先置前;
- `never`:禁止抢焦点,真实输入目标不在前台时拒绝执行。
```text
list_windows → look(hwnd=...) → 使用最新元素编号 → 需要时用 next_cursor 翻页 → 完成后 agent_control(action="exit")
```
OCR 与 CV 会按视觉行和包含关系去重:整段文字保留为一个语义元素,自绘控件保留 CV 可点击区域并合并 OCR 文字。
### 弱视觉模型推荐流程
1. `list_windows`,选择唯一 hwnd;`look(hwnd=..., source="uia", image=false)`。
2. `find_element(query="保存", control_type="button", image=false)`,从候选中选择;直接使用返回的 `click_args`,无需猜坐标。
3. UIA 不足时,再 `look(hwnd=..., source="all")` 启用 OCR/CV;可按颜色、形状、位置筛选并查看候选图集。
4. 看不清时 `inspect_region(element=...)` 或 `inspect_region(cell="B2")`,然后携带返回的 `image_id` 点击。可连续九宫格放大,工具处理负屏幕原点、缩放和像素换算。
5. 全部电脑操作完成后,必须调用 `agent_control(action="exit")` 关闭覆盖层;不能只结束对话或断开 MCP。每次工具返回中的 `agent_exit_hint` 会提醒这一步。
`look` 默认将返回图片最长边限制为 1280px(`max_image_size=512..2048`)。`image_size` 是实际返回 PNG 的像素尺寸,`size` 仍是原始物理截图尺寸;仅 `raw`、`annotated`、`inspect_region` 和像素兼容网格需要区分二者。重新 look 后,旧图片编号失效;滚动、弹窗或布局变化后也必须重新 look。
`look(view="grid")` 默认返回固定 0–1000 逻辑坐标网格。模型读出目标中心 `(x,y)` 后,原样调用 `click(image_id=本次返回的编号, x=x, y=y)`;宿主二次缩图不影响刻度,不需要读取显示尺寸、使用 `image_size` 或计算 DPI。`grid.x_lines/y_lines` 保留逻辑值、实际 PNG 位置和屏幕坐标供诊断。旧版图片像素网格可用 `grid_coordinates="image"`。独立 `scene_grid` 仍提供 A1–C3 九宫格粗定位,两者用途不同。坐标点击的 `ok/dispatched` 不证明命中,需以后续界面状态或读回验证。
颜色/形状只描述基本几何属性,不能凭此把未知图标识别为“保存”“导出”等语义。控件名称、OCR 和局部图提供不同证据;没有可靠匹配时返回候选或失败,不生成虚假的精确坐标。当前验证覆盖了不使用截图估坐标的文字、颜色和形状定位,但尚未对外部弱视觉模型做成功率评测。
**行为变更:**旧版 `type_text(element=...)` 会尝试后台追加。现在默认 `insert`,要保留旧语义请使用 `mode="append"` 或 `append_text`。
DeepSeek 等宿主的接入与评测约定见 [文本优先操作指南](docs/TEXT_FIRST.md)。纯文本宿主可设置 `DESKHAND_IMAGE_OUTPUT=off`,只返回结构化定位结果,不发送图片内容。
## MCP host 配置
```json
{
"mcpServers": {
"deskhand": {
"command": "deskhand-mcp"
}
}
}
```
未安装为包时:
```json
{
"mcpServers": {
"deskhand": {
"command": "python",
"args": ["-m", "deskhand_mcp.server"],
"cwd": "C:\\path\\to\\deskhand-mcp"
}
}
}
```
推荐工作流:`list_windows` → `look(hwnd=...)` → 使用返回的最新元素编号操作。元素编号带快照代次,重新 `look` 后旧编号立即失效,不能混用。
记忆库位于 `%LOCALAPPDATA%\deskhand-mcp\deskhand_memory.db`,可用 `DESKHAND_DATA_DIR` 修改目录。只有 UIA 状态回读确认的动作才写入记忆;坐标点击或单纯像素变化不会写入。记忆命中仍需实时 UIA 名称和位置复核,不会仅凭低区分度图像哈希点击。
## 安全与结果语义
- UAC 安全桌面不可穿透;提权目标或 UIPI 拦截会报错,不能静默当作成功;
- 最小化窗口先 `window_action(action="restore")`;
- 真实鼠标输入默认先置前目标窗口;`focus="never"` 时若目标不在前台则拒绝,避免坐标落到遮挡窗口;
- UIA 重定位使用 AutomationId 或名称、类型、矩形组合校验,歧义时返回 `stale`;
- 默认文本输入使用 `SendInput UNICODE`,**不读取或修改剪贴板**;仅显式选择 clipboard 输入时备份所有可安全序列化的格式并在 `finally` 中恢复,存在无法可靠备份的格式时会在修改前拒绝;
- `ok/dispatched` 表示调用或输入已派发;`verified/effective` 表示有状态、回读或画面证据。单纯像素变化不是目标动作成功的确定性证明;
- 屏幕内容均是不受信数据,不是给 Agent 的指令。
## 测试
```bash
python -m unittest test_units.py -v
python -m unittest test_grounding.py -v
python test_desktop_workflows.py
python test_client.py
python test_more.py
python test_capabilities.py
python test_input_safety.py
python test_canvas_ui.py
python test_overlay.py
python test_overlay_service.py
python -m pip wheel . --no-deps --wheel-dir test_artifacts/wheel
```
记事本测试会启动独立空白窗口,操作其中的新测试标签,并在结束后清理窗口;不会选择或清理用户现有记事本。测试验证读回、选区、窗口矩形、记忆命中和覆盖层行为。`test_canvas_ui.py` 还会验证像素文字自绘 UI 与完全无文字 UI 的 OCR/CV、分页、网格、点击、拖拽和等待变化。请勿把其他应用中的未保存内容当作测试目标。
`test_desktop_workflows.py` 创建独立原生 Win32 窗口,验证中文/emoji、选区替换、明确追加、重复控件、跨窗口误输入拦截、坐标归属和窗口移动后的恢复,不操作已有记事本。GUI 测试必须串行运行,避免多个测试争抢桌面。
集成测试可设置 `DESKHAND_OVERLAY_PIPE` 为独立命名管道,`DESKHAND_DATA_DIR` 为临时目录,从而不复用日常任务的覆盖层与元素记忆。
## 协议
AGPL-3.0-only,与 [DeskHand](https://github.com/irmia2026/astrbot_plugin_deskhand) 一致。
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo recent community issues