← 知识库 / 技术

给 AI 装上操作 Windows 的手:Windows-MCP 部署、加固与实测

把 Windows-MCP 接进 MCP 宿主(WorkBuddy / Cursor)的完整过程:三处官方文档与实测不符的坑、权限策略该写在哪、三步验证法、一条长链路的效率实测(8 分钟 → 8.77 秒),附速查清单、已知脆弱点与回滚方案。

起因:想让 AI 直接操作 Windows 桌面——点按钮、填表单、跑那些没有 API 的老软件。 本文记录把 Windows-MCP 接进 MCP 宿主(WorkBuddy / Cursor)的完整过程,包含三处官方文档与实测不符的坑、权限策略该写在哪的判断,以及一次长链路实测的效率数据。 所有命令与配置都在 Windows 11 + 中文系统上实际跑过并验证。


零、先决条件

  • Python ≥ 3.12(uvx 会自带 3.12+;宿主侧只需装 uv 即可)
  • 不需要视觉模型:Windows-MCP 走 UI Automation 元素树,任何支持 tool calling 的 LLM 都能驱动,截图只是可选兜底
  • 中文 Windows 可用,但要留意 App 工具按开始菜单名匹配不稳(非致命,可用 PowerShell 兜底启动)

一、为什么是 Windows-MCP

选它的原因很实际:它不需要视觉模型。

市面上多数 Computer Use 方案要么依赖微调过的 GUI 专用模型,要么靠纯截图 + 坐标回归,对本地硬件要求高、稳定性差。Windows-MCP 走的是另一条路——读 Windows 的 UI Automation 元素树,直接拿到”这里有个按钮叫 XX、坐标是 (x,y)“这类结构化信息。

这带来两个直接好处:

  • 任何支持 tool calling 的 LLM 都能驱动,不需要显卡、不需要本地模型;
  • 动作延迟实测 0.2~0.9 秒(官方标称区间,实测符合),因为没有图像推理这一步。

它暴露的工具面大致分四类:状态感知(Snapshot / Screenshot / DisplayInventory)、键鼠原语(Click / Type / Scroll / Move / Shortcut / Clipboard)、等待(Wait / WaitFor)、以及高风险类(PowerShell / Registry / FileSystem / Process)。

它的坑也很明确:对 Electron / Chromium 应用,元素树是”断裂”的——后面第七节会详细说。

二、部署三步

2.1 装 uv

Windows-MCP 官方唯一的启动方式是 uvx,所以先装 uv:

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

装到 %USERPROFILE%\.local\bin,并自动写入用户级 PATH(无需管理员权限)。

⚠️ 不要用 pip install uv 装到某个托管运行时里——会污染宿主运行时,独立安装器是更干净的路径。

2.2 先冒烟测试,别急着常驻

uvx windows-mcp serve --transport streamable-http --host 127.0.0.1 --port 8000

这一步产生零持久改动,但必须做——因为下面三个坑只有跑起来才能发现(见第三节)。

首次运行会拉几十个依赖包,可能要等一两分钟。官方也提示:首跑可能超时,忽略并重试一次即可。

2.3 注册为常驻服务

uvx windows-mcp install --transport streamable-http --host 127.0.0.1 --port 8000

产物:

项位置
计划任务windows-mcp-server(每用户,登录自启,装完立即启动)
包装脚本~/.windows-mcp/start-server.cmd
日志~/.windows-mcp/server.log / server.error.log

已知无害噪音:在中文 Windows 上执行 install 会往 stderr 抛一串 UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb4——这是 uvx 用 UTF-8 读取 GBK 子进程输出的解码问题。exit code 仍为 0,任务正常创建,可以直接忽略。


三、三个官方文档与实测不符的坑

这三个坑如果不实测,配出来的东西就是坏的。建议任何 MCP server 接入都先做一次冒烟测试。

坑一:端点路径是 /mcp,不是 /mcp/

