godot-agent-tools-mcp 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/server.mjs +185 -9
package/package.json
CHANGED
package/server.mjs
CHANGED
|
@@ -125,7 +125,22 @@ const TOOLS = [
|
|
|
125
125
|
name: "scene_set_property",
|
|
126
126
|
method: "scene.set_property",
|
|
127
127
|
description:
|
|
128
|
-
"Set a property on a node.
|
|
128
|
+
"Set a property on a node. JSON-to-Godot coercion: " +
|
|
129
|
+
"primitives (bool/int/float/String/StringName) pass through; " +
|
|
130
|
+
"NodePath from a string; " +
|
|
131
|
+
"Vector2/2i/3/3i/4/4i from [x, y(, z(, w))]; " +
|
|
132
|
+
"Rect2/2i from [x, y, width, height]; " +
|
|
133
|
+
"Quaternion from [x, y, z, w]; " +
|
|
134
|
+
"Color from [r, g, b(, a)] or '#rrggbb(aa)'; " +
|
|
135
|
+
"Transform2D from {origin: [x,y], rotation: radians, scale: [x,y], skew?}; " +
|
|
136
|
+
"Transform3D from {origin: [x,y,z], rotation: [x,y,z] (euler rad), scale: [x,y,z]}; " +
|
|
137
|
+
"Basis from {rotation, scale} (same shape as Transform3D minus origin); " +
|
|
138
|
+
"AABB from {position: [x,y,z], size: [x,y,z]}; " +
|
|
139
|
+
"Plane from {normal: [x,y,z], d: float}; " +
|
|
140
|
+
"Resource-typed properties (e.g. a CollisionShape2D's 'shape') auto-load from 'res://...' or 'uid://...' path strings; " +
|
|
141
|
+
"Packed{String,Int32,Int64,Float32,Float64,Vector2,Vector3,Color}Array from plain JSON arrays (element-wise coerced); " +
|
|
142
|
+
"other types pass through as-is. " +
|
|
143
|
+
"Returns an error if Godot drops the assignment (e.g. type mismatch) instead of echoing a misleading null.",
|
|
129
144
|
inputSchema: {
|
|
130
145
|
type: "object",
|
|
131
146
|
required: ["node_path", "property"],
|
|
@@ -150,6 +165,56 @@ const TOOLS = [
|
|
|
150
165
|
},
|
|
151
166
|
},
|
|
152
167
|
},
|
|
168
|
+
{
|
|
169
|
+
name: "scene_build_tree",
|
|
170
|
+
method: "scene.build_tree",
|
|
171
|
+
description:
|
|
172
|
+
"Build a subtree in the currently-edited scene in one call — recursive spec instead of dozens of scene_add_node + scene_set_property + script_attach round trips. " +
|
|
173
|
+
"Each tree entry: {type: 'ClassName' (required), name?: string, properties?: {propname: value, ...}, script?: 'res://...', children?: [entry, ...]}. " +
|
|
174
|
+
"Properties are coerced via the same rules as scene_set_property (Vectors from arrays, Resources auto-loaded from res:// paths, Transforms from {origin,rotation,scale}, etc.). " +
|
|
175
|
+
"Script is attached before properties so script-exported vars are settable in the same call. " +
|
|
176
|
+
"Atomic: if any entry fails (unknown type, missing property, coercion error, read-only slot, silent-null assignment), every node created during this call is rolled back — the scene is left in its pre-call state.",
|
|
177
|
+
inputSchema: {
|
|
178
|
+
type: "object",
|
|
179
|
+
required: ["nodes"],
|
|
180
|
+
properties: {
|
|
181
|
+
parent_path: { type: "string", default: ".", description: "Where to attach the new subtree. '.' = scene root." },
|
|
182
|
+
nodes: {
|
|
183
|
+
type: "array",
|
|
184
|
+
description: "Top-level tree entries (each may have arbitrarily nested children).",
|
|
185
|
+
items: {
|
|
186
|
+
type: "object",
|
|
187
|
+
required: ["type"],
|
|
188
|
+
properties: {
|
|
189
|
+
type: { type: "string", description: "Godot class name (e.g. 'PanelContainer', 'Button')." },
|
|
190
|
+
name: { type: "string" },
|
|
191
|
+
script: { type: "string", description: "Path to a .gd to attach before properties are applied." },
|
|
192
|
+
properties: { type: "object", additionalProperties: true, description: "Property → value pairs; values use the scene_set_property coercion rules." },
|
|
193
|
+
children: { type: "array", description: "Nested entries using the same shape as this one." },
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: "scene_call_method",
|
|
202
|
+
method: "scene.call_method",
|
|
203
|
+
description:
|
|
204
|
+
"Invoke a method on a node in the currently-edited scene (e.g. a custom helper, or Godot built-ins like 'queue_free', 'add_to_group'). Args are coerced against the method's declared parameter types using the same rules as scene_set_property — including 'res://...' → Resource auto-load. Return value is JSON-native. Useful when properties alone can't express what you need — e.g. calling a script method with multiple args.",
|
|
205
|
+
inputSchema: {
|
|
206
|
+
type: "object",
|
|
207
|
+
required: ["node_path", "method"],
|
|
208
|
+
properties: {
|
|
209
|
+
node_path: { type: "string" },
|
|
210
|
+
method: { type: "string" },
|
|
211
|
+
args: {
|
|
212
|
+
type: "array",
|
|
213
|
+
description: "Method arguments in order. Each is coerced based on the method's declared parameter type.",
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
},
|
|
153
218
|
{
|
|
154
219
|
name: "scene_open",
|
|
155
220
|
method: "scene.open",
|
|
@@ -164,10 +229,11 @@ const TOOLS = [
|
|
|
164
229
|
name: "scene_save",
|
|
165
230
|
method: "scene.save",
|
|
166
231
|
description:
|
|
167
|
-
"Save the currently-edited scene. Pass 'path' to save-as (rebinds the scene to that path)."
|
|
232
|
+
"Save the currently-edited scene. Pass 'path' to save-as (rebinds the scene to that path). " +
|
|
233
|
+
"REQUIRED for fresh scenes: if the scene has never been saved (no backing file), the tool returns an error rather than triggering Godot's native Save-As dialog (which would block the editor waiting for a human click). Always pass 'path' when you built the scene from scratch via scene.build_tree / scene.add_node without going through scene.new.",
|
|
168
234
|
inputSchema: {
|
|
169
235
|
type: "object",
|
|
170
|
-
properties: { path: { type: "string", description: "
|
|
236
|
+
properties: { path: { type: "string", description: "Save-as target, required when the scene has no existing file." } },
|
|
171
237
|
},
|
|
172
238
|
},
|
|
173
239
|
{
|
|
@@ -290,7 +356,8 @@ const TOOLS = [
|
|
|
290
356
|
{
|
|
291
357
|
name: "resource_set_property",
|
|
292
358
|
method: "resource.set_property",
|
|
293
|
-
description:
|
|
359
|
+
description:
|
|
360
|
+
"Load a .tres, set one property, and save. Same JSON-to-Godot coercion as scene_set_property: primitives, NodePath, Vector2/2i/3/3i/4/4i, Rect2/2i, Quaternion, Color, Transform2D/3D, Basis, AABB, Plane, Resource auto-load from 'res://'/'uid://' paths, and all Packed*Array variants. Errors on silently-dropped assignments.",
|
|
294
361
|
inputSchema: {
|
|
295
362
|
type: "object",
|
|
296
363
|
required: ["path", "property"],
|
|
@@ -301,6 +368,22 @@ const TOOLS = [
|
|
|
301
368
|
},
|
|
302
369
|
},
|
|
303
370
|
},
|
|
371
|
+
{
|
|
372
|
+
name: "resource_call_method",
|
|
373
|
+
method: "resource.call_method",
|
|
374
|
+
description:
|
|
375
|
+
"Load a .tres, invoke a method on it, save, return the method's result. Rounds out what set_property can't express — e.g. StyleBoxFlat.set_border_width_all(4), set_corner_radius_all(14), Curve.add_point(...). Args coerce via the same rules as resource_set_property. Pass save:false to call without persisting (read-only method calls).",
|
|
376
|
+
inputSchema: {
|
|
377
|
+
type: "object",
|
|
378
|
+
required: ["path", "method"],
|
|
379
|
+
properties: {
|
|
380
|
+
path: { type: "string" },
|
|
381
|
+
method: { type: "string" },
|
|
382
|
+
args: { type: "array", description: "Method arguments in order, coerced per the method's parameter types." },
|
|
383
|
+
save: { type: "boolean", default: true, description: "Save the resource after the call. Set false for read-only method calls." },
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
},
|
|
304
387
|
{
|
|
305
388
|
name: "refs_validate_project",
|
|
306
389
|
method: "refs.validate_project",
|
|
@@ -450,7 +533,11 @@ const TOOLS = [
|
|
|
450
533
|
name: "input_map_add_event",
|
|
451
534
|
method: "input_map.add_event",
|
|
452
535
|
description:
|
|
453
|
-
"Attach an input event to an existing action.
|
|
536
|
+
"Attach an input event to an existing action. Every event accepts an optional 'device' field (default -1 = all devices; set 0, 1, etc. for local-multiplayer device-specific bindings). Event shapes: " +
|
|
537
|
+
"{type:'key', keycode:'A'|'Space'|...}; " +
|
|
538
|
+
"{type:'mouse_button', button_index:1|2|3}; " +
|
|
539
|
+
"{type:'joy_button', button_index:0..}; " +
|
|
540
|
+
"{type:'joy_motion', axis:0..5 or 'left_x'|'left_y'|'right_x'|'right_y'|'trigger_left'|'trigger_right', axis_value:-1.0..1.0} (axis_value sign picks direction).",
|
|
454
541
|
inputSchema: {
|
|
455
542
|
type: "object",
|
|
456
543
|
required: ["action", "event"],
|
|
@@ -460,10 +547,13 @@ const TOOLS = [
|
|
|
460
547
|
type: "object",
|
|
461
548
|
required: ["type"],
|
|
462
549
|
properties: {
|
|
463
|
-
type: { type: "string", enum: ["key", "mouse_button", "joy_button"] },
|
|
550
|
+
type: { type: "string", enum: ["key", "mouse_button", "joy_button", "joy_motion"] },
|
|
551
|
+
device: { type: "integer", default: -1, description: "-1 = all devices (default); 0, 1, etc. bind a specific controller for local multiplayer." },
|
|
464
552
|
keycode: { description: "For type='key': 'A', 'Space', 'F1', or int keycode." },
|
|
465
|
-
button_index: { type: "integer", description: "For mouse/joy button events." },
|
|
466
553
|
physical: { type: "boolean", default: true, description: "For type='key': use physical keycode (recommended)." },
|
|
554
|
+
button_index: { type: "integer", description: "For mouse/joy button events." },
|
|
555
|
+
axis: { description: "For type='joy_motion': int 0..5 or 'left_x'|'left_y'|'right_x'|'right_y'|'trigger_left'|'trigger_right'." },
|
|
556
|
+
axis_value: { type: "number", description: "For type='joy_motion': -1.0 to 1.0; sign selects direction that triggers the action." },
|
|
467
557
|
},
|
|
468
558
|
},
|
|
469
559
|
},
|
|
@@ -509,18 +599,104 @@ const TOOLS = [
|
|
|
509
599
|
name: "run_scene_headless",
|
|
510
600
|
method: "run.scene_headless",
|
|
511
601
|
description:
|
|
512
|
-
"Run a scene in a
|
|
602
|
+
"Run a scene in a child Godot process with structured output. " +
|
|
603
|
+
"MODES: BARE (default) runs --headless — fast, no window, good for 'does _ready not crash' checks. " +
|
|
604
|
+
"DRIVEN (anything beyond path + quit_after_seconds) runs under a wrapper driver that can inject scripted input, capture screenshots at multiple frames, dump final scene state as JSON, and use a deterministic RNG seed. " +
|
|
605
|
+
"SCREENSHOTS: when screenshot(s) requested the subprocess drops --headless and runs with a real (offscreen) window because Godot 4.6's headless mode uses a dummy renderer. Expect a brief window flash. " +
|
|
606
|
+
"STRUCTURED RESULTS: tool parses stdout for ERROR: / USER ERROR: / SCRIPT ERROR: / WARNING: / USER WARNING: lines and returns them as result.errors and result.warnings arrays so the agent doesn't have to regex the raw output. state_dump:true adds result.final_state with the scene tree + common properties. " +
|
|
607
|
+
"Event types for input_script: " +
|
|
608
|
+
"{frame, type: 'action_tap', action}; " +
|
|
609
|
+
"{frame, type: 'action_press', action, strength?}; " +
|
|
610
|
+
"{frame, type: 'action_release', action}; " +
|
|
611
|
+
"{frame, type: 'key', keycode: 'Space'|int, pressed?: true}; " +
|
|
612
|
+
"{frame, type: 'mouse_click', position: [x, y], button?: 1}; " +
|
|
613
|
+
"{frame, type: 'mouse_motion', position: [x, y]}. " +
|
|
614
|
+
"BLOCKS the editor for the duration — use small quit_after_seconds (1-5).",
|
|
513
615
|
inputSchema: {
|
|
514
616
|
type: "object",
|
|
515
617
|
required: ["path"],
|
|
516
618
|
properties: {
|
|
517
619
|
path: { type: "string", description: "Scene to run, e.g. 'res://Main.tscn'." },
|
|
518
|
-
quit_after_seconds: { type: "number", default: 2, description: "Converted to frames assuming 60 fps." },
|
|
620
|
+
quit_after_seconds: { type: "number", default: 2, description: "Converted to frames assuming 60 fps. In DRIVEN mode this is also the quit_frame the driver targets." },
|
|
519
621
|
extra_args: {
|
|
520
622
|
type: "array",
|
|
521
623
|
items: { type: "string" },
|
|
522
624
|
description: "Additional CLI args passed to the child godot process.",
|
|
523
625
|
},
|
|
626
|
+
input_script: {
|
|
627
|
+
type: "array",
|
|
628
|
+
description: "Optional: enters DRIVEN mode. Array of event specs keyed by frame. See description for event type shapes.",
|
|
629
|
+
items: {
|
|
630
|
+
type: "object",
|
|
631
|
+
required: ["frame", "type"],
|
|
632
|
+
properties: {
|
|
633
|
+
frame: { type: "integer", description: "0-based frame to fire on." },
|
|
634
|
+
type: { type: "string", enum: ["action_press", "action_release", "action_tap", "key", "mouse_click", "mouse_motion"] },
|
|
635
|
+
action: { type: "string" },
|
|
636
|
+
strength: { type: "number", default: 1.0 },
|
|
637
|
+
keycode: { description: "For type='key': 'A', 'Space', 'F1', or int keycode." },
|
|
638
|
+
pressed: { type: "boolean" },
|
|
639
|
+
button: { type: "integer", description: "Mouse button (1=left, 2=right, 3=middle)." },
|
|
640
|
+
position: { type: "array", description: "[x, y] viewport coordinates." },
|
|
641
|
+
},
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
screenshot: {
|
|
645
|
+
type: "string",
|
|
646
|
+
description: "Shorthand: one PNG saved at the final frame. Equivalent to screenshots:[{frame: quit_frame, path: ...}]. Triggers offscreen-windowed subprocess (brief window flash).",
|
|
647
|
+
},
|
|
648
|
+
screenshots: {
|
|
649
|
+
type: "array",
|
|
650
|
+
description: "Capture PNGs at multiple specific frames during the run — useful for verifying state transitions (spawn at frame 30, mid-animation at 60, final at 120).",
|
|
651
|
+
items: {
|
|
652
|
+
type: "object",
|
|
653
|
+
required: ["frame", "path"],
|
|
654
|
+
properties: {
|
|
655
|
+
frame: { type: "integer" },
|
|
656
|
+
path: { type: "string" },
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
},
|
|
660
|
+
resolution: {
|
|
661
|
+
type: "string",
|
|
662
|
+
default: "320x240",
|
|
663
|
+
description: "Window resolution 'WxH' when screenshots are captured. Default is tiny to minimize offscreen footprint; bump to 1280x720 or similar for UI verification.",
|
|
664
|
+
},
|
|
665
|
+
state_dump: {
|
|
666
|
+
type: "boolean",
|
|
667
|
+
default: false,
|
|
668
|
+
description: "When true, driver writes a JSON snapshot of the final scene tree (name, class, node_path, script, children, plus common props like visible/position/text/value/modulate). Returned as result.final_state. Lets agents verify end state programmatically instead of eyeballing the screenshot.",
|
|
669
|
+
},
|
|
670
|
+
seed: {
|
|
671
|
+
type: "integer",
|
|
672
|
+
description: "Optional RNG seed. Set before the target scene is instanced so randi/randf are reproducible. Useful for deterministic tests.",
|
|
673
|
+
},
|
|
674
|
+
},
|
|
675
|
+
},
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
name: "user_fs_read",
|
|
679
|
+
method: "user_fs.read",
|
|
680
|
+
description:
|
|
681
|
+
"Read a text file from the user:// data directory — where games persist save files, custom-level JSON, settings, etc. Separate from fs_list (which is res://-only) because user:// is runtime-written state.",
|
|
682
|
+
inputSchema: {
|
|
683
|
+
type: "object",
|
|
684
|
+
required: ["path"],
|
|
685
|
+
properties: {
|
|
686
|
+
path: { type: "string", description: "Must begin with 'user://'." },
|
|
687
|
+
},
|
|
688
|
+
},
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
name: "user_fs_list",
|
|
692
|
+
method: "user_fs.list",
|
|
693
|
+
description:
|
|
694
|
+
"List files and subdirectories under a user:// directory. Optionally recursive.",
|
|
695
|
+
inputSchema: {
|
|
696
|
+
type: "object",
|
|
697
|
+
properties: {
|
|
698
|
+
dir: { type: "string", default: "user://", description: "Must begin with 'user://'." },
|
|
699
|
+
recursive: { type: "boolean", default: false },
|
|
524
700
|
},
|
|
525
701
|
},
|
|
526
702
|
},
|