Skip to main content
Glama

OSWright

PyPI Tests Python License

面向 AI 代理的桌面自动化,无需为每一步截屏付费。

mcp-name: io.github.Ask-812/oswright

这是一个 MCP 服务器,让 LLM 能够驱动真实的桌面应用程序——相当于桌面版的 Playwright MCP。它在操作之间保留屏幕模型,只重新读取发生变化的部分,因此同样的工作所消耗的 token 数量减少一个数量级。

OSWright 正在将发票转录到费用表单

从发票上读取八个字段并输入费用表单,由应用程序本身验证。同样的任务、同样的结果,与每次操作后返回截图相比,上下文减少 7.4 倍。运行过程中的每一个屏幕数字都被测量——可使用 python benchmarks/record_demo.py 重新生成整个过程。

为什么会有这个项目

大多数 GUI 代理在每一步都会重新感知整个屏幕:截图、OCR、把图像交给模型,如此循环。在真实桌面上测量时,观察结果的中位数仅改变屏幕像素的 0.012%。全部重新读取所做的工作远超变化本身所需,而且无论是否发生变化,都要花费约 2,800 个图像 token。

OSWright 会询问合成器发生了什么变化,只重新扫描这些区域,并尽可能从最廉价的来源回答元素查找。下面的结论是在这台机器上测量得到的,并且可以通过 benchmarks/ 复现——包括那些对 OSWright 不利的结论。

主要特性

  • 跨平台。 Windows(Win32 API)、Linux(pynput/X11)、macOS(pynput/Quartz)。

  • 辅助功能树。 通过 Windows UI Automation 按角色和名称确定性地查找元素——100% 准确、即时,无需模型。

  • 快速 OCR。 使用 Windows 内置 OCR(即时)并在 Linux/macOS 上以 EasyOCR 作为后备。结果会自动缓存。

  • Windows 上轻量。 无需下载 PyTorch——Windows 使用内置 OCR 引擎,因此完整安装只有几 MB,而不是几 GB。

  • 图像匹配。 通过 OpenCV 按模板图像定位元素。

  • 窗口管理。 列出、聚焦、最小化、关闭特定窗口并对其截图。

  • 截图差异对比。 通过 wait_for_change 检测屏幕变化。

  • 剪贴板访问。 读取和写入系统剪贴板以传输数据。

  • 应用启动器。 启动应用程序并等待其加载完成。

  • 自动快照。 每个操作都会返回截图,因此代理始终能看到当前状态。

  • 43 个 MCP 工具。 涵盖屏幕、OCR、UIA、鼠标、键盘、窗口、剪贴板和复合操作。

  • 增量感知。 只重新扫描屏幕中发生变化的部分,并且可以返回变化内容而非完整截图——每一步的 token 消耗减少约 21 倍。

  • 屏幕记忆。 识别之前读取过的屏幕并复用它们,并通过像素验证——比重新读取便宜 89 倍。

  • 推测式感知。 学习操作会产生什么结果,并确认预期结果而不是重新读取——成本降低 19–23 倍,当界面出现意外行为时会生成 surprise 报告。

  • 自适应等待。 等待屏幕真正稳定下来,而不是固定等待 300 ms——在 50 步的任务中节省 11.9 秒。

  • 分辨率级联。 元素查找会在最先奏效的最廉价方法处停止;重复查找约耗时 0.05 ms。

  • DPI 正确。 坐标到处都使用物理像素,因此点击在缩放显示上能准确定位。

  • 测试套件。 237 个自动化测试;在没有显示环境时会自动跳过驱动桌面的测试。

环境要求

  • Python 3.10 或更高版本

  • VS Code、Cursor、Windsurf、Claude Desktop,或任何其他 MCP 客户端

快速开始

首先,随你的客户端安装 OSWright MCP 服务器。

标准配置 适用于大多数工具:

{
  "mcpServers": {
    "oswright": {
      "command": "uvx",
      "args": ["oswright"]
    }
  }
}

注意: 如果你没有 uvx,可以使用 pip install oswright,然后将 "command" 直接设为 "oswright"。

