ultimate-unreal-engine-mcp 0.1.12 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-unreal-engine-mcp",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "MCP server giving AI assistants full access to Unreal Engine 5.7 projects",
5
5
  "type": "module",
6
6
  "engines": {
@@ -478,8 +478,35 @@ void RegisterActorCommands(FMCPCommandRouter& Router)
478
478
  return;
479
479
  }
480
480
 
481
- // Find the UPROPERTY by name on the actor's class.
482
- FProperty* Prop = TargetActor->GetClass()->FindPropertyByName(FName(*PropertyName));
481
+ // Optional component_name if set, resolve property on the named component
482
+ // instead of the actor itself. Enables "DoorMesh" + "StaticMesh" patterns.
483
+ UObject* PropertyOwner = TargetActor;
484
+ FString ComponentName;
485
+ if (Payload->TryGetStringField(TEXT("component_name"), ComponentName) && !ComponentName.IsEmpty())
486
+ {
487
+ UActorComponent* Comp = TargetActor->FindComponentByClass<UActorComponent>();
488
+ // Search all components by their subobject name.
489
+ TInlineComponentArray<UActorComponent*> Components;
490
+ TargetActor->GetComponents(Components);
491
+ UActorComponent* FoundComp = nullptr;
492
+ for (UActorComponent* C : Components)
493
+ {
494
+ if (C && C->GetName() == ComponentName)
495
+ {
496
+ FoundComp = C;
497
+ break;
498
+ }
499
+ }
500
+ if (!FoundComp)
501
+ {
502
+ SendResponse(BuildActorErrorResponse(CorrId, TEXT("component_not_found")) + TEXT("\n"));
503
+ return;
504
+ }
505
+ PropertyOwner = FoundComp;
506
+ }
507
+
508
+ // Find the UPROPERTY by name on the target object's class.
509
+ FProperty* Prop = PropertyOwner->GetClass()->FindPropertyByName(FName(*PropertyName));
483
510
  if (!Prop)
484
511
  {
485
512
  SendResponse(BuildActorErrorResponse(CorrId, TEXT("property_not_found")) + TEXT("\n"));
@@ -487,7 +514,7 @@ void RegisterActorCommands(FMCPCommandRouter& Router)
487
514
  }
488
515
 
489
516
  TargetActor->Modify();
490
- void* ValuePtr = Prop->ContainerPtrToValuePtr<void>(TargetActor);
517
+ void* ValuePtr = Prop->ContainerPtrToValuePtr<void>(PropertyOwner);
491
518
 
492
519
  if (ValueType == TEXT("bool"))
493
520
  {
@@ -236,9 +236,15 @@ void RegisterImportExportCommands(FMCPCommandRouter& Router)
236
236
  Payload->TryGetBoolField(TEXT("combine_meshes"), bCombineMeshes);
237
237
  Payload->TryGetNumberField(TEXT("scale_factor"), ScaleFactor);
238
238
 
239
- // Capture everything for async dispatch.
240
- AsyncTask(ENamedThreads::GameThread, [CorrId, SendResponse, SourceFile, DestPath,
241
- bImportMaterials, bCombineMeshes, ScaleFactor]()
239
+ // Defer the import to the next engine tick via FTSTicker.
240
+ // Reason: ImportAssetTasks triggers the Interchange pipeline which
241
+ // enqueues task-graph work. Running it inside the router's
242
+ // AsyncTask(GameThread) hits the recursion guard (Assertion
243
+ // ++Queue.RecursionGuard == 1). A ticker callback runs on the
244
+ // game thread but OUTSIDE the task-graph scope.
245
+ FTSTicker::GetCoreTicker().AddTicker(
246
+ FTickerDelegate::CreateLambda([CorrId, SendResponse, SourceFile, DestPath,
247
+ bImportMaterials, bCombineMeshes, ScaleFactor](float) -> bool
242
248
  {
243
249
  // Split dest_path into package path + asset name.
244
250
  FString PackagePath = DestPath;
@@ -307,7 +313,8 @@ void RegisterImportExportCommands(FMCPCommandRouter& Router)
307
313
  Data->SetNumberField(TEXT("count"), static_cast<double>(AssetsArray.Num()));
308
314
 
309
315
  SendResponse(BuildImpSuccessResponse(CorrId, Data) + TEXT("\n"));
310
- });
316
+ return false; // One-shot: remove ticker after execution
317
+ }), 0.0f);
311
318
  });
312
319
 
313
320
  // -----------------------------------------------------------------------
@@ -753,7 +760,9 @@ void RegisterImportExportCommands(FMCPCommandRouter& Router)
753
760
  Payload->TryGetBoolField(TEXT("import_materials"), bImportMaterials);
754
761
  Payload->TryGetNumberField(TEXT("scale_factor"), ScaleFactor);
755
762
 
756
- AsyncTask(ENamedThreads::GameThread, [CorrId, SendResponse, Directory, DestPath, Extensions, bImportMaterials, ScaleFactor]()
763
+ // Defer to next tick to avoid Interchange task-graph recursion (same as import.fbx fix).
764
+ FTSTicker::GetCoreTicker().AddTicker(
765
+ FTickerDelegate::CreateLambda([CorrId, SendResponse, Directory, DestPath, Extensions, bImportMaterials, ScaleFactor](float) -> bool
757
766
  {
758
767
  // Enumerate all matching files in the directory.
759
768
  TArray<FString> FoundFiles;
@@ -843,6 +852,7 @@ void RegisterImportExportCommands(FMCPCommandRouter& Router)
843
852
  Data->SetArrayField(TEXT("errors"), ErrorsArray);
844
853
 
845
854
  SendResponse(BuildImpSuccessResponse(CorrId, Data) + TEXT("\n"));
846
- });
855
+ return false; // One-shot: remove ticker after execution
856
+ }), 0.0f);
847
857
  });
848
858
  }