README 和 OAuth 配置示例都写的是 /mcp/(带尾斜杠)。实测:

POST /mcp/  →  307 Temporary Redirect
POST /mcp   →  200 OK

服务自身的启动日志也确认端点是 http://127.0.0.1:8000/mcp。

结论:配置里用不带尾斜杠的版本,省掉一次重定向。 对部分 MCP 客户端来说,多一跳重定向可能就是连接失败。

坑二:工具名不是 Shell-Tool / App-Tool

README 的正文里出现过 Shell-Tool、App-Tool、Launch-Tool 这些名字,但实际工具清单里没有它们。实测 20 个工具的真实名称是:

App, DisplayInventory, PowerShell, FileSystem, Snapshot, Screenshot, Click, Type,
Scroll, Move, Shortcut, Wait, WaitFor, Scrape, MultiSelect, MultiEdit, Clipboard,
Process, Notification, Registry

对应关系:Shell-Tool → PowerShell;App-Tool + Launch-Tool → 合并进 App。

为什么这很要命:工具过滤是靠名字匹配的。名字写错,策略会静默失效——你以为禁用了某个工具,其实什么都没禁。

坑三:配置文件里只能”排除”,不能”白名单”

源码 infrastructure/config.py 里:

@dataclass
class ToolsConfig:
    exclude: list[str] = field(default_factory=list)

只有 exclude,没有白名单键。 想要白名单只能走 CLI 的 --tools 参数。

三种过滤方式(优先级:CLI > 环境变量 > 配置文件):

方式写法
CLI 白名单serve --tools "Snapshot,Click,Type"
CLI 黑名单serve --exclude-tools "Registry,PowerShell"
环境变量WINDOWS_MCP_TOOLS / WINDOWS_MCP_EXCLUDE_TOOLS
配置文件config.toml → [tools] exclude = [...]

策略口径速查

口径配置说明
最小权限CLI --tools "Snapshot,Screenshot,DisplayInventory,Click,Type,Scroll,Move,Shortcut,Clipboard,Wait,WaitFor,MultiSelect"白名单只能走 CLI;未来新增工具默认不放行
标准exclude = ["Registry", "App"]App 在中文系统按名匹配不稳
宽松exclude = ["Registry"]仅挡唯一能直接破坏系统的工具

四、权限策略该写在哪(本文最重要的一条)

这是整个接入过程中最容易做错、也最不容易发现的地方。

Windows-MCP 支持把工具过滤写进两个地方:命令行参数,或 config.toml。看起来等价,实际上有天壤之别。

关键事实:windows-mcp install 生成的 ~/.windows-mcp/start-server.cmd 是安装器的产物。如果你把权限策略写在那个脚本里,下次执行 windows-mcp install(比如升级版本、或修复问题)时,它会被原样覆盖,你的策略静默消失——而且不会有任何报错。

查源码可以确认:install() 函数根本不读写 config.toml,唯一调用 write_config 的是 auth 命令。

所以:

✅ 策略写进 ~/.windows-mcp/config.toml   ← 用户配置,重装不丢
❌ 策略写进 ~/.windows-mcp/start-server.cmd ← 安装器产物,重装被覆盖

完整 schema:

[server]
transport = "streamable-http"      # stdio | sse | streamable-http
host = "127.0.0.1"
port = 8000
stateless_http = false             # true 时不发 Mcp-Session-Id,抗服务重启
auth_key = ""                      # 端点 Bearer 鉴权

[security]
ip_allowlist = []                  # 服务只绑 loopback 时没用(源 IP 恒为 127.0.0.1)
cors_origins = []

[tools]
exclude = ["Registry"]             # ← 唯一可用的工具过滤键

关于”该禁哪些工具”的一点诚实意见

网上很多配置模板会一口气禁用 PowerShell、FileSystem、Process、Registry 显得很安全。但这里有个逻辑问题:

