ue-mcp 1.3.10-beta.13 → 1.3.10-beta.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.
@@ -34,6 +34,6 @@
34
34
  },
35
35
  "nativeToolsets": 52,
36
36
  "nativeToolActions": 830,
37
- "generatedAt": "2026-10-06T00:55:16.577Z",
38
- "version": "1.3.10-beta.13"
37
+ "generatedAt": "2026-10-06T12:57:09.853Z",
38
+ "version": "1.3.10-beta.14"
39
39
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ue-mcp",
3
- "version": "1.3.10-beta.13",
3
+ "version": "1.3.10-beta.14",
4
4
  "description": "Unreal Engine MCP server - 26 tools, 1972+ actions for AI-driven editor control",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -786,9 +786,7 @@ TSharedPtr<FJsonValue> FPCGHandlers::ConnectPCGNodes(const TSharedPtr<FJsonObjec
786
786
  return MCPResult(Result);
787
787
  }
788
788
 
789
- // #346: per-edge removal. UPCGGraph has no public RemoveEdge; do it manually by
790
- // finding the matching UPCGEdge on the source pin and clearing it from both
791
- // pins' Edges arrays, then PostEditChange + save.
789
+ // #346: per-edge removal through UPCGGraph::RemoveEdge, so the graph recompiles.
792
790
  TSharedPtr<FJsonValue> FPCGHandlers::DisconnectPCGNodes(const TSharedPtr<FJsonObject>& Params)
