mcpField
D
dsh-cua - Windows Computer Use

dsh-cua - Windows Computer Use

本地服务
github.com/Hutusion生产力73 周前更新

关于 dsh-cua - Windows Computer Use

dsh-cua 是面向 Windows 的计算机操作 MCP 服务和 Agent 技能,可供 Claude Code、Codex、Cursor、Cline 等客户端使用。它优先通过可访问性元素执行操作,并协调多个 Agent;检测到有人使用电脑时会让出物理输入。

主要功能

  1. 01通过 stdio 提供 19 个计算机操作工具
  2. 02读取和操作 UI Automation 元素
  3. 03串行化多个 Agent 进程的操作
  4. 04有人使用时让出物理输入
  5. 05以回执区分操作发送与效果验证

适用场景

  1. 01让 Agent 检查和操作 Windows 应用
  2. 02协调共享一台 Windows 电脑的多个 Agent
  3. 03避免在人使用电脑时执行物理输入操作

MCP 相关

通过 stdio MCP 服务提供 19 个工具,名称均以 `tool_` 开头。任何 stdio MCP 客户端都能枚举工具;调用工具需要 Windows。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"hutusion-dsh-cua": {
"command": "uvx",
"args": [
"dsh-cua"
]
}
}
}
启动命令uvx dsh-cua

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypidsh-cua0.4.1

工具

19

收录的工具列表 · 更新于 2 周前

参数类型必填说明
filter_titlestring否

详细介绍

dsh-cua

ci PyPI Hutusion/dsh-cua MCP server LINUX DO

English · 中文

用于 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 个工具及其完整架构;实际调用工具时会返回清晰的“需要 Windows”错误,而不会导致进程无法启动。在 0.3.0 中,导入本身会引发错误,导致此类爬虫完全无法发现该服务(tests/linux-handshake.py 是针对这一属性的回归测试,CI 会在 ubuntu-latest 上运行该测试)。

内容简介

这是一个通过 stdio 通信的 MCP 服务,提供 19 个工具。每个工具名称都以 tool_ 为前缀,与 tools/list 返回的名称完全一致。

  • 观察(只读,可随时调用):tool_skyshot(以紧凑、可对比的文本树读取窗口——比截图小三个数量级)、tool_element_at_point、 tool_read_element、tool_find_elements、tool_capture_window(感知 DPI,并裁剪至客户区)、 tool_list_windows / tool_find_window / tool_get_window_rect、tool_list_displays、tool_cursor_position、 tool_clipboard_read、tool_coexistence_status
  • 元素操作(软门控:在各 Agent 之间串行执行,不注入物理输入): tool_element_action / tool_element_action_at — press / set_value / select / toggle / expand / collapse / scroll_into_view / focus,直接发送给 UIA 元素,因此 绝不会抢占焦点,也无需考虑 Z 序
  • 其他会修改状态的调用(同样采用软门控:使用互斥锁,但不会因人类操作而让出): tool_type_text(通过 PostMessage 定向输入,并提供 effect_verified)、tool_clipboard_write、 tool_open_application。它们 不会模拟物理输入,因此不会等待你停止操作——但写入剪贴板仍会覆盖你上次复制的内容,所以操作时请先说明。
  • 物理输入(硬门控:在各 Agent 之间串行执行,并会让出给人类):只有两类操作共用你的鼠标和键盘——tool_click_at 的 raw_event 路径和 tool_send_keys 的全局快捷键。门控会等到机器输入静默,然后返回 user-active,而不是和你争抢鼠标。tool_click_at 会先尝试其元素路径 (ax_press);该路径不会注入物理输入,因此只需获取互斥锁。回执中的 method 字段会说明实际执行的路径。

此处的“只读”是指不会执行修改操作,也不会模拟输入,因此有人正在使用机器时也可以安全调用。其中有两个工具会产生值得了解的副作用: tool_capture_window 会将截图写入磁盘(指定 save_path;若省略则使用临时文件),而 tool_skyshot 会更新服务端的差异基线,供下一张截图进行比对。

