unreal-engine-mcp-server 0.5.0 → 0.5.2

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 (188) hide show
  1. package/.env.example +1 -1
  2. package/.github/release-drafter-config.yml +51 -0
  3. package/.github/workflows/greetings.yml +5 -1
  4. package/.github/workflows/labeler.yml +2 -1
  5. package/.github/workflows/publish-mcp.yml +2 -4
  6. package/.github/workflows/release-drafter.yml +3 -2
  7. package/.github/workflows/release.yml +3 -3
  8. package/CHANGELOG.md +109 -0
  9. package/CONTRIBUTING.md +1 -1
  10. package/GEMINI.md +115 -0
  11. package/Public/Plugin_setup_guide.mp4 +0 -0
  12. package/README.md +166 -200
  13. package/dist/automation/bridge.d.ts +1 -2
  14. package/dist/automation/bridge.js +24 -23
  15. package/dist/automation/connection-manager.d.ts +1 -0
  16. package/dist/automation/connection-manager.js +10 -0
  17. package/dist/automation/message-handler.js +5 -4
  18. package/dist/automation/request-tracker.d.ts +4 -0
  19. package/dist/automation/request-tracker.js +11 -3
  20. package/dist/config.d.ts +0 -1
  21. package/dist/config.js +0 -1
  22. package/dist/constants.d.ts +4 -0
  23. package/dist/constants.js +4 -0
  24. package/dist/graphql/loaders.d.ts +64 -0
  25. package/dist/graphql/loaders.js +117 -0
  26. package/dist/graphql/resolvers.d.ts +3 -3
  27. package/dist/graphql/resolvers.js +33 -30
  28. package/dist/graphql/server.js +3 -1
  29. package/dist/graphql/types.d.ts +2 -0
  30. package/dist/index.d.ts +2 -0
  31. package/dist/index.js +13 -2
  32. package/dist/server-setup.d.ts +0 -1
  33. package/dist/server-setup.js +0 -40
  34. package/dist/tools/actors.d.ts +58 -24
  35. package/dist/tools/actors.js +22 -6
  36. package/dist/tools/assets.d.ts +19 -71
  37. package/dist/tools/assets.js +28 -22
  38. package/dist/tools/base-tool.d.ts +4 -4
  39. package/dist/tools/base-tool.js +1 -1
  40. package/dist/tools/blueprint.d.ts +45 -61
  41. package/dist/tools/blueprint.js +43 -14
  42. package/dist/tools/consolidated-tool-definitions.js +2 -1
  43. package/dist/tools/consolidated-tool-handlers.js +96 -110
  44. package/dist/tools/dynamic-handler-registry.d.ts +11 -9
  45. package/dist/tools/dynamic-handler-registry.js +17 -95
  46. package/dist/tools/editor.d.ts +19 -193
  47. package/dist/tools/editor.js +11 -2
  48. package/dist/tools/environment.d.ts +8 -14
  49. package/dist/tools/foliage.d.ts +18 -143
  50. package/dist/tools/foliage.js +4 -2
  51. package/dist/tools/handlers/actor-handlers.d.ts +1 -1
  52. package/dist/tools/handlers/actor-handlers.js +14 -13
  53. package/dist/tools/handlers/asset-handlers.js +454 -454
  54. package/dist/tools/handlers/sequence-handlers.d.ts +1 -1
  55. package/dist/tools/handlers/sequence-handlers.js +24 -13
  56. package/dist/tools/introspection.d.ts +1 -1
  57. package/dist/tools/introspection.js +1 -1
  58. package/dist/tools/landscape.d.ts +16 -116
  59. package/dist/tools/landscape.js +7 -3
  60. package/dist/tools/level.d.ts +22 -103
  61. package/dist/tools/level.js +26 -18
  62. package/dist/tools/lighting.d.ts +54 -7
  63. package/dist/tools/lighting.js +9 -5
  64. package/dist/tools/materials.d.ts +1 -1
  65. package/dist/tools/materials.js +5 -1
  66. package/dist/tools/niagara.js +37 -2
  67. package/dist/tools/performance.d.ts +0 -1
  68. package/dist/tools/performance.js +0 -1
  69. package/dist/tools/physics.js +5 -1
  70. package/dist/tools/sequence.d.ts +24 -24
  71. package/dist/tools/sequence.js +13 -0
  72. package/dist/tools/ui.d.ts +0 -2
  73. package/dist/types/automation-responses.d.ts +115 -0
  74. package/dist/types/automation-responses.js +2 -0
  75. package/dist/types/responses.d.ts +249 -0
  76. package/dist/types/responses.js +2 -0
  77. package/dist/types/tool-interfaces.d.ts +135 -135
  78. package/dist/types/tool-types.d.ts +2 -0
  79. package/dist/unreal-bridge.js +4 -4
  80. package/dist/utils/command-validator.js +7 -5
  81. package/dist/utils/error-handler.d.ts +24 -2
  82. package/dist/utils/error-handler.js +58 -23
  83. package/dist/utils/normalize.d.ts +7 -4
  84. package/dist/utils/normalize.js +12 -10
  85. package/dist/utils/path-security.d.ts +2 -0
  86. package/dist/utils/path-security.js +24 -0
  87. package/dist/utils/response-factory.d.ts +4 -4
  88. package/dist/utils/response-factory.js +15 -21
  89. package/dist/utils/response-validator.js +88 -73
  90. package/dist/utils/unreal-command-queue.d.ts +2 -0
  91. package/dist/utils/unreal-command-queue.js +8 -1
  92. package/docs/Migration-Guide-v0.5.0.md +1 -9
  93. package/docs/handler-mapping.md +4 -2
  94. package/docs/testing-guide.md +2 -2
  95. package/package.json +12 -6
  96. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +298 -33
  97. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +7 -8
  98. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +229 -319
  99. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +98 -0
  100. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +24 -0
  101. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +96 -0
  102. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +52 -5
  103. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +5 -268
  104. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +57 -2
  105. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +0 -1
  106. package/scripts/run-all-tests.mjs +25 -20
  107. package/server.json +3 -2
  108. package/src/automation/bridge.ts +27 -25
  109. package/src/automation/connection-manager.ts +18 -0
  110. package/src/automation/message-handler.ts +33 -8
  111. package/src/automation/request-tracker.ts +39 -7
  112. package/src/config.ts +1 -1
  113. package/src/constants.ts +7 -0
  114. package/src/graphql/loaders.ts +244 -0
  115. package/src/graphql/resolvers.ts +47 -49
  116. package/src/graphql/server.ts +3 -1
  117. package/src/graphql/types.ts +3 -0
  118. package/src/index.ts +15 -2
  119. package/src/resources/assets.ts +5 -4
  120. package/src/server/tool-registry.ts +3 -3
  121. package/src/server-setup.ts +3 -37
  122. package/src/tools/actors.ts +77 -44
  123. package/src/tools/animation.ts +1 -0
  124. package/src/tools/assets.ts +76 -65
  125. package/src/tools/base-tool.ts +3 -3
  126. package/src/tools/blueprint.ts +170 -104
  127. package/src/tools/consolidated-tool-definitions.ts +2 -1
  128. package/src/tools/consolidated-tool-handlers.ts +129 -150
  129. package/src/tools/dynamic-handler-registry.ts +22 -140
  130. package/src/tools/editor.ts +43 -29
  131. package/src/tools/environment.ts +21 -27
  132. package/src/tools/foliage.ts +28 -25
  133. package/src/tools/handlers/actor-handlers.ts +16 -17
  134. package/src/tools/handlers/asset-handlers.ts +484 -484
  135. package/src/tools/handlers/sequence-handlers.ts +85 -62
  136. package/src/tools/introspection.ts +7 -7
  137. package/src/tools/landscape.ts +34 -28
  138. package/src/tools/level.ts +100 -80
  139. package/src/tools/lighting.ts +25 -20
  140. package/src/tools/materials.ts +9 -3
  141. package/src/tools/niagara.ts +44 -2
  142. package/src/tools/performance.ts +1 -2
  143. package/src/tools/physics.ts +7 -1
  144. package/src/tools/sequence.ts +42 -26
  145. package/src/tools/ui.ts +1 -3
  146. package/src/types/automation-responses.ts +119 -0
  147. package/src/types/responses.ts +355 -0
  148. package/src/types/tool-interfaces.ts +135 -135
  149. package/src/types/tool-types.ts +4 -0
  150. package/src/unreal-bridge.ts +71 -26
  151. package/src/utils/command-validator.ts +47 -5
  152. package/src/utils/error-handler.ts +128 -45
  153. package/src/utils/normalize.test.ts +162 -0
  154. package/src/utils/normalize.ts +38 -16
  155. package/src/utils/path-security.ts +43 -0
  156. package/src/utils/response-factory.ts +29 -24
  157. package/src/utils/response-validator.ts +103 -87
  158. package/src/utils/safe-json.test.ts +90 -0
  159. package/src/utils/unreal-command-queue.ts +13 -1
  160. package/src/utils/validation.test.ts +184 -0
  161. package/tests/test-animation.mjs +358 -33
  162. package/tests/test-asset-graph.mjs +311 -0
  163. package/tests/test-audio.mjs +314 -116
  164. package/tests/test-behavior-tree.mjs +327 -144
  165. package/tests/test-blueprint-graph.mjs +343 -12
  166. package/tests/test-control-editor.mjs +85 -53
  167. package/tests/test-graphql.mjs +58 -8
  168. package/tests/test-input.mjs +349 -0
  169. package/tests/test-inspect.mjs +291 -61
  170. package/tests/test-landscape.mjs +304 -48
  171. package/tests/test-lighting.mjs +428 -0
  172. package/tests/test-manage-level.mjs +70 -51
  173. package/tests/test-performance.mjs +539 -0
  174. package/tests/test-sequence.mjs +82 -46
  175. package/tests/test-system.mjs +72 -33
  176. package/tests/test-wasm.mjs +98 -8
  177. package/vitest.config.ts +35 -0
  178. package/.github/release-drafter.yml +0 -148
  179. package/dist/prompts/index.d.ts +0 -21
  180. package/dist/prompts/index.js +0 -217
  181. package/dist/tools/blueprint/helpers.d.ts +0 -29
  182. package/dist/tools/blueprint/helpers.js +0 -182
  183. package/src/prompts/index.ts +0 -249
  184. package/src/tools/blueprint/helpers.ts +0 -189
  185. package/tests/test-blueprint-events.mjs +0 -35
  186. package/tests/test-extra-tools.mjs +0 -38
  187. package/tests/test-render.mjs +0 -33
  188. package/tests/test-search-assets.mjs +0 -66
