ue-mcp 1.0.9 → 1.0.10

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.
@@ -25,6 +25,6 @@
25
25
  "feedback": 1,
26
26
  "statetree": 35
27
27
  },
28
- "generatedAt": "2026-05-18T03:38:46.726Z",
29
- "version": "1.0.9"
28
+ "generatedAt": "2026-05-18T20:52:21.686Z",
29
+ "version": "1.0.10"
30
30
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ue-mcp",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "Unreal Engine MCP server - 20 tools, 522+ actions for AI-driven editor control",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,7 @@
1
1
  #pragma once
2
2
 
3
3
  #include "CoreMinimal.h"
4
+ #include "Runtime/Launch/Resources/Version.h"
4
5
  #include "Dom/JsonValue.h"
5
6
  #include "Dom/JsonObject.h"
6
7
  #include "UObject/UObjectIterator.h"
@@ -12,6 +13,12 @@
12
13
  #include "EngineUtils.h"
13
14
  #include "GameFramework/Actor.h"
14
15
 
16
+ // True on UE 5.5+ (and any future 6.x). Used to gate APIs introduced in 5.5
17
+ // that don't exist in 5.4: StateTreeEditingSubsystem, FExpressionInputIterator,
18
+ // AActor::Get/SetNetUpdateFrequency, UWidgetBlueprint::WidgetVariableNameToGuidMap,
19
+ // UPCGEditorGraphNodeBase, UIKRetargeterController::AssignIKRigToAllOps, etc.
20
+ #define UE_MCP_HAS_5_5_API ((ENGINE_MAJOR_VERSION > 5) || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 5))
21
+
15
22
  // ── Quick result builders ────────────────────────────────────────────────────
16
23
 
17
24
  /** Return an error response: { success: false, error: "..." } */
@@ -19,7 +19,11 @@
19
19
  #include "Animation/AnimNotifies/AnimNotify.h"
20
20
  #include "PhysicsEngine/PhysicsAsset.h"
21
21
  #include "Engine/SkeletalMeshSocket.h"
22
+ // PhysicsEngine/SkeletalBodySetup.h is unavailable as a public include on
23
+ // UE 5.4. USkeletalBodySetup is still defined transitively via PhysicsAsset.h.
24
+ #if __has_include("PhysicsEngine/SkeletalBodySetup.h")
22
25
  #include "PhysicsEngine/SkeletalBodySetup.h"
26
+ #endif
23
27
  #include "EditorScriptingUtilities/Public/EditorAssetLibrary.h"
24
28
  #include "Factories/AnimBlueprintFactory.h"
25
29
  #include "Factories/AnimMontageFactory.h"
@@ -47,6 +47,19 @@
47
47
  #include "Dom/JsonValue.h"
48
48
 
49
49
 
50
+ // UPoseSearchDatabase's "asset count" accessor was renamed between 5.4 and 5.5:
51
+ // 5.4: GetAnimationAssets().Num()
52
+ // 5.5+: GetNumAnimationAssets()
53
+ // Use one helper so the call sites stay readable.
54
+ static int32 GetPoseSearchAssetCount(const UPoseSearchDatabase* Database)
55
+ {
56
+ #if UE_MCP_HAS_5_5_API
57
+ return Database->GetNumAnimationAssets();
58
+ #else
59
+ return GetPoseSearchAssetCount(Database);
60
+ #endif
61
+ }
62
+
50
63
  // ─── State Machine Helpers ────────────────────────────────────────
51
64
 
52
65
  static UAnimBlueprint* LoadAnimBP(const FString& Path)
@@ -966,6 +979,7 @@ TSharedPtr<FJsonValue> FAnimationHandlers::CreateIKRetargeter(const TSharedPtr<F
966
979
  UIKRetargeterController* Controller = UIKRetargeterController::GetController(Retargeter);
967
980
  if (Controller)
968
981
  {
982
+ #if UE_MCP_HAS_5_5_API
969
983
  if (!SourceRigPath.IsEmpty())
970
984
  {
971
985
  if (UIKRigDefinition* SrcRig = Cast<UIKRigDefinition>(LoadObject<UObject>(nullptr, *SourceRigPath)))
@@ -989,10 +1003,16 @@ TSharedPtr<FJsonValue> FAnimationHandlers::CreateIKRetargeter(const TSharedPtr<F
989
1003
  if (!Controller->GetSourceChain(C.ChainName).IsNone()) ChainsMapped++;
990
1004
  }
991
1005
  }
1006
+ #else
1007
+ // 5.4 has no ops-stack: per-op rig assignment + AutoMapChains(EAutoMapChainType,bool)
1008
+ // don't exist. The retargeter still works from SourceIKRigAsset/TargetIKRigAsset
1009
+ // properties; chain auto-mapping must be triggered manually in the IK Retargeter editor.
1010
+ OpsWarning = TEXT("autoMapChains requires UE 5.5+; open the IK Retargeter and auto-map chains manually.");
1011
+ #endif
992
1012
  }
993
1013
  else
994
1014
  {
995
- OpsWarning = TEXT("IKRetargeterController unavailable chains not mapped (call autoMapChains=false to suppress)");
1015
+ OpsWarning = TEXT("IKRetargeterController unavailable - chains not mapped (call autoMapChains=false to suppress)");
996
1016
  }
997
1017
  }