按照 MCP 安装指南,使用上面的标准配置。

claude mcp add oswright uvx oswright

在你的用户或工作区 settings.json 中的 mcp.servers 下添加:

{
  "mcp": {
    "servers": {
      "oswright": {
        "command": "uvx",
        "args": ["oswright"]
      }
    }
  }
}

或者使用 VS Code CLI:

code --add-mcp '{"name":"oswright","command":"uvx","args":["oswright"]}'

转到 Cursor Settings -> MCP -> Add new MCP Server。将其命名为 oswright,使用 command 类型,命令为 uvx oswright。

按照 Windsurf MCP 文档操作。使用上面的标准配置。

添加到你的 cline_mcp_settings.json:

{
  "mcpServers": {
    "oswright": {
      "type": "stdio",
      "command": "uvx",
      "args": ["oswright"],
      "disabled": false
    }
  }
}

转到 Advanced settings -> Extensions -> Add custom extension。将其命名为 oswright,使用 STDIO 类型,并将 command 设置为 uvx oswright。

如果你更喜欢标准的 pip 安装:

pip install oswright

然后使用此配置:

{
  "mcpServers": {
    "oswright": {
      "command": "oswright"
    }
  }
}

或者直接运行:

python -m oswright

Related MCP server: screen-use

增量感知

大多数 GUI 代理在每一步都会重新感知整个屏幕:完整截图、完整 OCR,然后把新图像交给模型。在真实桌面上测量时,观察结果的中位数仅改变 0.012% 的像素——因此完整重新扫描所做的工作大约是变化本身所需的 240 倍,而且无论是否发生变化,它返回的截图都要花费约 2,800 个图像 token。

OSWright 在两次观察之间保留屏幕模型,只重新扫描实际移动过的区域。

observe()  ->  {"changed": true,
                "added":   [{"text": "Saved", "x": 812, "y": 447}],
                "removed": ["Unsaved changes"],
                "screen_fraction_scanned": 0.015}

在这台机器上对一个 14 步代理循环进行测量:

v0.4.0(完整 OCR + 截图)

增量

每步中位延迟

212 ms

33 ms

每次观察的 token 数

~2,764

~49

14 步总 token 数

38,696

1,025

屏幕重新读取比例

100%

16%

屏幕越繁忙,差距越大:完整 OCR 的开销随屏幕上的文本量增长,而增量路径只随 变化量 增长。同样的对比在安静的桌面上测得 6.5 倍,在打开密集网页时测得 14.3 倍。请用 benchmarks/ 重新测量,而不是相信这些数字。

不过,成本只是一个代理指标,一条更便宜但悄悄降低准确性的感知路径比没有更糟。因此,我们以任务完成情况来检验:脚本化任务驱动四个应用程序中的真实工具界面,并根据每个应用程序自身的状态评分——计算器和文件资源管理器使用 UI Automation,Chrome 和 VS Code 使用窗口标题——绝不对照 OCR。

配置

计算器

文件资源管理器

Chrome

token 数

v0.4 风格(完整截图)

9/9

3/3

3/3

118,858

仅 delta

9/9

3/3

3/3

5,252

delta + 记忆

9/9

3/3

3/3

5,099

delta + 记忆 + 预测

9/9

3/3

3/3

7,981

在所有配置下准确率完全相同,而 token 成本下降了 23 倍。 使用 python benchmarks/bench_tasks.py 运行。

为什么同时需要像素和辅助功能

这个设计押注的是,没有任何一种感知路径能包打天下。分别关闭其中一半来验证这一点,而不是凭空断言:

配置

计算器

文件资源管理器

Chrome

完整级联

9/9

3/3

3/3

仅辅助功能

9/9

0/3

0/3

仅像素

6/9

3/3

3/3

仅辅助功能——大多数 Windows GUI 代理采用的方式——在 XAML 上表现完美,但在 Win32 列表视图和网页内容上如同盲人。对 VS Code 进行探测时,它只能看到 18 个元素,整个 IDE 是名为 Chrome Legacy Window 的单一节点,而 OCR 能读取 94 个元素,包括每个文件名。