一旦开放了 PowerShell,再单独禁用 FileSystem 和 Process 就没有任何实际安全意义——PowerShell 本来就能读写文件、结束进程。把它们关掉只会制造”我已经收紧过了”的错觉。

真正能直接破坏系统的只有一个:Registry(注册表写入)。其余的,要么被 PowerShell 覆盖,要么只是影响便利性。

另一个容易被滥用的配置是 ip_allowlist:当服务只绑 loopback 时,加上它零收益——本机任何进程连过来的源 IP 恒为 127.0.0.1,这条规则拦不住任何东西,却多了一个让连接莫名失败的故障点。

建议的判断标准:只拦”能造成不可逆破坏”的;其余按实际需要开放,并且如实说明这不是安全措施。

改完策略要重启服务

Stop-ScheduledTask -TaskName 'windows-mcp-server' -ErrorAction SilentlyContinue
Start-Sleep 2
Get-NetTCPConnection -LocalPort 8000 -State Listen -ErrorAction SilentlyContinue |
  ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }
Start-Sleep 2
Start-ScheduledTask -TaskName 'windows-mcp-server'

注意:只 Stop-ScheduledTask 不会杀掉已启动的 python 子进程,必须按端口找 PID 补刀。

可选的包装脚本增强(易失,install 会覆盖)

@echo off
setlocal
set ANONYMIZED_TELEMETRY=false
set WINDOWS_MCP_DISABLE_FLASH=1
<uvx 解析出的 python.exe 绝对路径> -m windows_mcp serve --transport streamable-http --host 127.0.0.1 --port 8000 1>>"%USERPROFILE%\.windows-mcp\server.log" 2>>"%USERPROFILE%\.windows-mcp\server.error.log"

五、接入 MCP 宿主

WorkBuddy

配置文件在 ~/.workbuddy/mcp.json(不是 .mcp.json):

{
  "mcpServers": {
    "windows-mcp": {
      "type": "streamableHttp",
      "url": "http://127.0.0.1:8000/mcp",
      "timeout": 60000,
      "disabled": false
    }
  }
}

timeout 建议放宽到 60000——默认 30000 对多步 GUI 链路偏紧。

改完还需要手动:连接器管理 → 找到该服务 → 信任 → 启用 → 从系统托盘完全退出再重开(只关窗口不生效)。

  • 信任记录写在 ~/.workbuddy/mcp-approvals.json,不点信任不会加载
  • 改配置前先备份 mcp.json 为 .bak.<时间戳>

Cursor

~/.cursor/mcp.json(用户级,全项目生效)或项目内 .cursor/mcp.json,mcpServers 结构同上。

为什么推荐单实例 HTTP,而不是 stdio

stdio 模式下,每个宿主各起一个子进程。两个进程同时驱动同一套鼠标键盘与焦点窗口会互相抢焦点,表现为点击错位、输入丢字,而且极难定位原因。

用常驻 HTTP 服务、两个宿主连同一个实例,这个问题在架构层面就消失了。

配套纪律:同一时刻只让一个宿主接管桌面。

连不上时的回退

回退 stdio 形态(代价:每个宿主各起一个进程,会争抢同一套键鼠焦点):

"windows-mcp": {
  "command": "%USERPROFILE%\\.local\\bin\\uvx.exe",
  "args": ["windows-mcp", "serve"]
}

command 两种写法:

  • 环境变量形式 "%USERPROFILE%\\.local\\bin\\uvx.exe" —— 更去个人化,但依赖宿主展开环境变量;
  • 展开后的绝对路径 "C:\\Users\\<用户名>\\.local\\bin\\uvx.exe" —— 宿主不展开变量时用(等价于 spawn(shell:false) 不解析 %VAR%),注意 JSON 里反斜杠要双写。

六、验证:三步法,第三步最容易漏

