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.
@@ -0,0 +1,95 @@
1
+ import { z } from "zod";
2
+ import { gate } from "../../confirm.js";
3
+ /** Scene lifecycle: open / save / new / reload / pack / dependencies. */
4
+ export function registerSceneTools(server, call) {
5
+ server.registerTool("scene_get_tree", {
6
+ title: "Get scene tree",
7
+ description: "Return the node tree of the currently edited scene (name, type, path, script, children).",
8
+ inputSchema: { max_depth: z.number().int().positive().optional().describe("Max recursion depth (default 64)") },
9
+ }, async ({ max_depth }) => call("scene.get_tree", max_depth ? { max_depth } : {}));
10
+ server.registerTool("scene_open", {
11
+ title: "Open scene",
12
+ description: "Open an existing scene in the editor by res:// path.",
13
+ inputSchema: { path: z.string().describe("Scene path, e.g. res://scenes/main.tscn") },
14
+ }, async ({ path }) => call("scene.open", { path }));
15
+ server.registerTool("scene_save", { title: "Save scene", description: "Save the currently edited scene to its existing path.", inputSchema: {} }, async () => call("scene.save"));
16
+ server.registerTool("scene_new", {
17
+ title: "New scene",
18
+ description: "Create a new scene with the given root class, save it to a path, and open it. DESTRUCTIVE (writes a file) — gated by confirmation.",
19
+ inputSchema: {
20
+ root_type: z.string().describe("Root node class, e.g. Node2D, Node3D, Control"),
21
+ path: z.string().describe("Where to save, e.g. res://scenes/new.tscn"),
22
+ name: z.string().optional().describe("Root node name (defaults to the class name)"),
23
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
24
+ },
25
+ }, async ({ root_type, path, name, confirm }) => {
26
+ const blocked = await gate(server, confirm, `Create and overwrite scene file "${path}"`);
27
+ if (blocked)
28
+ return blocked;
29
+ return call("scene.new", { root_type, path, name });
30
+ });
31
+ server.registerTool("scene_list_open", {
32
+ title: "List open scenes",
33
+ description: "List the res:// paths of all scenes open in the editor, which one is current, and which have unsaved changes. Read-only.",
34
+ inputSchema: {},
35
+ }, async () => call("scene.list_open"));
36
+ server.registerTool("scene_reload", {
37
+ title: "Reload scene from disk",
38
+ description: "Reload a scene from disk, discarding unsaved changes to it. DESTRUCTIVE — gated by confirmation. Defaults to the current scene.",
39
+ inputSchema: {
40
+ path: z.string().optional().describe("Scene res:// path; omitted = the current edited scene"),
41
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
42
+ },
43
+ }, async ({ path, confirm }) => {
44
+ const blocked = await gate(server, confirm, `Reload scene ${path ?? "(current)"} from disk, discarding unsaved changes`);
45
+ if (blocked)
46
+ return blocked;
47
+ return call("scene.reload", path !== undefined ? { path } : {});
48
+ });
49
+ server.registerTool("scene_close", {
50
+ title: "Close current scene",
51
+ description: "Close the current scene tab, discarding unsaved changes. DESTRUCTIVE — gated by confirmation. Only the current scene can be closed; pass its path to assert which one.",
52
+ inputSchema: {
53
+ path: z.string().optional().describe("Optional assertion: must equal the current edited scene's path"),
54
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
55
+ },
56
+ }, async ({ path, confirm }) => {
57
+ const blocked = await gate(server, confirm, "Close the current scene, discarding unsaved changes");
58
+ if (blocked)
59
+ return blocked;
60
+ return call("scene.close", path !== undefined ? { path } : {});
61
+ });
62
+ server.registerTool("scene_pack", {
63
+ title: "Pack branch as scene",
64
+ description: "Save a node branch (the node and its subtree) as a new PackedScene file. DESTRUCTIVE (writes a file) — gated by confirmation. Does not modify the edited scene.",
65
+ inputSchema: {
66
+ path: z.string().describe("Branch root node path relative to the scene root"),
67
+ to_path: z.string().describe("Where to save the PackedScene, e.g. res://scenes/branch.tscn"),
68
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
69
+ },
70
+ }, async ({ path, to_path, confirm }) => {
71
+ const blocked = await gate(server, confirm, `Pack branch "${path}" to ${to_path}`);
72
+ if (blocked)
73
+ return blocked;
74
+ return call("scene.pack", { path, to_path });
75
+ });
76
+ server.registerTool("scene_get_dependencies", {
77
+ title: "Scene dependencies",
78
+ description: "List the external resource dependencies of a scene file. Read-only. Defaults to the current scene.",
79
+ inputSchema: { path: z.string().optional().describe("Scene res:// path; omitted = the current edited scene") },
80
+ }, async ({ path }) => call("scene.get_dependencies", path !== undefined ? { path } : {}));
81
+ server.registerTool("scene_save_as", {
82
+ title: "Save scene as",
83
+ description: "Save the current scene to a new res:// path (Save As). DESTRUCTIVE (writes a file) — gated by confirmation.",
84
+ inputSchema: {
85
+ path: z.string().describe("Destination path, e.g. res://scenes/copy.tscn"),
86
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
87
+ },
88
+ }, async ({ path, confirm }) => {
89
+ const blocked = await gate(server, confirm, `Save the current scene to ${path}`);
90
+ if (blocked)
91
+ return blocked;
92
+ return call("scene.save_as", { path });
93
+ });
94
+ }
95
+ //# sourceMappingURL=scene.js.map
@@ -0,0 +1,59 @@
1
+ import { z } from "zod";
2
+ import { gate } from "../../confirm.js";
3
+ /** Shader + ShaderMaterial authoring. */
4
+ export function registerShaderTools(server, call) {
5
+ server.registerTool("shader_create", {
6
+ title: "Create shader",
7
+ description: "Create a Shader resource with optional initial code and save it as a .gdshader file. DESTRUCTIVE (writes a file) — gated by confirmation.",
8
+ inputSchema: {
9
+ to_path: z.string().describe("Destination res:// path, e.g. res://shaders/glow.gdshader"),
10
+ code: z.string().optional().describe("Initial GDShader source (e.g. \"shader_type canvas_item; ...\")"),
11
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
12
+ },
13
+ }, async ({ to_path, code, confirm }) => {
14
+ const blocked = await gate(server, confirm, `Create shader at ${to_path}`);
15
+ if (blocked)
16
+ return blocked;
17
+ return call("shader.create", code !== undefined ? { to_path, code } : { to_path });
18
+ });
19
+ server.registerTool("shader_set_code", {
20
+ title: "Set shader code",
21
+ description: "Replace the source code of an existing .gdshader resource and save it. DESTRUCTIVE (writes a file) — gated by confirmation.",
22
+ inputSchema: {
23
+ path: z.string().describe("Shader res:// path"),
24
+ code: z.string().describe("New GDShader source"),
25
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
26
+ },
27
+ }, async ({ path, code, confirm }) => {
28
+ const blocked = await gate(server, confirm, `Overwrite shader code at ${path}`);
29
+ if (blocked)
30
+ return blocked;
31
+ return call("shader.set_code", { path, code });
32
+ });
33
+ server.registerTool("shadermaterial_create", {
34
+ title: "Create shader material",
35
+ description: "Create a ShaderMaterial and assign it to a node's material slot in the edited scene (undoable). Targets CanvasItem.material (2D / Control) or GeometryInstance3D.material_override (3D); other node types return unsupported. Optionally assign a Shader loaded from a res:// path. In-scene and undoable — not gated.",
36
+ inputSchema: {
37
+ path: z.string().describe("Node path relative to the scene root (a CanvasItem or GeometryInstance3D)"),
38
+ shader_path: z.string().optional().describe("res:// path to a Shader to assign to the new material"),
39
+ },
40
+ }, async ({ path, shader_path }) => call("shadermaterial.create", shader_path !== undefined ? { path, shader_path } : { path }));
41
+ server.registerTool("shadermaterial_set_shader", {
42
+ title: "Set shader material shader",
43
+ description: "Load a Shader from a res:// path and assign it to the ShaderMaterial on a node's material slot in the edited scene (undoable). The node must already have a ShaderMaterial. In-scene and undoable — not gated.",
44
+ inputSchema: {
45
+ path: z.string().describe("Node path relative to the scene root"),
46
+ shader_path: z.string().describe("res:// path to a Shader resource"),
47
+ },
48
+ }, async ({ path, shader_path }) => call("shadermaterial.set_shader", { path, shader_path }));
49
+ server.registerTool("shadermaterial_set_param", {
50
+ title: "Set shader material parameter",
51
+ description: "Set a shader uniform parameter on the ShaderMaterial of a node's material slot in the edited scene (undoable). The node must already have a ShaderMaterial. The value uses the tagged-Variant convention. In-scene and undoable — not gated.",
52
+ inputSchema: {
53
+ path: z.string().describe("Node path relative to the scene root"),
54
+ param: z.string().describe("Shader uniform name"),
55
+ value: z.any().describe("New value (JSON scalar or __type__-tagged Variant)"),
56
+ },
57
+ }, async ({ path, param, value }) => call("shadermaterial.set_param", { path, param, value }));
58
+ }
59
+ //# sourceMappingURL=shader.js.map
@@ -0,0 +1,67 @@
1
+ import { z } from "zod";
2
+ import { gate } from "../../confirm.js";
3
+ /** Signal introspection and (dis)connection on nodes. */
4
+ export function registerSignalTools(server, call) {
5
+ server.registerTool("signal_list", {
6
+ title: "List node signals",
7
+ description: "List the signals a node declares (name and argument names), including user signals. Read-only.",
8
+ inputSchema: { path: z.string().describe("Node path relative to the scene root") },
9
+ }, async ({ path }) => call("signal.list", { path }));
10
+ server.registerTool("signal_list_connections", {
11
+ title: "List signal connections",
12
+ description: "List a node's outgoing signal connections (signal, target node path, method, flags). Optionally filter to one signal. Read-only.",
13
+ inputSchema: {
14
+ path: z.string().describe("Node path relative to the scene root"),
15
+ signal: z.string().optional().describe("Only list connections for this signal"),
16
+ },
17
+ }, async ({ path, signal }) => call("signal.list_connections", signal !== undefined ? { path, signal } : { path }));
18
+ server.registerTool("signal_connect", {
19
+ title: "Connect signal",
20
+ description: "Connect a source node's signal to a target node's method (undoable). Persistent by default (flags=2, CONNECT_PERSIST) so it saves into the scene. No-op if already connected.",
21
+ inputSchema: {
22
+ path: z.string().describe("Source node path (emitter)"),
23
+ signal: z.string().describe("Signal name on the source"),
24
+ target_path: z.string().describe("Target node path (receiver)"),
25
+ method: z.string().describe("Method to call on the target"),
26
+ flags: z.number().int().optional().describe("Object.ConnectFlags bitmask (default 2 = CONNECT_PERSIST)"),
27
+ },
28
+ }, async ({ path, signal, target_path, method, flags }) => call("signal.connect", flags !== undefined ? { path, signal, target_path, method, flags } : { path, signal, target_path, method }));
29
+ server.registerTool("signal_disconnect", {
30
+ title: "Disconnect signal",
31
+ description: "Disconnect a source node's signal from a target node's method (undoable). No-op if not connected.",
32
+ inputSchema: {
33
+ path: z.string().describe("Source node path (emitter)"),
34
+ signal: z.string().describe("Signal name on the source"),
35
+ target_path: z.string().describe("Target node path (receiver)"),
36
+ method: z.string().describe("Connected method on the target"),
37
+ },
38
+ }, async ({ path, signal, target_path, method }) => call("signal.disconnect", { path, signal, target_path, method }));
39
+ server.registerTool("signal_add_user_signal", {
40
+ title: "Add user signal",
41
+ description: "Declare a new user signal on a node (undoable). Optional typed arguments. Errors if the signal already exists.",
42
+ inputSchema: {
43
+ path: z.string().describe("Node path relative to the scene root"),
44
+ signal: z.string().describe("New signal name"),
45
+ args: z
46
+ .array(z.object({ name: z.string(), type: z.number().int().optional() }))
47
+ .optional()
48
+ .describe("Signal parameters (name + Variant type int)"),
49
+ },
50
+ }, async ({ path, signal, args }) => call("signal.add_user_signal", args !== undefined ? { path, signal, args } : { path, signal }));
51
+ server.registerTool("signal_emit", {
52
+ title: "Emit signal (edit-time)",
53
+ description: "Emit a signal from a node in the EDITED scene, firing its connected callables now. DESTRUCTIVE (edit-time side effects) — gated by confirmation. Args use the tagged-Variant convention.",
54
+ inputSchema: {
55
+ path: z.string().describe("Node path relative to the scene root"),
56
+ signal: z.string().describe("Signal name"),
57
+ args: z.array(z.any()).optional().describe("Signal arguments (JSON scalars or __type__-tagged Variants)"),
58
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
59
+ },
60
+ }, async ({ path, signal, args, confirm }) => {
61
+ const blocked = await gate(server, confirm, `Emit signal "${signal}" from ${path} in the edited scene`);
62
+ if (blocked)
63
+ return blocked;
64
+ return call("signal.emit", { path, signal, args: args ?? [] });
65
+ });
66
+ }
67
+ //# sourceMappingURL=signal.js.map
@@ -0,0 +1,185 @@
1
+ import { z } from "zod";
2
+ import { gate } from "../../confirm.js";
3
+ /** 3D & navigation. meshinstance/mesh/light/camera/csg/navregion/navagent mutate the EDITED scene and are undoable + ungated (the node_* model). primitive_mesh_create and the two environment_* tools author a resource on disk, so are file-writers gated by confirmation. */
4
+ export function registerSpatialTools(server, call) {
5
+ server.registerTool("meshinstance_create", {
6
+ title: "Create MeshInstance3D",
7
+ description: "Add a MeshInstance3D under a parent (undoable). Optional 'mesh_path' loads a Mesh resource (e.g. a primitive_mesh_create output) and assigns it. 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
+ name: z.string().optional().describe("Node name (default MeshInstance3D)"),
11
+ mesh_path: z.string().optional().describe("Mesh resource res:// path to assign to the instance's 'mesh'"),
12
+ },
13
+ }, async ({ parent_path, name, mesh_path }) => {
14
+ const params = { parent_path };
15
+ if (name !== undefined)
16
+ params.name = name;
17
+ if (mesh_path !== undefined)
18
+ params.mesh_path = mesh_path;
19
+ return call("meshinstance.create", params);
20
+ });
21
+ server.registerTool("mesh_set_surface_material", {
22
+ title: "Set mesh surface material",
23
+ description: "Assign a Material (res:// path) to a MeshInstance3D (undoable). Default surface -1 sets 'material_override' (whole instance); a surface index >= 0 sets that surface's override material (must be within the mesh's surface count). Refuses a non-MeshInstance3D node or a non-Material resource.",
24
+ inputSchema: {
25
+ path: z.string().describe("MeshInstance3D node path relative to the scene root"),
26
+ material_path: z.string().describe("Material resource res:// path (StandardMaterial3D / ShaderMaterial / …)"),
27
+ surface: z.number().int().optional().describe("Surface index, or -1 (default) for material_override"),
28
+ },
29
+ }, async ({ path, material_path, surface }) => {
30
+ const params = { path, material_path };
31
+ if (surface !== undefined)
32
+ params.surface = surface;
33
+ return call("mesh.set_surface_material", params);
34
+ });
35
+ server.registerTool("primitive_mesh_create", {
36
+ title: "Create primitive mesh",
37
+ description: "Create a PrimitiveMesh resource (box/sphere/cylinder/plane/capsule/prism/torus/quad) and save it to a res:// path. DESTRUCTIVE (writes a file) — gated by confirmation. Assign it with meshinstance_create's 'mesh_path'.",
38
+ inputSchema: {
39
+ to_path: z.string().describe("Destination res:// path, e.g. res://meshes/box.tres"),
40
+ shape: z.string().optional().describe("box | sphere | cylinder | plane | capsule | prism | torus | quad (default box)"),
41
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
42
+ },
43
+ }, async ({ to_path, shape, confirm }) => {
44
+ const blocked = await gate(server, confirm, `Create ${shape ?? "box"} PrimitiveMesh at ${to_path}`);
45
+ if (blocked)
46
+ return blocked;
47
+ const params = { to_path };
48
+ if (shape !== undefined)
49
+ params.shape = shape;
50
+ return call("primitive_mesh.create", params);
51
+ });
52
+ server.registerTool("light_create", {
53
+ title: "Create Light3D",
54
+ description: "Add a 3D light under a parent (undoable). 'kind' selects DirectionalLight3D (dir), OmniLight3D (omni), or SpotLight3D (spot). Returns the new node's path.",
55
+ inputSchema: {
56
+ parent_path: z.string().describe("Parent node path relative to the scene root; \".\" for the root"),
57
+ kind: z.enum(["dir", "directional", "omni", "spot"]).optional().describe("Light kind: dir | omni | spot (default omni)"),
58
+ name: z.string().optional().describe("Node name (defaults to the class name)"),
59
+ },
60
+ }, async ({ parent_path, kind, name }) => {
61
+ const params = { parent_path };
62
+ if (kind !== undefined)
63
+ params.kind = kind;
64
+ if (name !== undefined)
65
+ params.name = name;
66
+ return call("light.create", params);
67
+ });
68
+ server.registerTool("camera_create", {
69
+ title: "Create Camera3D",
70
+ description: "Add a Camera3D under a parent (undoable). Optional 'current' makes it the active camera. Returns the new node's path.",
71
+ inputSchema: {
72
+ parent_path: z.string().describe("Parent node path relative to the scene root; \".\" for the root"),
73
+ name: z.string().optional().describe("Node name (default Camera3D)"),
74
+ current: z.boolean().optional().describe("Make this the current/active camera (default false)"),
75
+ },
76
+ }, async ({ parent_path, name, current }) => {
77
+ const params = { parent_path };
78
+ if (name !== undefined)
79
+ params.name = name;
80
+ if (current !== undefined)
81
+ params.current = current;
82
+ return call("camera.create", params);
83
+ });
84
+ server.registerTool("csg_create", {
85
+ title: "Create CSG node",
86
+ description: "Add a CSG node under a parent (undoable). 'shape' selects CSGBox3D (box), CSGSphere3D (sphere), CSGCylinder3D (cylinder), CSGTorus3D (torus), CSGPolygon3D (polygon), CSGMesh3D (mesh), or CSGCombiner3D (combiner). Returns the new node's path.",
87
+ inputSchema: {
88
+ parent_path: z.string().describe("Parent node path relative to the scene root; \".\" for the root"),
89
+ shape: z.string().optional().describe("box | sphere | cylinder | torus | polygon | mesh | combiner (default box)"),
90
+ name: z.string().optional().describe("Node name (defaults to the class name)"),
91
+ },
92
+ }, async ({ parent_path, shape, name }) => {
93
+ const params = { parent_path };
94
+ if (shape !== undefined)
95
+ params.shape = shape;
96
+ if (name !== undefined)
97
+ params.name = name;
98
+ return call("csg.create", params);
99
+ });
100
+ server.registerTool("navregion_create", {
101
+ title: "Create NavigationRegion3D",
102
+ description: "Add a NavigationRegion3D under a parent (undoable). By default seeds a fresh empty NavigationMesh (set with_navmesh=false to skip). Returns the new node's path and whether a navmesh was attached.",
103
+ inputSchema: {
104
+ parent_path: z.string().describe("Parent node path relative to the scene root; \".\" for the root"),
105
+ name: z.string().optional().describe("Node name (default NavigationRegion3D)"),
106
+ with_navmesh: z.boolean().optional().describe("Seed a fresh empty NavigationMesh resource (default true)"),
107
+ },
108
+ }, async ({ parent_path, name, with_navmesh }) => {
109
+ const params = { parent_path };
110
+ if (name !== undefined)
111
+ params.name = name;
112
+ if (with_navmesh !== undefined)
113
+ params.with_navmesh = with_navmesh;
114
+ return call("navregion.create", params);
115
+ });
116
+ server.registerTool("navagent_configure", {
117
+ title: "Add & configure NavigationAgent3D",
118
+ description: "Add a NavigationAgent3D under a parent (undoable) and set any of its steering/avoidance properties. Returns the new node's path and the resulting config (radius, height, max_speed, path/target desired distances, avoidance_enabled).",
119
+ inputSchema: {
120
+ parent_path: z.string().describe("Parent node path relative to the scene root; \".\" for the root"),
121
+ name: z.string().optional().describe("Node name (default NavigationAgent3D)"),
122
+ radius: z.number().optional().describe("Agent radius"),
123
+ height: z.number().optional().describe("Agent height"),
124
+ max_speed: z.number().optional().describe("Maximum movement speed"),
125
+ path_desired_distance: z.number().optional().describe("Distance to a path point before advancing"),
126
+ target_desired_distance: z.number().optional().describe("Distance to the target that counts as arrived"),
127
+ avoidance_enabled: z.boolean().optional().describe("Enable RVO avoidance"),
128
+ },
129
+ }, async ({ parent_path, name, radius, height, max_speed, path_desired_distance, target_desired_distance, avoidance_enabled }) => {
130
+ const params = { parent_path };
131
+ if (name !== undefined)
132
+ params.name = name;
133
+ if (radius !== undefined)
134
+ params.radius = radius;
135
+ if (height !== undefined)
136
+ params.height = height;
137
+ if (max_speed !== undefined)
138
+ params.max_speed = max_speed;
139
+ if (path_desired_distance !== undefined)
140
+ params.path_desired_distance = path_desired_distance;
141
+ if (target_desired_distance !== undefined)
142
+ params.target_desired_distance = target_desired_distance;
143
+ if (avoidance_enabled !== undefined)
144
+ params.avoidance_enabled = avoidance_enabled;
145
+ return call("navagent.configure", params);
146
+ });
147
+ server.registerTool("environment_create", {
148
+ title: "Create Environment",
149
+ description: "Create an Environment resource and save it to a res:// path. 'background' sets the mode (clear_color/color/sky/canvas, default clear_color); optional 'ambient_color' ([r,g,b(,a)], 0..1) sets the ambient light color. DESTRUCTIVE (writes a file) — gated by confirmation.",
150
+ inputSchema: {
151
+ to_path: z.string().describe("Destination res:// path, e.g. res://world.tres"),
152
+ background: z.string().optional().describe("clear_color | color | sky | canvas (default clear_color)"),
153
+ ambient_color: z.array(z.number()).optional().describe("Ambient light color [r,g,b] or [r,g,b,a], 0..1"),
154
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
155
+ },
156
+ }, async ({ to_path, background, ambient_color, confirm }) => {
157
+ const blocked = await gate(server, confirm, `Create Environment at ${to_path}`);
158
+ if (blocked)
159
+ return blocked;
160
+ const params = { to_path };
161
+ if (background !== undefined)
162
+ params.background = background;
163
+ if (ambient_color !== undefined)
164
+ params.ambient_color = ambient_color;
165
+ return call("environment.create", params);
166
+ });
167
+ server.registerTool("environment_set_sky", {
168
+ title: "Set Environment sky",
169
+ description: "Attach a Sky to an existing Environment resource file (setting a ProceduralSkyMaterial, PhysicalSkyMaterial, or PanoramaSkyMaterial) and switch its background to SKY, then re-save. DESTRUCTIVE (writes a file) — gated by confirmation. Refuses a path that is not an Environment.",
170
+ inputSchema: {
171
+ path: z.string().describe("Environment res:// path"),
172
+ sky_material: z.enum(["procedural", "physical", "panorama"]).optional().describe("Sky material kind (default procedural)"),
173
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
174
+ },
175
+ }, async ({ path, sky_material, confirm }) => {
176
+ const blocked = await gate(server, confirm, `Set sky on Environment ${path}`);
177
+ if (blocked)
178
+ return blocked;
179
+ const params = { path };
180
+ if (sky_material !== undefined)
181
+ params.sky_material = sky_material;
182
+ return call("environment.set_sky", params);
183
+ });
184
+ }
185
+ //# sourceMappingURL=spatial.js.map
@@ -0,0 +1,160 @@
1
+ import { z } from "zod";
2
+ import { gate } from "../../confirm.js";
3
+ /** TileSet authoring (disk-backed, gated) + TileMapLayer cell painting. */
4
+ export function registerTileTools(server, call) {
5
+ server.registerTool("tileset_create", {
6
+ title: "Create TileSet",
7
+ description: "Instantiate a TileSet resource and save it as a new .tres file. DESTRUCTIVE (writes a file) — gated by confirmation. tile_size is the base grid cell size in pixels (default 16×16).",
8
+ inputSchema: {
9
+ to_path: z.string().describe("Destination res:// path, e.g. res://tiles/world.tres"),
10
+ tile_size: z.array(z.number().int()).optional().describe("Base tile grid size [x, y] in pixels (default [16, 16])"),
11
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
12
+ },
13
+ }, async ({ to_path, tile_size, confirm }) => {
14
+ const blocked = await gate(server, confirm, `Create TileSet resource at ${to_path}`);
15
+ if (blocked)
16
+ return blocked;
17
+ return call("tileset.create", tile_size !== undefined ? { to_path, tile_size } : { to_path });
18
+ });
19
+ server.registerTool("tileset_add_source", {
20
+ title: "Add TileSet atlas source",
21
+ description: "Add a TileSetAtlasSource (backed by a Texture2D) to a TileSet .tres and re-save. DESTRUCTIVE (writes a file) — gated by confirmation. texture_region_size defaults to the TileSet's tile_size; source_id -1 auto-assigns.",
22
+ inputSchema: {
23
+ tileset_path: z.string().describe("TileSet res:// .tres path"),
24
+ texture_path: z.string().describe("Texture2D res:// path used as the atlas image"),
25
+ texture_region_size: z.array(z.number().int()).optional().describe("Atlas cell size [x, y] in pixels (default = tile_size)"),
26
+ source_id: z.number().int().optional().describe("Explicit source id, or -1 to auto-assign (default -1)"),
27
+ margins: z.array(z.number().int()).optional().describe("Atlas margins [x, y] in pixels"),
28
+ separation: z.array(z.number().int()).optional().describe("Atlas separation [x, y] in pixels"),
29
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
30
+ },
31
+ }, async ({ tileset_path, texture_path, texture_region_size, source_id, margins, separation, confirm }) => {
32
+ const blocked = await gate(server, confirm, `Add atlas source (${texture_path}) to TileSet ${tileset_path}`);
33
+ if (blocked)
34
+ return blocked;
35
+ const params = { tileset_path, texture_path };
36
+ if (texture_region_size !== undefined)
37
+ params.texture_region_size = texture_region_size;
38
+ if (source_id !== undefined)
39
+ params.source_id = source_id;
40
+ if (margins !== undefined)
41
+ params.margins = margins;
42
+ if (separation !== undefined)
43
+ params.separation = separation;
44
+ return call("tileset.add_source", params);
45
+ });
46
+ server.registerTool("tileset_add_tile", {
47
+ title: "Add TileSet tile",
48
+ description: "Create a tile at atlas_coords in an atlas source of a TileSet .tres and re-save. DESTRUCTIVE (writes a file) — gated by confirmation. size is measured in atlas cells (default [1, 1]).",
49
+ inputSchema: {
50
+ tileset_path: z.string().describe("TileSet res:// .tres path"),
51
+ source_id: z.number().int().describe("Atlas source id within the TileSet"),
52
+ atlas_coords: z.array(z.number().int()).describe("Tile atlas coordinates [x, y] (in cells)"),
53
+ size: z.array(z.number().int()).optional().describe("Tile size in atlas cells [x, y] (default [1, 1])"),
54
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
55
+ },
56
+ }, async ({ tileset_path, source_id, atlas_coords, size, confirm }) => {
57
+ const blocked = await gate(server, confirm, `Add tile ${JSON.stringify(atlas_coords)} to source ${source_id} in ${tileset_path}`);
58
+ if (blocked)
59
+ return blocked;
60
+ const params = { tileset_path, source_id, atlas_coords };
61
+ if (size !== undefined)
62
+ params.size = size;
63
+ return call("tileset.add_tile", params);
64
+ });
65
+ server.registerTool("tileset_set_tile_collision", {
66
+ title: "Set tile collision polygon",
67
+ description: "Add a collision polygon to a tile on a TileSet physics layer and re-save. DESTRUCTIVE (writes a file) — gated by confirmation. Physics layers are created as needed. polygon is an array of [x, y] points (>= 3), tile-local pixels.",
68
+ inputSchema: {
69
+ tileset_path: z.string().describe("TileSet res:// .tres path"),
70
+ source_id: z.number().int().describe("Atlas source id within the TileSet"),
71
+ atlas_coords: z.array(z.number().int()).describe("Tile atlas coordinates [x, y] (in cells)"),
72
+ polygon: z.array(z.array(z.number())).describe("Collision polygon points [[x, y], ...] (>= 3), tile-local pixels"),
73
+ physics_layer: z.number().int().optional().describe("TileSet physics layer index (default 0; created if missing)"),
74
+ one_way: z.boolean().optional().describe("Mark the polygon as one-way collision"),
75
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
76
+ },
77
+ }, async ({ tileset_path, source_id, atlas_coords, polygon, physics_layer, one_way, confirm }) => {
78
+ const blocked = await gate(server, confirm, `Set collision on tile ${JSON.stringify(atlas_coords)} in ${tileset_path}`);
79
+ if (blocked)
80
+ return blocked;
81
+ const params = { tileset_path, source_id, atlas_coords, polygon };
82
+ if (physics_layer !== undefined)
83
+ params.physics_layer = physics_layer;
84
+ if (one_way !== undefined)
85
+ params.one_way = one_way;
86
+ return call("tileset.set_tile_collision", params);
87
+ });
88
+ server.registerTool("tilemaplayer_create", {
89
+ title: "Create TileMapLayer",
90
+ description: "Add a TileMapLayer node under a parent in the edited scene (undoable), optionally binding a TileSet .tres so cells can be painted. In-scene and undoable — not gated.",
91
+ inputSchema: {
92
+ parent_path: z.string().describe("Parent node path relative to the scene root; \".\" for the root"),
93
+ name: z.string().optional().describe("Node name (default \"TileMapLayer\")"),
94
+ tileset_path: z.string().optional().describe("TileSet res:// .tres path to bind as tile_set (e.g. from tileset_create)"),
95
+ },
96
+ }, async ({ parent_path, name, tileset_path }) => {
97
+ const params = { parent_path };
98
+ if (name !== undefined)
99
+ params.name = name;
100
+ if (tileset_path !== undefined)
101
+ params.tileset_path = tileset_path;
102
+ return call("tilemaplayer.create", params);
103
+ });
104
+ server.registerTool("tilemap_set_cell", {
105
+ title: "Set TileMapLayer cell",
106
+ description: "Paint a single cell on a TileMapLayer (undoable). source_id -1 erases the cell (default). atlas_coords defaults to [0, 0]; alternative defaults to 0.",
107
+ inputSchema: {
108
+ path: z.string().describe("TileMapLayer node path relative to the scene root"),
109
+ coords: z.array(z.number().int()).describe("Cell coordinates [x, y] (in cells)"),
110
+ source_id: z.number().int().optional().describe("Atlas source id from the bound TileSet, or -1 to erase (default -1)"),
111
+ atlas_coords: z.array(z.number().int()).optional().describe("Tile atlas coordinates [x, y] within the source (default [0, 0])"),
112
+ alternative: z.number().int().optional().describe("Alternative tile id (default 0)"),
113
+ },
114
+ }, async ({ path, coords, source_id, atlas_coords, alternative }) => {
115
+ const params = { path, coords };
116
+ if (source_id !== undefined)
117
+ params.source_id = source_id;
118
+ if (atlas_coords !== undefined)
119
+ params.atlas_coords = atlas_coords;
120
+ if (alternative !== undefined)
121
+ params.alternative = alternative;
122
+ return call("tilemap.set_cell", params);
123
+ });
124
+ server.registerTool("tilemap_set_cells_rect", {
125
+ title: "Fill TileMapLayer cells (rect)",
126
+ description: "Paint every cell in a rectangular region of a TileMapLayer with one tile (undoable, one action). source_id -1 clears the region. Capped at 65536 cells; split larger fills across calls.",
127
+ inputSchema: {
128
+ path: z.string().describe("TileMapLayer node path relative to the scene root"),
129
+ rect: z.array(z.number().int()).describe("Region [x, y, width, height] in cells (width/height > 0)"),
130
+ source_id: z.number().int().optional().describe("Atlas source id from the bound TileSet, or -1 to clear (default -1)"),
131
+ atlas_coords: z.array(z.number().int()).optional().describe("Tile atlas coordinates [x, y] within the source (default [0, 0])"),
132
+ alternative: z.number().int().optional().describe("Alternative tile id (default 0)"),
133
+ },
134
+ }, async ({ path, rect, source_id, atlas_coords, alternative }) => {
135
+ const params = { path, rect };
136
+ if (source_id !== undefined)
137
+ params.source_id = source_id;
138
+ if (atlas_coords !== undefined)
139
+ params.atlas_coords = atlas_coords;
140
+ if (alternative !== undefined)
141
+ params.alternative = alternative;
142
+ return call("tilemap.set_cells_rect", params);
143
+ });
144
+ server.registerTool("tilemap_get_cell", {
145
+ title: "Get TileMapLayer cell",
146
+ description: "Read one cell of a TileMapLayer. An empty cell reads back as source_id -1, atlas_coords [-1, -1], alternative 0 (empty = true).",
147
+ inputSchema: {
148
+ path: z.string().describe("TileMapLayer node path relative to the scene root"),
149
+ coords: z.array(z.number().int()).describe("Cell coordinates [x, y] (in cells)"),
150
+ },
151
+ }, async ({ path, coords }) => call("tilemap.get_cell", { path, coords }));
152
+ server.registerTool("tilemap_clear", {
153
+ title: "Clear TileMapLayer",
154
+ description: "Remove every painted cell from a TileMapLayer (undoable — prior cells are restored on undo). Returns the number of cells cleared.",
155
+ inputSchema: {
156
+ path: z.string().describe("TileMapLayer node path relative to the scene root"),
157
+ },
158
+ }, async ({ path }) => call("tilemap.clear", { path }));
159
+ }
160
+ //# sourceMappingURL=tiles.js.map