仅像素方案在计算器的按钮上失败,因为人类读作 7 的按钮 名为 Seven,而 Windows OCR 在计算器上根本返回不了任何数字。

级联是唯一在所有场景下都通过的配置。

分辨率级联

find_element 和 click_element 会在第一个能回答的方法处停止,因此成本取决于请求的 新颖程度,而不是屏幕的大小:

层级

方法

典型成本

0

已在屏幕模型中

~0.05 ms

1

只重新扫描发生变化的部分

~70 ms

2

辅助功能树(知道按钮 就是 按钮)

~40 ms

3

通过 UIA TextPattern 访问应用自身的文本缓冲区——精确字符

~400 ms

4

全屏 OCR

~250 ms

查找模型已知的文本比 v0.4.0 路径便宜 约 5,000 倍(0.05 ms 对 244 ms)。响应会报告由哪一层回答,因此你可以看到任务实际消耗了什么。

第 3 层值得理解:UIA 的 TextRange.FindText 会搜索应用程序自己的文本缓冲区,并返回精确的边界矩形。它不受字体、DPI、抗锯齿和 OCR 错误的影响。它排在像素层级之下,仅仅是因为扫描窗口控件需要数百毫秒的跨进程 COM 开销——它是精确的一层,而不是快速的一层。

关于排序的说明。 这些层级是按 实测结果 排序的,而不是按理论。常见的建议是把辅助功能树作为首选,但在真实应用程序上它并不总是更便宜:在这里遍历 Chrome 的树花了 537 ms,比全屏 OCR 一遍还慢,而且 VS Code 只向它暴露了 18 个元素。无论是像素还是辅助功能,都无法在所有场景下胜出,这就是为什么这是一个级联而不是二选一。

询问合成器而不是自己看

在 Windows 上,桌面合成器已经知道哪些像素发生了变化,并通过 DXGI Desktop Duplication 暴露这些变化。询问它的成本为 0.14 ms,且不传输任何像素,而捕获一帧并发现它完全相同则需要几十毫秒——因此空闲观察会完全跳过捕获。

当确实有变化时,合成器会持有那一帧,因此可以直接从 GPU 读取其像素,而不是通过另一个 API 再次抓取——在本文的测量中比 mss 快 1.5–2.3 倍。

合成器仅用作变更检测的快速 否定。当它报告有变化时,脏区域仍然来自对捕获帧的哈希计算:两者的测量间隔略有不同,因此合成器矩形相对于实际捕获的像素可能少报,而少报的区域就是永远不会被重新读取的文本。在 Desktop Duplication 不可用的地方,它会静默降级为瓦片哈希和正常捕获。

使用 --observation-mode delta 为操作工具启用增量观察。默认值仍为 screenshot,以兼容现有客户端。

请自行完整复现所有这些: 参见 benchmarks/。 每个决策背后的理由(包括走过的弯路)都记录在 docs/ENGINEERING_LOG.md 中。

配置

OSWright MCP 服务器支持以下参数。它们可以在 JSON 配置中作为 "args" 列表的一部分提供:

选项

描述

环境变量

--port <port>

SSE 传输的端口。若省略,则使用 stdio(默认)。

FASTMCP_PORT

--host <host>

HTTP/SSE 服务器绑定的主机。默认:127.0.0.1。

FASTMCP_HOST

--transport <mode>

传输协议:stdio、sse、streamable-http。根据 --port 自动检测。

--ocr-languages <langs>

OCR 语言(默认:en)。示例:--ocr-languages en es fr

OSWRIGHT_OCR_LANGUAGES

--timeout <seconds>

自动等待操作的默认超时(默认:10)。

OSWRIGHT_TIMEOUT

--snapshot-max-width <px>

对每次操作后返回的自动快照进行降采样。0(默认)保持完整分辨率。较低的值可显著降低 token 成本。

OSWRIGHT_SNAPSHOT_MAX_WIDTH

--observation-mode <mode>

操作工具返回的内容:screenshot(默认)、delta(仅返回变化部分,token 数约为原来的 1/30)或 both。