步骤做法通过标准
1. 清单tools/list工具数与策略一致(如排除 Registry 后应为 19)
2. 正向调一个只读工具(如 DisplayInventory)返回真实数据(分辨率、DPI、work area),不是幻觉
3. 反向调一个被禁用的工具返回 isError: true + Unknown tool

第 3 步是唯一能区分**“策略生效”和”配置文件根本没被读到”**的方法。跳过它,你永远不知道自己是安全还是裸奔。

实测输出(正向):

[{"index":0,"device":"\\\\.\\DISPLAY1","primary":true,
  "bounds":{"left":0,"top":0,"right":1920,"bottom":1080},
  "work_area":{"left":0,"top":0,"right":1920,"bottom":1032},
  "resolution":"1920x1080","effective_dpi":96,"scale":1.0}]

反向(调用被禁的 Registry):

HTTP: 200 | isError: true
Unknown tool: 'Registry'

服务自报版本 4.0.4(PyPI 包版本号与之不同,属正常)。

宿主侧联通后,可用一个只读用例试水:「列出当前打开的所有窗口标题」(Snapshot + use_vision=false)。

Snapshot 的 use_vision 默认 false,因此不会把整屏截图带进上下文——做验证时保持默认,尊重用户隐私。 另注意 Snapshot 返回的窗口列表里混有大量非可见后台窗口(无标题窗口、任务栏、显卡驱动 overlay、输入法窗口等),汇报时要把它们和用户意义上的”打开的应用”区分开。


七、长链路实测:让 AI 自己发一条扣扣消息

验证单个工具能调通是一回事,能不能跑完一条多步、有状态、还可能中途蹦弹窗的链路是另一回事。

测试任务:打开扣扣(已开启自动登录)→ 找到「我的手机」→ 发送一条消息。

结果:成功送达,全程无人介入,零卡死。

实测发现(这部分最有价值)

1. Electron 应用的 UIA 覆盖是断裂的

扣扣新版客户端是 Electron 架构。它的 UI Automation 元素树里:

能拿到拿不到
标题栏按钮聊天面板全部内容
左侧导航项消息输入框
会话列表每一行发送按钮

也就是说,纯文本模式(不给模型视觉能力)在 Electron 应用上做不了完整链路,必须切 use_vision=true 看画面来定位输入框。

顺带发现一个工具缺陷:Snapshot(use_ui_tree=false) 模式下,窗口枚举会返回 No windows found / No active window found,但窗口明明开着。这个模式只能纯看图,不能信它的窗口列表。

2. 窗口状态一变,所有坐标立刻失效

一次最大化操作,让同一个按钮从 (583,830) 变成了 (30,966)。按旧坐标点的结果是白点一次,而且不会有任何报错——它只会安静地点在错误的位置上。

规则:任何窗口状态变化(最大化 / 还原 / 缩放 / 切换视图)之后,必须重新获取元素树。

3. 点击列表行 ≠ 打开会话

在联系人列表里单击甚至双击某一行,只会让它高亮选中,右侧聊天面板不渲染。真正可靠的是用应用内置的搜索框按名字寻址——这是整个任务的关键转折点。

4. 同名入口陷阱

左侧导航栏里有一个「我的手机」按钮,看起来名字完全对得上。但点它只会弹出跨设备内容同步卡片,根本没有输入框。真正的聊天对象藏在 联系人 → 我的设备 → 我的手机。

别只按名字匹配就点,先确认入口类型。

5. 一条必须记住的纪律:终态验证不可省

Click 工具返回的是:

Single left clicked at (1515,966).

这只证明”点击事件注入成功”,完全不证明”消息发出去了”。

每轮测试我都靠截图确认蓝色气泡出现、输入框清空,才算真的完成。任何把”工具返回 OK”当作”任务成功”的自动化,都会在某次失败时静默地什么都不做,而且没人发现。

6. 隐私面:agent 看到的和你看到的一样

