unreal-engine-mcp-server 0.5.1 → 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 (75) hide show
  1. package/.github/workflows/publish-mcp.yml +1 -4
  2. package/.github/workflows/release-drafter.yml +2 -1
  3. package/CHANGELOG.md +38 -0
  4. package/dist/automation/bridge.d.ts +1 -2
  5. package/dist/automation/bridge.js +24 -23
  6. package/dist/automation/connection-manager.d.ts +1 -0
  7. package/dist/automation/connection-manager.js +10 -0
  8. package/dist/automation/message-handler.js +5 -4
  9. package/dist/automation/request-tracker.d.ts +4 -0
  10. package/dist/automation/request-tracker.js +11 -3
  11. package/dist/tools/actors.d.ts +19 -1
  12. package/dist/tools/actors.js +15 -5
  13. package/dist/tools/assets.js +1 -1
  14. package/dist/tools/blueprint.d.ts +12 -0
  15. package/dist/tools/blueprint.js +43 -14
  16. package/dist/tools/consolidated-tool-definitions.js +2 -1
  17. package/dist/tools/editor.js +3 -2
  18. package/dist/tools/handlers/actor-handlers.d.ts +1 -1
  19. package/dist/tools/handlers/actor-handlers.js +14 -8
  20. package/dist/tools/handlers/sequence-handlers.d.ts +1 -1
  21. package/dist/tools/handlers/sequence-handlers.js +24 -13
  22. package/dist/tools/introspection.d.ts +1 -1
  23. package/dist/tools/introspection.js +1 -1
  24. package/dist/tools/level.js +3 -3
  25. package/dist/tools/lighting.d.ts +54 -7
  26. package/dist/tools/lighting.js +4 -4
  27. package/dist/tools/materials.d.ts +1 -1
  28. package/dist/types/tool-types.d.ts +2 -0
  29. package/dist/unreal-bridge.js +4 -4
  30. package/dist/utils/command-validator.js +6 -5
  31. package/dist/utils/error-handler.d.ts +24 -2
  32. package/dist/utils/error-handler.js +58 -23
  33. package/dist/utils/normalize.d.ts +7 -4
  34. package/dist/utils/normalize.js +12 -10
  35. package/dist/utils/response-validator.js +88 -73
  36. package/dist/utils/unreal-command-queue.d.ts +2 -0
  37. package/dist/utils/unreal-command-queue.js +8 -1
  38. package/docs/handler-mapping.md +4 -2
  39. package/package.json +1 -1
  40. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +298 -33
  41. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +7 -8
  42. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +229 -319
  43. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +98 -0
  44. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +24 -0
  45. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +96 -0
  46. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +52 -5
  47. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +5 -268
  48. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +57 -2
  49. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +0 -1
  50. package/server.json +3 -3
  51. package/src/automation/bridge.ts +27 -25
  52. package/src/automation/connection-manager.ts +18 -0
  53. package/src/automation/message-handler.ts +33 -8
  54. package/src/automation/request-tracker.ts +39 -7
  55. package/src/server/tool-registry.ts +3 -3
  56. package/src/tools/actors.ts +44 -19
  57. package/src/tools/assets.ts +3 -3
  58. package/src/tools/blueprint.ts +115 -49
  59. package/src/tools/consolidated-tool-definitions.ts +2 -1
  60. package/src/tools/editor.ts +4 -3
  61. package/src/tools/handlers/actor-handlers.ts +14 -9
  62. package/src/tools/handlers/sequence-handlers.ts +86 -63
  63. package/src/tools/introspection.ts +7 -7
  64. package/src/tools/level.ts +6 -6
  65. package/src/tools/lighting.ts +19 -19
  66. package/src/tools/materials.ts +1 -1
  67. package/src/tools/sequence.ts +1 -1
  68. package/src/tools/ui.ts +1 -1
  69. package/src/types/tool-types.ts +4 -0
  70. package/src/unreal-bridge.ts +71 -26
  71. package/src/utils/command-validator.ts +46 -5
  72. package/src/utils/error-handler.ts +128 -45
  73. package/src/utils/normalize.ts +38 -16
  74. package/src/utils/response-validator.ts +103 -87
  75. package/src/utils/unreal-command-queue.ts +13 -1