OSWRIGHT_OBSERVATION_MODE

--no-atlas

不跨访问记忆屏幕。

OSWRIGHT_NO_ATLAS

--no-speculate

不预测操作结果。

OSWRIGHT_NO_SPECULATE

--allow-remote

绑定非回环地址时需要。参见 安全。

--log-level <level>

日志级别:DEBUG、INFO、WARNING、ERROR。默认:INFO。

OSWRIGHT_LOG_LEVEL

显式命令行标志始终优先于相应的环境变量。

示例:多语言 OCR

{
  "mcpServers": {
    "oswright": {
      "command": "uvx",
      "args": ["oswright", "--ocr-languages", "en", "es", "fr"]
    }
  }
}

独立 MCP 服务器(SSE)

当从工作进程或其他机器运行时,请使用 SSE 传输:

uvx oswright --port 8931

然后在你的 MCP 客户端配置中:

{
  "mcpServers": {
    "oswright": {
      "url": "http://127.0.0.1:8931/sse"
    }
  }
}

安全

OSWright 没有任何身份验证。 任何能访问该端口的人都能完全控制机器的键盘、鼠标、屏幕和剪贴板——这相当于远程桌面接管,而不是沙箱 API。

因此,服务器默认绑定到 127.0.0.1,并且除非传入 --allow-remote,否则拒绝在非回环地址上启动。要从另一台机器访问它,建议使用 SSH 隧道而不是暴露端口:

ssh -L 8931:127.0.0.1:8931 user@desktop-host

Stdio 传输(默认方式,上述所有 MCP 客户端配置都使用它)完全不暴露于网络,是运行 OSWright 的推荐方式。

可能破坏工作的工具会得到相应标注:close_window 被标记为破坏性操作,launch_app 可启动任意程序。截图工具拒绝覆盖已存在的 save_path。

平台说明

平台

输入后端

OCR 后端

额外下载

Windows

Win32 API (SendInput)

Windows OCR(即时、内置)

无。 不需要 PyTorch。包含 UI Automation。

Linux

pynput (X11)

EasyOCR

PyTorch(约 2.5 GB)。需要 X11;Wayland 支持有限。

macOS

pynput (Quartz)

EasyOCR

PyTorch(约 2.5 GB)。请在“系统设置”>“隐私与安全性”>“辅助功能”中授予辅助功能权限。

在 Windows 上,不安装 EasyOCR,因为内置的 Windows OCR 引擎更快,且无需下载模型。仅当你需要 Windows OCR 不支持的语言时才安装它:

pip install "oswright[easyocr]"

坐标

OCR、图像匹配和 UI Automation 返回的所有坐标均为绝对物理屏幕像素,可直接传给 mouse_click。这同样适用于子区域以及虚拟桌面从负原点开始的多显示器设置。screenshot 还会报告 origin_x/origin_y,即图像左上角像素的绝对位置,方便你自行从图像上读取坐标。

工具

  • screenshot -- 截取屏幕或某个区域的截图。以原生 MCP 图像内容返回图像。也可以选择保存到文件路径。

    • 只读:true

  • get_screen_info -- 获取屏幕尺寸和显示器数量。

    • 只读

  • get_ui_tree -- 获取焦点窗口的无障碍树。返回所有交互元素及其名称、类型、位置。结果确定且即时。

    • 参数:window_title、max_depth

    • 只读:true

  • click_ui_element -- 使用无障碍树点击 UI 元素。比 OCR 更可靠。

    • 参数:name、control_type、automation_id、window_title

  • fill_ui_element -- 设置 UI 元素(例如文本框)的值。比基于 OCR 的填充更可靠。

    • 参数:value、name、automation_id、window_title

  • get_active_window -- 获取当前焦点窗口的信息。

    • 只读:true

  • wait_for_change -- 等待屏幕发生视觉变化。截取基准截图,轮询直到画面不同。

    • 参数:timeout、poll_interval

Python 库

OSWright 也可以作为独立的 Python 库使用,提供 Playwright 风格的 API:

from oswright import OSWright