小型树结构并不能证明窗口是空的。tool_skyshot 和 tool_find_elements 会报告 minimized,因为最小化窗口可能显示的是“未显示任何内容”,而不是“里面什么都没有”——且隐藏内容的多少取决于应用,因此回执会报告状态,而不会猜测原因。此处的实测结果:在读取前最小化的 Edge 窗口会显示浏览器框架,完全没有页面内容(include_offscreen 也无法恢复内容);Explorer 最小化时,元素数量会从 40 个降至 8 个;Notepad 则不受影响。如果结果很重要,请先还原窗口,再重新读取。

每项操作都会返回回执,而不是仅报告自身是否成功:action_sent / effect_verified / foreground_changed / user-active / arbiter-busy。“调用已被接受”和“效果已发生”是两回事,工具会将两者区分开来,供 Agent 判断。

差异

目前已有几个成熟的 Windows 开源实现。dsh-cua 的差异集中在一点上:与人共用一台机器。

dsh-cuacua-driverahk-mcplean-computer-use-mcp
通过 UIA 模式执行元素操作(不抢焦点,z 顺序无关)✅✅(ax 模式)❌ 通过 UIA 读取,按坐标点击执行操作通过 cua-driver
最近有人类输入 → 拒绝操作✅ user-active❌❌❌
跨 Agent 序列化(多进程)✅ 命名互斥体❌❌❌
每项操作的效果断言✅ 三态 effect_verified报告交付等级❌❌ 仅有 state_changed 启发式判断
测量抢占前台的副作用✅ foreground_changed❌❌❌
工具数量1959156

关键区别在于两件经常被混为一谈的事:

  • “不抢焦点”是一项机制保证——通过 UIA 模式或定向的 PostMessage 实现,因此不会实际触碰光标和键盘焦点。cua-driver 支持此功能(ax 模式)。ahk-mcp 不支持,而且区别并不只是“是否使用 UIA”:它通过 UIA 读取(ahk_uia_tree / ahk_uia_find / ahk_uia_url),但没有 UIA 模式操作——根据其 README,它通过坐标点击或模拟按键执行操作,因此操作会移动真实光标。
  • “你一开始操作就让出控制权”是一项时序保证——它通过 GetLastInputInfo 读取人类上次输入距今的时间,检测到你正在使用机器时会等待,超时后则拒绝操作(user-active),而不是贸然介入。截至 2026-09-25,对表中另外三个代码库进行模式搜索后,未发现等效功能——这只是搜索所得证据,并非证明;而且仅涵盖输入时间检测:cua-driver 确实有其他类型的面向人类的防护措施(要求征得同意,以及检测抢占前台并进行恢复)。

effect_verified 也是替代方案所缺少的功能:它区分“调用已被接受”和“效果已发生”,并提供三种状态(true 表示已按预期发生变化;false 表示已接受但没有变化,降级为失败;null 表示没有可比较的状态,即未确认)。通常的替代做法是在操作后再观察一次,并由模型自行判断。

dsh-cua 不提供的功能(提前说明,以免产生误解):不提供自身的定位能力——服务不会分析像素,因此纯文本模型无法操作树无法表达的界面(画布、游戏、远程桌面)。使用支持视觉的模型时,端到端支持像素操作路径:capture_window 会返回图像以及经过验证的图像→屏幕映射(bounds、scale、dpi_verified),定位由模型完成。还不提供录制和回放,也不提供隔离沙箱。这些功能有更合适的工具。

常见问题

为什么不让 Agent 在第二个桌面或虚拟显示器上运行,这样就不会触碰我的桌面?

因为 Windows 没有可用于此用途的构建基础,而可行的移动端设计恰恰依赖于 Windows 缺少的功能。在 Android 上,应用可以创建 VirtualDisplay 并向其发送输入——输入事件带有显示器 ID,因此 Agent 的点击会发送到自己的屏幕,人类使用的触摸屏则不会受到影响。**Windows 按桌面而非显示器路由输入:**一个桌面只有一个输入队列和一个光标位置。

