dsh-cua
# dsh-cua
[](https://github.com/Hutusion/dsh-cua/actions/workflows/ci.yml)
[](https://pypi.org/project/dsh-cua/)
<!-- mcp-name: io.github.Hutusion/dsh-cua -->
Windows 电脑操控的 MCP 服务器 + agent 技能:**无障碍元素动作优先,截图只是兜底**;
带跨会话仲裁器——多个 agent 共享一台电脑时自动串行化,并在你正在使用电脑时主动让行。
本仓库只含 MCP 服务器与技能本身,**对任何 stdio MCP 客户端保持中立**
(dsh / Claude Code / Codex / Cursor / Cline / ZCode …)—— 不依赖 dsh 才能用。
**平台语义(0.3.1 起)**:工具只在 Windows 上可用——它们驱动 user32/kernel32 与 UI Automation。
但**包本身在别的平台也能导入、服务器也能启动**,并照常回应 `tools/list`,所以任何客户端
或目录抓取器都能枚举到这 19 个工具及其完整 schema;真去调用某个工具时得到的是一句明确的
「需要 Windows」错误,而不是进程根本起不来。0.3.0 在导入时就抛错,导致这类抓取完全看不到它
(`tests/linux-handshake.py` 是这条性质的回归测试,CI 在 `ubuntu-latest` 上跑它)。
## 它是什么
一个 stdio MCP 服务器,暴露 19 个工具:
- **观测**(只读,随时可调):`skyshot`(把窗口读成带 diff 的紧凑文本树,比截图小三个数量级)、
`element_at_point`、`read_element`、`find_elements`、`capture_window`(DPI 感知 + 客户区裁剪)、
`list_windows` / `find_window` / `get_window_rect`、`list_displays`、`cursor_position`、
`clipboard_read`、`coexistence_status`
- **元素动作**(软门:跨 agent 串行,无物理输入注入):`element_action` /
`element_action_at`——press / set_value / select / toggle / expand / collapse /
scroll_into_view / focus,直接作用于 UIA 元素,**不抢焦点、不关心 z 序**
- **物理输入**(硬门:跨 agent 串行 + 人机让行):`click_at`(先元素路径后裸事件)、
`send_keys`、`type_text`(PostMessage 定向)、`clipboard_write`、`open_application`
每个动作返回**回执**而非自述成功:`action_sent` / `effect_verified` / `foreground_changed` /
`user-active` / `arbiter-busy`。「调用被接受」和「效果发生」是两件事——工具替 agent 分清。
## 与同类有何不同
Windows 侧已经有好几个成熟的开源实现。dsh-cua 的差异集中在**「和人类共用一台机器」**这一点上:
| | dsh-cua | [cua-driver](https://github.com/trycua/cua) | [ahk-mcp](https://github.com/anomalous3/ahk-mcp) | [lean-computer-use-mcp](https://github.com/Kvxw1105/lean-computer-use-mcp) |
|---|---|---|---|---|
| 元素动作走 UIA 模式(不抢焦点、不关心 z 序) | ✅ | ✅(ax 档) | ❌ 只有坐标点击 | 经 cua-driver |
| **检测到人正在输入 → 拒绝** | ✅ `user-active` | ❌ | ❌ | ❌ |
| 跨 agent 串行化(多进程) | ✅ named mutex | ❌ | ❌ | ❌ |
| 逐动作效果断言 | ✅ `effect_verified` 三态 | 报告交付档位 | ❌ | ❌ 仅 `state_changed` 启发式 |
| 抢前台副作用度量 | ✅ `foreground_changed` | ❌ | ❌ | ❌ |
| 工具数 | 19 | 59 | 15 | 6 |
**关键区别是两件常被混为一谈的事:**
- **「不抢焦点」是机制保证** —— 走 UIA 模式或定向 `PostMessage`,物理上不碰光标和键盘焦点。
cua-driver 有(ax 档);**ahk-mcp 其实没有** —— 它只有坐标点击,每次都移动真实光标。
- **「你一动就让路」是时间保证** —— 用 `GetLastInputInfo` 读人类最后一次输入的年龄,
检测到你正在用就等待,超时则**拒绝**(`user-active`)而不是硬上。**上表另外三个实现里都没有这一条。**
`effect_verified` 同样是同类没有的:它把「调用被接受」和「效果发生」分开,给出三态
(`true` 变化符合预期 / `false` 接受了但没变并降级为失败 / `null` 无可比状态即未确认)。
同类的替代做法是动作后重新观察一次,把判断留给模型。
**dsh-cua 不做的事**(先说清楚,避免误解):没有像素/视觉接地——树表达不了的界面(canvas、
游戏、远程桌面)用不了;没有录制回放;没有隔离沙箱。这些各有更合适的方案。
## 安装
需要 **Windows x64 + 交互式桌面会话 + Python ≥3.10** 才能真正操控桌面。
(包在 Linux/macOS 上同样可以安装与启动,`tools/list` 正常返回,只是调用工具时会明确
报「需要 Windows」——见上文"平台语义"。)
```bash
# 方式一:uvx 零安装(推荐)
uvx dsh-cua # 直接运行 stdio MCP server
# 方式二:pip
pip install dsh-cua
# 方式三:从源码
pip install git+https://github.com/Hutusion/dsh-cua.git
```
三种方式装完后,**用 `python -m dsh_cua` 起服务**:
```bash
python -m dsh_cua # 不依赖 PATH 上的任何可执行文件
```
> **为什么不写 `dsh-cua-server`**:pip 会把 console script 装进解释器的 `Scripts` 目录,
> 而**那个目录不一定在 PATH 上** —— 实测 stock python.org 3.12 的 User 与 Machine PATH
> 都不含它,于是 `pip install dsh-cua` 成功、`dsh-cua-server` 却报 command not found。
> `python -m` 不需要任何 PATH 条目。console script 仍然提供,PATH 里有它时可用。
>
> 方式一/二现在都可用:包已发布在 PyPI(<https://pypi.org/project/dsh-cua/>)。
> 若哪一天 `uvx`/`pip` 报 404,用方式三 —— 它总是可用。
## 接线
任何 MCP 客户端,把 server 命名为 **`win32`**(skill 的工具名约定是 `mcp__win32__*`)。
**`python -m`(不依赖 PATH,推荐)**:
```json
{ "mcpServers": { "win32": { "command": "python", "args": ["-m", "dsh_cua"] } } }
```
**`uvx`(PyPI 发布后)**:
```json
{ "mcpServers": { "win32": { "command": "uvx", "args": ["dsh-cua"] } } }
```
更多形状见 [`examples/`](examples/):Claude Code / 通用客户端 / dsh 的 cordis.patch.yml 片段 /
想让模型看懂截图时所需的路由模态声明(`tr-route-settings.yml`)。
## 技能(可选但强烈建议)
[`skill/computer-use/SKILL.md`](skill/computer-use/SKILL.md) 是配套的使用教条:观察→定位→动作→复核
的循环、回执语义、重试安全、与人类共存的纪律。没有它模型也能用工具,但有了它模型会**自己选对
路径**——实测差别很大。把它复制进你的技能目录即可:
```bash
# Claude Code / 通用 agents
cp -r skill/computer-use ~/.agents/skills/
# dsh
cp -r skill/computer-use ~/.dsh/skills/
```
## 安全模型
| 级别 | 覆盖操作 | 门 |
|---|---|---|
| 只读 | 观测类 12 个工具 | 不进门,随时可调 |
| 软门 | 元素动作、PostMessage 打字、剪贴板写、启动应用 | 跨 agent 互斥锁(named mutex,多进程自动串行) |
| 硬门 | 裸点击、全局热键 | 互斥锁 + `GetLastInputInfo` 人机让行:用户最近有输入就等待,超时则拒绝 `user-active` 而非抢光标 |
诚实边界:让行是合作协议不是硬保证(注入前 150ms 紧检查已尽量收窄窗口);
个别应用连 `set_value` 都会自激活(回执会如实报告 `foreground_changed`);
同一窗口的「双人操作」没有技术解,别和 agent 同时操作同一个窗口。
## 测试
```bash
python tests/verify-coexistence.py # 25 项:零输入证明 / 跨进程互斥 / 合成人机争用 / 杀开关
python tests/verify-p0-fixes.py # 0.2.0 修掉的三个 P0:每项在修复前必失败
```
测试不需要真人配合——「用户输入」由一次真实 1px 光标移动合成,跑完还原。
**测试需要真实交互式桌面会话**(部分检查要创建窗口并用 UIA 寻址),所以**不能在 GitHub 托管的
runner 上跑**。CI 覆盖的是不需要桌面的那部分:打包安装、模块导入、diff 索引与树行转义的回归、
仲裁器的判定逻辑 —— 见 [`.github/workflows/ci.yml`](.github/workflows/ci.yml)。
```bash
python tests/ci-desktop-free.py # 上面这些的本地等价物,不需要桌面
```
## License
MIT
TDQS
Scored across 19 tools
Most tools have clearly distinct purposes (list_windows vs find_window vs get_window_rect each serve different needs), and the description-heavy element discovery trio (element_at_point, skyshot, find_elements) is distinguished by use case. The main ambiguity is element_action vs element_action_at — the same action vocabulary addressed by different handles — and the three 'reach an element' paths could cause misselection, though the descriptions mitigate this.
Action tools follow a solid verb_noun pattern (list_windows, capture_window, send_keys, open_application), but several query/diagnostic tools break it by dropping the verb: cursor_position, skyshot, coexistence_status, and element_at_point are noun phrases or brand names. The uniform tool_ prefix and readable names keep it navigable, but the mixed convention is noticeable.
At 19 tools this sits in the heavy range, and the count is inflated by redundant addressing paths (element_action vs element_action_at, and three separate element-discovery tools). However, the server genuinely spans several sub-domains — window management, element access, input injection, clipboard, display enumeration, and coexistence policy — so the breadth is defensible.
The surface covers the full automation loop well: discover (find_window, find_elements, element_at_point, skyshot), read (read_element, capture_window, clipboard_read), and act (click_at, type_text, send_keys, element_action, element_action_at, open_application). Notable gaps are window manipulation (no close/resize/move/activate tool — only geometry reads) and no drag/gesture operation, but agents can work around these.