← 返回博客

我们如何为 Godot 打造实时 MCP Server

拆解 VberAI 的 Godot MCP 架构:实时 Model Context Protocol 服务如何在不卡死场景树的前提下,把 Godot 编辑器接到 AI 客户端。

发布于
  • vberai
  • godot
  • mcp
  • architecture
  • realtime

为什么 Godot 上的 MCP Server 必须「实时」

多数 MCP Demo 活在 REST 心态里:模型问一句,工具回一段 JSON,往返两秒也没人在意。Godot 不一样。编辑器握着活的 场景树、资源导入管线,以及 Play 循环。若 MCP Server 堵住主线程——或只能看见过期的工程快照——AI 就不再像副驾,而像「多绕了一步的远程文件编辑器」。

我们为 VberAI 设计 Godot MCP 时,目标写得很清楚:

  • AI 助手(Cursor、Claude、Windsurf 等 MCP 客户端)必须能 操作编辑器,而不能只读写磁盘上的 .gd
  • 创建节点、重命名、挂脚本、查询选中项等动作,要在编辑器保持可响应的前提下完成
  • Play 模式与编辑模式上下文必须诚实——不安全的操作应结构化报错,而不是悄悄失败

本文是工程侧的复盘:我们如何做出一个贴着 Godot 跑的 实时 MCP Server,而不是把工程假装成静态仓库。

「只碰文件」的 MCP 对引擎为何不够

天真方案很诱人:

  1. 把 MCP Server 指到工程目录
  2. 暴露 read_file / write_file / list_dir
  3. 让模型编 GDScript,祈祷编辑器Reload顺利

这对文档站够用,对引擎工作会翻车:

  • 场景所有权在内存里 — 未保存的 .tscn、打开的场景、编辑器选中,磁盘上都看不见
  • 导入管线有状态 — 写入 PNG ≠「资源已按正确导入设置就绪」
  • 信号与节点路径是图数据 — 字符串乱改会静默拆线
  • 延迟会叠加 — 每次确认「节点出现了吗」都再扫一遍文件系统

我们需要的 MCP 表面,要贴近人在 Godot 停靠栏里已经看到的东西:层级、类似 Inspector 的属性、资源句柄——而不只是路径字符串。

架构总览

Godot MCP 可看成三层协作:

MCP 客户端(Cursor / Claude / …)
        │  JSON-RPC(stdio 或本地传输)

MCP Server(VberAI Godot 桥接进程)
        │  命令队列 + 结果信封

Godot 编辑器插件(GDExtension / Editor Plugin)
        │  主线程延迟调用

编辑器场景树 / ResourceDB / 脚本编辑器

第一层 — MCP 工具面

工具刻意做小、做成动词形态:

  • godot_get_scene_tree — 当前编辑场景的快照(类型 + 路径)
  • godot_create_node / godot_set_property
  • godot_attach_script / godot_run_script_snippet(受控)
  • godot_list_resources / godot_get_selection

避免一个巨大的 do_anything。小工具更容易校验、更好记日志,模型也更难刷出失控大补丁。

第二层 — 实时桥

「实时」真正发生在这里:

  • MCP 进程与编辑器插件之间的 双向通道(开发期常用本地 socket / 命名管道;产品构建可由 VberAI 桌面助手封装)
  • 命令队列,把写操作串行化,避免两次工具调用在场景树上竞态
  • 请求 ID + 确认,让 MCP 客户端等待「已应用」,而不是轮询文件系统
  • 心跳 / 编辑器存活探测,客户端能立刻知道 Godot 中途退出

第三层 — Godot 主线程安全

Godot 的 UI 与场景 API 不是自由多线程的。插件从不在 MCP I/O 线程上改节点,而是:

  1. 桥线程收到 MCP 命令
  2. 入队
  3. 编辑器在空闲帧 / call_deferred 上执行
  4. 结果信封返回成功、结构化错误,或「Play 结束后再试」

这套模式刻意无聊。无聊,才能让「实时」不变成「随机卡死编辑器」。

