Skip to content

1.1.6 — close the gaps found by building a game with it

Choose a tag to compare

@TomasLucasUTN TomasLucasUTN released this 28 Jul 17:19
· 69 commits to main since this release
bedf492

godot-mcp-bridge 1.1.6 — everything here came from building a game with it

The previous release was a backlog being closed. This one is different: every
item below was found by using the server to build a real Metroidvania vertical
slice — a player controller with a state machine, a component-based combat
system, a boss with phases, a level — and writing down every point where the
tools got in the way.

That produced a list of seven problems and four missing tools. Three of the
seven were not bugs at all
, which is the part worth reading.


The validator was calling working code broken

validate_scripts reported 4 of 4 scripts invalid in a real project. All four
compiled and ran.

It built a GDScript in memory and called reload() on the source, which
compiles the file in isolation — and isolation is exactly what a project
script does not have. Any file referencing an autoload came back as a parse
error, because singletons do not exist to a standalone compile. Any file
extending a class_name from another file came back as "could not find base
class". A comment above the function asserted that autoloads were visible
there. That comment was wrong, and nobody had checked it.

It now loads the file the way the editor does and judges it by whether the
parser resolved a base type. Two seemingly-reasonable alternatives are wrong and
are documented in the code so they do not get "simplified" back in:

  • can_instantiate() is false for a perfectly valid script that references a
    singleton.
  • CACHE_MODE_IGNORE builds a separate copy per load, so sweeping scripts that
    extend each other produces two live copies of one global class and hard-crashes
    the engine
    (exit 5, reproduced on a 3-file sweep of this addon).

The sweep is also bounded now. Validation costs ~34ms per script on the editor's
main thread, so a whole-project sweep on a large codebase could approach the
bridge's 20-second ping watchdog and drop the connection it was answering
through — the failure mode run_scene and find_unused_resources have both
shipped before. addons/ is skipped by default and the response carries
elapsed_ms.

A scene was built with 400 HP and shipped with 100

create_scene was called with a node carrying both a script and
{"max_health": 400}. It answered ok. The value was 100 — the default — and
that was only noticed hours later, at runtime.

Properties were applied before set_script. A node's exported properties do
not exist until its script is attached, so every exported value passed in the
same call was silently dropped. Deterministic, in four separate code paths, for
as long as the tools have existed.

Scripts attach first now. Anything that still cannot be applied comes back under
warnings rather than nowhere.

Writes are read back

A property can exist, accept an assignment without complaint, and still hold
something else. A TextureRect asked for size.y = 6.667 keeps 16, because a
Control's minimum size is its texture's. In the game, that produced grass
strips three times taller than intended and nothing anywhere said so.

Scene tools now compare what landed against what was asked for and report the
difference. Values that land exactly stay silent.

Three tools that were missing

set_node_reference points an exported node slot (@export var target: Area2D, @export var health: HealthComponent) at another node. There was no way
to do this at all — every property tool takes a JSON value and this needs a live
object reference. The workaround, during the game build, was to redesign the
game's components
so they discovered each other at runtime instead of being
wired in the scene. That is the tool dictating the architecture, which is
backwards.

render_scene_preview renders a 2D scene to a PNG without launching the
game, auto-framed on its content. Looking at a scene previously meant a launch, a
runtime connection, and remembering to stop it — so in practice nobody looked,
and every visual mistake was found late or not at all.

restart_editor saves and restarts. A new autoload or class_name is
invisible to a running editor: measured, the setting registers and the script
still fails to compile. Combined with the silent property drop above, that cost
about fifteen minutes of a fifty-minute session to diagnose, and the only way out
was killing the process from a shell.

Two things that were not bugs

send_input works. It was reported broken because a jump never fired. The
real cause was floor snapping cancelling the velocity — something confirmed
later in the same session without connecting the two facts. All three delivery
routes update is_action_pressed and is_action_just_pressed, now covered by a
test that runs as a scene, because Input only advances its action state on real
frames.

create_sprite_animation already sets UPDATE_DISCRETE on the frame track.
That was a hypothesis recorded without checking.

Both are written down because a misdiagnosis nobody records is one somebody
repeats — and this project has now had five of them.

Smaller, but paid for on every call

The piggybacked editor digest no longer carries filesystem_changed. It fires on
every file the agent writes but arrives deferred, so it was attributed to the
developer and rode along on nearly every response — roughly 90% of all digest
content, telling the agent it had written a file it had just written.

game_eval rejects a node_path that does not resolve instead of handing null
to the snippet. The null access that follows cannot be caught from GDScript,
breaks the attached debugger, and costs the entire runtime connection.

The About on GitHub said 213 tools

It said 213. The server shipped 228. Fifteen releases of drift on the first
sentence anyone reads about the project, because the number was maintained by
hand.

scripts/sync-about.mjs derives that sentence from the built tool registry, and
CI checks it on every run using the default token (read-only, so no secret is
needed). "Out of date" is no longer a state the description can be in.


Upgrading

Nothing to do. npx godot-mcp-bridge install stages the updated addon; existing
tool calls are unchanged, with two additions to what responses may contain:

  • Scene-building tools may now include a warnings array. It is advisory — the
    call still succeeded — but it is the only place a dropped or clamped property
    is reported.
  • validate_scripts entries carry message instead of a bare error_code, and
    the sweep skips addons/ unless include_addons is set.