@satelliteoflove/godot-mcp 0.1.5 → 0.1.6

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 (33) hide show
  1. package/dist/__tests__/helpers/mock-godot.d.ts +17 -0
  2. package/dist/__tests__/helpers/mock-godot.d.ts.map +1 -0
  3. package/dist/__tests__/helpers/mock-godot.js +33 -0
  4. package/dist/__tests__/helpers/mock-godot.js.map +1 -0
  5. package/dist/__tests__/tools/animation.test.d.ts +2 -0
  6. package/dist/__tests__/tools/animation.test.d.ts.map +1 -0
  7. package/dist/__tests__/tools/animation.test.js +315 -0
  8. package/dist/__tests__/tools/animation.test.js.map +1 -0
  9. package/dist/__tests__/tools/node.test.d.ts +2 -0
  10. package/dist/__tests__/tools/node.test.d.ts.map +1 -0
  11. package/dist/__tests__/tools/node.test.js +173 -0
  12. package/dist/__tests__/tools/node.test.js.map +1 -0
  13. package/dist/__tests__/tools/scene.test.d.ts +2 -0
  14. package/dist/__tests__/tools/scene.test.d.ts.map +1 -0
  15. package/dist/__tests__/tools/scene.test.js +127 -0
  16. package/dist/__tests__/tools/scene.test.js.map +1 -0
  17. package/dist/__tests__/tools/tilemap.test.d.ts +2 -0
  18. package/dist/__tests__/tools/tilemap.test.d.ts.map +1 -0
  19. package/dist/__tests__/tools/tilemap.test.js +333 -0
  20. package/dist/__tests__/tools/tilemap.test.js.map +1 -0
  21. package/dist/tools/animation.d.ts +152 -216
  22. package/dist/tools/animation.d.ts.map +1 -1
  23. package/dist/tools/animation.js +324 -372
  24. package/dist/tools/animation.js.map +1 -1
  25. package/dist/tools/tilemap.d.ts +267 -253
  26. package/dist/tools/tilemap.d.ts.map +1 -1
  27. package/dist/tools/tilemap.js +273 -304
  28. package/dist/tools/tilemap.js.map +1 -1
  29. package/package.json +4 -2
  30. package/dist/__tests__/registry.test.d.ts +0 -2
  31. package/dist/__tests__/registry.test.d.ts.map +0 -1
  32. package/dist/__tests__/registry.test.js +0 -116
  33. package/dist/__tests__/registry.test.js.map +0 -1
@@ -1,382 +1,334 @@
1
+ /**
2
+ * Animation Tools - consolidated tools for animation operations
3
+ *
4
+ * SCHEMA PATTERN FOR CONSOLIDATED TOOLS:
5
+ *
6
+ * Claude Code requires tool schemas to have `type: "object"` at the root.
7
+ * Do NOT use z.discriminatedUnion() - it generates `anyOf` at the root level,
8
+ * which causes Claude Code to reject the entire MCP tool list.
9
+ *
10
+ * Instead, use a flat object schema with .refine() for runtime validation:
11
+ *
12
+ * const Schema = z.object({
13
+ * action: z.enum(['foo', 'bar']).describe('Action: foo, bar'),
14
+ * param1: z.string().optional().describe('Description (foo only)'),
15
+ * param2: z.number().optional().describe('Description (bar only)'),
16
+ * }).refine((data) => {
17
+ * switch (data.action) {
18
+ * case 'foo': return !!data.param1;
19
+ * case 'bar': return data.param2 !== undefined;
20
+ * }
21
+ * }, { message: 'Missing required fields for action' });
22
+ *
23
+ * The .refine() runs at runtime only - it doesn't affect the JSON schema output.
24
+ * Use field descriptions to indicate which action each field applies to.
25
+ */
1
26
  import { z } from 'zod';
2
27
  import { defineTool } from '../core/define-tool.js';
