godot-mcp-runtime 2.2.3 → 3.0.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.
Files changed (59) hide show
  1. package/README.md +30 -93
  2. package/dist/dispatch.d.ts +1 -11
  3. package/dist/dispatch.d.ts.map +1 -1
  4. package/dist/dispatch.js +7 -8
  5. package/dist/dispatch.js.map +1 -1
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +12 -10
  9. package/dist/index.js.map +1 -1
  10. package/dist/scripts/godot_operations.gd +134 -283
  11. package/dist/scripts/mcp_bridge.gd +210 -43
  12. package/dist/tools/autoload-tools.d.ts +51 -0
  13. package/dist/tools/autoload-tools.d.ts.map +1 -0
  14. package/dist/tools/autoload-tools.js +187 -0
  15. package/dist/tools/autoload-tools.js.map +1 -0
  16. package/dist/tools/node-tools.d.ts +9 -78
  17. package/dist/tools/node-tools.d.ts.map +1 -1
  18. package/dist/tools/node-tools.js +114 -310
  19. package/dist/tools/node-tools.js.map +1 -1
  20. package/dist/tools/project-tools.d.ts +0 -168
  21. package/dist/tools/project-tools.d.ts.map +1 -1
  22. package/dist/tools/project-tools.js +120 -1192
  23. package/dist/tools/project-tools.js.map +1 -1
  24. package/dist/tools/runtime-tools.d.ts +108 -0
  25. package/dist/tools/runtime-tools.d.ts.map +1 -0
  26. package/dist/tools/runtime-tools.js +808 -0
  27. package/dist/tools/runtime-tools.js.map +1 -0
  28. package/dist/tools/scene-tools.d.ts +6 -48
  29. package/dist/tools/scene-tools.d.ts.map +1 -1
  30. package/dist/tools/scene-tools.js +67 -211
  31. package/dist/tools/scene-tools.js.map +1 -1
  32. package/dist/tools/validate-tools.d.ts.map +1 -1
  33. package/dist/tools/validate-tools.js +35 -29
  34. package/dist/tools/validate-tools.js.map +1 -1
  35. package/dist/utils/autoload-ini.d.ts +32 -0
  36. package/dist/utils/autoload-ini.d.ts.map +1 -0
  37. package/dist/utils/autoload-ini.js +111 -0
  38. package/dist/utils/autoload-ini.js.map +1 -0
  39. package/dist/utils/bridge-manager.d.ts +29 -0
  40. package/dist/utils/bridge-manager.d.ts.map +1 -0
  41. package/dist/utils/bridge-manager.js +136 -0
  42. package/dist/utils/bridge-manager.js.map +1 -0
  43. package/dist/utils/bridge-protocol.d.ts +34 -0
  44. package/dist/utils/bridge-protocol.d.ts.map +1 -0
  45. package/dist/utils/bridge-protocol.js +65 -0
  46. package/dist/utils/bridge-protocol.js.map +1 -0
  47. package/dist/utils/godot-runner.d.ts +70 -15
  48. package/dist/utils/godot-runner.d.ts.map +1 -1
  49. package/dist/utils/godot-runner.js +309 -277
  50. package/dist/utils/godot-runner.js.map +1 -1
  51. package/dist/utils/handler-helpers.d.ts +34 -0
  52. package/dist/utils/handler-helpers.d.ts.map +1 -0
  53. package/dist/utils/handler-helpers.js +55 -0
  54. package/dist/utils/handler-helpers.js.map +1 -0
  55. package/dist/utils/logger.d.ts +3 -0
  56. package/dist/utils/logger.d.ts.map +1 -0
  57. package/dist/utils/logger.js +11 -0
  58. package/dist/utils/logger.js.map +1 -0
  59. package/package.json +7 -4
@@ -1,11 +1,13 @@
1
1
  import { existsSync } from 'fs';
2
2
  import { join } from 'path';