这次为了定位输入框,一共取了 4 张全屏截图,另有多次元素树文本导出。这些内容里包含:完整的会话列表、未读消息预览、私聊/群聊名称、历史文件传输记录的文件名。

这不是 bug,是桌面自动化的固有代价。 任何”让 AI 操作我的电脑”的方案都得接受这一点——它不该被当成纯技术问题处理。

所以:在方案评估和实施阶段都应主动向用户说明这一点。优先用 Snapshot(use_vision=false) 取文本元素树;确有必要再取截图,并说明取了几张。


八、效率:从 8 分钟到 8.77 秒

同一个任务跑三遍,对比”探索态”和”成熟态”的成本:

维度① 探索态② 熟悉流程·Agent 优化③ 脚本化
墙钟耗时约 8 分钟约 3 分 17 秒8.77 秒
工具调用数41175
元素树导出3 次00
全屏截图6 张4 张0
试错/无效动作6 次(15%)1 次(6%)0
决策点每步重新判断模型逐步决策0(无模型参与)

时间到底花在哪

脚本化的单步实测延迟:

步骤耗时
App resize(钉住窗口几何)1501 ms
Type 输入搜索词1518 ms
Click 搜索结果562 ms
Type 输入消息2144 ms
Click 发送559 ms
5 次调用合计6284 ms

而第①轮:41 次调用 × 约 0.6 秒 ≈ 25 秒的”机器时间”——剩下 7 分半全是模型每一步重新推理的开销。

结论:瓶颈从来不是 MCP 的执行速度,而是”每一步都要模型重新想一遍”。 缩短时间的正确做法是减少决策点,不是优化单步延迟。

(顺带:Type 是最慢的动作,1.5~2.1 秒/次,因为逐字符注入;Click 只要 0.56 秒。能用一次 Type 表达的内容不要拆成多次。)

收益最大的一招:把坐标常量化

App(mode="resize", name="<窗口名>", window_loc=[0,0], window_size=[1600,1000])

窗口几何一旦钉死,所有坐标从”每次都要探测的变量”变成”一次算好的常量”。

探索态最大的浪费正是来自这里:一次最大化让按钮位置全变,旧坐标失效。固定几何让这类问题在结构上消失,而不是”下次注意点”。

第二招:名字寻址 > 位置寻址

走搜索框输入名称这条路,而不是去点列表里那一行。会话列表顺序会随新消息浮动(实测 3 分钟内同一个会话挪了约 80px),按位置点迟早会漂;按名字搜不漂。

实测细节:搜索输入后不一定自动打开会话(第一次成功是巧合)。稳定做法是再多点一次搜索结果里的首项。

第三招:元素树导出应降为 0

Snapshot(use_ui_tree=true) 输出量极大(列表每一行的每个字都展开),单次约 7k token。坐标已常量化之后,它没有信息增量,只剩成本。Snapshot(use_vision=true) 只保留 1~2 次用于终态验证。

脚本化:天花板,以及它的代价

真正的成熟形态是把流程固化成脚本,不再让模型参与执行:

决策点 0 | 截图 0 | 元素树 0 | 试错 0 | 5 次调用 8.77 秒

设计要点:先 App resize 钉住几何 → 搜索框按名字寻址 → 直接对 http://127.0.0.1:8000/mcp 发 JSON-RPC。

但必须说清代价:

它没有的能力后果
不会看画面窗口没成功 resize、弹了广告窗、应用改版换了布局——全都发现不了,会对着错误坐标盲点
不会纠偏任一步失败就整体停住
不会验证返回码只代表”点击已注入”,不代表任务完成

所以脚本化不是”更高级”,是拿鲁棒性换速度。它适合高频、稳定、失败可容忍的重复操作。

建议的架构

探索期 → Agent(慢,但会找路、会纠偏)
稳定期 → 脚本(快,零试错)
恢复期 → 脚本失败时交回 Agent 诊断并重建脚本