@@ -1,17 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Blueprint Graph Test Suite
4
- * Tools: manage_blueprint, manage_blueprint_graph
3
+ * Comprehensive Blueprint Graph Test Suite
4
+ * Tool: manage_blueprint_graph
5
+ * Coverage: All 9 actions with success, error, and edge cases
5
6
  */
6
7
 
7
8
  import { runToolTests } from './test-runner.mjs';
8
9
 
9
10
  // Use a unique blueprint name per run to avoid collisions
10
11
  const ts = new Date().toISOString().replace(/[^0-9]/g, '').slice(0, 12);
11
- const BP_NAME = `BP_Graph_${ts}`;
12
+ const BP_NAME = `BP_GraphFull_${ts}`;
12
13
  const BP_PATH = `/Game/Blueprints/${BP_NAME}`;
13
14
 
14
15
  const testCases = [
16
+ // === SETUP ===
15
17
  {
16
18
  scenario: 'Create blueprint for graph tests',
17
19
  toolName: 'manage_blueprint',
@@ -25,7 +27,73 @@ const testCases = [
25
27
  expected: 'success'
26
28
  },
27
29
  {
28
- scenario: 'Create Literal object node via manage_blueprint_graph',
30
+ scenario: 'Add variable for node tests',
31
+ toolName: 'manage_blueprint',
32
+ arguments: {
33
+ action: 'add_variable',
34
+ name: BP_PATH,
35
+ variableName: 'TestFloat',
36
+ variableType: 'Float',
37
+ defaultValue: 0.0
38
+ },
39
+ expected: 'success'
40
+ },
41
+ {
42
+ scenario: 'Add second variable',
43
+ toolName: 'manage_blueprint',
44
+ arguments: {
45
+ action: 'add_variable',
46
+ name: BP_PATH,
47
+ variableName: 'TestBool',
48
+ variableType: 'Bool',
49
+ defaultValue: false
50
+ },
51
+ expected: 'success'
52
+ },
53
+
54
+ // === GET GRAPH DETAILS ===
55
+ {
56
+ scenario: 'Get EventGraph details',
57
+ toolName: 'manage_blueprint_graph',
58
+ arguments: {
59
+ action: 'get_graph_details',
60
+ blueprintPath: BP_PATH,
61
+ graphName: 'EventGraph'
62
+ },
63
+ expected: 'success'
64
+ },
65
+
66
+ // === CREATE NODES ===
67
+ {
68
+ scenario: 'Create VariableGet node',
69
+ toolName: 'manage_blueprint_graph',
70
+ arguments: {
71
+ action: 'create_node',
72
+ blueprintPath: BP_PATH,
73
+ graphName: 'EventGraph',
74
+ nodeType: 'VariableGet',
75
+ memberName: 'TestFloat',
76
+ x: 0,
77
+ y: 0
78
+ },
79
+ expected: 'success'
80
+ },
81
+ {
82
+ scenario: 'Create VariableSet node',
83
+ toolName: 'manage_blueprint_graph',
84
+ arguments: {
85
+ action: 'create_node',
86
+ blueprintPath: BP_PATH,
87
+ graphName: 'EventGraph',
88
+ nodeType: 'VariableSet',
89
+ memberName: 'TestFloat',
90
+ x: 200,
91
+ y: 0
92
+ },
93
+ expected: 'success'
94
+ },
95
+ {
96
+ scenario: 'Create Literal node (object)',
29
97
  toolName: 'manage_blueprint_graph',
30
98
  arguments: {
31
99
  action: 'create_node',
@@ -35,10 +103,255 @@ const testCases = [
35
103
  literalType: 'object',
36
104
  objectPath: '/Engine/BasicShapes/Cube.Cube',
37
105
  x: 0,
106
+ y: 100
107
+ },
108
+ expected: 'success'
109
+ },
110
+ {
111
+ scenario: 'Create CallFunction node',
112
+ toolName: 'manage_blueprint_graph',
113
+ arguments: {
114
+ action: 'create_node',
115
+ blueprintPath: BP_PATH,
116
+ graphName: 'EventGraph',
117
+ nodeType: 'CallFunction',
118
+ memberName: 'PrintString',
119
+ x: 300,
120
+ y: 0
121
+ },
122
+ expected: 'success'
123
+ },
124
+ {
125
+ scenario: 'Create Event node (BeginPlay)',
126
+ toolName: 'manage_blueprint_graph',
127
+ arguments: {
128
+ action: 'create_node',
129
+ blueprintPath: BP_PATH,
130
+ graphName: 'EventGraph',
131
+ nodeType: 'Event',
132
+ memberName: 'ReceiveBeginPlay',
133
+ x: -200,
134
+ y: 0
135
+ },
136
+ expected: 'success'
137
+ },
138
+ {
139
+ scenario: 'Create Branch node',
140
+ toolName: 'manage_blueprint_graph',
141
+ arguments: {
142
+ action: 'create_node',
143
+ blueprintPath: BP_PATH,
144
+ graphName: 'EventGraph',
145
+ nodeType: 'Branch',
146
+ x: 100,
147
+ y: 150
148
+ },
149
+ expected: 'success'
150
+ },
151
+ {
152
+ scenario: 'Create Sequence node',
153
+ toolName: 'manage_blueprint_graph',
154
+ arguments: {
155
+ action: 'create_node',
156
+ blueprintPath: BP_PATH,
157
+ graphName: 'EventGraph',
158
+ nodeType: 'Sequence',
159
+ x: 200,
160
+ y: 150
161
+ },
162
+ expected: 'success'
163
+ },
164
+ {
165
+ scenario: 'Create ForEachLoop node',
166
+ toolName: 'manage_blueprint_graph',
167
+ arguments: {
168
+ action: 'create_node',
169
+ blueprintPath: BP_PATH,
170
+ graphName: 'EventGraph',
171
+ nodeType: 'ForEachLoop',
172
+ x: 300,
173
+ y: 150
174
+ },
175
+ expected: 'success'
176
+ },
177
+ {
178
+ scenario: 'Create Delay node',
179
+ toolName: 'manage_blueprint_graph',
180
+ arguments: {
181
+ action: 'create_node',
182
+ blueprintPath: BP_PATH,
183
+ graphName: 'EventGraph',
184
+ nodeType: 'Delay',
185
+ x: 400,
186
+ y: 0
187
+ },
188
+ expected: 'success'
189
+ },
190
+ {
191
+ scenario: 'Create SpawnActor node',
192
+ toolName: 'manage_blueprint_graph',
193
+ arguments: {
194
+ action: 'create_node',
195
+ blueprintPath: BP_PATH,
196
+ graphName: 'EventGraph',
197
+ nodeType: 'SpawnActor',
198
+ x: 500,
38
199
  y: 0
39
200
  },
40
201
  expected: 'success'
41
202
  },
203
+
204
+ // === CREATE REROUTE NODE ===
205
+ {
206
+ scenario: 'Create Reroute node',
207
+ toolName: 'manage_blueprint_graph',
208
+ arguments: {
209
+ action: 'create_reroute_node',
210
+ blueprintPath: BP_PATH,
211
+ graphName: 'EventGraph',
212
+ x: 150,
213
+ y: 50
214
+ },
215
+ expected: 'success'
216
+ },
217
+
218
+ // === GET NODE DETAILS ===
219
+ {
220
+ scenario: 'Get node details',
221
+ toolName: 'manage_blueprint_graph',
222
+ arguments: {
223
+ action: 'get_node_details',
224
+ blueprintPath: BP_PATH,
225
+ graphName: 'EventGraph'
226
+ },
227
+ expected: 'success'
228
+ },
229
+
230
+ // === GET PIN DETAILS ===
231
+ {
232
+ scenario: 'Get pin details',
233
+ toolName: 'manage_blueprint_graph',
234
+ arguments: {
235
+ action: 'get_pin_details',
236
+ blueprintPath: BP_PATH,
237
+ graphName: 'EventGraph'
238
+ },
239
+ expected: 'success'
240
+ },
241
+
242
+ // === SET NODE PROPERTY ===
243
+ {
244
+ scenario: 'Set Delay duration',
245
+ toolName: 'manage_blueprint_graph',
246
+ arguments: {
247
+ action: 'set_node_property',
248
+ blueprintPath: BP_PATH,
249
+ graphName: 'EventGraph',
250
+ nodeId: 'Delay_0',
251
+ propertyName: 'Duration',
252
+ value: 2.0
253
+ },
254
+ expected: 'success|not_found'
255
+ },
256
+
257
+ // === CONNECT PINS ===
258
+ {
259
+ scenario: 'Connect BeginPlay to SetVariable',
260
+ toolName: 'manage_blueprint_graph',
261
+ arguments: {
262
+ action: 'connect_pins',
263
+ blueprintPath: BP_PATH,
264
+ graphName: 'EventGraph',
265
+ fromPin: 'ReceiveBeginPlay.Execute',
266
+ linkedTo: 'SetTestFloat.Execute'
267
+ },
268
+ expected: 'success|not_found'
269
+ },
270
+
271
+ // === BREAK PIN LINKS ===
272
+ {
273
+ scenario: 'Break pin links',
274
+ toolName: 'manage_blueprint_graph',
275
+ arguments: {
276
+ action: 'break_pin_links',
277
+ blueprintPath: BP_PATH,
278
+ graphName: 'EventGraph',
279
+ pinName: 'SetTestFloat.Execute'
280
+ },
281
+ expected: 'success|not_found'
282
+ },
283
+
284
+ // === DELETE NODE ===
285
+ {
286
+ scenario: 'Delete ForEachLoop node',
287
+ toolName: 'manage_blueprint_graph',
288
+ arguments: {
289
+ action: 'delete_node',
290
+ blueprintPath: BP_PATH,
291
+ graphName: 'EventGraph',
292
+ nodeId: 'ForEachLoop_0'
293
+ },
294
+ expected: 'success|not_found'
295
+ },
296
+
297
+ // === ERROR CASES ===
298
+ {
299
+ scenario: 'Error: Get invalid graph details',
300
+ toolName: 'manage_blueprint_graph',
301
+ arguments: {
302
+ action: 'get_graph_details',
303
+ blueprintPath: BP_PATH,
304
+ graphName: 'InvalidGraphName_DOES_NOT_EXIST'
305
+ },
306
+ expected: 'error|graph_not_found'
307
+ },
308
+ {
309
+ scenario: 'Error: Create node in invalid graph',
310
+ toolName: 'manage_blueprint_graph',
311
+ arguments: {
312
+ action: 'create_node',
313
+ blueprintPath: BP_PATH,
314
+ graphName: 'NonExistentGraph',
315
+ nodeType: 'VariableGet',
316
+ memberName: 'TestFloat'
317
+ },
318
+ expected: 'error|graph_not_found'
319
+ },
320
+ {
321
+ scenario: 'Error: Connect invalid pins',
322
+ toolName: 'manage_blueprint_graph',
323
+ arguments: {
324
+ action: 'connect_pins',
325
+ blueprintPath: BP_PATH,
326
+ graphName: 'EventGraph',
327
+ fromPin: 'Invalid.Pin',
328
+ linkedTo: 'Also.Invalid'
329
+ },
330
+ expected: 'error|not_found'
331
+ },
332
+ {
333
+ scenario: 'Error: Delete non-existent node',
334
+ toolName: 'manage_blueprint_graph',
335
+ arguments: {
336
+ action: 'delete_node',
337
+ blueprintPath: BP_PATH,
338
+ graphName: 'EventGraph',
339
+ nodeId: 'NonExistentNode_XYZ'
340
+ },
341
+ expected: 'error|not_found'
342
+ },
343
+ {
344
+ scenario: 'Error: Create node in invalid blueprint',
345
+ toolName: 'manage_blueprint_graph',
346
+ arguments: {
347
+ action: 'create_node',
348
+ blueprintPath: '/Game/Blueprints/DOES_NOT_EXIST',
349
+ graphName: 'EventGraph',
350
+ nodeType: 'VariableGet',
351
+ memberName: 'TestFloat'
352
+ },
353
+ expected: 'error|not_found'
354
+ },
42
355
  {
43
356
  scenario: 'Error: Literal object path not found',
44
357
  toolName: 'manage_blueprint_graph',
@@ -54,26 +367,44 @@ const testCases = [
54
367
  },
55
368
  expected: 'error|object_not_found'
56
369
  },
370
+
371
+ // === EDGE CASES ===
57
372
  {
58
- scenario: 'Get graph details via manage_blueprint_graph',
373
+ scenario: 'Edge: Negative node position',
59
374
  toolName: 'manage_blueprint_graph',
60
375
  arguments: {
61
- action: 'get_graph_details',
376
+ action: 'create_node',
62
377
  blueprintPath: BP_PATH,
63
- graphName: 'EventGraph'
378
+ graphName: 'EventGraph',
379
+ nodeType: 'VariableGet',
380
+ memberName: 'TestFloat',
381
+ x: -1000,
382
+ y: -1000
64
383
  },
65
384
  expected: 'success'
66
385
  },
67
386
  {
68
- scenario: 'Error: Graph not found for manage_blueprint_graph',
387
+ scenario: 'Edge: Very large node position',
69
388
  toolName: 'manage_blueprint_graph',
70
389
  arguments: {
71
- action: 'get_graph_details',
390
+ action: 'create_node',
72
391
  blueprintPath: BP_PATH,
73
- graphName: 'InvalidGraphName_DOES_NOT_EXIST'
392
+ graphName: 'EventGraph',
393
+ nodeType: 'VariableGet',
394
+ memberName: 'TestFloat',
395
+ x: 10000,
396
+ y: 10000
74
397
  },
75
- expected: 'error|graph_not_found'
398
+ expected: 'success'
399
+ },
400
+
401
+ // === CLEANUP ===
402
+ {
403
+ scenario: 'Cleanup: Delete test blueprint',
404
+ toolName: 'manage_asset',
405
+ arguments: { action: 'delete', assetPaths: [BP_PATH] },
406
+ expected: 'success'
76
407
  }
77
408
  ];
78
409
 
79
- await runToolTests('BlueprintGraph', testCases);
410
+ await runToolTests('Blueprint Graph Full', testCases);
@@ -1,80 +1,112 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Condensed Editor Control Test Suite (15 cases) — safe for real Editor runs.
3
+ * Comprehensive Editor Control Test Suite
4
4
  * Tool: control_editor
5
+ * Coverage: All 24 actions with success, error, and edge cases
5
6
  */
6
7
 
7
8
  import { runToolTests } from './test-runner.mjs';
8
9
 
9
10
  const testCases = [
11
+ // === PRE-FIX ===
10
12
  {
11
13
  scenario: 'Pre-Fix: Remove Invalid Data Layers (Native)',
12
14
  toolName: 'manage_level',
13
15
  arguments: {
14
16
  action: 'cleanup_invalid_datalayers'
15
17
  },
16
- // We expect success if supported, or error if not.
17
- // Since we just added it, it should succeed if plugin is rebuilt.
18
- // If plugin is NOT rebuilt yet, this will fail with valid error or "unknown action" depending on how it falls through.
19
- // But since the user is expected to rebuild, we expect success.
20
18
  expected: 'success'
21
19
  },
20
+
21
+ // === PIE CONTROL (play, stop, stop_pie, pause, resume) ===
22
22
  { scenario: 'Start PIE (Play in Editor)', toolName: 'control_editor', arguments: { action: 'play' }, expected: 'success - PIE started' },
23
+ { scenario: 'Pause PIE', toolName: 'control_editor', arguments: { action: 'pause' }, expected: 'success - PIE paused' },
24
+ { scenario: 'Resume PIE', toolName: 'control_editor', arguments: { action: 'resume' }, expected: 'success - PIE resumed' },
23
25
  { scenario: 'Stop PIE', toolName: 'control_editor', arguments: { action: 'stop' }, expected: 'success - PIE stopped' },
24
- { scenario: 'Set camera location', toolName: 'control_editor', arguments: { action: 'set_camera', location: { x: 0, y: 0, z: 500 }, rotation: { pitch: -45, yaw: 0, roll: 0 } }, expected: 'success - camera set' },
26
+ { scenario: 'Stop PIE (alias)', toolName: 'control_editor', arguments: { action: 'stop_pie' }, expected: 'success - PIE stopped' },
27
+
28
+ // === GAME SPEED ===
29
+ { scenario: 'Set game speed 0.5x', toolName: 'control_editor', arguments: { action: 'set_game_speed', speed: 0.5 }, expected: 'success' },
30
+ { scenario: 'Set game speed 2x', toolName: 'control_editor', arguments: { action: 'set_game_speed', speed: 2.0 }, expected: 'success' },
31
+ { scenario: 'Set game speed 1x (reset)', toolName: 'control_editor', arguments: { action: 'set_game_speed', speed: 1.0 }, expected: 'success' },
32
+
33
+ // === EJECT / POSSESS ===
34
+ { scenario: 'Eject from possessed pawn', toolName: 'control_editor', arguments: { action: 'eject' }, expected: 'success|handled' },
35
+ { scenario: 'Possess pawn', toolName: 'control_editor', arguments: { action: 'possess' }, expected: 'success|handled' },
36
+
37
+ // === CAMERA CONTROL ===
38
+ { scenario: 'Set camera location and rotation', toolName: 'control_editor', arguments: { action: 'set_camera', location: { x: 0, y: 0, z: 500 }, rotation: { pitch: -45, yaw: 0, roll: 0 } }, expected: 'success - camera set' },
39
+ { scenario: 'Set camera position only', toolName: 'control_editor', arguments: { action: 'set_camera_position', location: { x: 100, y: 100, z: 300 } }, expected: 'success' },
40
+ { scenario: 'Set camera rotation only', toolName: 'control_editor', arguments: { action: 'set_camera', rotation: { pitch: -30, yaw: 45, roll: 0 } }, expected: 'success' },
25
41
  { scenario: 'Focus camera on actor', toolName: 'control_editor', arguments: { action: 'set_camera', targetActor: 'TC_Cube' }, expected: 'success - camera focused' },
42
+ { scenario: 'Set camera FOV 90', toolName: 'control_editor', arguments: { action: 'set_camera_fov', fov: 90 }, expected: 'success' },
43
+ { scenario: 'Set camera FOV 60', toolName: 'control_editor', arguments: { action: 'set_camera_fov', fov: 60 }, expected: 'success' },
44
+ { scenario: 'Set camera FOV extreme (120)', toolName: 'control_editor', arguments: { action: 'set_camera_fov', fov: 120 }, expected: 'success' },
45
+
46
+ // === VIEW MODE ===
26
47
  { scenario: 'Set view mode to Lit', toolName: 'control_editor', arguments: { action: 'set_view_mode', viewMode: 'Lit' }, expected: 'success - view mode set' },
27
48
  { scenario: 'Set view mode to Wireframe', toolName: 'control_editor', arguments: { action: 'set_view_mode', viewMode: 'Wireframe' }, expected: 'success - view mode set' },
49
+ { scenario: 'Set view mode to Unlit', toolName: 'control_editor', arguments: { action: 'set_view_mode', viewMode: 'Unlit' }, expected: 'success' },
50
+ { scenario: 'Set view mode to PathTracing', toolName: 'control_editor', arguments: { action: 'set_view_mode', viewMode: 'PathTracing' }, expected: 'success|not_supported' },
51
+
52
+ // === VIEWPORT SETTINGS ===
53
+ { scenario: 'Set viewport resolution 1920x1080', toolName: 'control_editor', arguments: { action: 'set_viewport_resolution', width: 1920, height: 1080 }, expected: 'success' },
54
+ { scenario: 'Set viewport resolution 1280x720', toolName: 'control_editor', arguments: { action: 'set_viewport_resolution', width: 1280, height: 720 }, expected: 'success' },
55
+ { scenario: 'Set viewport realtime on', toolName: 'control_editor', arguments: { action: 'set_viewport_realtime', enabled: true }, expected: 'success' },
56
+ { scenario: 'Set viewport realtime off', toolName: 'control_editor', arguments: { action: 'set_viewport_realtime', enabled: false }, expected: 'success' },
57
+
58
+ // === SCREENSHOT ===
28
59
  { scenario: 'Take screenshot', toolName: 'control_editor', arguments: { action: 'screenshot', filename: 'TC_Screenshot' }, expected: 'success - screenshot taken' },
60
+ { scenario: 'Take screenshot with resolution', toolName: 'control_editor', arguments: { action: 'screenshot', filename: 'TC_Screenshot_HD', width: 1920, height: 1080 }, expected: 'success' },
61
+
62
+ // === RECORDING ===
29
63
  { scenario: 'Start recording', toolName: 'control_editor', arguments: { action: 'start_recording', filename: 'TC_Record', frameRate: 30 }, expected: 'success - recording started' },
30
64
  { scenario: 'Stop recording', toolName: 'control_editor', arguments: { action: 'stop_recording' }, expected: 'success - recording stopped' },
31
- { scenario: 'Pause PIE', toolName: 'control_editor', arguments: { action: 'pause' }, expected: 'success - PIE paused' },
32
- { scenario: 'Resume PIE', toolName: 'control_editor', arguments: { action: 'resume' }, expected: 'success - PIE resumed' },
33
- { scenario: 'Step PIE frame', toolName: 'control_editor', arguments: { action: 'step_frame' }, expected: 'success - frame stepped' },
34
- { scenario: 'Create camera bookmark', toolName: 'control_editor', arguments: { action: 'create_bookmark', bookmarkName: 'TC_Bookmark' }, expected: 'success - bookmark created' },
35
- { scenario: 'Jump to camera bookmark', toolName: 'control_editor', arguments: { action: 'jump_to_bookmark', bookmarkName: 'TC_Bookmark' }, expected: 'success - jumped to bookmark' },
36
- { scenario: 'Set editor preferences', toolName: 'control_editor', arguments: { action: 'set_preferences', category: 'Editor', preferences: { UseGrid: true, SnapToGrid: true } }, expected: 'success - editor preferences set' },
37
- // Additional
38
- // Real-World Scenario: Editor Automation
39
- { scenario: 'Editor Auto - Open Asset', toolName: 'control_editor', arguments: { action: 'open_asset', assetPath: '/Engine/BasicShapes/Cube' }, expected: 'success' },
40
- { scenario: 'Editor Auto - Focus Viewport', toolName: 'control_editor', arguments: { action: 'set_camera', location: { x: 500, y: 500, z: 500 }, rotation: { pitch: -30, yaw: 225, roll: 0 } }, expected: 'success' },
41
- { scenario: 'Editor Auto - Run Command', toolName: 'control_editor', arguments: { action: 'execute_command', command: 'stat fps' }, expected: 'success' },
42
- {
43
- scenario: "Error: Invalid view mode",
44
- toolName: "control_editor",
45
- arguments: { action: "set_view_mode", viewMode: "InvalidMode" },
46
- expected: "error|unknown_viewmode"
47
- },
48
- {
49
- scenario: "Edge: Extreme FOV (1 degree)",
50
- toolName: "control_editor",
51
- arguments: { action: "set_camera_fov", fov: 1 },
52
- expected: "success"
53
- },
54
- {
55
- scenario: "Border: Negative speed",
56
- toolName: "control_editor",
57
- arguments: { action: "set_game_speed", speed: -1 },
58
- expected: "success|handled"
59
- },
60
- {
61
- scenario: "Error: Screenshot invalid res",
62
- toolName: "control_editor",
63
- arguments: { action: "screenshot", resolution: "invalidxres" },
64
- expected: "error"
65
- },
66
- {
67
- scenario: "Edge: Empty command",
68
- toolName: "control_editor",
69
- arguments: { action: "console_command", command: "" },
70
- expected: "error|empty"
71
- },
72
- {
73
- scenario: "Timeout short fail",
74
- toolName: "control_editor",
75
- arguments: { action: "play", timeoutMs: 1 },
76
- expected: "timeout|error"
77
- }
65
+
66
+ // === FRAME STEPPING ===
67
+ { scenario: 'Step single frame', toolName: 'control_editor', arguments: { action: 'step_frame' }, expected: 'success - frame stepped' },
68
+ { scenario: 'Step multiple frames', toolName: 'control_editor', arguments: { action: 'step_frame', steps: 5 }, expected: 'success' },
69
+
70
+ // === BOOKMARKS ===
71
+ { scenario: 'Create camera bookmark', toolName: 'control_editor', arguments: { action: 'create_bookmark', bookmarkName: 'TC_Bookmark_1' }, expected: 'success - bookmark created' },
72
+ { scenario: 'Create second bookmark', toolName: 'control_editor', arguments: { action: 'create_bookmark', bookmarkName: 'TC_Bookmark_2' }, expected: 'success' },
73
+ { scenario: 'Jump to bookmark 1', toolName: 'control_editor', arguments: { action: 'jump_to_bookmark', bookmarkName: 'TC_Bookmark_1' }, expected: 'success - jumped to bookmark' },
74
+ { scenario: 'Jump to bookmark 2', toolName: 'control_editor', arguments: { action: 'jump_to_bookmark', bookmarkName: 'TC_Bookmark_2' }, expected: 'success' },
75
+
76
+ // === PREFERENCES ===
77
+ { scenario: 'Set editor preferences - grid', toolName: 'control_editor', arguments: { action: 'set_preferences', category: 'Editor', preferences: { UseGrid: true, SnapToGrid: true } }, expected: 'success - editor preferences set' },
78
+ { scenario: 'Set editor preferences - realtime', toolName: 'control_editor', arguments: { action: 'set_preferences', category: 'Viewport', preferences: { bRealtimeUpdate: true } }, expected: 'success' },
79
+
80
+ // === CONSOLE COMMANDS ===
81
+ { scenario: 'Execute console command (stat fps)', toolName: 'control_editor', arguments: { action: 'execute_command', command: 'stat fps' }, expected: 'success' },
82
+ { scenario: 'Console command (alias)', toolName: 'control_editor', arguments: { action: 'console_command', command: 'stat unit' }, expected: 'success' },
83
+ { scenario: 'Console command - toggle stat', toolName: 'control_editor', arguments: { action: 'console_command', command: 'stat none' }, expected: 'success' },
84
+
85
+ // === OPEN ASSET ===
86
+ { scenario: 'Open asset in editor', toolName: 'control_editor', arguments: { action: 'open_asset', assetPath: '/Engine/BasicShapes/Cube' }, expected: 'success' },
87
+ { scenario: 'Open material asset', toolName: 'control_editor', arguments: { action: 'open_asset', assetPath: '/Engine/EngineMaterials/DefaultMaterial' }, expected: 'success' },
88
+
89
+ // === SIMULATE INPUT ===
90
+ { scenario: 'Simulate key press W', toolName: 'control_editor', arguments: { action: 'simulate_input', keyName: 'W', eventType: 'KeyDown' }, expected: 'success' },
91
+ { scenario: 'Simulate key release W', toolName: 'control_editor', arguments: { action: 'simulate_input', keyName: 'W', eventType: 'KeyUp' }, expected: 'success' },
92
+ { scenario: 'Simulate key press and release Space', toolName: 'control_editor', arguments: { action: 'simulate_input', keyName: 'SpaceBar', eventType: 'Both' }, expected: 'success' },
93
+
94
+ // === ERROR CASES ===
95
+ { scenario: 'Error: Invalid view mode', toolName: 'control_editor', arguments: { action: 'set_view_mode', viewMode: 'InvalidMode' }, expected: 'error|unknown_viewmode' },
96
+ { scenario: 'Error: Invalid bookmark', toolName: 'control_editor', arguments: { action: 'jump_to_bookmark', bookmarkName: 'NonExistent_Bookmark_XYZ' }, expected: 'error|not_found' },
97
+ { scenario: 'Error: Empty command', toolName: 'control_editor', arguments: { action: 'console_command', command: '' }, expected: 'error|empty' },
98
+ { scenario: 'Error: Invalid resolution', toolName: 'control_editor', arguments: { action: 'set_viewport_resolution', width: -100, height: -100 }, expected: 'error|validation' },
99
+ { scenario: 'Error: Open non-existent asset', toolName: 'control_editor', arguments: { action: 'open_asset', assetPath: '/Game/NonExistent/Asset' }, expected: 'error|not_found' },
100
+ { scenario: 'Error: Screenshot invalid resolution', toolName: 'control_editor', arguments: { action: 'screenshot', resolution: 'invalidxres' }, expected: 'error' },
101
+
102
+ // === EDGE CASES ===
103
+ { scenario: 'Edge: Extreme FOV (1 degree)', toolName: 'control_editor', arguments: { action: 'set_camera_fov', fov: 1 }, expected: 'success' },
104
+ { scenario: 'Edge: Negative speed', toolName: 'control_editor', arguments: { action: 'set_game_speed', speed: -1 }, expected: 'success|handled' },
105
+ { scenario: 'Edge: Zero speed (frozen)', toolName: 'control_editor', arguments: { action: 'set_game_speed', speed: 0 }, expected: 'success' },
106
+ { scenario: 'Edge: Very high speed', toolName: 'control_editor', arguments: { action: 'set_game_speed', speed: 10.0 }, expected: 'success' },
107
+
108
+ // === TIMEOUT TEST ===
109
+ { scenario: 'Timeout short fail', toolName: 'control_editor', arguments: { action: 'play', timeoutMs: 1 }, expected: 'timeout|error' }
78
110
  ];
79
111
 
80
112
  await runToolTests('Editor Control', testCases);
@@ -150,10 +150,19 @@ async function testGraphQLServer() {
150
150
  log.info('You can test it with: curl -X POST -H "Content-Type: application/json" -d \'{"query": "{ assets { edges { node { name path } } } }"\' http://127.0.0.1:4000/graphql');
151
151
 
152
152
  // Run tests
153
- await runGraphQLTests();
154
-
155
- // Keep server running for manual testing
156
- log.info('\nGraphQL server is still running. Press Ctrl+C to stop.');
153
+ const results = await runGraphQLTests();
154
+
155
+ // Auto-exit after tests complete
156
+ log.info('\nTests completed. Shutting down...');
157
+ await graphQLServer.stop();
158
+
159
+ if (results.failed > 0) {
160
+ log.info(`\n❌ ${results.failed}/${results.total} tests failed`);
161
+ process.exit(1);
162
+ } else {
163
+ log.info(`\n✅ All ${results.passed}/${results.total} tests passed`);
164
+ process.exit(0);
165
+ }
157
166
  } catch (error) {
158
167
  log.error('Failed to start GraphQL server:', error);
159
168
  process.exit(1);
@@ -259,6 +268,37 @@ async function runGraphQLTests() {
259
268
  }
260
269
  }
261
270
  `
271
+ },
272
+ {
273
+ name: 'Error Handling - Invalid Query',
274
+ query: `
275
+ {
276
+ invalidField {
277
+ name
278
+ }
279
+ }
280
+ `,
281
+ expectError: true
282
+ },
283
+ {
284
+ name: 'Pagination Test',
285
+ query: `
286
+ {
287
+ assets(pagination: { limit: 1, offset: 0 }) {
288
+ edges {
289
+ node {
290
+ name
291
+ }
292
+ cursor
293
+ }
294
+ pageInfo {
295
+ hasNextPage
296
+ hasPreviousPage
297
+ }
298
+ totalCount
299
+ }
300
+ }
301
+ `
262
302
  }
263
303
  ];
264
304
 
@@ -278,11 +318,21 @@ async function runGraphQLTests() {
278
318
  const result = await response.json();
279
319
 
280
320
  if (result.errors) {
281
- log.error(` ❌ ${test.name} - GraphQL Errors:`, JSON.stringify(result.errors));
282
- failed++;
321
+ if (test.expectError) {
322
+ log.info(` ✅ ${test.name} - Expected error received`);
323
+ passed++;
324
+ } else {
325
+ log.error(` ❌ ${test.name} - GraphQL Errors:`, JSON.stringify(result.errors));
326
+ failed++;
327
+ }
283
328
  } else if (result.data) {
284
- log.info(` ✅ ${test.name} - Success`);
285
- passed++;
329
+ if (test.expectError) {
330
+ log.error(` ❌ ${test.name} - Expected error but got data`);
331
+ failed++;
332
+ } else {
333
+ log.info(` ✅ ${test.name} - Success`);
334
+ passed++;
335
+ }
286
336
  } else {
287
337
  log.error(` ❌ ${test.name} - No data returned`);
288
338
  failed++;