@@ -19,11 +19,13 @@ DEFINE_LOG_CATEGORY(LogMcpAutomationBridgeSubsystem);
19
19
  // '?' and truncate long messages so logs remain readable and do not
20
20
  // attempt to render unprintable glyphs in the editor which can spam
21
21
  /**
22
- * @brief Produces a log-safe copy of a string by replacing control characters and truncating long input.
22
+ * @brief Produces a log-safe copy of a string by replacing control characters
23
+ * and truncating long input.
23
24
  *
24
- * Creates a sanitized version of the input string where characters with code points less than 32 or equal to 127
25
- * are replaced with '?' and the result is truncated to 512 characters with "[TRUNCATED]" appended if the input
26
- * is longer.
25
+ * Creates a sanitized version of the input string where characters with code
26
+ * points less than 32 or equal to 127 are replaced with '?' and the result is
27
+ * truncated to 512 characters with "[TRUNCATED]" appended if the input is
28
+ * longer.
27
29
  *
28
30
  * @param In Input string to sanitize.
29
31
  * @return FString Sanitized string suitable for logging.
@@ -46,11 +48,15 @@ static inline FString SanitizeForLog(const FString &In) {
46
48
  }
47
49
 
48
50
  /**
49
- * @brief Initialize the automation bridge subsystem, preparing networking, handlers, and periodic processing.
51
+ * @brief Initialize the automation bridge subsystem, preparing networking,
52
+ * handlers, and periodic processing.
50
53
  *
51
- * Creates and initializes the connection manager, registers automation action handlers and a message-received callback, starts the connection manager, and registers a recurring ticker to process pending automation requests.
54
+ * Creates and initializes the connection manager, registers automation action
55
+ * handlers and a message-received callback, starts the connection manager, and
56
+ * registers a recurring ticker to process pending automation requests.
52
57
  *
53
- * @param Collection Subsystem collection provided by the engine during initialization.
58
+ * @param Collection Subsystem collection provided by the engine during
59
+ * initialization.
54
60
  */