不要把 Agent 优化成脚本——它的价值本来就不在速度,而在”遇到没见过的情况能自己想办法”。两者是探索器与执行器的分工,不是替代关系。


九、路线判断:什么时候不该用 Windows-MCP

用例是 Electron 应用(扣扣这类)时,其实有更优解。

如果一个项目有现成的三层自动化策略(CDP 协议层 / UIA 控件层 / 视觉+注入层),Electron 和浏览器应该优先走 CDP 协议层——可以直接拿到 DOM 节点,免截图、免坐标猜测,稳定性和速度都显著更好。这条路线的完整原理与实测数据,见《让 AI 操作桌面程序:从「截图点击」到三层降级架构》。

Windows-MCP 的不可替代价值在:

  • 原生 Win32 窗口
  • 系统设置界面
  • 没有 CDP 入口的传统桌面软件

它在这里没有竞品。选型时按”目标应用有没有协议层入口”来分流,比”哪个工具更强”更有意义。


十、回滚

撤销什么做法
宿主配置用备份 mcp.json.bak.<时间戳> 覆盖回去
常驻服务uvx windows-mcp uninstall
权限策略删除 ~/.windows-mcp/config.toml(恢复全部工具)
包装脚本改动用 start-server.cmd.bak.<时间戳> 覆盖
彻底卸载删 %USERPROFILE%\.local\bin 并从用户 PATH 移除

一个容易被忽略的脆弱点

包装脚本 start-server.cmd 里写死了 uv 缓存目录下的一个绝对路径:

%LOCALAPPDATA%\uv\cache\archive-v0\<hash>\Scripts\python.exe

如果你执行 uv cache clean 或 uv cache prune,这个路径会消失,常驻服务就再也起不来了,而且报错信息不会直接指向原因。

恢复方法:重跑一次 windows-mcp install(注意:包装脚本里手加的其它配置会一并丢失,需要重新施加;写在 config.toml 里的策略不受影响——这正是第四节那条结论的价值)。


十一、已知脆弱点速查

项症状应对
uv 缓存路径写死包装脚本指向 ...\uv\cache\archive-v0\<hash>\Scripts\python.exe;执行 uv cache clean / uv cache prune 后服务起不来重跑 windows-mcp install,并重新施加包装脚本里的环境变量(config.toml 策略不受影响)
中文 Windows 上 App 工具不准按开始菜单名启动失败用 PowerShell 启动,或用可执行文件全路径
双宿主争抢键鼠点击错位、输入丢字单实例 HTTP 常驻 + 同一时刻只让一个宿主接管桌面
长任务失控多步任务遇弹窗 / 锁屏 / 权限框会卡住先跑 ≤5 步短任务;长任务需人在旁边盯
首跑超时首次拉依赖较慢,宿主可能判定启动失败忽略并重启宿主一次

附录 A:把整套流程沉淀成 Skill

上面这些内容如果每次都靠记忆操作,一定会漏掉步骤。所以把它们固化成了一份 Skill(MCP 宿主可直接加载的指令包)。

职责边界:它只负责「装好、收紧、接入、验证、可回滚」;真正操作桌面的部分属于另一份桌面自动化 Skill(三层路线)。两者互补,不重叠。

Skill 覆盖的内容:

章节内容
先决条件Python 版本要求;澄清”不需要视觉模型”这个常见误解
装 uv官方安装器的正确姿势与反面做法
冒烟测试附完整探测脚本;三处文档纠错对照表
常驻服务install 产物清单;中文系统的无害报错说明
权限策略完整 config.toml schema;写在哪 / 为什么;口径取舍的诚实意见
接入宿主WorkBuddy 与 Cursor 的配置;单实例 vs stdio 的取舍
验证三步法;反向验证的具体命令
回滚逐项撤销表
已知脆弱点uv 缓存路径、中文系统工具失效、双宿主争抢、长任务失控
附录工具行为坑清单 / 隐私面 / 路线选择 / 成熟流程固化手法