因此,显而易见的类似方案都行不通:

思路为什么无法隔离
添加虚拟显示器(间接显示驱动程序)只是多了一个画布,而不是多了一个光标——指针在所有显示器上仍只有一个位置
Windows 虚拟桌面(Win+Ctrl+D)同一会话中的视图切换:输入队列和光标都相同
第二个会话(RDP 或另一个用户)确实实现了隔离,但客户端 Windows 每个用户一次只能有一个交互会话——远程连接会锁定控制台,因此人就无法使用自己的屏幕,而这恰恰是整个方案的目的
隐藏的 Win32 桌面(CreateDesktop)Agent 会拥有自己的输入队列和光标,但它的窗口不可见,因此只能操作自己启动的实例,无法操作你正在看的程序。(根据窗口站/桌面模型推断;此处未进行测量。)

这张表讨论的是输入。第二台显示器究竟能带来什么,值得准确说明,因为答案并非“什么都没有”。在 Edge 上使用隔离的配置文件进行测量(tests/verify-visible-vs-foreground.py):对于仅仅显示出来的窗口,Chromium 会构建页面的无障碍树。查询前已最小化的窗口报告没有页面;同一窗口一经显示,就会报告一个有名称的页面——此时它不是活动窗口(GetGUIThreadInfo(hwndActive) 为 false),且被完全遮挡(其区域露出在最上层的部分为 0%)。所有状态都从操作系统读取,而非凭假设得出;每次遍历期间的 ForegroundWatch 监测(140 万至 270 万次采样,前台窗口始终未变)表明,读取过程完全不会抢占焦点。因此,把浏览器窗口停放在第二台显示器上,就能用 skyshot 读取内容,同时焦点始终留在你的屏幕上。

窗口移到第二台显示器后,输入能力并不会保留。向同一窗口发送的 WM_CHAR 会被 PostMessage 接受,却不会产生任何变化——无论窗口是否被遮挡;而在同一窗口处于活动状态时发送完全相同的消息,则会插入字符。因此,第二台显示器带来的是读取能力,而非操作能力;而需要操作的界面(Chromium 内部界面、画布、游戏),恰恰都需要它无法提供的激活状态。限制在于输入:对于输入,每个桌面仍只有一个光标和一个前台窗口。

需要说明一点,因为“我的浏览器在第二台显示器上,运行正常”确实描述了另一条路径。以上所有内容都针对操作系统级注入,并不适用于浏览器自身的协议。在同一台机器上,通过 CDP 操作一个带界面的 Edge 窗口进行测量(Input.dispatchMouseEvent,即 Playwright 的 page.mouse.click 所发送的消息):点击到达时是可信事件,系统光标没有移动哪怕一个像素(GetCursorPos 前后完全相同),前台窗口没有改变;即使进一步将窗口最小化(IsIconic 为 true,前台窗口是另一个窗口),后续点击仍然成功。CDP 将输入注入浏览器自身的输入管线,位于操作系统输入队列之上,因此单光标/单前台窗口规则根本无法影响它——同样,这条规则也无法影响通过应用自身 API 而非屏幕进行操作的对象。(窗口最小化时,页面的 document.visibilityState 仍显示为 visible;这是 Playwright 启动 Chromium 时设置的标志导致的现象,并非 Chromium 的普遍行为。点击能否送达是协议本身的特性,与此无关。)

决定结果的是输入进入的层级:

路径是否需要光标/前台是否跨越显示器或遮挡边界
应用自身的协议(CDP、Playwright、DOM/JS 事件、应用 API)否 — 不会注入任何 OS 级输入是,窗口甚至不必可见
element_action(发送给元素的 UIA 模式)否 — 但窗口必须存在并公开一个元素是
向窗口发送 PostMessage(type_text)在 Chromium 上的实测结果:是 — 目标必须处于活动状态,文本才会显示否
物理输入注入(SendInput、click_at 的原始事件路径、send_keys)是 — 整个桌面只有一个光标和一个前台窗口否

