关于 pyjab-mcp
pyjab-mcp 是供 Agent 在 Windows 上检查和操作 Java Swing、AWT 桌面应用的 MCP 服务。它通过 Java Access Bridge 读取 JVM 无障碍树,不修改或注入目标进程;不支持 JavaFX。
主要功能
- 01读取 Java 无障碍树
- 02查找并检查界面元素
- 03点击、输入、选择和填表
- 04通过 AccessibleTable 读取 JTable 单元格
- 05执行操作前重新核验元素
- 06将窗口置前并报告焦点状态
适用场景
- 01自动操作现有 Swing 客户端
- 02检查 AWT 应用中的控件
- 03分页读取 JTable 数据
- 04操作无法修改的生产应用
MCP 相关
它向 MCP 客户端提供 16 个工具,用于诊断 Java Access Bridge、定位窗口和元素、读取界面状态及执行桌面操作。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
16收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| load_bridge | boolean | 否 |
详细介绍
pyjab-mcp
一个 MCP 服务,可让 AI Agent 通过 pyjab 操控Java 桌面应用——Swing 和 AWT。pyjab 通过 Java Access Bridge 读取 JVM 自带的无障碍树。不支持 JavaFX:JavaFX 通过 UI Automation 而非 Java Access Bridge 提供无障碍功能,因此在 JAB 看来,JavaFX 窗口并不是 Java 窗口 (pyjab#227)。
mcp-name: io.github.gaozhao1989/pyjab-mcp
它绝不会修改、重启目标应用,也不会向其中注入代码。 这正是关键所在:启动参数、类路径和依赖 JAR 都保持原样,因此它可以用于无法更改的生产环境 Java 客户端。
另一种方案是在目标 JVM 内运行 Agent。 Agent 可以附加到已在运行的 JVM,也可以在应用启动时通过命令行中的 -javaagent 指定;无论哪种方式,工具代码都会进入其他进程——一个它既未启动、也不受其控制的 JVM。本服务两者都不做:它不会向应用加载任何内容,而是通过 Java Access Bridge 从外部读取无障碍树,因此能够连接到并非由它启动的进程。
JDK 对动态加载的态度,也就是将 Agent 附加到已在运行的 JVM,已有方向,但尚无发布日期。
JEP 451 在 JDK 21 中作为准备阶段发布:现在向已运行的 JVM 加载 Agent 会发出警告。该 JEP 将目标表述为 “让用户为未来默认禁止动态加载 Agent 的版本做好准备”;这个版本尚未确定。JEP 自身的非目标则明确,启动时通过 -javaagent 指定的 Agent,以及不用于加载 Agent 的 Attach API 用法,都不在这一限制范围内。
状态
已在 PyPI 发布——提供 16 个工具:java_diagnostics、java_list_windows、java_attach_window、java_snapshot、java_find、java_get_element、java_get_text、java_get_table、java_click、java_set_text、java_select、java_fill_form、java_send_keys、java_focus_window、java_wait_for 和 java_screenshot。它需要 pyjab>=1.11.0:1.9.0 提供了构建快照所依赖的树遍历功能;1.10.0 提供了公开的窗口列表和 JABDriver.detach(),后者可解除驱动程序的绑定而不终止进程——本服务没有释放会话的工具,因此绑定会持续到进程结束;1.11.0 提供了 JABDriver.send_keys,因此 java_send_keys 可以发送真正的按键组合;还提供了 JABDriver.focus(),因此 java_focus_window 可以将窗口置于前台。
java_send_keys 会向当前拥有键盘焦点的窗口发送按键组合,而连接窗口并不会让它获得焦点(pyjab#180)。java_focus_window 补上了这一环:它会将窗口置于前台;由于请求获得焦点与实际获得焦点并非一回事,它会报告窗口是否确实已置于前台,而不会想当然地认为操作成功。它会探测 API 是否存在,而非默认其存在,因此低于所要求版本的安装环境会收到包含适用版本的提示,而不是遇到 AttributeError。
两个元素可能同名,因此句柄会记录元素所在的位置以及它的名称:按名称搜索会返回所有匹配项,每个匹配项都有自己的句柄,端到端测试套件会验证点击其中一个只会作用于该元素,而非另一个。
只有经过核验后才会执行操作。 每次操作都会重新查找元素,并将其角色、名称、在同级元素中的位置和边界与快照记录进行比对,然后对刚核验过的元素执行操作;如果有任何差异,就不会执行操作,而是报告差异。
java_get_table 正是这套基础设施发挥作用的地方:它通过 JVM 自身的 AccessibleTable 接口读取 JTable——先获取行数和列数,再逐页读取单元格文本;而平台桥接层根本无法提供这些信息。
windows-gui.yml 会在托管的 Windows 运行器上验证实际行为:端到端测试套件会连接到真实的 Swing 应用,获取其快照,根据真实元素解析句柄,并检查在桥接层空闲后才打开的窗口是否仍能被发现。最后这一点正是这层存在的原因——在错误线程上运行消息泵,足以通过本仓库中的其他所有测试。但它仍无法覆盖你的应用:测试运行时操作的是 pyjab 的测试应用,而真正重要的是你关心的那个应用。
它需要与上方工具列表中指定的相同版本 pyjab>=1.11.0,并适用于其中列出的版本;这里值得再提的一项新增功能是 JABDriver.focus(),java_focus_window 会用它将窗口置于前台,并报告窗口是否确实已置于前台。这个返回值正是该工具存在的理由:发送组合键之前,调用方需要知道的是键盘焦点实际在哪里,而不是是否已请求设置焦点。
java_snapshot 返回的句柄表示位置,而非引用。后续调用会重新查找元素,并在执行任何操作前检查其角色、名称、位置和边界,因此,如果窗口已经移动,系统会返回提示信息,而不会对错误的控件执行操作。
有一件事已经有了定论,那就是这整个方向所依据的问题。在真实的 Java 窗口上,通过通用 Windows 自动化所用的同一个 UIAutomationCore 进行测量:
| 元素数 | 有名称的元素 | 深度 | |
|---|---|---|---|
| 通用 UI Automation,Java 窗口 | 6 | 5 | 3 |
| pyjab,同一个窗口 | 601 | 513 | 12 |
| 对照:UI Automation,整个桌面 | 147 | 99 | 已完成 |
对照项让这 6 个数字有了意义:对整个桌面进行同样的测量可以完成,因此测量方法有效,而 Java 窗口才是难点。
首先阅读
| 文件 | 内容 |
|---|---|
docs/ARCHITECTURE.md | 服务的构成方式、塑造它的约束,以及每项防护机制的作用 |
docs/CLIENTS.md | 连接 Claude Desktop、Claude Code、Cursor 和 VS Code |
docs/RELEASING.md | 如何发布,以及发布前必须具备什么 |
CHANGELOG.md | 发生了哪些变更,以及变更原因 |
维护者的工作笔记会刻意保留在本仓库之外。用户或贡献者所需的一切都已列在上方;非文档形式的规则则由 tools/ 中的防护机制强制执行——这些机制会在每次推送时于 CI 中运行。
要求
- Windows
- Python 3.10 或更高版本
- 启用了 Java Access Bridge 的 JDK——对要操作的应用运行
"%JAVA_HOME%\bin\jabswitch" -enable,然后重启 Java 应用 pyjab,它会引入其余依赖
安装
pip install pyjab-mcp
要直接开发服务本身,请运行 pip install -e ".[dev]"——参见参与开发。
或安装捆绑包:单个文件,无需构建
v0.1.0–v0.1.2 早于捆绑包,因此不包含任何资源;此后发布的每个版本都包含一个。它是
pyjab-mcp-<version>.mcpb——构建好的 wheel 及其完整依赖项,按支持的每个 CPython 次版本分别安装到一个目录树中,另加一个入口脚本,全部打包在单个文件中。每个目录树都包含软件包本身,因此该文件中每个次版本各有一份 pyjab_mcp,而不是在目录树旁边放一个 wheel。从发布页面下载并交给客户端:
Claude Desktop 只需打开 .mcpb 即可安装;详情请参阅 docs/CLIENTS.md。
此捆绑包不会改变上述任何要求,但会将其中一项限制为它实际包含的解释器。 它不包含 Python 解释器,也不包含 JDK:此服务会驱动 Java Access Bridge,因此机器仍须提供 64 位 Windows、64 位 CPython 3.10-3.13,以及启用了 Java Access Bridge 的 JDK;而且在连接到 Java 应用程序之前,该应用程序仍须处于运行状态。这个范围比没有上限的 pip install 更窄,而且这并非无关紧要的细节:捆绑包有几十 MB,因为它为每个受支持的次版本都包含一棵依赖目录树,这是因为 pydantic-core、cffi、rpds-py、cryptography 和 pywin32 提供的编译 wheel 都分别针对一个次版本打包。在其他任何解释器上启动时,它会停止运行,并用一句话指出检测到的解释器以及文件中包含的解释器,而不是抛出原生模块的 ImportError。
解释器必须是 PATH 上的 python。 清单未声明 user_config 回退选项,因此客户端会运行在该路径下解析到的 python;如果机器上安装了 CPython,但它不在 PATH 上,客户端将不会显示任何工具,也没有任何内容能说明原因:解释器根本没有启动,因此捆绑包自身的拒绝提示也无法运行。where.exe python
会打印将要使用的解释器;如果没有任何输出,请将安装目录添加到 PATH。
MCPB 不会强制实施任何权限限制。 清单级别没有沙箱机制——捆绑的服务会以安装它的用户的全部权限运行,与手动启动时完全一样。安装任何人提供的 .mcpb 前,请先阅读其源代码,包括此处的这个捆绑包。
使用方法
软件包安装的控制台脚本:
{
"mcpServers": {
"pyjab-mcp": { "command": "python", "args": ["-m", "pyjab_mcp"] }
}
}
Claude Desktop 会读取 %APPDATA%\Claude\claude_desktop_config.json,并接受以下配置(反斜杠要加倍——JSON 需要这样处理):
{
"mcpServers": {
"pyjab-mcp": { "command": "C:\\Users\\you\\.venv\\Scripts\\pyjab-mcp.exe", "args": [] }
}
}
Claude Code 会为你写入相同的条目:claude mcp add pyjab-mcp -- pyjab-mcp。
docs/CLIENTS.md 还介绍了 Cursor 和 VS Code、它们各自读取的配置文件、导致大多数设置失败的 Windows 路径问题,以及工具返回错误时的处理方法。
请从 java_diagnostics 开始。它会报告环境中缺少的部分——未启用 Java Access Bridge、找不到桥接 DLL、使用 32 位解释器(导致它搜索 32 位桥接 DLL)——而不是让工具调用以定位器错误告终。
它能做什么,以及不能做什么
Java Access Bridge 会公开应用程序声明的自身信息:由角色、名称、值、状态以及控件支持的操作组成的树。此服务正是通过这棵树来驱动应用程序,这也界定了它的能力范围。
它适用于公开了辅助功能信息的 Java 应用程序,包括作为桌面应用程序运行的 Swing 和 AWT 应用程序及小程序。不支持 JavaFX:它通过 UI Automation 而非 Java Access Bridge 发布辅助功能信息。按钮、菜单、表格、树、列表、文本字段及其值均可读取和操作;java_get_table 会通过表格自身的接口读取单元格,而不是读取像素。
它无法触及以下内容,因为其中没有可供读取的信息:
| 不支持的内容 | 原因 |
|---|---|
在 Canvas 上绘制的界面 — 自行绘制控件的 JPanel | 没有可访问的子元素,因此无从查找、点击或读取。java_screenshot 会显示一张图片,但图片不是可以操作的对象。如果需要驱动这样的面板,应用程序必须公开控件。这是经过测量的结果,并非预期推断。 pyjab 调查了一个通过 Canvas 绘制的面板(pyjab#73),发现没有可访问的子元素;本仓库现在针对这一情况新增了回归测试,使用的 fixture 应用会绘制面板且不包含任何子组件(tests/e2e/fixtures/BoundaryApp.java)。测试断言了这一对结果:面板是叶节点,children_count = 0;对同一个元素截图则会返回真实的 PNG 字节——有像素却无结构,这正是关键所在。该测试会在 windows-gui 作业中运行;pyjab 也记录了同一问题(pyjab#169),并于 2026-10-10 将其标记为已完成,期间为这类行为新增了四项回归测试。 |
| 浏览器内的 Java(嵌入式 JVM、Java Web Start 的后代进程) | 窗口由浏览器管理,浏览器不会通过 JAB 发布小程序的 Java 辅助功能树。 |
| 插件宿主中的小程序 | 原因相同:JAB 树不会传递到桌面。 |
| 非 Java Windows 应用程序 | 本项目不支持 UIA。应使用基于 UIA 的 MCP 服务。 |
| 任何基于像素的操作 — 读取截图来猜测布局、点击坐标 | 这有意不在支持范围内。这个项目的存在,正是为了取代对像素的猜测。 |
另请注意:
- 应用程序必须在启动前启用 JAB。 之后再启用 JAB,无法为已经运行的进程添加树;请重启应用程序。
- 此服务只会附加到应用程序。 它不会启动或终止应用程序——
JABDriver.detach()会释放绑定,但进程仍会继续运行。 - 图片不等于结构。
java_screenshot用于查看;你想要操作的任何内容都必须先在树中找到,因为句柄和名称都在树中。
刻意不纳入范围
上面的列表是底层基础无法触及的内容。下面是另一类:此服务本可以实现、但没有实现的功能,让评估者在将它与托管替代方案比较时,清楚知道会失去什么,以及要添加每项功能需要做哪些改动。
| 未提供 | 原因,以及添加该功能需要做什么 |
|---|---|
| 远程传输(streamable HTTP、SSE) | 它只通过 stdio 使用 MCP:客户端在本地启动它,并连接到同一台桌面设备上的应用程序。远程传输意味着需要可连接的 Windows 主机、身份验证机制,还需要决定第二个客户端可以对第一个客户端正在操控的应用程序做什么。 |
| 一次处理多个会话 | 会话开销很小且彼此独立(每个窗口一个 java_attach_window),所以目前可以同时处理多个窗口;缺少的是多租户仲裁机制——由谁拥有应用程序,以及两个 Agent 同时操控它时会发生什么。这是策略问题,而非功能问题。 |
| 审计日志 | 此处没有任何内容会记录操作日志。MCP 客户端保存的对话记录就是审计跟踪,其完整性由客户端负责。服务端日志需要一个只追加的文件,并决定哪些内容绝不能写入其中(窗口标题和单元格值都是用户数据)。 |
| 批量执行 | java_fill_form 是唯一的批量工具,而且是有意限制了范围的。通用的“按顺序运行这些工具调用”功能会变成一种附带安全模型的脚本语言,而客户端已经具备这种能力。 |
| CI 集成 | 此仓库中的端到端测试层就是针对此仓库的解决方案。从他人的流水线驱动其应用程序,需要一个近似无头的 Windows 运行器和桌面会话——这种运行器确实存在(windows-gui 作业使用的就是一个)——并且还需要在其上安装并启用 JAB 的应用程序。 |
每项都是有意设定的边界,而非尚未完成的功能;其中属于架构层面的部分(传输、多租户)会改变 ServerState 的结构,而不是作为附加功能并列存在。
支持
- 免费支持 — Bug、问题和功能请求请提交至
GitHub issues。请附上
pyjab-mcp --version的输出和java_diagnostics工具的输出:这两者通常能在环境问题被提出前就回答其中的大多数。 - 商业和企业支持 — 在受限网络内部署、适配特定应用程序或提供 SLA:请联系 [email protected](此软件包元数据中的邮箱地址)。开源软件包功能完整且不受限制;无需许可证密钥,没有配额,也没有任何为此而保留的功能。
验证
每次推送时运行的测试套件无法驱动应用程序:它需要 Windows、JDK、桌面会话和正在运行的 Swing 应用。该测试层位于 tests/e2e/,除非设置 PYJAB_MCP_E2E=1,否则会跳过;windows-gui.yml 会在托管的 windows-latest 运行器上运行它——可手动触发,也可按每周计划运行。它会在桥接程序空闲一段时间后启动第二个 JVM,因为在错误线程上运行的消息泵会通过其他所有测试,却恰好在此处失败。
gh workflow run windows-gui.yml -f java=17 # the end-to-end run and the JDK matrix
gh run watch
这不是状态检查:除非有人请求运行(或到了每周运行的时间),否则不会有人运行它,因此默认 CI 运行显示绿色仍然不能说明实时窗口的情况。
发布
由标签驱动:更新 src/pyjab_mcp/__init__.py,将变更日志中的 Unreleased 部分移至新版本之下,设置 server.json,然后运行 python tools/check_release_version.py vX.Y.Z 并打标签。
docs/RELEASING.md 介绍了完整流程、两项一次性设置(PyPI 待发布者和注册表命名空间),以及四个发布作业的作用。
进行中
python -m pytest tests/ # the portable suite, any platform
python tools/check_pyjab_api_surface.py # every pyjab call is public, released, recorded
python tools/check_documented_surface.py # docs/ARCHITECTURE.md's tool table and the code agree
python tools/check_local_only_files.py # maintainer notes stay unpublished
python tools/check_pyjab_readiness.py # which pyjab APIs the pin expects, and whether it resolves them
python tools/check_pyjab_changelog.py # every marked pyjab change has an answer here
python tools/check_published_references.py # shipped files do not cite unpublished ones
python tools/check_changelog_immutable.py # a released changelog section is frozen
python tools/check_release_version.py vX.Y.Z # the four places a version lives agree
python -m pytest tests/contract/ # the pyjab shapes this project depends on
python -m build && python tools/check_dist_contents.py --dist dist
另外两个脚本——check_release_l3.py 和 check_release_unpublished.py——只有在有标签或有运行记录可供读取时才有意义,因此它们会在发布流水线中运行,而不是手动运行。
这些守卫将项目规则转化为机械检查,采用与 pyjab 自身相同的形式:注册表列出允许的内容,并进行双向检查,因此新增调用点前,必须有人先写明其为何可接受。docs/ARCHITECTURE.md 列出了这些守卫及其各自的检查规则。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。