godot-mcp-runtime 2.3.0 → 3.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.
- package/README.md +46 -132
- package/dist/dispatch.d.ts +1 -11
- package/dist/dispatch.d.ts.map +1 -1
- package/dist/dispatch.js +32 -33
- package/dist/dispatch.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -10
- package/dist/index.js.map +1 -1
- package/dist/scripts/godot_operations.gd +268 -382
- package/dist/scripts/mcp_bridge.gd +206 -44
- package/dist/tools/autoload-tools.d.ts +51 -0
- package/dist/tools/autoload-tools.d.ts.map +1 -0
- package/dist/tools/autoload-tools.js +191 -0
- package/dist/tools/autoload-tools.js.map +1 -0
- package/dist/tools/node-tools.d.ts +9 -78
- package/dist/tools/node-tools.d.ts.map +1 -1
- package/dist/tools/node-tools.js +188 -312
- package/dist/tools/node-tools.js.map +1 -1
- package/dist/tools/project-tools.d.ts +0 -168
- package/dist/tools/project-tools.d.ts.map +1 -1
- package/dist/tools/project-tools.js +191 -1240
- package/dist/tools/project-tools.js.map +1 -1
- package/dist/tools/runtime-tools.d.ts +108 -0
- package/dist/tools/runtime-tools.d.ts.map +1 -0
- package/dist/tools/runtime-tools.js +994 -0
- package/dist/tools/runtime-tools.js.map +1 -0
- package/dist/tools/scene-tools.d.ts +6 -48
- package/dist/tools/scene-tools.d.ts.map +1 -1
- package/dist/tools/scene-tools.js +76 -212
- package/dist/tools/scene-tools.js.map +1 -1
- package/dist/tools/validate-tools.d.ts.map +1 -1
- package/dist/tools/validate-tools.js +115 -51
- package/dist/tools/validate-tools.js.map +1 -1
- package/dist/utils/autoload-ini.d.ts +38 -0
- package/dist/utils/autoload-ini.d.ts.map +1 -0
- package/dist/utils/autoload-ini.js +124 -0
- package/dist/utils/autoload-ini.js.map +1 -0
- package/dist/utils/bridge-manager.d.ts +46 -0
- package/dist/utils/bridge-manager.d.ts.map +1 -0
- package/dist/utils/bridge-manager.js +186 -0
- package/dist/utils/bridge-manager.js.map +1 -0
- package/dist/utils/bridge-protocol.d.ts +37 -0
- package/dist/utils/bridge-protocol.d.ts.map +1 -0
- package/dist/utils/bridge-protocol.js +78 -0
- package/dist/utils/bridge-protocol.js.map +1 -0
- package/dist/utils/godot-runner.d.ts +102 -16
- package/dist/utils/godot-runner.d.ts.map +1 -1
- package/dist/utils/godot-runner.js +497 -284
- package/dist/utils/godot-runner.js.map +1 -1
- package/dist/utils/handler-helpers.d.ts +34 -0
- package/dist/utils/handler-helpers.d.ts.map +1 -0
- package/dist/utils/handler-helpers.js +55 -0
- package/dist/utils/handler-helpers.js.map +1 -0
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +11 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +8 -4
package/dist/tools/node-tools.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { existsSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
|
-
import { normalizeParameters,
|
|
3
|
+
import { normalizeParameters, validateSubPath, validateNodePath, createErrorResponse, validateSceneArgs, } from '../utils/godot-runner.js';
|
|
4
|
+
import { executeSceneOp } from '../utils/handler-helpers.js';
|
|
4
5
|
// --- Tool definitions ---
|
|
5
6
|
export const nodeToolDefinitions = [
|
|
6
7
|
{
|
|
7
|
-
name: '
|
|
8
|
-
description: 'Remove
|
|
8
|
+
name: 'delete_nodes',
|
|
9
|
+
description: 'Remove one or more nodes (and their descendants) from a scene file. Always-array: pass a single-element nodePaths array for one-off deletes. Saves once at the end. Cannot delete the scene root — that entry returns an error and the rest still process. Returns: results array with one entry per nodePath in input order (success or error message).',
|
|
9
10
|
annotations: { destructiveHint: true },
|
|
10
11
|
inputSchema: {
|
|
11
12
|
type: 'object',
|
|
@@ -15,36 +16,34 @@ export const nodeToolDefinitions = [
|
|
|
15
16
|
type: 'string',
|
|
16
17
|
description: 'Scene file path relative to the project (e.g. "scenes/main.tscn")',
|
|
17
18
|
},
|
|
18
|
-
|
|
19
|
-
type: '
|
|
20
|
-
|
|
19
|
+
nodePaths: {
|
|
20
|
+
type: 'array',
|
|
21
|
+
items: { type: 'string' },
|
|
22
|
+
description: 'Node paths from scene root to delete (e.g. ["root/Player/Sprite2D"])',
|
|
21
23
|
},
|
|
22
24
|
},
|
|
23
|
-
required: ['projectPath', 'scenePath', '
|
|
25
|
+
required: ['projectPath', 'scenePath', 'nodePaths'],
|
|
24
26
|
},
|
|
25
|
-
|
|
26
|
-
{
|
|
27
|
-
name: 'set_node_property',
|
|
28
|
-
description: 'Set a single property on a node in a Godot scene file. For multiple properties on the same or different nodes, use batch_set_node_properties — one Godot process instead of N. Saves automatically. Primitives pass through; Vector2 ({x,y}), Vector3 ({x,y,z}), and Color ({r,g,b,a}) auto-convert. For other complex GDScript types (Resource, NodePath, etc.), use run_script. Errors if nodePath or property does not exist. Returns { success, nodePath, property }.',
|
|
29
|
-
annotations: { idempotentHint: true },
|
|
30
|
-
inputSchema: {
|
|
27
|
+
outputSchema: {
|
|
31
28
|
type: 'object',
|
|
32
29
|
properties: {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
results: {
|
|
31
|
+
type: 'array',
|
|
32
|
+
items: {
|
|
33
|
+
type: 'object',
|
|
34
|
+
properties: {
|
|
35
|
+
nodePath: { type: 'string' },
|
|
36
|
+
success: { type: 'boolean' },
|
|
37
|
+
error: { type: 'string' },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
39
40
|
},
|
|
40
|
-
value: { description: 'New property value' },
|
|
41
41
|
},
|
|
42
|
-
required: ['projectPath', 'scenePath', 'nodePath', 'property', 'value'],
|
|
43
42
|
},
|
|
44
43
|
},
|
|
45
44
|
{
|
|
46
|
-
name: '
|
|
47
|
-
description: 'Set
|
|
45
|
+
name: 'set_node_properties',
|
|
46
|
+
description: 'Set one or more node properties on a scene in a single Godot process. Always-array: pass a single-element updates array for one-off edits. Vector2 ({x,y}), Vector3 ({x,y,z}), and Color ({r,g,b,a}) auto-convert; primitives pass through. For other complex GDScript types (Resource, NodePath, etc.), use run_script. abortOnError stops on first failure (default false continues). Saves once at the end. Returns: results[] with one entry per update in input order (success or error).',
|
|
48
47
|
annotations: { idempotentHint: true },
|
|
49
48
|
inputSchema: {
|
|
50
49
|
type: 'object',
|
|
@@ -57,8 +56,14 @@ export const nodeToolDefinitions = [
|
|
|
57
56
|
items: {
|
|
58
57
|
type: 'object',
|
|
59
58
|
properties: {
|
|
60
|
-
nodePath: {
|
|
61
|
-
|
|
59
|
+
nodePath: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
description: 'Node path from scene root (e.g. "root/Player")',
|
|
62
|
+
},
|
|
63
|
+
property: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
description: 'GDScript property name in snake_case (e.g. "position", "modulate", "collision_layer")',
|
|
66
|
+
},
|
|
62
67
|
value: { description: 'New property value' },
|
|
63
68
|
},
|
|
64
69
|
required: ['nodePath', 'property', 'value'],
|
|
@@ -71,28 +76,27 @@ export const nodeToolDefinitions = [
|
|
|
71
76
|
},
|
|
72
77
|
required: ['projectPath', 'scenePath', 'updates'],
|
|
73
78
|
},
|
|
74
|
-
|
|
75
|
-
{
|
|
76
|
-
name: 'get_node_properties',
|
|
77
|
-
description: "Read a node's current property values from a scene file. For multiple nodes, use batch_get_node_properties (one process). changedOnly:true filters out properties matching their class defaults — useful for compact diffs. Errors if nodePath does not exist. Returns { nodePath, nodeType, properties: { [key]: value } }.",
|
|
78
|
-
annotations: { readOnlyHint: true },
|
|
79
|
-
inputSchema: {
|
|
79
|
+
outputSchema: {
|
|
80
80
|
type: 'object',
|
|
81
81
|
properties: {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
results: {
|
|
83
|
+
type: 'array',
|
|
84
|
+
items: {
|
|
85
|
+
type: 'object',
|
|
86
|
+
properties: {
|
|
87
|
+
nodePath: { type: 'string' },
|
|
88
|
+
property: { type: 'string' },
|
|
89
|
+
success: { type: 'boolean' },
|
|
90
|
+
error: { type: 'string' },
|
|
91
|
+
},
|
|
92
|
+
},
|
|
88
93
|
},
|
|
89
94
|
},
|
|
90
|
-
required: ['projectPath', 'scenePath', 'nodePath'],
|
|
91
95
|
},
|
|
92
96
|
},
|
|
93
97
|
{
|
|
94
|
-
name: '
|
|
95
|
-
description: '
|
|
98
|
+
name: 'get_node_properties',
|
|
99
|
+
description: "Read one or more nodes' current property values from a scene file in a single Godot process. Always-array: pass a single-element nodes array for one-off reads. Per-node changedOnly:true filters out properties matching class defaults (useful for compact diffs). Returns: { results: [{ nodePath, nodeType, properties?, error? }] }; failed reads include error and omit properties.",
|
|
96
100
|
annotations: { readOnlyHint: true },
|
|
97
101
|
inputSchema: {
|
|
98
102
|
type: 'object',
|
|
@@ -105,7 +109,10 @@ export const nodeToolDefinitions = [
|
|
|
105
109
|
items: {
|
|
106
110
|
type: 'object',
|
|
107
111
|
properties: {
|
|
108
|
-
nodePath: {
|
|
112
|
+
nodePath: {
|
|
113
|
+
type: 'string',
|
|
114
|
+
description: 'Node path from scene root (e.g. "root/Player")',
|
|
115
|
+
},
|
|
109
116
|
changedOnly: {
|
|
110
117
|
type: 'boolean',
|
|
111
118
|
description: 'Only return properties differing from defaults (default: false)',
|
|
@@ -120,7 +127,7 @@ export const nodeToolDefinitions = [
|
|
|
120
127
|
},
|
|
121
128
|
{
|
|
122
129
|
name: 'attach_script',
|
|
123
|
-
description: 'Attach an existing GDScript file to a node in a scene. Use after writing the script with the standard file tools and validating it via the validate tool. Replaces any previously attached script. Saves automatically. Errors if scriptPath does not exist or nodePath is not found.
|
|
130
|
+
description: 'Attach an existing GDScript file to a node in a scene. Use after writing the script with the standard file tools and validating it via the validate tool. Replaces any previously attached script. Saves automatically. Returns: success with the resolved nodePath and scriptPath that were attached. Errors if scriptPath does not exist or nodePath is not found.',
|
|
124
131
|
annotations: { idempotentHint: true },
|
|
125
132
|
inputSchema: {
|
|
126
133
|
type: 'object',
|
|
@@ -135,10 +142,18 @@ export const nodeToolDefinitions = [
|
|
|
135
142
|
},
|
|
136
143
|
required: ['projectPath', 'scenePath', 'nodePath', 'scriptPath'],
|
|
137
144
|
},
|
|
145
|
+
outputSchema: {
|
|
146
|
+
type: 'object',
|
|
147
|
+
properties: {
|
|
148
|
+
success: { type: 'boolean' },
|
|
149
|
+
nodePath: { type: 'string' },
|
|
150
|
+
scriptPath: { type: 'string' },
|
|
151
|
+
},
|
|
152
|
+
},
|
|
138
153
|
},
|
|
139
154
|
{
|
|
140
155
|
name: 'get_scene_tree',
|
|
141
|
-
description: 'Get the scene hierarchy as a nested tree of { name, type, path, children }. Use maxDepth:1 for a shallow listing of direct children only; default -1 returns the full tree. parentPath scopes the result to a subtree. Errors if scene does not exist or parentPath is not found.',
|
|
156
|
+
description: 'Get the scene hierarchy as a nested tree of { name, type, path, script, children }. Use maxDepth:1 for a shallow listing of direct children only; default -1 returns the full tree. parentPath scopes the result to a subtree. Returns the nested tree as JSON text. Errors if scene does not exist or parentPath is not found.',
|
|
142
157
|
annotations: { readOnlyHint: true },
|
|
143
158
|
inputSchema: {
|
|
144
159
|
type: 'object',
|
|
@@ -159,7 +174,7 @@ export const nodeToolDefinitions = [
|
|
|
159
174
|
},
|
|
160
175
|
{
|
|
161
176
|
name: 'duplicate_node',
|
|
162
|
-
description: 'Duplicate a node and its descendants in a Godot scene. Use to clone a configured subtree without re-creating it node-by-node via add_node. newName defaults to the original name + "2"; targetParentPath defaults to the original parent. Saves automatically. Errors if nodePath does not exist or targetParentPath cannot accept children.
|
|
177
|
+
description: 'Duplicate a node and its descendants in a Godot scene. Use to clone a configured subtree without re-creating it node-by-node via add_node. newName defaults to the original name + "2"; targetParentPath defaults to the original parent. Saves automatically. Returns: success with originalPath and the newPath where the duplicate now lives — use newPath for follow-up edits. Errors if nodePath does not exist or targetParentPath cannot accept children.',
|
|
163
178
|
inputSchema: {
|
|
164
179
|
type: 'object',
|
|
165
180
|
properties: {
|
|
@@ -177,10 +192,18 @@ export const nodeToolDefinitions = [
|
|
|
177
192
|
},
|
|
178
193
|
required: ['projectPath', 'scenePath', 'nodePath'],
|
|
179
194
|
},
|
|
195
|
+
outputSchema: {
|
|
196
|
+
type: 'object',
|
|
197
|
+
properties: {
|
|
198
|
+
success: { type: 'boolean' },
|
|
199
|
+
originalPath: { type: 'string' },
|
|
200
|
+
newPath: { type: 'string' },
|
|
201
|
+
},
|
|
202
|
+
},
|
|
180
203
|
},
|
|
181
204
|
{
|
|
182
205
|
name: 'get_node_signals',
|
|
183
|
-
description: 'List all signals defined on a node and their current connections. Use before connect_signal/disconnect_signal to verify signal/method names.
|
|
206
|
+
description: 'List all signals defined on a node and their current connections. Use before connect_signal/disconnect_signal to verify signal/method names. The connections[].target field uses Godot absolute path format (/root/Scene/Node) — convert to scene-root-relative (root/Node) before passing to connect/disconnect_signal. Returns: nodeType and signals[], each with name and current connections (signal/target/method). Errors if node not found.',
|
|
184
207
|
annotations: { readOnlyHint: true },
|
|
185
208
|
inputSchema: {
|
|
186
209
|
type: 'object',
|
|
@@ -191,10 +214,37 @@ export const nodeToolDefinitions = [
|
|
|
191
214
|
},
|
|
192
215
|
required: ['projectPath', 'scenePath', 'nodePath'],
|
|
193
216
|
},
|
|
217
|
+
outputSchema: {
|
|
218
|
+
type: 'object',
|
|
219
|
+
properties: {
|
|
220
|
+
nodePath: { type: 'string' },
|
|
221
|
+
nodeType: { type: 'string' },
|
|
222
|
+
signals: {
|
|
223
|
+
type: 'array',
|
|
224
|
+
items: {
|
|
225
|
+
type: 'object',
|
|
226
|
+
properties: {
|
|
227
|
+
name: { type: 'string' },
|
|
228
|
+
connections: {
|
|
229
|
+
type: 'array',
|
|
230
|
+
items: {
|
|
231
|
+
type: 'object',
|
|
232
|
+
properties: {
|
|
233
|
+
signal: { type: 'string' },
|
|
234
|
+
target: { type: 'string' },
|
|
235
|
+
method: { type: 'string' },
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
},
|
|
194
244
|
},
|
|
195
245
|
{
|
|
196
246
|
name: 'connect_signal',
|
|
197
|
-
description: 'Connect a signal
|
|
247
|
+
description: 'Connect a signal on a source node to a method on a target node, persisting the connection in the .tscn. Use after get_node_signals to confirm the signal name on the source and the method name on the target. Connecting the same signal+method pair twice creates a duplicate connection — call get_node_signals first if uncertain. Saves automatically. Returns a plain-text confirmation naming the source, signal, target, and method. Errors if the signal does not exist on the source node or the method does not exist on the target node.',
|
|
198
248
|
inputSchema: {
|
|
199
249
|
type: 'object',
|
|
200
250
|
properties: {
|
|
@@ -219,7 +269,8 @@ export const nodeToolDefinitions = [
|
|
|
219
269
|
},
|
|
220
270
|
{
|
|
221
271
|
name: 'disconnect_signal',
|
|
222
|
-
description: '
|
|
272
|
+
description: 'Remove an existing signal connection between two nodes, persisting the change in the .tscn. Use get_node_signals first to confirm the connection exists; recovery requires reconnecting via connect_signal. Saves automatically. Returns a plain-text confirmation naming the disconnected signal and target. Errors if the connection does not exist.',
|
|
273
|
+
annotations: { destructiveHint: true },
|
|
223
274
|
inputSchema: {
|
|
224
275
|
type: 'object',
|
|
225
276
|
properties: {
|
|
@@ -235,71 +286,29 @@ export const nodeToolDefinitions = [
|
|
|
235
286
|
},
|
|
236
287
|
];
|
|
237
288
|
// --- Handlers ---
|
|
238
|
-
export async function
|
|
289
|
+
export async function handleDeleteNodes(runner, args) {
|
|
239
290
|
args = normalizeParameters(args);
|
|
240
291
|
const v = validateSceneArgs(args);
|
|
241
292
|
if ('isError' in v)
|
|
242
293
|
return v;
|
|
243
|
-
if (!args.
|
|
244
|
-
return createErrorResponse('
|
|
245
|
-
'Provide
|
|
294
|
+
if (!args.nodePaths || !Array.isArray(args.nodePaths) || args.nodePaths.length === 0) {
|
|
295
|
+
return createErrorResponse('nodePaths array is required', [
|
|
296
|
+
'Provide a non-empty array of node paths (e.g. ["root/Player"])',
|
|
246
297
|
]);
|
|
247
298
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return createErrorResponse(`Failed to delete node: ${extractGdError(stderr)}`, [
|
|
253
|
-
'Check if the node path is correct',
|
|
299
|
+
for (const p of args.nodePaths) {
|
|
300
|
+
if (typeof p !== 'string' || !validateNodePath(p)) {
|
|
301
|
+
return createErrorResponse('Invalid nodePath in nodePaths', [
|
|
302
|
+
'Provide a scene-tree path without ".." (e.g. "root/Player")',
|
|
254
303
|
]);
|
|
255
304
|
}
|
|
256
|
-
return { content: [{ type: 'text', text: stdout }] };
|
|
257
|
-
}
|
|
258
|
-
catch (error) {
|
|
259
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
260
|
-
return createErrorResponse(`Failed to delete node: ${errorMessage}`, [
|
|
261
|
-
'Ensure Godot is installed correctly',
|
|
262
|
-
]);
|
|
263
305
|
}
|
|
306
|
+
const params = { scenePath: args.scenePath, nodePaths: args.nodePaths };
|
|
307
|
+
return executeSceneOp(runner, 'delete_nodes', params, v.projectPath, 'Failed to delete nodes', [
|
|
308
|
+
'Check if the node paths are correct',
|
|
309
|
+
]);
|
|
264
310
|
}
|
|
265
|
-
export async function
|
|
266
|
-
args = normalizeParameters(args);
|
|
267
|
-
const v = validateSceneArgs(args);
|
|
268
|
-
if ('isError' in v)
|
|
269
|
-
return v;
|
|
270
|
-
if (!args.nodePath || !validatePath(args.nodePath)) {
|
|
271
|
-
return createErrorResponse('Valid nodePath is required', [
|
|
272
|
-
'Provide the node path (e.g. "root/Player")',
|
|
273
|
-
]);
|
|
274
|
-
}
|
|
275
|
-
if (!args.property || args.value === undefined) {
|
|
276
|
-
return createErrorResponse('property and value are required', [
|
|
277
|
-
'Provide both property name and value',
|
|
278
|
-
]);
|
|
279
|
-
}
|
|
280
|
-
try {
|
|
281
|
-
const params = {
|
|
282
|
-
scenePath: args.scenePath,
|
|
283
|
-
nodePath: args.nodePath,
|
|
284
|
-
property: args.property,
|
|
285
|
-
value: args.value,
|
|
286
|
-
};
|
|
287
|
-
const { stdout, stderr } = await runner.executeOperation('set_node_property', params, v.projectPath);
|
|
288
|
-
if (!stdout.trim()) {
|
|
289
|
-
return createErrorResponse(`Failed to update property: ${extractGdError(stderr)}`, [
|
|
290
|
-
'Check if the property name is valid for this node type',
|
|
291
|
-
]);
|
|
292
|
-
}
|
|
293
|
-
return { content: [{ type: 'text', text: stdout }] };
|
|
294
|
-
}
|
|
295
|
-
catch (error) {
|
|
296
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
297
|
-
return createErrorResponse(`Failed to set node property: ${errorMessage}`, [
|
|
298
|
-
'Ensure Godot is installed correctly',
|
|
299
|
-
]);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
export async function handleBatchSetNodeProperties(runner, args) {
|
|
311
|
+
export async function handleSetNodeProperties(runner, args) {
|
|
303
312
|
args = normalizeParameters(args);
|
|
304
313
|
const v = validateSceneArgs(args);
|
|
305
314
|
if ('isError' in v)
|
|
@@ -309,58 +318,14 @@ export async function handleBatchSetNodeProperties(runner, args) {
|
|
|
309
318
|
'Provide an array of { nodePath, property, value }',
|
|
310
319
|
]);
|
|
311
320
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
};
|
|
319
|
-
const { stdout, stderr } = await runner.executeOperation('batch_set_node_properties', params, v.projectPath);
|
|
320
|
-
if (!stdout.trim()) {
|
|
321
|
-
return createErrorResponse(`Batch update failed: ${extractGdError(stderr)}`, [
|
|
322
|
-
'Check node paths and property names',
|
|
323
|
-
]);
|
|
324
|
-
}
|
|
325
|
-
return { content: [{ type: 'text', text: stdout }] };
|
|
326
|
-
}
|
|
327
|
-
catch (error) {
|
|
328
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
329
|
-
return createErrorResponse(`Batch set properties failed: ${errorMessage}`, [
|
|
330
|
-
'Ensure Godot is installed correctly',
|
|
331
|
-
]);
|
|
332
|
-
}
|
|
321
|
+
const params = {
|
|
322
|
+
scenePath: args.scenePath,
|
|
323
|
+
updates: args.updates,
|
|
324
|
+
abortOnError: args.abortOnError ?? false,
|
|
325
|
+
};
|
|
326
|
+
return executeSceneOp(runner, 'set_node_properties', params, v.projectPath, 'Failed to set node properties', ['Check node paths and property names']);
|
|
333
327
|
}
|
|
334
328
|
export async function handleGetNodeProperties(runner, args) {
|
|
335
|
-
args = normalizeParameters(args);
|
|
336
|
-
const v = validateSceneArgs(args);
|
|
337
|
-
if ('isError' in v)
|
|
338
|
-
return v;
|
|
339
|
-
if (!args.nodePath || !validatePath(args.nodePath)) {
|
|
340
|
-
return createErrorResponse('Valid nodePath is required', [
|
|
341
|
-
'Provide the node path (e.g. "root/Player")',
|
|
342
|
-
]);
|
|
343
|
-
}
|
|
344
|
-
try {
|
|
345
|
-
const params = { scenePath: args.scenePath, nodePath: args.nodePath };
|
|
346
|
-
if (args.changedOnly)
|
|
347
|
-
params.changedOnly = args.changedOnly;
|
|
348
|
-
const { stdout, stderr } = await runner.executeOperation('get_node_properties', params, v.projectPath);
|
|
349
|
-
if (!stdout.trim()) {
|
|
350
|
-
return createErrorResponse(`Failed to get properties: ${extractGdError(stderr)}`, [
|
|
351
|
-
'Check if the node path is correct',
|
|
352
|
-
]);
|
|
353
|
-
}
|
|
354
|
-
return { content: [{ type: 'text', text: stdout }] };
|
|
355
|
-
}
|
|
356
|
-
catch (error) {
|
|
357
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
358
|
-
return createErrorResponse(`Failed to get node properties: ${errorMessage}`, [
|
|
359
|
-
'Ensure Godot is installed correctly',
|
|
360
|
-
]);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
export async function handleBatchGetNodeProperties(runner, args) {
|
|
364
329
|
args = normalizeParameters(args);
|
|
365
330
|
const v = validateSceneArgs(args);
|
|
366
331
|
if ('isError' in v)
|
|
@@ -370,37 +335,22 @@ export async function handleBatchGetNodeProperties(runner, args) {
|
|
|
370
335
|
'Provide an array of { nodePath, changedOnly? }',
|
|
371
336
|
]);
|
|
372
337
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
const params = { scenePath: args.scenePath, nodes: snakeNodes };
|
|
376
|
-
const { stdout, stderr } = await runner.executeOperation('batch_get_node_properties', params, v.projectPath);
|
|
377
|
-
if (!stdout.trim()) {
|
|
378
|
-
return createErrorResponse(`Batch get_properties failed: ${extractGdError(stderr)}`, [
|
|
379
|
-
'Check node paths',
|
|
380
|
-
]);
|
|
381
|
-
}
|
|
382
|
-
return { content: [{ type: 'text', text: stdout }] };
|
|
383
|
-
}
|
|
384
|
-
catch (error) {
|
|
385
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
386
|
-
return createErrorResponse(`Batch get properties failed: ${errorMessage}`, [
|
|
387
|
-
'Ensure Godot is installed correctly',
|
|
388
|
-
]);
|
|
389
|
-
}
|
|
338
|
+
const params = { scenePath: args.scenePath, nodes: args.nodes };
|
|
339
|
+
return executeSceneOp(runner, 'get_node_properties', params, v.projectPath, 'Failed to get node properties', ['Check node paths']);
|
|
390
340
|
}
|
|
391
341
|
export async function handleAttachScript(runner, args) {
|
|
392
342
|
args = normalizeParameters(args);
|
|
393
343
|
const v = validateSceneArgs(args);
|
|
394
344
|
if ('isError' in v)
|
|
395
345
|
return v;
|
|
396
|
-
if (!args.nodePath || !
|
|
346
|
+
if (!args.nodePath || !validateNodePath(args.nodePath)) {
|
|
397
347
|
return createErrorResponse('Valid nodePath is required', [
|
|
398
348
|
'Provide the node path (e.g. "root/Player")',
|
|
399
349
|
]);
|
|
400
350
|
}
|
|
401
|
-
if (!args.scriptPath || !
|
|
351
|
+
if (!args.scriptPath || !validateSubPath(v.projectPath, args.scriptPath)) {
|
|
402
352
|
return createErrorResponse('Valid scriptPath is required', [
|
|
403
|
-
'Provide
|
|
353
|
+
'Provide a relative script path that stays inside the project directory',
|
|
404
354
|
]);
|
|
405
355
|
}
|
|
406
356
|
const scriptFullPath = join(v.projectPath, args.scriptPath);
|
|
@@ -409,123 +359,72 @@ export async function handleAttachScript(runner, args) {
|
|
|
409
359
|
'Create the script file first',
|
|
410
360
|
]);
|
|
411
361
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
return createErrorResponse(`Failed to attach script: ${extractGdError(stderr)}`, [
|
|
421
|
-
'Ensure the script is valid for this node type',
|
|
422
|
-
]);
|
|
423
|
-
}
|
|
424
|
-
return { content: [{ type: 'text', text: stdout }] };
|
|
425
|
-
}
|
|
426
|
-
catch (error) {
|
|
427
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
428
|
-
return createErrorResponse(`Failed to attach script: ${errorMessage}`, [
|
|
429
|
-
'Ensure Godot is installed correctly',
|
|
430
|
-
]);
|
|
431
|
-
}
|
|
362
|
+
const params = {
|
|
363
|
+
scenePath: args.scenePath,
|
|
364
|
+
nodePath: args.nodePath,
|
|
365
|
+
scriptPath: args.scriptPath,
|
|
366
|
+
};
|
|
367
|
+
return executeSceneOp(runner, 'attach_script', params, v.projectPath, 'Failed to attach script', [
|
|
368
|
+
'Ensure the script is valid for this node type',
|
|
369
|
+
]);
|
|
432
370
|
}
|
|
433
371
|
export async function handleGetSceneTree(runner, args) {
|
|
434
372
|
args = normalizeParameters(args);
|
|
435
373
|
const v = validateSceneArgs(args);
|
|
436
374
|
if ('isError' in v)
|
|
437
375
|
return v;
|
|
438
|
-
if (args.parentPath && !
|
|
439
|
-
return createErrorResponse('Invalid parentPath', [
|
|
440
|
-
|
|
441
|
-
try {
|
|
442
|
-
const params = { scenePath: args.scenePath };
|
|
443
|
-
if (args.parentPath)
|
|
444
|
-
params.parentPath = args.parentPath;
|
|
445
|
-
if (typeof args.maxDepth === 'number')
|
|
446
|
-
params.maxDepth = args.maxDepth;
|
|
447
|
-
const { stdout, stderr } = await runner.executeOperation('get_scene_tree', params, v.projectPath);
|
|
448
|
-
if (!stdout.trim()) {
|
|
449
|
-
return createErrorResponse(`Failed to get scene tree: ${extractGdError(stderr)}`, [
|
|
450
|
-
'Ensure the scene is valid',
|
|
451
|
-
]);
|
|
452
|
-
}
|
|
453
|
-
return { content: [{ type: 'text', text: stdout }] };
|
|
454
|
-
}
|
|
455
|
-
catch (error) {
|
|
456
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
457
|
-
return createErrorResponse(`Failed to get scene tree: ${errorMessage}`, [
|
|
458
|
-
'Ensure Godot is installed correctly',
|
|
376
|
+
if (args.parentPath && !validateNodePath(args.parentPath)) {
|
|
377
|
+
return createErrorResponse('Invalid parentPath', [
|
|
378
|
+
'Provide a scene-tree path without ".." (e.g. "root/Player")',
|
|
459
379
|
]);
|
|
460
380
|
}
|
|
381
|
+
const params = { scenePath: args.scenePath };
|
|
382
|
+
if (args.parentPath)
|
|
383
|
+
params.parentPath = args.parentPath;
|
|
384
|
+
if (typeof args.maxDepth === 'number')
|
|
385
|
+
params.maxDepth = args.maxDepth;
|
|
386
|
+
return executeSceneOp(runner, 'get_scene_tree', params, v.projectPath, 'Failed to get scene tree', ['Ensure the scene is valid']);
|
|
461
387
|
}
|
|
462
388
|
export async function handleDuplicateNode(runner, args) {
|
|
463
389
|
args = normalizeParameters(args);
|
|
464
390
|
const v = validateSceneArgs(args);
|
|
465
391
|
if ('isError' in v)
|
|
466
392
|
return v;
|
|
467
|
-
if (!args.nodePath || !
|
|
393
|
+
if (!args.nodePath || !validateNodePath(args.nodePath)) {
|
|
468
394
|
return createErrorResponse('Valid nodePath is required', [
|
|
469
395
|
'Provide the node path to duplicate',
|
|
470
396
|
]);
|
|
471
397
|
}
|
|
472
|
-
if (args.targetParentPath && !
|
|
473
|
-
return createErrorResponse('Invalid targetParentPath', [
|
|
474
|
-
|
|
475
|
-
try {
|
|
476
|
-
const params = { scenePath: args.scenePath, nodePath: args.nodePath };
|
|
477
|
-
if (args.newName)
|
|
478
|
-
params.newName = args.newName;
|
|
479
|
-
if (args.targetParentPath)
|
|
480
|
-
params.targetParentPath = args.targetParentPath;
|
|
481
|
-
const { stdout, stderr } = await runner.executeOperation('duplicate_node', params, v.projectPath);
|
|
482
|
-
if (!stdout.trim()) {
|
|
483
|
-
return createErrorResponse(`Failed to duplicate node: ${extractGdError(stderr)}`, [
|
|
484
|
-
'Check if the node path and target parent path are correct',
|
|
485
|
-
]);
|
|
486
|
-
}
|
|
487
|
-
return { content: [{ type: 'text', text: stdout }] };
|
|
488
|
-
}
|
|
489
|
-
catch (error) {
|
|
490
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
491
|
-
return createErrorResponse(`Failed to duplicate node: ${errorMessage}`, [
|
|
492
|
-
'Ensure Godot is installed correctly',
|
|
398
|
+
if (args.targetParentPath && !validateNodePath(args.targetParentPath)) {
|
|
399
|
+
return createErrorResponse('Invalid targetParentPath', [
|
|
400
|
+
'Provide a scene-tree path without ".." (e.g. "root/Player")',
|
|
493
401
|
]);
|
|
494
402
|
}
|
|
403
|
+
const params = { scenePath: args.scenePath, nodePath: args.nodePath };
|
|
404
|
+
if (args.newName)
|
|
405
|
+
params.newName = args.newName;
|
|
406
|
+
if (args.targetParentPath)
|
|
407
|
+
params.targetParentPath = args.targetParentPath;
|
|
408
|
+
return executeSceneOp(runner, 'duplicate_node', params, v.projectPath, 'Failed to duplicate node', ['Check if the node path and target parent path are correct']);
|
|
495
409
|
}
|
|
496
410
|
export async function handleGetNodeSignals(runner, args) {
|
|
497
411
|
args = normalizeParameters(args);
|
|
498
412
|
const v = validateSceneArgs(args);
|
|
499
413
|
if ('isError' in v)
|
|
500
414
|
return v;
|
|
501
|
-
if (!args.nodePath || !
|
|
415
|
+
if (!args.nodePath || !validateNodePath(args.nodePath)) {
|
|
502
416
|
return createErrorResponse('Valid nodePath is required', [
|
|
503
417
|
'Provide the node path (e.g. "root/Button")',
|
|
504
418
|
]);
|
|
505
419
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
const { stdout, stderr } = await runner.executeOperation('get_node_signals', params, v.projectPath);
|
|
509
|
-
if (!stdout.trim()) {
|
|
510
|
-
return createErrorResponse(`Failed to get signals: ${extractGdError(stderr)}`, [
|
|
511
|
-
'Check if the node path is correct',
|
|
512
|
-
]);
|
|
513
|
-
}
|
|
514
|
-
return { content: [{ type: 'text', text: stdout }] };
|
|
515
|
-
}
|
|
516
|
-
catch (error) {
|
|
517
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
518
|
-
return createErrorResponse(`Failed to get node signals: ${errorMessage}`, [
|
|
519
|
-
'Ensure Godot is installed correctly',
|
|
520
|
-
]);
|
|
521
|
-
}
|
|
420
|
+
const params = { scenePath: args.scenePath, nodePath: args.nodePath };
|
|
421
|
+
return executeSceneOp(runner, 'get_node_signals', params, v.projectPath, 'Failed to get node signals', ['Check if the node path is correct']);
|
|
522
422
|
}
|
|
523
|
-
|
|
524
|
-
args = normalizeParameters(args);
|
|
423
|
+
function validateSignalArgs(args) {
|
|
525
424
|
const v = validateSceneArgs(args);
|
|
526
425
|
if ('isError' in v)
|
|
527
426
|
return v;
|
|
528
|
-
if (!args.nodePath || !
|
|
427
|
+
if (!args.nodePath || !validateNodePath(args.nodePath)) {
|
|
529
428
|
return createErrorResponse('Valid nodePath is required', ['Provide the source node path']);
|
|
530
429
|
}
|
|
531
430
|
if (!args.signal || !args.targetNodePath || !args.method) {
|
|
@@ -533,69 +432,46 @@ export async function handleConnectSignal(runner, args) {
|
|
|
533
432
|
'Provide all three parameters',
|
|
534
433
|
]);
|
|
535
434
|
}
|
|
536
|
-
if (!
|
|
537
|
-
return createErrorResponse('Invalid targetNodePath', [
|
|
538
|
-
|
|
539
|
-
try {
|
|
540
|
-
const params = {
|
|
541
|
-
scenePath: args.scenePath,
|
|
542
|
-
nodePath: args.nodePath,
|
|
543
|
-
signal: args.signal,
|
|
544
|
-
targetNodePath: args.targetNodePath,
|
|
545
|
-
method: args.method,
|
|
546
|
-
};
|
|
547
|
-
const { stdout, stderr } = await runner.executeOperation('connect_signal', params, v.projectPath);
|
|
548
|
-
if (!stdout.trim()) {
|
|
549
|
-
return createErrorResponse(`Failed to connect signal: ${extractGdError(stderr)}`, [
|
|
550
|
-
'Ensure the signal exists on the source node and the method exists on the target node',
|
|
551
|
-
]);
|
|
552
|
-
}
|
|
553
|
-
return { content: [{ type: 'text', text: stdout }] };
|
|
554
|
-
}
|
|
555
|
-
catch (error) {
|
|
556
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
557
|
-
return createErrorResponse(`Failed to connect signal: ${errorMessage}`, [
|
|
558
|
-
'Ensure Godot is installed correctly',
|
|
435
|
+
if (!validateNodePath(args.targetNodePath)) {
|
|
436
|
+
return createErrorResponse('Invalid targetNodePath', [
|
|
437
|
+
'Provide a scene-tree path without ".." (e.g. "root/Player")',
|
|
559
438
|
]);
|
|
560
439
|
}
|
|
440
|
+
return {
|
|
441
|
+
projectPath: v.projectPath,
|
|
442
|
+
scenePath: v.scenePath,
|
|
443
|
+
nodePath: args.nodePath,
|
|
444
|
+
signal: args.signal,
|
|
445
|
+
targetNodePath: args.targetNodePath,
|
|
446
|
+
method: args.method,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
export async function handleConnectSignal(runner, args) {
|
|
450
|
+
args = normalizeParameters(args);
|
|
451
|
+
const v = validateSignalArgs(args);
|
|
452
|
+
if ('isError' in v)
|
|
453
|
+
return v;
|
|
454
|
+
const params = {
|
|
455
|
+
scenePath: v.scenePath,
|
|
456
|
+
nodePath: v.nodePath,
|
|
457
|
+
signal: v.signal,
|
|
458
|
+
targetNodePath: v.targetNodePath,
|
|
459
|
+
method: v.method,
|
|
460
|
+
};
|
|
461
|
+
return executeSceneOp(runner, 'connect_signal', params, v.projectPath, 'Failed to connect signal', ['Ensure the signal exists on the source node and the method exists on the target node']);
|
|
561
462
|
}
|
|
562
463
|
export async function handleDisconnectSignal(runner, args) {
|
|
563
464
|
args = normalizeParameters(args);
|
|
564
|
-
const v =
|
|
465
|
+
const v = validateSignalArgs(args);
|
|
565
466
|
if ('isError' in v)
|
|
566
467
|
return v;
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
if (!validatePath(args.targetNodePath)) {
|
|
576
|
-
return createErrorResponse('Invalid targetNodePath', ['Provide a valid path without ".."']);
|
|
577
|
-
}
|
|
578
|
-
try {
|
|
579
|
-
const params = {
|
|
580
|
-
scenePath: args.scenePath,
|
|
581
|
-
nodePath: args.nodePath,
|
|
582
|
-
signal: args.signal,
|
|
583
|
-
targetNodePath: args.targetNodePath,
|
|
584
|
-
method: args.method,
|
|
585
|
-
};
|
|
586
|
-
const { stdout, stderr } = await runner.executeOperation('disconnect_signal', params, v.projectPath);
|
|
587
|
-
if (!stdout.trim()) {
|
|
588
|
-
return createErrorResponse(`Failed to disconnect signal: ${extractGdError(stderr)}`, [
|
|
589
|
-
'Ensure the signal connection exists before trying to disconnect it',
|
|
590
|
-
]);
|
|
591
|
-
}
|
|
592
|
-
return { content: [{ type: 'text', text: stdout }] };
|
|
593
|
-
}
|
|
594
|
-
catch (error) {
|
|
595
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
596
|
-
return createErrorResponse(`Failed to disconnect signal: ${errorMessage}`, [
|
|
597
|
-
'Ensure Godot is installed correctly',
|
|
598
|
-
]);
|
|
599
|
-
}
|
|
468
|
+
const params = {
|
|
469
|
+
scenePath: v.scenePath,
|
|
470
|
+
nodePath: v.nodePath,
|
|
471
|
+
signal: v.signal,
|
|
472
|
+
targetNodePath: v.targetNodePath,
|
|
473
|
+
method: v.method,
|
|
474
|
+
};
|
|
475
|
+
return executeSceneOp(runner, 'disconnect_signal', params, v.projectPath, 'Failed to disconnect signal', ['Ensure the signal connection exists before trying to disconnect it']);
|
|
600
476
|
}
|
|
601
477
|
//# sourceMappingURL=node-tools.js.map
|