立项背景与目标:让大模型真正"接管电脑"这件事,目前主流方案要么把整个桌面关进沙箱(无法操作用户的真实软件),要么直接把完整控制权交给模型、中间没有任何人工确认环节——一次误点就可能发出无法撤回的消息、完成一笔付款或删掉重要文件。
本项目要解决的就是这个矛盾:做一个既能操作真实 Windows 桌面、又把危险动作拦在人工确认之前的桌面控制 MCP 服务。目标是"给智能体一双手,但不交出钥匙"。
软件功能与核心模块:
1. 共 26 个 MCP 工具,分七类:观察(整屏截图、局部放大、屏幕指纹、等待画面变化)、语义定位(UI Automation)、OCR 文字识别、鼠标、键盘、窗口与程序启动、编排(批处理)。
2. 三级定位策略:优先用 UI Automation 按控件名直接触发(连鼠标都不用移动);读不到控件树时退回 OCR 识别屏幕文字、按识别出的坐标点击;两者都不行才用纯坐标。
3. 策略引擎:四张可配置名单,分"硬拒绝"与"必须审批"两档。密码管理器、加密钱包、注册表与磁盘工具直接拒绝且无法绕过;通讯、邮件、远程桌面类应用则不做禁止,但其中任何一次点击或按键都要先弹窗。
4. 人机确认闸门:危险动作会弹出真实的 Windows 对话框并阻塞等待,只有真人点击才能放行;回车键刻意不绑定,避免误按放行。
5. 防护开关面板:三个互相独立的开关(审批弹窗 / 黑名单 / 审计日志),通过标记文件实现,改动即时生效、无需重启服务。
6. 审计日志:把每个动作连同解析出的目标进程一起写入 audit.jsonl,事后可追溯。
整体架构:MCP 客户端 ←stdio JSON-RPC→ Node 服务端 ←base64 行协议→ PowerShell 常驻宿主(内嵌 C# 调用 Win32)。
我负责的模块与量化结果:
1. 全部模块独立实现,没有引入任何第三方自动化库,唯一依赖是官方 @modelcontextprotocol/sdk;宿主层是自写的 C# + Win32。
2. 采集与渲染:用 StretchBlt 在单次 GDI 调用里同时完成截屏与缩放,2560×1600 全屏截图 116ms,其中 55ms 是整屏从显存读回的物理下限;图片格式改用 JPEG 后编码耗时从 19–33ms 降到 4–8ms。
3. 常规调用延迟 1–12ms(光标位置、窗口枚举、按键等)。自研的屏幕指纹接口 42ms 且只消耗 9 个 token,相比整屏截图的约 2133 个 token 便宜 237 倍;批处理接口可把多步操作合并为一次往返。
4. OCR 中间件:对比测试后把系统自带的 Windows.Media.Ocr 换成 RapidOCR(PaddleOCR 模型跑在 ONNXRuntime 上)。同一块计算器按钮区域,系统 OCR 返回 0 个词,RapidOCR 完整识别出全部 14 个按钮;再把一次性进程改成常驻 worker 后,重复调用从 2.3 秒降到 350ms,内存占用 96MB 且空闲 60 秒自动回收。
遇到的难点与解决方案:
1. Windows 前台锁:后台进程调用 SetForegroundWindow 会被系统静默忽略——函数返回成功,但窗口根本没到前台,导致后续键击发给了别的窗口。改用 AttachThreadInput 挂到前台线程的输入队列后解决。
2. 文本注入丢换行:用 KEYEVENTF_UNICODE 发送换行符时浏览器和多数输入框不识别,多行文本被拼成一行。改为对换行与制表符发送真实的 VK_RETURN / VK_TAB 击键。
3. 安全闸门一度判错目标:语义点击的目标窗口往往并不是前台窗口,最初用前台窗口做策略判断会判错对象。改为先定位元素中心、再用 WindowFromPoint 解析真实归属窗口。
4. PowerShell 5.1 读取无 BOM 的脚本时按 ANSI 解码,一个中文字符会吞掉换行并破坏内嵌的 C# 代码,因此宿主脚本严格保持纯 ASCII 并用自检脚本校验。
项目已开源(MIT 许可):github.com/be1st6666/guarded-computer-use-mcp