<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>UIAutomation on 我的博客</title><link>https://blog.5616760.com/tags/uiautomation/</link><description>Recent content in UIAutomation on 我的博客</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Sun, 18 Oct 2026 13:40:00 +0800</lastBuildDate><atom:link href="https://blog.5616760.com/tags/uiautomation/index.xml" rel="self" type="application/rss+xml"/><item><title>Windows-MCP：给 AI 一副能操作 Windows 的手——无障碍树、桌面租约与 21 个工具</title><link>https://blog.5616760.com/0052.html</link><pubDate>Sun, 18 Oct 2026 13:40:00 +0800</pubDate><guid>https://blog.5616760.com/0052.html</guid><description>&lt;p&gt;先给结论：&lt;strong&gt;Windows-MCP 不是&amp;quot;AI 看屏幕点鼠标&amp;quot;，它把 Windows 的无障碍树当成了 API。&lt;/strong&gt; 大多数 computer-use 方案走的是「截图 → 视觉模型猜坐标 → 点击」，代价是每一步都烧一张图、每一步都可能点偏、每一个模型都要单独适配。Windows-MCP 换了一条路：&lt;strong&gt;用 UIAutomation 把当前桌面翻译成一棵带 id 的元素树，让 LLM 直接说&amp;quot;点第 37 号按钮&amp;quot;&lt;/strong&gt;。这决定了它所有的优点（快、省 token、任意 LLM 可用）和所有的缺点（依赖目标应用的无障碍实现质量）。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="图：Windows-MCP 封面——无障碍树驱动的桌面操控" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://blog.5616760.com/img/0052-001.png"&gt;&lt;/p&gt;&#10;&lt;h2 id="目录"&gt;目录&#10;&lt;/h2&gt;&lt;ol&gt;&#10;&lt;li&gt;&lt;a class="link" href="#%e4%b8%80%e5%ae%83%e6%98%af%e4%bb%80%e4%b9%88%e4%b8%80%e4%bb%bd%e6%95%b0%e6%8d%ae%e5%bf%ab%e7%85%a7" &gt;它是什么：一份数据快照&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="#%e4%ba%8c%e5%ae%9e%e6%b5%8b%e8%a3%85%e4%b8%80%e9%81%8d%e7%9c%8b%e5%ae%83%e5%88%b0%e5%ba%95%e4%bb%80%e4%b9%88%e6%a0%b7" &gt;实测：装一遍，看它到底什么样&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="#%e4%b8%89%e7%9c%9f%e9%97%ae%e9%a2%98%e8%a7%86%e8%a7%89%e7%8c%9c%e5%9d%90%e6%a0%87%e8%b5%b0%e4%b8%8d%e9%80%9a" &gt;真问题：视觉猜坐标走不通&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="#%e5%9b%9b%e5%88%86%e5%b1%82%e6%9e%b6%e6%9e%84%e4%bb%8e-__main__-%e5%88%b0-uia" &gt;分层架构：从 &lt;strong&gt;main&lt;/strong&gt; 到 uia&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="#%e4%ba%94%e4%b8%a4%e6%9d%a1%e6%84%9f%e7%9f%a5%e8%b7%af%e5%be%84screenshot-%e4%b8%8e-snapshot" &gt;两条感知路径：Screenshot 与 Snapshot&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="#%e5%85%ad%e6%a0%91%e6%8a%93%e5%8f%96%e7%9a%84%e4%b8%89%e4%b8%aa%e5%b7%a5%e7%a8%8b%e7%ba%a6%e6%9d%9f%e9%a2%84%e7%ae%97%e6%8e%92%e9%99%a4%e7%ba%bf%e7%a8%8b" &gt;树抓取的三个工程约束：预算、排除、线程&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="#%e4%b8%8321-%e4%b8%aa%e5%b7%a5%e5%85%b7%e7%9a%84%e8%83%bd%e5%8a%9b%e7%9f%a9%e9%98%b5" &gt;21 个工具的能力矩阵&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="#%e5%85%abdom-%e6%a8%a1%e5%bc%8f%e6%b5%8f%e8%a7%88%e5%99%a8%e8%87%aa%e5%8a%a8%e5%8c%96%e7%9a%84%e7%89%b9%e6%ae%8a%e9%80%9a%e9%81%93" &gt;DOM 模式：浏览器自动化的特殊通道&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="#%e4%b9%9d%e6%a1%8c%e9%9d%a2%e7%a7%9f%e7%ba%a6%e5%bd%93-ai-%e5%92%8c%e4%bd%a0%e6%8a%a2%e5%90%8c%e4%b8%80%e5%8f%aa%e9%bc%a0%e6%a0%87" &gt;桌面租约：当 AI 和你抢同一只鼠标&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="#%e5%8d%81%e7%89%a9%e7%90%86%e8%be%93%e5%85%a5%e6%8e%a5%e7%ae%a1%e4%b8%8e%e8%be%93%e5%85%a5%e8%b4%a6%e6%9c%ac" &gt;物理输入接管与输入账本&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="#%e5%8d%81%e4%b8%80%e5%ae%89%e5%85%a8%e9%9d%a2%e9%bb%98%e8%ae%a4%e5%85%b3%e9%97%ad%e7%9a%84%e9%82%a3%e4%ba%9b%e5%bc%80%e5%85%b3" &gt;安全面：默认关闭的那些开关&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="#%e5%8d%81%e4%ba%8c%e5%8f%91%e5%b8%83%e5%b7%a5%e7%a8%8b%e5%9b%9b%e5%a4%84%e7%89%88%e6%9c%ac%e5%8f%b7%e4%b8%8e%e4%b8%80%e9%81%93%e5%ae%88%e5%8d%ab" &gt;发布工程：四处版本号与一道守卫&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="#%e5%8d%81%e4%b8%89%e5%ae%83%e8%87%aa%e5%b7%b1%e6%89%bf%e8%ae%a4%e7%9a%84%e8%be%b9%e7%95%8c" &gt;它自己承认的边界&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="#%e5%8d%81%e5%9b%9b%e4%bb%80%e4%b9%88%e6%97%b6%e5%80%99%e8%af%a5%e7%94%a8%e5%ae%83%e4%bb%80%e4%b9%88%e6%97%b6%e5%80%99%e4%b8%8d%e8%af%a5" &gt;什么时候该用它，什么时候不该&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="#%e5%8d%81%e4%ba%94%e6%88%90%e7%86%9f%e5%ba%a6%e8%af%84%e4%bc%b0" &gt;成熟度评估&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="#%e5%8d%81%e5%85%ad%e7%bb%93%e8%af%ad" &gt;结语&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="一它是什么一份数据快照"&gt;一、它是什么：一份数据快照&#10;&lt;/h2&gt;&lt;p&gt;一句话：&lt;strong&gt;一个跑在本机 Windows 上的 MCP server，把 21 个桌面操作工具暴露给任意支持 MCP 的 LLM 客户端。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;数据快照（&lt;strong&gt;2026-10-18&lt;/strong&gt; 通过 GitHub API 与仓库文件核实）：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;维度&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;数据&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;仓库&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;CursorTouch/Windows-MCP&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;定位&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;MCP Server for Computer Use in Windows&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Stars / Forks&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;9,387 / 1,013&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Open Issues&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;25&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;首次提交&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2025-05-13&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;累计提交&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;775&lt;/strong&gt;（末页 page=775）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;当前版本&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;0.8.8&lt;/strong&gt;（2026-10-10 发布）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;主语言 / 运行时&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Python &lt;strong&gt;3.14+&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;许可&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;MIT&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;依赖体量&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;17 个运行时依赖，无深度学习框架&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;作者&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Jeomon George（&lt;code&gt;pyproject.toml&lt;/code&gt; 署名），组织 CursorTouch&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;&lt;strong&gt;注意 Python 3.14+ 这条硬门槛&lt;/strong&gt;：不是建议，是 &lt;code&gt;requires-python = &amp;quot;&amp;gt;=3.14&amp;quot;&lt;/code&gt;。这意味着 Windows 10 是它的实际下限（3.14 官方支持 Windows 10 及以上），也意味着很多企业环境里预装的 3.11/3.12 跑不起来，必须靠 &lt;code&gt;uv&lt;/code&gt; 拉一个独立解释器。这道门槛劝退了一批人，但也让它能用上 3.14 的自由线程构建与新版类型语法。&lt;/p&gt;&#10;&lt;p&gt;一个容易被忽略的细节：&lt;code&gt;pyproject.toml&lt;/code&gt; 里 &lt;code&gt;Development Status :: 4 - Beta&lt;/code&gt;。&lt;strong&gt;9.4k star 但自我标注 Beta&lt;/strong&gt;，这个组合本身就说明了它的成熟度定位——能用、有人在生产里用，但作者不承诺 API 稳定。&lt;/p&gt;&#10;&lt;p&gt;顺带记一个文档内部的小矛盾：README 的 Key Features 写&amp;quot;典型动作延迟 &lt;strong&gt;0.2–0.5 秒&lt;/strong&gt;&amp;quot;，而 &lt;code&gt;manifest.json&lt;/code&gt; 里给商店用的描述写的是&amp;quot;&lt;strong&gt;0.2–0.9 秒&lt;/strong&gt;&amp;quot;。两个数字出自同一仓库、同一版本，说明这个指标没有统一测量口径（不同机器、不同目标应用、不同截图后端都会影响）。&lt;strong&gt;看趋势即可，别当 SLA 用。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;h3 id="一个必须先说清的前提main-不等于-pypi"&gt;一个必须先说清的前提：&lt;code&gt;main&lt;/code&gt; 不等于 PyPI&#10;&lt;/h3&gt;&lt;p&gt;我在本机装了 PyPI 上的 &lt;code&gt;windows-mcp==0.8.8&lt;/code&gt; 做实测，撞上一件必须写在前面的怪事：&lt;strong&gt;仓库 &lt;code&gt;main&lt;/code&gt; 分支的 README 描述的能力，与 PyPI 上那个 0.8.8 的实际行为不一致。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;项目&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;code&gt;main&lt;/code&gt; 分支（README 描述）&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;PyPI 0.8.8 实测&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;serve --control-policy&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;有，三档 &lt;code&gt;relaxed&lt;/code&gt;/&lt;code&gt;balanced&lt;/code&gt;/&lt;code&gt;strict&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;不存在&lt;/strong&gt;（&lt;code&gt;Error: No such option&lt;/code&gt;）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;serve --no-control&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;有&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;不存在&lt;/strong&gt;（&lt;code&gt;Error: No such option&lt;/code&gt;）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;[control] policy&lt;/code&gt; / &lt;code&gt;enabled&lt;/code&gt; / &lt;code&gt;idle_timeout_seconds&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;支持&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;静默忽略&lt;/strong&gt;（配置能加载，但值不生效）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;鼠标接管阈值默认值&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;500 设备单位&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;120&lt;/strong&gt;（&lt;code&gt;ControlConfig&lt;/code&gt; 默认值）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;门控范围&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;按工具分类，感知类工具豁免&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;除 &lt;code&gt;ControlStatus&lt;/code&gt; 外全部工具&lt;/strong&gt;都过门控&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;元素预算 / 进程排除 / IA2 回退&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;有&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;有（这部分一致）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;原因不难推：三档门控策略那个 PR（#466）是 &lt;strong&gt;2026-10-16&lt;/strong&gt; 合并进 &lt;code&gt;main&lt;/code&gt; 的，而 0.8.8 发布于 &lt;strong&gt;2026-10-10&lt;/strong&gt;。README 跟着 &lt;code&gt;main&lt;/code&gt; 一起更新了，但包没重发。&lt;/p&gt;&#10;&lt;p&gt;这不是&amp;quot;项目有 bug&amp;quot;，而是&lt;strong&gt;源码仓库与发行包处于不同状态&lt;/strong&gt;——如果一个读者只照 README 配置，他会写出一个语法正确但完全不生效的 &lt;code&gt;config.toml&lt;/code&gt;（我实测过：写了 &lt;code&gt;policy = &amp;quot;strict&amp;quot;&lt;/code&gt; 和 &lt;code&gt;idle_timeout_seconds = 3.0&lt;/code&gt;，&lt;code&gt;load_config&lt;/code&gt; 正常返回，两个字段被直接丢弃，不报错、不警告）。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;所以本文的写法是：涉及门控策略的部分标注为&amp;quot;&lt;code&gt;main&lt;/code&gt; 分支 / 未发布&amp;quot;，涉及实际可安装行为的部分以 0.8.8 实测为准。&lt;/strong&gt; 这个区分很重要，否则你会照着未来版本的功能去配一个当前版本的包。&lt;/p&gt;&#10;&lt;h2 id="二实测装一遍看它到底什么样"&gt;二、实测：装一遍，看它到底什么样&#10;&lt;/h2&gt;&lt;p&gt;光读 README 写不出可信的文章，所以我在本机装了它。过程本身就有信息量：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python -m venv wmcp_venv&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./wmcp_venv/Scripts/python.exe -m pip install windows-mcp&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 官方源 11 分钟无进展，中止&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 换清华镜像：&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./wmcp_venv/Scripts/python.exe -m pip install &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --index-url https://pypi.tuna.tsinghua.edu.cn/simple windows-mcp&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Downloading windows_mcp-0.8.8-py3-none-any.whl (257 kB)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Successfully installed windows-mcp-0.8.8&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;它只有 257 KB&lt;/strong&gt;。一个能操作整台 Windows 的 MCP server，轮子比一张截图还小——因为重活全压在 &lt;code&gt;comtypes&lt;/code&gt;、&lt;code&gt;pywin32&lt;/code&gt;、&lt;code&gt;fastmcp&lt;/code&gt; 这些上游依赖上。&lt;/p&gt;&#10;&lt;h3 id="21-cli-就四个命令"&gt;2.1 CLI 就四个命令&#10;&lt;/h3&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;Usage: windows-mcp [OPTIONS] COMMAND [ARGS]...&#10;&#10; Windows-MCP: MCP server for Windows desktop automation.&#10;&#10;Commands:&#10; auth Generate an auth key (and optionally TLS certs) and save to...&#10; install Install windows-mcp as a scheduled task that starts at login.&#10; serve&#10; uninstall Remove the windows-mcp scheduled task and stop the...&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;auth&lt;/code&gt; / &lt;code&gt;install&lt;/code&gt; / &lt;code&gt;serve&lt;/code&gt; / &lt;code&gt;uninstall&lt;/code&gt;——没有 &lt;code&gt;doctor&lt;/code&gt;、没有 &lt;code&gt;--version&lt;/code&gt;、没有配置校验子命令。设计上够用，运维上偏薄：配置写错了没有 lint 能查，只能靠启动日志。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;serve&lt;/code&gt; 的选项我逐个看过，有两处跟 README 对不上：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ windows-mcp serve --no-control&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Error: No such option &lt;span style="color:#e6db74"&gt;&amp;#39;--no-control&amp;#39;&lt;/span&gt;.&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ windows-mcp serve --control-policy strict&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Error: No such option &lt;span style="color:#e6db74"&gt;&amp;#39;--control-policy&amp;#39;&lt;/span&gt;.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;--help&lt;/code&gt; 里也没有这两项。这就是前面说的&amp;quot;仓库超前于发行包&amp;quot;的具体表现。&lt;/p&gt;&#10;&lt;h3 id="22-21-个工具实测确认"&gt;2.2 21 个工具，实测确认&#10;&lt;/h3&gt;&lt;p&gt;直接调 FastMCP 的工具注册表，拿到真实列表：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; windows_mcp.__main__ &lt;span style="color:#f92672"&gt;import&lt;/span&gt; _build_mcp&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;m &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _build_mcp()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tools &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; m&lt;span style="color:#f92672"&gt;.&lt;/span&gt;list_tools()&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;COUNT: 21&#10;[&amp;#39;App&amp;#39;, &amp;#39;Click&amp;#39;, &amp;#39;Clipboard&amp;#39;, &amp;#39;ControlStatus&amp;#39;, &amp;#39;DisplayInventory&amp;#39;,&#10; &amp;#39;FileSystem&amp;#39;, &amp;#39;Move&amp;#39;, &amp;#39;MultiEdit&amp;#39;, &amp;#39;MultiSelect&amp;#39;, &amp;#39;Notification&amp;#39;,&#10; &amp;#39;PowerShell&amp;#39;, &amp;#39;Process&amp;#39;, &amp;#39;Registry&amp;#39;, &amp;#39;Scrape&amp;#39;, &amp;#39;Screenshot&amp;#39;, &amp;#39;Scroll&amp;#39;,&#10; &amp;#39;Shortcut&amp;#39;, &amp;#39;Snapshot&amp;#39;, &amp;#39;Type&amp;#39;, &amp;#39;Wait&amp;#39;, &amp;#39;WaitFor&amp;#39;]&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;21 个，跟 &lt;code&gt;CLAUDE.md&lt;/code&gt; 声明的数字一致。&lt;/strong&gt; 顺带把 &lt;code&gt;Snapshot&lt;/code&gt; / &lt;code&gt;Screenshot&lt;/code&gt; 的真实参数也拉了出来，发现两处容易踩的坑：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;坑&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;说明&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;参数名是 &lt;code&gt;width_reference_line&lt;/code&gt;（单数）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;README 正文里写的是 &lt;code&gt;width_reference_lines&lt;/code&gt;（复数），照着写会报 unexpected keyword&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Screenshot&lt;/code&gt; 也支持 &lt;code&gt;use_annotation&lt;/code&gt; 和参考线&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;一般人以为只有 &lt;code&gt;Snapshot&lt;/code&gt; 能标注，其实截图也能&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="23-一个静默失效的配置"&gt;2.3 一个静默失效的配置&#10;&lt;/h3&gt;&lt;p&gt;我写了一份刻意混入未发布字段的配置：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#a6e22e"&gt;control&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;enabled&lt;/span&gt; = &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;policy&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;strict&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;idle_timeout_seconds&lt;/span&gt; = &lt;span style="color:#ae81ff"&gt;3.0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;mouse_takeover_units&lt;/span&gt; = &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;加载结果：&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;control: ControlConfig(mouse_takeover_units=300, mouse_takeover_pixels=120)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;mouse_takeover_units = 300&lt;/code&gt; 生效了，&lt;code&gt;policy&lt;/code&gt; 和 &lt;code&gt;idle_timeout_seconds&lt;/code&gt; &lt;strong&gt;静默消失&lt;/strong&gt;——不报错、不警告、不提示 unknown key。这类行为在配置系统里是最伤人的一种：用户以为自己配了，实际什么都没发生，等到线上发现门控不严，已经很难回溯到是这里。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;能带走的结论&lt;/strong&gt;：用这类工具前，先 &lt;code&gt;pip show windows-mcp&lt;/code&gt; 确认版本，再对着&lt;strong&gt;该 tag 的&lt;/strong&gt; README 配，别对着 &lt;code&gt;main&lt;/code&gt; 的 README 配。&lt;/p&gt;&#10;&lt;h2 id="三真问题视觉猜坐标走不通"&gt;三、真问题：视觉猜坐标走不通&#10;&lt;/h2&gt;&lt;p&gt;要理解这个项目的设计选择，得先看清「截图 + 视觉模型」方案的三笔账：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;成本&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;具体表现&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;Token&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;一张 1920×1080 截图编码后动辄几十万 token，每步都要重发&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;精度&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;模型输出坐标有像素级偏差，密集 UI 上点偏是常态，且难以自纠&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;延迟&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;视觉推理本身就慢，叠加多轮纠错后单任务动辄几十秒&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;适配&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;换个模型、换个分辨率，坐标体系就可能漂移，得重新调 prompt&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Windows-MCP 的答案是把「感知」从像素层抬到语义层：&lt;/p&gt;&#10;&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart LR&#10; subgraph OLD["传统 computer-use"]&#10; A1["截图 PNG"] --&gt; A2["视觉模型&lt;br/&gt;猜坐标 (x, y)"]&#10; A2 --&gt; A3["点击"]&#10; A3 --&gt; A4["再截图验证"]&#10; A4 --&gt; A1&#10; end&#10; subgraph NEW["Windows-MCP"]&#10; B1["UIAutomation&lt;br/&gt;无障碍树"] --&gt; B2["元素 + id + 边界框"]&#10; B2 --&gt; B3["LLM 输出&lt;br/&gt;Click(id=37)"]&#10; B3 --&gt; B4["坐标由 UIA 提供&lt;br/&gt;无像素猜测"]&#10; B4 --&gt; B1&#10; end&#10; style A2 fill:#fff1f0,stroke:#d9534f,color:#8a2b28&#10; style B2 fill:#f0f7ff,stroke:#2f6fb5,color:#1c4570&lt;/pre&gt;&lt;p&gt;关键在于&lt;strong&gt;坐标从哪来&lt;/strong&gt;：传统方案里坐标是模型&amp;quot;猜&amp;quot;的，Windows-MCP 里坐标是 Windows 自己报的——UIA 的 &lt;code&gt;BoundingRectangle&lt;/code&gt; 属性给出每个元素的精确屏幕矩形，中心点直接算出来。模型只需要做一件事：&lt;strong&gt;在列表里挑对那个元素&lt;/strong&gt;。这是分类问题，不是回归问题，难度低一个量级。&lt;/p&gt;&#10;&lt;p&gt;顺带解决的是「视觉模型不可用」场景：本地跑 7B 模型、或者用纯文本模型（DeepSeek、Qwen 之类），一样能干活。README 把这条写成卖点「Use Any LLM (Vision Optional)」，不是营销话术——它的架构里确实没有视觉模型的硬依赖，&lt;code&gt;use_vision&lt;/code&gt; 只是个可选参数。&lt;/p&gt;&#10;&lt;h2 id="四分层架构从-__main__-到-uia"&gt;四、分层架构：从 &lt;code&gt;__main__&lt;/code&gt; 到 &lt;code&gt;uia&lt;/code&gt;&#10;&lt;/h2&gt;&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; 里给了官方分层说明，我按文件树核对了一遍，实际结构比 README 描述的更细：&lt;/p&gt;&#10;&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart TD&#10; A["__main__.py&lt;br/&gt;FastMCP 构建 · CLI 解析 · 传输选择"] --&gt; B["tools/&lt;br/&gt;21 个工具的注册与参数归一化"]&#10; B --&gt; C["desktop/service.py&lt;br/&gt;窗口 · 截图 · 键鼠 · 剪贴板"]&#10; B --&gt; D["tree/service.py&lt;br/&gt;无障碍树抓取与裁剪"]&#10; B --&gt; E["domain services&lt;br/&gt;filesystem / registry /&lt;br/&gt;powershell / process / notifications"]&#10; C --&gt; D&#10; D --&gt; F["uia/&lt;br/&gt;UIAutomation COM 封装"]&#10; D --&gt; G["tree/ia2.py&lt;br/&gt;Firefox MSAA 回退"]&#10; C --&gt; H["vdm/core.py&lt;br/&gt;虚拟桌面归属"]&#10; F --&gt; I["comtypes → Windows UIA COM API"]&#10; style A fill:#1f6feb,stroke:#1a5cbf,color:#ffffff&#10; style F fill:#2ea043,stroke:#237a35,color:#ffffff&#10; style I fill:#6e7681,stroke:#57606a,color:#ffffff&lt;/pre&gt;&lt;p&gt;三层职责分得很干净：&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;工具层（&lt;code&gt;tools/&lt;/code&gt;）&lt;/strong&gt; 每个模块对应一组工具，函数体都很薄——归一化参数、调用 service、返回。所有横切关注点（遥测）由一个 &lt;code&gt;@with_analytics&lt;/code&gt; 装饰器统一处理，不在每个工具里重复。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;桌面服务层（&lt;code&gt;desktop/&lt;/code&gt; + &lt;code&gt;tree/&lt;/code&gt;）&lt;/strong&gt; 是真正干活的地方。&lt;code&gt;desktop/service.py&lt;/code&gt; 管窗口生命周期、截图、键鼠注入；&lt;code&gt;tree/service.py&lt;/code&gt; 管无障碍树；两者通过元素发现逻辑耦合——点一个元素之前，得先在树里找到它。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;UIA 封装层（&lt;code&gt;uia/&lt;/code&gt;）&lt;/strong&gt; 把 COM 那套东西包起来：&lt;code&gt;core.py&lt;/code&gt; 是主自动化对象，&lt;code&gt;controls.py&lt;/code&gt; 是控件类型特化，&lt;code&gt;patterns.py&lt;/code&gt; 包 UIA 的 Pattern（Invoke、Value、Scroll、Text），&lt;code&gt;enums.py&lt;/code&gt; 是 COM 枚举，&lt;code&gt;events.py&lt;/code&gt; 管事件订阅。这层用 &lt;code&gt;comtypes&lt;/code&gt; 而不是 &lt;code&gt;pywin32&lt;/code&gt; 的 UIA 封装，原因是 &lt;code&gt;comtypes&lt;/code&gt; 能动态生成接口定义，对 UIA 这种接口庞大的 COM API 更省事。&lt;/p&gt;&#10;&lt;p&gt;还有个不在 README 主结构里、但确实存在的模块：&lt;strong&gt;&lt;code&gt;vdm/core.py&lt;/code&gt;&lt;/strong&gt;。它用 COM 接口 &lt;code&gt;IVirtualDesktopManager&lt;/code&gt;（CLSID &lt;code&gt;{aa509086-5ca9-4c25-8f95-589d3c07b48a}&lt;/code&gt;）判断某个窗口属于哪个虚拟桌面。这件事看着小，实际上决定了 &lt;code&gt;Snapshot&lt;/code&gt; 会不会把你在另一个虚拟桌面上的窗口也扫进来——如果扫了，元素 id 就全乱了。&lt;/p&gt;&#10;&lt;h2 id="五两条感知路径screenshot-与-snapshot"&gt;五、两条感知路径：Screenshot 与 Snapshot&#10;&lt;/h2&gt;&lt;p&gt;新手最容易踩的坑是把这两个搞混。它们的取舍非常明确：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;code&gt;Screenshot&lt;/code&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;code&gt;Snapshot&lt;/code&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;抓什么&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;图像 + 光标位置 + 窗口列表&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;图像 + 完整可交互元素树 + 可滚动区域&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;走 UI 树吗&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;不走&lt;/strong&gt;（明确跳过）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;走，且是主路径&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;耗时&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;快&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;慢（取决于目标应用的无障碍质量）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Token&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;高（一张图）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;中（文本树，但可能很长）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;适用&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&amp;ldquo;现在屏幕上大概是什么样&amp;rdquo;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&amp;ldquo;我要精确点第 N 个元素&amp;rdquo;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;参数（实测）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;use_annotation&lt;/code&gt;、&lt;code&gt;width_reference_line&lt;/code&gt;、&lt;code&gt;height_reference_line&lt;/code&gt;、&lt;code&gt;display&lt;/code&gt;、&lt;code&gt;region&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;use_vision&lt;/code&gt;、&lt;code&gt;use_dom&lt;/code&gt;、&lt;code&gt;use_annotation&lt;/code&gt;、&lt;code&gt;use_ui_tree&lt;/code&gt;、&lt;code&gt;width_reference_line&lt;/code&gt;、&lt;code&gt;height_reference_line&lt;/code&gt;、&lt;code&gt;display&lt;/code&gt;、&lt;code&gt;region&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;工具描述里写得很直白：&lt;strong&gt;&lt;code&gt;Screenshot&lt;/code&gt; 应该是你主要需要视觉上下文时的默认第一次调用&lt;/strong&gt;。这句话暗含了一个设计判断——大多数时候 agent 并不需要完整元素树，看一眼截图就够决定下一步了，抓树反而是浪费。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;Snapshot&lt;/code&gt; 的参数堆得比较满，几个值得单独说：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;region=[left, top, right, bottom]&lt;/code&gt;&lt;/strong&gt;（虚拟桌面像素坐标）：只抓一块矩形。当你已经知道要看哪里时，这是最省 token 的选项。文档明确写了 &lt;strong&gt;&lt;code&gt;region&lt;/code&gt; 优先于 &lt;code&gt;display&lt;/code&gt;&lt;/strong&gt;，且越界会直接报错而不是静默抓别的地方——这个&amp;quot;报错而不是猜&amp;quot;的选择很重要，静默失败会让 agent 在错误的上下文里继续推理。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;use_annotation&lt;/code&gt;&lt;/strong&gt;（默认 &lt;code&gt;True&lt;/code&gt;）：在检测到的元素上画彩色矩形框。开着它，模型能同时看到图像和标注框，视觉与语义对齐；关掉得到干净截图。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;width_reference_line&lt;/code&gt; / &lt;code&gt;height_reference_line&lt;/code&gt;&lt;/strong&gt;：叠加网格参考线，帮助模型做空间推理。注意参数名是&lt;strong&gt;单数 &lt;code&gt;line&lt;/code&gt;&lt;/strong&gt;，不是 &lt;code&gt;lines&lt;/code&gt;——这种地方照着自己直觉写就会失败。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;use_ui_tree=False&lt;/code&gt;&lt;/strong&gt;：退化成&amp;quot;只要截图不要树&amp;quot;的快速模式。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;use_vision&lt;/code&gt;&lt;/strong&gt;：把截图一并塞进返回结果。默认 &lt;code&gt;False&lt;/code&gt;，因为纯文本模型用不上，白白烧 token。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;&lt;code&gt;Screenshot&lt;/code&gt; 还有个不太显眼但很实用的设计：抓完图会在捕获区域画一圈&lt;strong&gt;橙红色发光边框&lt;/strong&gt;做视觉确认，然后这个边框是在&lt;strong&gt;捕获之后&lt;/strong&gt;画在透明置顶窗口上的，所以永远不会出现在截图里。可以用 &lt;code&gt;WINDOWS_MCP_DISABLE_FLASH=1&lt;/code&gt; 关掉。这个细节透露出作者对&amp;quot;人机共用桌面&amp;quot;场景的考虑——AI 抓图这件事本身，应该让人看得见。&lt;/p&gt;&#10;&lt;h2 id="六树抓取的三个工程约束预算排除线程"&gt;六、树抓取的三个工程约束：预算、排除、线程&#10;&lt;/h2&gt;&lt;p&gt;无障碍树听起来很美，真跑起来会遇到三个硬问题。这个项目为每个问题都留了专门的机制，这是我判断它&amp;quot;工程上认真&amp;quot;的主要依据。&lt;/p&gt;&#10;&lt;h3 id="51-元素预算treebudgetpy"&gt;5.1 元素预算：&lt;code&gt;tree/budget.py&lt;/code&gt;&#10;&lt;/h3&gt;&lt;p&gt;有些应用会通过 UIA 暴露巨大的扁平列表或表格——比如一个没加筛选的库存视图，几千行。遍历并序列化每一行会让目标应用卡死好几分钟，返回的响应也可能大到客户端处理不了。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;TreeElementBudget&lt;/code&gt; 的解法很朴素但很对：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;DEFAULT_MAX_TREE_ELEMENTS &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#10;&lt;li&gt;每次抓取新建一个预算对象（&lt;code&gt;get_state&lt;/code&gt; 里 &lt;code&gt;self.element_budget = TreeElementBudget(...)&lt;/code&gt;），不跨次累积&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;try_consume()&lt;/code&gt; 返回 &lt;code&gt;False&lt;/code&gt; 时调用方停止递归，并标记 &lt;code&gt;truncated = True&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;count&lt;/code&gt; 硬封顶在 &lt;code&gt;limit&lt;/code&gt;——单次传入一个超大的 &lt;code&gt;amount&lt;/code&gt; 也推不过去&lt;/li&gt;&#10;&lt;li&gt;结果里明确标注&amp;quot;已截断&amp;quot;，而不是假装抓全了返回一个残缺的树&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;环境变量 &lt;code&gt;WINDOWS_MCP_MAX_TREE_ELEMENTS&lt;/code&gt; 可调（默认 500），非法值/非正整数会警告并回落到默认值。&lt;strong&gt;这个&amp;quot;截断并声明&amp;quot;的设计比&amp;quot;抓不完就超时失败&amp;quot;好得多&lt;/strong&gt;：agent 拿到一个明确标注&amp;quot;我只看到前 500 个元素&amp;quot;的树，可以决定先筛选再抓；拿一个超时报错，它只能瞎猜。&lt;/p&gt;&#10;&lt;h3 id="52-进程排除windows_mcp_exclude_processes"&gt;5.2 进程排除：&lt;code&gt;WINDOWS_MCP_EXCLUDE_PROCESSES&lt;/code&gt;&#10;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;tree/service.py&lt;/code&gt; 里有个函数叫 &lt;code&gt;_window_process_name&lt;/code&gt;，注释写得很克制：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;Resolves HWND -&amp;gt; PID -&amp;gt; process name using Win32 and psutil only, &lt;strong&gt;never UI Automation&lt;/strong&gt;: an excluded window&amp;rsquo;s accessibility provider must not be entered just to decide whether to skip it.&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;这句话是整个机制的关键。如果要判断一个窗口该不该跳过，而去调用 UIA 查它的进程名，那你就已经进入了那个可能有问题的 accessibility provider——问题没解决，反而先踩了坑。所以它走的是 &lt;code&gt;win32process.GetWindowThreadProcessId&lt;/code&gt; + &lt;code&gt;psutil.Process(pid).name()&lt;/code&gt;，纯 Win32 路径，不碰 UIA。&lt;/p&gt;&#10;&lt;p&gt;配套的使用方式：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;WINDOWS_MCP_EXCLUDE_PROCESSES&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Code.exe,Cursor.exe&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;匹配规则是&lt;strong&gt;大小写不敏感、精确匹配、不支持通配符和正则&lt;/strong&gt;，逗号分隔，空白会被 trim，空项忽略。文档明确说这是 &lt;strong&gt;mitigation 不是完整修复&lt;/strong&gt;（针对 issue #383）——它只跳过你点名的进程，没点名的病态窗口照样能卡住抓取。这种&amp;quot;承认自己只解决了一部分&amp;quot;的表述，比&amp;quot;彻底解决&amp;quot;可信。&lt;/p&gt;&#10;&lt;h3 id="53-线程亲和uia-的-com-单线程陷阱"&gt;5.3 线程亲和：UIA 的 COM 单线程陷阱&#10;&lt;/h3&gt;&lt;p&gt;UIA 是 COM API，有线程套间（apartment）要求。在错误的线程上调 UIA，轻则报错，重则让整个进程挂住。仓库的提交历史里能看到这条线的演进：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;PR&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;内容&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#449&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Run UIA-bound tools on the event-loop thread to preserve COM affinity&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#451&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Run UIA-affine tools on a dedicated worker thread&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;两次改动方向相反，说明第一次尝试（放事件循环线程）在实践中不够——把可能阻塞几秒的 UIA 调用塞进事件循环，会拖住所有并发请求。最终方案是&lt;strong&gt;给 UIA 操作单独开一个 worker 线程&lt;/strong&gt;，用 &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; 做多窗口并行遍历（&lt;code&gt;THREAD_MAX_RETRIES=3&lt;/code&gt; 做重试）。&lt;/p&gt;&#10;&lt;p&gt;代价是文档里那句提醒：&lt;strong&gt;一个长的 &lt;code&gt;WaitFor&lt;/code&gt; 会阻塞其他 UIA 工作&lt;/strong&gt;，因为它们共享同一个 worker。这是个诚实的权衡——COM 亲和性没法既要又要。&lt;/p&gt;&#10;&lt;h2 id="七21-个工具的能力矩阵"&gt;七、21 个工具的能力矩阵&#10;&lt;/h2&gt;&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; 里按组列出了全部 21 个工具，我按&amp;quot;是否需要桌面所有权&amp;quot;重新分了一次组，因为这直接对应后面的门控策略：&lt;/p&gt;&#10;&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart TD&#10; subgraph CAP["感知 Capture"]&#10; C1["Screenshot"]&#10; C2["Snapshot"]&#10; C3["Scrape"]&#10; C4["DisplayInventory"]&#10; end&#10; subgraph INPUT["直接输入 Input"]&#10; I1["Click"]&#10; I2["Type"]&#10; I3["Scroll"]&#10; I4["Move / drag"]&#10; I5["Shortcut"]&#10; I6["MultiSelect"]&#10; I7["MultiEdit"]&#10; end&#10; subgraph TIME["时序 Timing"]&#10; T1["Wait"]&#10; T2["WaitFor"]&#10; end&#10; subgraph SYS["系统 System"]&#10; S1["App"]&#10; S2["PowerShell"]&#10; S3["FileSystem"]&#10; S4["Registry"]&#10; S5["Process"]&#10; S6["Clipboard"]&#10; S7["Notification"]&#10; S8["ControlStatus"]&#10; end&#10; style INPUT fill:#fff1f0,stroke:#d9534f,color:#8a2b28&#10; style CAP fill:#f0f7ff,stroke:#2f6fb5,color:#1c4570&lt;/pre&gt;&lt;p&gt;几个设计上值得单独点出来的：&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Click&lt;/code&gt; 走的是 UIA 坐标系&lt;/strong&gt;，不是原始屏幕像素。&lt;code&gt;CLAUDE.md&lt;/code&gt; 里写明：&amp;ldquo;Mouse/keyboard input uses UIA (same coordinate space as BoundingRectangle; no DPI mismatch)&amp;quot;。这一条解决了 Windows 高分屏最常见的坑——150% 缩放下，原始 Win32 坐标和 UIA 坐标会差一个缩放因子，混用就点偏。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Type&lt;/code&gt; 必须绑定元素&lt;/strong&gt;，不是&amp;quot;往当前焦点打字&amp;rdquo;。它需要知道目标元素，因为要处理&amp;quot;是否先清空原内容&amp;quot;这类逻辑。这也解释了它的限制：它面向文本框，不适合在 IDE 里写代码——会把整段程序一次性灌进文件，而不是逐行编辑。作者把这条明确写进 Limitations，还算清醒。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Move&lt;/code&gt; 支持确定性拖拽&lt;/strong&gt;：&lt;code&gt;drag=True&lt;/code&gt; + &lt;code&gt;from_loc=[x, y]&lt;/code&gt;，在一次工具调用里完成&amp;quot;在起点按下 → 移动到 &lt;code&gt;loc&lt;/code&gt; → 松开&amp;quot;。如果只给 &lt;code&gt;drag=True&lt;/code&gt; 不给 &lt;code&gt;from_loc&lt;/code&gt;，起点就是当前光标位置——那就不确定了。&lt;code&gt;duration&lt;/code&gt; 参数加中间帧，用于需要触发 hover 或拖拽预览的场景。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&lt;code&gt;WaitFor&lt;/code&gt; 比 &lt;code&gt;Wait&lt;/code&gt; 值钱得多&lt;/strong&gt;。&lt;code&gt;Wait&lt;/code&gt; 是死等固定时长，&lt;code&gt;WaitFor&lt;/code&gt; 是在一次调用内&lt;strong&gt;轮询&lt;/strong&gt;直到文本/活动窗口/元素/焦点元素出现。前者是赌博，后者是同步原语。异步 UI 场景下用错这个，整个任务的成功率会差一个档次。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Scrape&lt;/code&gt; 内置 SSRF 防护&lt;/strong&gt;，封私网 IP、回环、link-local、URL 内嵌凭据、非 HTTP scheme。最近还加了 Fake-IP DNS 的诊断（#473）——这是针对 Clash/mihomo 那类 Fake-IP 模式做的适配，因为在那类环境下 DNS 解析返回的是假 IP，SSRF 判断会误伤。这个细节说明作者在自己踩过。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Registry&lt;/code&gt; 是通过 PowerShell cmdlet 实现的&lt;/strong&gt;，不是直接调 Win32 注册表 API。&lt;code&gt;CLAUDE.md&lt;/code&gt; 里承认了后果：&amp;ldquo;their latency is dominated by process startup&amp;rdquo;。所以它慢，一次注册表读可能要几百毫秒。这是个用简单换性能的取舍。&lt;/p&gt;&#10;&lt;h2 id="八dom-模式浏览器自动化的特殊通道"&gt;八、DOM 模式：浏览器自动化的特殊通道&#10;&lt;/h2&gt;&lt;p&gt;&lt;code&gt;use_dom=True&lt;/code&gt; 是 &lt;code&gt;Snapshot&lt;/code&gt; 的一个模式开关，但它值得单独一节，因为它解决的是一个完全不同的问题。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：抓浏览器窗口的无障碍树，你会拿到一大堆浏览器外壳元素——地址栏、书签栏、扩展图标、标签页、开发者工具按钮。真正想操作的网页内容被埋在里面，agent 很容易点错。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;解法&lt;/strong&gt;：DOM 模式只抓网页内容区域，过滤掉浏览器 chrome。实现上靠找 &lt;code&gt;AutomationId == &amp;quot;RootWebArea&amp;quot;&lt;/code&gt; 的元素，那是 Chrome/Edge 的约定标记，找到它就以此为根往下遍历。&lt;/p&gt;&#10;&lt;p&gt;Firefox 是个例外，&lt;code&gt;tree/ia2.py&lt;/code&gt; 的模块注释把原因写得很清楚：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;Firefox exposes its browser chrome via UIA but its web DOM only via MSAA / IAccessible2. The UIA traversal looks for an element with &lt;code&gt;AutomationId == &amp;quot;RootWebArea&amp;quot;&lt;/code&gt; (the Chrome / Edge convention) — Firefox has no such marker, so DOM extraction is skipped entirely.&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;所以 Firefox 走的是完全独立的路径：用 &lt;code&gt;AccessibleObjectFromWindow&lt;/code&gt;（&lt;code&gt;oleacc.dll&lt;/code&gt;）拿到根 &lt;code&gt;IAccessible&lt;/code&gt;，然后走 MSAA/IA2 树遍历，最后把结果转成和 UIA 路径相同的结构（&lt;code&gt;dom_informative_nodes&lt;/code&gt; / &lt;code&gt;dom_interactive_nodes&lt;/code&gt; / &lt;code&gt;dom_bounding_box&lt;/code&gt;）。&lt;strong&gt;输出同构、输入双路&lt;/strong&gt;，这个设计让上层的 &lt;code&gt;Snapshot&lt;/code&gt; 不需要知道底下用的是哪套 API。&lt;/p&gt;&#10;&lt;p&gt;这条路径的实际价值：如果你要用 agent 做 Web 应用的 E2E 测试，DOM 模式比纯视觉方案稳定得多——网页元素有稳定的文本和角色，不像截图那样受渲染影响。&lt;/p&gt;&#10;&lt;h2 id="九桌面租约当-ai-和你抢同一只鼠标"&gt;九、桌面租约：当 AI 和你抢同一只鼠标&#10;&lt;/h2&gt;&lt;p&gt;这是整个项目里我最感兴趣的部分，也是它跟普通自动化库最本质的区别。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;普通自动化库的假设&lt;/strong&gt;：机器是我的，我独占。&lt;strong&gt;Windows-MCP 的假设&lt;/strong&gt;：人也在用这台机器，随时可能回来。&lt;/p&gt;&#10;&lt;p&gt;于是它引入了一个「桌面所有权租约」（desktop lease）的概念。逻辑在 &lt;code&gt;desktop/control.py&lt;/code&gt; 的 &lt;code&gt;ControlCoordinator&lt;/code&gt; 里，核心状态机：&lt;/p&gt;&#10;&lt;pre class="mermaid" style="visibility:hidden"&gt;stateDiagram-v2&#10; [*] --&gt; user: 启动&#10; user --&gt; ai: 工具调用申请租约&lt;br/&gt;且用户空闲 &gt; 10 秒&#10; ai --&gt; ai: 连续工具调用续租&#10; ai --&gt; takeover_pending: 检测到物理鼠标位移&lt;br/&gt;达阈值（120 / 500）&#10; ai --&gt; takeover_pending: Ctrl+Alt+Shift+Backspace&#10; takeover_pending --&gt; user: 释放注入按键&lt;br/&gt;停止后续步骤&#10; ai --&gt; user: 租约到期（默认 10 秒无操作）&#10; user --&gt; ai: 用户静止 &gt; 10 秒后新的工具调用&#10; note right of ai&#10; 可见边缘 + 光标光晕提示&#10; 截图期间临时隐藏&#10; end note&lt;/pre&gt;&lt;h3 id="81-门控范围088-是全量main-才分级"&gt;8.1 门控范围：0.8.8 是&amp;quot;全量&amp;quot;，main 才分级&#10;&lt;/h3&gt;&lt;p&gt;先说&lt;strong&gt;已发布的 0.8.8&lt;/strong&gt;：门控是&lt;strong&gt;全量&lt;/strong&gt;的。&lt;code&gt;ControlToolGate.on_call_tool&lt;/code&gt; 里只有一个例外：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; name &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;ControlStatus&amp;#34;&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; call_next(context)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;除此之外所有工具都要先拿租约。也就是说，在 0.8.8 上，哪怕你只是想读一个文件、抓一张截图，只要 AI 当前不持有桌面，调用就会被 &lt;code&gt;USER_CONTROL&lt;/code&gt; 挡回去。这在实际使用里很烦——用户在看屏幕，agent 连&amp;quot;看一眼现在什么情况&amp;quot;都做不到。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;main&lt;/code&gt; 分支（&lt;strong&gt;未发布&lt;/strong&gt;）改成按工具分级，逻辑在 &lt;code&gt;tools/control_policy.py&lt;/code&gt;：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;策略&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;需要所有权的操作&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;&lt;code&gt;relaxed&lt;/code&gt;（A，默认）&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;直接输入：&lt;code&gt;Click&lt;/code&gt;、&lt;code&gt;Type&lt;/code&gt;、&lt;code&gt;Scroll&lt;/code&gt;、&lt;code&gt;Move&lt;/code&gt;、&lt;code&gt;Shortcut&lt;/code&gt;、&lt;code&gt;MultiSelect&lt;/code&gt;、&lt;code&gt;MultiEdit&lt;/code&gt;；&lt;code&gt;App&lt;/code&gt; 的 &lt;code&gt;switch&lt;/code&gt; 和 &lt;code&gt;resize&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;&lt;code&gt;balanced&lt;/code&gt;（B）&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;A 的全部，加上 &lt;code&gt;PowerShell&lt;/code&gt;、&lt;code&gt;Notification&lt;/code&gt;、&lt;code&gt;App&lt;/code&gt; 的 &lt;code&gt;launch&lt;/code&gt;/&lt;code&gt;launch_executable&lt;/code&gt;、&lt;code&gt;Process&lt;/code&gt; 的 &lt;code&gt;kill&lt;/code&gt;、&lt;code&gt;Clipboard&lt;/code&gt; 的 &lt;code&gt;set&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;&lt;code&gt;strict&lt;/code&gt;（C）&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;除 &lt;code&gt;ControlStatus&lt;/code&gt; 之外全部（等价于 0.8.8 的现有行为）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;&lt;strong&gt;在 A 和 B 下完全绕过所有权&lt;/strong&gt;的工具：&lt;code&gt;FileSystem&lt;/code&gt;、&lt;code&gt;Registry&lt;/code&gt;、&lt;code&gt;Snapshot&lt;/code&gt;、&lt;code&gt;Screenshot&lt;/code&gt;、&lt;code&gt;Scrape&lt;/code&gt;、&lt;code&gt;Wait&lt;/code&gt;、&lt;code&gt;WaitFor&lt;/code&gt;、&lt;code&gt;DisplayInventory&lt;/code&gt;、&lt;code&gt;Process&lt;/code&gt; 的 &lt;code&gt;list&lt;/code&gt;、&lt;code&gt;Clipboard&lt;/code&gt; 的 &lt;code&gt;get&lt;/code&gt;。&lt;/p&gt;&#10;&lt;p&gt;这个分类的依据是&lt;strong&gt;桌面干扰性，不是危险性&lt;/strong&gt;。代码注释把这点写死了：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Categories describe desktop interference, not whether an operation is destructive.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个区分很重要。&amp;ldquo;危险&amp;quot;和&amp;quot;抢鼠标&amp;quot;是两个正交的维度。删文件很危险但不抢鼠标；点一下几乎无害但会抢焦点。混淆这两件事，就会得到一套既烦人又不安全的分级。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;requires_control()&lt;/code&gt; 的兜底逻辑也值得看：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;category &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;direct&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Unknown tools/modes must not silently bypass ownership.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;未知的工具或模式默认走最严的那一档&lt;/strong&gt;。新加工具如果忘了在表里登记，不会悄悄绕过门控，而是会被保守地当成直接输入。配套的是&amp;quot;注册覆盖率测试&amp;rdquo;——漏登记会被测试发现。&lt;/p&gt;&#10;&lt;h3 id="82-空闲超时的两个坑main-分支"&gt;8.2 空闲超时的两个坑（&lt;code&gt;main&lt;/code&gt; 分支）&#10;&lt;/h3&gt;&lt;p&gt;配置在 &lt;code&gt;~/.windows-mcp/config.toml&lt;/code&gt;：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#a6e22e"&gt;control&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;enabled&lt;/span&gt; = &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;policy&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;relaxed&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;idle_timeout_seconds&lt;/span&gt; = &lt;span style="color:#ae81ff"&gt;10.0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;文档里有两句必须读清楚的话：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&lt;strong&gt;&amp;ldquo;zero does not disable control&amp;rdquo;&lt;/strong&gt; —— 写 0 不是关闭，0 会被拒绝（只接受&lt;strong&gt;有限正数&lt;/strong&gt;，可以是小数）。要关得用 &lt;code&gt;enabled = false&lt;/code&gt; 或 &lt;code&gt;--no-control&lt;/code&gt;。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&amp;ldquo;shortening it does not allow gated calls while you keep using the computer&amp;rdquo;&lt;/strong&gt; —— 空闲计时器&lt;strong&gt;随物理输入重置&lt;/strong&gt;。你把超时从 10 秒改成 3 秒，并不能让你一边打字一边让 AI 操作；只要你在动，计时器就一直归零。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;⚠️ &lt;strong&gt;这条对当前发布版有个陷阱&lt;/strong&gt;：0.8.8 的 &lt;code&gt;ControlConfig&lt;/code&gt; 只有两个字段——&lt;code&gt;mouse_takeover_units&lt;/code&gt; 和 &lt;code&gt;mouse_takeover_pixels&lt;/code&gt;，没有 &lt;code&gt;policy&lt;/code&gt;、&lt;code&gt;enabled&lt;/code&gt;、&lt;code&gt;idle_timeout_seconds&lt;/code&gt;。我实测写入这三项，&lt;code&gt;load_config&lt;/code&gt; 正常返回、不报错、不警告，值全部丢失：&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;control: ControlConfig(mouse_takeover_units=300, mouse_takeover_pixels=120)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;输入里的 &lt;code&gt;policy&lt;/code&gt; 和 &lt;code&gt;idle_timeout_seconds&lt;/code&gt; 就这么消失了。&lt;/strong&gt; 这类&amp;quot;配置写了但不生效&amp;quot;的问题最难查——你没有报错可看，只能靠观察行为发现不对劲。&lt;/p&gt;&#10;&lt;p&gt;更实际的问题是：&lt;strong&gt;0.8.8 上没有任何关闭控制的途径&lt;/strong&gt;。&lt;code&gt;--no-control&lt;/code&gt; 不存在，&lt;code&gt;[control] enabled = false&lt;/code&gt; 不被识别，&lt;code&gt;WINDOWS_MCP_CONTROL&lt;/code&gt; 环境变量也没实现。门控是硬编码常开的。如果你需要一个&amp;quot;不要管我，随便操作&amp;quot;的环境（比如全自动回归测试的虚拟机），当前发布版给不了。&lt;/p&gt;&#10;&lt;p&gt;还有个操作细节：&lt;strong&gt;改完配置必须重启 server，没有热重载，也没有让 AI 自己改配置的工具&lt;/strong&gt;。这是有意为之——如果 AI 能改自己的门控策略，那门控就没意义了。&lt;/p&gt;&#10;&lt;h2 id="十物理输入接管与输入账本"&gt;十、物理输入接管与输入账本&#10;&lt;/h2&gt;&lt;p&gt;门控解决的是&amp;quot;该不该让 AI 动手&amp;quot;，接管解决的是&amp;quot;AI 动手时人回来了怎么办&amp;quot;。&lt;/p&gt;&#10;&lt;h3 id="91-双通道接管"&gt;9.1 双通道接管&#10;&lt;/h3&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;通道&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;触发条件&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;细节&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;鼠标&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;累计相对原始位移达阈值&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0.8.8：&lt;strong&gt;120&lt;/strong&gt;；&lt;code&gt;main&lt;/code&gt;：&lt;strong&gt;500&lt;/strong&gt;（可在 &lt;code&gt;control.mouse_takeover_units&lt;/code&gt; 调 40–2000）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;键盘&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Ctrl+Alt+Shift+Backspace&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;给不提供可识别相对原始输入的设备（部分触摸板、远程输入设备）留的后路&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;⚠️ 阈值在 0.8.8 和 &lt;code&gt;main&lt;/code&gt; 上差了 4 倍多。120 设备单位大约是鼠标快速甩一下的量，实际体感是&amp;quot;很容易误触发&amp;quot;；500 才更接近&amp;quot;蓄意夺回&amp;quot;。如果你在 0.8.8 上觉得 AI 稍微一动就被踢出控制，这是原因。&lt;/p&gt;&#10;&lt;p&gt;鼠标这条的实现有几个精确到&amp;quot;设备单位&amp;quot;的细节：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;是设备单位，不是屏幕像素&lt;/strong&gt;。&lt;code&gt;control_hooks.py&lt;/code&gt; 里累加的是 &lt;code&gt;_read_raw_mouse()&lt;/code&gt; 读到的原始相对位移，绕过指针加速和 DPI 缩放。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;累加方式在 0.8.8 和 &lt;code&gt;main&lt;/code&gt; 上不同&lt;/strong&gt;。0.8.8 是 &lt;code&gt;math.hypot(x, y)&lt;/code&gt; 算&lt;strong&gt;净位移&lt;/strong&gt;——你往右移 300 再往左移 300，累计净位移回到原点，&lt;strong&gt;永远触发不了&lt;/strong&gt;。&lt;code&gt;main&lt;/code&gt; 改成 &lt;code&gt;self._raw_distance += math.hypot(dx, dy)&lt;/code&gt;，逐次累加&lt;strong&gt;路径长度&lt;/strong&gt;，来回移动也计入：&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (dx &lt;span style="color:#f92672"&gt;or&lt;/span&gt; dy) &lt;span style="color:#f92672"&gt;and&lt;/span&gt; stamp &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_motion_since:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; device &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_raw_device &lt;span style="color:#f92672"&gt;or&lt;/span&gt; stamp &lt;span style="color:#f92672"&gt;-&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_last_move &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_raw_distance &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_last_move &lt;span style="color:#f92672"&gt;=&lt;/span&gt; stamp&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_raw_distance &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; math&lt;span style="color:#f92672"&gt;.&lt;/span&gt;hypot(dx, dy)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_raw_distance &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mouse_takeover_units:&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个改动修的是一个真实的坑：净位移算法下，如果你把鼠标&amp;quot;来回抖&amp;quot;，AI 控制永远夺不回来。&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;0.5 秒无移动就分段清零&lt;/strong&gt;（&lt;code&gt;main&lt;/code&gt;）：所以你不能慢慢磨够阈值——得在连续动作里达到。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;切换设备或开启新的控制周期也会清零&lt;/strong&gt;。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;关于阈值的语义：README 说这是&amp;quot;deliberate takeover&amp;quot;（蓄意接管）的门槛，不是&amp;quot;任何移动&amp;quot;。但 120 这个默认值跟&amp;quot;蓄意&amp;quot;这个词是不匹配的——它更像&amp;quot;检测到明显移动&amp;quot;。这一点上 &lt;code&gt;main&lt;/code&gt; 的 500 更名副其实。&lt;/p&gt;&#10;&lt;h3 id="92-输入账本control_ledgerpy"&gt;9.2 输入账本：&lt;code&gt;control_ledger.py&lt;/code&gt;&#10;&lt;/h3&gt;&lt;p&gt;这是我认为整个项目里工程质量最高的一段代码。问题场景很尖锐：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;AI 按下了 Ctrl 键，还没松开，用户接管了。这时 Ctrl 处于物理按下状态。如果 AI 直接发一个 Ctrl-up，会跟用户真实按下的 Ctrl-up 冲突——用户按的键被&amp;quot;偷&amp;quot;了。&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;&lt;code&gt;InputLedger&lt;/code&gt; 的解法：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;press&lt;/span&gt;(self, name, send_down, send_up, physical_down_delivered):&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_lock:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_blocked:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; InputUnavailable(&lt;span style="color:#e6db74"&gt;&amp;#34;AI input is unavailable&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; name &lt;span style="color:#f92672"&gt;in&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_holds:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;RuntimeError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;AI input already held: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;name&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_holds[name] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (send_up, physical_down_delivered)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Keep the lock through injection so a watchdog cannot send&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# up before the matching down has actually reached Windows.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; send_down()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;BaseException&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;release(name)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Exception&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;pass&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Keep the entry for shutdown&amp;#39;s second attempt.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;三个关键决策：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&lt;strong&gt;注入前先记账&lt;/strong&gt;。&lt;code&gt;self._holds[name] = ...&lt;/code&gt; 在 &lt;code&gt;send_down()&lt;/code&gt; 之前。注释解释了原因：发送例程可能注入成功后抛异常，如果先注入后记账，这个&amp;quot;已按下&amp;quot;的状态就丢了，之后永远不会被释放——Ctrl 会卡在按下状态。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;持锁穿越注入&lt;/strong&gt;。不能&amp;quot;先放锁再注入&amp;quot;，否则看门狗可能在 down 到达 Windows 之前就发出 up，顺序错乱。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;release()&lt;/code&gt; 检查物理键是否已送达&lt;/strong&gt;：&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; physical_down_delivered():&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; send_up()&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果用户在接管后又按了同一个键，那个键的 up 事件会由用户自己产生，此时 AI 再补一个 up 就会把用户的按键状态搞乱。所以只在&amp;quot;物理按键没送达&amp;quot;时才补发 up。&lt;/p&gt;&#10;&lt;p&gt;这段代码处理的是一类很容易被忽略的 bug：&lt;strong&gt;注入式输入和物理输入共享同一个键盘状态机&lt;/strong&gt;。任何做 RPA 或远程控制的项目都会撞上，但大多数选择忽略，代价是偶发的&amp;quot;按键卡住&amp;quot;。这里是正面解决。&lt;/p&gt;&#10;&lt;h3 id="93-但接管不是安全边界"&gt;9.3 但接管不是安全边界&#10;&lt;/h3&gt;&lt;p&gt;README 有一段话说得很不客气，我原样转述其要点：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;接管是 &lt;strong&gt;best-effort&lt;/strong&gt;，&lt;strong&gt;不是安全边界&lt;/strong&gt;，也不是&amp;quot;独占控制&amp;quot;的保证&lt;/li&gt;&#10;&lt;li&gt;Windows 可能&lt;strong&gt;静默摘除&lt;/strong&gt;低级键盘钩子且不通知进程；周期性重建钩子能缩小但不能消除这个窗口&lt;/li&gt;&#10;&lt;li&gt;已经被触发的外部命令（比如 &lt;code&gt;PowerShell&lt;/code&gt;、&lt;code&gt;App&lt;/code&gt; 启动）&lt;strong&gt;不会被撤销&lt;/strong&gt;，门控不杀进程、不回滚已完成的副作用&lt;/li&gt;&#10;&lt;li&gt;钩子故障时注入的按键是&lt;strong&gt;异步释放&lt;/strong&gt;的，所以物理输入可能短暂地与仍被 AI 按住的键重叠&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;一个项目主动把自己的安全机制说成这样，不是自曝其短，是划清责任边界。&lt;strong&gt;把 best-effort 说成 guarantee 才是问题&lt;/strong&gt;。用户读到这段，就知道不能靠它做无人值守的高危操作。&lt;/p&gt;&#10;&lt;h2 id="十一安全面默认关闭的那些开关"&gt;十一、安全面：默认关闭的那些开关&#10;&lt;/h2&gt;&lt;p&gt;这个工具的默认姿态是&amp;quot;本地、单用户、不加防护&amp;quot;，这从设计上是合理的——stdio 传输本来就不出本机。但一旦你把它开到网络上，风险等级就完全不同了。它的安全选项设计得比较讲究，核心思路是&lt;strong&gt;默认最严、显式放开&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;h3 id="101-cors-默认不发光任何头"&gt;10.1 CORS 默认不发光任何头&#10;&lt;/h3&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;By default, no CORS headers are emitted.&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;配合 &lt;code&gt;TrustedHostMiddleware&lt;/code&gt; 做 Host 头校验（DNS rebinding 防护），效果是：即使 server 跑在 &lt;code&gt;localhost:8000&lt;/code&gt;，任意网页也&lt;strong&gt;打不到&lt;/strong&gt;它。因为浏览器同源策略会拦掉跨域请求，而 server 不返回 CORS 头，浏览器就不会放行。&lt;/p&gt;&#10;&lt;p&gt;这条对应一个已修复的漏洞 &lt;strong&gt;GHSA-vrxg-gm77-7q5g&lt;/strong&gt;（2026-05-14）。修复前的代码发了 &lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt;，意味着你随便打开一个恶意网页，页面里的 JS 就能连上你本机的 MCP server 然后为所欲为。&lt;strong&gt;这是一个把本地工具变成 RCE 通道的经典模式&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;需要浏览器客户端直连时，必须显式白名单：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;windows-mcp serve --cors-origins &lt;span style="color:#e6db74"&gt;&amp;#34;https://my-client.example.com&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;只有列表里的源能收到 &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt;，其余跨域请求由浏览器拦掉。&lt;/p&gt;&#10;&lt;h3 id="102-oauth-20--pkce-的克制实现"&gt;10.2 OAuth 2.0 + PKCE 的克制实现&#10;&lt;/h3&gt;&lt;p&gt;仅 &lt;code&gt;streamable-http&lt;/code&gt; 支持，暴露四个端点：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;端点&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;作用&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;GET /.well-known/oauth-authorization-server&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;RFC 8414 服务器元数据&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;GET /oauth/authorize&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Authorization Code 流程，&lt;strong&gt;强制 S256&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;POST /oauth/token&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;换 token，需要 client secret&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;POST /oauth/register&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;禁用&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;三个克制之处：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;强制 S256&lt;/strong&gt;，不接受 &lt;code&gt;plain&lt;/code&gt;。PKCE 用 plain 等于没用。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;禁用动态注册&lt;/strong&gt;。client 必须预先配置。这砍掉了 OAuth 生态里最容易被滥用的环节——开放式注册等于任何人都能给自己发一个 client。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;redirect URI 只允许 loopback&lt;/strong&gt;。防止 token 被导到外部地址。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;&lt;code&gt;auth_key&lt;/code&gt; 和 OAuth 可以共存，两者都被接受为有效的 Bearer token。这个设计方便迁移——先上静态 key，逐步切到 OAuth。&lt;/p&gt;&#10;&lt;h3 id="103-auth-辅助命令"&gt;10.3 &lt;code&gt;auth&lt;/code&gt; 辅助命令&#10;&lt;/h3&gt;&lt;p&gt;手工配一套 auth + TLS 挺烦，所以有个一键命令：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;windows-mcp auth --transport streamable-http --host 0.0.0.0 --port &lt;span style="color:#ae81ff"&gt;8000&lt;/span&gt; --with-tls&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;它会生成 32 字节的 &lt;code&gt;secrets.token_urlsafe&lt;/code&gt; 作为 key，写进 &lt;code&gt;~/.windows-mcp/config.toml&lt;/code&gt;，用 OpenSSL 生成自签证书（RSA 4096，365 天），最后打印对应传输方式的客户端配置示例。省掉了最容易出错的几个环节。&lt;/p&gt;&#10;&lt;h3 id="104-配置优先级"&gt;10.4 配置优先级&#10;&lt;/h3&gt;&lt;p&gt;四层，从高到低：&lt;/p&gt;&#10;&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart LR&#10; A["CLI 参数"] --&gt; B["环境变量"] --&gt; C["config.toml"] --&gt; D["内置默认"]&#10; style A fill:#1f6feb,stroke:#1a5cbf,color:#ffffff&#10; style D fill:#6e7681,stroke:#57606a,color:#ffffff&lt;/pre&gt;&lt;p&gt;这个顺序符合惯例，但有个细节值得注意：&lt;strong&gt;证书路径相对于 &lt;code&gt;~/.windows-mcp/&lt;/code&gt; 解析&lt;/strong&gt;，不是当前工作目录。所以配置文件里写 &lt;code&gt;ssl_certfile = &amp;quot;cert.pem&amp;quot;&lt;/code&gt;，实际指向 &lt;code&gt;~/.windows-mcp/cert.pem&lt;/code&gt;，跟你从哪个目录启动无关。这类&amp;quot;相对谁的路径&amp;quot;问题，是配置系统里最容易埋坑的地方。&lt;/p&gt;&#10;&lt;h3 id="105-遥测"&gt;10.5 遥测&#10;&lt;/h3&gt;&lt;p&gt;默认开启 PostHog 匿名遥测，文档声称不收集个人信息、工具参数、输出。关闭方式：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;env&amp;#34;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; { &lt;span style="color:#f92672"&gt;&amp;#34;ANONYMIZED_TELEMETRY&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;false&amp;#34;&lt;/span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;POSTHOG_API_KEY&lt;/code&gt; 设成空字符串可以完全跳过 PostHog 客户端初始化，&lt;code&gt;POSTHOG_HOST&lt;/code&gt; 可以指向自建实例。对一个能操作整个系统的工具来说，&amp;ldquo;默认开遥测&amp;quot;这个选择会让一部分企业用户直接放弃——但至少开关给得很完整。&lt;/p&gt;&#10;&lt;h2 id="十二发布工程四处版本号与一道守卫"&gt;十二、发布工程：四处版本号与一道守卫&#10;&lt;/h2&gt;&lt;p&gt;这部分跟功能无关，但能看出团队的工程成熟度。&lt;/p&gt;&#10;&lt;p&gt;版本号在&lt;strong&gt;四个地方&lt;/strong&gt;声明，分别供给不同渠道：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;文件&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;消费方&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;pyproject.toml&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;PyPI 包&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;uv.lock&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;锁定的工作区成员&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;manifest.json&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Claude Desktop 扩展&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;server.json&lt;/code&gt; 的 &lt;code&gt;packages[]&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;MCP Registry&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;&lt;code&gt;scripts/check_versions.py&lt;/code&gt; 的模块注释把动机讲成了一个事故复盘：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;At the v0.8.5 release &lt;code&gt;manifest.json&lt;/code&gt; and &lt;code&gt;server.json&lt;/code&gt;&amp;rsquo;s package entry were both still on 0.8.1, two releases behind &lt;code&gt;pyproject.toml&lt;/code&gt;, because the 0.8.2 bump touched only &lt;code&gt;pyproject.toml&lt;/code&gt;. &lt;strong&gt;Nothing failed&lt;/strong&gt; — the extension simply kept advertising a version that was no longer what shipped.&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;这正是典型的静默漂移：没有任何测试失败，没有任何报警，只是扩展商店里显示的版本号慢慢变成假的。CI 现在在 &lt;code&gt;uv build&lt;/code&gt; 之前强制跑这个脚本，不一致就阻断发布。&lt;/p&gt;&#10;&lt;p&gt;还有个更细的判断：&lt;code&gt;server.json&lt;/code&gt; 的&lt;strong&gt;顶层&lt;/strong&gt; &lt;code&gt;version&lt;/code&gt; 故意不检查。注释解释：那个字段标识的是 registry 条目本身，走独立的 1.x 线，强行对齐等于把 registry 降级。只检查 &lt;code&gt;packages[].version&lt;/code&gt;——那个才必须跟 PyPI 一致。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&amp;ldquo;检查该检查的，放过不该检查的&amp;rdquo;&lt;/strong&gt;，这个取舍比&amp;quot;全都对齐&amp;quot;更难做对。&lt;/p&gt;&#10;&lt;h2 id="十三它自己承认的边界"&gt;十三、它自己承认的边界&#10;&lt;/h2&gt;&lt;p&gt;README 的 Limitations 只有三条，但每条都很实在：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;限制&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;原因&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;状态&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;无法选中段落内的局部文本&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;依赖 a11y tree，树只到段/节点粒度&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;开发中&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Type&lt;/code&gt; 不适合在 IDE 写代码&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;把整段程序一次性键入文件，不逐行编辑&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;开发中&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;不能用来打电子游戏&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;实时性 + 反作弊 + 输入模式都不匹配&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;无计划&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;第一条影响比看起来大：&lt;strong&gt;任何&amp;quot;选中这句话然后改写&amp;quot;的需求都做不了&lt;/strong&gt;。文本编辑器、文档工具、代码审查场景都会撞上。这是从像素层抬到语义层的代价——你换来了确定性，失去了像素级的自由度。&lt;/p&gt;&#10;&lt;p&gt;还有几条散落在其他章节、容易被忽略的：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;UIA 共享一个 worker&lt;/strong&gt;：长的 &lt;code&gt;WaitFor&lt;/code&gt; 会拖住其他 UIA 操作&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;Registry&lt;/code&gt; 通过 PowerShell 实现&lt;/strong&gt;：延迟由进程启动主导&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Playwright 类能力缺失&lt;/strong&gt;：DOM 模式只做提取，不做导航/等待/断言，不是浏览器测试框架替代品&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;WINDOWS_MCP_WATCHDOG&lt;/code&gt; 默认关闭&lt;/strong&gt;：因为它的 UIA 事件订阅在长时间运行后（跨睡眠/恢复、会话切换）可能导致 server 崩溃（#332）。官方态度是：默认别开，需要调试再开。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;还有两条是本文实测才发现的，&lt;strong&gt;README 不会告诉你&lt;/strong&gt;：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;main&lt;/code&gt; 与 PyPI 不同步&lt;/strong&gt;：当前 0.8.8 缺三档门控、缺 &lt;code&gt;--no-control&lt;/code&gt;、缺 &lt;code&gt;idle_timeout&lt;/code&gt; 配置。照 README 配会得到一份&amp;quot;看起来对、实际没生效&amp;quot;的配置。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;0.8.8 无法关闭控制门&lt;/strong&gt;：所有工具都要过租约，除了 &lt;code&gt;ControlStatus&lt;/code&gt;。没有环境变量、没有 CLI、没有配置能把它关掉。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;参数名陷阱&lt;/strong&gt;：&lt;code&gt;width_reference_line&lt;/code&gt; 是单数 &lt;code&gt;line&lt;/code&gt;，README 正文写作复数 &lt;code&gt;lines&lt;/code&gt;。这类不一致在快速迭代的项目里很常见，但足以让一次调用直接失败。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="十四什么时候该用它什么时候不该"&gt;十四、什么时候该用它，什么时候不该&#10;&lt;/h2&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;场景&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;判断&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;理由&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;把本地 Windows 应用接进 agent 工作流&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;✅ 合适&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;这是它唯一的设计目标，也是最强的地方&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;用文本模型做桌面自动化（无视觉）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;✅ 合适&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;21 个工具都不依赖视觉，&lt;code&gt;use_vision&lt;/code&gt; 可选&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Web 应用 E2E 测试&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;⚠️ 谨慎&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;DOM 模式能做元素提取，但没有断言/等待/录制，不如 Playwright&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;无人值守的高危操作（改注册表、批量删文件）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;❌ 不合适&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;无沙箱、无审计、无回滚，接管只是 best-effort&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;实时性要求高的交互（游戏、音视频编辑）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;❌ 不合适&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0.2–0.5s 的动作延迟是数量级不匹配&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;需要精确文本选区编辑的任务&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;❌ 暂不合适&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;a11y tree 到不了字符粒度&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;多用户共享的 Windows Server&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;❌ 不合适&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;设计假设是单用户本地桌面&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;关于&amp;quot;全系统权限&amp;quot;这条，&lt;code&gt;CLAUDE.md&lt;/code&gt; 写得很直接：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;This server has &lt;strong&gt;full system access&lt;/strong&gt; with no sandboxing. &lt;code&gt;PowerShell&lt;/code&gt;, &lt;code&gt;FileSystem&lt;/code&gt;, &lt;code&gt;Registry&lt;/code&gt;, &lt;code&gt;Process&lt;/code&gt;, and &lt;code&gt;App&lt;/code&gt; can all perform irreversible operations, and there is no audit log or rollback. The recommended deployment target is a VM or Windows Sandbox.&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;&lt;strong&gt;推荐部署目标是虚拟机或 Windows Sandbox&lt;/strong&gt;——这是项目自己给的定位。如果你打算在生产环境用它，先想清楚这一点。&lt;/p&gt;&#10;&lt;p&gt;实用建议：用 &lt;code&gt;--exclude-tools&lt;/code&gt; 砍掉用不上的高危工具。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;windows-mcp serve --exclude-tools &lt;span style="color:#e6db74"&gt;&amp;#34;PowerShell,Registry,Process&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对纯 UI 自动化场景，这三个基本用不到，砍掉之后风险面小很多，而且不影响核心能力。&lt;/p&gt;&#10;&lt;h2 id="十五成熟度评估"&gt;十五、成熟度评估&#10;&lt;/h2&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;维度&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;评价&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;依据&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;功能完整度&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;高&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;21 个工具覆盖感知/输入/系统三层，日常任务够用&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;工程严谨度&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;高&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;输入账本、元素预算、版本守卫、进程排除、COM 亲和，都是踩过坑之后的正解&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;文档质量&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;高&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;README + CLAUDE.md 详尽，代价和限制写在明面上&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;测试覆盖&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;中&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;有 tests/ 目录与 CI（windows-latest），但测试依赖 pywin32，只能 Windows 跑&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;API 稳定性&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;低&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;自我标注 Beta；0.8.x 系列仍在快速迭代，接口有变动&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;安全默认值&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;中高&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;本地场景合理；上网络需要显式配置，但配置项给全了&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;运维友好度&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;中&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;有 Scheduled Task 自启、日志落盘，但无热重载、无健康检查端点&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;社区活跃度&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;高&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;56 位贡献者，775 次提交，dependabot 常态运行，issue 响应及时&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;一句话：&lt;strong&gt;这是一个&amp;quot;作者自己天天在用&amp;quot;的项目&lt;/strong&gt;。判断依据不是 star 数，而是那些只有真正用过才会加的机制——元素预算（被几千行的表格卡过）、进程排除（被 VS Code 的无障碍树卡过）、输入账本（被卡住的 Ctrl 键坑过）、版本守卫（被 0.8.5 的漂移事故坑过）。每一条都对应一次具体的翻车。&lt;/p&gt;&#10;&lt;h2 id="十六结语"&gt;十六、结语&#10;&lt;/h2&gt;&lt;p&gt;回到开头那个判断：&lt;strong&gt;它把无障碍树当成了 API&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;这个选择的好处是确定性和通用性——不训练模型、不做视觉推理、任意 LLM 都能用，动作延迟压到 0.2–0.5 秒。代价是它继承了 UIA 的所有局限：选不了字符、读不到自绘 UI、被病态无障碍实现拖死、Firefox 得走另一套 API。&lt;/p&gt;&#10;&lt;p&gt;但我觉得它真正值得学的地方不在技术选型，而在&lt;strong&gt;对人机共存的正面处理&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;绝大多数自动化工具假设&amp;quot;机器归程序所有&amp;rdquo;。Windows-MCP 承认&amp;quot;人在旁边，随时会回来&amp;quot;，然后做了三件事：给接管留通道（鼠标位移和热键双路）、给按键状态记账（不错乱、不卡键）、给 AI 的占用加可视提示（边缘光晕 + 截图闪烁边框）。&lt;code&gt;main&lt;/code&gt; 分支上还在做第四件——按工具分级，让读文件、抓截图这类不抢焦点的操作不必等租约。&lt;/p&gt;&#10;&lt;p&gt;而且它诚实地说明这套机制只是 best-effort，不是安全边界。&lt;/p&gt;&#10;&lt;p&gt;这条路走得并不完美。0.8.8 的门控是全量的，粒度过粗；阈值 120 偏敏感；配置里写了不生效的键也不报错。但从设计方向上看，&lt;strong&gt;它把&amp;quot;人在回路&amp;quot;当成了架构的一部分，而不是事后补的开关&lt;/strong&gt;——这个起点是对的，剩下的都是打磨。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;承认自己做不到什么，比宣称自己什么都能做，更能建立信任。&lt;/strong&gt; 这条在工程上和在别处一样成立。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&lt;strong&gt;免责声明&lt;/strong&gt;：本文基于 &lt;strong&gt;2026-10-18&lt;/strong&gt; 的仓库快照与 PyPI 包 &lt;code&gt;windows-mcp==0.8.8&lt;/code&gt; 在本机的实际测试撰写。文中&amp;quot;实测&amp;quot;结论均可复现（命令与代码片段已在正文给出）；&amp;quot;&lt;code&gt;main&lt;/code&gt; 分支&amp;quot;的结论来自 2026-10-18 的源码与 README 交叉核对，&lt;strong&gt;该部分功能在 0.8.8 中尚未发布&lt;/strong&gt;。项目仍在活跃迭代，接口、默认值、安全策略均可能变化，部署前请以仓库最新 README 与 SECURITY.md 为准。未对全部 179 个文件逐一审计；涉及安全的结论不构成安全评估。Windows-MCP 具备完整系统权限且无沙箱，请仅在虚拟机、Windows Sandbox 或可回滚环境中试用。&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;</description></item></channel></rss>