内容
# Godot Stagehand
[](https://github.com/mrf/godot-stagehand/actions/workflows/ci.yml)
[](https://github.com/mrf/godot-stagehand/issues)
[](https://github.com/mrf/godot-stagehand/pulls)
[](LICENSE)
用于运行Godot游戏的外部自动化工具,作为MCP服务器为AI代理提供服务,并作为CLI和场景运行器为CI和终端调试提供服务。想象一下Playwright,但它是为游戏引擎设计的。
> ### Stagehand 控制正在运行的游戏,而不是Godot编辑器。
>
> 这是在阅读更多内容之前需要明确的一点,因为大多数Godot MCP服务器都做相反的事情。它们是**创作**工具:它们打开编辑器,创建场景和节点,编写脚本,连接资源。代理为您构建游戏。
>
> Stagehand不做这些事情。它没有编辑器集成,无法创建场景,也不会触及项目文件。它附加到一个已经运行并正在播放的游戏中,单击真正的按钮,在运行时读取真正的节点状态,拍摄真正的屏幕截图,捕获真正的回归。
>
> **想要一个代理来构建您的游戏?** 使用编辑器MCP服务器。
> **想要一个来玩和测试它?** 就是这个。
**状态:测试版,预1.0.** 预构建的二进制文件已发布,支持Linux,macOS(Intel和Apple Silicon)和Windows - 请参见[设置](#设置)。
工具模式和线路协议可能会在小版本之间发生变化。
**Stagehand新手?** → [快速入门指南](docs/quickstart.md),无需Go或JSON经验。
## 为什么
游戏测试是手动的。你点击菜单,观察结果,并希望你能捕获到回归。Godot自己的测试工具(GUT,GdUnit4)在编辑器或无头引擎实例中运行在进程内,因此它们无法为正在运行的游戏提供外部进程的实时连接。现有的Godot MCP服务器旨在解决问题的另一半:帮助代理在编辑器中创作项目,这使得正在运行的游戏仍然无法被观察。
Stagehand填补了运行时这一部分的空白。
Stagehand为MCP客户端(Claude,另一个AI代理或您自己的MCP调用脚本)提供了一个与正在运行的游戏的真实连接。单击按钮,读取属性,等待信号,拍摄屏幕截图,断言性能,所有这些都在引擎外部。
## 对于AI代理(MCP)
将MCP客户端(Claude Code,Claude Desktop,Cursor,任何支持MCP的客户端)指向二进制文件,它将获得与正在运行的游戏的实时连接:单击按钮,读取节点状态,等待信号,拍摄屏幕截图,断言性能,记录和重放输入。
```json
{
"mcpServers": {
"godot-stagehand": {
"command": "/绝对路径/to/godot-stagehand"
}
}
}
```
- **AI辅助游戏测试。** 让Claude探索您的游戏,查找错误并在无需手动点击的情况下验证修复。
- **视觉回归测试。** 保存基准截图,稍后进行差异比较。在玩家之前捕获UI回归。有关如何在游戏仓库中设置视觉门控,请参见[视觉烟雾合约](docs/visual-smoke-contract.md)。**无头Godot无法渲染真实截图**,因此即使在CI中也需要一个可见的窗口(真实显示或类似Xvfb)。
- **输入记录/重放。** 记录播放会话的输入事件,带有毫秒时间戳,然后在相同的时钟时间表上重放它们,可选地加速。 这会重现粗略的重现案例,而不是完美的帧确定性运行:实际游戏状态在重放期间仍然取决于帧时序,这可能会在运行之间发生变化。 磁盘格式是版本化的;请参见[记录格式](docs/recording-format.md)。
- **性能监控。** `godot_assert_performance`可以采样和断言监视器:可选的预热,固定的样本数或持续时间,以及一个统计数据(最小值,最大值,平均值,中位数,p95)以阈值来对抗,而不是一次瞬时读取。 这仍然不是经过验证的统计回归门控(无基准跟踪,离群值拒绝或方差感知阈值),因此将其视为更稳定的烟雾检查,而不是认证的回归门控。
- **代理技能。** [`skills/stagehand.md`](skills/stagehand.md)向代理教授完整的工具工作流程 - 启动,检查,交互,测试 - 这样您就不必在每个会话中重新解释它。 将您的代理指向文件,或将其复制到您的客户端加载自定义技能的位置(例如Claude Code项目的`.claude/skills/`目录)。
完整的操作指南:[快速入门指南](docs/quickstart.md)。完整的工具列表:[可用工具](#可用工具)如下。
## 用于CI和测试自动化(CLI)
同一个二进制文件也是一个独立的CLI和场景运行器 - 不需要MCP客户端。将其用于终端调试或作为CI管道步骤。
```bash
# 对正在运行的游戏进行一次性检查和操作
export STAGEHAND_AUTH_TOKEN=<此Godot会话打印的令牌>
godot-stagehand tree --port 26788 --max-depth 3
godot-stagehand find --port 26788 'class:Button' --properties text
# 整个场景,无需MCP客户端参与
godot-stagehand run scenarios/menu-smoke.json --out-dir ci-artifacts
```
- **CI检查。** `godot-stagehand run scenario.json`在真正的Godot构建上执行声明性的启动,操作,等待和断言步骤,并在失败时退出非零,因此管道步骤不需要MCP客户端和包装脚本。 它发出JSON报告,JUnit XML,截图/差异,引擎自己的日志和RPC时间跟踪。 无头Godot涵盖结构检查(场景树,属性,性能计数器); 视觉步骤需要渲染的窗口。
- **脚本化探索。** `tree`,`find`,`property get`,`input click`,`wait node`等是打印JSON的一次性命令 - 适用于不编写场景文件的终端调试。
- **稳定的退出代码。** `0`是成功,`5`是真正的回归(断言或视觉差异失败),`6`是超时 - 有关完整合约,请参见[退出代码表](#命令行)。
完整参考:[CLI和场景运行器指南](docs/cli.md),或下面的[命令行](#命令行)部分。
## 工作原理
```
┌─────────────┐ stdio ┌───────────────────┐ ┌──────────────┐
│ MCP客户端 │◄───────►│ │ │ │
│ (Claude, │ │ godot-stagehand │◄───────►│ 您的游戏 │
│ Cursor…) │ │ (一个Go │ WS │ (Godot + │
├─────────────┤ argv │ 二进制文件) │ :26700 │ 插件) │
│ CLI / CI / │◄───────►│ │ │ │
│ 场景 │ └───────────────────┘ └──────────────┘
└─────────────┘
```
**插件** 生活在您的Godot游戏中。它打开一个WebSocket端口并等待命令。当它收到命令(例如“单击此按钮”或“获取场景树”)时,它在正在运行的游戏中执行它并发送结果。
**Go二进制文件** 位于中间,讲Godot线路协议。在另一侧,它提供了两个前端:用于AI代理的MCP stdio协议和用于管道和人类的CLI和场景运行器。两者并非建立在彼此之上。它处理连接管理,选择器解析,截图编码和错误转换,以便插件保持简单。
运行带有**无参数**的二进制文件通过stdio提供MCP服务。这是MCP客户端配置调用的内容,它没有改变。
## 可用工具
| 工具 | 描述 |
|------|-------------|
| `godot_connect` | 身份验证并连接到正在运行的游戏 |
| `godot_launch` | 启动Godot并连接,带有新鲜的会话密钥 |
| `godot_status` | 连接状态 |
| `godot_list_instances` | 列出由该服务器管理的活动Godot连接 |
| `godot_disconnect` | 断开并删除命名实例 |
| `godot_get_tree` | 快照场景树 |
| `godot_find_nodes` | 按选择器查找节点 |
| `godot_get_accessibility_tree` | 语义UI视图:角色,可访问名称,状态(Godot 4.5+) |
| `godot_get_property` / `godot_set_property` | 读取/写入节点属性 |
| `godot_call_method` | 调用节点上的方法 |
| `godot_evaluate` | 评估GDScript表达式 |
| `godot_click` | 点击节点或坐标 |
| `godot_press_key` | 模拟键盘输入 |
| `godot_press_action` | 触发输入操作 |
| `godot_focus_window` | 焦点窗口(例如模态对话框),使键盘输入到达它 |
| `godot_touch` | 模拟触摸/拖动 |
| `godot_type_text` | 在控件中输入文本 |
| `godot_mouse_move` | 移动鼠标光标 |
| `godot_screenshot` | 捕获视口 |
| `godot_screenshot_save_baseline` / `godot_screenshot_diff` | 视觉回归测试([指南](docs/visual-regression.md)) |
| `godot_wait_for_node` | 等待节点存在 |
| `godot_wait_for_signal` | 等待信号发射 |
| `godot_wait_for_property` | 等待属性条件 |
| `godot_change_scene` | 更改场景 |
| `godot_get_game_state` | 运行时信息(场景,FPS,窗口) |
| `godot_get_performance` / `godot_assert_performance` | 性能监控 |
| `godot_record_start` / `godot_record_stop` / `godot_replay` | 输入记录/重放 |
## 命令行
同一个二进制文件是一个可执行的测试运行器。完整参考:[docs/cli.md](docs/cli.md)。
```bash
# 对正在运行的游戏进行一次性检查和操作
export STAGEHAND_AUTH_TOKEN=<此Godot会话打印的令牌>
godot-stagehand tree --port 26788 --max-depth 3
godot-stagehand find --port 26788 'class:Button' --properties text
godot-stagehand property get --port 26788 'name:titleLabel' text
godot-stagehand input click --port 26788 --selector 'text:Start'
godot-stagehand wait node --port 26788 'name:Hud' --state visible
godot-stagehand performance --port 26788 --assert TIME_FPS --threshold 55 --op gte
# 整个场景,无需MCP客户端参与
godot-stagehand run scenarios/menu-smoke.json --out-dir ci-artifacts
```
`run`启动(或连接到)Godot,执行有序操作,等待和断言,并在失败时退出非零。 `--out-dir`收集`report.json`,`junit.xml`,`rpc-trace.json`,`godot.log`,截图和差异图像。
退出代码是一个稳定的契约:
| 代码 | 含义 |
|------|---------|
| 0 | 成功 |
| 1 | 内部错误 |
| 2 | 用法:错误的标志或无效的场景;未到达Godot |
| 3 | 连接:无法启动,访问或验证 |
| 4 | Godot拒绝了格式良好的请求 |
| 5 | **断言或视觉差异失败**:真正的回归 |
| 6 | 超时 |
`godot-stagehand actions`列出场景步骤可能使用的每个操作。
## 选择器
使用熟悉的模式定位节点。完整的语法,匹配规则和链接语义:[docs/selectors.md](docs/selectors.md)。
| 语法 | 示例 | 查找 |
|--------|---------|-------|
| 路径 | `/root/UI/StartButton` | 位于确切路径的节点 |
| 名称 | `name:*Button*` | 节点名称的全局匹配 |
| 类 | `class:Button` | 所有类节点 |
| 组 | `group:interactive` | 所有节点组 |
| 文本 | `text:Start` | 包含文本的节点 |
| 元数据 | `meta:id=player` | 具有元数据的节点 |
| 角色 | `role:button` | 具有可访问性角色的节点(Godot 4.5+) |
| 链 | `class:Panel >> name:*Btn*` | 作用域搜索(在范围内查找) |
## 安全边界
Stagehand是一个开发自动化控制平面,而不是公共游戏端点。它默认绑定到`127.0.0.1`,并在每个WebSocket对等体提供当前会话令牌之前拒绝所有命令。 `godot_launch`自动创建并使用新鲜的密钥进行身份验证;手动/编辑器启动生成一个并在本地Godot输出中打印出来。
远程绑定需要非环回`STAGEHAND_BIND_ADDRESS`和`STAGEHAND_ALLOW_REMOTE=1`,并发出显着的警告。仅在具有适当主机防火墙的受信任网络上使用它,并且永远不要发布令牌。 WebSocket传输未加密;此边界不是TLS,网络隔离或可信本地主机的替代品。表达式求值和任意方法调用在会话单独选择加入不安全功能之前被禁用。身份验证限制谁可以访问自动化;不安全选择加入控制经过身份验证的对等体可以执行的操作。
**在下面的设置步骤之前阅读此内容。** 一旦启用了带有Stagehand的游戏,任何拥有会话令牌的人都可以检查和修改其状态,包括调用任意方法(如果选择了不安全功能)。像对待其他本地开发/调试端口一样对待它。
## 设置
以下步骤使用终端。 如果不喜欢,可以复制`addons/stagehand/`文件夹到项目中,并在**项目→项目设置→插件**中启用它(参见[快速入门,第2步](docs/quickstart.md#step-2-install-the-addon)),然后在编辑器工具栏中出现的**设置...**按钮将下载服务器二进制文件,生成MCP客户端配置片段,并测试与正在运行的游戏的连接 - 无需触摸终端即可覆盖步骤1,4和部分3。
### 1. 获取服务器二进制文件
**下载** - 无需Go工具链。 从[最新版本](https://github.com/mrf/godot-stagehand/releases/latest)选择您的平台:
| 平台 | 文件 |
|----------|------|
| Linux x86-64 | `godot-stagehand-linux-amd64` |
| macOS Apple Silicon | `godot-stagehand-darwin-arm64` |
| macOS Intel | `godot-stagehand-darwin-amd64` |
| Windows x86-64 | `godot-stagehand-windows-amd64.exe` |
或直接从终端(macOS / Linux - 替换您平台的文件):
```bash
curl -fsSLo godot-stagehand \
https://github.com/mrf/godot-stagehand/releases/latest/download/godot-stagehand-linux-amd64
chmod +x godot-stagehand
```
<details>
<summary><strong>从源代码构建</strong>(适用于贡献者或不支持的平台)</summary>
需要Go 1.25+和Godot 4.3+:
```bash
go build -o godot-stagehand .
```
</details>
### 2. 安装到您的Godot项目(一个命令)
```bash
godot-stagehand setup /path/to/your/godot/project
```
这会复制插件,启用插件并注册`StagehandServer`自动加载。 它是幂等的,因此可以安全地重新运行。 然后它会打印MCP客户端配置片段(带有此二进制文件的检测路径)和运行游戏的精确命令。 传递`--force`以覆盖现有的插件安装。 在WSL上,它还会打印WSL特定的连接指南。
> 旧的`./copy-addon.sh`脚本已弃用;它现在转发到
> `godot-stagehand setup`.
### 3. 运行游戏并启用Stagehand
```bash
# 您自己的项目,或您控制的任意位置
godot --path /path/to/your/project --stagehand
# 安装到现有第三方项目
STAGEHAND_ENABLED=1 godot --path /path/to/your/project
```
如果安装到您编写的项目,请使用环境变量。 许多主机项目(编辑器,工具,任何具有自己的`--help`)解析它们自己的命令行参数,并将拒绝`--stagehand`,即使Stagehand本身启动正常。
您应该在输出中看到一次性身份验证令牌,接着是`Stagehand: Server listening on port 26700 (127.0.0.1)`。 保留令牌的私密性;将其用作`godot_connect`的`auth_token`参数。
### 4. 添加到您的MCP客户端
`setup`命令为您打印此片段;将其添加到您的MCP客户端配置中(例如`.claude/settings.json`):
```json
{
"mcpServers": {
"godot-stagehand": {
"command": "/绝对路径/to/godot-stagehand"
}
}
}
```
使用启动`auth_token`调用`godot_connect`以附加到正在运行的游戏。 本地Linux / macOS和WSL内的Linux Godot使用默认的`127.0.0.1`。 有关Windows Godot在WSL中受控的远程绑定,请参见[Windows设置指南](docs/windows-setup.md)。
> **Windows / WSL?** 请参见[Windows设置指南](docs/windows-setup.md)。
**可选:给予您的代理技能。** [`skills/stagehand.md`](skills/stagehand.md)是一个现成的技能文件,涵盖上述工具工作流程。 它存储在此源树中(不在二进制文件发布中,因为它是一个提示文件,而不是运行时资产) - 将其复制到您的代理加载自定义技能的位置(例如Claude Code项目的`.claude/skills/`目录)。
## 配置
| 方法 | 示例 |
|--------|---------|
| 命令行标志 | `godot --stagehand` |
| 环境变量 | `STAGEHAND_ENABLED=1 godot ...` |
| 编辑器开关 | 工具栏中的 Stagehand 按钮 |
| 发布导出选项 | `STAGEHAND_ENABLED=1 STAGEHAND_ALLOW_RELEASE=1 ./game` |
| 自定义端口 | `STAGEHAND_PORT=9999` 或 `--stagehand-port=9999` |
| 固定身份验证令牌 | `STAGEHAND_AUTH_TOKEN=<secret>` (否则会生成并打印一个新的令牌) |
| 绑定地址 | `STAGEHAND_BIND_ADDRESS=127.0.0.1` (默认环回地址) |
| 远程访问 | `STAGEHAND_BIND_ADDRESS=0.0.0.0 STAGEHAND_ALLOW_REMOTE=1` |
| 不安全方法 | `STAGEHAND_ALLOW_UNSAFE=1` 或 `godot_launch(allow_unsafe=true)` |
| 普通 RPC 超时 | `STAGEHAND_CALL_TIMEOUT_MS=30000` (默认;1–86400000 毫秒) |
| 严格的多实例模式 | 在 MCP 服务器进程上设置 `STAGEHAND_MULTI=1` (使 `godot_connect` 的 `port` 成为必填项) |
编辑器开关存储在编辑器专属项目元数据中,并且仅在编辑器启动游戏时注入 `--stagehand`。它永远不会被写入为运行时项目设置。发布导出会忽略普通的命令行标志和 `STAGEHAND_ENABLED`,除非还明确设置了 `STAGEHAND_ALLOW_RELEASE=1`。
普通的 Godot 工具调用在 30 秒后超时,因此冻结的游戏无法无限期占用 MCP 工作线程。 在 MCP 服务器进程上设置 `STAGEHAND_CALL_TIMEOUT_MS` 以有意更改该默认值。`godot_wait_*` 工具改为遵循其明确的 `timeout_ms` 值加上一个短的传输缓冲区。最多有四个远程 Godot 操作并发运行,为本地状态和断开连接请求保留一个 MCP 工作线程,如果游戏冻结。WebSocket 传输每 10 秒发送一个 ping,并且需要在 30 秒内收到一个 pong 或其他入站消息;一个沉默的对等方会触发重新连接。
## 同时运行多个代理
该插件接受许多 WebSocket 客户端进入**一个** SceneTree,默认端口为 26700。因此,两个都使用默认值调用 `godot_connect` 的代理驱动相同的游戏:它们的输入、属性写入和场景更改交织在一起,测试变得不再可复制。`instance_id` 仅在单个 MCP 服务器进程内隔离连接,而不是跨进程。
**启动您自己的实例。这是一条铺好的路。** `godot_launch(project_path=...)` 默认设置为 `port=0`,自动分配一个空闲端口,因此它启动的游戏对启动它的代理是私有的。为您知道是您自己的游戏保留 `godot_connect`,并传递其明确的 `port`。
托管多个代理的主机可以 在 MCP 服务器进程上设置 `STAGEHAND_MULTI=1`。在此模式下,`godot_connect` 拒绝回退到共享默认值并需要显式的 `port`,因此意外默认连接会大声失败而不是静默地加入其他人的游戏。单实例设置不需要新的参数;保持 `STAGEHAND_MULTI` 未设置。
## Godot 版本兼容性
**最低支持版本:Godot 4.3.** 开发发生在 4.6.x 上;4.3-4.7 都经过测试和支持。
| Godot 版本 | 状态 | 备注 |
|----------------|--------|-------|
| 4.2 | **不支持** | 插件无法解析;CI 未测试(见下文) |
| 4.3 | 支持(最低) | |
| 4.4 | 支持 | |
| 4.5 | 支持 | |
| 4.6 | 支持(本地开发基准) | |
| 4.7 | 支持 | |
通过针对每个版本的真实无头 Godot 二进制文件运行完整的 connect-and-drive 协议(解析 → 激活 → 身份验证 ping → `get_tree`/`find_nodes`/`click`/`screenshot`)进行验证。参见 `scripts/test-godot-compat.sh` 和 `.github/workflows/ci.yml` 中的 `gdscript-parse` 作业,它在每次推送/拉取请求到 `main` 时运行此矩阵。
### 已知不兼容性
- **Godot 4.2:插件依赖的两个 4.3+ 功能。** 在真正的 `Godot_v4.2-stable_linux.x86_64` 二进制文件上验证:
1. **GDScript `is not`.** 插件出于可读性考虑使用 `is not`(例如 `stagehand_server.gd`、`input_recorder.gd`、`protocol/json_rpc.gd`)。该运算符在 [godotengine/godot#87939](https://github.com/godotengine/godot/pull/87939) 中添加,首次在 Godot 4.3 中发布,因此 4.2 在加载时失败,出现 `Parse Error: Expected type specifier after "is"`。
2. **`OS.get_entropy()`** (`addons/stagehand/autoload/stagehand_server.gd`),它生成每个会话的身份验证令牌。同样是 4.3+;在 4.2 上,它报告 `Static function "get_entropy()" not found in base "GDScriptNativeClass"`。
它仅在 `is not` 错误清除后出现。
支持 4.2 需要重写这些检查为 `not (x is T)` 并在令牌路径中添加 `Crypto.generate_random_bytes()` 回退。4.2 被视为不支持,而不是为一个较旧的版本保留两者,并且 CI 不运行它。兼容矩阵中的每个作业都是阻塞的,因此红色作业意味着真正的回归。
## 故障排除
**"连接被拒绝"**:游戏未使用 `--stagehand` 运行,或主机/端口错误。
**"未知选项:--stagehand"**:主机项目解析其自己的命令行参数,不识别 `--stagehand`,因此在 Stagehand(已经启动正常)可用之前中止。使用 `STAGEHAND_ENABLED=1 godot --path /path/to/your/project` 代替 - 它完全绕过参数解析。参见上面的 [步骤 3](#3-使用-stagehand-启用运行您的游戏)。
**"身份验证需要/失败"**:传递此 Godot 会话打印的令牌,或其配置的 `STAGEHAND_AUTH_TOKEN`,作为 `godot_connect(auth_token=...)`。先前运行生成的令牌无效。
**"连接重置"**:Godot 已启动,但 `_process` 未被触发(通常在无头且场景较重时)。使用可见窗口或较轻场景。
**截图为空、白或灰色**:视觉工作流需要可见的渲染窗口。使用 `godot_launch(headless=false, expect_screenshots=true)`;无头启动用于结构化工具。
**端口冲突**:另一个实例占用 26700。设置 `STAGEHAND_PORT=26701`。
**插件不在插件列表中**:再次运行 `godot-stagehand setup /path/to/project`;它幂等地启用插件和自动加载。
**读取失败**:每个失败的调用都会以错误返回,从不作为看似成功的结果,其文本命名方法、选择器、稳定的机器可读类型和下一步骤尝试。类型和它们的 JSON-RPC 代码在[错误模型](docs/error-model.md)中列出。
## 开发
```bash
go vet ./... # 校验
go test ./... # Go 测试(无需 Godot)
# 针对真正的无头 Godot 运行场景测试
GODOT_BIN=/path/to/godot go test -tags=godot -run '^TestScenarioRunner' .
# GDScript 单元测试套件(GdUnit4,无头,需要 Godot 4.6+)
GODOT_BIN=/path/to/godot ./scripts/run-gdscript-tests.sh
```
参见 [GDScript 测试指南](docs/gdscript-testing.md),了解套件布局和严格模式规则测试文件必须遵循,[插件同步合约](docs/addon-sync-contract.md),在编辑任何插件副本之前, 和 [错误模型](docs/error-model.md),在添加可能失败的处理程序之前。
## 许可证
MIT。参见 [LICENSE](LICENSE)。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器