如何让它「感觉」实时(又不撒谎)

这里的实时不是魔法零延迟,而是 反馈环贴近人在编辑器里的工作节奏

快照 vs 增量

早期原型每次整树序列化,大开放世界工程一碰就塌。我们改成:

  • 默认 浅快照(根 + 一层,或以选中为中心)
  • 模型已知子树时用 路径作用域读取
  • 可选 变更 token:后续只问「相对 X 改了什么」,而不是整树重传

便于模型继续操作的结果

工具返回尽量包含:

  • 规范的 NodePath
  • 类型名(CharacterBody2DControl 等)
  • 能映射到 Inspector 字段的属性键
  • 写入已生效但场景仍 dirty / 未保存时的明确警告

结果看起来像 UI 状态,而不是散文,模型迭代会快很多。

有边界的 Play 行为

一半「AI 弄坏工程」的工单都发生在 Play 模式。我们的规则:

模式允许禁止或门控
编辑场景变更、资源查询无确认的破坏性工程删除
Play多为只读 / 查询运行时节点改正在编辑的场景结构
切换中等待 / 重试提示静默空操作

清晰错误胜过自作聪明。若 Play 中不可编辑,MCP 就用结构化结果说清楚——客户端可提示用户先停 Play,再重试。

我们踩过、并且留下的硬问题

1. 编辑器不是数据库

节点顺序、owner 关系、打包场景的组合,在演示 GIF 里很简单,在 .tscn 里很脏。我们尽量站在 Godot 自有 API 上(NodeEditorInterface、资源加载),而不是另造一套会漂移的平行场景模型。

2. 脚本与场景是两套真相源

挂上脚本 ≠ 编译通过且类名已解析。Server 把编译 / 挂载结果分开回报,压掉一类「工具报成功、Inspector 什么也没有」的故障。

3. 多窗口 / 多工程会话

开发者会开多个 Godot 实例。桥绑定明确的编辑器会话 ID,避免周末睡一觉再打开后,工具调用打进错误工程。

4. 安全边界

能改写场景的 MCP Server 权限很大。本地优先传输、显式工具白名单、禁止静默外传整棵工程树,是底线。「AI 助手」和「对着你的游戏开远程壳」必须是不同品类。

与 AI Studio 及 VberAI 栈的衔接

Godot MCP 扮演 引擎操作者。互补部件包括:

  • VberAI AI StudioAI Studio)— 设计稿(Figma/PSD)到引擎结构;MCP 再在导入后接线、重命名节点
  • Unity MCP / Cocos MCP — 同一套 MCP 思路,不同宿主编辑器与安全规则
  • AI Super Matting — 贴图进入引擎、再被 MCP 赋值之前做透明抠图

共同主张:AI 应触达 活的生产面(编辑器、资源、场景),而不只是仓库文本。

若你要自建引擎 MCP:几条实用建议

  1. 写操作排队到引擎主线程 — 别假装游戏引擎是无共享的服务器
  2. 工具宜小、有 schema — 校验胜过堆砌提示词
  3. 返回 NodePath 与类型,而不是散文 — 模型需要可操作句柄
  4. 每次响应标明 Play / 编辑模式 — 这里含糊会毁掉信任
  5. 度量「停靠栏里看见了」的往返 — 不只量 JSON 编码时间

若只优化 MCP 进程、忽略编辑器桥,你只会得到一条更快的幻觉回路。

结语

为 Godot 打造实时 MCP Server,本质是把编辑器当成在线协作者:队列化、主线程安全的桥;形状像编辑器动作的工具;以及对 Play 边界的诚实。只用文件的 MCP 更简单——但对场景原生工作来说不完整。

想直接走现成路径,而不是周末原型?从 VberAI Godot MCP 起步,接上你常用的 MCP 客户端,先做一次最简单的工具调用:列出当前场景树,再在选中节点下创建一个子节点。查询 → 变更 → 在停靠栏看见——这就是产品本身;其余都是围绕它的可靠性工程。

你可能还会喜欢这些文章