Skip to content

Releases

v4.0.1

Choose a tag to compare

@github-actions github-actions released this 11 Nov 08:38

Full Changelog: v4.0.0...v4.0.1

Earlier 4.x release notes: v4.0.0

v4.0.0

Choose a tag to compare

@github-actions github-actions released this 11 Nov 07:34

4.0 is the biggest release of the server so far, built so that every tool's answer is something an agent can trust. All 43 tools now return structured results, several projects can run at once, and new tools cover resources, node moves, project settings and render checks. I replaced the script security scan with a one-time confirmation per project. This release breaks clients written against 3.8.1 in several places, and Migrating to 4.0 lists every breaking case with its before and after.

Breaking changes

  • Godot 4.5 or later is required. Older engines cannot parse the server's GDScript. CI runs on 4.5.1, 4.6.2 and 4.7.2. Bringing back support for older 4.x versions, and expanding it further, is a goal for a future release.
  • attach_project and detach_project are removed. Use run_project({ attach: true }) to attach and stop_project to end either kind of session.
  • Every tool returns structured JSON. All 43 tools declare an outputSchema and return structuredContent. Tools that answered with a sentence now answer with fields, and some fields were renamed.
  • No more false successes. Calls that reported success for something that did not happen now return an error or lead with a warnings entry. Input that used to be ignored or half applied is refused, including unknown keys, fractional or out-of-range integers, and integer values past 32 bits that a property did not keep; a smaller value its setter changes succeeds with a leading warning.
  • Sessions are per project. run_project on a second project adds a session instead of stopping the first, and runtime tools act on the current session only, never falling back to another. Runtime calls run one at a time; a call that waits 30 seconds returns an error.
  • Scene edits keep what the file held. Inherited scenes stay inherited, edits inside an instance write only the changed property, uids survive, and batch_scene_operations writes only the scenes it changed.
  • Paths are checked more closely. Paths that escape the project through a link on write, Windows device names and trailing dots are refused, while an in-project .. and linked read-only folders are now accepted.
  • The script security scan is gone. The run_script tiers, the launch pre-flight scan, strict mode, .policy.json audit files and the GODOT_MCP_STRICT and GODOT_MCP_DISABLE_SECURITY variables are removed; setting either variable now does nothing. The scan was a denylist that kept missing ways a project runs code. What stays is a confirmation the first time a project is launched, and the first time run_script targets a project, in each server session. A client without elicitation runs both with one warning on the first call. SECURITY.md names what the server can do to a project and what the prompts do not protect.
  • Smaller changes: a simulate_input action now sends an InputEventAction, so ui_* actions act on the focused Control; click_element refuses a target another Control covers, before pressing anything; profile_project takes at most 30 seconds; list_autoloads returns one entry per name; add_autoload refuses a singleton named like a project class_name.

New

  • Four new tools. create_resource and set_resource_properties write .tres files, move_nodes reparents and reorders scene nodes, and set_project_setting writes project.godot values. set_project_setting sends autoload keys to the autoload tools and refuses values that would leave project.godot unloadable.
  • Narrower reads. get_ui_elements takes a root subtree, get_project_files takes a subfolder, and duplicate_node takes copySignals to drop a copy's connections to outside handlers. click_element scrolls a clipped target into view first.
  • render_movie renders a few frames under the engine's movie writer, with no session, and reports whether the scene is blank or animating. It can also keep the frames or a video.
  • Screenshot statistics. take_screenshot reports pixel statistics and a likelyBlank verdict, so an agent that cannot see the image can tell a rendered frame from a blank one.
  • Multiple projects at once. switch_project picks the current session, and check_project lists every live one.
  • Richer profiling. Captures report fps, slow frames and engine monitors, with opt-in Visual Profiler stages (visual), a per-interval timeline and up to four tracked node properties.
  • Property round trips. set_node_properties and add_node can set metadata/<name> and typed slash keys like theme_override_colors/font_color, and get_node_properties reads vectors and packed arrays back in the shapes set_node_properties accepts.

