ultimate-unreal-engine-mcp 0.1.1 → 0.1.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 (25) hide show
  1. package/package.json +1 -1
  2. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/MCPBridgeEditor.Build.cs +6 -1
  3. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.cpp +14 -11
  4. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.cpp +2 -2
  5. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.cpp +18 -11
  6. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.cpp +1 -1
  7. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.cpp +2 -2
  8. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.cpp +21 -9
  9. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.cpp +3 -3
  10. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.cpp +3 -3
  11. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.cpp +1 -0
  12. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.cpp +35 -15
  13. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.cpp +13 -8
  14. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.cpp +14 -7
  15. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.cpp +6 -6
  16. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.cpp +38 -29
  17. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.cpp +9 -9
  18. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.cpp +4 -9
  19. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.cpp +4 -4
  20. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.cpp +4 -3
  21. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.cpp +1 -1
  22. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.cpp +22 -44
  23. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.cpp +11 -11
  24. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.cpp +1 -1
  25. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.cpp +43 -26
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-unreal-engine-mcp",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "MCP server giving AI assistants full access to Unreal Engine 5.7 projects",
5
5
  "type": "module",
6
6
  "engines": {
@@ -27,6 +27,7 @@ public class MCPBridgeEditor : ModuleRules
27
27
  "Kismet",
28
28
  "BlueprintGraph",
29
29
  "AssetRegistry",
30
+ "InputCore",
30
31
  "EnhancedInput",
31
32
  "LevelEditor",
32
33
  "SlateCore",
@@ -41,10 +42,12 @@ public class MCPBridgeEditor : ModuleRules
41
42
  "AnimGraphRuntime",
42
43
  "IKRig",
43
44
  "WorldPartitionEditor",
45
+ "DataLayerEditor",
44
46
  "PhysicsCore",
45
47
  "MetasoundEngine",
46
48
  "MetasoundFrontend",
47
49
  "InterchangeEngine",
50
+ "InterchangeCore", // UInterchangeSourceData
48
51
  "InterchangePipelines",
49
52
  "AIModule",
50
53
  "StateTreeModule",
@@ -53,13 +56,15 @@ public class MCPBridgeEditor : ModuleRules
53
56
  "GameplayTags", // Phase 25 - GAS
54
57
  "GameplayTasks", // Phase 25 - GAS
55
58
  "GeometryCollectionEngine", // Phase 26 - Chaos destruction (UGeometryCollectionComponent)
59
+ "Chaos", // Phase 26 - FManagedArrayCollection, FTransformCollection
60
+ "ClothingSystemRuntimeCommon", // Phase 26 - UClothingAssetCommon
56
61
  "ChaosCloth", // Phase 26 - Chaos cloth simulation parameters
57
62
  "LiveLinkInterface", // Phase 27 - ILiveLinkClient, FLiveLinkSubjectKey, FLiveLinkSubjectFrameData
58
63
  "LiveLink", // Phase 27 - ULiveLinkSubjectSettings for per-subject enabled state control
59
- "AvalancheRundown", // Phase 28 - Motion Design Scene State machines (experimental)
60
64
  "AvalancheTransition", // Phase 28 - Motion Design Transition Logic trees (experimental)
61
65
  "RemoteControl", // Phase 28 - Remote Control preset read/write
62
66
  "MovieRenderPipelineCore", // Phase 29 - UMoviePipelineQueue, UMoviePipelineExecutorJob, config/settings types
67
+ "MovieRenderPipelineSettings", // Phase 29 - UMoviePipelineBurnInSetting
63
68
  "MovieRenderPipelineEditor", // Phase 29 - UMoviePipelineQueueSubsystem
64
69
  "OnlineSubsystem", // Phase 30 - IOnlineSubsystem, IOnlineSession, FOnlineSessionSettings
65
70
  "OnlineSubsystemUtils", // Phase 30 - Online subsystem helper utilities
@@ -112,19 +112,22 @@ static TSharedPtr<FJsonObject> BuildBTCompositeNodeJson(UBTCompositeNode* Compos
112
112
  NodeObj->SetStringField(TEXT("node_class"), Composite->GetClass()->GetName());
113
113
  NodeObj->SetStringField(TEXT("node_type"), TEXT("Composite"));
114
114
 
115
- // Build decorators array.
115
+ // Build decorators array from all children's decorators.
116
116
  TArray<TSharedPtr<FJsonValue>> DecoratorsArray;
117
- for (UBTDecorator* Decorator : Composite->Decorators)
117
+ for (const FBTCompositeChild& Child : Composite->Children)
118
118
  {
119
- if (!Decorator)
119
+ for (UBTDecorator* Decorator : Child.Decorators)
120
120
  {
121
- continue;
121
+ if (!Decorator)
122
+ {
123
+ continue;
124
+ }
125
+ TSharedPtr<FJsonObject> DecObj = MakeShared<FJsonObject>();
126
+ DecObj->SetStringField(TEXT("node_name"), Decorator->GetNodeName());
127
+ DecObj->SetStringField(TEXT("node_class"), Decorator->GetClass()->GetName());
128
+ DecObj->SetStringField(TEXT("node_type"), TEXT("Decorator"));
129
+ DecoratorsArray.Add(MakeShared<FJsonValueObject>(DecObj));
122
130
  }
123
- TSharedPtr<FJsonObject> DecObj = MakeShared<FJsonObject>();
124
- DecObj->SetStringField(TEXT("node_name"), Decorator->GetNodeName());
125
- DecObj->SetStringField(TEXT("node_class"), Decorator->GetClass()->GetName());
126
- DecObj->SetStringField(TEXT("node_type"), TEXT("Decorator"));
127
- DecoratorsArray.Add(MakeShared<FJsonValueObject>(DecObj));
128
131
  }
129
132
  NodeObj->SetArrayField(TEXT("decorators"), DecoratorsArray);
130
133
 
@@ -680,7 +683,7 @@ void RegisterAICommands(FMCPCommandRouter& Router)
680
683
 
681
684
  // Iterate the Options array.
682
685
  TArray<TSharedPtr<FJsonValue>> OptionsArray;
683
- for (UEnvQueryOption* Option : Query->Options)
686
+ for (UEnvQueryOption* Option : Query->GetOptions())
684
687
  {
685
688
  if (!Option)
686
689
  {
@@ -821,7 +824,7 @@ void RegisterAICommands(FMCPCommandRouter& Router)
821
824
  }
822
825
 
823
826
  // Get the main navigation data (e.g., RecastNavMesh).
824
- ANavigationData* NavData = NavSys->GetMainNavData();
827
+ ANavigationData* NavData = NavSys->GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
825
828
 
826
829
  FString BuildStatus = TEXT("not_built");
827
830
  FString NavDataClass = TEXT("None");
@@ -177,10 +177,10 @@ void RegisterActorCommands(FMCPCommandRouter& Router)
177
177
  }
178
178
 
179
179
  // Find actor class by name; try exact name then A-prefixed name.
180
- UClass* ActorClass = FindObject<UClass>(ANY_PACKAGE, *ClassName);
180
+ UClass* ActorClass = FindFirstObject<UClass>( *ClassName);
181
181
  if (!ActorClass)
182
182
  {
183
- ActorClass = FindObject<UClass>(ANY_PACKAGE, *(TEXT("A") + ClassName));
183
+ ActorClass = FindFirstObject<UClass>( *(TEXT("A") + ClassName));
184
184
  }
185
185
  if (!ActorClass)
186
186
  {
@@ -17,11 +17,14 @@
17
17
  #include "Animation/AnimBlueprint.h"
18
18
  #include "Animation/AnimMontage.h"
19
19
  #include "Animation/BlendSpace.h"
20
+ #include "Animation/BlendSpace1D.h"
20
21
  #include "Animation/AnimSequence.h"
21
22
 
22
23
  // AnimGraph headers for state machine node access
23
24
  #include "AnimGraphNode_StateMachine.h"
25
+ #include "AnimationStateMachineGraph.h"
24
26
  #include "AnimStateNode.h"
27
+ #include "AnimStateTransitionNode.h"
25
28
 
26
29
  // Asset Registry
27
30
  #include "AssetRegistry/AssetRegistryModule.h"
@@ -30,6 +33,7 @@
30
33
  // IKRetargeter -- from IKRig plugin
31
34
  #if WITH_EDITOR
32
35
  #include "Retargeter/IKRetargeter.h"
36
+ #include "Retargeter/IKRetargetChainMapping.h"
33
37
  #endif
34
38
 
35
39
  // JSON
@@ -268,8 +272,8 @@ void RegisterAnimationCommands(FMCPCommandRouter& Router)
268
272
  TArray<TSharedPtr<FJsonValue>> StatesArray;
269
273
  TArray<TSharedPtr<FJsonValue>> TransitionsArray;
270
274
 
271
- // The state machine graph is referenced by BoundGraph.
272
- UEdGraph* SMGraph = SMNode->GetBoundGraph();
275
+ // The state machine graph is referenced by EditorStateMachineGraph.
276
+ UEdGraph* SMGraph = Cast<UEdGraph>(SMNode->EditorStateMachineGraph.Get());
273
277
  if (SMGraph)
274
278
  {
275
279
  for (UEdGraphNode* SMSubNode : SMGraph->Nodes)
@@ -427,9 +431,9 @@ void RegisterAnimationCommands(FMCPCommandRouter& Router)
427
431
  for (const FCompositeSection& Section : Montage->CompositeSections)
428
432
  {
429
433
  FString LinkedSequenceName;
430
- if (Section.LinkedSequence)
434
+ if (Section.GetLinkedSequence())
431
435
  {
432
- LinkedSequenceName = Section.LinkedSequence->GetPathName();
436
+ LinkedSequenceName = Section.GetLinkedSequence()->GetPathName();
433
437
  }
434
438
 
435
439
  TSharedPtr<FJsonObject> SectionObj = MakeShared<FJsonObject>();
@@ -536,7 +540,7 @@ void RegisterAnimationCommands(FMCPCommandRouter& Router)
536
540
  const int32 NumAxes = bIs1D ? 1 : 2; // 1D uses 1 axis, 2D uses 2 axes (index 0 and 1).
537
541
  for (int32 i = 0; i < NumAxes; ++i)
538
542
  {
539
- const FBlendParameter& Param = BlendSpace->BlendParameters[i];
543
+ const FBlendParameter& Param = BlendSpace->GetBlendParameter(i);
540
544
  TSharedPtr<FJsonObject> AxisObj = MakeShared<FJsonObject>();
541
545
  AxisObj->SetStringField(TEXT("name"), Param.DisplayName);
542
546
  AxisObj->SetNumberField(TEXT("min"), static_cast<double>(Param.Min));
@@ -547,7 +551,7 @@ void RegisterAnimationCommands(FMCPCommandRouter& Router)
547
551
 
548
552
  // Build samples array from SampleData.
549
553
  TArray<TSharedPtr<FJsonValue>> SamplesArray;
550
- for (const FBlendSample& Sample : BlendSpace->SampleData)
554
+ for (const FBlendSample& Sample : BlendSpace->GetBlendSamples())
551
555
  {
552
556
  FString AnimPath;
553
557
  if (Sample.Animation)
@@ -617,13 +621,13 @@ void RegisterAnimationCommands(FMCPCommandRouter& Router)
617
621
  FString SourceRigPath;
618
622
  FString TargetRigPath;
619
623
 
620
- const UIKRigDefinition* SourceRig = Retargeter->GetSourceIKRig();
624
+ const UIKRigDefinition* SourceRig = Retargeter->GetIKRig(ERetargetSourceOrTarget::Source);
621
625
  if (SourceRig)
622
626
  {
623
627
  SourceRigPath = SourceRig->GetPathName();
624
628
  }
625
629
 
626
- const UIKRigDefinition* TargetRig = Retargeter->GetTargetIKRig();
630
+ const UIKRigDefinition* TargetRig = Retargeter->GetIKRig(ERetargetSourceOrTarget::Target);
627
631
  if (TargetRig)
628
632
  {
629
633
  TargetRigPath = TargetRig->GetPathName();
@@ -631,11 +635,14 @@ void RegisterAnimationCommands(FMCPCommandRouter& Router)
631
635
 
632
636
  // Collect chain mappings.
633
637
  TArray<TSharedPtr<FJsonValue>> ChainMappingsArray;
634
- for (const FRetargetChainMap& ChainMap : Retargeter->GetChainMapping())
638
+ PRAGMA_DISABLE_DEPRECATION_WARNINGS
639
+ const TArray<FRetargetChainPair>& ChainPairs = Retargeter->GetChainMapping().GetChainPairs();
640
+ PRAGMA_ENABLE_DEPRECATION_WARNINGS
641
+ for (const FRetargetChainPair& ChainPair : ChainPairs)
635
642
  {
636
643
  TSharedPtr<FJsonObject> MappingObj = MakeShared<FJsonObject>();
637
- MappingObj->SetStringField(TEXT("source_chain"), ChainMap.SourceChain.ToString());
638
- MappingObj->SetStringField(TEXT("target_chain"), ChainMap.TargetChain.ToString());
644
+ MappingObj->SetStringField(TEXT("source_chain"), ChainPair.SourceChainName.ToString());
645
+ MappingObj->SetStringField(TEXT("target_chain"), ChainPair.TargetChainName.ToString());
639
646
  ChainMappingsArray.Add(MakeShared<FJsonValueObject>(MappingObj));
640
647
  }
641
648
 
@@ -287,7 +287,7 @@ void RegisterAudioCommands(FMCPCommandRouter& Router)
287
287
  AssetObj->SetNumberField(TEXT("sample_rate"), static_cast<double>(SoundWave->GetSampleRateForCurrentPlatform()));
288
288
  AssetObj->SetNumberField(TEXT("num_channels"), static_cast<double>(SoundWave->NumChannels));
289
289
  // Compression name from the compression format enum via Audio::ToName().
290
- const FName CompressionFName = Audio::ToName(SoundWave->GetSoundCompressionType());
290
+ const FName CompressionFName = Audio::ToName(SoundWave->GetSoundAssetCompressionType());
291
291
  AssetObj->SetStringField(TEXT("compression_name"), CompressionFName.IsNone() ? TEXT("Unknown") : CompressionFName.ToString());
292
292
  }
293
293
  else
@@ -12,7 +12,7 @@
12
12
 
13
13
  // Blueprint APIs
14
14
  #include "Engine/Blueprint.h"
15
- #include "BlueprintEditorUtils.h"
15
+ #include "Kismet2/BlueprintEditorUtils.h"
16
16
  #include "EdGraph/EdGraph.h"
17
17
  #include "EdGraph/EdGraphNode.h"
18
18
  #include "EdGraph/EdGraphPin.h"
@@ -28,7 +28,7 @@
28
28
  #include "AssetRegistry/AssetRegistryModule.h"
29
29
  #include "AssetRegistry/IAssetRegistry.h"
30
30
  #include "AssetRegistry/AssetData.h"
31
- #include "Blueprint/BlueprintTags.h"
31
+ #include "Blueprint/BlueprintSupport.h"
32
32
 
33
33
  // JSON APIs
34
34
  #include "Dom/JsonObject.h"
@@ -31,6 +31,7 @@
31
31
  // Asset APIs
32
32
  #include "AssetRegistry/AssetRegistryModule.h"
33
33
  #include "UObject/Package.h"
34
+ #include "UObject/SavePackage.h"
34
35
  #include "Misc/PackageName.h"
35
36
 
36
37
  // JSON APIs
@@ -146,17 +147,17 @@ void RegisterBlueprintWriteHandlers(FMCPCommandRouter& Router)
146
147
 
147
148
  // T-10-01: Validate the asset path is a well-formed long package name.
148
149
  // Rejects path traversal ("../") and bare filenames.
149
- FString ValidationError;
150
+ FText ValidationError;
150
151
  if (!FPackageName::IsValidLongPackageName(AssetPath, /*bIncludeReadOnlyRoots=*/false, &ValidationError))
151
152
  {
152
153
  UE_LOG(LogTemp, Warning, TEXT("[MCPBridge] blueprint.create: Invalid asset path '%s': %s"),
153
- *AssetPath, *ValidationError);
154
+ *AssetPath, *ValidationError.ToString());
154
155
  SendError(SendResponse, CorrelationId, TEXT("invalid_asset_path"));
155
156
  return;
156
157
  }
157
158
 
158
159
  // Resolve the parent class by name (try short name first, then full path).
159
- UClass* ParentClass = FindObject<UClass>(ANY_PACKAGE, *ParentClassName);
160
+ UClass* ParentClass = FindFirstObject<UClass>( *ParentClassName);
160
161
  if (!ParentClass)
161
162
  {
162
163
  ParentClass = LoadObject<UClass>(nullptr, *ParentClassName);
@@ -214,8 +215,10 @@ void RegisterBlueprintWriteHandlers(FMCPCommandRouter& Router)
214
215
  // Save the package to disk so the asset persists.
215
216
  FString FilePath = FPackageName::LongPackageNameToFilename(
216
217
  AssetPath, FPackageName::GetAssetPackageExtension());
217
- UPackage::SavePackage(Pkg, NewBP, RF_Standalone, *FilePath,
218
- GError, nullptr, false, true, SAVE_NoError);
218
+ FSavePackageArgs SaveArgs;
219
+ SaveArgs.TopLevelFlags = RF_Standalone;
220
+ SaveArgs.SaveFlags = SAVE_NoError;
221
+ UPackage::SavePackage(Pkg, NewBP, *FilePath, SaveArgs);
219
222
 
220
223
  // Notify the Asset Registry so the asset appears in the Content Browser.
221
224
  FAssetRegistryModule::AssetCreated(NewBP);
@@ -288,7 +291,7 @@ void RegisterBlueprintWriteHandlers(FMCPCommandRouter& Router)
288
291
  }
289
292
 
290
293
  // T-10-02: Resolve node class and validate it is a non-abstract UEdGraphNode subclass.
291
- UClass* NodeClass = FindObject<UClass>(ANY_PACKAGE, *NodeType);
294
+ UClass* NodeClass = FindFirstObject<UClass>( *NodeType);
292
295
  if (!NodeClass)
293
296
  {
294
297
  UE_LOG(LogTemp, Warning, TEXT("[MCPBridge] blueprint.addNode: Node type '%s' not found"), *NodeType);
@@ -649,9 +652,18 @@ void RegisterBlueprintWriteHandlers(FMCPCommandRouter& Router)
649
652
  return;
650
653
  }
651
654
 
652
- // T-10-05: SetBlueprintVariableDefaultValue returns bool; use named error code on failure.
653
- bool bSet = FBlueprintEditorUtils::SetBlueprintVariableDefaultValue(
654
- BP, FName(*TargetName), DefaultValue);
655
+ // Find the variable in NewVariables and set its DefaultValue directly.
656
+ bool bSet = false;
657
+ const FName VarName(*TargetName);
658
+ for (FBPVariableDescription& VarDesc : BP->NewVariables)
659
+ {
660
+ if (VarDesc.VarName == VarName)
661
+ {
662
+ VarDesc.DefaultValue = DefaultValue;
663
+ bSet = true;
664
+ break;
665
+ }
666
+ }
655
667
  if (!bSet)
656
668
  {
657
669
  UE_LOG(LogTemp, Warning, TEXT("[MCPBridge] blueprint.setDefault: Variable '%s' not found in '%s'"),
@@ -399,10 +399,10 @@ void RegisterChaosCommands(FMCPCommandRouter& Router)
399
399
  GeoComp->Modify();
400
400
 
401
401
  // Reset to initial unfractured state.
402
- // UGeometryCollectionComponent::SetSimulatePhysics(false) + SetSimulatePhysics(true)
403
- // is the reliable fallback approach that works across UE 5.x versions.
402
+ // ResetDynamicCollection() is protected in UE 5.7, so we use
403
+ // SetSimulatePhysics(false) + RecreatePhysicsState() as the public alternative.
404
404
  GeoComp->SetSimulatePhysics(false);
405
- GeoComp->ResetDynamicCollection();
405
+ GeoComp->RecreatePhysicsState();
406
406
 
407
407
  // Notify editor of the change.
408
408
  GeoComp->PostEditChange();
@@ -21,7 +21,7 @@
21
21
  #include "EngineUtils.h"
22
22
  #include "Components/PrimitiveComponent.h"
23
23
  #include "Engine/EngineTypes.h"
24
- #include "CollisionProfile.h"
24
+ #include "Engine/CollisionProfile.h"
25
25
  #include "PhysicsEngine/BodyInstance.h"
26
26
  #include "PhysicalMaterials/PhysicalMaterial.h"
27
27
  #include "PhysicsEngine/PhysicsAsset.h"
@@ -118,7 +118,7 @@ static TArray<TSharedPtr<FJsonValue>> BuildCollisionResponseArray(UPrimitiveComp
118
118
  if (Profile)
119
119
  {
120
120
  // GetChannelName returns the display name for a given channel index.
121
- ChannelName = Profile->GetChannelName(Channel).ToString();
121
+ ChannelName = Profile->ReturnChannelNameFromContainerIndex(static_cast<int32>(Channel)).ToString();
122
122
  }
123
123
  if (ChannelName.IsEmpty())
124
124
  {
@@ -148,7 +148,7 @@ static TSharedPtr<FJsonObject> BuildCollisionData(UPrimitiveComponent* Comp)
148
148
  FString ObjectTypeName;
149
149
  if (Profile)
150
150
  {
151
- ObjectTypeName = Profile->GetChannelName(ObjCh).ToString();
151
+ ObjectTypeName = Profile->ReturnChannelNameFromContainerIndex(static_cast<int32>(ObjCh)).ToString();
152
152
  }
153
153
  if (ObjectTypeName.IsEmpty())
154
154
  {
@@ -9,6 +9,7 @@
9
9
  #include "MCPEditorStateCommands.h"
10
10
 
11
11
  #include "Editor.h"
12
+ #include "Selection.h"
12
13
  #include "EditorViewportClient.h"
13
14
  #include "LevelEditorViewport.h"
14
15
  #include "EngineUtils.h"
@@ -226,19 +226,35 @@ static void HandleGasAbilities(TSharedPtr<FJsonObject> Cmd, FMCPResponseSender S
226
226
  AbilityObj->SetArrayField(TEXT("ability_tags"),
227
227
  GameplayTagContainerToJsonArray(AbilityCDO->AbilityTags));
228
228
 
229
- // Cancel / block tags
230
- AbilityObj->SetArrayField(TEXT("cancel_abilities_with_tag"),
231
- GameplayTagContainerToJsonArray(AbilityCDO->CancelAbilitiesWithTag));
232
- AbilityObj->SetArrayField(TEXT("block_abilities_with_tag"),
233
- GameplayTagContainerToJsonArray(AbilityCDO->BlockAbilitiesWithTag));
229
+ // Cancel / block tags (protected in UE 5.7, read via UE reflection)
230
+ {
231
+ TArray<TSharedPtr<FJsonValue>> CancelTagsArray;
232
+ TArray<TSharedPtr<FJsonValue>> BlockTagsArray;
233
+ if (FStructProperty* CancelProp = CastField<FStructProperty>(AbilityCDO->GetClass()->FindPropertyByName(TEXT("CancelAbilitiesWithTag"))))
234
+ {
235
+ const FGameplayTagContainer* CancelTags = CancelProp->ContainerPtrToValuePtr<FGameplayTagContainer>(AbilityCDO);
236
+ if (CancelTags)
237
+ {
238
+ CancelTagsArray = GameplayTagContainerToJsonArray(*CancelTags);
239
+ }
240
+ }
241
+ if (FStructProperty* BlockProp = CastField<FStructProperty>(AbilityCDO->GetClass()->FindPropertyByName(TEXT("BlockAbilitiesWithTag"))))
242
+ {
243
+ const FGameplayTagContainer* BlockTags = BlockProp->ContainerPtrToValuePtr<FGameplayTagContainer>(AbilityCDO);
244
+ if (BlockTags)
245
+ {
246
+ BlockTagsArray = GameplayTagContainerToJsonArray(*BlockTags);
247
+ }
248
+ }
249
+ AbilityObj->SetArrayField(TEXT("cancel_abilities_with_tag"), CancelTagsArray);
250
+ AbilityObj->SetArrayField(TEXT("block_abilities_with_tag"), BlockTagsArray);
251
+ }
234
252
 
235
253
  // Cost and cooldown GE class references
236
- FString CostGEName = AbilityCDO->CostGameplayEffectClass
237
- ? AbilityCDO->CostGameplayEffectClass->GetName()
238
- : TEXT("None");
239
- FString CooldownGEName = AbilityCDO->CooldownGameplayEffectClass
240
- ? AbilityCDO->CooldownGameplayEffectClass->GetName()
241
- : TEXT("None");
254
+ UGameplayEffect* CostGE = AbilityCDO->GetCostGameplayEffect();
255
+ FString CostGEName = CostGE ? CostGE->GetClass()->GetName() : TEXT("None");
256
+ UGameplayEffect* CooldownGE = AbilityCDO->GetCooldownGameplayEffect();
257
+ FString CooldownGEName = CooldownGE ? CooldownGE->GetClass()->GetName() : TEXT("None");
242
258
  AbilityObj->SetStringField(TEXT("cost_gameplay_effect_class"), CostGEName);
243
259
  AbilityObj->SetStringField(TEXT("cooldown_gameplay_effect_class"), CooldownGEName);
244
260
 
@@ -415,13 +431,17 @@ static void HandleGasEffects(TSharedPtr<FJsonObject> Cmd, FMCPResponseSender Sen
415
431
  Data->SetNumberField(TEXT("stack_limit_count"), (double)GEObj->StackLimitCount);
416
432
 
417
433
  // Period interval (period is a FScalableFloat, extract base value)
418
- float PeriodValue = 0.0f;
419
- GEObj->Period.GetStaticValue(PeriodValue);
434
+ float PeriodValue = GEObj->Period.GetValue();
420
435
  Data->SetNumberField(TEXT("period_interval"), (double)PeriodValue);
421
436
 
422
- // Gameplay Cue tags
437
+ // Gameplay Cue tags (collected from all FGameplayEffectCue entries)
438
+ FGameplayTagContainer AllCueTags;
439
+ for (const FGameplayEffectCue& Cue : GEObj->GameplayCues)
440
+ {
441
+ AllCueTags.AppendTags(Cue.GameplayCueTags);
442
+ }
423
443
  Data->SetArrayField(TEXT("gameplay_cue_tags"),
424
- GameplayTagContainerToJsonArray(GEObj->GameplayCueTags));
444
+ GameplayTagContainerToJsonArray(AllCueTags));
425
445
 
426
446
  SendResponse(BuildGASSuccessResponse(CorrId, Data) + TEXT("\n"));
427
447
  }
@@ -21,14 +21,17 @@
21
21
  #include "AssetToolsModule.h"
22
22
  #include "IAssetTools.h"
23
23
  #include "AssetImportTask.h"
24
- #include "PackageName.h"
24
+ #include "Misc/PackageName.h"
25
25
 
26
26
  // FBX Factory and Export
27
27
  #include "Factories/FbxFactory.h"
28
+ #include "Factories/FbxImportUI.h"
29
+ #include "Factories/FbxStaticMeshImportData.h"
28
30
  #include "Exporters/FbxExportOption.h"
29
31
 
30
32
  // Interchange (USD import)
31
33
  #include "InterchangeManager.h"
34
+ #include "InterchangeSourceData.h"
32
35
 
33
36
  // Mesh types
34
37
  #include "Engine/StaticMesh.h"
@@ -209,9 +212,9 @@ void RegisterImportExportCommands(FMCPCommandRouter& Router)
209
212
  if (FbxFactory->ImportUI)
210
213
  {
211
214
  FbxFactory->ImportUI->bImportMaterials = bImportMaterials;
212
- FbxFactory->ImportUI->bCombineMeshes = bCombineMeshes;
213
215
  if (FbxFactory->ImportUI->StaticMeshImportData)
214
216
  {
217
+ FbxFactory->ImportUI->StaticMeshImportData->bCombineMeshes = bCombineMeshes;
215
218
  FbxFactory->ImportUI->StaticMeshImportData->ImportUniformScale = static_cast<float>(ScaleFactor);
216
219
  }
217
220
  }
@@ -301,12 +304,11 @@ void RegisterImportExportCommands(FMCPCommandRouter& Router)
301
304
 
302
305
  if (GEditor && FModuleManager::Get().IsModuleLoaded(TEXT("InterchangeEngine")))
303
306
  {
304
- UInterchangeManager* InterchangeManager = UInterchangeManager::GetInterchangeManager();
305
- if (InterchangeManager)
307
+ UInterchangeManager& InterchangeManager = UInterchangeManager::GetInterchangeManager();
308
+ if (true) // InterchangeManager is always valid when module is loaded
306
309
  {
307
- // Use Interchange to import the USD file.
308
310
  // Set up import parameters with the destination path.
309
- UE::Interchange::FImportAssetParameters Params;
311
+ FImportAssetParameters Params;
310
312
  Params.bIsAutomated = true;
311
313
 
312
314
  // Determine content path for Interchange. Normalize dest_path to directory.
@@ -320,9 +322,12 @@ void RegisterImportExportCommands(FMCPCommandRouter& Router)
320
322
  ContentDir = FPackageName::GetLongPackagePath(ContentDir);
321
323
  }
322
324
  }
323
- Params.OverrideDestinationPath = ContentDir;
324
325
 
325
- InterchangeManager->ImportAsset(SourceFile, Params);
326
+ // Create UInterchangeSourceData from the source file path.
327
+ UInterchangeSourceData* SourceData = NewObject<UInterchangeSourceData>(GetTransientPackage());
328
+ SourceData->SetFilename(SourceFile);
329
+
330
+ InterchangeManager.ImportAsset(ContentDir, SourceData, Params);
326
331
 
327
332
  // Interchange is async; we report success with empty assets list.
328
333
  // The engine will place assets at ContentDir when complete.
@@ -23,6 +23,9 @@
23
23
  // Asset Registry APIs
24
24
  #include "AssetRegistry/AssetRegistryModule.h"
25
25
 
26
+ // Package saving
27
+ #include "UObject/SavePackage.h"
28
+
26
29
  // UObject/Package APIs
27
30
  #include "UObject/Package.h"
28
31
  #include "Misc/PackageName.h"
@@ -177,11 +180,11 @@ void RegisterInputHandlers(FMCPCommandRouter& Router)
177
180
 
178
181
  // T-14-01: Validate the asset path is a well-formed long package name.
179
182
  // Rejects path traversal ("../") and bare filenames.
180
- FString ValidationError;
183
+ FText ValidationError;
181
184
  if (!FPackageName::IsValidLongPackageName(AssetPath, /*bIncludeReadOnlyRoots=*/false, &ValidationError))
182
185
  {
183
186
  UE_LOG(LogTemp, Warning, TEXT("[MCPBridge] input.createAction: Invalid asset path '%s': %s"),
184
- *AssetPath, *ValidationError);
187
+ *AssetPath, *ValidationError.ToString());
185
188
  SendError(SendResponse, CorrelationId, TEXT("invalid_asset_path"));
186
189
  return;
187
190
  }
@@ -227,8 +230,10 @@ void RegisterInputHandlers(FMCPCommandRouter& Router)
227
230
  // Save the package to disk so the asset persists.
228
231
  FString FilePath = FPackageName::LongPackageNameToFilename(
229
232
  AssetPath, FPackageName::GetAssetPackageExtension());
230
- UPackage::SavePackage(Pkg, Action, RF_Standalone, *FilePath,
231
- GError, nullptr, false, true, SAVE_NoError);
233
+ FSavePackageArgs SaveArgs;
234
+ SaveArgs.TopLevelFlags = RF_Standalone;
235
+ SaveArgs.SaveFlags = SAVE_NoError;
236
+ UPackage::SavePackage(Pkg, Action, *FilePath, SaveArgs);
232
237
 
233
238
  // Notify the Asset Registry so the asset appears in the Content Browser.
234
239
  FAssetRegistryModule::AssetCreated(Action);
@@ -331,7 +336,7 @@ void RegisterInputHandlers(FMCPCommandRouter& Router)
331
336
 
332
337
  // T-14-02: Validate that the key name resolves to a known FKey before calling MapKey.
333
338
  // FKey constructor accepts any FName; IsValid() checks whether it is a registered key.
334
- const FKey ResolvedKey(FName(*KeyName));
339
+ const FKey ResolvedKey = FKey(FName(*KeyName));
335
340
  if (!ResolvedKey.IsValid())
336
341
  {
337
342
  UE_LOG(LogTemp, Warning, TEXT("[MCPBridge] input.addBinding: Key '%s' is not a valid FKey"), *KeyName);
@@ -369,8 +374,10 @@ void RegisterInputHandlers(FMCPCommandRouter& Router)
369
374
  UPackage* Pkg = IMC->GetPackage();
370
375
  FString FilePath = FPackageName::LongPackageNameToFilename(
371
376
  AssetPath, FPackageName::GetAssetPackageExtension());
372
- UPackage::SavePackage(Pkg, IMC, RF_Standalone, *FilePath,
373
- GError, nullptr, false, true, SAVE_NoError);
377
+ FSavePackageArgs SaveArgs2;
378
+ SaveArgs2.TopLevelFlags = RF_Standalone;
379
+ SaveArgs2.SaveFlags = SAVE_NoError;
380
+ UPackage::SavePackage(Pkg, IMC, *FilePath, SaveArgs2);
374
381
 
375
382
  TSharedPtr<FJsonObject> Data = MakeShared<FJsonObject>();
376
383
  Data->SetBoolField(TEXT("bound"), true);
@@ -206,7 +206,7 @@ void RegisterLiveLinkCommands(FMCPCommandRouter& Router)
206
206
  ILiveLinkClient& Client = IModularFeatures::Get().GetModularFeature<ILiveLinkClient>(ILiveLinkClient::ModularFeatureName);
207
207
 
208
208
  // Include disabled subjects so the full list is visible.
209
- TArray<FLiveLinkSubjectKey> SubjectKeys = Client.GetSubjects(/*bIncludeDisabledSubjects=*/true);
209
+ TArray<FLiveLinkSubjectKey> SubjectKeys = Client.GetSubjects(/*bIncludeDisabledSubjects=*/true, /*bIncludeVirtualSubjects=*/true);
210
210
 
211
211
  TArray<TSharedPtr<FJsonValue>> SubjectArray;
212
212
  SubjectArray.Reserve(SubjectKeys.Num());
@@ -223,8 +223,8 @@ void RegisterLiveLinkCommands(FMCPCommandRouter& Router)
223
223
  if (Settings)
224
224
  {
225
225
  RoleName = GetRoleFriendlyName(Settings->Role);
226
- bEnabled = Settings->bEnabled;
227
226
  }
227
+ bEnabled = Client.IsSubjectEnabled(SubjectKey, /*bForThisFrame=*/false);
228
228
 
229
229
  TSharedPtr<FJsonObject> SubjectObj = MakeShared<FJsonObject>();
230
230
  SubjectObj->SetStringField(TEXT("subject_name"), SubjectName);
@@ -278,7 +278,7 @@ void RegisterLiveLinkCommands(FMCPCommandRouter& Router)
278
278
  ILiveLinkClient& Client = IModularFeatures::Get().GetModularFeature<ILiveLinkClient>(ILiveLinkClient::ModularFeatureName);
279
279
 
280
280
  // T-27-01: Find subject by exact name match from GetSubjects() -- never use raw input as key.
281
- TArray<FLiveLinkSubjectKey> SubjectKeys = Client.GetSubjects(/*bIncludeDisabledSubjects=*/true);
281
+ TArray<FLiveLinkSubjectKey> SubjectKeys = Client.GetSubjects(/*bIncludeDisabledSubjects=*/true, /*bIncludeVirtualSubjects=*/true);
282
282
 
283
283
  bool bFound = false;
284
284
  for (const FLiveLinkSubjectKey& SubjectKey : SubjectKeys)
@@ -294,7 +294,7 @@ void RegisterLiveLinkCommands(FMCPCommandRouter& Router)
294
294
  }
295
295
 
296
296
  // Toggle the enabled state (runtime-only; no Modify() needed for Live Link subject state).
297
- Settings->bEnabled = bEnabled;
297
+ Client.SetSubjectEnabled(SubjectKey, bEnabled);
298
298
 
299
299
  const FString StateMessage = bEnabled
300
300
  ? TEXT("Subject resumed")
@@ -349,7 +349,7 @@ void RegisterLiveLinkCommands(FMCPCommandRouter& Router)
349
349
  ILiveLinkClient& Client = IModularFeatures::Get().GetModularFeature<ILiveLinkClient>(ILiveLinkClient::ModularFeatureName);
350
350
 
351
351
  // T-27-01: Find subject by exact name match from GetSubjects() -- never use raw input as key.
352
- TArray<FLiveLinkSubjectKey> SubjectKeys = Client.GetSubjects(/*bIncludeDisabledSubjects=*/true);
352
+ TArray<FLiveLinkSubjectKey> SubjectKeys = Client.GetSubjects(/*bIncludeDisabledSubjects=*/true, /*bIncludeVirtualSubjects=*/true);
353
353
 
354
354
  bool bFound = false;
355
355
  for (const FLiveLinkSubjectKey& SubjectKey : SubjectKeys)
@@ -374,7 +374,7 @@ void RegisterLiveLinkCommands(FMCPCommandRouter& Router)
374
374
 
375
375
  // Retrieve the latest frame data.
376
376
  FLiveLinkSubjectFrameData FrameData;
377
- const bool bHasData = Client.EvaluateFrame_AnyThread(SubjectKey, RoleClass, FrameData);
377
+ const bool bHasData = Client.EvaluateFrame_AnyThread(SubjectKey.SubjectName, RoleClass, FrameData);
378
378
 
379
379
  if (!bHasData || !FrameData.StaticData.IsValid() || !FrameData.FrameData.IsValid())
380
380
  {