breakpoint-mcp 1.0.0 → 1.2.0
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/addon/breakpoint_mcp/README.md +28 -0
- package/addon/breakpoint_mcp/bridge_server.gd +121 -0
- package/addon/breakpoint_mcp/icon.png +0 -0
- package/addon/breakpoint_mcp/operations.gd +4578 -0
- package/addon/breakpoint_mcp/plugin.cfg +7 -0
- package/addon/breakpoint_mcp/plugin.gd +64 -0
- package/addon/breakpoint_mcp/runtime_bridge.gd +440 -0
- package/addon/breakpoint_mcp/variant_json.gd +108 -0
- package/dist/cli/args.js +40 -0
- package/dist/cli/clients.js +74 -0
- package/dist/cli/doctor.js +269 -0
- package/dist/cli/init.js +199 -0
- package/dist/config.js +6 -30
- package/dist/index.js +49 -4
- package/dist/tools/editor/animation.js +162 -0
- package/dist/tools/editor/audio.js +109 -0
- package/dist/tools/editor/common.js +31 -0
- package/dist/tools/editor/core.js +49 -0
- package/dist/tools/editor/filesystem.js +35 -0
- package/dist/tools/editor/introspection.js +73 -0
- package/dist/tools/editor/node.js +171 -0
- package/dist/tools/editor/particles.js +96 -0
- package/dist/tools/editor/physics.js +236 -0
- package/dist/tools/editor/project_input_test.js +187 -0
- package/dist/tools/editor/resource.js +104 -0
- package/dist/tools/editor/scene.js +95 -0
- package/dist/tools/editor/shader.js +59 -0
- package/dist/tools/editor/signal.js +67 -0
- package/dist/tools/editor/spatial.js +185 -0
- package/dist/tools/editor/tiles.js +160 -0
- package/dist/tools/editor/ui.js +175 -0
- package/dist/tools/editor.js +39 -1914
- package/package.json +4 -2
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { gate } from "../../confirm.js";
|
|
3
|
+
/** AnimationPlayer / Animation authoring + AnimationTree state machines. */
|
|
4
|
+
export function registerAnimationTools(server, call) {
|
|
5
|
+
server.registerTool("anim_player_create", {
|
|
6
|
+
title: "Create AnimationPlayer",
|
|
7
|
+
description: "Add an AnimationPlayer node under a parent (undoable). Seeds an empty default animation library so anim_create works immediately.",
|
|
8
|
+
inputSchema: {
|
|
9
|
+
parent_path: z.string().describe("Parent node path relative to the scene root; \".\" for the root"),
|
|
10
|
+
name: z.string().optional().describe("Node name (default \"AnimationPlayer\")"),
|
|
11
|
+
},
|
|
12
|
+
}, async ({ parent_path, name }) => call("anim.player_create", { parent_path, name }));
|
|
13
|
+
server.registerTool("anim_create", {
|
|
14
|
+
title: "Create animation",
|
|
15
|
+
description: "Create an empty Animation in an AnimationPlayer's library (undoable). Creates the library if it does not exist yet.",
|
|
16
|
+
inputSchema: {
|
|
17
|
+
player_path: z.string().describe("AnimationPlayer node path relative to the scene root"),
|
|
18
|
+
name: z.string().describe("Animation name (unique within its library)"),
|
|
19
|
+
library: z.string().optional().describe("Animation library name (default \"\", the player's default library)"),
|
|
20
|
+
},
|
|
21
|
+
}, async ({ player_path, name, library }) => call("anim.create", { player_path, name, library: library ?? "" }));
|
|
22
|
+
server.registerTool("anim_delete", {
|
|
23
|
+
title: "Delete animation",
|
|
24
|
+
description: "Delete an Animation from an AnimationPlayer's library (undoable). DESTRUCTIVE — gated by confirmation.",
|
|
25
|
+
inputSchema: {
|
|
26
|
+
player_path: z.string().describe("AnimationPlayer node path relative to the scene root"),
|
|
27
|
+
name: z.string().describe("Animation name"),
|
|
28
|
+
library: z.string().optional().describe("Animation library name (default \"\")"),
|
|
29
|
+
confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
|
|
30
|
+
},
|
|
31
|
+
}, async ({ player_path, name, library, confirm }) => {
|
|
32
|
+
const blocked = await gate(server, confirm, `Delete animation "${name}"`);
|
|
33
|
+
if (blocked)
|
|
34
|
+
return blocked;
|
|
35
|
+
return call("anim.delete", { player_path, name, library: library ?? "" });
|
|
36
|
+
});
|
|
37
|
+
server.registerTool("anim_add_track", {
|
|
38
|
+
title: "Add animation track",
|
|
39
|
+
description: "Add a track to an Animation and set its target path (undoable). Returns the new track index.",
|
|
40
|
+
inputSchema: {
|
|
41
|
+
player_path: z.string().describe("AnimationPlayer node path"),
|
|
42
|
+
name: z.string().describe("Animation name"),
|
|
43
|
+
path: z.string().describe("Track target: a node path, or \"Node:property\" for value tracks (e.g. \"Sprite2D:position\")"),
|
|
44
|
+
type: z
|
|
45
|
+
.string()
|
|
46
|
+
.optional()
|
|
47
|
+
.describe("Track type: value (default), position_3d, rotation_3d, scale_3d, blend_shape, method, bezier, audio, animation"),
|
|
48
|
+
library: z.string().optional().describe("Animation library name (default \"\")"),
|
|
49
|
+
},
|
|
50
|
+
}, async ({ player_path, name, path, type, library }) => call("anim.add_track", { player_path, name, path, type: type ?? "value", library: library ?? "" }));
|
|
51
|
+
server.registerTool("anim_insert_key", {
|
|
52
|
+
title: "Insert animation key",
|
|
53
|
+
description: "Insert a keyframe on a track at a given time (undoable). A key already at that exact time is overwritten.",
|
|
54
|
+
inputSchema: {
|
|
55
|
+
player_path: z.string().describe("AnimationPlayer node path"),
|
|
56
|
+
name: z.string().describe("Animation name"),
|
|
57
|
+
track: z.number().int().describe("Track index"),
|
|
58
|
+
time: z.number().describe("Key time in seconds"),
|
|
59
|
+
value: z.any().describe("Key value (JSON scalar, array, object, or a __type__-tagged Variant matching the track type)"),
|
|
60
|
+
transition: z.number().optional().describe("Transition curve exponent (default 1.0)"),
|
|
61
|
+
library: z.string().optional().describe("Animation library name (default \"\")"),
|
|
62
|
+
},
|
|
63
|
+
}, async ({ player_path, name, track, time, value, transition, library }) => call("anim.insert_key", { player_path, name, track, time, value, transition: transition ?? 1.0, library: library ?? "" }));
|
|
64
|
+
server.registerTool("anim_remove_key", {
|
|
65
|
+
title: "Remove animation key",
|
|
66
|
+
description: "Remove a keyframe by index from a track (undoable).",
|
|
67
|
+
inputSchema: {
|
|
68
|
+
player_path: z.string().describe("AnimationPlayer node path"),
|
|
69
|
+
name: z.string().describe("Animation name"),
|
|
70
|
+
track: z.number().int().describe("Track index"),
|
|
71
|
+
key: z.number().int().describe("Key index within the track"),
|
|
72
|
+
library: z.string().optional().describe("Animation library name (default \"\")"),
|
|
73
|
+
},
|
|
74
|
+
}, async ({ player_path, name, track, key, library }) => call("anim.remove_key", { player_path, name, track, key, library: library ?? "" }));
|
|
75
|
+
server.registerTool("anim_set_length", {
|
|
76
|
+
title: "Set animation length",
|
|
77
|
+
description: "Set an Animation's length in seconds (undoable).",
|
|
78
|
+
inputSchema: {
|
|
79
|
+
player_path: z.string().describe("AnimationPlayer node path"),
|
|
80
|
+
name: z.string().describe("Animation name"),
|
|
81
|
+
length: z.number().describe("New length in seconds (> 0)"),
|
|
82
|
+
library: z.string().optional().describe("Animation library name (default \"\")"),
|
|
83
|
+
},
|
|
84
|
+
}, async ({ player_path, name, length, library }) => call("anim.set_length", { player_path, name, length, library: library ?? "" }));
|
|
85
|
+
server.registerTool("anim_set_loop", {
|
|
86
|
+
title: "Set animation loop mode",
|
|
87
|
+
description: "Set an Animation's loop mode (undoable).",
|
|
88
|
+
inputSchema: {
|
|
89
|
+
player_path: z.string().describe("AnimationPlayer node path"),
|
|
90
|
+
name: z.string().describe("Animation name"),
|
|
91
|
+
mode: z.string().describe("Loop mode: none, linear, or pingpong"),
|
|
92
|
+
library: z.string().optional().describe("Animation library name (default \"\")"),
|
|
93
|
+
},
|
|
94
|
+
}, async ({ player_path, name, mode, library }) => call("anim.set_loop", { player_path, name, mode, library: library ?? "" }));
|
|
95
|
+
server.registerTool("anim_get_track_keys", {
|
|
96
|
+
title: "Get animation track keys",
|
|
97
|
+
description: "List all keyframes on a track (index, time, value, transition). Read-only.",
|
|
98
|
+
inputSchema: {
|
|
99
|
+
player_path: z.string().describe("AnimationPlayer node path"),
|
|
100
|
+
name: z.string().describe("Animation name"),
|
|
101
|
+
track: z.number().int().describe("Track index"),
|
|
102
|
+
library: z.string().optional().describe("Animation library name (default \"\")"),
|
|
103
|
+
},
|
|
104
|
+
}, async ({ player_path, name, track, library }) => call("anim.get_track_keys", { player_path, name, track, library: library ?? "" }));
|
|
105
|
+
server.registerTool("anim_list", {
|
|
106
|
+
title: "List animations",
|
|
107
|
+
description: "List all animations in an AnimationPlayer across its libraries, with length, loop mode, and track count. Read-only.",
|
|
108
|
+
inputSchema: {
|
|
109
|
+
player_path: z.string().describe("AnimationPlayer node path"),
|
|
110
|
+
},
|
|
111
|
+
}, async ({ player_path }) => call("anim.list", { player_path }));
|
|
112
|
+
server.registerTool("anim_tree_create", {
|
|
113
|
+
title: "Create AnimationTree",
|
|
114
|
+
description: "Add an AnimationTree node under a parent (undoable) with a fresh tree_root graph. root_type \"blend_tree\" (AnimationNodeBlendTree) or \"state_machine\" (AnimationNodeStateMachine). Created inactive by default; set anim_player_path to the AnimationPlayer it should drive.",
|
|
115
|
+
inputSchema: {
|
|
116
|
+
parent_path: z.string().describe("Parent node path relative to the scene root; \".\" for the root"),
|
|
117
|
+
name: z.string().optional().describe("Node name (default \"AnimationTree\")"),
|
|
118
|
+
root_type: z.enum(["blend_tree", "state_machine"]).optional().describe("tree_root graph type (default blend_tree)"),
|
|
119
|
+
anim_player_path: z.string().optional().describe("NodePath to the AnimationPlayer this tree drives, relative to the AnimationTree node"),
|
|
120
|
+
active: z.boolean().optional().describe("Whether the tree processes immediately (default false)"),
|
|
121
|
+
},
|
|
122
|
+
}, async ({ parent_path, name, root_type, anim_player_path, active }) => call("anim.tree_create", { parent_path, name, root_type: root_type ?? "blend_tree", anim_player_path: anim_player_path ?? "", active: active ?? false }));
|
|
123
|
+
server.registerTool("anim_tree_add_node", {
|
|
124
|
+
title: "Add AnimationTree graph node",
|
|
125
|
+
description: "Add a node to an AnimationTree's tree_root graph (AnimationNodeBlendTree or AnimationNodeStateMachine), undoable. node_type is any AnimationNode subclass (e.g. AnimationNodeAnimation, AnimationNodeBlend2, AnimationNodeStateMachine). For AnimationNodeAnimation, pass animation to bind a clip.",
|
|
126
|
+
inputSchema: {
|
|
127
|
+
tree_path: z.string().describe("AnimationTree node path relative to the scene root"),
|
|
128
|
+
node_name: z.string().describe("Unique node name within the graph"),
|
|
129
|
+
node_type: z.string().describe("AnimationNode subclass to instantiate (e.g. AnimationNodeAnimation)"),
|
|
130
|
+
animation: z.string().optional().describe("For AnimationNodeAnimation: the animation name to play"),
|
|
131
|
+
position: z.array(z.number()).optional().describe("Graph editor position [x, y] (default [0, 0])"),
|
|
132
|
+
},
|
|
133
|
+
}, async ({ tree_path, node_name, node_type, animation, position }) => call("anim.tree_add_node", { tree_path, node_name, node_type, animation, position }));
|
|
134
|
+
server.registerTool("anim_statemachine_add_state", {
|
|
135
|
+
title: "Add state machine state",
|
|
136
|
+
description: "Add a state to an AnimationNodeStateMachine (undoable). Targets the AnimationTree's tree_root when it is a state machine, or a nested state-machine node via state_machine. Defaults the state to an AnimationNodeAnimation; pass animation to bind a clip.",
|
|
137
|
+
inputSchema: {
|
|
138
|
+
tree_path: z.string().describe("AnimationTree node path relative to the scene root"),
|
|
139
|
+
state_name: z.string().describe("Unique state name within the state machine"),
|
|
140
|
+
animation: z.string().optional().describe("For an AnimationNodeAnimation state: the animation name to play"),
|
|
141
|
+
node_type: z.string().optional().describe("AnimationNode subclass for the state (default AnimationNodeAnimation)"),
|
|
142
|
+
state_machine: z.string().optional().describe("Name of a nested AnimationNodeStateMachine node within tree_root; omit to target tree_root itself"),
|
|
143
|
+
position: z.array(z.number()).optional().describe("Graph editor position [x, y] (default [0, 0])"),
|
|
144
|
+
},
|
|
145
|
+
}, async ({ tree_path, state_name, animation, node_type, state_machine, position }) => call("anim.statemachine_add_state", { tree_path, state_name, animation, node_type: node_type ?? "AnimationNodeAnimation", state_machine: state_machine ?? "", position }));
|
|
146
|
+
server.registerTool("anim_statemachine_add_transition", {
|
|
147
|
+
title: "Add state machine transition",
|
|
148
|
+
description: "Add a transition between two states in an AnimationNodeStateMachine (undoable). from_state/to_state must exist (or be the built-in \"Start\"/\"End\"). switch_mode: immediate|sync|at_end; advance_mode: disabled|enabled|auto.",
|
|
149
|
+
inputSchema: {
|
|
150
|
+
tree_path: z.string().describe("AnimationTree node path relative to the scene root"),
|
|
151
|
+
from_state: z.string().describe("Source state name (or \"Start\")"),
|
|
152
|
+
to_state: z.string().describe("Destination state name (or \"End\")"),
|
|
153
|
+
state_machine: z.string().optional().describe("Name of a nested AnimationNodeStateMachine node within tree_root; omit to target tree_root itself"),
|
|
154
|
+
xfade_time: z.number().optional().describe("Cross-fade time in seconds (default 0)"),
|
|
155
|
+
switch_mode: z.enum(["immediate", "sync", "at_end"]).optional().describe("Switch mode (default immediate)"),
|
|
156
|
+
advance_mode: z.enum(["disabled", "enabled", "auto"]).optional().describe("Advance mode (default enabled)"),
|
|
157
|
+
advance_condition: z.string().optional().describe("Advance condition parameter name (used with advance_mode auto)"),
|
|
158
|
+
priority: z.number().int().optional().describe("Transition priority (lower wins when multiple are valid)"),
|
|
159
|
+
},
|
|
160
|
+
}, async ({ tree_path, from_state, to_state, state_machine, xfade_time, switch_mode, advance_mode, advance_condition, priority }) => call("anim.statemachine_add_transition", { tree_path, from_state, to_state, state_machine: state_machine ?? "", xfade_time: xfade_time ?? 0.0, switch_mode: switch_mode ?? "immediate", advance_mode: advance_mode ?? "enabled", advance_condition: advance_condition ?? "", priority }));
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=animation.js.map
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { gate } from "../../confirm.js";
|
|
3
|
+
/** AudioStreamPlayer nodes + audio bus layout / effects. */
|
|
4
|
+
export function registerAudioTools(server, call) {
|
|
5
|
+
server.registerTool("audio_player_create", {
|
|
6
|
+
title: "Create audio player",
|
|
7
|
+
description: "Add an AudioStreamPlayer / AudioStreamPlayer2D / AudioStreamPlayer3D node under a parent in the edited scene (undoable). `dim` selects \"none\" (default, non-positional AudioStreamPlayer), \"2d\", or \"3d\". Optional initial stream_path (res:// AudioStream), autoplay, volume_db, bus. In-scene and undoable — not gated.",
|
|
8
|
+
inputSchema: {
|
|
9
|
+
parent_path: z.string().describe("Parent node path relative to the scene root; \".\" for the root"),
|
|
10
|
+
dim: z.enum(["none", "2d", "3d"]).optional().describe("Player kind: \"none\" (default, AudioStreamPlayer), \"2d\" (AudioStreamPlayer2D), or \"3d\" (AudioStreamPlayer3D)"),
|
|
11
|
+
name: z.string().optional().describe("Node name (default matches the player class)"),
|
|
12
|
+
stream_path: z.string().optional().describe("res:// path to an AudioStream to assign to the new player"),
|
|
13
|
+
autoplay: z.boolean().optional().describe("Whether the player starts automatically on scene load"),
|
|
14
|
+
volume_db: z.number().optional().describe("Player volume in dB"),
|
|
15
|
+
bus: z.string().optional().describe("Target audio bus name (default \"Master\")"),
|
|
16
|
+
},
|
|
17
|
+
}, async ({ parent_path, dim, name, stream_path, autoplay, volume_db, bus }) => {
|
|
18
|
+
const params = { parent_path };
|
|
19
|
+
if (dim !== undefined)
|
|
20
|
+
params.dim = dim;
|
|
21
|
+
if (name !== undefined)
|
|
22
|
+
params.name = name;
|
|
23
|
+
if (stream_path !== undefined)
|
|
24
|
+
params.stream_path = stream_path;
|
|
25
|
+
if (autoplay !== undefined)
|
|
26
|
+
params.autoplay = autoplay;
|
|
27
|
+
if (volume_db !== undefined)
|
|
28
|
+
params.volume_db = volume_db;
|
|
29
|
+
if (bus !== undefined)
|
|
30
|
+
params.bus = bus;
|
|
31
|
+
return call("audio.player_create", params);
|
|
32
|
+
});
|
|
33
|
+
server.registerTool("audio_set_stream", {
|
|
34
|
+
title: "Set audio stream",
|
|
35
|
+
description: "Load an AudioStream from a res:// path and assign it as stream on an AudioStreamPlayer/2D/3D in the edited scene (undoable). In-scene and undoable — not gated.",
|
|
36
|
+
inputSchema: {
|
|
37
|
+
path: z.string().describe("AudioStreamPlayer/2D/3D node path relative to the scene root"),
|
|
38
|
+
stream_path: z.string().describe("res:// path to an AudioStream resource"),
|
|
39
|
+
},
|
|
40
|
+
}, async ({ path, stream_path }) => call("audio.set_stream", { path, stream_path }));
|
|
41
|
+
server.registerTool("audio_bus_add", {
|
|
42
|
+
title: "Add audio bus",
|
|
43
|
+
description: "Add a bus to the project's global AudioServer layout, optionally naming it, positioning it (at_position), and setting its send bus. DESTRUCTIVE (project-wide audio state) — gated by confirmation.",
|
|
44
|
+
inputSchema: {
|
|
45
|
+
name: z.string().optional().describe("Name for the new bus"),
|
|
46
|
+
at_position: z.number().optional().describe("Insert index (default -1 = append at end)"),
|
|
47
|
+
send: z.string().optional().describe("Name of the bus this bus sends its output to"),
|
|
48
|
+
confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
|
|
49
|
+
},
|
|
50
|
+
}, async ({ name, at_position, send, confirm }) => {
|
|
51
|
+
const blocked = await gate(server, confirm, `Add audio bus${name ? ` "${name}"` : ""}`);
|
|
52
|
+
if (blocked)
|
|
53
|
+
return blocked;
|
|
54
|
+
const params = {};
|
|
55
|
+
if (name !== undefined)
|
|
56
|
+
params.name = name;
|
|
57
|
+
if (at_position !== undefined)
|
|
58
|
+
params.at_position = at_position;
|
|
59
|
+
if (send !== undefined)
|
|
60
|
+
params.send = send;
|
|
61
|
+
return call("audio.bus_add", params);
|
|
62
|
+
});
|
|
63
|
+
server.registerTool("audio_bus_add_effect", {
|
|
64
|
+
title: "Add audio bus effect",
|
|
65
|
+
description: "Instantiate an AudioEffect subclass (by class name, e.g. \"AudioEffectReverb\") and add it to a named bus on the global AudioServer, optionally at a position in the bus's effect chain. DESTRUCTIVE (project-wide audio state) — gated by confirmation.",
|
|
66
|
+
inputSchema: {
|
|
67
|
+
bus: z.string().describe("Target bus name"),
|
|
68
|
+
effect: z.string().describe("AudioEffect subclass name, e.g. \"AudioEffectReverb\", \"AudioEffectDelay\""),
|
|
69
|
+
at_position: z.number().optional().describe("Insert index within the bus's effect chain (default -1 = append)"),
|
|
70
|
+
confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
|
|
71
|
+
},
|
|
72
|
+
}, async ({ bus, effect, at_position, confirm }) => {
|
|
73
|
+
const blocked = await gate(server, confirm, `Add ${effect} to audio bus "${bus}"`);
|
|
74
|
+
if (blocked)
|
|
75
|
+
return blocked;
|
|
76
|
+
const params = { bus, effect };
|
|
77
|
+
if (at_position !== undefined)
|
|
78
|
+
params.at_position = at_position;
|
|
79
|
+
return call("audio.bus_add_effect", params);
|
|
80
|
+
});
|
|
81
|
+
server.registerTool("audio_bus_set_volume", {
|
|
82
|
+
title: "Set audio bus volume",
|
|
83
|
+
description: "Set the volume (in dB) of a named bus on the global AudioServer. DESTRUCTIVE (project-wide audio state) — gated by confirmation.",
|
|
84
|
+
inputSchema: {
|
|
85
|
+
bus: z.string().describe("Bus name"),
|
|
86
|
+
volume_db: z.number().describe("Volume in dB"),
|
|
87
|
+
confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
|
|
88
|
+
},
|
|
89
|
+
}, async ({ bus, volume_db, confirm }) => {
|
|
90
|
+
const blocked = await gate(server, confirm, `Set audio bus "${bus}" volume to ${volume_db} dB`);
|
|
91
|
+
if (blocked)
|
|
92
|
+
return blocked;
|
|
93
|
+
return call("audio.bus_set_volume", { bus, volume_db });
|
|
94
|
+
});
|
|
95
|
+
server.registerTool("audio_set_bus_layout", {
|
|
96
|
+
title: "Save audio bus layout",
|
|
97
|
+
description: "Save the current AudioServer bus layout (buses, effects, volumes) to a .tres resource on disk (default res://default_bus_layout.tres) via generate_bus_layout + ResourceSaver.save. DESTRUCTIVE (writes a file) — gated by confirmation.",
|
|
98
|
+
inputSchema: {
|
|
99
|
+
to_path: z.string().optional().describe("Destination res:// path (default res://default_bus_layout.tres)"),
|
|
100
|
+
confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
|
|
101
|
+
},
|
|
102
|
+
}, async ({ to_path, confirm }) => {
|
|
103
|
+
const blocked = await gate(server, confirm, `Save audio bus layout to ${to_path ?? "res://default_bus_layout.tres"}`);
|
|
104
|
+
if (blocked)
|
|
105
|
+
return blocked;
|
|
106
|
+
return call("audio.set_bus_layout", to_path !== undefined ? { to_path } : {});
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=audio.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ok } from "../lsp-common.js";
|
|
2
|
+
/**
|
|
3
|
+
* MCP error envelope for a failed editor-bridge call (never throws to the
|
|
4
|
+
* caller). Distinct from lsp-common's `fail` (which labels errors "LSP error");
|
|
5
|
+
* this one labels them "Bridge error".
|
|
6
|
+
*/
|
|
7
|
+
export function fail(err) {
|
|
8
|
+
const be = err;
|
|
9
|
+
const code = be?.code ?? "error";
|
|
10
|
+
const message = be?.message ?? String(err);
|
|
11
|
+
return {
|
|
12
|
+
isError: true,
|
|
13
|
+
content: [{ type: "text", text: `Bridge error [${code}]: ${message}` }],
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Build the shared bridge-call helper used by every editor tool group: forward
|
|
18
|
+
* a method to the in-editor addon over TCP and wrap the result in the standard
|
|
19
|
+
* MCP success envelope, or a friendly Bridge-error envelope when unreachable.
|
|
20
|
+
*/
|
|
21
|
+
export function makeCall(bridge) {
|
|
22
|
+
return async (method, params = {}) => {
|
|
23
|
+
try {
|
|
24
|
+
return ok(await bridge.request(method, params));
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
return fail(err);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=common.js.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { gate } from "../../confirm.js";
|
|
3
|
+
/** Editor session + core project settings (editor_* / project get/set). */
|
|
4
|
+
export function registerCoreTools(server, call) {
|
|
5
|
+
server.registerTool("editor_ping", { title: "Ping editor bridge", description: "Check that the editor is running with the Breakpoint MCP plugin enabled.", inputSchema: {} }, async () => call("ping"));
|
|
6
|
+
server.registerTool("editor_get_state", { title: "Editor state", description: "Return the currently edited scene, its root type/path, and the current node selection.", inputSchema: {} }, async () => call("editor.get_state"));
|
|
7
|
+
server.registerTool("editor_undo", {
|
|
8
|
+
title: "Undo last edit",
|
|
9
|
+
description: "Undo the most recent edit in the editor's undo history (like Ctrl-Z). Defaults to the edited scene's history; pass scope='global' for the editor-wide history. Returns whether an action was undone, its name, and the remaining undo/redo depth.",
|
|
10
|
+
inputSchema: {
|
|
11
|
+
scope: z
|
|
12
|
+
.enum(["scene", "global"])
|
|
13
|
+
.optional()
|
|
14
|
+
.describe("Which history to step: 'scene' (default, the edited scene) or 'global' (editor-wide)"),
|
|
15
|
+
},
|
|
16
|
+
}, async ({ scope }) => call("edit.undo", scope ? { scope } : {}));
|
|
17
|
+
server.registerTool("editor_redo", {
|
|
18
|
+
title: "Redo last undone edit",
|
|
19
|
+
description: "Re-apply the most recently undone edit in the editor's undo history (like Ctrl-Shift-Z). Defaults to the edited scene's history; pass scope='global' for the editor-wide history.",
|
|
20
|
+
inputSchema: {
|
|
21
|
+
scope: z
|
|
22
|
+
.enum(["scene", "global"])
|
|
23
|
+
.optional()
|
|
24
|
+
.describe("Which history to step: 'scene' (default, the edited scene) or 'global' (editor-wide)"),
|
|
25
|
+
},
|
|
26
|
+
}, async ({ scope }) => call("edit.redo", scope ? { scope } : {}));
|
|
27
|
+
server.registerTool("project_get_info", { title: "Project info", description: "Return project name, main scene, project root path, Godot version, and feature tags.", inputSchema: {} }, async () => call("project.get_info"));
|
|
28
|
+
server.registerTool("project_get_setting", {
|
|
29
|
+
title: "Get project setting",
|
|
30
|
+
description: "Read a single ProjectSettings value by dotted key (e.g. application/config/name).",
|
|
31
|
+
inputSchema: { name: z.string().describe("ProjectSettings key") },
|
|
32
|
+
}, async ({ name }) => call("project.get_setting", { name }));
|
|
33
|
+
server.registerTool("project_set_setting", {
|
|
34
|
+
title: "Set project setting",
|
|
35
|
+
description: "Write a ProjectSettings value. Set save=true to persist to project.godot. DESTRUCTIVE — gated by confirmation.",
|
|
36
|
+
inputSchema: {
|
|
37
|
+
name: z.string().describe("ProjectSettings key"),
|
|
38
|
+
value: z.any().describe("New value; rich types use the {\"__type__\":...} tagging convention"),
|
|
39
|
+
save: z.boolean().optional().describe("Persist to disk (default false)"),
|
|
40
|
+
confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
|
|
41
|
+
},
|
|
42
|
+
}, async ({ name, value, save, confirm }) => {
|
|
43
|
+
const blocked = await gate(server, confirm, `Set project setting "${name}"${save ? " and save project.godot" : ""}`);
|
|
44
|
+
if (blocked)
|
|
45
|
+
return blocked;
|
|
46
|
+
return call("project.set_setting", { name, value, save: save ?? false });
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=core.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { gate } from "../../confirm.js";
|
|
3
|
+
/** Project FileSystem dock ops: list / scan / move / mkdir. */
|
|
4
|
+
export function registerFilesystemTools(server, call) {
|
|
5
|
+
server.registerTool("filesystem_list", {
|
|
6
|
+
title: "List project directory",
|
|
7
|
+
description: "List the subdirectories and files of a directory in the project filesystem (hidden entries like .godot are skipped). Read-only.",
|
|
8
|
+
inputSchema: { path: z.string().optional().describe("Directory res:// path (default res://)") },
|
|
9
|
+
}, async ({ path }) => call("filesystem.list", path !== undefined ? { path } : {}));
|
|
10
|
+
server.registerTool("filesystem_scan", {
|
|
11
|
+
title: "Rescan filesystem",
|
|
12
|
+
description: "Trigger an editor rescan of the project filesystem so newly added or externally-changed files are picked up. Read-only side effect.",
|
|
13
|
+
inputSchema: {},
|
|
14
|
+
}, async () => call("filesystem.scan"));
|
|
15
|
+
server.registerTool("filesystem_move", {
|
|
16
|
+
title: "Move / rename file",
|
|
17
|
+
description: "Move or rename a file or directory within the project (carrying its .import sidecar), then rescan. DESTRUCTIVE (moves on disk; does NOT remap references in other resources) — gated by confirmation.",
|
|
18
|
+
inputSchema: {
|
|
19
|
+
from_path: z.string().describe("Source res:// path (file or directory)"),
|
|
20
|
+
to_path: z.string().describe("Destination res:// path"),
|
|
21
|
+
confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
|
|
22
|
+
},
|
|
23
|
+
}, async ({ from_path, to_path, confirm }) => {
|
|
24
|
+
const blocked = await gate(server, confirm, `Move ${from_path} to ${to_path}`);
|
|
25
|
+
if (blocked)
|
|
26
|
+
return blocked;
|
|
27
|
+
return call("filesystem.move", { from_path, to_path });
|
|
28
|
+
});
|
|
29
|
+
server.registerTool("filesystem_create_dir", {
|
|
30
|
+
title: "Create directory",
|
|
31
|
+
description: "Create a directory (recursively) in the project filesystem, then rescan. No-op if it already exists.",
|
|
32
|
+
inputSchema: { path: z.string().describe("Directory res:// path to create") },
|
|
33
|
+
}, async ({ path }) => call("filesystem.create_dir", { path }));
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=filesystem.js.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { fail } from "./common.js";
|
|
3
|
+
/** Editor selection, ClassDB / docs lookups, and viewport screenshot. */
|
|
4
|
+
export function registerIntrospectionTools(server, call, bridge) {
|
|
5
|
+
server.registerTool("selection_get", { title: "Get selection", description: "Return the paths of the nodes currently selected in the editor.", inputSchema: {} }, async () => call("selection.get"));
|
|
6
|
+
server.registerTool("selection_set", {
|
|
7
|
+
title: "Set selection",
|
|
8
|
+
description: "Replace the editor's node selection with the given node paths.",
|
|
9
|
+
inputSchema: { paths: z.array(z.string()).describe("Node paths relative to the scene root") },
|
|
10
|
+
}, async ({ paths }) => call("selection.set", { paths }));
|
|
11
|
+
server.registerTool("classdb_get_class", {
|
|
12
|
+
title: "Introspect class",
|
|
13
|
+
description: "Return the parent class, methods, properties, and signals of an engine class via ClassDB.",
|
|
14
|
+
inputSchema: {
|
|
15
|
+
class_name: z.string().describe("Engine class name, e.g. AudioStreamPlayer3D"),
|
|
16
|
+
include_inherited: z.boolean().optional().describe("Include inherited members (default false)"),
|
|
17
|
+
},
|
|
18
|
+
}, async ({ class_name, include_inherited }) => call("classdb.get_class", { class_name, include_inherited: include_inherited ?? false }));
|
|
19
|
+
server.registerTool("class_reference", {
|
|
20
|
+
title: "Class reference",
|
|
21
|
+
description: "Full engine-class reference via ClassDB: method SIGNATURES (return type + typed args), signal " +
|
|
22
|
+
"signatures, and typed properties — the detailed view classdb_get_class summarises as bare names. " +
|
|
23
|
+
"Read-only. Includes the canonical online docs URL. Pass member to filter to a single method/property/signal.",
|
|
24
|
+
inputSchema: {
|
|
25
|
+
class_name: z.string().describe("Engine class name, e.g. AudioStreamPlayer3D"),
|
|
26
|
+
include_inherited: z.boolean().optional().describe("Include inherited members (default false)"),
|
|
27
|
+
member: z.string().optional().describe("Only return members whose name contains this substring"),
|
|
28
|
+
},
|
|
29
|
+
}, async ({ class_name, include_inherited, member }) => call("classdb.reference", {
|
|
30
|
+
class_name,
|
|
31
|
+
include_inherited: include_inherited ?? false,
|
|
32
|
+
member: member ?? "",
|
|
33
|
+
}));
|
|
34
|
+
server.registerTool("docs_search", {
|
|
35
|
+
title: "Search the class reference",
|
|
36
|
+
description: "Search the Godot class reference (ClassDB) by keyword — matching class names and, unless a scope narrows " +
|
|
37
|
+
"it, their methods/properties/signals — and return each hit with its canonical docs URL. Read-only. " +
|
|
38
|
+
"Use kind to restrict to one member type, class_name to scope to a single class, and limit to bound results.",
|
|
39
|
+
inputSchema: {
|
|
40
|
+
query: z.string().describe("Case-insensitive substring to match against class / member names"),
|
|
41
|
+
kind: z.enum(["any", "class", "method", "property", "signal"]).optional().describe("Restrict to one result kind (default any)"),
|
|
42
|
+
class_name: z.string().optional().describe("Scope the member search to a single class (still returns class-name matches project-wide)"),
|
|
43
|
+
limit: z.number().int().positive().optional().describe("Max results before truncation (default 40)"),
|
|
44
|
+
deep: z.boolean().optional().describe("Also scan members, not just class names (default true)"),
|
|
45
|
+
},
|
|
46
|
+
}, async ({ query, kind, class_name, limit, deep }) => call("docs.search", {
|
|
47
|
+
query,
|
|
48
|
+
kind: kind ?? "any",
|
|
49
|
+
class_name: class_name ?? "",
|
|
50
|
+
limit: limit ?? 40,
|
|
51
|
+
deep: deep ?? true,
|
|
52
|
+
}));
|
|
53
|
+
server.registerTool("screenshot_editor", {
|
|
54
|
+
title: "Screenshot editor viewport",
|
|
55
|
+
description: "Capture the 2D or 3D editor viewport as a PNG and return it as image content so the assistant can see the scene. " +
|
|
56
|
+
"Requires the matching editor tab (2D/3D) to be active and rendered.",
|
|
57
|
+
inputSchema: { viewport: z.enum(["2d", "3d"]).optional().describe("Which viewport (default 3d)") },
|
|
58
|
+
}, async ({ viewport }) => {
|
|
59
|
+
try {
|
|
60
|
+
const r = (await bridge.request("screenshot.editor_viewport", { viewport: viewport ?? "3d" }));
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{ type: "image", data: r.base64, mimeType: r.mime },
|
|
64
|
+
{ type: "text", text: `Captured ${r.viewport} viewport (${r.width}x${r.height}).` },
|
|
65
|
+
],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
return fail(err);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=introspection.js.map
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { gate } from "../../confirm.js";
|
|
3
|
+
/** Scene-tree node authoring: add / delete / reparent / property / group ops. */
|
|
4
|
+
export function registerNodeTools(server, call) {
|
|
5
|
+
server.registerTool("node_add", {
|
|
6
|
+
title: "Add node",
|
|
7
|
+
description: "Instance a node of the given class under a parent path (undoable). Returns the new node's path.",
|
|
8
|
+
inputSchema: {
|
|
9
|
+
parent_path: z.string().describe("Parent node path relative to the scene root; \".\" for the root"),
|
|
10
|
+
type: z.string().describe("Node class to instance, e.g. Sprite2D, AudioStreamPlayer3D"),
|
|
11
|
+
name: z.string().optional().describe("Node name (defaults to the class name)"),
|
|
12
|
+
},
|
|
13
|
+
}, async ({ parent_path, type, name }) => call("node.add", { parent_path, type, name }));
|
|
14
|
+
server.registerTool("node_delete", {
|
|
15
|
+
title: "Delete node",
|
|
16
|
+
description: "Delete a node (undoable). DESTRUCTIVE — gated by confirmation. Refuses to delete the scene root.",
|
|
17
|
+
inputSchema: {
|
|
18
|
+
path: z.string().describe("Node path relative to the scene root"),
|
|
19
|
+
confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
|
|
20
|
+
},
|
|
21
|
+
}, async ({ path, confirm }) => {
|
|
22
|
+
const blocked = await gate(server, confirm, `Delete node "${path}"`);
|
|
23
|
+
if (blocked)
|
|
24
|
+
return blocked;
|
|
25
|
+
return call("node.delete", { path });
|
|
26
|
+
});
|
|
27
|
+
server.registerTool("node_rename", {
|
|
28
|
+
title: "Rename node",
|
|
29
|
+
description: "Rename a node (undoable).",
|
|
30
|
+
inputSchema: {
|
|
31
|
+
path: z.string().describe("Node path relative to the scene root"),
|
|
32
|
+
new_name: z.string().describe("New node name"),
|
|
33
|
+
},
|
|
34
|
+
}, async ({ path, new_name }) => call("node.rename", { path, new_name }));
|
|
35
|
+
server.registerTool("node_reparent", {
|
|
36
|
+
title: "Reparent node",
|
|
37
|
+
description: "Move a node under a new parent (undoable).",
|
|
38
|
+
inputSchema: {
|
|
39
|
+
path: z.string().describe("Node path relative to the scene root"),
|
|
40
|
+
new_parent_path: z.string().describe("New parent path; \".\" for the root"),
|
|
41
|
+
keep_global_transform: z.boolean().optional().describe("Preserve global transform (default true)"),
|
|
42
|
+
},
|
|
43
|
+
}, async ({ path, new_parent_path, keep_global_transform }) => call("node.reparent", { path, new_parent_path, keep_global_transform: keep_global_transform ?? true }));
|
|
44
|
+
server.registerTool("node_set_property", {
|
|
45
|
+
title: "Set node property",
|
|
46
|
+
description: "Set a property on a node (undoable). Rich types use the {\"__type__\":\"Vector3\",\"x\":..} convention. " +
|
|
47
|
+
"Example value for position: {\"__type__\":\"Vector3\",\"x\":1,\"y\":0,\"z\":2}.",
|
|
48
|
+
inputSchema: {
|
|
49
|
+
path: z.string().describe("Node path relative to the scene root"),
|
|
50
|
+
property: z.string().describe("Property name, e.g. position, modulate, text"),
|
|
51
|
+
value: z.any().describe("New value (JSON scalar, array, object, or a __type__-tagged Variant)"),
|
|
52
|
+
},
|
|
53
|
+
}, async ({ path, property, value }) => call("node.set_property", { path, property, value }));
|
|
54
|
+
server.registerTool("node_get_property", {
|
|
55
|
+
title: "Get node property",
|
|
56
|
+
description: "Read a single property from a node. Rich types come back __type__-tagged.",
|
|
57
|
+
inputSchema: {
|
|
58
|
+
path: z.string().describe("Node path relative to the scene root"),
|
|
59
|
+
property: z.string().describe("Property name"),
|
|
60
|
+
},
|
|
61
|
+
}, async ({ path, property }) => call("node.get_property", { path, property }));
|
|
62
|
+
server.registerTool("node_duplicate", {
|
|
63
|
+
title: "Duplicate node",
|
|
64
|
+
description: "Duplicate a node and its children under the same parent (undoable). Returns the new node's path.",
|
|
65
|
+
inputSchema: {
|
|
66
|
+
path: z.string().describe("Node path relative to the scene root"),
|
|
67
|
+
name: z.string().optional().describe("Name for the duplicate (defaults to Godot's auto-numbered name)"),
|
|
68
|
+
},
|
|
69
|
+
}, async ({ path, name }) => call("node.duplicate", name !== undefined ? { path, name } : { path }));
|
|
70
|
+
server.registerTool("node_get_children", {
|
|
71
|
+
title: "Get node children",
|
|
72
|
+
description: "List the direct children of a node (name, type, path). Read-only.",
|
|
73
|
+
inputSchema: { path: z.string().describe("Node path relative to the scene root; \".\" for the root") },
|
|
74
|
+
}, async ({ path }) => call("node.get_children", { path }));
|
|
75
|
+
server.registerTool("node_find", {
|
|
76
|
+
title: "Find nodes",
|
|
77
|
+
description: "Search a node's descendants by class and/or name substring (case-insensitive). Read-only.",
|
|
78
|
+
inputSchema: {
|
|
79
|
+
root_path: z.string().optional().describe("Where to search from; \".\" or omitted for the scene root"),
|
|
80
|
+
type: z.string().optional().describe("Only match nodes of this class (is_class), e.g. Sprite2D"),
|
|
81
|
+
name_contains: z.string().optional().describe("Only match nodes whose name contains this substring"),
|
|
82
|
+
limit: z.number().int().positive().optional().describe("Max matches to return (default 200)"),
|
|
83
|
+
},
|
|
84
|
+
}, async ({ root_path, type, name_contains, limit }) => call("node.find", {
|
|
85
|
+
root_path: root_path ?? ".",
|
|
86
|
+
type: type ?? "",
|
|
87
|
+
name_contains: name_contains ?? "",
|
|
88
|
+
limit: limit ?? 200,
|
|
89
|
+
}));
|
|
90
|
+
server.registerTool("node_list_groups", {
|
|
91
|
+
title: "List node groups",
|
|
92
|
+
description: "List the groups a node belongs to. Read-only.",
|
|
93
|
+
inputSchema: { path: z.string().describe("Node path relative to the scene root") },
|
|
94
|
+
}, async ({ path }) => call("node.list_groups", { path }));
|
|
95
|
+
server.registerTool("node_add_to_group", {
|
|
96
|
+
title: "Add node to group",
|
|
97
|
+
description: "Add a node to a group (persistent, undoable). No-op if already a member.",
|
|
98
|
+
inputSchema: {
|
|
99
|
+
path: z.string().describe("Node path relative to the scene root"),
|
|
100
|
+
group: z.string().describe("Group name"),
|
|
101
|
+
},
|
|
102
|
+
}, async ({ path, group }) => call("node.add_to_group", { path, group }));
|
|
103
|
+
server.registerTool("node_remove_from_group", {
|
|
104
|
+
title: "Remove node from group",
|
|
105
|
+
description: "Remove a node from a group (undoable). No-op if not a member.",
|
|
106
|
+
inputSchema: {
|
|
107
|
+
path: z.string().describe("Node path relative to the scene root"),
|
|
108
|
+
group: z.string().describe("Group name"),
|
|
109
|
+
},
|
|
110
|
+
}, async ({ path, group }) => call("node.remove_from_group", { path, group }));
|
|
111
|
+
server.registerTool("node_instantiate_scene", {
|
|
112
|
+
title: "Instance scene under node",
|
|
113
|
+
description: "Instance an external PackedScene (res:// path) as an editable child of a parent node (undoable). Returns the new node's path.",
|
|
114
|
+
inputSchema: {
|
|
115
|
+
parent_path: z.string().describe("Parent node path relative to the scene root; \".\" for the root"),
|
|
116
|
+
scene_path: z.string().describe("Scene to instance, e.g. res://actors/enemy.tscn"),
|
|
117
|
+
name: z.string().optional().describe("Node name (defaults to the instanced scene's root name)"),
|
|
118
|
+
},
|
|
119
|
+
}, async ({ parent_path, scene_path, name }) => call("node.instantiate_scene", name !== undefined ? { parent_path, scene_path, name } : { parent_path, scene_path }));
|
|
120
|
+
server.registerTool("node_move_child", {
|
|
121
|
+
title: "Move child",
|
|
122
|
+
description: "Reorder a node among its siblings by index (undoable). Negative indices count from the end (-1 = last).",
|
|
123
|
+
inputSchema: {
|
|
124
|
+
path: z.string().describe("Node path relative to the scene root"),
|
|
125
|
+
to_index: z.number().int().describe("New sibling index (0-based; negative counts from the end)"),
|
|
126
|
+
},
|
|
127
|
+
}, async ({ path, to_index }) => call("node.move_child", { path, to_index }));
|
|
128
|
+
server.registerTool("node_change_type", {
|
|
129
|
+
title: "Change node type",
|
|
130
|
+
description: "Replace a node with a new node of a different class, carrying over compatible properties, children, and groups (undoable). Refuses the scene root.",
|
|
131
|
+
inputSchema: {
|
|
132
|
+
path: z.string().describe("Node path relative to the scene root"),
|
|
133
|
+
type: z.string().describe("New node class, e.g. Sprite2D, StaticBody3D"),
|
|
134
|
+
},
|
|
135
|
+
}, async ({ path, type }) => call("node.change_type", { path, type }));
|
|
136
|
+
server.registerTool("node_set_owner", {
|
|
137
|
+
title: "Set node owner",
|
|
138
|
+
description: "Set a node's owner (undoable). Owner must be an ancestor; \".\" or omitted sets the scene root. Ownership determines which scene a node is saved into.",
|
|
139
|
+
inputSchema: {
|
|
140
|
+
path: z.string().describe("Node path relative to the scene root"),
|
|
141
|
+
owner_path: z.string().optional().describe("Ancestor to own the node; \".\" or omitted for the scene root"),
|
|
142
|
+
},
|
|
143
|
+
}, async ({ path, owner_path }) => call("node.set_owner", owner_path !== undefined ? { path, owner_path } : { path }));
|
|
144
|
+
server.registerTool("node_call_method", {
|
|
145
|
+
title: "Call node method (edit-time)",
|
|
146
|
+
description: "Invoke a method on a node in the EDITED scene. DESTRUCTIVE (arbitrary invocation, not undoable) — gated by confirmation. " +
|
|
147
|
+
"Args use the tagged-Variant convention; the return value comes back tagged.",
|
|
148
|
+
inputSchema: {
|
|
149
|
+
path: z.string().describe("Node path relative to the scene root"),
|
|
150
|
+
method: z.string().describe("Method name"),
|
|
151
|
+
args: z.array(z.any()).optional().describe("Positional arguments (JSON scalars or __type__-tagged Variants)"),
|
|
152
|
+
confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
|
|
153
|
+
},
|
|
154
|
+
}, async ({ path, method, args, confirm }) => {
|
|
155
|
+
const blocked = await gate(server, confirm, `Call ${path}.${method}() in the edited scene`);
|
|
156
|
+
if (blocked)
|
|
157
|
+
return blocked;
|
|
158
|
+
return call("node.call_method", { path, method, args: args ?? [] });
|
|
159
|
+
});
|
|
160
|
+
server.registerTool("node_get_path", {
|
|
161
|
+
title: "Get node path",
|
|
162
|
+
description: "Return a node's scene-relative path, class, sibling index, parent path, and child count. Read-only.",
|
|
163
|
+
inputSchema: { path: z.string().describe("Node path relative to the scene root; \".\" for the root") },
|
|
164
|
+
}, async ({ path }) => call("node.get_path", { path }));
|
|
165
|
+
server.registerTool("node_list_properties", {
|
|
166
|
+
title: "List node properties",
|
|
167
|
+
description: "List a node's inspector-visible properties (name, Variant type, class_name, usage flags). Read-only. Use node_get_property to read a value.",
|
|
168
|
+
inputSchema: { path: z.string().describe("Node path relative to the scene root") },
|
|
169
|
+
}, async ({ path }) => call("node.list_properties", { path }));
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=node.js.map
|