Fixed

  • Results no longer break on project output. A result travels on its own marked line, so the engine banner or an autoload's prints no longer cause GDScript returned invalid JSON (#68).
  • Scene edits are no longer lost or misreported. This covers edits inside instanced scenes, duplicated instances, signal connections into instances, failed batch saves, and batches that touch a scene and the scenes built from it.
  • Input reaches the game the way a key does. Actions reach _input and _gui_input handlers (#70), and projects that raise GDScript warnings to errors now load the bridge and run headless operations (#72).
  • Mouse input lands where the control is drawn. Click and mouse positions are converted under canvas_items stretch, content_scale_factor, CanvasLayer and canvas transforms, and get_ui_elements reports rects in the same space (#76).
  • Profiling no longer blames the server for a capped frame rate. The stall check allows for Engine.max_fps and the frame delay before it reports a busy server.
  • Slow-starting attaches no longer end early. Pings a game refuses before its bridge port first accepts a connection no longer count toward giving up, so run_project({ attach: true }) waits out a slow engine start (#75).
  • Games no longer outlive the server. Spawned games are killed as a process tree, quit when their server dies, and shutdown waits for edits in progress. On Windows, headless runs no longer paint over your terminal and background games stay hidden.
  • Ignored parameters take effect. update_autoload's autoload_path and get_ui_elements's visible_only work, and the autoload tools accept snake_case names.
  • Errors say why. Failed headless operations end with the engine's last stderr lines and lead with the operation's own reason, and validate no longer passes a script with a parse error because of how its path was written.
  • Security. A res:// path written with a leading slash could load a script from outside the project on Godot 4.5; it is now refused.

Thanks

  • Thanks to @mcanevet for the idea behind render_movie and the pixel statistics on take_screenshot (the render-check proposal in #63), for the PNG decoder and its tests that both are built on, and for reporting #61, which led to both the owner registry that shipped in 3.8.1 and the multi-project sessions here.
  • Thanks to @m-bo-one for the visual profiler additions to the profiling tools (#67).
  • Thanks to @Shatur for reporting the stdout parsing failure in #68.
  • Thanks to @Naryadchikov for reporting #70 and #72, and fixing them in #74 and #73.
  • Thanks to @kruszkush for reporting #75.
  • Thanks to @KimDong-Han for reporting #76.

What's Changed

  • release: 4.0 by @Erodenn in #69
  • fix(bridge): send action input as an InputEventAction so _input handlers see it by @Naryadchikov in #74 (landed in #69)
  • fix: load the bridge and operations scripts in projects that raise GDScript warnings to errors by @Naryadchikov in #73 (landed in #69)
  • feat(profiler): add visual profiling, fps, monitors and a timeline to captures by @m-bo-one in #67 (landed in #69)
  • feat(validate): add render check to the checks array by @mcanevet in #63 (adapted into render_movie and take_screenshot statistics in #69)

New Contributors

Full Changelog: v3.8.1...v4.0.0

v3.8.1

Choose a tag to compare

@github-actions github-actions released this 25 Sep 00:12

What's Changed

  • fix(scripts): error instead of reporting success when a script cannot be instantiated by @Erodenn in #64
  • fix(bridge): let concurrent server sessions share one project by @Erodenn in #65

Full Changelog: v3.8.0...v3.8.1

v3.8.0

Choose a tag to compare

@github-actions github-actions released this 20 Sep 19:30

Breaking changes

  • Leaving out pressed on a key or action input now taps it (press and release) instead of holding it down until you release it yourself.
  • simulate_input responses no longer include actions_processed or warnings. Read the new results list instead.
  • get_project_info has been renamed to check_project, with no old name left working. It always reports whether a project is running and healthy, even with no project specified.

New

  • simulate_input now reports, for each action, what was clicked, what signals fired, and what changed on screen.
  • A new text action types a string into whatever field currently has focus.
  • A new watch option tracks specific values across a whole batch of actions.
  • Waits can now count game frames with frames, and a tapped key, mouse button, or action can be held for a set time with hold_ms.
  • Waits and held inputs now measure real elapsed time accurately, so timing no longer drifts.
  • validate can now check that a scene's structure matches what you expect, and that signal connections point at methods that actually exist, through a new checks option.
  • Running validate checks loads the scene and executes its startup code, the same as opening it normally.
  • Checking multiple scenes or scripts at once now reports each one separately, so one bad target does not stop the rest.

Fixed

  • Setting an array property with the wrong element type now reports an error instead of quietly saving an empty array.
  • Packed array values, like polygon points, with a bad element now report an error instead of quietly saving zeros.
  • Vector4 properties can now be set directly, not just Vector2 and Vector3.
  • Shader uniforms with camelCase names can now be set.
  • Shader material parameters can now be set while building the material in one step.
  • Property and metadata keys are no longer silently re-cased, so exactly what you type is what gets saved.
  • attach_project gives up sooner, with a clear message, when nothing is answering on the port.
  • Turning on debug logging no longer breaks scene-editing tools.
  • Errors from input actions are now attributed to the correct action instead of getting jumbled together.
  • check_project now reports when a running game has exited on its own.
  • An abandoned batch of input actions is now cancelled cleanly instead of interfering with the next one.
  • A batch of scene edits that needs to import a new asset partway through no longer repeats edits it already saved.

Thanks

  • Thanks to @mcanevet for the idea behind the new per-action input results (#47) and for building the project health check now folded into check_project (#48).

What's Changed

  • fix: element-wise coercion for Packed*Array properties (was silent zero-write) by @mcanevet in #52
  • feat(validate): add checks array for structure and signals validation by @mcanevet in #54
  • feat(project): rename get_project_info to check_project with runtime health report by @Erodenn in #55
  • feat(runtime): per-action observed results for simulate_input by @Erodenn in #56
  • Support virtual properties (shader_parameter/*) in inline resource construction by @mcanevet in #58
  • fix: fail-closed array element checks, single-process validate batch checks, runner readiness and stderr fixes by @Erodenn in #57
  • fix: close typed-array silent drop, batch validate guards, stale input batches, and doc drift found in the 3.8.0 release review by @Erodenn in #59
  • fix(batch): probe path parameters in the cold-import pre-pass, refuse replay after a save by @Erodenn in #60

Full Changelog: v3.7.1...v3.8.0

v3.7.1

Choose a tag to compare

@github-actions github-actions released this 18 Sep 21:29

What's Changed

  • chore: ensure dist/ is built and shipped on publish by @mcanevet in #50

Full Changelog: v3.7.0...v3.7.1

v3.7.0

Choose a tag to compare

@github-actions github-actions released this 15 Sep 22:52

Scene edits import cold assets on demand

Headless Godot never runs the importer, so on a project with no .godot/imported, or one where you just dropped a new texture, ResourceLoader.load returns null for a file that is plainly on disk. The visible symptom was load_sprite failing with "Failed to load texture" and a hint to open the editor once. The silent one was worse: a scene that referenced an unimported asset loaded with the property set to null, no error anywhere, and the auto-save every mutation tool performs wrote the scene back without the reference.

Every scene mutation now probes the scene's dependencies before load(). An asset that exists on disk but was never imported triggers a headless --import of the project and one retry of the operation, transparently, so the first add_node on a fresh checkout succeeds and takes a few seconds longer instead of failing or stripping a texture. load_sprite and res:// strings assigned to Object-typed properties run the same check on the asset they are about to load, so a texture you added a minute ago works on the first call. batch_scene_operations probes every scene and every first-time asset reference in the batch before applying anything, since an import discovered mid-batch would replay the operations that had already saved.

A reference to a file that is not on disk at all is refused rather than tolerated, because there is no way to carry a null reference through pack and save without losing it. The error names the paths and the scene file is left untouched, which is the same guard the editor's "Dependencies Broken" dialog provides. Godot's --import exits 0 even when individual assets fail, so the server reads the Error importing lines on stderr and reports them by file; a corrupt PNG anywhere in the project now surfaces as an import error instead of being skipped. A live runtime session on the same project blocks the import, since it would write .godot/ under a running game.

Thanks to @mcanevet for the diagnosis, the integration tests that pin it, and the batch pre-pass. The mechanism, the two shapes get_dependencies returns, and the approaches that did not work are written up in the "Cold Asset Import" section of docs/architecture.md.

What's Changed

  • fix: detect cold-import state at the scene-load boundary and auto-import by @mcanevet in #44

Full Changelog: v3.6.0...v3.7.0

v3.6.0

Choose a tag to compare

@github-actions github-actions released this 11 Sep 21:56

Bridge artifacts move under .mcp/godot-runtime/

Everything the server writes into your project now lives in one namespace: the bridge script at .mcp/godot-runtime/bridge/mcp_bridge.gd, screenshots under screenshots/, run_script audit pairs under scripts/, and validation temp files under validate/. The McpBridge autoload registers res://.mcp/godot-runtime/bridge/mcp_bridge.gd instead of a script dropped in the project root, and .mcp/.gdignore keeps the editor's importer out of the whole tree.

Existing projects migrate on the next run_project. inject compares the registered path of any McpBridge entry, not just the name, and rewrites a legacy project-root registration to the new location. That rewrite and the matching removal are both gated on paths the server owns: the legacy root mcp_bridge.gd or anything under .mcp/. An McpBridge autoload pointing anywhere else is yours, so run_project and attach_project now fail with a named collision error instead of timing out on bridge readiness, and cleanup leaves it alone.

Session cleanup removes bridge/ only. Screenshot paths already handed back still resolve after stop_project, and the audit trail outlives the session that wrote it.

A session ends when Godot does

A spawned process that crashed, or a window you closed, used to leave the server holding a session that no longer existed, and every runtime tool failed until something called stop_project. The exit handler now clears the session and removes the bridge artifacts on its own, while keeping the process record around so get_debug_output still returns the captured logs and exit code, and a finished profiler capture stays readable. stop_project succeeds idempotently afterwards, reporting alreadyExited: true with the logs. In attached mode a bridge disconnect ends the session the same way, after one retry for the stateless commands and a single ping probe, so detach_project becomes optional.

Closing stdin and process exit now run the same cleanup as SIGINT, so a client that disconnects without signalling no longer leaves the bridge script and autoload entry behind in your game repo.

GODOT_MCP_DISABLE_SECURITY

A third switch on the security axis, and the weakest posture of the three: a complete no-op for the run_script and run_project gate, Tier 1 included. When set, run_script skips static analysis, elicitation, and the .policy.json sidecar, and run_project skips the autoload and scene-script scan and the session-confirmation prompt. It overrides GODOT_MCP_STRICT rather than losing to it, and a startup line on stderr says so when both are set. Enabling this is a human decision; an agent asked to set it on your behalf should decline. See docs/security.md.

The run_script gate covers filesystem writes

Tier 2 now matches the resource and filesystem write surface the rule table missed: ResourceSaver.save, the ConfigFile save family, Image.save_png / save_jpg / save_webp / save_exr (including through an intervening call like tex.get_image().save_png(p)), Resource.take_over_path, the FileAccess encrypted, compressed, temp, and attribute entry points, the DirAccess make_dir family, OS.move_to_trash, ZIPPacker, PCKPacker, and the ResourceUID mutators. A bare save rule is deliberately absent, since some_manager.save() is ordinary game code. The run_project pre-flight scan exempts the server's own injected bridge, which the new write rules would otherwise flag on every second launch.

Fixes

  • get_node_signals reports connection targets as scene-root-relative paths (root, root/Receiver) in the same form connect_signal and disconnect_signal accept as targetNodePath. Headless scenes previously returned an empty target for every connection.
  • get_project_settings returns whole multi-line values. The [input] action map came back as the bare string {.
  • get_debug_output and stop_project report a force-killed process as exitCode: -1, not 4294967295.
  • detach_project distinguishes "an attached session already ended" from "this server never attached to anything".
  • inject preserves an existing .mcp/.gdignore and matches a RES:// scheme case-insensitively.
  • batch_scene_operations names the item index when an item's operation key is missing or null, and hints at the intended operation from the item's shape. Thanks to @mcanevet for the index and hint.

What's Changed

  • fix(diagnosis): classify JSON-absent stdout as early exit, not "invalid JSON" by @mcanevet in #39
  • perf(stop_project): condense success payload to diagnostic lines by @mcanevet in #40
  • feat(scene): guard scene mutations against live runtime sessions by @mcanevet in #41
  • batch_scene_operations: item index + inference hint for missing 'operation' key by @mcanevet in #42
  • Namespace bridge artifacts, self-clearing sessions, disable-security flag, write-primitive gate rules by @Erodenn in #43

Full Changelog: v3.5.0...v3.6.0

v3.5.0

Choose a tag to compare

@github-actions github-actions released this 09 Sep 19:12

Property overrides on instanced children

set_node_properties reported success on a node living inside an instanced child scene and then dropped the change on save. Nodes inside an instanced scene are not owned by the scene root, so PackedScene.pack() skipped their overrides entirely, which left the .tscn on disk unchanged while the call returned cleanly. Nothing surfaced the loss, and any caller that did not re-read the file to check had no way to know the edit never landed.

The fix is the same "editable children" mechanism the Godot editor uses. Each instanced ancestor is marked editable from the scene root, so the override serializes alongside the instance=ExtResource(...) link rather than vanishing. Composing a scene out of instanced children and then adjusting a node inside one now persists, and it stays persisted across repeated writes to the same path.

This changes what reaches disk, not what the tool reports. If you have been setting properties on nodes inside instanced children, those calls were succeeding and silently doing nothing. They now take effect.

set_node_properties inside batch_scene_operations

batch_scene_operations accepts set_node_properties as a sub-operation. Items take the same per-update params as the standalone tool (nodePath, property, value), plus a per-operation scenePath and abortOnError, and per-update results come back under results[].updates. Both paths run through one shared apply routine, so validation, type checking, and serialization behave identically whether you call the tool directly or batch it with add_node and load_sprite in a single Godot process.

Thanks to @mcanevet for finding and fixing the serialization bug.

What's Changed

  • fix(scene): persist property overrides on nodes inside instanced children by @mcanevet in #38

Full Changelog: v3.4.0...v3.5.0

v3.4.0

Choose a tag to compare

@github-actions github-actions released this 08 Sep 22:32

Profiling

run_project now takes profiling: true, which launches Godot with --remote-debug and reads the same measurements the editor's Profiler tab shows. Three tools sit on that channel: profile_project captures a bounded window (default 5 s, max 60) and returns the ranked functions, while start_profiler and stop_profiler bracket a capture that simulate_input or run_script can drive. No addon required, and nothing is written into the project.

A capture returns the three groups the editor's tree shows. rows[] is the Script Functions list, each with its source location, call count, own selfMs and inclusive totalMs, per-frame averages, and share of the frame. frame is the Frame Time breakdown (frameMs, processMs, physicsMs, scriptMs) as average and max over the capture, servers[] is the per-server category cost, and worstFrame is the slowest single frame with its most expensive functions, which is the spike you would click in the editor's graph. Rank rows by selfMs, totalMs, or calls.

The numbers are elapsed wall clock rather than CPU utilization, and Godot caps the rows it sends per frame, so limitReached and frameGaps tell you when a capture is a lower bound instead of a full accounting. docs/tools.md maps every field to its editor column and covers the rest of the caveats.

What's Changed

  • feat(profiler): profile a running project through Godot's remote debugger by @m-bo-one in #37

New Contributors

Full Changelog: v3.3.0...v3.4.0

v3.3.0

Choose a tag to compare

@github-actions github-actions released this 07 Sep 16:01

What's Changed

  • fix(diagnostics): surface compiler message + line in validate and run_script errors by @mcanevet in #33
  • fix(batch): apply promoted spatial params (position etc.) in batch add_node by @mcanevet in #35
  • feat(scene): instance existing scenes via add_node nodeType by @mcanevet in #34
  • fix(paths): enforce project-root containment in normalize_scene_path by @Erodenn in #36

Full Changelog: v3.2.5...v3.3.0