3
- import { normalizeParameters, convertCamelToSnakeCase, validatePath, createErrorResponse, extractGdError, validateSceneArgs, } from '../utils/godot-runner.js';
3
+ import { normalizeParameters, convertCamelToSnakeCase, validatePath, 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: 'delete_node',
8
- description: 'Remove a node from a Godot scene file. Saves automatically.',
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: [{ nodePath, success?, error? }] }.',
10
+ annotations: { destructiveHint: true },
9
11
  inputSchema: {
10
12
  type: 'object',
11
13
  properties: {
@@ -14,35 +16,19 @@ export const nodeToolDefinitions = [
14
16
  type: 'string',
15
17
  description: 'Scene file path relative to the project (e.g. "scenes/main.tscn")',
16
18
  },
17
- nodePath: {
18
- type: 'string',
19
- description: 'Node path from scene root (e.g. "root/Player/Sprite2D")',
20
- },
21
- },
22
- required: ['projectPath', 'scenePath', 'nodePath'],
23
- },
24
- },
25
- {
26
- name: 'set_node_property',
27
- description: 'Set a property on a node in a Godot scene file. Saves automatically. Primitives (string, number, boolean, array, object) are passed as-is. Vector2 ({"x","y"}), Vector3 ({"x","y","z"}), and Color ({"r","g","b","a"}) are automatically converted. Use run_script for other complex GDScript types.',
28
- inputSchema: {
29
- type: 'object',
30
- properties: {
31
- projectPath: { type: 'string', description: 'Path to the Godot project directory' },
32
- scenePath: { type: 'string', description: 'Scene file path relative to the project' },
33
- nodePath: { type: 'string', description: 'Node path from scene root (e.g. "root/Player")' },
34
- property: {
35
- type: 'string',
36
- description: 'GDScript property name in snake_case (e.g. "position", "modulate", "collision_layer"). Use get_node_properties to discover valid names.',
19
+ nodePaths: {
20
+ type: 'array',
21
+ items: { type: 'string' },
22
+ description: 'Node paths from scene root to delete (e.g. ["root/Player/Sprite2D"])',
37
23
  },
38
- value: { description: 'New property value' },
39
24
  },
40
- required: ['projectPath', 'scenePath', 'nodePath', 'property', 'value'],
25
+ required: ['projectPath', 'scenePath', 'nodePaths'],
41
26
  },
42
27
  },
43
28
  {
44
- name: 'batch_set_node_properties',
45
- description: 'Set multiple node properties in a single Godot process. Saves automatically. Returns { results: [{ nodePath, property, success?, error? }] }.',
29
+ name: 'set_node_properties',
30
+ 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: [{ nodePath, property, success?, error? }] }.',
31
+ annotations: { idempotentHint: true },
46
32
  inputSchema: {
47
33
  type: 'object',
48
34
  properties: {
@@ -54,8 +40,14 @@ export const nodeToolDefinitions = [
54
40
  items: {
55
41
  type: 'object',
56
42
  properties: {
57
- nodePath: { type: 'string', description: 'Node path from scene root' },
58
- property: { type: 'string', description: 'GDScript property name in snake_case' },
43
+ nodePath: {
44
+ type: 'string',
45
+ description: 'Node path from scene root (e.g. "root/Player")',
46
+ },
47
+ property: {
48
+ type: 'string',
49
+ description: 'GDScript property name in snake_case (e.g. "position", "modulate", "collision_layer")',
50
+ },
59
51
  value: { description: 'New property value' },
60
52
  },
61
53
  required: ['nodePath', 'property', 'value'],
@@ -71,24 +63,8 @@ export const nodeToolDefinitions = [
71
63
  },
72
64
  {
73
65
  name: 'get_node_properties',
74
- description: "Read a node's current property values from a Godot scene file.",
75
- inputSchema: {
76
- type: 'object',
77
- properties: {
78
- projectPath: { type: 'string', description: 'Path to the Godot project directory' },
79
- scenePath: { type: 'string', description: 'Scene file path relative to the project' },
80
- nodePath: { type: 'string', description: 'Node path from scene root (e.g. "root/Player")' },
81
- changedOnly: {
82
- type: 'boolean',
83
- description: 'Only return properties whose values differ from their class defaults (default: false)',
84
- },
85
- },
86
- required: ['projectPath', 'scenePath', 'nodePath'],
87
- },
88
- },
89
- {
90
- name: 'batch_get_node_properties',
91
- description: 'Get properties from multiple nodes in a single Godot process. Returns { results: [{ nodePath, nodeType, properties?, error? }] }.',
66
+ 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.",
67
+ annotations: { readOnlyHint: true },
92
68
  inputSchema: {
93
69
  type: 'object',
94
70
  properties: {
@@ -100,7 +76,10 @@ export const nodeToolDefinitions = [
100
76
  items: {
101
77
  type: 'object',
102
78
  properties: {
103
- nodePath: { type: 'string', description: 'Node path from scene root' },
79
+ nodePath: {
80
+ type: 'string',
81
+ description: 'Node path from scene root (e.g. "root/Player")',
82
+ },
104
83
  changedOnly: {
105
84
  type: 'boolean',
106
85
  description: 'Only return properties differing from defaults (default: false)',
@@ -115,7 +94,8 @@ export const nodeToolDefinitions = [
115
94
  },
116
95
  {
117
96
  name: 'attach_script',
118
- description: 'Attach a GDScript file to a node in a Godot scene. Saves automatically.',
97
+ 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. Returns { success, nodePath, scriptPath }.',
98
+ annotations: { idempotentHint: true },
119
99
  inputSchema: {
120
100
  type: 'object',
121
101
  properties: {
@@ -132,7 +112,8 @@ export const nodeToolDefinitions = [
132
112
  },
133
113
  {
134
114
  name: 'get_scene_tree',
135
- description: 'Get the scene hierarchy as a tree structure. Use maxDepth: 1 for a shallow listing of direct children only.',
115
+ 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.',
116
+ annotations: { readOnlyHint: true },
136
117
  inputSchema: {
137
118
  type: 'object',
138
119
  properties: {
@@ -152,7 +133,7 @@ export const nodeToolDefinitions = [
152
133
  },
153
134
  {
154
135
  name: 'duplicate_node',
155
- description: 'Duplicate a node and its children in a Godot scene. Saves automatically.',
136
+ 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. Returns { success, originalPath, newPath }.',
156
137
  inputSchema: {
157
138
  type: 'object',
158
139
  properties: {
@@ -173,7 +154,8 @@ export const nodeToolDefinitions = [
173
154
  },
174
155
  {
175
156
  name: 'get_node_signals',
176
- description: 'List all signals defined on a node and their current connections. Returns { nodePath, nodeType, signals: [{ name, connections: [{ signal, target, method }] }] }. Note: the target field uses Godot absolute path format (e.g. /root/Scene/Node) — convert to scene-root-relative (e.g. root/Node) before passing to connect_signal or disconnect_signal.',
157
+ description: 'List all signals defined on a node and their current connections. Use before connect_signal/disconnect_signal to verify signal/method names. Returns { nodePath, nodeType, signals: [{ name, connections: [{ signal, target, method }] }] }. The target field uses Godot absolute path format (/root/Scene/Node) — convert to scene-root-relative (root/Node) before passing to connect/disconnect_signal. Errors if node not found.',
158
+ annotations: { readOnlyHint: true },
177
159
  inputSchema: {
178
160
  type: 'object',
179
161
  properties: {
@@ -186,7 +168,7 @@ export const nodeToolDefinitions = [
186
168
  },
187
169
  {
188
170
  name: 'connect_signal',
189
- description: 'Connect a signal from one node to a method on another node. Saves automatically. Errors if the signal does not exist on the source node or the method does not exist on the target node.',
171
+ 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.',
190
172
  inputSchema: {
191
173
  type: 'object',
192
174
  properties: {
@@ -211,7 +193,8 @@ export const nodeToolDefinitions = [
211
193
  },
212
194
  {
213
195
  name: 'disconnect_signal',
214
- description: 'Disconnect a signal connection between two nodes. Saves automatically. Errors if the connection does not exist — use get_node_signals first to verify.',
196
+ 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.',
197
+ annotations: { destructiveHint: true },
215
198
  inputSchema: {
216
199
  type: 'object',
217
200
  properties: {
@@ -227,71 +210,29 @@ export const nodeToolDefinitions = [
227
210
  },
228
211
  ];
229
212
  // --- Handlers ---
230
- export async function handleDeleteNode(runner, args) {
231
- args = normalizeParameters(args);
232
- const v = validateSceneArgs(args);
233
- if ('isError' in v)
234
- return v;
235
- if (!args.nodePath || !validatePath(args.nodePath)) {
236
- return createErrorResponse('Valid nodePath is required', [
237
- 'Provide the node path (e.g. "root/Player")',
238
- ]);
239
- }
240
- try {
241
- const params = { scenePath: args.scenePath, nodePath: args.nodePath };
242
- const { stdout, stderr } = await runner.executeOperation('delete_node', params, v.projectPath);
243
- if (!stdout.trim()) {
244
- return createErrorResponse(`Failed to delete node: ${extractGdError(stderr)}`, [
245
- 'Check if the node path is correct',
246
- ]);
247
- }
248
- return { content: [{ type: 'text', text: stdout }] };
249
- }
250
- catch (error) {
251
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
252
- return createErrorResponse(`Failed to delete node: ${errorMessage}`, [
253
- 'Ensure Godot is installed correctly',
254
- ]);
255
- }
256
- }
257
- export async function handleSetNodeProperty(runner, args) {
213
+ export async function handleDeleteNodes(runner, args) {
258
214
  args = normalizeParameters(args);
259
215
  const v = validateSceneArgs(args);
260
216
  if ('isError' in v)
261
217
  return v;
262
- if (!args.nodePath || !validatePath(args.nodePath)) {
263
- return createErrorResponse('Valid nodePath is required', [
264
- 'Provide the node path (e.g. "root/Player")',
265
- ]);
266
- }
267
- if (!args.property || args.value === undefined) {
268
- return createErrorResponse('property and value are required', [
269
- 'Provide both property name and value',
218
+ if (!args.nodePaths || !Array.isArray(args.nodePaths) || args.nodePaths.length === 0) {
219
+ return createErrorResponse('nodePaths array is required', [
220
+ 'Provide a non-empty array of node paths (e.g. ["root/Player"])',
270
221
  ]);
271
222
  }
272
- try {
273
- const params = {
274
- scenePath: args.scenePath,
275
- nodePath: args.nodePath,
276
- property: args.property,
277
- value: args.value,
278
- };
279
- const { stdout, stderr } = await runner.executeOperation('set_node_property', params, v.projectPath);
280
- if (!stdout.trim()) {
281
- return createErrorResponse(`Failed to update property: ${extractGdError(stderr)}`, [
282
- 'Check if the property name is valid for this node type',
223
+ for (const p of args.nodePaths) {
224
+ if (typeof p !== 'string' || !validatePath(p)) {
225
+ return createErrorResponse('Invalid nodePath in nodePaths', [
226
+ 'Provide valid paths without ".." (e.g. "root/Player")',
283
227
  ]);
284
228
  }
285
- return { content: [{ type: 'text', text: stdout }] };
286
- }
287
- catch (error) {
288
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
289
- return createErrorResponse(`Failed to set node property: ${errorMessage}`, [
290
- 'Ensure Godot is installed correctly',
291
- ]);
292
229
  }
230
+ const params = { scenePath: args.scenePath, nodePaths: args.nodePaths };
231
+ return executeSceneOp(runner, 'delete_nodes', params, v.projectPath, 'Failed to delete nodes', [
232
+ 'Check if the node paths are correct',
233
+ ]);
293
234
  }
294
- export async function handleBatchSetNodeProperties(runner, args) {
235
+ export async function handleSetNodeProperties(runner, args) {
295
236
  args = normalizeParameters(args);
296
237
  const v = validateSceneArgs(args);
297
238
  if ('isError' in v)
@@ -301,58 +242,15 @@ export async function handleBatchSetNodeProperties(runner, args) {
301
242
  'Provide an array of { nodePath, property, value }',
302
243
  ]);
303
244
  }
304
- try {
305
- const snakeUpdates = args.updates.map((u) => convertCamelToSnakeCase(u));
306
- const params = {
307
- scenePath: args.scenePath,
308
- updates: snakeUpdates,
309
- abortOnError: args.abortOnError ?? false,
310
- };
311
- const { stdout, stderr } = await runner.executeOperation('batch_set_node_properties', params, v.projectPath);
312
- if (!stdout.trim()) {
313
- return createErrorResponse(`Batch update failed: ${extractGdError(stderr)}`, [
314
- 'Check node paths and property names',
315
- ]);
316
- }
317
- return { content: [{ type: 'text', text: stdout }] };
318
- }
319
- catch (error) {
320
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
321
- return createErrorResponse(`Batch set properties failed: ${errorMessage}`, [
322
- 'Ensure Godot is installed correctly',
323
- ]);
324
- }
245
+ const snakeUpdates = args.updates.map((u) => convertCamelToSnakeCase(u));
246
+ const params = {
247
+ scenePath: args.scenePath,
248
+ updates: snakeUpdates,
249
+ abortOnError: args.abortOnError ?? false,
250
+ };
251
+ return executeSceneOp(runner, 'set_node_properties', params, v.projectPath, 'Failed to set node properties', ['Check node paths and property names']);
325
252
  }
326
253
  export async function handleGetNodeProperties(runner, args) {
327
- args = normalizeParameters(args);
328
- const v = validateSceneArgs(args);
329
- if ('isError' in v)
330
- return v;
331
- if (!args.nodePath || !validatePath(args.nodePath)) {
332
- return createErrorResponse('Valid nodePath is required', [
333
- 'Provide the node path (e.g. "root/Player")',
334
- ]);
335
- }
336
- try {
337
- const params = { scenePath: args.scenePath, nodePath: args.nodePath };
338
- if (args.changedOnly)
339
- params.changedOnly = args.changedOnly;
340
- const { stdout, stderr } = await runner.executeOperation('get_node_properties', params, v.projectPath);
341
- if (!stdout.trim()) {
342
- return createErrorResponse(`Failed to get properties: ${extractGdError(stderr)}`, [
343
- 'Check if the node path is correct',
344
- ]);
345
- }
346
- return { content: [{ type: 'text', text: stdout }] };
347
- }
348
- catch (error) {
349
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
350
- return createErrorResponse(`Failed to get node properties: ${errorMessage}`, [
351
- 'Ensure Godot is installed correctly',
352
- ]);
353
- }
354
- }
355
- export async function handleBatchGetNodeProperties(runner, args) {
356
254
  args = normalizeParameters(args);
357
255
  const v = validateSceneArgs(args);
358
256
  if ('isError' in v)
@@ -362,23 +260,9 @@ export async function handleBatchGetNodeProperties(runner, args) {
362
260
  'Provide an array of { nodePath, changedOnly? }',
363
261
  ]);
364
262
  }
365
- try {
366
- const snakeNodes = args.nodes.map((n) => convertCamelToSnakeCase(n));
367
- const params = { scenePath: args.scenePath, nodes: snakeNodes };
368
- const { stdout, stderr } = await runner.executeOperation('batch_get_node_properties', params, v.projectPath);
369
- if (!stdout.trim()) {
370
- return createErrorResponse(`Batch get_properties failed: ${extractGdError(stderr)}`, [
371
- 'Check node paths',
372
- ]);
373
- }
374
- return { content: [{ type: 'text', text: stdout }] };
375
- }
376
- catch (error) {
377
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
378
- return createErrorResponse(`Batch get properties failed: ${errorMessage}`, [
379
- 'Ensure Godot is installed correctly',
380
- ]);
381
- }
263
+ const snakeNodes = args.nodes.map((n) => convertCamelToSnakeCase(n));
264
+ const params = { scenePath: args.scenePath, nodes: snakeNodes };
265
+ return executeSceneOp(runner, 'get_node_properties', params, v.projectPath, 'Failed to get node properties', ['Check node paths']);
382
266
  }
383
267
  export async function handleAttachScript(runner, args) {
384
268
  args = normalizeParameters(args);
@@ -401,26 +285,14 @@ export async function handleAttachScript(runner, args) {
401
285
  'Create the script file first',
402
286
  ]);
403
287
  }
404
- try {
405
- const params = {
406
- scenePath: args.scenePath,
407
- nodePath: args.nodePath,
408
- scriptPath: args.scriptPath,
409
- };
410
- const { stdout, stderr } = await runner.executeOperation('attach_script', params, v.projectPath);
411
- if (!stdout.trim()) {
412
- return createErrorResponse(`Failed to attach script: ${extractGdError(stderr)}`, [
413
- 'Ensure the script is valid for this node type',
414
- ]);
415
- }
416
- return { content: [{ type: 'text', text: stdout }] };
417
- }
418
- catch (error) {
419
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
420
- return createErrorResponse(`Failed to attach script: ${errorMessage}`, [
421
- 'Ensure Godot is installed correctly',
422
- ]);
423
- }
288
+ const params = {
289
+ scenePath: args.scenePath,
290
+ nodePath: args.nodePath,
291
+ scriptPath: args.scriptPath,
292
+ };
293
+ return executeSceneOp(runner, 'attach_script', params, v.projectPath, 'Failed to attach script', [
294
+ 'Ensure the script is valid for this node type',
295
+ ]);
424
296
  }
425
297
  export async function handleGetSceneTree(runner, args) {
426
298
  args = normalizeParameters(args);
@@ -430,26 +302,12 @@ export async function handleGetSceneTree(runner, args) {
430
302
  if (args.parentPath && !validatePath(args.parentPath)) {
431
303
  return createErrorResponse('Invalid parentPath', ['Provide a valid path without ".."']);
432
304
  }
433
- try {
434
- const params = { scenePath: args.scenePath };
435
- if (args.parentPath)
436
- params.parentPath = args.parentPath;
437
- if (typeof args.maxDepth === 'number')
438
- params.maxDepth = args.maxDepth;
439
- const { stdout, stderr } = await runner.executeOperation('get_scene_tree', params, v.projectPath);
440
- if (!stdout.trim()) {
441
- return createErrorResponse(`Failed to get scene tree: ${extractGdError(stderr)}`, [
442
- 'Ensure the scene is valid',
443
- ]);
444
- }
445
- return { content: [{ type: 'text', text: stdout }] };
446
- }
447
- catch (error) {
448
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
449
- return createErrorResponse(`Failed to get scene tree: ${errorMessage}`, [
450
- 'Ensure Godot is installed correctly',
451
- ]);
452
- }
305
+ const params = { scenePath: args.scenePath };
306
+ if (args.parentPath)
307
+ params.parentPath = args.parentPath;
308
+ if (typeof args.maxDepth === 'number')
309
+ params.maxDepth = args.maxDepth;
310
+ return executeSceneOp(runner, 'get_scene_tree', params, v.projectPath, 'Failed to get scene tree', ['Ensure the scene is valid']);
453
311
  }
454
312
  export async function handleDuplicateNode(runner, args) {
455
313
  args = normalizeParameters(args);
@@ -464,26 +322,12 @@ export async function handleDuplicateNode(runner, args) {
464
322
  if (args.targetParentPath && !validatePath(args.targetParentPath)) {
465
323
  return createErrorResponse('Invalid targetParentPath', ['Provide a valid path without ".."']);
466
324
  }
467
- try {
468
- const params = { scenePath: args.scenePath, nodePath: args.nodePath };
469
- if (args.newName)
470
- params.newName = args.newName;
471
- if (args.targetParentPath)
472
- params.targetParentPath = args.targetParentPath;
473
- const { stdout, stderr } = await runner.executeOperation('duplicate_node', params, v.projectPath);
474
- if (!stdout.trim()) {
475
- return createErrorResponse(`Failed to duplicate node: ${extractGdError(stderr)}`, [
476
- 'Check if the node path and target parent path are correct',
477
- ]);
478
- }
479
- return { content: [{ type: 'text', text: stdout }] };
480
- }
481
- catch (error) {
482
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
483
- return createErrorResponse(`Failed to duplicate node: ${errorMessage}`, [
484
- 'Ensure Godot is installed correctly',
485
- ]);
486
- }
325
+ const params = { scenePath: args.scenePath, nodePath: args.nodePath };
326
+ if (args.newName)
327
+ params.newName = args.newName;
328
+ if (args.targetParentPath)
329
+ params.targetParentPath = args.targetParentPath;
330
+ return executeSceneOp(runner, 'duplicate_node', params, v.projectPath, 'Failed to duplicate node', ['Check if the node path and target parent path are correct']);
487
331
  }
488
332
  export async function handleGetNodeSignals(runner, args) {
489
333
  args = normalizeParameters(args);
@@ -495,25 +339,10 @@ export async function handleGetNodeSignals(runner, args) {
495
339
  'Provide the node path (e.g. "root/Button")',
496
340
  ]);
497
341
  }
498
- try {
499
- const params = { scenePath: args.scenePath, nodePath: args.nodePath };
500
- const { stdout, stderr } = await runner.executeOperation('get_node_signals', params, v.projectPath);
501
- if (!stdout.trim()) {
502
- return createErrorResponse(`Failed to get signals: ${extractGdError(stderr)}`, [
503
- 'Check if the node path is correct',
504
- ]);
505
- }
506
- return { content: [{ type: 'text', text: stdout }] };
507
- }
508
- catch (error) {
509
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
510
- return createErrorResponse(`Failed to get node signals: ${errorMessage}`, [
511
- 'Ensure Godot is installed correctly',
512
- ]);
513
- }
342
+ const params = { scenePath: args.scenePath, nodePath: args.nodePath };
343
+ return executeSceneOp(runner, 'get_node_signals', params, v.projectPath, 'Failed to get node signals', ['Check if the node path is correct']);
514
344
  }
515
- export async function handleConnectSignal(runner, args) {
516
- args = normalizeParameters(args);
345
+ function validateSignalArgs(args) {
517
346
  const v = validateSceneArgs(args);
518
347
  if ('isError' in v)
519
348
  return v;
@@ -528,66 +357,41 @@ export async function handleConnectSignal(runner, args) {
528
357
  if (!validatePath(args.targetNodePath)) {
529
358
  return createErrorResponse('Invalid targetNodePath', ['Provide a valid path without ".."']);
530
359
  }
531
- try {
532
- const params = {
533
- scenePath: args.scenePath,
534
- nodePath: args.nodePath,
535
- signal: args.signal,
536
- targetNodePath: args.targetNodePath,
537
- method: args.method,
538
- };
539
- const { stdout, stderr } = await runner.executeOperation('connect_signal', params, v.projectPath);
540
- if (!stdout.trim()) {
541
- return createErrorResponse(`Failed to connect signal: ${extractGdError(stderr)}`, [
542
- 'Ensure the signal exists on the source node and the method exists on the target node',
543
- ]);
544
- }
545
- return { content: [{ type: 'text', text: stdout }] };
546
- }
547
- catch (error) {
548
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
549
- return createErrorResponse(`Failed to connect signal: ${errorMessage}`, [
550
- 'Ensure Godot is installed correctly',
551
- ]);
552
- }
360
+ return {
361
+ projectPath: v.projectPath,
362
+ scenePath: v.scenePath,
363
+ nodePath: args.nodePath,
364
+ signal: args.signal,
365
+ targetNodePath: args.targetNodePath,
366
+ method: args.method,
367
+ };
368
+ }
369
+ export async function handleConnectSignal(runner, args) {
370
+ args = normalizeParameters(args);
371
+ const v = validateSignalArgs(args);
372
+ if ('isError' in v)
373
+ return v;
374
+ const params = {
375
+ scenePath: v.scenePath,
376
+ nodePath: v.nodePath,
377
+ signal: v.signal,
378
+ targetNodePath: v.targetNodePath,
379
+ method: v.method,
380
+ };
381
+ 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']);
553
382
  }
554
383
  export async function handleDisconnectSignal(runner, args) {
555
384
  args = normalizeParameters(args);
556
- const v = validateSceneArgs(args);
385
+ const v = validateSignalArgs(args);
557
386
  if ('isError' in v)
558
387
  return v;
559
- if (!args.nodePath || !validatePath(args.nodePath)) {
560
- return createErrorResponse('Valid nodePath is required', ['Provide the source node path']);
561
- }
562
- if (!args.signal || !args.targetNodePath || !args.method) {
563
- return createErrorResponse('signal, targetNodePath, and method are required', [
564
- 'Provide all three parameters',
565
- ]);
566
- }
567
- if (!validatePath(args.targetNodePath)) {
568
- return createErrorResponse('Invalid targetNodePath', ['Provide a valid path without ".."']);
569
- }
570
- try {
571
- const params = {
572
- scenePath: args.scenePath,
573
- nodePath: args.nodePath,
574
- signal: args.signal,
575
- targetNodePath: args.targetNodePath,
576
- method: args.method,
577
- };
578
- const { stdout, stderr } = await runner.executeOperation('disconnect_signal', params, v.projectPath);
579
- if (!stdout.trim()) {
580
- return createErrorResponse(`Failed to disconnect signal: ${extractGdError(stderr)}`, [
581
- 'Ensure the signal connection exists before trying to disconnect it',
582
- ]);
583
- }
584
- return { content: [{ type: 'text', text: stdout }] };
585
- }
586
- catch (error) {
587
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
588
- return createErrorResponse(`Failed to disconnect signal: ${errorMessage}`, [
589
- 'Ensure Godot is installed correctly',
590
- ]);
591
- }
388
+ const params = {
389
+ scenePath: v.scenePath,
390
+ nodePath: v.nodePath,
391
+ signal: v.signal,
392
+ targetNodePath: v.targetNodePath,
393
+ method: v.method,
394
+ };
395
+ return executeSceneOp(runner, 'disconnect_signal', params, v.projectPath, 'Failed to disconnect signal', ['Ensure the signal connection exists before trying to disconnect it']);
592
396
  }
593
397
  //# sourceMappingURL=node-tools.js.map