55
61
  void UMcpAutomationBridgeSubsystem::Initialize(
56
62
  FSubsystemCollectionBase &Collection) {
@@ -90,9 +96,12 @@ void UMcpAutomationBridgeSubsystem::Initialize(
90
96
  }
91
97
 
92
98
  /**
93
- * @brief Shuts down the MCP Automation Bridge subsystem and releases its resources.
99
+ * @brief Shuts down the MCP Automation Bridge subsystem and releases its
100
+ * resources.
94
101
  *
95
- * Removes the registered ticker, stops and clears the connection manager, detaches and clears the log capture device, and calls the superclass deinitialization.
102
+ * Removes the registered ticker, stops and clears the connection manager,
103
+ * detaches and clears the log capture device, and calls the superclass
104
+ * deinitialization.
96
105
  */
97
106
  void UMcpAutomationBridgeSubsystem::Deinitialize() {
98
107
  if (TickHandle.IsValid()) {
@@ -118,9 +127,11 @@ void UMcpAutomationBridgeSubsystem::Deinitialize() {
118
127
  }
119
128
 
120
129
  /**
121
- * @brief Reports whether the automation bridge currently has any active connections.
130
+ * @brief Reports whether the automation bridge currently has any active
131
+ * connections.
122
132
  *
123
- * @return `true` if the connection manager exists and has one or more active sockets, `false` otherwise.
133
+ * @return `true` if the connection manager exists and has one or more active
134
+ * sockets, `false` otherwise.
124
135
  */
125
136
  bool UMcpAutomationBridgeSubsystem::IsBridgeActive() const {
126
137
  return ConnectionManager.IsValid() &&
@@ -130,7 +141,9 @@ bool UMcpAutomationBridgeSubsystem::IsBridgeActive() const {
130
141
  /**
131
142
  * @brief Determine the bridge's connection state from active sockets.
132
143
  *
133
- * @return EMcpAutomationBridgeState `EMcpAutomationBridgeState::Connected` if one or more active sockets are present, `EMcpAutomationBridgeState::Disconnected` otherwise.
144
+ * @return EMcpAutomationBridgeState `EMcpAutomationBridgeState::Connected` if
145
+ * one or more active sockets are present,
146
+ * `EMcpAutomationBridgeState::Disconnected` otherwise.
134
147
  */
135
148
  EMcpAutomationBridgeState
136
149
  UMcpAutomationBridgeSubsystem::GetBridgeState() const {
@@ -144,7 +157,8 @@ UMcpAutomationBridgeSubsystem::GetBridgeState() const {
144
157
  * @brief Forward a raw text message to the connection manager for transmission.
145
158
  *
146
159
  * @param Message The raw message string to send.
147
- * @return `true` if the connection manager accepted the message for sending, `false` otherwise.
160
+ * @return `true` if the connection manager accepted the message for sending,
161
+ * `false` otherwise.
148
162
  */
149
163
  bool UMcpAutomationBridgeSubsystem::SendRawMessage(const FString &Message) {
150
164
  if (ConnectionManager.IsValid()) {
@@ -154,9 +168,12 @@ bool UMcpAutomationBridgeSubsystem::SendRawMessage(const FString &Message) {
154
168
  }
155
169
 
156
170
  /**
157
- * @brief Per-frame tick that processes deferred automation requests when it is safe to do so.
171
+ * @brief Per-frame tick that processes deferred automation requests when it is
172
+ * safe to do so.
158
173
  *
159
- * Invokes processing of any pending automation requests that were previously deferred due to unsafe engine states (saving, garbage collection, or async loading).
174
+ * Invokes processing of any pending automation requests that were previously
175
+ * deferred due to unsafe engine states (saving, garbage collection, or async
176
+ * loading).
160
177
  *
161
178
  * @param DeltaTime Time elapsed since the last tick, in seconds.
162
179
  * @return true to remain registered and continue receiving ticks.
@@ -176,14 +193,16 @@ bool UMcpAutomationBridgeSubsystem::Tick(float DeltaTime) {
176
193
  // McpAutomationBridge_ProcessRequest.cpp to avoid duplicate definitions and
177
194
  // to keep this file focused. See that file for the full request dispatcher
178
195
  /**
179
- * @brief Sends an automation response for a specific request to the given socket.
196
+ * @brief Sends an automation response for a specific request to the given
197
+ * socket.
180
198
  *
181
199
  * If the connection manager is not available this call is a no-op.
182
200
  *
183
201
  * @param TargetSocket WebSocket to which the response will be sent.
184
202
  * @param RequestId Identifier of the automation request being responded to.
185
203
  * @param bSuccess `true` if the request succeeded, `false` otherwise.
186
- * @param Message Human-readable message or description associated with the response.
204
+ * @param Message Human-readable message or description associated with the
205
+ * response.
187
206
  * @param Result Optional JSON object containing result data; may be null.
188
207
  * @param ErrorCode Error code string to include when `bSuccess` is `false`.
189
208
  */
@@ -205,10 +224,12 @@ void UMcpAutomationBridgeSubsystem::SendAutomationResponse(
205
224
  * with the resolved error and message, and sends a failure response for the
206
225
  * specified request.
207
226
  *
208
- * @param TargetSocket Optional socket to target the response; may be null to broadcast or use a default.
227
+ * @param TargetSocket Optional socket to target the response; may be null to
228
+ * broadcast or use a default.
209
229
  * @param RequestId Identifier of the automation request that failed.
210
230
  * @param Message Human-readable failure message.
211
- * @param ErrorCode Error code to include with the response; "AUTOMATION_ERROR" is used if empty.
231
+ * @param ErrorCode Error code to include with the response; "AUTOMATION_ERROR"
232
+ * is used if empty.
212
233
  */
213
234
  void UMcpAutomationBridgeSubsystem::SendAutomationError(
214
235
  TSharedPtr<FMcpBridgeWebSocket> TargetSocket, const FString &RequestId,
@@ -229,7 +250,8 @@ void UMcpAutomationBridgeSubsystem::SendAutomationError(
229
250
  * error code to the connection manager for telemetry/logging.
230
251
  *
231
252
  * @param RequestId Unique identifier of the automation request.
232
- * @param bSuccess `true` if the request completed successfully, `false` otherwise.
253
+ * @param bSuccess `true` if the request completed successfully, `false`
254
+ * otherwise.
233
255
  * @param Message Human-readable message describing the outcome or context.
234
256
  * @param ErrorCode Short error identifier (empty if none).
235
257
  */
@@ -245,8 +267,9 @@ void UMcpAutomationBridgeSubsystem::RecordAutomationTelemetry(
245
267
  /**
246
268
  * @brief Registers an automation action handler for the given action string.
247
269
  *
248
- * If a non-empty handler is provided, stores it under Action (replacing any existing handler for the same key).
249
- * If Handler is null/invalid, the call is a no-op.
270
+ * If a non-empty handler is provided, stores it under Action (replacing any
271
+ * existing handler for the same key). If Handler is null/invalid, the call is a
272
+ * no-op.
250
273
  *
251
274
  * @param Action The action identifier string used to look up the handler.
252
275
  * @param Handler Callable invoked when the specified action is requested.
@@ -259,16 +282,20 @@ void UMcpAutomationBridgeSubsystem::RegisterHandler(
259
282
  }
260
283
 
261
284
  /**
262
- * @brief Registers all automation action handlers used by the MCP Automation Bridge.
285
+ * @brief Registers all automation action handlers used by the MCP Automation
286
+ * Bridge.
263
287
  *
264
- * Populates the subsystem's handler registry with mappings from action name strings
265
- * (for example: core/property actions, array/map/set container ops, asset dependency queries,
266
- * console/system and editor tooling actions, blueprint/world/asset management, rendering/materials,
267
- * input/control, audio/lighting/physics/effects, and performance actions) to the functions that
268
- * handle those actions so incoming automation requests can be dispatched by action name.
288
+ * Populates the subsystem's handler registry with mappings from action name
289
+ * strings (for example: core/property actions, array/map/set container ops,
290
+ * asset dependency queries, console/system and editor tooling actions,
291
+ * blueprint/world/asset management, rendering/materials, input/control,
292
+ * audio/lighting/physics/effects, and performance actions) to the functions
293
+ * that handle those actions so incoming automation requests can be dispatched
294
+ * by action name.
269
295
  *
270
- * This also registers a few common alias actions (e.g., "create_effect", "clear_debug_shapes")
271
- * so those actions dispatch directly to the intended handler.
296
+ * This also registers a few common alias actions (e.g., "create_effect",
297
+ * "clear_debug_shapes") so those actions dispatch directly to the intended
298
+ * handler.
272
299
  */
273
300
  void UMcpAutomationBridgeSubsystem::InitializeHandlers() {
274
301
  // Core & Properties
@@ -402,6 +429,243 @@ void UMcpAutomationBridgeSubsystem::InitializeHandlers() {
402
429
  return HandleGetAssetDependencies(R, A, P, S);
403
430
  });
404
431
 
432
+ // Asset Workflow
433
+ RegisterHandler(TEXT("fixup_redirectors"),
434
+ [this](const FString &R, const FString &A,
435
+ const TSharedPtr<FJsonObject> &P,
436
+ TSharedPtr<FMcpBridgeWebSocket> S) {
437
+ return HandleFixupRedirectors(R, A, P, S);
438
+ });
439
+ RegisterHandler(TEXT("source_control_checkout"),
440
+ [this](const FString &R, const FString &A,
441
+ const TSharedPtr<FJsonObject> &P,
442
+ TSharedPtr<FMcpBridgeWebSocket> S) {
443
+ return HandleSourceControlCheckout(R, A, P, S);
444
+ });
445
+ RegisterHandler(TEXT("source_control_submit"),
446
+ [this](const FString &R, const FString &A,
447
+ const TSharedPtr<FJsonObject> &P,
448
+ TSharedPtr<FMcpBridgeWebSocket> S) {
449
+ return HandleSourceControlSubmit(R, A, P, S);
450
+ });
451
+ RegisterHandler(TEXT("bulk_rename_assets"),
452
+ [this](const FString &R, const FString &A,
453
+ const TSharedPtr<FJsonObject> &P,
454
+ TSharedPtr<FMcpBridgeWebSocket> S) {
455
+ return HandleBulkRenameAssets(R, A, P, S);
456
+ });
457
+ RegisterHandler(TEXT("bulk_delete_assets"),
458
+ [this](const FString &R, const FString &A,
459
+ const TSharedPtr<FJsonObject> &P,
460
+ TSharedPtr<FMcpBridgeWebSocket> S) {
461
+ return HandleBulkDeleteAssets(R, A, P, S);
462
+ });
463
+ RegisterHandler(TEXT("generate_thumbnail"),
464
+ [this](const FString &R, const FString &A,
465
+ const TSharedPtr<FJsonObject> &P,
466
+ TSharedPtr<FMcpBridgeWebSocket> S) {
467
+ return HandleGenerateThumbnail(R, A, P, S);
468
+ });
469
+
470
+ // Landscape
471
+ RegisterHandler(TEXT("create_landscape"),
472
+ [this](const FString &R, const FString &A,
473
+ const TSharedPtr<FJsonObject> &P,
474
+ TSharedPtr<FMcpBridgeWebSocket> S) {
475
+ return HandleCreateLandscape(R, A, P, S);
476
+ });
477
+ RegisterHandler(TEXT("create_procedural_terrain"),
478
+ [this](const FString &R, const FString &A,
479
+ const TSharedPtr<FJsonObject> &P,
480
+ TSharedPtr<FMcpBridgeWebSocket> S) {
481
+ return HandleCreateProceduralTerrain(R, A, P, S);
482
+ });
483
+ RegisterHandler(TEXT("create_landscape_grass_type"),
484
+ [this](const FString &R, const FString &A,
485
+ const TSharedPtr<FJsonObject> &P,
486
+ TSharedPtr<FMcpBridgeWebSocket> S) {
487
+ return HandleCreateLandscapeGrassType(R, A, P, S);
488
+ });
489
+ RegisterHandler(TEXT("sculpt_landscape"),
490
+ [this](const FString &R, const FString &A,
491
+ const TSharedPtr<FJsonObject> &P,
492
+ TSharedPtr<FMcpBridgeWebSocket> S) {
493
+ return HandleSculptLandscape(R, A, P, S);
494
+ });
495
+ RegisterHandler(TEXT("set_landscape_material"),
496
+ [this](const FString &R, const FString &A,
497
+ const TSharedPtr<FJsonObject> &P,
498
+ TSharedPtr<FMcpBridgeWebSocket> S) {
499
+ return HandleSetLandscapeMaterial(R, A, P, S);
500
+ });
501
+ RegisterHandler(TEXT("edit_landscape"),
502
+ [this](const FString &R, const FString &A,
503
+ const TSharedPtr<FJsonObject> &P,
504
+ TSharedPtr<FMcpBridgeWebSocket> S) {
505
+ return HandleEditLandscape(R, A, P, S);
506
+ });
507
+
508
+ // Foliage
509
+ RegisterHandler(TEXT("add_foliage_type"),
510
+ [this](const FString &R, const FString &A,
511
+ const TSharedPtr<FJsonObject> &P,
512
+ TSharedPtr<FMcpBridgeWebSocket> S) {
513
+ return HandleAddFoliageType(R, A, P, S);
514
+ });
515
+ RegisterHandler(TEXT("create_procedural_foliage"),
516
+ [this](const FString &R, const FString &A,
517
+ const TSharedPtr<FJsonObject> &P,
518
+ TSharedPtr<FMcpBridgeWebSocket> S) {
519
+ return HandleCreateProceduralFoliage(R, A, P, S);
520
+ });
521
+ RegisterHandler(TEXT("paint_foliage"),
522
+ [this](const FString &R, const FString &A,
523
+ const TSharedPtr<FJsonObject> &P,
524
+ TSharedPtr<FMcpBridgeWebSocket> S) {
525
+ return HandlePaintFoliage(R, A, P, S);
526
+ });
527
+ RegisterHandler(TEXT("add_foliage_instances"),
528
+ [this](const FString &R, const FString &A,
529
+ const TSharedPtr<FJsonObject> &P,
530
+ TSharedPtr<FMcpBridgeWebSocket> S) {
531
+ return HandleAddFoliageInstances(R, A, P, S);
532
+ });
533
+ RegisterHandler(TEXT("remove_foliage"),
534
+ [this](const FString &R, const FString &A,
535
+ const TSharedPtr<FJsonObject> &P,
536
+ TSharedPtr<FMcpBridgeWebSocket> S) {
537
+ return HandleRemoveFoliage(R, A, P, S);
538
+ });
539
+ RegisterHandler(TEXT("get_foliage_instances"),
540
+ [this](const FString &R, const FString &A,
541
+ const TSharedPtr<FJsonObject> &P,
542
+ TSharedPtr<FMcpBridgeWebSocket> S) {
543
+ return HandleGetFoliageInstances(R, A, P, S);
544
+ });
545
+
546
+ // Niagara
547
+ RegisterHandler(TEXT("create_niagara_system"),
548
+ [this](const FString &R, const FString &A,
549
+ const TSharedPtr<FJsonObject> &P,
550
+ TSharedPtr<FMcpBridgeWebSocket> S) {
551
+ return HandleCreateNiagaraSystem(R, A, P, S);
552
+ });
553
+ RegisterHandler(TEXT("create_niagara_ribbon"),
554
+ [this](const FString &R, const FString &A,
555
+ const TSharedPtr<FJsonObject> &P,
556
+ TSharedPtr<FMcpBridgeWebSocket> S) {
557
+ return HandleCreateNiagaraRibbon(R, A, P, S);
558
+ });
559
+ RegisterHandler(TEXT("create_niagara_emitter"),
560
+ [this](const FString &R, const FString &A,
561
+ const TSharedPtr<FJsonObject> &P,
562
+ TSharedPtr<FMcpBridgeWebSocket> S) {
563
+ return HandleCreateNiagaraEmitter(R, A, P, S);
564
+ });
565
+ RegisterHandler(TEXT("spawn_niagara_actor"),
566
+ [this](const FString &R, const FString &A,
567
+ const TSharedPtr<FJsonObject> &P,
568
+ TSharedPtr<FMcpBridgeWebSocket> S) {
569
+ return HandleSpawnNiagaraActor(R, A, P, S);
570
+ });
571
+ RegisterHandler(TEXT("modify_niagara_parameter"),
572
+ [this](const FString &R, const FString &A,
573
+ const TSharedPtr<FJsonObject> &P,
574
+ TSharedPtr<FMcpBridgeWebSocket> S) {
575
+ return HandleModifyNiagaraParameter(R, A, P, S);
576
+ });
577
+
578
+ // Animation
579
+ RegisterHandler(TEXT("create_anim_blueprint"),
580
+ [this](const FString &R, const FString &A,
581
+ const TSharedPtr<FJsonObject> &P,
582
+ TSharedPtr<FMcpBridgeWebSocket> S) {
583
+ return HandleCreateAnimBlueprint(R, A, P, S);
584
+ });
585
+ RegisterHandler(TEXT("play_anim_montage"),
586
+ [this](const FString &R, const FString &A,
587
+ const TSharedPtr<FJsonObject> &P,
588
+ TSharedPtr<FMcpBridgeWebSocket> S) {
589
+ return HandlePlayAnimMontage(R, A, P, S);
590
+ });
591
+ RegisterHandler(TEXT("setup_ragdoll"),
592
+ [this](const FString &R, const FString &A,
593
+ const TSharedPtr<FJsonObject> &P,
594
+ TSharedPtr<FMcpBridgeWebSocket> S) {
595
+ return HandleSetupRagdoll(R, A, P, S);
596
+ });
597
+
598
+ // Material Graph
599
+ RegisterHandler(TEXT("add_material_texture_sample"),
600
+ [this](const FString &R, const FString &A,
601
+ const TSharedPtr<FJsonObject> &P,
602
+ TSharedPtr<FMcpBridgeWebSocket> S) {
603
+ return HandleAddMaterialTextureSample(R, A, P, S);
604
+ });
605
+ RegisterHandler(TEXT("add_material_expression"),
606
+ [this](const FString &R, const FString &A,
607
+ const TSharedPtr<FJsonObject> &P,
608
+ TSharedPtr<FMcpBridgeWebSocket> S) {
609
+ return HandleAddMaterialExpression(R, A, P, S);
610
+ });
611
+ RegisterHandler(TEXT("create_material_nodes"),
612
+ [this](const FString &R, const FString &A,
613
+ const TSharedPtr<FJsonObject> &P,
614
+ TSharedPtr<FMcpBridgeWebSocket> S) {
615
+ return HandleCreateMaterialNodes(R, A, P, S);
616
+ });
617
+
618
+ // Sequencer
619
+ RegisterHandler(TEXT("add_sequencer_keyframe"),
620
+ [this](const FString &R, const FString &A,
621
+ const TSharedPtr<FJsonObject> &P,
622
+ TSharedPtr<FMcpBridgeWebSocket> S) {
623
+ return HandleAddSequencerKeyframe(R, A, P, S);
624
+ });
625
+ RegisterHandler(TEXT("manage_sequencer_track"),
626
+ [this](const FString &R, const FString &A,
627
+ const TSharedPtr<FJsonObject> &P,
628
+ TSharedPtr<FMcpBridgeWebSocket> S) {
629
+ return HandleManageSequencerTrack(R, A, P, S);
630
+ });
631
+ RegisterHandler(TEXT("add_camera_track"),
632
+ [this](const FString &R, const FString &A,
633
+ const TSharedPtr<FJsonObject> &P,
634
+ TSharedPtr<FMcpBridgeWebSocket> S) {
635
+ return HandleAddCameraTrack(R, A, P, S);
636
+ });
637
+ RegisterHandler(TEXT("add_animation_track"),
638
+ [this](const FString &R, const FString &A,
639
+ const TSharedPtr<FJsonObject> &P,
640
+ TSharedPtr<FMcpBridgeWebSocket> S) {
641
+ return HandleAddAnimationTrack(R, A, P, S);
642
+ });
643
+ RegisterHandler(TEXT("add_transform_track"),
644
+ [this](const FString &R, const FString &A,
645
+ const TSharedPtr<FJsonObject> &P,
646
+ TSharedPtr<FMcpBridgeWebSocket> S) {
647
+ return HandleAddTransformTrack(R, A, P, S);
648
+ });
649
+
650
+ // UI & Environment
651
+ RegisterHandler(TEXT("manage_ui"), [this](const FString &R, const FString &A,
652
+ const TSharedPtr<FJsonObject> &P,
653
+ TSharedPtr<FMcpBridgeWebSocket> S) {
654
+ return HandleUiAction(R, A, P, S);
655
+ });
656
+ RegisterHandler(TEXT("control_environment"),
657
+ [this](const FString &R, const FString &A,
658
+ const TSharedPtr<FJsonObject> &P,
659
+ TSharedPtr<FMcpBridgeWebSocket> S) {
660
+ return HandleControlEnvironmentAction(R, A, P, S);
661
+ });
662
+ RegisterHandler(TEXT("build_environment"),
663
+ [this](const FString &R, const FString &A,
664
+ const TSharedPtr<FJsonObject> &P,
665
+ TSharedPtr<FMcpBridgeWebSocket> S) {
666
+ return HandleBuildEnvironmentAction(R, A, P, S);
667
+ });
668
+
405
669
  // Tools & System
406
670
  RegisterHandler(TEXT("console_command"),
407
671
  [this](const FString &R, const FString &A,
@@ -551,9 +815,10 @@ void UMcpAutomationBridgeSubsystem::InitializeHandlers() {
551
815
  /**
552
816
  * @brief Processes all queued automation requests on the game thread.
553
817
  *
554
- * Ensures execution on the game thread (re-dispatches if called from another thread),
555
- * moves the shared pending-request queue into a local list under a lock, clears the shared queue
556
- * and the scheduled flag, then dispatches each request to ProcessAutomationRequest.
818
+ * Ensures execution on the game thread (re-dispatches if called from another
819
+ * thread), moves the shared pending-request queue into a local list under a
820
+ * lock, clears the shared queue and the scheduled flag, then dispatches each
821
+ * request to ProcessAutomationRequest.
557
822
  */
558
823
  void UMcpAutomationBridgeSubsystem::ProcessPendingAutomationRequests() {
559
824
  if (!IsInGameThread()) {
@@ -218,7 +218,7 @@ static void ApplyBlendSpaceConfiguration(UObject *BlendSpaceAsset,
218
218
  * current editor world. \
219
219
  * \
220
220
  * Skips empty or whitespace-only commands. If any command fails or the \
221
- * editor/world is unavailable, an explanatory message is written to \
221
+ * editor/world is unavailable, an explanatory message is written to \
222
222
  * OutErrorMessage. \
223
223
  * \
224
224
  * @param Commands Array of editor command strings to execute. \
@@ -795,15 +795,14 @@ bool UMcpAutomationBridgeSubsystem::HandleAnimationPhysicsAction(
795
795
  {TEXT("plane"), TEXT("Aircraft")}};
796
796
 
797
797
  const FString *VehicleTypePtr = VehicleTypeMap.Find(NormalizedType);
798
- if (!VehicleTypePtr) {
799
- Message = TEXT(
800
- "Invalid vehicleType: expected Car, Bike, Tank, or Aircraft");
801
- ErrorCode = TEXT("INVALID_ARGUMENT");
802
- Resp->SetStringField(TEXT("error"), Message);
803
- } else {
798
+ // Use mapped value or passthrough raw value for unknown types
799
+ FString FinalVehicleType =
800
+ VehicleTypePtr ? *VehicleTypePtr : VehicleTypeRaw;
801
+
802
+ {
804
803
  TArray<FString> Commands;
805
804
  Commands.Add(FString::Printf(TEXT("CreateVehicle %s %s"),
806
- *VehicleName, **VehicleTypePtr));
805
+ *VehicleName, *FinalVehicleType));
807
806
 
808
807
  const TArray<TSharedPtr<FJsonValue>> *WheelsArray = nullptr;
809
808
  if (Payload->TryGetArrayField(TEXT("wheels"), WheelsArray) &&