breakpoint-mcp 1.0.0 → 1.1.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,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
@@ -0,0 +1,175 @@
1
+ import { z } from "zod";
2
+ import { gate } from "../../confirm.js";
3
+ /** UI / Control / theming. control_* + container_add_child mutate the EDITED scene and are undoable via EditorUndoRedoManager and ungated (the node_* model). theme_* author a Theme on disk via ResourceSaver, so — like resource_* — they are file-writers gated by confirmation. */
4
+ export function registerUiTools(server, call) {
5
+ server.registerTool("control_create", {
6
+ title: "Create control",
7
+ description: "Instance a Control-derived UI node (Button, Label, Panel, VBoxContainer, TextureRect, …) under a parent (undoable). Refuses non-Control classes. Optional 'text' is applied only to controls that expose a 'text' property. 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("Control subclass to instance, e.g. Button, Label, Panel, VBoxContainer, TextureRect"),
11
+ name: z.string().optional().describe("Node name (defaults to the class name)"),
12
+ text: z.string().optional().describe("Initial text; applied only if the control has a 'text' property"),
13
+ },
14
+ }, async ({ parent_path, type, name, text }) => {
15
+ const params = { parent_path, type };
16
+ if (name !== undefined)
17
+ params.name = name;
18
+ if (text !== undefined)
19
+ params.text = text;
20
+ return call("control.create", params);
21
+ });
22
+ server.registerTool("container_add_child", {
23
+ title: "Add control to container",
24
+ description: "Instance a Control-derived child under a Container node (VBoxContainer, GridContainer, MarginContainer, …) so it participates in the container's layout (undoable). Refuses a non-Container parent or a non-Control child. Returns the new node's path.",
25
+ inputSchema: {
26
+ container_path: z.string().describe("Container node path relative to the scene root"),
27
+ type: z.string().describe("Control subclass to instance as the container's child, e.g. Button, Label"),
28
+ name: z.string().optional().describe("Node name (defaults to the class name)"),
29
+ },
30
+ }, async ({ container_path, type, name }) => call("container.add_child", name !== undefined ? { container_path, type, name } : { container_path, type }));
31
+ server.registerTool("control_set_anchors", {
32
+ title: "Set control anchors",
33
+ description: "Set one or more of a Control's anchors (left/top/right/bottom, each 0..1) directly (undoable). Only the sides you pass are changed; offsets are left as-is. Provide at least one side.",
34
+ inputSchema: {
35
+ path: z.string().describe("Control node path relative to the scene root"),
36
+ left: z.number().optional().describe("anchor_left (0..1)"),
37
+ top: z.number().optional().describe("anchor_top (0..1)"),
38
+ right: z.number().optional().describe("anchor_right (0..1)"),
39
+ bottom: z.number().optional().describe("anchor_bottom (0..1)"),
40
+ },
41
+ }, async ({ path, left, top, right, bottom }) => {
42
+ const params = { path };
43
+ if (left !== undefined)
44
+ params.left = left;
45
+ if (top !== undefined)
46
+ params.top = top;
47
+ if (right !== undefined)
48
+ params.right = right;
49
+ if (bottom !== undefined)
50
+ params.bottom = bottom;
51
+ return call("control.set_anchors", params);
52
+ });
53
+ server.registerTool("control_set_layout_preset", {
54
+ title: "Apply control layout preset",
55
+ description: "Apply a LayoutPreset to a Control via set_anchors_and_offsets_preset (undoable). 'preset' is a name (full_rect, center, top_left, center_top, left_wide, hcenter_wide, …) or the integer enum value. Optional resize_mode (0=min_size,1=keep_width,2=keep_height,3=keep_size) and margin.",
56
+ inputSchema: {
57
+ path: z.string().describe("Control node path relative to the scene root"),
58
+ preset: z.union([z.string(), z.number()]).describe("LayoutPreset name or integer (0..15)"),
59
+ resize_mode: z.number().int().optional().describe("LayoutPresetMode 0..3 (default 0 = min size)"),
60
+ margin: z.number().int().optional().describe("Margin in pixels applied by the preset (default 0)"),
61
+ },
62
+ }, async ({ path, preset, resize_mode, margin }) => {
63
+ const params = { path, preset };
64
+ if (resize_mode !== undefined)
65
+ params.resize_mode = resize_mode;
66
+ if (margin !== undefined)
67
+ params.margin = margin;
68
+ return call("control.set_layout_preset", params);
69
+ });
70
+ server.registerTool("control_set_size_flags", {
71
+ title: "Set control size flags",
72
+ description: "Set a Control's container size flags and/or stretch ratio (undoable). 'horizontal'/'vertical' are SizeFlags bitmasks (1=fill, 2=expand, 3=expand_fill, 4=shrink_center, 8=shrink_end). Provide at least one of horizontal/vertical/stretch_ratio.",
73
+ inputSchema: {
74
+ path: z.string().describe("Control node path relative to the scene root"),
75
+ horizontal: z.number().int().optional().describe("size_flags_horizontal bitmask"),
76
+ vertical: z.number().int().optional().describe("size_flags_vertical bitmask"),
77
+ stretch_ratio: z.number().optional().describe("size_flags_stretch_ratio (default 1.0)"),
78
+ },
79
+ }, async ({ path, horizontal, vertical, stretch_ratio }) => {
80
+ const params = { path };
81
+ if (horizontal !== undefined)
82
+ params.horizontal = horizontal;
83
+ if (vertical !== undefined)
84
+ params.vertical = vertical;
85
+ if (stretch_ratio !== undefined)
86
+ params.stretch_ratio = stretch_ratio;
87
+ return call("control.set_size_flags", params);
88
+ });
89
+ server.registerTool("control_set_theme", {
90
+ title: "Set control theme",
91
+ description: "Assign a Theme resource (res:// path) to a Control's 'theme' property so it and its children inherit it (undoable). Pass an empty theme_path to clear the override.",
92
+ inputSchema: {
93
+ path: z.string().describe("Control node path relative to the scene root"),
94
+ theme_path: z.string().describe("Theme res:// path, or \"\" to clear the theme override"),
95
+ },
96
+ }, async ({ path, theme_path }) => call("control.set_theme", { path, theme_path }));
97
+ server.registerTool("theme_create", {
98
+ title: "Create theme",
99
+ description: "Create a new empty Theme resource and save it to a res:// path. DESTRUCTIVE (writes a file) — gated by confirmation.",
100
+ inputSchema: {
101
+ to_path: z.string().describe("Destination res:// path, e.g. res://ui/main.theme or res://ui/main.tres"),
102
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
103
+ },
104
+ }, async ({ to_path, confirm }) => {
105
+ const blocked = await gate(server, confirm, `Create Theme resource at ${to_path}`);
106
+ if (blocked)
107
+ return blocked;
108
+ return call("theme.create", { to_path });
109
+ });
110
+ server.registerTool("theme_set_color", {
111
+ title: "Set theme color",
112
+ description: "Set a color entry on a Theme resource file and save it (e.g. font_color for the Button type). DESTRUCTIVE (writes a file) — gated by confirmation. 'color' is [r,g,b] or [r,g,b,a] with components 0..1.",
113
+ inputSchema: {
114
+ path: z.string().describe("Theme res:// path"),
115
+ name: z.string().describe("Theme item name, e.g. font_color"),
116
+ theme_type: z.string().describe("Theme type the item belongs to, e.g. Button, Label"),
117
+ color: z.array(z.number()).describe("[r,g,b] or [r,g,b,a], components 0..1"),
118
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
119
+ },
120
+ }, async ({ path, name, theme_type, color, confirm }) => {
121
+ const blocked = await gate(server, confirm, `Set theme color ${theme_type}/${name} in ${path}`);
122
+ if (blocked)
123
+ return blocked;
124
+ return call("theme.set_color", { path, name, theme_type, color });
125
+ });
126
+ server.registerTool("theme_set_font", {
127
+ title: "Set theme font",
128
+ description: "Set a font entry on a Theme resource file (loading a Font from a res:// path) and save it. DESTRUCTIVE (writes a file) — gated by confirmation.",
129
+ inputSchema: {
130
+ path: z.string().describe("Theme res:// path"),
131
+ name: z.string().describe("Theme item name, e.g. font"),
132
+ theme_type: z.string().describe("Theme type the item belongs to, e.g. Button, Label"),
133
+ font_path: z.string().describe("Font resource res:// path (FontFile / SystemFont / …)"),
134
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
135
+ },
136
+ }, async ({ path, name, theme_type, font_path, confirm }) => {
137
+ const blocked = await gate(server, confirm, `Set theme font ${theme_type}/${name} in ${path}`);
138
+ if (blocked)
139
+ return blocked;
140
+ return call("theme.set_font", { path, name, theme_type, font_path });
141
+ });
142
+ server.registerTool("theme_set_stylebox", {
143
+ title: "Set theme stylebox",
144
+ description: "Set a StyleBox entry on a Theme resource file (loading a StyleBox from a res:// path) and save it. DESTRUCTIVE (writes a file) — gated by confirmation.",
145
+ inputSchema: {
146
+ path: z.string().describe("Theme res:// path"),
147
+ name: z.string().describe("Theme item name, e.g. normal, pressed, panel"),
148
+ theme_type: z.string().describe("Theme type the item belongs to, e.g. Button, PanelContainer"),
149
+ stylebox_path: z.string().describe("StyleBox resource res:// path (StyleBoxFlat / StyleBoxTexture / …)"),
150
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
151
+ },
152
+ }, async ({ path, name, theme_type, stylebox_path, confirm }) => {
153
+ const blocked = await gate(server, confirm, `Set theme stylebox ${theme_type}/${name} in ${path}`);
154
+ if (blocked)
155
+ return blocked;
156
+ return call("theme.set_stylebox", { path, name, theme_type, stylebox_path });
157
+ });
158
+ server.registerTool("theme_set_constant", {
159
+ title: "Set theme constant",
160
+ description: "Set an integer constant entry on a Theme resource file and save it (e.g. h_separation for a BoxContainer). DESTRUCTIVE (writes a file) — gated by confirmation.",
161
+ inputSchema: {
162
+ path: z.string().describe("Theme res:// path"),
163
+ name: z.string().describe("Theme item name, e.g. h_separation, margin_left"),
164
+ theme_type: z.string().describe("Theme type the item belongs to, e.g. HBoxContainer, MarginContainer"),
165
+ value: z.number().int().describe("Integer constant value"),
166
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
167
+ },
168
+ }, async ({ path, name, theme_type, value, confirm }) => {
169
+ const blocked = await gate(server, confirm, `Set theme constant ${theme_type}/${name} in ${path}`);
170
+ if (blocked)
171
+ return blocked;
172
+ return call("theme.set_constant", { path, name, theme_type, value });
173
+ });
174
+ }
175
+ //# sourceMappingURL=ui.js.map