3
- export const listAnimationPlayers = defineTool({
4
- name: 'list_animation_players',
5
- description: 'Find all AnimationPlayer nodes in the scene',
6
- schema: z.object({
7
- root_path: z
8
- .string()
9
- .optional()
10
- .describe('Starting node path, defaults to scene root'),
11
- }),
12
- async execute({ root_path }, { godot }) {
13
- const result = await godot.sendCommand('list_animation_players', { root_path });
14
- if (result.animation_players.length === 0) {
15
- return 'No AnimationPlayer nodes found in scene';
28
+ const AnimationQuerySchema = z
29
+ .object({
30
+ action: z
31
+ .enum(['list_players', 'get_info', 'get_details', 'get_keyframes'])
32
+ .describe('Action: list_players, get_info, get_details, get_keyframes'),
33
+ root_path: z.string().optional().describe('Starting node path (list_players only)'),
34
+ node_path: z.string().optional().describe('Path to AnimationPlayer (required except list_players)'),
35
+ animation_name: z.string().optional().describe('Animation name (get_details, get_keyframes)'),
36
+ track_index: z.number().optional().describe('Track index (get_keyframes only)'),
37
+ })
38
+ .refine((data) => {
39
+ switch (data.action) {
40
+ case 'list_players':
41
+ return true;
42
+ case 'get_info':
43
+ return !!data.node_path;
44
+ case 'get_details':
45
+ return !!data.node_path && !!data.animation_name;
46
+ case 'get_keyframes':
47
+ return !!data.node_path && !!data.animation_name && data.track_index !== undefined;
48
+ }
49
+ }, { message: 'Missing required fields for action' });
50
+ export const animationQuery = defineTool({
51
+ name: 'animation_query',
52
+ description: 'Query animation data. Actions: list_players (find AnimationPlayers), get_info (player state), get_details (animation tracks/length), get_keyframes (track keyframes)',
53
+ schema: AnimationQuerySchema,
54
+ async execute(args, { godot }) {
55
+ switch (args.action) {
56
+ case 'list_players': {
57
+ const result = await godot.sendCommand('list_animation_players', { root_path: args.root_path });
58
+ if (result.animation_players.length === 0) {
59
+ return 'No AnimationPlayer nodes found in scene';
60
+ }
61
+ return `Found ${result.animation_players.length} AnimationPlayer(s):\n${result.animation_players.map((p) => ` - ${p.path}`).join('\n')}`;
62
+ }
63
+ case 'get_info': {
64
+ const result = await godot.sendCommand('get_animation_player_info', { node_path: args.node_path });
65
+ return JSON.stringify(result, null, 2);
66
+ }
67
+ case 'get_details': {
68
+ const result = await godot.sendCommand('get_animation_details', {
69
+ node_path: args.node_path,
70
+ animation_name: args.animation_name,
71
+ });
72
+ return JSON.stringify(result, null, 2);
73
+ }
74
+ case 'get_keyframes': {
75
+ const result = await godot.sendCommand('get_track_keyframes', {
76
+ node_path: args.node_path,
77
+ animation_name: args.animation_name,
78
+ track_index: args.track_index,
79
+ });
80
+ return JSON.stringify(result, null, 2);
81
+ }
16
82
  }
17
- return `Found ${result.animation_players.length} AnimationPlayer(s):\n${result.animation_players.map((p) => ` - ${p.path}`).join('\n')}`;
18
83
  },
19
84
  });
20
- export const getAnimationPlayerInfo = defineTool({
21
- name: 'get_animation_player_info',
22
- description: 'Get AnimationPlayer state and available animations',
23
- schema: z.object({
24
- node_path: z.string().describe('Path to AnimationPlayer node'),
25
- }),
26
- async execute({ node_path }, { godot }) {
27
- const result = await godot.sendCommand('get_animation_player_info', { node_path });
28
- return JSON.stringify(result, null, 2);
29
- },
30
- });
31
- export const getAnimationDetails = defineTool({
32
- name: 'get_animation_details',
33
- description: 'Get detailed information about a specific animation',
34
- schema: z.object({
35
- node_path: z.string().describe('Path to AnimationPlayer'),
36
- animation_name: z
37
- .string()
38
- .describe('Name of animation (format: "library/anim" or just "anim")'),
39
- }),
40
- async execute({ node_path, animation_name }, { godot }) {
41
- const result = await godot.sendCommand('get_animation_details', { node_path, animation_name });
42
- return JSON.stringify(result, null, 2);
43
- },
44
- });
45
- export const getTrackKeyframes = defineTool({
46
- name: 'get_track_keyframes',
47
- description: 'Get all keyframes for a specific track',
48
- schema: z.object({
49
- node_path: z.string().describe('Path to AnimationPlayer'),
50
- animation_name: z.string().describe('Animation name'),
51
- track_index: z.number().describe('Track index'),
52
- }),
53
- async execute({ node_path, animation_name, track_index }, { godot }) {
54
- const result = await godot.sendCommand('get_track_keyframes', { node_path, animation_name, track_index });
55
- return JSON.stringify(result, null, 2);
56
- },
57
- });
58
- export const playAnimation = defineTool({
59
- name: 'play_animation',
60
- description: 'Play an animation',
61
- schema: z.object({
62
- node_path: z.string().describe('Path to AnimationPlayer'),
63
- animation_name: z.string().describe('Animation to play'),
64
- custom_blend: z
65
- .number()
66
- .optional()
67
- .describe('Custom blend time (-1 for default)'),
68
- custom_speed: z
69
- .number()
70
- .optional()
71
- .describe('Custom playback speed (1.0 default)'),
72
- from_end: z.boolean().optional().describe('Play from end (for reverse)'),
73
- }),
74
- async execute({ node_path, animation_name, custom_blend, custom_speed, from_end }, { godot }) {
75
- const result = await godot.sendCommand('play_animation', {
76
- node_path,
77
- animation_name,
78
- custom_blend,
79
- custom_speed,
80
- from_end,
81
- });
82
- return `Playing animation: ${result.playing}`;
83
- },
84
- });
85
- export const stopAnimation = defineTool({
86
- name: 'stop_animation',
87
- description: 'Stop current animation',
88
- schema: z.object({
89
- node_path: z.string().describe('Path to AnimationPlayer'),
90
- keep_state: z
91
- .boolean()
92
- .optional()
93
- .describe('Keep current animation state (default false)'),
94
- }),
95
- async execute({ node_path, keep_state }, { godot }) {
96
- await godot.sendCommand('stop_animation', { node_path, keep_state });
97
- return 'Animation stopped';
98
- },
99
- });
100
- export const pauseAnimation = defineTool({
101
- name: 'pause_animation',
102
- description: 'Pause/unpause animation playback',
103
- schema: z.object({
104
- node_path: z.string().describe('Path to AnimationPlayer'),
105
- paused: z.boolean().describe('True to pause, false to unpause'),
106
- }),
107
- async execute({ node_path, paused }, { godot }) {
108
- await godot.sendCommand('pause_animation', { node_path, paused });
109
- return paused ? 'Animation paused' : 'Animation unpaused';
110
- },
111
- });
112
- export const seekAnimation = defineTool({
113
- name: 'seek_animation',
114
- description: 'Seek to a specific position in the animation',
115
- schema: z.object({
116
- node_path: z.string().describe('Path to AnimationPlayer'),
117
- seconds: z.number().describe('Position to seek to'),
118
- update: z
119
- .boolean()
120
- .optional()
121
- .describe('Update node immediately (default true)'),
122
- }),
123
- async execute({ node_path, seconds, update }, { godot }) {
124
- const result = await godot.sendCommand('seek_animation', { node_path, seconds, update });
125
- return `Seeked to position: ${result.position}`;
126
- },
127
- });
128
- export const queueAnimation = defineTool({
129
- name: 'queue_animation',
130
- description: 'Queue an animation to play after current one finishes',
131
- schema: z.object({
132
- node_path: z.string().describe('Path to AnimationPlayer'),
133
- animation_name: z.string().describe('Animation to queue'),
134
- }),
135
- async execute({ node_path, animation_name }, { godot }) {
136
- const result = await godot.sendCommand('queue_animation', { node_path, animation_name });
137
- return `Queued animation: ${result.queued} (queue length: ${result.queue_length})`;
138
- },
139
- });
140
- export const clearAnimationQueue = defineTool({
141
- name: 'clear_animation_queue',
142
- description: 'Clear the animation queue',
143
- schema: z.object({
144
- node_path: z.string().describe('Path to AnimationPlayer'),
145
- }),
146
- async execute({ node_path }, { godot }) {
147
- await godot.sendCommand('clear_animation_queue', { node_path });
148
- return 'Animation queue cleared';
149
- },
150
- });
151
- export const createAnimation = defineTool({
152
- name: 'create_animation',
153
- description: 'Create a new animation',
154
- schema: z.object({
155
- node_path: z.string().describe('Path to AnimationPlayer'),
156
- animation_name: z.string().describe('Name for new animation'),
157
- library_name: z
158
- .string()
159
- .optional()
160
- .describe('Library to add to (default "")'),
161
- length: z
162
- .number()
163
- .optional()
164
- .describe('Animation length in seconds (default 1.0)'),
165
- loop_mode: z
166
- .enum(['none', 'linear', 'pingpong'])
167
- .optional()
168
- .describe('Loop mode (default "none")'),
169
- step: z.number().optional().describe('Step value for keyframe snapping'),
170
- }),
171
- async execute({ node_path, animation_name, library_name, length, loop_mode, step }, { godot }) {
172
- const result = await godot.sendCommand('create_animation', { node_path, animation_name, library_name, length, loop_mode, step });
173
- return `Created animation: ${result.created}${result.library ? ` in library: ${result.library}` : ''}`;
174
- },
175
- });
176
- export const deleteAnimation = defineTool({
177
- name: 'delete_animation',
178
- description: 'Delete an animation',
179
- schema: z.object({
180
- node_path: z.string().describe('Path to AnimationPlayer'),
181
- animation_name: z.string().describe('Animation to delete'),
182
- library_name: z
183
- .string()
184
- .optional()
185
- .describe('Library containing animation'),
186
- }),
187
- async execute({ node_path, animation_name, library_name }, { godot }) {
188
- const result = await godot.sendCommand('delete_animation', { node_path, animation_name, library_name });
189
- return `Deleted animation: ${result.deleted}`;
190
- },
191
- });
192
- export const renameAnimation = defineTool({
193
- name: 'rename_animation',
194
- description: 'Rename an animation',
195
- schema: z.object({
196
- node_path: z.string().describe('Path to AnimationPlayer'),
197
- old_name: z.string().describe('Current animation name'),
198
- new_name: z.string().describe('New animation name'),
199
- library_name: z
200
- .string()
201
- .optional()
202
- .describe('Library containing animation'),
203
- }),
204
- async execute({ node_path, old_name, new_name, library_name }, { godot }) {
205
- const result = await godot.sendCommand('rename_animation', { node_path, old_name, new_name, library_name });
206
- return `Renamed animation: ${result.renamed.from} -> ${result.renamed.to}`;
207
- },
208
- });
209
- export const updateAnimationProperties = defineTool({
210
- name: 'update_animation_properties',
211
- description: 'Update animation properties (length, loop mode, step)',
212
- schema: z.object({
213
- node_path: z.string().describe('Path to AnimationPlayer'),
214
- animation_name: z.string().describe('Animation to update'),
215
- length: z.number().optional().describe('New length'),
216
- loop_mode: z
217
- .enum(['none', 'linear', 'pingpong'])
218
- .optional()
219
- .describe('New loop mode'),
220
- step: z.number().optional().describe('New step value'),
221
- }),
222
- async execute({ node_path, animation_name, length, loop_mode, step }, { godot }) {
223
- const result = await godot.sendCommand('update_animation_properties', {
224
- node_path,
225
- animation_name,
226
- length,
227
- loop_mode,
228
- step,
229
- });
230
- return `Updated animation: ${result.updated}\nProperties: ${JSON.stringify(result.properties)}`;
231
- },
232
- });
233
- export const addAnimationTrack = defineTool({
234
- name: 'add_animation_track',
235
- description: 'Add a new track to an animation',
236
- schema: z.object({
237
- node_path: z.string().describe('Path to AnimationPlayer'),
238
- animation_name: z.string().describe('Animation to modify'),
239
- track_type: z
240
- .enum([
241
- 'value',
242
- 'position_3d',
243
- 'rotation_3d',
244
- 'scale_3d',
245
- 'blend_shape',
246
- 'method',
247
- 'bezier',
248
- 'audio',
249
- 'animation',
250
- ])
251
- .describe('Type of track'),
252
- track_path: z
253
- .string()
254
- .describe('Node path and property (e.g., "Sprite2D:frame")'),
255
- insert_at: z
256
- .number()
257
- .optional()
258
- .describe('Track index to insert at (-1 for end)'),
259
- }),
260
- async execute({ node_path, animation_name, track_type, track_path, insert_at }, { godot }) {
261
- const result = await godot.sendCommand('add_animation_track', {
262
- node_path,
263
- animation_name,
264
- track_type,
265
- track_path,
266
- insert_at,
267
- });
268
- return `Added track ${result.track_index}: ${result.track_type} -> ${result.track_path}`;
269
- },
270
- });
271
- export const removeAnimationTrack = defineTool({
272
- name: 'remove_animation_track',
273
- description: 'Remove a track from an animation',
274
- schema: z.object({
275
- node_path: z.string().describe('Path to AnimationPlayer'),
276
- animation_name: z.string().describe('Animation to modify'),
277
- track_index: z.number().describe('Index of track to remove'),
278
- }),
279
- async execute({ node_path, animation_name, track_index }, { godot }) {
280
- const result = await godot.sendCommand('remove_animation_track', { node_path, animation_name, track_index });
281
- return `Removed track: ${result.removed_track}`;
282
- },
283
- });
284
- export const addKeyframe = defineTool({
285
- name: 'add_keyframe',
286
- description: 'Add a keyframe to a track',
287
- schema: z.object({
288
- node_path: z.string().describe('Path to AnimationPlayer'),
289
- animation_name: z.string().describe('Animation to modify'),
290
- track_index: z.number().describe('Track index'),
291
- time: z.number().describe('Keyframe time in seconds'),
292
- value: z.unknown().describe('Keyframe value (type depends on track type)'),
293
- transition: z
294
- .number()
295
- .optional()
296
- .describe('Transition curve (1.0 = linear)'),
297
- method_name: z
298
- .string()
299
- .optional()
300
- .describe('Method name (for method tracks)'),
301
- args: z.array(z.unknown()).optional().describe('Method arguments'),
302
- }),
303
- async execute({ node_path, animation_name, track_index, time, value, transition, method_name, args, }, { godot }) {
304
- const result = await godot.sendCommand('add_keyframe', {
305
- node_path,
306
- animation_name,
307
- track_index,
308
- time,
309
- value,
310
- transition,
311
- method_name,
312
- args,
313
- });
314
- return `Added keyframe ${result.keyframe_index} at ${result.time}s`;
315
- },
316
- });
317
- export const removeKeyframe = defineTool({
318
- name: 'remove_keyframe',
319
- description: 'Remove a keyframe from a track',
320
- schema: z.object({
321
- node_path: z.string().describe('Path to AnimationPlayer'),
322
- animation_name: z.string().describe('Animation to modify'),
323
- track_index: z.number().describe('Track index'),
324
- keyframe_index: z.number().describe('Index of keyframe to remove'),
325
- }),
326
- async execute({ node_path, animation_name, track_index, keyframe_index }, { godot }) {
327
- const result = await godot.sendCommand('remove_keyframe', {
328
- node_path,
329
- animation_name,
330
- track_index,
331
- keyframe_index,
332
- });
333
- return `Removed keyframe ${result.removed_keyframe} from track ${result.track_index}`;
85
+ const AnimationPlaybackSchema = z
86
+ .object({
87
+ action: z
88
+ .enum(['play', 'stop', 'pause', 'seek', 'queue', 'clear_queue'])
89
+ .describe('Action: play, stop, pause, seek, queue, clear_queue'),
90
+ node_path: z.string().describe('Path to AnimationPlayer'),
91
+ animation_name: z.string().optional().describe('Animation name (play, queue)'),
92
+ custom_blend: z.number().optional().describe('Custom blend time, -1 for default (play)'),
93
+ custom_speed: z.number().optional().describe('Playback speed, 1.0 default (play)'),
94
+ from_end: z.boolean().optional().describe('Play from end for reverse (play)'),
95
+ keep_state: z.boolean().optional().describe('Keep current animation state (stop)'),
96
+ paused: z.boolean().optional().describe('True to pause, false to unpause (pause)'),
97
+ seconds: z.number().optional().describe('Position to seek to (seek)'),
98
+ update: z.boolean().optional().describe('Update node immediately, default true (seek)'),
99
+ })
100
+ .refine((data) => {
101
+ switch (data.action) {
102
+ case 'play':
103
+ case 'queue':
104
+ return !!data.animation_name;
105
+ case 'stop':
106
+ case 'clear_queue':
107
+ return true;
108
+ case 'pause':
109
+ return data.paused !== undefined;
110
+ case 'seek':
111
+ return data.seconds !== undefined;
112
+ }
113
+ }, { message: 'Missing required fields for action' });
114
+ export const animationPlayback = defineTool({
115
+ name: 'animation_playback',
116
+ description: 'Control animation playback. Actions: play, stop, pause, seek, queue, clear_queue',
117
+ schema: AnimationPlaybackSchema,
118
+ async execute(args, { godot }) {
119
+ switch (args.action) {
120
+ case 'play': {
121
+ const result = await godot.sendCommand('play_animation', {
122
+ node_path: args.node_path,
123
+ animation_name: args.animation_name,
124
+ custom_blend: args.custom_blend,
125
+ custom_speed: args.custom_speed,
126
+ from_end: args.from_end,
127
+ });
128
+ return `Playing animation: ${result.playing}`;
129
+ }
130
+ case 'stop': {
131
+ await godot.sendCommand('stop_animation', {
132
+ node_path: args.node_path,
133
+ keep_state: args.keep_state,
134
+ });
135
+ return 'Animation stopped';
136
+ }
137
+ case 'pause': {
138
+ await godot.sendCommand('pause_animation', {
139
+ node_path: args.node_path,
140
+ paused: args.paused,
141
+ });
142
+ return args.paused ? 'Animation paused' : 'Animation unpaused';
143
+ }
144
+ case 'seek': {
145
+ const result = await godot.sendCommand('seek_animation', {
146
+ node_path: args.node_path,
147
+ seconds: args.seconds,
148
+ update: args.update,
149
+ });
150
+ return `Seeked to position: ${result.position}`;
151
+ }
152
+ case 'queue': {
153
+ const result = await godot.sendCommand('queue_animation', {
154
+ node_path: args.node_path,
155
+ animation_name: args.animation_name,
156
+ });
157
+ return `Queued animation: ${result.queued} (queue length: ${result.queue_length})`;
158
+ }
159
+ case 'clear_queue': {
160
+ await godot.sendCommand('clear_animation_queue', { node_path: args.node_path });
161
+ return 'Animation queue cleared';
162
+ }
163
+ }
334
164
  },
335
165
  });
336
- export const updateKeyframe = defineTool({
337
- name: 'update_keyframe',
338
- description: "Update an existing keyframe's value or time",
339
- schema: z.object({
340
- node_path: z.string().describe('Path to AnimationPlayer'),
341
- animation_name: z.string().describe('Animation to modify'),
342
- track_index: z.number().describe('Track index'),
343
- keyframe_index: z.number().describe('Keyframe index'),
344
- time: z.number().optional().describe('New time'),
345
- value: z.unknown().optional().describe('New value'),
346
- transition: z.number().optional().describe('New transition curve'),
347
- }),
348
- async execute({ node_path, animation_name, track_index, keyframe_index, time, value, transition, }, { godot }) {
349
- const result = await godot.sendCommand('update_keyframe', {
350
- node_path,
351
- animation_name,
352
- track_index,
353
- keyframe_index,
354
- time,
355
- value,
356
- transition,
357
- });
358
- return `Updated keyframe ${result.updated_keyframe}: ${JSON.stringify(result.changes)}`;
166
+ const LoopModeEnum = z.enum(['none', 'linear', 'pingpong']);
167
+ const TrackTypeEnum = z.enum([
168
+ 'value',
169
+ 'position_3d',
170
+ 'rotation_3d',
171
+ 'scale_3d',
172
+ 'blend_shape',
173
+ 'method',
174
+ 'bezier',
175
+ 'audio',
176
+ 'animation',
177
+ ]);
178
+ const AnimationEditSchema = z
179
+ .object({
180
+ action: z
181
+ .enum([
182
+ 'create',
183
+ 'delete',
184
+ 'rename',
185
+ 'update_props',
186
+ 'add_track',
187
+ 'remove_track',
188
+ 'add_keyframe',
189
+ 'remove_keyframe',
190
+ 'update_keyframe',
191
+ ])
192
+ .describe('Action: create, delete, rename, update_props, add_track, remove_track, add_keyframe, remove_keyframe, update_keyframe'),
193
+ node_path: z.string().describe('Path to AnimationPlayer'),
194
+ animation_name: z.string().optional().describe('Animation name (most actions)'),
195
+ library_name: z.string().optional().describe('Library name (create, delete, rename)'),
196
+ length: z.number().optional().describe('Animation length in seconds (create, update_props)'),
197
+ loop_mode: LoopModeEnum.optional().describe('Loop mode: none, linear, pingpong (create, update_props)'),
198
+ step: z.number().optional().describe('Step value for keyframe snapping (create, update_props)'),
199
+ old_name: z.string().optional().describe('Current animation name (rename)'),
200
+ new_name: z.string().optional().describe('New animation name (rename)'),
201
+ track_type: TrackTypeEnum.optional().describe('Type of track (add_track)'),
202
+ track_path: z.string().optional().describe('Node path and property, e.g. "Sprite2D:frame" (add_track)'),
203
+ insert_at: z.number().optional().describe('Track index to insert at, -1 for end (add_track)'),
204
+ track_index: z.number().optional().describe('Track index (remove_track, add/remove/update_keyframe)'),
205
+ time: z.number().optional().describe('Keyframe time in seconds (add_keyframe, update_keyframe)'),
206
+ value: z.unknown().optional().describe('Keyframe value (add_keyframe, update_keyframe)'),
207
+ transition: z.number().optional().describe('Transition curve, 1.0 = linear (add_keyframe, update_keyframe)'),
208
+ method_name: z.string().optional().describe('Method name for method tracks (add_keyframe)'),
209
+ args: z.array(z.unknown()).optional().describe('Method arguments (add_keyframe)'),
210
+ keyframe_index: z.number().optional().describe('Keyframe index (remove_keyframe, update_keyframe)'),
211
+ })
212
+ .refine((data) => {
213
+ switch (data.action) {
214
+ case 'create':
215
+ return !!data.animation_name;
216
+ case 'delete':
217
+ return !!data.animation_name;
218
+ case 'rename':
219
+ return !!data.old_name && !!data.new_name;
220
+ case 'update_props':
221
+ return !!data.animation_name;
222
+ case 'add_track':
223
+ return !!data.animation_name && !!data.track_type && !!data.track_path;
224
+ case 'remove_track':
225
+ return !!data.animation_name && data.track_index !== undefined;
226
+ case 'add_keyframe':
227
+ return !!data.animation_name && data.track_index !== undefined && data.time !== undefined;
228
+ case 'remove_keyframe':
229
+ return (!!data.animation_name && data.track_index !== undefined && data.keyframe_index !== undefined);
230
+ case 'update_keyframe':
231
+ return (!!data.animation_name && data.track_index !== undefined && data.keyframe_index !== undefined);
232
+ }
233
+ }, { message: 'Missing required fields for action' });
234
+ export const animationEdit = defineTool({
235
+ name: 'animation_edit',
236
+ description: 'Edit animations. Actions: create, delete, rename, update_props, add_track, remove_track, add_keyframe, remove_keyframe, update_keyframe',
237
+ schema: AnimationEditSchema,
238
+ async execute(args, { godot }) {
239
+ switch (args.action) {
240
+ case 'create': {
241
+ const result = await godot.sendCommand('create_animation', {
242
+ node_path: args.node_path,
243
+ animation_name: args.animation_name,
244
+ library_name: args.library_name,
245
+ length: args.length,
246
+ loop_mode: args.loop_mode,
247
+ step: args.step,
248
+ });
249
+ return `Created animation: ${result.created}${result.library ? ` in library: ${result.library}` : ''}`;
250
+ }
251
+ case 'delete': {
252
+ const result = await godot.sendCommand('delete_animation', {
253
+ node_path: args.node_path,
254
+ animation_name: args.animation_name,
255
+ library_name: args.library_name,
256
+ });
257
+ return `Deleted animation: ${result.deleted}`;
258
+ }
259
+ case 'rename': {
260
+ const result = await godot.sendCommand('rename_animation', {
261
+ node_path: args.node_path,
262
+ old_name: args.old_name,
263
+ new_name: args.new_name,
264
+ library_name: args.library_name,
265
+ });
266
+ return `Renamed animation: ${result.renamed.from} -> ${result.renamed.to}`;
267
+ }
268
+ case 'update_props': {
269
+ const result = await godot.sendCommand('update_animation_properties', {
270
+ node_path: args.node_path,
271
+ animation_name: args.animation_name,
272
+ length: args.length,
273
+ loop_mode: args.loop_mode,
274
+ step: args.step,
275
+ });
276
+ return `Updated animation: ${result.updated}\nProperties: ${JSON.stringify(result.properties)}`;
277
+ }
278
+ case 'add_track': {
279
+ const result = await godot.sendCommand('add_animation_track', {
280
+ node_path: args.node_path,
281
+ animation_name: args.animation_name,
282
+ track_type: args.track_type,
283
+ track_path: args.track_path,
284
+ insert_at: args.insert_at,
285
+ });
286
+ return `Added track ${result.track_index}: ${result.track_type} -> ${result.track_path}`;
287
+ }
288
+ case 'remove_track': {
289
+ const result = await godot.sendCommand('remove_animation_track', {
290
+ node_path: args.node_path,
291
+ animation_name: args.animation_name,
292
+ track_index: args.track_index,
293
+ });
294
+ return `Removed track: ${result.removed_track}`;
295
+ }
296
+ case 'add_keyframe': {
297
+ const result = await godot.sendCommand('add_keyframe', {
298
+ node_path: args.node_path,
299
+ animation_name: args.animation_name,
300
+ track_index: args.track_index,
301
+ time: args.time,
302
+ value: args.value,
303
+ transition: args.transition,
304
+ method_name: args.method_name,
305
+ args: args.args,
306
+ });
307
+ return `Added keyframe ${result.keyframe_index} at ${result.time}s`;
308
+ }
309
+ case 'remove_keyframe': {
310
+ const result = await godot.sendCommand('remove_keyframe', {
311
+ node_path: args.node_path,
312
+ animation_name: args.animation_name,
313
+ track_index: args.track_index,
314
+ keyframe_index: args.keyframe_index,
315
+ });
316
+ return `Removed keyframe ${result.removed_keyframe} from track ${result.track_index}`;
317
+ }
318
+ case 'update_keyframe': {
319
+ const result = await godot.sendCommand('update_keyframe', {
320
+ node_path: args.node_path,
321
+ animation_name: args.animation_name,
322
+ track_index: args.track_index,
323
+ keyframe_index: args.keyframe_index,
324
+ time: args.time,
325
+ value: args.value,
326
+ transition: args.transition,
327
+ });
328
+ return `Updated keyframe ${result.updated_keyframe}: ${JSON.stringify(result.changes)}`;
329
+ }
330
+ }
359
331
  },
360
332
  });
361
- export const animationTools = [
362
- listAnimationPlayers,
363
- getAnimationPlayerInfo,
364
- getAnimationDetails,
365
- getTrackKeyframes,
366
- playAnimation,
367
- stopAnimation,
368
- pauseAnimation,
369
- seekAnimation,
370
- queueAnimation,
371
- clearAnimationQueue,
372
- createAnimation,
373
- deleteAnimation,
374
- renameAnimation,
375
- updateAnimationProperties,
376
- addAnimationTrack,
377
- removeAnimationTrack,
378
- addKeyframe,
379
- removeKeyframe,
380
- updateKeyframe,
381
- ];
333
+ export const animationTools = [animationQuery, animationPlayback, animationEdit];
382
334
  //# sourceMappingURL=animation.js.map