with OSWright() as ow:
    screen = ow.screen()
    screen.click(text="Start")
    screen.type_text("Hello World")
    screen.press("Ctrl+S")
    screen.screenshot("desktop.png")

更多示例请参阅 examples/ 目录。

架构

oswright/
  __init__.py          # Package entry point (single source of __version__)
  core.py              # OSWright class (= Browser)
  screen.py            # Screen class (= Page)
  locator.py           # Locator + Assertions (= Locator + expect)
  capture.py           # Screen capture (mss - cross-platform, thread-safe)
  dirty.py             # Change detection - which parts of the screen moved
  screenmodel.py       # Persistent screen model, updated incrementally
  cascade.py           # Resolution cascade - cheapest method that can answer
  atlas.py             # Remembers screens across visits and sessions
  settle.py            # Knowing when the screen has finished responding
  speculate.py         # Predicting what an action does, instead of looking
  textprovider.py      # Exact text from the app itself via UIA TextPattern
  detect.py            # OCR dispatcher with caching (auto-selects best backend)
  _ocr_windows.py      # Windows OCR backend (instant, built-in)
  accessibility.py     # Windows UI Automation (deterministic element finding)
  cache.py             # Screenshot diffing, image hashing, OCR result cache
  _dpi.py              # Process DPI awareness (keeps every API in physical pixels)
  _dxgi_windows.py     # Compositor dirty rectangles via DXGI Desktop Duplication
  input.py             # Platform dispatcher for input backends
  _input_windows.py    # Windows input backend (Win32 API)
  _input_pynput.py     # Linux/macOS input backend (pynput)
  window.py            # Window management (list, focus, close)
  clipboard.py         # Clipboard read/write (cross-platform)
  mcp_server.py        # MCP server (43 tools for AI agents)
tests/
  conftest.py          # Fixtures that skip when no display/OCR is available
  test_core.py         # Unit tests (no desktop required)
  test_perception.py   # Incremental perception (stubbed, runs headless)
  test_atlas.py        # Screen memory and its failure modes (headless)
  test_speculate.py    # Prediction, settling, and their limits (headless)
  test_e2e.py          # End-to-end tests against the real desktop (marked `e2e`)

记住屏幕

应用是确定性的——同一个对话框每次都有相同的布局。OSWright 会记住它读取过的屏幕,并在下次访问时跨会话复用:125 ms 冷读取 → 1.4 ms 热召回(89×)。

被记住的屏幕绝不会仅凭识别结果就受到信任。在复用布局之前,会按像素抽查几个区域,因此发生变化的屏幕会被拒绝,而不是被操作。验证采用失败关闭原则:没有任何可检查内容的屏幕根本不会被记住。

可通过 --no-atlas 禁用。记住的屏幕存放在 ~/.oswright/atlas.json 中。

预测动作而非观察动作

应用是确定性的——点击保存每次都会产生相同的对话框——因此在第一次观察之后,动作的结果已经可知。OSWright 会学习动作的效果,并确认预期的屏幕,而不是重新读取:比观察便宜 19–23 倍(2.3 ms 对比 43–50 ms)。

一个预测必须被看到两次才会被信任,如果被证明是错的就会被弃用,并且会以与记住的屏幕相同的两种方式进行检查。失败的预测会以 surprise 的形式报告给代理——界面做了它通常不会做的事情,这值得知晓,而不是被默默吸收。

已确认的预测能保证什么: 布局——相同的控件位于相同的位置。而不是每个字符都完全相同。单个数字的变化所影响的像素比闪烁的光标还少,因此任何分辨率下的整屏检查都无法将它们区分开。当精确文本很重要时,请使用 observe(force_full=True)。

可通过 --no-speculate 禁用。

只等待必要的时间

动作工具过去会固定休眠 300 ms,这是为最慢的情况选择的,因此每个动作都按最坏情况付出代价。合成器知道屏幕何时停止变化,因此现在等待会在界面真正稳定时结束:

之前的固定休眠

300 ms

实际等待中位数

61.5 ms

50 步任务中节省的时间

11.9 s