793
791
  {
794
792
  FString AssetPath;
@@ -842,21 +840,27 @@ TSharedPtr<FJsonValue> FPCGHandlers::DisconnectPCGNodes(const TSharedPtr<FJsonOb
842
840
  if (!OutPin) continue;
843
841
  if (!SourcePinLabel.IsEmpty() && OutPin->Properties.Label != FName(*SourcePinLabel)) continue;
844
842
 
845
- // Mutating the Edges array during iteration is unsafe; collect first.
846
- TArray<UPCGEdge*> ToRemove;
843
+ // RemoveEdge mutates the Edges array; collect the target labels first.
844
+ TArray<FName> ToRemove;
847
845
  for (const TObjectPtr<UPCGEdge>& Edge : OutPin->Edges)
848
846
  {
849
847
  if (!Edge || !Edge->OutputPin) continue;
850
848
  UPCGNode* EdgeDstNode = Edge->OutputPin->Node;
851
849
  if (EdgeDstNode != TargetNode) continue;
852
850
  if (!TargetPinLabel.IsEmpty() && Edge->OutputPin->Properties.Label != FName(*TargetPinLabel)) continue;
853
- ToRemove.Add(Edge);
851
+ ToRemove.Add(Edge->OutputPin->Properties.Label);
854
852
  }
855
- for (UPCGEdge* Edge : ToRemove)
853
+ for (const FName& TargetLabel : ToRemove)
856
854
  {
857
- if (!Edge) continue;
855
+ // The graph's own RemoveEdge notifies a structural change, which recompiles the graph and
856
+ // drops cached results; editing the pins' Edges arrays directly left both stale.
857
+ if (!Graph->RemoveEdge(SourceNode, OutPin->Properties.Label, TargetNode, TargetLabel))
858
+ {
859
+ return MCPError(FString::Printf(TEXT("UPCGGraph::RemoveEdge refused %s.%s -> %s.%s after %d edge(s) were removed"),
860
+ *SourceNodeName, *OutPin->Properties.Label.ToString(), *TargetNodeName, *TargetLabel.ToString(), RemovedCount));
861
+ }
858
862
  const FString CutSourcePin = OutPin->Properties.Label.ToString();
859
- const FString CutTargetPin = Edge->OutputPin ? Edge->OutputPin->Properties.Label.ToString() : FString();
863
+ const FString CutTargetPin = TargetLabel.ToString();
860
864
  TSharedPtr<FJsonObject> Cut = MakeShared<FJsonObject>();
861
865
  Cut->SetStringField(TEXT("sourcePinLabel"), CutSourcePin);
862
866
  Cut->SetStringField(TEXT("targetPinLabel"), CutTargetPin);
@@ -866,11 +870,6 @@ TSharedPtr<FJsonValue> FPCGHandlers::DisconnectPCGNodes(const TSharedPtr<FJsonOb
866
870
  FirstRemovedSourcePin = CutSourcePin;
867
871
  FirstRemovedTargetPin = CutTargetPin;
868
872
  }
869
- if (Edge->OutputPin)
870
- {
871
- Edge->OutputPin->Edges.Remove(Edge);
872
- }
873
- OutPin->Edges.Remove(Edge);
874
873
  RemovedCount++;
875
874
  }
876
875
  }
@@ -0,0 +1,67 @@
1
+ // disconnect_pcg_nodes must tell the graph its structure changed; without that the compiled graph and
2
+ // cached results keep the removed edge until something else invalidates them.
3
+ #if WITH_DEV_AUTOMATION_TESTS
4
+
5
+ #include "HandlerRegistry.h"
6
+ #include "Handlers/PCG/PCGHandlers.h"
7
+ #include "Dom/JsonObject.h"
8
+ #include "Dom/JsonValue.h"
9
+ #include "Elements/PCGAddTag.h"
10
+ #include "Misc/AutomationTest.h"
11
+ #include "Misc/Guid.h"
12
+ #include "PCGEdge.h"
13
+ #include "PCGGraph.h"
14
+ #include "PCGNode.h"
15
+ #include "PCGPin.h"
16
+ #include "UObject/Package.h"
17
+ #include "Tests/MCPScopedTestMount.h"
18
+
19
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMCPPCGDisconnectNotifiesTest,
20
+ "UE.MCP.PCG.Disconnect.NotifiesStructureChange",
21
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
22
+
23
+ bool FMCPPCGDisconnectNotifiesTest::RunTest(const FString& Parameters)
24
+ {
25
+ FMCPScopedTestMount Mount(
26
+ TEXT("/UEMCPPCGDisconnect_") + FGuid::NewGuid().ToString(EGuidFormats::Digits) + TEXT("/"), TEXT("UEMCPPCGDisconnect"));
27
+ const FString PackageName = Mount.RootPath + TEXT("PG_DisconnectProbe");
28
+
29
+ UPackage* Package = CreatePackage(*PackageName);
30
+ UPCGGraph* Graph = NewObject<UPCGGraph>(Package, TEXT("PG_DisconnectProbe"), RF_Public | RF_Standalone | RF_Transactional);
31
+ if (!TestNotNull(TEXT("PCG graph fixture created"), Graph)) return false;
32
+
33
+ UPCGAddTagSettings* FromSettings = nullptr;
34
+ UPCGAddTagSettings* ToSettings = nullptr;
35
+ UPCGNode* From = Graph->AddNodeOfType(FromSettings);
36
+ UPCGNode* To = Graph->AddNodeOfType(ToSettings);
37
+ if (!TestTrue(TEXT("two nodes added"), From && To)) return false;
38
+ if (!TestNotNull(TEXT("edge added"), Graph->AddEdge(From, PCGPinConstants::DefaultOutputLabel, To, PCGPinConstants::DefaultInputLabel))) return false;
39
+
40
+ int32 StructuralEdgeChanges = 0;
41
+ const FDelegateHandle Handle = Graph->OnGraphChangedDelegate.AddLambda([&StructuralEdgeChanges](UPCGGraphInterface*, EPCGChangeType ChangeType)
42
+ {
43
+ if (EnumHasAnyFlags(ChangeType, EPCGChangeType::Structural | EPCGChangeType::Edge)) ++StructuralEdgeChanges;
44
+ });
45
+
46
+ FMCPHandlerRegistry Registry;
47
+ FPCGHandlers::RegisterHandlers(Registry);
48
+ auto Params = MakeShared<FJsonObject>();
49
+ Params->SetStringField(TEXT("assetPath"), PackageName);
50
+ Params->SetStringField(TEXT("sourceNode"), From->GetName());
51
+ Params->SetStringField(TEXT("targetNode"), To->GetName());
52
+ const TSharedPtr<FJsonValue> Value = Registry.ExecuteHandler(TEXT("disconnect_pcg_nodes"), Params);
53
+ Graph->OnGraphChangedDelegate.Remove(Handle);
54
+
55
+ const TSharedPtr<FJsonObject> Result = Value.IsValid() && Value->Type == EJson::Object ? Value->AsObject() : nullptr;
56
+ double Removed = 0;
57
+ if (!TestTrue(TEXT("disconnect_pcg_nodes reports one removed edge"), Result.IsValid() && Result->TryGetNumberField(TEXT("removedEdges"), Removed) && Removed == 1)) return false;
58
+
59
+ const UPCGPin* OutPin = From->GetOutputPin(PCGPinConstants::DefaultOutputLabel);
60
+ const UPCGPin* InPin = To->GetInputPin(PCGPinConstants::DefaultInputLabel);
61
+ TestTrue(TEXT("the source pin has no edges"), OutPin && OutPin->Edges.Num() == 0);
62
+ TestTrue(TEXT("the target pin has no edges"), InPin && InPin->Edges.Num() == 0);
63
+ TestTrue(TEXT("the graph broadcast a structural or edge change"), StructuralEdgeChanges > 0);
64
+ return true;
65
+ }
66
+
67
+ #endif
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "FileVersion": 3,
3
3
  "Version": 1,
4
- "VersionName": "1.3.10-beta.13",
4
+ "VersionName": "1.3.10-beta.14",
5
5
  "FriendlyName": "UE MCP Bridge",
6
6
  "Description": "C++ WebSocket bridge for UE-MCP, providing 1911+ editor actions over the MCP protocol",
7
7
  "Category": "Developer Tools",