Skip to content

v1.0.0

Choose a tag to compare

@TomasLucasUTN TomasLucasUTN released this 25 Jul 18:29
· 144 commits to main since this release

godot-mcp-bridge 1.0.0 — Runtime loop, generic resource editing, and a serious crash purge

This is the first public release under this name. It introduces a dedicated in-game runtime channel, a much richer set of scene/resource editing tools, an MCP resources layer for discoverable long-form docs, and fixes a half-dozen long-standing footguns — including two reproducible Godot editor crashes.


Headline features

1. Agents can now drive and observe the running game

Until 0.5.0, MCP tools could edit .tscn files and start run_scene, but once the game was running the agent went blind. This release ships a tiny MCPRuntime autoload that the plugin auto-registers when enabled. When you hit Play (or call run_scene), the autoload boots inside the running game and opens its own WebSocket connection to the server as role: "runtime". The server routes runtime tool calls to it, while editor tool calls still flow to the plugin.

With that in place, the agent can:

  • take_screenshot — capture the live viewport as a PNG (not an editor screenshot). Files land in res://addons/godot_mcp/cache/screenshots/ so they survive project renames.
  • send_input — synthesize a mouse button, motion, key, or named InputMap action directly into the running game. This is what makes the "AI plays the game" loop work.
  • query_runtime_node — read any property off a live node by path (/root/Main/Player, visible_global_position, global_transform, etc.).
  • get_runtime_log — pull from a ring buffer maintained inside the game, separate from the editor's get_console_log / get_errors.
  • list_signal_connections (source: "runtime") — see what's actually connected at runtime vs. what the .tscn says should be connected.

2. A reliable testing loop: run_scene that actually waits

run_scene now has:

  • block_until_started (default true) — returns only once Godot reports the scene is playing.
  • wait_for_runtime — additionally wait for the runtime autoload to connect back to the server before returning.
  • startup_timeout_ms (default 10000, up from 6000).
  • Returns scene_path and runtime_root (e.g. /root/Main) computed from the actual root node name — this is the correct prefix for query_runtime_node arguments and fixes a very common "node not found" footgun where the agent assumed the runtime root was named after the scene file.

Combined with the new get_runtime_status (unified playing / scene / uptime / runtime-connected report) and a non-blocking wait tool (see below), this gives the agent the primitives it needs to run a scripted testing loop: start the scene, wait for runtime, send input, screenshot, assert, repeat.

3. Generic resource editing

Previously get_resource_info only understood PNGs saved to disk. Now:

  • get_resource_info introspects any Resource on disk (textures, meshes, audio streams, packed scenes, materials, animations, shapes) AND accepts {scene_path, node_path, resource_property} to read a resource that lives on a node without having to save it to a .tres first.
  • set_resource_property edits any property of any embedded Resource (CircleShape2D.radius, StandardMaterial3D.albedo_color, AudioStreamPlayer.stream.loop, …).
  • save_resource_to_file persists an inline resource as a reusable .tres.

This pattern replaces a long tail of domain-specific tools we would have otherwise needed to add.

4. Scene-editing productivity

  • add_node now accepts script, groups, and children — build an entire subtree with scripts attached and groups assigned in one call.
  • set_node_properties — apply many property changes to one node in a single tool call, with per-property success/failure reporting. Cuts round-trips during scene setup.
  • Node groups: set_node_groups, get_node_groups, find_nodes_in_group — Godot group memberships are now first-class MCP operations.
  • Signal wiring in both directions: list_signal_connections, connect_signal, and disconnect_signal. list_signal_connections accepts source: "scene_file" | "runtime" so you can compare what the .tscn says with what is actually wired at runtime.

5. MCP resources + get_guide tool

Tool descriptions have been trimmed. The long-form guidance moved to proper MCP resources under godot-mcp://guide/...:

  • testing-loop
  • scene-editing
  • asset-generation
  • troubleshooting
  • tool-index

For clients that do not implement resources/list / resources/read (Claude Desktop, Cursor chat, etc.), the same guides are exposed through the new get_guide tool: call with no args to list, or {slug: "testing-loop"} to read.