其中「成熟流程固化手法」一节记录了从探索态到脚本化的六条具体手法,以及探索器/执行器的分工结论。

一个值得强调的设计:策略写在”抗重装”的位置

Skill 里反复强调第四节的判断——把权限策略放在安装器不会覆盖的文件里。

这类”看起来等价、实际差一个数量级”的选择,在自动化运维里非常常见:配置文件放对位置,比配置内容写得漂亮重要得多。


附录 B:接入检查清单

□ uv 已安装,uvx 可用(用户级 PATH)
□ 冒烟测试通过,已确认真实工具清单(名字必须与实测一致)
□ 端点确认为 /mcp(不带尾斜杠)
□ 常驻服务 install 完成,计划任务状态 Running
□ 权限策略写入 config.toml(不是 start-server.cmd)
□ 策略重启后生效,工具数已核对
□ 反向验证通过(被禁工具返回 Unknown tool)
□ 宿主配置已写入并备份原文件
□ 宿主侧已信任 + 启用 + 完全重启
□ 正向验证通过(只读工具返回真实数据)
□ 终态验证机制已确定(每一步如何确认真实结果)
□ 回滚路径已验证(知道怎么撤)

附录 C:工具行为实测坑(补充清单)

坑现象应对
坐标随窗口状态失效窗口最大化后,同一按钮从 (583,830) 变 (30,966)任何窗口状态变化后必须重新 Snapshot,禁止复用旧坐标
use_ui_tree=false 时窗口枚举返回空报 No windows found,但窗口明明开着这个模式只看图,不要信它的窗口列表
Electron 应用 UIA 覆盖断裂元素树只有标题栏 + 导航项 + 列表行;聊天面板、输入框、发送按钮全不可见必须切 use_vision=true;纯文本模式做不了完整链路
点击列表行 ≠ 打开会话点/双击只高亮选中,右侧不渲染改用应用内置搜索框按名字寻址
同名入口陷阱导航栏里同名入口其实是功能面板(无输入框)别只按名字匹配就点,先确认入口类型
中文系统控制台乱码捕获 PowerShell stdout 时中文变 GBK 乱码诊断脚本内显式设 [Console]::OutputEncoding,或忽略(不影响判定)
Click / Type 的 label 参数schema 声明为 integer(元素 id),而 Snapshot 输出里没有显式数字 id一律用 loc=[x,y],别指望 label
Wait 的 duration只接受 integer,传 1.5 会被 schema 拒绝用整数秒;需要更细粒度就用 WaitFor
Type 是最慢的动作逐字符注入,实测 1.5~2.1 s/次,明显慢于 Click(约 0.56 s)能用一次 Type 表达的内容别拆成多次

实测效率基线:首次探索性完成一个多步 GUI 任务 41 次工具调用、85% 有效;单步延迟约 0.6 s,符合官方标称 0.2~0.9 s,零卡死。瓶颈是定位稳定性,不是执行速度。


附录 D:无工具环境下的探测技巧

若所在会话的 shell 缺 coreutils(ls / head / grep 全部 command not found),且 PowerShell 工具的 stdout 不回传,可用:

node -e "require('child_process').execSync(<命令>, {stdio:['ignore','pipe','ignore']}).toString()"

或写一个 node exec.js "<powershell 命令>" 包装器统一代跑并打印 stdout / stderr / exit code。


结语

Windows-MCP 解决的是一个很实在的问题:那些没有 API 的软件,终于也能被自动化了。

但接入它真正花时间的地方不在安装,而在三件事上:确认文档与实现是否一致、把权限策略放在不会被覆盖的位置、以及接受”agent 看到的和你看到的是一样的”这个隐私事实。

最后一句话给所有做桌面自动化的人:工具返回 OK,从来不等于任务完成。 只有看到真实世界的状态变化,才算数。