v1.0.0
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 inres://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'sget_console_log/get_errors.list_signal_connections(source: "runtime") — see what's actually connected at runtime vs. what the.tscnsays should be connected.
2. A reliable testing loop: run_scene that actually waits
run_scene now has:
block_until_started(defaulttrue) — 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_pathandruntime_root(e.g./root/Main) computed from the actual root node name — this is the correct prefix forquery_runtime_nodearguments 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_infointrospects anyResourceon 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.tresfirst.set_resource_propertyedits any property of any embeddedResource(CircleShape2D.radius,StandardMaterial3D.albedo_color,AudioStreamPlayer.stream.loop, …).save_resource_to_filepersists 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_nodenow acceptsscript,groups, andchildren— 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, anddisconnect_signal.list_signal_connectionsacceptssource: "scene_file" | "runtime"so you can compare what the.tscnsays 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-loopscene-editingasset-generationtroubleshootingtool-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_assetwas rewritten to decode SVG viaImage.load_svg_from_bufferinstead of writing a temp file inuser://. Also accepts optionalwidth,height, andscale. No more temp-file races, no more silent failure after project rename, and the single-quote attribute parser works now.update_project_settingsdetectsapplication/config/namechanges, pre-creates the newuser://directory, and returns a structured warning explaining why previously generated files look like they "disappeared."set_sprite_texturenow reportstexture_class,texture_path,width, andheightin its response. The canonicaltexture_typeisFromPath;ImageTextureis kept as a deprecated alias, andNewImageTextureforces an in-memoryImageTexturewhen you actually want one.modify_node_propertyrefusesproperty_name == "script"(it would only update the.tscnon disk, not the editor's live node, which silently broke subsequentconnect_signalcalls). Useattach_scriptinstead.delete_filedescription clarifies thatconfirm: trueis required and that a.bakbackup is written next to the deleted file.waitaccepts eithermsorseconds. Any value over 20 000 ms is clamped; the response includesrequested_msandclamped: truewhen that happens.
Bug fixes
Crashes
waitfroze the editor and crashed the session on large values. The old implementation usedOS.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_filecrashed 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-inforce: truebypasses the guard for known-safe cases.- Stale-primary replacement could SIGTERM Godot. On macOS/Linux
killProcessOnPortusedlsof -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.lsofis now filtered with-sTCP:LISTENso only listeners are killed. (Windows already filtered forLISTENING.)
Correctness
- Project rename silently broke asset generation and other
user://writes. Renaming the project re-resolvesuser://, and Godot does not always create the new folder. A newMCPPaths.ensure_user_dir()helper is called from every writer that still touchesuser://, and the MCP plugin's own scratch space moved underres://addons/godot_mcp/cache/(project-relative, survives renames). connect_signaldid not persist to.tscn. Connections were made with runtime-only flags, soPackedScene.pack()dropped them on save. We now forceObject.CONNECT_PERSISTon every connection AND re-read the saved scene to verify the[connection]entry landed. Returns a clear error instead of falsely reporting success.add_nodesilently accepted wrong keys insidechildren. Passing{node_name, node_type}(the same keys used at the top level) used to fall back to a genericNodewith 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'sopen_in_editor/where/is_active,wait'sclamped/requested_ms) had those extra fields stripped before the agent ever saw them.mcp_client.gdnow ships the full result dict on failure too, the bridge attaches it to the rejected error asdetails, and the server merges those details into the visible response. get_godot_statusreported a stale hardcoded version. The server now reads its version frompackage.jsonat startup, somode, MCPserverInfo, 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, andaddons/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 includesclamped: trueandrequested_ms. Existing usage under 20 s is unaffected. - If you were setting scripts via
modify_node_propertywithproperty_name: "script", switch toattach_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.gdANDruntime/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_readywithrole, plus a new test that verifies a runtime connection is accepted alongside an existing editor connection). - 52 automated tests passing;