内容
# GodotMCP
MCP(模型上下文协议)服务器,暴露一个实时Godot 4.6(C#)项目给MCP客户端,如Claude。两个适配器表面 — **编辑器**(驱动编辑器构建游戏)和 **运行时**(操控正在运行的游戏)— 共享一个服务器核心,但由于Godot的要求,它们与不同的API进行通信。
```
MCP 客户端(Claude等)
│ JSON-RPC 2.0 via stdio
▼
┌─────────────────────────────┐
│ godot-mcp-server (.NET 8) │ 协议、工具注册、路由
└─────────────────────────────┘
│ 带分隔的JSON via本地TCP(默认为127.0.0.1:4936)
│
┌────┴─────┐
▼ ▼
编辑器 运行时
适配器 适配器
(EditorPlugin) (自动加载单例)
```
服务器是一个普通的.NET进程 — 无需Godot依赖,无需引擎启动即可测试。适配器是C#脚本,它们包含在您放入Godot项目的插件中。
## 仓库布局
```
server/
GodotMCP.Server.sln
GodotMCP.Server/ .NET MCP服务器(目标:net8.0)
addon/
godot_mcp/ Godot插件 — 复制到<your_project>/addons/godot_mcp/
plugin.cfg
编辑器/ EditorPlugin + 编辑器端处理程序
运行时/ 自动加载 + 运行时端处理程序
共享/ 传输、JSON↔Variant、主线程调度器
```
## 快速入门
### 1. 构建服务器
```bash
cd server
dotnet build -c Release
```
二进制文件位于 `server/GodotMCP.Server/bin/Release/net8.0/godot-mcp-server.dll`。使用 `dotnet GodotMCP.Server.dll` 运行它,或发布一个独立的可执行文件。
### 2. 在Godot项目中安装插件
```bash
cp -r addon/godot_mcp <your_project>/addons/
```
在Godot 4.6中打开项目,然后 **项目 → 项目设置 → 插件** 并启用 **Godot MCP**。这:
- 在EditorPlugin下添加编辑器适配器,它会立即连接到服务器
- 注册一个`GodotMcpRuntime`自动加载,以便运行时适配器在您播放游戏时启动
Godot项目必须启用C#(在您创建第一个C#脚本时就是如此)。插件的`.cs`文件被您的项目的主程序集拾取。
### 3. 将其连接到MCP客户端
示例Claude Desktop配置(`claude_desktop_config.json`):
```json
{
"mcpServers": {
"godot": {
"command": "dotnet",
"args": ["/abs/path/to/server/GodotMCP.Server/bin/Release/net8.0/GodotMCP.Server.dll"]
}
}
}
```
对于其他客户端,请指向同一个可执行文件。服务器通过stdio按照MCP规范使用换行分隔的JSON-RPC 2.0进行通信。
连接顺序无关紧要。适配器会重试并退避,因此您可以先启动服务器,然后再打开Godot。
## 服务器选项
| 标志 / 环境变量 | 默认值 | 含义 |
| --- | --- | --- |
| `--port <n>` / `GODOT_MCP_PORT` | `4936` | 适配器连接的TCP端口 |
| `--read-only` / `GODOT_MCP_READ_ONLY=1` | 关闭 | 拒绝任何可能改变编辑器或运行时状态的工具调用 |
| `--allow-unsafe` / `GODOT_MCP_ALLOW_UNSAFE=1` | 关闭 | 启用标记为`unsafe`的工具(目前为`editor_eval_expression`、`runtime_eval_expression`) |
| `--help` | — | 打印用法 |
在启动Godot之前,在shell中设置`GODOT_MCP_PORT`,如果您已更改服务器的端口 — 编辑器和运行时适配器读取相同的变量。
## 可用工具(v0.4)
**内置**
- `mcp_status` — 哪些适配器已连接,带有Godot版本和项目路径
- `mcp_list_tools_by_surface` — 按曲面分组的工具目录
**编辑器曲面**
状态 / 反思
- `editor_get_state` — 打开的场景、编辑的场景、选定的节点
- `editor_get_scene_tree` — 层次结构(带有`max_depth`和可选属性)
- `editor_get_class_reference` — 任意类的ClassDB方法/属性/信号
- `editor_resolve_id` — 实例ID → 当前路径
列表
- `editor_list_classes` — 按继承/子字符串/仅具体类筛选
- `editor_list_node_methods` / `editor_list_node_properties` / `editor_list_node_signals` / `editor_list_node_groups`
- `editor_list_input_actions`
- `editor_list_project_settings` — 按前缀筛选
- `editor_list_files` — 目录遍历,带有可选递归/扩展筛选
搜索
- `editor_find_nodes` — 按类型、名称子字符串、组membership
- `editor_grep_project` — 在.gd/.cs/.tscn/.tres/.gdshader上进行模式搜索
观察(AI的眼睛和耳朵)
- `editor_get_logs` — 尾随Godot日志文件,带有`since_last_call`进行增量轮询
- `editor_screenshot_viewport` — 主视口PNG,作为MCP图像内容块返回
场景突变
- `editor_add_node`、`editor_remove_node`
- `editor_set_node_property`、`editor_get_node_property`
- `editor_set_node_properties` — **批量**版本,一次往返多个编辑
- `editor_reparent_node` — 将节点移动到不同父节点下(可选地保留全局变换)
- `editor_move_node` — 在节点与其兄弟姐妹之间更改节点的索引(UI绘制顺序,容器布局)
- `editor_duplicate_node` — 深度复制带有脚本/组/信号
- `editor_save_scene`
- `editor_open_scene` — 打开一个`.tscn`
- `editor_create_scene` — 将子树打包到一个新的`.tscn`
- `editor_instantiate_scene` — 将`.tscn`添加到节点的子节点中
- `editor_attach_script`
选择 / 焦点
- `editor_select_node` — 在场景停靠中选择一个节点(或多个);给观看的人一个可见的提示
- `editor_focus_node` — 选择 + 将视口聚焦到一个节点
文件系统
- `editor_create_folder`、`editor_delete_file`、`editor_rename_file`、`editor_move_file`
自动加载
- `editor_list_autoloads`、`editor_add_autoload`、`editor_remove_autoload`
资源图
- `editor_get_resource_dependencies` — 此资源依赖于什么(前向边)
- `editor_find_resource_users` — 哪些文件引用此资源(后向边);在删除或移动资产之前调用
TileMap(适用于`TileMapLayer`和旧版`TileMap`)
- `editor_tilemap_get_cell`、`editor_tilemap_set_cell`、`editor_tilemap_paint_rect`、`editor_tilemap_get_used_cells`、`editor_tilemap_clear`
脚本和构建
- `editor_read_script`、`editor_write_script`、`editor_patch_script`(基于锚点)
- `editor_reload_scripts` — 触发文件系统重新扫描
- `editor_build_project` — `dotnet build`;返回从MSBuild输出中解析的**结构化`errors`/`warnings`**数组({file, line, severity, code, message}),以及原始stdout/stderr
项目设置
- `editor_get_project_setting`、`editor_set_project_setting`、`editor_save_project_settings`
输入映射编辑
- `editor_add_input_action`、`editor_remove_input_action`
- `editor_bind_input_event`(事件类型:`key`、`mouse_button`、`joy_button`、`joy_axis`)
- `editor_unbind_input_events`
资源(.tres / .res)
- `editor_read_resource`、`editor_write_resource`(使用`create: true, type: ...`创建)
播放控制
- `editor_play_scene` — 播放当前、主场景或特定场景
- `editor_stop_play`、`editor_is_playing`
评估(不安全 - 需要 `--allow-unsafe`)
- `editor_eval_expression` — 在编辑器中运行Godot表达式
**运行时曲面**
反思
- `runtime_get_tree` — 带有`max_depth`的实时场景树
- `runtime_get_node_property` / `runtime_set_node_property`
- `runtime_set_node_properties` — 批量
- `runtime_call_method`
- `runtime_get_performance` — FPS、帧时间、物理时间、节点/绘制计数
- `runtime_resolve_id`、`runtime_list_node_methods`、`runtime_list_node_properties`、`runtime_list_node_signals`、`runtime_list_node_groups`、`runtime_get_nodes_in_group`
- `runtime_find_nodes`
观察
- `runtime_get_logs`、`runtime_screenshot`
输入
- `runtime_inject_action` — 触发输入映射操作
- `runtime_mouse_move`、`runtime_mouse_button`、`runtime_mouse_scroll` — 通过`Input.parse_input_event`进行原始鼠标事件
- `runtime_key`、`runtime_text_input` — 合成键盘事件/键入文本
- `runtime_focus_control` — 调用`Control.grab_focus`,以便后续键/文本事件转到该控件
- `runtime_set_paused`
- `runtime_step_frames` — 确定性地推进N帧(异步;多帧等待)
信号
- `runtime_connect_signal`、`runtime_disconnect_signal`、`runtime_emit_signal`
- `runtime_listen_signal`、`runtime_unlisten_signal`、`runtime_list_signal_listeners` — 服务器在每次信号触发时推送`signal_fired`通知(以及源离开树时的`listen_ended`)
TileMap(适用于`TileMapLayer`和旧版`TileMap`)
- `runtime_tilemap_get_cell`、`runtime_tilemap_set_cell`、`runtime_tilemap_get_used_cells`
组
- `runtime_add_to_group`、`runtime_remove_from_group`
动画
- `runtime_animation_list`、`runtime_animation_play`、`runtime_animation_stop`、`runtime_animation_seek`
物理查询
- `runtime_physics_raycast_2d`、`runtime_physics_raycast_3d`
- `runtime_physics_overlap_point_2d`
场景操作
- `runtime_instantiate_scene` — 加载`.tscn`并在运行时添加
- `runtime_free_node` — `QueueFree`
- `runtime_change_scene` — `SceneTree.ChangeSceneToFile`
状态实验
- `runtime_snapshot_state` / `runtime_restore_state` — “尝试、观察、撤销”循环
基于推送的属性监视
- `runtime_watch_property` — 服务器在每次JSON值不同的情况下推送`notifications/message`,带有`name: 'watch_changed'`(以及节点释放时的`watch_ended`)
- `runtime_unwatch_property`、`runtime_list_watches`
评估(不安全 - 需要 `--allow-unsafe`)
- `runtime_eval_expression`
具有`Mutates = true`的突变工具在服务器启动时被拒绝,并且 `--read-only` 启动。标记为`Unsafe = true`的工具还受到 `--allow-unsafe` 的限制。
### 稳定实例ID
每个返回节点的工具也返回一个`id`(Godot的实例ID,作为数字字符串)。每个接受`path`的工具也接受`instance_id`作为替代。在需要一个在会话中生存的节点被用户重命名或重新父节点时,请使用ID — 如果节点已被释放,则会干净地失败。
### 错误提示
当路径或属性或方法或类的`node_not_found`错误发生时,错误会携带一个`hints.did_you_mean`数组,其中包含3个最接近的现有名称。因此,通常在下一次调用时,错误的NodePath会自我纠正。
### 图片内容
截图工具返回一个MCP `image`内容块(带有`mimeType`的base64 PNG)以及一个带有宽度/高度/字节计数的结构化回声。支持图片内容的客户端(包括Claude)会看到实际的图片。
## 设计笔记
**线程。** 所有处理程序都在Godot的主线程上运行,通过每个适配器的`MainThreadDispatcher`编组,该调度器在 `_Process` 中排空其队列。因此,突变发生在空闲帧之间,永远不会在物理步骤中。TCP读取循环在后台线程上运行。
**JSON ↔ Variant。** `addon/godot_mcp/Shared/WireJson.cs` 是尽力而为的:原始类型、向量、颜色、数组、字典、打包数组。复合类型接受 `{x,y,z}` 样式的对象或数组。对象引用序列化为 `{__type:"Object", class, id}` 信封,但不能在客户端重新水化。
**寻址。** 节点通过 `NodePath` 进行寻址。在编辑时,路径相对于编辑场景的根节点(容忍并剥离前导 `/`)。在运行时,路径从 `SceneTree.Root` 绝对开始(例如 `/root/Main/Player`)。
**C# 构建周期。** 在同一个MCP调用中创建脚本并实例化其新类型是不起作用的 — C#脚本在新的类可见之前需要重新构建。因此,`editor_add_node` 只添加现有的引擎类;`build_project` 动作加上脚本编写工具被跟踪为下一个明显的添加。
**只读模式。** `--read-only` 阻止任何标记为 `Mutates: true` 的工具在适配器到达之前在服务器上。 当您希望AI在不触摸场景的情况下自由检查时很有用。
**不安全模式。** `--allow-unsafe` 是针对标记为 `Unsafe: true` 的工具(目前为 `editor_eval_expression` 和 `runtime_eval_expression`)的单独门控。 默认关闭,因为表达式求值具有无边界的范围(任何注册的类,任何方法)。
**推送通知。** 服务器将适配器事件作为 `notifications/message`(级别=info,data.kind="godot.event")转发给MCP客户端。 `runtime_watch_property` 使用此通道推送 `watch_changed` / `watch_ended` 事件,而不是强制客户端轮询。
**异步处理程序。** 适配器可以注册异步处理程序(`Task<JsonNode?>`),这些处理程序跨多个帧运行 — 由 `runtime_step_frames` 使用,它等待N个 `SceneTree.ProcessFrame` 信号。 主线程调度器路由同步和异步处理程序; 信号在主线程上恢复,因为Godot在那里触发它们。
**结构化构建错误。** `editor_build_project` 将MSBuild输出解析为 `errors[]` / `warnings[]` 数组,包含 `{file, line, column?, severity, code, message}`。 文件路径在项目内规范化为 `res://`。原始 `stdout` / `stderr` 保留在响应中,以防正则表达式遗漏。
**撤销/重做集成。** 编辑器突变(`editor_add_node`、`editor_remove_node`、`editor_set_node_property` 及其批量变体、`editor_attach_script`、`editor_instantiate_scene`,新的 `editor_reparent_node` / `editor_move_node` / `editor_duplicate_node`,以及所有 `editor_tilemap_*` 写入)通过Godot的 `EditorUndoRedoManager` 与编辑场景一起作为 `custom_context`。人类可以撤销AI所做的任何操作。项目设置/自动加载/输入映射编辑直接通过 `ProjectSettings.set_setting` 编写,因为它们有自己的持久化路径,不参与场景撤销堆栈。
**推送信号订阅。** `runtime_listen_signal` 将 `Callable` 连接到节点的信号(参数调度最多8个参数)。每次触发都会发出带有JSON序列化参数的`signal_fired`事件,并作为`notifications/message`转发给MCP客户端。当源发出`tree_exiting`时,会触发`listen_ended`事件,以便客户端可以自行判断订阅已消失。
## 状态
v0.4 — 树编辑(重新父节点/移动/复制)、文件系统操作、自动加载管理、资源依赖图、编辑器选择/焦点、TileMap查询+编辑、信号订阅(事件推送)、原始鼠标/键盘/文本输入、完整的编辑器突变的撤销/重做包装。
v0.3 — 推送通知、监视、step_frames、运行时信号/组/动画/物理查询、输入映射编辑、项目设置R/W、场景CRUD、结构化构建错误、播放/停止场景、评估(不安全)。与Godot SDK 4.6.x编译干净;如果您在不同的补丁版本上遇到API漂移,请使用Godot版本和失败的处理程序打开一个问题。