关于 desktop-mcp
desktop-mcp 是用于观察和控制本地桌面的 Windows 专用 MCP 服务。各工具组分别受配置控制,鼠标和键盘输入默认关闭。
主要功能
- 01将屏幕截图保存为 PNG 文件
- 02列出并检查窗口
- 03聚焦、调整大小、最小化和还原窗口
- 04发送鼠标和键盘输入
- 05使用 ffmpeg 录制屏幕
适用场景
- 01检查桌面上可见的窗口
- 02为桌面任务排列窗口
- 03在明确启用后自动执行鼠标和键盘操作
- 04录制屏幕
MCP 相关
该服务通过本地 stdio MCP 主机提供桌面观察、窗口管理、输入和录屏工具。窗口、输入和录屏工具组需通过环境变量显式启用;输入默认关闭。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
desktop-mcp
用于控制 Windows 桌面的 MCP 服务:支持截图、窗口管理、鼠标/键盘输入和 ffmpeg 屏幕录制,并通过配置按工具组启用(默认关闭 input)——按照与 mcp-factory 和 rag-mcp 相同的标准打造:独立的 pyproject、fastmcp 服务、内容可靠的 README,以及完整的测试套件。
快速入门
pip install desktop-mcp
// ~/.claude.json (or any MCP-host stdio client config)
{
"mcpServers": {
"desktop-mcp": {
"command": "desktop-mcp"
// or: "command": "python", "args": ["-m", "desktop_mcp"]
}
}
}
window/input/record 工具组由环境变量控制——除非你明确启用,否则 input 会保持关闭。完整列表请参阅下方的环境变量。
工具组
| 组 | 工具 | 默认状态 |
|---|---|---|
observe | screenshot、list_windows、get_active_window、window_info | 始终开启 |
window | focus_window、move_resize_window、minimize_window、restore_window | 由环境变量控制;除非设置 DESKTOP_MCP_ENABLE_WINDOW=1,否则关闭 |
input | mouse_move、mouse_click、mouse_drag、mouse_scroll、key_press、hotkey、type_text | 由环境变量控制,默认关闭——需要设置 DESKTOP_MCP_ENABLE_INPUT=1 |
record | record_start、record_status、record_stop | 由环境变量控制;除非设置 DESKTOP_MCP_ENABLE_RECORD=1,否则关闭 |
已禁用的工具组会返回结构化的 policy_refusal 错误(不会静默地不执行任何操作,也不会崩溃)。输入操作还受到频率限制(默认每分钟 60 次,可通过 DESKTOP_MCP_RATE_LIMIT_PER_MIN 调整)——超过上限会返回结构化的 rate_limited 错误。
这是纵深防御:运行框架级别的权限提示是第一道关卡,但除非服务自身的环境变量明确启用相应功能,否则服务会拒绝输入、窗口和录制操作。因此,即使运行框架配置错误或权限过宽,也无法启用操作人员未选择为此进程开启的功能。
安全
注册 desktop-mcp 前请阅读此部分。这是一个桌面控制服务;它可能对你的机器造成的影响已在此明确列出,以便你做出知情决定。坦诚说明影响范围正是本节的目的。
observe组(screenshot、窗口枚举)始终启用,且无法禁用。 任何能够访问此服务的调用方都可以捕获机器上任意可见窗口的内容,包括密码管理器、身份验证器/双重身份验证应用、助记词、银行页面或私人消息。screenshot返回的是写入临时目录的 PNG 文件路径(默认路径为%TEMP%\desktop-mcp-scratch),而不是内嵌像素数据,因此捕获的屏幕内容也会持续保存在磁盘上,直到该目录被清空。请将临时目录视为敏感内容,并且只在你信任的机器和运行环境中运行此服务。DESKTOP_MCP_ENABLE_INPUT=1会授予向当前焦点窗口注入按键和鼠标操作的权限——这相当于远程代码执行。 启用input组后,调用方可以在当前获得焦点的窗口(终端、浏览器或管理工具)中输入命令、按下快捷键并操控鼠标。出于这个原因,默认情况下此功能处于关闭状态;只有在你明白这等于把键盘交给调用方时才启用。禁用时,每个输入工具都会返回结构化的policy_refusal(绝不会静默执行操作)。- 最小权限: 除非具体任务需要,否则请保持
input、window和record组关闭;将此服务部署在可信主机上;除非你已接受启用的input组可以操控高权限窗口,否则不要以提升的权限(管理员身份)运行它。
上述环境变量门控、默认关闭的 input 组、结构化拒绝和速率上限均为强制执行的控制措施;本节直言不讳地说明了按设计仍可访问的内容。
能力说明表
以下每项声明均对应实现该功能的文件及验证它的测试——每项能力都有对应的实现和测试作为依据。
| 声明 | 实现 | 验证方式 |
|---|---|---|
| 将屏幕截图(显示器/区域/窗口)保存为 PNG | desktop_mcp/groups/observe.py::screenshot | tests/test_observe.py::TestScreenshot,实时测试:tests/test_live_smoke.py::test_live_screenshot_real_png |
| 枚举窗口/获取活动窗口/按标题查找窗口 | desktop_mcp/groups/observe.py | tests/test_observe.py::TestListWindows、TestGetActiveWindow、TestWindowInfo |
| 聚焦/移动并调整窗口大小/最小化/还原窗口 | desktop_mcp/groups/window.py | tests/test_window.py |
| 鼠标移动/点击/拖动/滚动、按键/快捷键、输入文本 | desktop_mcp/groups/input_tools.py | tests/test_input.py(仅模拟 pyautogui——参见「限制」) |
| 通过 ffmpeg gdigrab 录制屏幕,设定时长硬上限,并支持正常停止 | desktop_mcp/groups/record.py | tests/test_record.py,实时测试:tests/test_live_smoke.py::test_live_record_real_mp4 |
| 输入组默认关闭,禁用时会结构化拒绝请求 | desktop_mcp/config.py::group_enabled、gated | tests/test_config.py::TestGroupEnabled、tests/test_input.py::TestGateDisabledByDefault |
| 限制输入操作速率(默认每分钟 60 次) | desktop_mcp/config.py::TokenBucket、RateLimiterRegistry | tests/test_config.py::TestTokenBucket、tests/test_input.py::TestRateLimit |
| DPI 感知初始化(per-monitor-v2),确保缩放显示器上的 mss/pyautogui 坐标一致 | desktop_mcp/config.py::ensure_dpi_awareness | tests/test_config.py::TestDpiAwareness(仅验证幂等性;不会自动测试视觉坐标是否一致——参见「限制」) |
| 执行任何鼠标操作前,先根据虚拟桌面边界验证坐标 | desktop_mcp/groups/input_tools.py::_validate_point | tests/test_input.py(越界情况) |
| 孤儿保护:启动新录制器前,先终止崩溃进程遗留的过期录制器 | desktop_mcp/groups/record.py::_orphan_guard | tests/test_record.py::TestRecordStart::test_orphan_guard_kills_stale_recorder |
限制(使用前请阅读)
- UIPI(用户界面特权隔离)——保护窗口句柄,而非输入。中等完整性级别的进程(本服务,除非你提升其权限)无法操作由更高完整性级别(已提升权限/管理员)进程拥有的窗口:
focus_window、move_resize_window、minimize_window和restore_window会通过pygetwindow的窗口句柄 API(desktop_mcp/groups/window.py)执行,Windows 会在 UIPI 下阻止这些操作,本服务会将其报告为结构化的window_action_failed错误,并指出 UIPI,不会静默地什么也不做。 键盘/鼠标输入工具也受 UIPI 限制——而且更糟的是,它们会静默失败。type_text、hotkey、key_press和鼠标操作(desktop_mcp/groups/input_tools.py)通过pyautogui执行,其 Windows 后端实际调用的是旧版keybd_event/mouse_eventWin32 函数(而非SendInput——pyautogui曾尝试使用SendInput,后来又恢复为较旧的调用;参见已安装软件包中的_pyautogui_win.py)。根据 Microsoft 自己的文档,UIPI 通常适用于合成输入:“[应用程序] 仅允许向完整性级别相同或更低的应用程序注入输入”(MSDNSendInput参考文档);关键在于,“GetLastError和返回值都不会表明失败是由 UIPI 阻止造成的。”keybd_event/mouse_event是无返回值的旧版调用,完全没有失败信号,因此pyautogui(以及本服务)在原理上都无法检测到拦截:如果焦点位于已提升权限的窗口,这些工具会报告{"ok": true},但 Windows 会静默丢弃注入的按键/点击——没有异常,没有错误字段,目标也收不到任何输入。这比上面的窗口句柄路径更糟,后者至少会引发结构化的window_action_failed错误并指出 UIPI。不存在代码层面的绕过方法,也没有其他解决办法;只有提升本服务的权限(或完全不启用输入组),而本项目不会这样做,也不建议这样做。操作人员应将任何可能获得焦点的已提升权限应用视为本服务输入工具无法触及且不会发出提示,而不是认为存在能让输入通过的漏洞。 可能的未来增强(尚未实现):注入输入前检测前台窗口的完整性级别,并返回结构化拒绝,而不是错误地返回{"ok": true}——这是跟踪中的 v2 构想,并非当前功能。 - **DPI 缩放。**本服务会在启动时设置每显示器 v2 DPI 感知,因此在经过缩放的显示器上,
mss像素坐标和pyautogui点坐标应当一致。此引导逻辑只经过了幂等性/不崩溃的单元测试——尚未通过自动化测试验证其在实际多 DPI、多显示器环境中的坐标一致性;如果你的目标显示器缩放比例不是 100%,应进行抽查。 - **UAC 安全桌面。**当 Windows 切换到安全桌面时(UAC 权限提升提示、Ctrl+Alt+Del、锁屏界面),在普通桌面上运行的进程——包括本服务——都无法查看或与其交互。截图会显示切换前普通桌面上的内容;输入调用则完全无法到达安全桌面。
- **仅限单台计算机和本地使用。**没有网络传输,也不支持远程控制。仅通过 stdio,由同一台计算机上的 MCP 主机启动。
- **本仓库自身注册时默认关闭输入组。**请参阅
~/.claude.json中的desktop-mcp条目——其中未设置DESKTOP_MCP_ENABLE_INPUT。是否启用是操作人员针对每次注册做出的主动选择,并非代码变更。 - pyautogui 安全保护。
FAILSAFE=True是有意设置的:操作过程中若将光标猛地移到屏幕角落,pyautogui 会引发异常并中止调用。这可能会中断正在执行的mouse_drag。这被视为可接受的 v1 行为(参见计划中的开放问题)——这是刻意设计的人为紧急停止机制,并非错误。 - **无 OCR/视觉分析。**截图为原始 PNG;解读其内容是使用方的工作,而非本服务的工作。
- **无剪贴板工具。**因涉及凭据相关操作,此功能推迟到 v2,并将采用独立的安全设计。
- **未注册到 mcp-factory hub。**本项目作为独立仓库发布(自带
pyproject,使用自有的免虚拟环境、系统级 Python 3.12 安装方式),与 rag-mcp 模式一致。Hub/注册表集成是 v2 候选功能。
环境变量
| 变量 | 作用 | 默认值 |
|---|---|---|
DESKTOP_MCP_ENABLE_WINDOW | 启用 window 工具组 | 未设置(关闭) |
DESKTOP_MCP_ENABLE_INPUT | 启用 input 工具组 | 未设置(关闭) |
DESKTOP_MCP_ENABLE_RECORD | 启用 record 工具组 | 未设置(关闭) |
DESKTOP_MCP_RATE_LIMIT_PER_MIN | input 组的速率上限 | 60 |
DESKTOP_MCP_SCRATCH_DIR | 截图/录制文件/pid 文件的写入位置 | %TEMP%\desktop-mcp-scratch |
DESKTOP_MCP_LIVE | 设为 1 以运行真实硬件冒烟测试(参见“测试”) | 未设置(跳过) |
使用示例
// A tool call from the MCP host, illustrative -- not a shell command.
{"tool": "screenshot", "arguments": {"monitor": 0}}
// -> {"ok": true, "path": "C:\\Users\\...\\Temp\\desktop-mcp-scratch\\screenshot-....png", "w": 3840, "h": 1080, "monitor": 0}
{"tool": "record_start", "arguments": {"fps": 30, "max_duration_s": 30}}
// -> {"ok": true, "path": "...\\recording-....mp4", "pid": 12345, "fps": 30, "max_duration_s": 30}
{"tool": "record_stop", "arguments": {}}
// -> {"ok": true, "path": "...\\recording-....mp4", "bytes": 800560, "duration_s": 3.13}
// input group disabled (default):
{"tool": "mouse_click", "arguments": {"x": 500, "y": 500}}
// -> {"ok": false, "error": {"type": "policy_refusal", "group": "input", "required_env": "DESKTOP_MCP_ENABLE_INPUT", ...}}
测试
CI(.github/workflows/ci.yml)会在每次推送/PR 时运行此测试套件(使用
windows-latest,因为 pywin32/PyGetWindow 是仅适用于 Windows 的依赖项,
无法安装在 Linux 运行器上),并在上方的 Tests 徽章与测试套件实际报告的结果
不一致时使构建失败——参见
scripts/check_readme_counts.py。
# unit suite (mocked backends, no real screen/input/recording touched)
python -m pytest -q
# handshake check -- prints every registered tool name
python scripts/list_tools.py
# real-hardware smokes (real screenshot PNG, real ~3s screen recording;
# never input-injection -- see safety rails above)
DESKTOP_MCP_LIVE=1 python -m pytest -q -k live_screenshot
DESKTOP_MCP_LIVE=1 python -m pytest -q -k live_record
安装
pip install -r requirements.txt # or: pip install .
# deps: fastmcp==3.4.2, mss==10.2.0, pyautogui==0.9.54, PyGetWindow==0.0.9, pywin32==312
# also requires ffmpeg + ffprobe on PATH for the record group
在 ~/.claude.json 中注册为 desktop-mcp(stdio,system Python312,
启用了 observe+window+record 组,环境中不包含 input 组)。
商业支持
由 Jaimen Bell 维护。如需生产环境 MCP 集成、定制服务或 Agent 可靠性方面的支持,请访问 jaimenbell.dev。
正在构建自己的 MCP 服务?MCP Starter Kit 提供模板、构建指南,以及开发并发布此工具时积累的实战经验。
mcp-name: io.github.jaimenbell/desktop-mcp
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。