"稳定"表示最近没有大的变化,而不是没有变化:真实的桌面永远不会静止——光标和时钟每 ~18 ms 产生一次变化事件,覆盖约 32 个像素,而真正的 UI 变化覆盖数以万计的像素。

尚未完成

  • Wayland 输入注入,以及作为 TextPattern 等效物的 macOS AXTextMarker。

  • 视觉模型层级,用于既不可访问又无法清晰读取文本的表面:游戏、画布、图像编辑器。

  • 以不止前一个屏幕为键的转换,用于结果取决于不可见状态的动作。

测量了什么,以及没有测量什么

感知成本和任务成功率都在本机上进行了测量,并可通过 benchmarks/ 重现——在四个应用中,更便宜的感知并不会以准确性为代价,而且像素/无障碍的划分是测量出来的,而非争论出来的。

与 Windows-MCP 对比

相同的任务,四个场景,每个都由应用本身评分。两个工具都不自我评分,Windows-MCP 使用其默认设置运行:

计算器

资源管理器

Chrome

Chrome,2 步

通过

tokens

oswright

5/5

4/5

5/5

5/5

19/20

832

Windows-MCP,每次动作快照

5/5

5/5

5/5

5/5

20/20

14,053

Windows-MCP,仅快照一次

5/5

5/5

5/5

5/5

20/20

8,214

坦白说:Windows-MCP 更可靠,而 oswright 便宜 16.9 倍。 oswright 每二十次点击丢掉一次,发生在一个刚刚打开的窗口上。

成本差异是结构性的,而不是调优的结果。Windows-MCP 将屏幕返回给代理——Snapshot 将无障碍树渲染为 (x,y) button "Seven" [action: click]——并取回坐标,因此屏幕描述会在每次动作时计入模型的上下文。oswright 接收文本并返回结果。

可靠性差距可能正是由速度造成的:oswright 在 ~100 ms 内完成解析并点击,有时在一个刚获得焦点的窗口准备好接收输入之前就完成了,而较慢的循环给了应用它从未需要主动索取的时间。这是一个假设,而非结论——在十次试验中,添加动作前的稳定等待没有产生可测量的差异,因此它被记录下来,而不是被修复。

Chrome, 2 steps 场景之所以存在,是因为这里的其他任务都足够短,工具可以读取一次屏幕并复用那些坐标。而在该场景中,第一次点击将控件向下移动 325 px,因此只快照一次的配置不得不重新读取屏幕——所以在界面会移动的任务中,它那个便宜的数字并不存在,其真实成本是每次动作的成本。

使用 python benchmarks/bench_head_to_head.py 重现(设置方法见文件中的 docstring)。

这并不能证明什么: 一台笔记本电脑上的四个短任务。不涉及长期多步骤工作、恢复或产品成熟度——Windows-MCP 拥有 OAuth、分析、看门狗和安装程序;oswright 这些都没有。它的无障碍遍历还会读取 Chrome 的页面内容,而 oswright 自己的无障碍层级则不会。"每次动作大幅更便宜,以很小的可靠性代价"是它的主张。"更好的产品"则不是。

开发

pip install -e ".[dev]"

pytest tests/                # everything available on this machine
pytest tests/ -m "not e2e"   # unit tests only, no desktop needed
ruff check oswright tests    # lint
python benchmarks/bench_pipeline.py   # reproduce the performance numbers
python benchmarks/bench_tasks.py      # task success (opens Calculator repeatedly)

设计决策、测量结果和死胡同都记录在 docs/ENGINEERING_LOG.md 中。

许可证

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Enables low-cost agent models to control Windows applications through a compact, state-safe proxy over Open Computer Use, reducing model-visible context by up to 99.8% with support for record/replay and reusable UI component memory.
    5
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to see, locate UI elements, and operate any Windows desktop app through natural language, using accessibility-tree matching with optional vision-model fallback, plus an autonomous visual loop with introspection and meta-learning.
    13
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to safely and truthfully control local desktop applications via native accessibility trees, supporting element inspection and automation without proprietary binaries or fake success responses.
    Apache 2.0