因此,对于网页来说,“将浏览器放到虚拟显示器上”是个不错的方案,而且本项目并不与之竞争:网页本身就是可寻址、可脚本化的界面。dsh-cua 面向的是没有这种界面的目标 — Explorer、原生对话框、Office、画布和游戏、没有脚本 API 的应用 — 在这些场景中,OS 输入是唯一可用的途径,而第二台显示器恰恰无法帮助这条途径。两种说法都成立。

因此,冲突并非实现上的缺陷,而是 OS 的限制:Windows 没有第二个光标。 鉴于此,让出控制权才是唯一正确的响应 — 而大多数调用根本不会遇到这个问题,因为它们完全不会注入输入:

  • element_action / element_action_at — 将 UIA 模式发送给元素:没有物理输入、没有光标移动、没有焦点变化。这些路径“可以在用户打字时运行”。
  • type_text — 面向窗口的 PostMessage,而非全局按键。它会先解析窗口内的文本控件(顶层窗口不会将 WM_CHAR 转发给其子编辑控件),并读取该控件的内容,因此回执会携带 effect_verified,而不只是报告已发送消息。 一项实测限制,针对 Chromium: 向非活动窗口中的 Chromium 窗口发送 WM_CHAR,PostMessage 会接受该消息,但不会产生任何变化;而向同一窗口(处于活动状态时)发送相同消息,则会插入字符(tests/verify-visible-vs-foreground.py)。因此,如果浏览器窗口在后台,不要指望文本会显示出来 — 请查看回执:你会得到 effect_verified: null(未确认),而不是错误的 ok。未经实测: 在实时网页中,解析器实际会选中哪个控件(Chromium 的渲染窗口并非 EDIT 类控件,因此输入位置可能会退回到窗口本身)。回执中的 resolved_by / target_hwnd 会告诉你实际选中了什么 — 这是用你自己的目标快速确认结果的方法。
  • 所有只读工具(skyshot、element_at_point、capture_window、…)— 不会触碰任何内容。

只有两条路径会注入物理输入,之所以存在,是因为画布、游戏和 Chromium 内部界面不会公开可寻址的元素:tool_click_at 的原始事件路径,以及 tool_send_keys 的全局热键。这两条路径正是仲裁器要防护的对象。

安装

要实际操作桌面,你需要 Windows x64 + 交互式桌面会话 + Python ≥3.10。(此软件包也可在 Linux/macOS 上安装并启动,tools/list 会正常响应,但调用工具时会提示“requires Windows”——参见上文“平台语义”。)

# Option 1: uvx, zero install (recommended)
uvx --from dsh-cua dsh-cua-server   # runs the stdio MCP server directly

# Option 2: pip
pip install dsh-cua

# Option 3: from source
pip install git+https://github.com/Hutusion/dsh-cua.git

无论采用哪种安装方式,都要使用 python -m dsh_cua 启动服务:

python -m dsh_cua                # depends on no executable being on PATH

为什么 uvx 命令要写出脚本名称。 此软件包提供 两个 控制台脚本:dsh-cua-server 和 dsh-cua,二者都会启动同一个服务。上文使用了显式形式(--from dsh-cua dsh-cua-server),因为它适用于所有已发布版本:短名称是在 0.4.1 中添加的,因此在 0.4.0 上运行 uvx dsh-cua 仍会失败,并提示 “An executable named dsh-cua is not provided by package dsh-cua”。在 uvx 中,你需要显式指定脚本;uvx 会在自己的临时环境中运行该脚本,因此不涉及 PATH。