998
1018
  }
@@ -1150,7 +1170,7 @@ TSharedPtr<FJsonValue> FAnimationHandlers::AddPoseSearchSequence(const TSharedPt
1150
1170
  return MCPError(FString::Printf(TEXT("Animation asset type not supported by PoseSearch: %s"), *AnimAsset->GetClass()->GetName()));
1151
1171
  }
1152
1172
 
1153
- const int32 PrevCount = Database->GetNumAnimationAssets();
1173
+ const int32 PrevCount = GetPoseSearchAssetCount(Database);
1154
1174
  FPoseSearchDatabaseAnimationAsset NewEntry;
1155
1175
  NewEntry.AnimAsset = AnimAsset;
1156
1176
  Database->Modify();
@@ -1158,7 +1178,7 @@ TSharedPtr<FJsonValue> FAnimationHandlers::AddPoseSearchSequence(const TSharedPt
1158
1178
  Database->PostEditChange();
1159
1179
  UEditorAssetLibrary::SaveLoadedAsset(Database);
1160
1180
 
1161
- const int32 NewCount = Database->GetNumAnimationAssets();
1181
+ const int32 NewCount = GetPoseSearchAssetCount(Database);
1162
1182
 
1163
1183
  TSharedPtr<FJsonObject> Res = MCPSuccess();
1164
1184
  MCPSetUpdated(Res);
@@ -1180,7 +1200,7 @@ TSharedPtr<FJsonValue> FAnimationHandlers::BuildPoseSearchIndex(const TSharedPtr
1180
1200
  UPoseSearchDatabase* Database = Cast<UPoseSearchDatabase>(UEditorAssetLibrary::LoadAsset(AssetPath));
1181
1201
  if (!Database) return MCPError(FString::Printf(TEXT("PoseSearchDatabase not found: %s"), *AssetPath));
1182
1202
  if (!Database->Schema) return MCPError(TEXT("Database has no Schema set — call set_pose_search_schema first"));
1183
- if (Database->GetNumAnimationAssets() == 0) return MCPError(TEXT("Database has no animation assets — call add_pose_search_sequence first"));
1203
+ if (GetPoseSearchAssetCount(Database) == 0) return MCPError(TEXT("Database has no animation assets — call add_pose_search_sequence first"));
1184
1204
 
1185
1205
  using namespace UE::PoseSearch;
1186
1206
  const ERequestAsyncBuildFlag Flag = bWait
@@ -1205,7 +1225,7 @@ TSharedPtr<FJsonValue> FAnimationHandlers::BuildPoseSearchIndex(const TSharedPtr
1205
1225
  Res->SetStringField(TEXT("path"), AssetPath);
1206
1226
  Res->SetStringField(TEXT("result"), ResultStr);
1207
1227
  Res->SetBoolField(TEXT("waitedForCompletion"), bWait);
1208
- Res->SetNumberField(TEXT("animationAssetCount"), Database->GetNumAnimationAssets());
1228
+ Res->SetNumberField(TEXT("animationAssetCount"), GetPoseSearchAssetCount(Database));
1209
1229
  return MCPResult(Res);
1210
1230
  }
1211
1231
 
@@ -1227,7 +1247,7 @@ TSharedPtr<FJsonValue> FAnimationHandlers::ReadPoseSearchDatabase(const TSharedP
1227
1247
  Res->SetNumberField(TEXT("loopingCostBias"), Database->LoopingCostBias);
1228
1248
  Res->SetNumberField(TEXT("kdTreeQueryNumNeighbors"), Database->KDTreeQueryNumNeighbors);
1229
1249
 
1230
- const int32 AssetCount = Database->GetNumAnimationAssets();
1250
+ const int32 AssetCount = GetPoseSearchAssetCount(Database);
1231
1251
  Res->SetNumberField(TEXT("animationAssetCount"), AssetCount);
1232
1252
 
1233
1253
  TArray<TSharedPtr<FJsonValue>> Animations;
@@ -6,6 +6,11 @@
6
6
  #include "AssetHandlers.h"
7
7
  #include "HandlerRegistry.h"
8
8
  #include "HandlerUtils.h"
9
+ // FSkeletalMaterial moved out of Engine/SkeletalMesh.h in later UE versions.
10
+ // Pull it explicitly via SkinnedAssetCommon when available.
11
+ #if __has_include("Engine/SkinnedAssetCommon.h")
12
+ #include "Engine/SkinnedAssetCommon.h"
13
+ #endif
9
14
  #include "HandlerJsonProperty.h"
10
15
  #include "AssetRegistry/AssetRegistryModule.h"
11
16
  #include "AssetRegistry/IAssetRegistry.h"
@@ -19,7 +19,9 @@
19
19
  #include "HAL/PlatformFileManager.h"
20
20
  #include "HAL/PlatformFile.h"
21
21
  #include "Modules/ModuleManager.h"
22
+ #if PLATFORM_WINDOWS
22
23
  #include "ILiveCodingModule.h"
24
+ #endif
23
25
  #include "Kismet/KismetSystemLibrary.h"
24
26
  #include "EditorValidatorSubsystem.h"
25
27
  #include "Dom/JsonObject.h"
@@ -574,7 +574,13 @@ TSharedPtr<FJsonValue> FLandscapeHandlers::CreateLandscape(const TSharedPtr<FJso
574
574
  nullptr,
575
575
  ImportLayerInfo,
576
576
  ELandscapeImportAlphamapType::Additive,
577
- MakeArrayView(EmptyLayers));
577
+ #if UE_MCP_HAS_5_5_API
578
+ MakeArrayView(EmptyLayers)
579
+ #else
580
+ // 5.4: last arg is const TArray<FLandscapeLayer>* (TArrayView signature added in 5.5).
581
+ &EmptyLayers
582
+ #endif
583
+ );
578
584
 
579
585
  if (!Label.IsEmpty())
580
586
  {
@@ -96,6 +96,7 @@ TSharedPtr<FJsonValue> FMaterialHandlers::ValidateMaterial(const TSharedPtr<FJso
96
96
  while (Stack.Num() > 0)
97
97
  {
98
98
  UMaterialExpression* Expr = Stack.Pop();
99
+ #if UE_MCP_HAS_5_5_API
99
100
  for (FExpressionInputIterator It{ Expr }; It; ++It)
100
101
  {
101
102
  if (It->Expression && !Referenced.Contains(It->Expression))
@@ -104,6 +105,18 @@ TSharedPtr<FJsonValue> FMaterialHandlers::ValidateMaterial(const TSharedPtr<FJso
104
105
  Stack.Add(It->Expression);
105
106
  }
106
107
  }
108
+ #else
109
+ // FExpressionInputIterator was added in 5.5; on 5.4 use the legacy GetInput(i) loop.
110
+ for (int32 InputIdx = 0, InputCount = Expr->GetInputs().Num(); InputIdx < InputCount; ++InputIdx)
111
+ {
112
+ FExpressionInput* In = Expr->GetInput(InputIdx);
113
+ if (In && In->Expression && !Referenced.Contains(In->Expression))
114
+ {
115
+ Referenced.Add(In->Expression);
116
+ Stack.Add(In->Expression);
117
+ }
118
+ }
119
+ #endif
107
120
  }
108
121
 
109
122
  auto AllExpressions = Material->GetExpressions();
@@ -71,8 +71,13 @@ TSharedPtr<FJsonValue> FNetworkingHandlers::GetNetworkingInfo(const TSharedPtr<F
71
71
 
72
72
  Result->SetStringField(TEXT("blueprintPath"), BlueprintPath);
73
73
  Result->SetBoolField(TEXT("replicates"), CDO->GetIsReplicated());
74
+ #if UE_MCP_HAS_5_5_API
74
75
  Result->SetNumberField(TEXT("netUpdateFrequency"), CDO->GetNetUpdateFrequency());
75
76
  Result->SetNumberField(TEXT("minNetUpdateFrequency"), CDO->GetMinNetUpdateFrequency());
77
+ #else
78
+ Result->SetNumberField(TEXT("netUpdateFrequency"), CDO->NetUpdateFrequency);
79
+ Result->SetNumberField(TEXT("minNetUpdateFrequency"), CDO->MinNetUpdateFrequency);
80
+ #endif
76
81
  Result->SetNumberField(TEXT("netPriority"), CDO->NetPriority);
77
82
  Result->SetBoolField(TEXT("alwaysRelevant"), CDO->bAlwaysRelevant);
78
83
  Result->SetBoolField(TEXT("replicateMovement"), CDO->IsReplicatingMovement());
@@ -129,20 +134,33 @@ TSharedPtr<FJsonValue> FNetworkingHandlers::ConfigureNetUpdateFrequency(const TS
129
134
  double NetUpdateFrequency = 0;
130
135
  if (Params->TryGetNumberField(TEXT("netUpdateFrequency"), NetUpdateFrequency))
131
136
  {
137
+ #if UE_MCP_HAS_5_5_API
132
138
  CDO->SetNetUpdateFrequency((float)NetUpdateFrequency);
139
+ #else
140
+ CDO->NetUpdateFrequency = (float)NetUpdateFrequency;
141
+ #endif
133
142
  }
134
143
  double MinNetUpdateFrequency = 0;
135
144
  if (Params->TryGetNumberField(TEXT("minNetUpdateFrequency"), MinNetUpdateFrequency))
136
145
  {
146
+ #if UE_MCP_HAS_5_5_API
137
147
  CDO->SetMinNetUpdateFrequency((float)MinNetUpdateFrequency);
148
+ #else
149
+ CDO->MinNetUpdateFrequency = (float)MinNetUpdateFrequency;
150
+ #endif
138
151
  }
139
152
 
140
153
  UBlueprint* Blueprint = LoadObject<UBlueprint>(nullptr, *BlueprintPath);
141
154
  SaveBlueprint(Blueprint);
142
155
 
143
156
  Result->SetStringField(TEXT("blueprintPath"), BlueprintPath);
157
+ #if UE_MCP_HAS_5_5_API
144
158
  Result->SetNumberField(TEXT("netUpdateFrequency"), CDO->GetNetUpdateFrequency());
145
159
  Result->SetNumberField(TEXT("minNetUpdateFrequency"), CDO->GetMinNetUpdateFrequency());
160
+ #else
161
+ Result->SetNumberField(TEXT("netUpdateFrequency"), CDO->NetUpdateFrequency);
162
+ Result->SetNumberField(TEXT("minNetUpdateFrequency"), CDO->MinNetUpdateFrequency);
163
+ #endif
146
164
  Result->SetBoolField(TEXT("success"), true);
147
165
  return MCPResult(Result);
148
166
  }
@@ -24,7 +24,13 @@
24
24
  #include "PCGVolume.h"
25
25
  #include "Elements/PCGStaticMeshSpawner.h"
26
26
  #include "MeshSelectors/PCGMeshSelectorWeighted.h"
27
+ // UPCGEditorGraphNodeBase ships in the PCGEditor module starting in UE 5.5.
28
+ // On 5.4 the editor-node-position round-trip is unavailable; we fall back to
29
+ // the runtime UPCGNode::PositionX/Y instead (only populated when this bridge
30
+ // authored the node).
31
+ #if UE_MCP_HAS_5_5_API
27
32
  #include "Nodes/PCGEditorGraphNodeBase.h"
33
+ #endif
28
34
  #include "UObject/UObjectIterator.h"
29
35
  #include "Engine/StaticMesh.h"
30
36
  #include "Engine/Brush.h"
@@ -1909,6 +1915,8 @@ TSharedPtr<FJsonValue> FPCGHandlers::ExportGraph(const TSharedPtr<FJsonObject>&
1909
1915
  // only populated when the node was authored through this bridge - the PCG
1910
1916
  // editor never writes back to it. Build a lookup so editor-authored
1911
1917
  // graphs round-trip their hand-laid-out positions.
1918
+ // (5.4 lacks UPCGEditorGraphNodeBase; falls back to runtime PositionX/Y below.)
1919
+ #if UE_MCP_HAS_5_5_API
1912
1920
  TMap<const UPCGNode*, const UPCGEditorGraphNodeBase*> EditorNodeByPCGNode;
1913
1921
  for (TObjectIterator<UPCGEditorGraphNodeBase> It; It; ++It)
1914
1922
  {
@@ -1921,6 +1929,7 @@ TSharedPtr<FJsonValue> FPCGHandlers::ExportGraph(const TSharedPtr<FJsonObject>&
1921
1929
  EditorNodeByPCGNode.Add(PCGN, EdNode);
1922
1930
  }
1923
1931
  }
1932
+ #endif
1924
1933
 
1925
1934
  TArray<TSharedPtr<FJsonValue>> NodesArr;
1926
1935
  for (const UPCGNode* Node : Graph->GetNodes())
@@ -1932,11 +1941,13 @@ TSharedPtr<FJsonValue> FPCGHandlers::ExportGraph(const TSharedPtr<FJsonObject>&
1932
1941
 
1933
1942
  double PosX = Node->PositionX;
1934
1943
  double PosY = Node->PositionY;
1944
+ #if UE_MCP_HAS_5_5_API
1935
1945
  if (const UPCGEditorGraphNodeBase* const* EdNodePtr = EditorNodeByPCGNode.Find(Node); EdNodePtr && *EdNodePtr)
1936
1946
  {
1937
1947
  PosX = (*EdNodePtr)->NodePosX;
1938
1948
  PosY = (*EdNodePtr)->NodePosY;
1939
1949
  }
1950
+ #endif
1940
1951
  NodeObj->SetNumberField(TEXT("posX"), PosX);
1941
1952
  NodeObj->SetNumberField(TEXT("posY"), PosY);
1942
1953
 
@@ -2,6 +2,12 @@
2
2
  #include "HandlerRegistry.h"
3
3
  #include "HandlerUtils.h"
4
4
 
5
+ // StateTree authoring depends on UStateTreeEditingSubsystem (compile +
6
+ // validate entry points) and the generic FPropertyBindingPath system, both
7
+ // introduced in UE 5.5. On 5.4 we register no handlers and emit a one-line
8
+ // log so the rest of the plugin still loads.
9
+ #if UE_MCP_HAS_5_5_API
10
+
5
11
  #include "StateTree.h"
6
12
  #include "StateTreeEditorData.h"
7
13
  #include "StateTreeState.h"
@@ -23,8 +29,14 @@
23
29
  #include "StateTreeTaskBase.h"
24
30
  #include "StateTreeEditorTypes.h"
25
31
 
32
+ #endif // UE_MCP_HAS_5_5_API
33
+
26
34
  void FStateTreeHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
27
35
  {
36
+ #if !UE_MCP_HAS_5_5_API
37
+ UE_LOG(LogTemp, Warning, TEXT("[UE_MCP_Bridge] StateTree handlers require UE 5.5+; skipped on this engine version."));
38
+ return;
39
+ #else
28
40
  Registry.RegisterHandler(TEXT("read_state_tree"), &ReadStateTree);
29
41
  Registry.RegisterHandler(TEXT("list_state_tree_states"), &ListStates);
30
42
  Registry.RegisterHandler(TEXT("add_state_tree_state"), &AddState);
@@ -60,8 +72,11 @@ void FStateTreeHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
60
72
  Registry.RegisterHandler(TEXT("set_state_tree_root_parameters"), &SetRootParameters);
61
73
  Registry.RegisterHandler(TEXT("compile_state_tree"), &CompileStateTree);
62
74
  Registry.RegisterHandler(TEXT("validate_state_tree"), &ValidateStateTree);
75
+ #endif // UE_MCP_HAS_5_5_API
63
76
  }
64
77
 
78
+ #if UE_MCP_HAS_5_5_API
79
+
65
80
  // ── Helpers ──────────────────────────────────────────────────────────────────
66
81
 
67
82
  UStateTree* FStateTreeHandlers::LoadStateTree(const FString& AssetPath)
@@ -2219,3 +2234,5 @@ TSharedPtr<FJsonValue> FStateTreeHandlers::ValidateStateTree(const TSharedPtr<FJ
2219
2234
  Result->SetBoolField(TEXT("validated"), true);
2220
2235
  return MCPResult(Result);
2221
2236
  }
2237
+
2238
+ #endif // UE_MCP_HAS_5_5_API
@@ -537,11 +537,15 @@ TSharedPtr<FJsonValue> FWidgetHandlers::AddWidget(const TSharedPtr<FJsonObject>&
537
537
  }
538
538
  }
539
539
 
540
- // Register widget GUID so the compiler doesn't assert
540
+ // Register widget GUID so the compiler doesn't assert.
541
+ // WidgetVariableNameToGuidMap was added in UE 5.5; the assert it dodges
542
+ // only exists in 5.5+, so the registration is unnecessary on 5.4.
543
+ #if UE_MCP_HAS_5_5_API
541
544
  if (!WidgetBP->WidgetVariableNameToGuidMap.Contains(NewWidget->GetFName()))
542
545
  {
543
546
  WidgetBP->WidgetVariableNameToGuidMap.Add(NewWidget->GetFName(), FGuid::NewGuid());
544
547
  }
548
+ #endif
545
549
 
546
550
  // ── Save ──
547
551
  WidgetBP->MarkPackageDirty();