Tool changes worth flagging

  • generate_2d_asset was rewritten to decode SVG via Image.load_svg_from_buffer instead of writing a temp file in user://. Also accepts optional width, height, and scale. No more temp-file races, no more silent failure after project rename, and the single-quote attribute parser works now.
  • update_project_settings detects application/config/name changes, pre-creates the new user:// directory, and returns a structured warning explaining why previously generated files look like they "disappeared."
  • set_sprite_texture now reports texture_class, texture_path, width, and height in its response. The canonical texture_type is FromPath; ImageTexture is kept as a deprecated alias, and NewImageTexture forces an in-memory ImageTexture when you actually want one.
  • modify_node_property refuses property_name == "script" (it would only update the .tscn on disk, not the editor's live node, which silently broke subsequent connect_signal calls). Use attach_script instead.
  • delete_file description clarifies that confirm: true is required and that a .bak backup is written next to the deleted file.
  • wait accepts either ms or seconds. Any value over 20 000 ms is clamped; the response includes requested_ms and clamped: true when that happens.

Bug fixes

Crashes

  • wait froze the editor and crashed the session on large values. The old implementation used OS.delay_msec, which blocks the editor's main thread — which also pumps the WebSocket. Any wait of ~30 s or more caused the MCP server's 30 s per-request timeout to fire on a still-running tool call; the server would reject the pending Promise and then Godot would try to write the eventual result to a disconnected socket. Net result: crash / broken session. The tool is now non-blocking (SceneTree.create_timer(...).timeout), the hard cap is 20 000 ms to leave buffer under the transport timeout, and the dispatch path (tool_executor.gd, plugin.gd) was made coroutine-aware so the async tool is awaited correctly.
  • delete_file crashed Godot when deleting a scene open in the editor. The previous guard only covered the simplest case. The tool now refuses to delete any file that is open anywhere in the editor (scene tab or script-editor tab), names which tab holds it, and reports whether it is the active one. Opt-in force: true bypasses the guard for known-safe cases.
  • Stale-primary replacement could SIGTERM Godot. On macOS/Linux killProcessOnPort used lsof -ti :PORT, which returns any process with a socket on that port — including WebSocket clients. Godot, as the client on port 6505, was on that list. When a newer MCP server replaced an older primary (e.g. Claude app starting while Cursor's older server was running), the port was killed twice and the second kill landed on Godot. lsof is now filtered with -sTCP:LISTEN so only listeners are killed. (Windows already filtered for LISTENING.)

Correctness

  • Project rename silently broke asset generation and other user:// writes. Renaming the project re-resolves user://, and Godot does not always create the new folder. A new MCPPaths.ensure_user_dir() helper is called from every writer that still touches user://, and the MCP plugin's own scratch space moved under res://addons/godot_mcp/cache/ (project-relative, survives renames).
  • connect_signal did not persist to .tscn. Connections were made with runtime-only flags, so PackedScene.pack() dropped them on save. We now force Object.CONNECT_PERSIST on every connection AND re-read the saved scene to verify the [connection] entry landed. Returns a clear error instead of falsely reporting success.
  • add_node silently accepted wrong keys inside children. Passing {node_name, node_type} (the same keys used at the top level) used to fall back to a generic Node with the default name. Child specs now accept BOTH {name, type} and {node_name, node_type} and reject any unknown keys with a clear error.
  • Structured error details were dropped on the wire. Tools that returned {ok: false, error, …extra fields} (e.g. delete_file's open_in_editor / where / is_active, wait's clamped / requested_ms) had those extra fields stripped before the agent ever saw them. mcp_client.gd now ships the full result dict on failure too, the bridge attaches it to the rejected error as details, and the server merges those details into the visible response.
  • get_godot_status reported a stale hardcoded version. The server now reads its version from package.json at startup, so mode, MCP serverInfo, and the status payload always agree with the actually-installed package.

Upgrade notes

  • Versions bumped to 1.0.0 across mcp-server/package.json, mcp-server/server.json, and addons/godot_mcp/plugin.cfg.
  • If you wrote a client against wait({ms: 60000}), note that values above 20 000 are now clamped and the response includes clamped: true and requested_ms. Existing usage under 20 s is unaffected.
  • If you were setting scripts via modify_node_property with property_name: "script", switch to attach_script — the old path now errors out with a pointer to the right tool.
  • The new runtime autoload is auto-registered on plugin enable and auto-removed on disable. It does not touch any non-MCP autoloads the user has configured.

Tests

  • Tool-registry alignment now scans both tool_executor.gd AND runtime/mcp_runtime.gd, enforcing that every advertised tool is implemented in exactly one of them.
  • Bridge tests updated for the editor/runtime split (explicit godot_ready with role, plus a new test that verifies a runtime connection is accepted alongside an existing editor connection).
  • 52 automated tests passing;