出于另一个无关原因,pip 下的同一脚本不太理想:pip 会将控制台脚本安装到解释器的 Scripts 目录中,而该目录不一定在 PATH 中——在原版 python.org 3.12 安装中实测,用户 PATH 和系统 PATH 均未包含此目录,因此 pip install dsh-cua 安装成功后,运行 dsh-cua-server 却提示找不到命令。python -m dsh_cua 完全不需要 PATH 条目。此控制台脚本仍会随软件包提供,只要 PATH 中包含它,就可以正常使用。

选项 1 和 2 目前都可用:此软件包已发布到 PyPI(https://pypi.org/project/dsh-cua/)。如果 uvx/pip 以后返回 404,请使用选项 3——它始终有效。

接入

任意 MCP 客户端均可;将服务命名为 win32(技能的工具名称约定为 mcp__win32__*)。

python -m(不依赖 PATH,推荐):

{ "mcpServers": { "win32": { "command": "python", "args": ["-m", "dsh_cua"] } } }

uvx:

{ "mcpServers": { "win32": { "command": "uvx", "args": ["--from", "dsh-cua", "dsh-cua-server"] } } }

更多配置示例见 examples/:Claude Code / 通用客户端 / dsh cordis.patch.yml 片段 / 如果你希望模型读取屏幕截图,则需要的路由模态声明(tr-route-settings.yml)。

技能(可选,但强烈推荐)

skill/computer-use/SKILL.md 是使用这些工具的配套准则:观察 → 定位 → 操作 → 验证循环、回执语义、重试安全性,以及与人类共存的规范。模型无需它也能使用这些工具,但有了它,模型就能自行选择正确的路径——实测差异很大。

技能位于此仓库中,而不在软件包中——uvx 和 pip 不会在你的磁盘上创建 skill/ 目录,因此只有在检出仓库后,下面的 cp 命令才能使用。若没有检出仓库,请获取此文件:

mkdir -p ~/.dsh/skills/computer-use
curl -fsSL https://raw.githubusercontent.com/Hutusion/dsh-cua/main/skill/computer-use/SKILL.md \
  -o ~/.dsh/skills/computer-use/SKILL.md      # use ~/.agents/skills/ for Claude Code

如果已克隆仓库,则复制整个目录:

# Claude Code / generic agents
cp -r skill/computer-use ~/.agents/skills/
# dsh
cp -r skill/computer-use ~/.dsh/skills/

安全模型

层级操作限制机制
只读12 个观察工具无限制,可随时调用
软性元素操作、PostMessage 输入、剪贴板写入、启动应用程序跨 Agent 互斥锁(命名互斥锁,多进程,自动串行化)
严格原始点击、全局热键互斥锁 + GetLastInputInfo 让出控制:如果用户近期输入过内容,操作会等待;超时后会拒绝操作并返回 user-active,而不是抢夺光标

需如实说明的边界:让出控制是一种协作协议,并非绝对保证(注入前 150 ms 的紧密检查会尽可能缩小操作窗口);少数应用程序即使在调用 set_value 时也会自行激活(回执会如实报告 foreground_changed);对于同一窗口上的两个操作者,目前没有技术解决方案——不要操作 Agent 正在操作的同一窗口。

测试

python tests/verify-coexistence.py    # 25 checks: zero-input proof / cross-process mutex / synthetic human contention / kill switch
python tests/verify-p0-fixes.py       # the three P0s fixed in 0.2.0: each fails before the fix

测试无需人工配合——“用户输入”由一次真实的 1 像素光标移动模拟,之后光标会恢复原位。

测试需要真实的交互式桌面会话(某些检查会创建窗口并通过 UIA 与其交互),因此无法在 GitHub 托管的运行器上运行。CI 覆盖的是无需桌面的部分:打包和安装、模块导入、差异索引和树形行转义的回归测试,以及仲裁器的决策逻辑——请参阅 .github/workflows/ci.yml。

python tests/ci-desktop-free.py       # the local equivalent of the above, no desktop needed

许可证

MIT

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。