diff --git a/Content/Testing/M_Test.uasset b/Content/Testing/M_Test.uasset index c60ba42d..efea0852 100644 --- a/Content/Testing/M_Test.uasset +++ b/Content/Testing/M_Test.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:316f5800bdf14dfc6902bf8c7196b8c3d5be1339ae169ef2b78114651aaef053 -size 16646 +oid sha256:1ff036b0a3e973370df6051f97e44120470ce2574230bd9c8a6e01629c942a5f +size 13931 diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_ListSyncGroups.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_ListSyncGroups.h index 102caf98..f48b33ab 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_ListSyncGroups.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_ListSyncGroups.h @@ -29,7 +29,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UAnimBlueprint* AnimBP = F.Walk(Path).Cast(); if (!AnimBP) return; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_SetBlendSpaceSamples.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_SetBlendSpaceSamples.h index af9b3be0..0cb337a0 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_SetBlendSpaceSamples.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_SetBlendSpaceSamples.h @@ -73,8 +73,6 @@ public: UBlendSpace* BS = Assets.Object(); // Set axis parameters - MCPUtils::PreEdit({BS}); - const FBlendParameter& ParamX = BS->GetBlendParameter(0); const FBlendParameter& ParamY = BS->GetBlendParameter(1); @@ -126,7 +124,6 @@ public: } BS->ValidateSampleData(); - MCPUtils::PostEdit({BS}); // Save bool bSaved = MCPUtils::SaveGenericPackage(BS); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Create.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Create.h index 3bad1368..2e69b9f2 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Create.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Create.h @@ -45,7 +45,7 @@ public: return; } - MCPFetcher F(Result); + MCPFetcher F; UBlueprint* BP = F.Walk(Blueprint).Cast(); if (!BP) return; @@ -69,8 +69,6 @@ public: } } - F.PreEdit(); - if (GraphType == TEXT("function")) { UEdGraph* NewGraph = FBlueprintEditorUtils::CreateNewGraph(BP, FName(*Graph), @@ -116,7 +114,6 @@ public: Result.Appendf(TEXT("Created custom event: %s\n"), *MCPUtils::FormatName(NewEvent)); } - F.PostEdit(); MCPUtils::SaveBlueprintPackage(BP); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Delete.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Delete.h index 6ad751dc..deafe890 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Delete.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Delete.h @@ -33,7 +33,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; F.Walk(Path); if (!F.Ok()) return; @@ -85,9 +85,7 @@ public: // Remove the graph FString GraphName = MCPUtils::FormatName(TargetGraph); - F.PreEdit(); FBlueprintEditorUtils::RemoveGraph(BP, TargetGraph, EGraphRemoveFlags::Default); - F.PostEdit(); bool bSaved = MCPUtils::SaveBlueprintPackage(BP); Result.Appendf(TEXT("Deleted %s graph %s\n"), *GraphType, *GraphName); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Rename.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Rename.h index 090ee29d..f2e7a5a1 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Rename.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Rename.h @@ -33,7 +33,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UEdGraph* TargetGraph = F.Walk(Graph).Cast(); if (!TargetGraph) return; @@ -73,9 +73,7 @@ public: } } - F.PreEdit(); FBlueprintEditorUtils::RenameGraph(TargetGraph, NewName); - F.PostEdit(); MCPUtils::SaveBlueprintPackage(BP); Result.Appendf(TEXT("Renamed to %s %s\n"), diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddComponent.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddComponent.h index bdedf716..edc6c51b 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddComponent.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddComponent.h @@ -106,7 +106,6 @@ public: } // Create the SCS node - MCPUtils::PreEdit({BP}); USCS_Node* NewNode = SCS->CreateNode(ComponentClassObj, FName(*Component)); if (!NewNode) { @@ -126,7 +125,6 @@ public: SCS->AddNode(NewNode); } - MCPUtils::PostEdit({BP}); bool bSaved = MCPUtils::SaveBlueprintPackage(BP); Result.Appendf(TEXT("Added component %s (%s)"), diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddEventDispatcher.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddEventDispatcher.h index 3e532d09..63f31835 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddEventDispatcher.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddEventDispatcher.h @@ -51,7 +51,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UBlueprint* BP = F.Walk(Path).Cast(); if (!BP) return; @@ -75,7 +75,6 @@ public: } // Add a member variable with PC_MCDelegate pin type - F.PreEdit(); FEdGraphPinType DelegateType; DelegateType.PinCategory = UEdGraphSchema_K2::PC_MCDelegate; if (!FBlueprintEditorUtils::AddMemberVariable(BP, DispatcherFName, DelegateType)) @@ -116,7 +115,6 @@ public: if (!EntryNode) { - F.PostEdit(); MCPUtils::SaveBlueprintPackage(BP); Result.Append(TEXT("Error: Event dispatcher created but entry node not found — parameters could not be added.\n")); return; @@ -137,7 +135,6 @@ public: } } - F.PostEdit(); MCPUtils::SaveBlueprintPackage(BP); Result.Appendf(TEXT("Created event dispatcher '%s'"), *DispatcherName); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddFunctionParameter.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddFunctionParameter.h index e2ddd952..8aee08b3 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddFunctionParameter.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddFunctionParameter.h @@ -133,9 +133,7 @@ public: } // Add the parameter pin (EGPD_Output on entry = input to callers) - MCPUtils::PreEdit({BP}); EntryNode->CreateUserDefinedPin(FName(*ParamName), PinType, EGPD_Output); - MCPUtils::PostEdit({BP}); bool bSaved = MCPUtils::SaveBlueprintPackage(BP); Result.Appendf(TEXT("Added %s parameter '%s' to %s '%s'%s\n"), diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddInterface.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddInterface.h index 1bd0f34d..eb21c0cb 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddInterface.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddInterface.h @@ -55,7 +55,6 @@ public: } FTopLevelAssetPath InterfacePath = InterfaceClass->GetClassPathName(); - MCPUtils::PreEdit({BP}); bool bAdded = FBlueprintEditorUtils::ImplementNewInterface(BP, InterfacePath); if (!bAdded) { @@ -66,8 +65,6 @@ public: } // Collect stub function graph names from the newly added interface entry - MCPUtils::PostEdit({BP}); - Result.Appendf(TEXT("Added interface %s\n"), *MCPUtils::FormatName(InterfaceClass)); Result.Appendf(TEXT("Function stubs:\n")); for (const FBPInterfaceDescription& IfaceDesc : BP->ImplementedInterfaces) diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddVariable.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddVariable.h index 2c5ef64c..70b55419 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddVariable.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddVariable.h @@ -69,7 +69,6 @@ public: PinType.ContainerType = EPinContainerType::Array; // Add the variable - MCPUtils::PreEdit({BP}); if (!FBlueprintEditorUtils::AddMemberVariable(BP, VarFName, PinType, DefaultValue)) { MCPErrorCallback(Result).SetError(FString::Printf( @@ -80,7 +79,6 @@ public: if (!Category.IsEmpty()) FBlueprintEditorUtils::SetBlueprintVariableCategory(BP, VarFName, nullptr, FText::FromString(Category)); - MCPUtils::PostEdit({BP}); bool bSaved = MCPUtils::SaveBlueprintPackage(BP); Result.Appendf(TEXT("Added %s %s to %s\n"), diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ChangeVariableType.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ChangeVariableType.h index 4a1a2817..e092debb 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ChangeVariableType.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ChangeVariableType.h @@ -45,7 +45,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UBlueprint* BP = F.Walk(Blueprint).Cast(); if (!BP) return; @@ -128,9 +128,7 @@ public: } // Apply the type change - F.PreEdit(); Found->VarType = NewPinType; - F.PostEdit(); bool bSaved = MCPUtils::SaveBlueprintPackage(BP); Result.Appendf(TEXT("Changed %s to %s.%s\n"), diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Dump.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Dump.h index 05b8857c..e35d6d65 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Dump.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Dump.h @@ -33,7 +33,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UBlueprint* BP = F.Walk(Blueprint).Cast(); if (!BP) return; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListComponents.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListComponents.h index 1cea7036..50faab5e 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListComponents.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListComponents.h @@ -31,7 +31,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; F.Walk(Path); if (!F.Ok()) return; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListEventDispatchers.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListEventDispatchers.h index d17b0cf4..d5c0a404 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListEventDispatchers.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListEventDispatchers.h @@ -33,7 +33,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UBlueprint* BP = F.Walk(Path).Cast(); if (!BP) return; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListInterfaces.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListInterfaces.h index 1ebfa294..ef6c1527 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListInterfaces.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListInterfaces.h @@ -29,7 +29,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; F.Walk(Path); if (!F.Ok()) return; UBlueprint* BP = F.Cast(); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RefreshAllNodes.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RefreshAllNodes.h index 92ebf4cc..60c1b2eb 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RefreshAllNodes.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RefreshAllNodes.h @@ -42,7 +42,6 @@ public: int32 NodeCount = MCPUtils::AllNodes(BP).Num(); // Refresh all nodes - MCPUtils::PreEdit({BP}); FBlueprintEditorUtils::RefreshAllNodes(BP); // Remove orphaned pins from all nodes @@ -61,9 +60,6 @@ public: } } - // Mark as modified and recompile after orphan removal - MCPUtils::PostEdit({BP}); - // Summary Result.Appendf(TEXT("Refreshed %s: %d graphs, %d nodes"), *MCPUtils::FormatName(BP), GraphCount, NodeCount); if (OrphanedPinsRemoved > 0) diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveComponent.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveComponent.h index a4592d1f..df9df8d4 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveComponent.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveComponent.h @@ -33,7 +33,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UBlueprint* BP = F.Walk(Blueprint).Cast(); if (!BP) return; @@ -83,9 +83,7 @@ public: FString RemovedName = MCPUtils::FormatName(NodeToRemove->ComponentTemplate); // Remove the node (promotes children to parent if it has any — but we've guarded root above) - F.PreEdit(); SCS->RemoveNodeAndPromoteChildren(NodeToRemove); - F.PostEdit(); bool bSaved = MCPUtils::SaveBlueprintPackage(BP); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveInterface.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveInterface.h index 15b1b9d9..17fb4793 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveInterface.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveInterface.h @@ -65,9 +65,7 @@ public: } FTopLevelAssetPath InterfacePath = FoundInterface->GetClassPathName(); - MCPUtils::PreEdit({BP}); FBlueprintEditorUtils::RemoveInterface(BP, InterfacePath, PreserveFunctions); - MCPUtils::PostEdit({BP}); Result.Appendf(TEXT("Removed interface %s\n"), *MCPUtils::FormatName(FoundInterface)); if (PreserveFunctions) diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveVariable.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveVariable.h index 07bfb5b8..10166fdd 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveVariable.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveVariable.h @@ -32,7 +32,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UBlueprint* BP = F.Walk(Blueprint).Cast(); if (!BP) return; @@ -62,9 +62,7 @@ public: FName VarFName = Found->VarName; // RemoveMemberVariable also cleans up Get/Set nodes - F.PreEdit(); FBlueprintEditorUtils::RemoveMemberVariable(BP, VarFName); - F.PostEdit(); bool bSaved = MCPUtils::SaveBlueprintPackage(BP); Result.Appendf(TEXT("Removed variable %s from %s.%s\n"), diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Reparent.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Reparent.h index f6c42a7a..09e911f8 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Reparent.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Reparent.h @@ -61,9 +61,7 @@ public: } // Perform reparent - MCPUtils::PreEdit({BP}); BP->ParentClass = NewParentClassObj; - MCPUtils::PostEdit({BP}); FBlueprintEditorUtils::RefreshAllNodes(BP); FKismetEditorUtilities::CompileBlueprint(BP); bool bSaved = MCPUtils::SaveBlueprintPackage(BP); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Editor_OpenAsset.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Editor_OpenAsset.h index ea567e5d..1057a272 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Editor_OpenAsset.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Editor_OpenAsset.h @@ -28,7 +28,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UObject* Obj = F.Walk(Path).Cast(); if (!Obj) return; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Create.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Create.h index eea775c6..fd25ed8a 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Create.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Create.h @@ -51,11 +51,10 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UEdGraph* TargetGraph = F.Walk(Graph).Cast(); if (!TargetGraph) return; - F.PreEdit(); int32 SuccessCount = 0; int32 TotalCount = Nodes.Array.Num(); @@ -97,8 +96,6 @@ public: SuccessCount++; } - F.PostEdit(); - Result.Appendf(TEXT("Spawned %d/%d nodes.\n"), SuccessCount, TotalCount); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Delete.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Delete.h index 1bf49ff5..b95ec4fa 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Delete.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Delete.h @@ -33,7 +33,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UEdGraphNode* FoundNode = F.Walk(Node).Cast(); if (!FoundNode) return; @@ -49,8 +49,6 @@ public: *NodeTitle, *GraphName)); } - F.PreEdit(); - if (Cast(FoundNode)) { // Use the material editor's DeleteNodes to properly remove @@ -65,8 +63,6 @@ public: Graph->RemoveNode(FoundNode); } - F.PostEdit(); - Result.Appendf(TEXT("Deleted node '%s' from graph '%s'.\n"), *NodeTitle, *GraphName); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Duplicate.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Duplicate.h index 5f5502c3..42ea1545 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Duplicate.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Duplicate.h @@ -41,7 +41,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UEdGraph* TargetGraph = F.Walk(Graph).Cast(); if (!TargetGraph) return; @@ -75,7 +75,6 @@ public: if (SourceNodes.Num() == 0) return; - F.PreEdit(); // Duplicate each node for (UEdGraphNode* SourceNode : SourceNodes) { @@ -100,6 +99,5 @@ public: Result.Appendf(TEXT("Duplicated: %s -> %s\n"), *MCPUtils::FormatName(SourceNode), *MCPUtils::FormatName(NewNode)); } - F.PostEdit(); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_GetComment.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_GetComment.h index c61e816b..2da53aa7 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_GetComment.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_GetComment.h @@ -28,7 +28,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UEdGraphNode* FoundNode = F.Walk(Node).Cast(); if (!FoundNode) return; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SearchTypes.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SearchTypes.h index b4e511b4..671cde1d 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SearchTypes.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SearchTypes.h @@ -39,7 +39,7 @@ public: { int32 ClampedMax = FMath::Clamp(MaxResults, 1, 500); - MCPFetcher F(Result); + MCPFetcher F; UEdGraph* TargetGraph = F.Walk(Graph).Cast(); if (!TargetGraph) return; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetComment.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetComment.h index b8d5470b..2ddf7b3b 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetComment.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetComment.h @@ -31,12 +31,10 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UEdGraphNode* FoundNode = F.Walk(Node).Cast(); if (!FoundNode) return; - F.PreEdit(); - FoundNode->NodeComment = Comment; // Make the comment bubble visible if setting a non-empty comment @@ -46,8 +44,6 @@ public: FoundNode->bCommentBubblePinned = true; } - F.PostEdit(); - Result.Appendf(TEXT("Comment set on %s\n"), *MCPUtils::FormatName(FoundNode)); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetDefaults.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetDefaults.h index 9f664da9..099032cf 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetDefaults.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetDefaults.h @@ -2,8 +2,8 @@ #include "CoreMinimal.h" #include "MCPHandler.h" +#include "MCPServer.h" #include "MCPFetcher.h" -#include "MCPNotifier.h" #include "MCPProperty.h" #include "MCPUtils.h" #include "EdGraph/EdGraphPin.h" @@ -54,9 +54,9 @@ public: // ----------------------------------------------------------------------- void HandleK2Entry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj, const UEdGraphSchema_K2* K2Schema, - MCPNotifier& N, FStringBuilderBase& Result) + FStringBuilderBase& Result) { - MCPFetcher F(Result, N, GraphObj); + MCPFetcher F(GraphObj); UEdGraphPin* Pin = F.Node(Entry.Node).Pin(Entry.Name).Cast(); if (!Pin) return; @@ -80,7 +80,7 @@ public: Result.Appendf(TEXT("error: %s: %s\n"), *MCPUtils::FormatName(Pin), *Error); return; } - N.PreEditAddObject(Node); + UMCPServer::AddTouchedObject(Node); K2Schema->TrySetDefaultValue(*Pin, Entry.Value); } @@ -89,16 +89,16 @@ public: // ----------------------------------------------------------------------- void HandleMaterialEntry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj, - MCPNotifier& N, FStringBuilderBase& Result) + FStringBuilderBase& Result) { - MCPFetcher F(Result, N, GraphObj); + MCPFetcher F(GraphObj); UEdGraphNode* Node = F.Node(Entry.Node).Cast(); if (!Node) return; MCPProperty P = MCPProperty::GetOneExactMatch(Node, CPF_Edit, Entry.Name, Result); if (!P) return; - N.PreEditAddObject(Node); + UMCPServer::AddTouchedObject(Node); if (!P.SetText(Entry.Value, Result)) return; @@ -109,8 +109,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { // Fetch the graph once. - MCPNotifier N; - MCPFetcher GraphFetcher(Result, N); + MCPFetcher GraphFetcher; UEdGraph* GraphObj = GraphFetcher.Walk(Graph).Cast(); if (!GraphObj) return; @@ -124,7 +123,6 @@ public: return; } - N.PreEdit(); for (const TSharedPtr& PinVal : Pins.Array) { FSetNodeDefaultEntry Entry; @@ -132,13 +130,11 @@ public: continue; if (K2Schema) - HandleK2Entry(Entry, GraphObj, K2Schema, N, Result); + HandleK2Entry(Entry, GraphObj, K2Schema, Result); else if (MGSchema) - HandleMaterialEntry(Entry, GraphObj, N, Result); + HandleMaterialEntry(Entry, GraphObj, Result); } - N.PostEdit(); - Result.Appendf(TEXT("Done.\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetPositions.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetPositions.h index fe8d4e42..6f6380fb 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetPositions.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetPositions.h @@ -48,12 +48,10 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UBlueprint* BP = F.Walk(Blueprint).Cast(); if (!BP) return; - F.PreEdit(); - int32 SuccessCount = 0; for (const TSharedPtr& NodeVal : Nodes.Array) @@ -61,7 +59,7 @@ public: FMoveNodeEntry Entry; if (!MCPUtils::PopulateFromJson(FMoveNodeEntry::StaticStruct(), &Entry, NodeVal, Result)) continue; - MCPFetcher FN(Result, BP); + MCPFetcher FN(BP); UEdGraphNode* Node = FN.Node(Entry.Node).Cast(); if (!Node) continue; @@ -71,7 +69,6 @@ public: SuccessCount++; } - F.PostEdit(); Result.Appendf(TEXT("Moved %d/%d nodes.\n"), SuccessCount, Nodes.Array.Num()); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphPin_Connect.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphPin_Connect.h index 91191df2..b8f3a613 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphPin_Connect.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphPin_Connect.h @@ -47,12 +47,10 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UEdGraph* G = F.Walk(Graph).ToGraph().Cast(); if (!G) return; - F.PreEdit(); - int32 SuccessCount = 0; int32 TotalCount = Connections.Array.Num(); @@ -62,11 +60,11 @@ public: if (!MCPUtils::PopulateFromJson(FConnectPinsEntry::StaticStruct(), &Entry, ConnVal, Result)) continue; - MCPFetcher FS(Result, G); + MCPFetcher FS(G); UEdGraphPin* SourcePin = FS.Walk(Entry.SourcePin).Cast(); if (!SourcePin) continue; - MCPFetcher FT(Result, G); + MCPFetcher FT(G); UEdGraphPin* TargetPin = FT.Walk(Entry.TargetPin).Cast(); if (!TargetPin) continue; @@ -85,8 +83,6 @@ public: SuccessCount++; } - F.PostEdit(); - Result.Appendf(TEXT("Connected %d/%d pins.\n"), SuccessCount, TotalCount); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphPin_Disconnect.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphPin_Disconnect.h index e04fef1f..717f495e 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphPin_Disconnect.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphPin_Disconnect.h @@ -47,12 +47,10 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UEdGraph* G = F.Walk(Graph).ToGraph().Cast(); if (!G) return; - F.PreEdit(); - int32 SuccessCount = 0; int32 TotalDisconnected = 0; @@ -61,7 +59,7 @@ public: FDisconnectPinEntry Entry; if (!MCPUtils::PopulateFromJson(FDisconnectPinEntry::StaticStruct(), &Entry, DiscVal, Result)) continue; - MCPFetcher FP(Result, G); + MCPFetcher FP(G); UEdGraphPin* Pin = FP.Walk(Entry.Pin).Cast(); if (!Pin) continue; @@ -69,7 +67,7 @@ public: if (!Entry.TargetPin.IsEmpty()) { - MCPFetcher FT(Result, G); + MCPFetcher FT(G); UEdGraphPin* Target = FT.Walk(Entry.TargetPin).Cast(); if (!Target) continue; @@ -100,8 +98,6 @@ public: TotalDisconnected += DisconnectedCount; } - F.PostEdit(); - Result.Appendf(TEXT("Done: %d/%d succeeded, %d links broken.\n"), SuccessCount, Disconnections.Array.Num(), TotalDisconnected); } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Graph_Dump.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Graph_Dump.h index 4f0d5902..a061b3f9 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Graph_Dump.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Graph_Dump.h @@ -2,6 +2,7 @@ #include "CoreMinimal.h" #include "MCPHandler.h" +#include "MCPServer.h" #include "MCPFetcher.h" #include "MCPUtils.h" #include "BlueprintExporter.h" @@ -33,13 +34,13 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; F.Walk(Path); if (!F.Ok()) return; if (UEdGraph* Graph = Cast(F.GetObj())) { - EmitGraph(Graph, Result); + EmitGraph(Graph); return; } @@ -48,8 +49,8 @@ public: TArray Graphs = MCPUtils::AllGraphs(BP); for (UEdGraph* Graph : Graphs) { - Result.Appendf(TEXT("\n======== %s ========\n"), *MCPUtils::FormatName(Graph)); - EmitGraph(Graph, Result); + UMCPServer::Printf(TEXT("\n======== %s ========\n"), *MCPUtils::FormatName(Graph)); + EmitGraph(Graph); } return; } @@ -59,27 +60,27 @@ public: MCPUtils::EnsureMaterialGraph(Mat); if (!Mat->MaterialGraph) { - Result.Append(TEXT("ERROR: Could not build MaterialGraph for this material\n")); + UMCPServer::Print(TEXT("ERROR: Could not build MaterialGraph for this material\n")); return; } - EmitGraph(Mat->MaterialGraph, Result); + EmitGraph(Mat->MaterialGraph); return; } - Result.Appendf(TEXT("ERROR: Expected a blueprint, material, or graph, got %s\n"), + UMCPServer::Printf(TEXT("ERROR: Expected a blueprint, material, or graph, got %s\n"), *MCPUtils::FormatName(F.GetObj()->GetClass())); } private: - void EmitGraph(UEdGraph* Graph, FStringBuilderBase& Result) + void EmitGraph(UEdGraph* Graph) { FlxBlueprintExporter Exporter(Graph); - Result.Append(Exporter.GetOutput()); + UMCPServer::Print(*Exporter.GetOutput()); FString Details = Exporter.GetDetails(); if (!Details.IsEmpty()) { - Result.Append(TEXT("\n")); - Result.Append(Details); + UMCPServer::Print(TEXT("\n")); + UMCPServer::Print(*Details); } } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_ClearParameter.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_ClearParameter.h index 79965bd1..584979a8 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_ClearParameter.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_ClearParameter.h @@ -38,7 +38,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UMaterialInstanceConstant* MI = F.Asset(Path).Cast(); if (!MI) return; @@ -54,7 +54,6 @@ public: return Arr.RemoveAll([&](auto& Entry) { return Entry.ParameterInfo == ParamID; }); }; - F.PreEdit(); int32 Removed = 0; Removed += RemoveFrom(MI->ScalarParameterValues); Removed += RemoveFrom(MI->VectorParameterValues); @@ -64,7 +63,6 @@ public: Removed += RemoveFrom(MI->RuntimeVirtualTextureParameterValues); Removed += RemoveFrom(MI->SparseVolumeTextureParameterValues); Removed += RemoveFrom(MI->FontParameterValues); - F.PostEdit(); if (Removed == 0) { diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_Create.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_Create.h index 11faf1db..3429d898 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_Create.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_Create.h @@ -74,10 +74,7 @@ public: if (!MI) return; // Set parent. - TArray Chain = { MI }; - MCPUtils::PreEdit(Chain); MI->Parent = ParentMaterialObj; - MCPUtils::PostEdit(Chain); // Save. bool bSaved = MCPUtils::SaveGenericPackage(MI); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_DumpParameters.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_DumpParameters.h index 452f2abf..3034d6d7 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_DumpParameters.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_DumpParameters.h @@ -29,7 +29,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UMaterialInstanceConstant* MI = F.Asset(Path).Cast(); if (!MI) return; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_SetParameter.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_SetParameter.h index 0e2c43f6..8f677438 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_SetParameter.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_SetParameter.h @@ -42,7 +42,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UMaterialInstanceConstant* MI = F.Asset(Path).Cast(); if (!MI) return; @@ -71,7 +71,6 @@ public: EMaterialParameterType Type = Found->Value.Type; - F.PreEdit(); switch (Type) { case EMaterialParameterType::Scalar: @@ -100,7 +99,6 @@ public: Result.Appendf(TEXT("Parameters of type %d (see EMaterialParameterType) are not implemented"), (int)Type); return; } - F.PostEdit(); MCPUtils::SaveGenericPackage(MI); Result.Appendf(TEXT("Set '%s' = %s on %s\n"), diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_Compile.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_Compile.h index 8659ae56..d93b83cb 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_Compile.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_Compile.h @@ -29,7 +29,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { // Load material - MCPFetcher F(Result); + MCPFetcher F; UMaterial* MaterialObj = F.Asset(Material).Cast(); if (!MaterialObj) return; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_Create.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_Create.h index d889ece3..931dc4c2 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_Create.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_Create.h @@ -65,9 +65,6 @@ public: if (!MaterialObj) return; // Apply optional properties. - TArray Chain = { MaterialObj }; - MCPUtils::PreEdit(Chain); - if (!Domain.IsEmpty()) MaterialObj->MaterialDomain = ParsedDomain; @@ -76,8 +73,6 @@ public: MaterialObj->TwoSided = TwoSided ? 1 : 0; - MCPUtils::PostEdit(Chain); - bool bSaved = MCPUtils::SaveGenericPackage(MaterialObj); Result.Appendf(TEXT("Created %s\n"), *MaterialObj->GetPathName()); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_DumpParameters.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_DumpParameters.h index 78aa695d..790181a3 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_DumpParameters.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_DumpParameters.h @@ -29,7 +29,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UMaterial* Mat = F.Asset(Path).Cast(); if (!Mat) return; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Dump.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Dump.h index b58666cb..4ebe274f 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Dump.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Dump.h @@ -39,7 +39,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { // Resolve the path to an object and get its editable template. - MCPFetcher F(Result); + MCPFetcher F; UObject* Template = F.Walk(Path).Template().Cast(); if (!Template) return; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Get.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Get.h index 24420234..c84f6646 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Get.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Get.h @@ -31,7 +31,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { - MCPFetcher F(Result); + MCPFetcher F; UObject* Template = F.Walk(Path).Template().Cast(); if (!Template) return; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Set.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Set.h index ca019854..d8ddce7b 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Set.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Set.h @@ -33,7 +33,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { // Resolve the path to an object and get its editable template. - MCPFetcher F(Result); + MCPFetcher F; UObject* Template = F.Walk(Path).Template().Cast(); if (!Template) return; @@ -59,9 +59,7 @@ public: Resolved.Emplace(P, ValueStr); } - // Apply all changes in a single Pre/PostEditChange bracket. - F.PreEdit(); - + // Apply all changes. int32 SuccessCount = 0; for (auto& [P, ValueStr] : Resolved) { @@ -70,8 +68,6 @@ public: SuccessCount++; } - F.PostEdit(); - // Save. bool bSaved = MCPUtils::SaveGenericPackage(Template); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_AddState.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_AddState.h index c6edc953..02d933ad 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_AddState.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_AddState.h @@ -52,7 +52,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { // Resolve the anim blueprint - MCPFetcher F(Result); + MCPFetcher F; UAnimBlueprint* AnimBP = F.Walk(Blueprint).Cast(); if (!AnimBP) return; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_RemoveState.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_RemoveState.h index bf34f9f1..ba1429ea 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_RemoveState.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_RemoveState.h @@ -36,7 +36,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { // Fetch the state machine graph via MCPFetcher - MCPFetcher F(Result); + MCPFetcher F; F.Walk(Path); if (!F.Ok()) return; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetAnimation.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetAnimation.h index 36e5dfed..66e23c72 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetAnimation.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetAnimation.h @@ -46,7 +46,7 @@ public: virtual void Handle(FStringBuilderBase& Result) override { // Resolve the anim blueprint - MCPFetcher F(Result); + MCPFetcher F; UAnimBlueprint* AnimBP = F.Walk(Blueprint).Cast(); if (!AnimBP) return; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetTransitionRule.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetTransitionRule.h index e0f358ba..0e14a37f 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetTransitionRule.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetTransitionRule.h @@ -81,10 +81,6 @@ public: TransNode->LogicType = (ETransitionLogicType::Type)LogicType; TransNode->Bidirectional = BBidirectional; - TArray Chain = { TransNode }; - MCPUtils::PreEdit(Chain); - MCPUtils::PostEdit(Chain); - // Compile and save FKismetEditorUtilities::CompileBlueprint(AnimBP); MCPUtils::SaveBlueprintPackage(AnimBP); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPCommandlet.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPCommandlet.cpp index 33ee0e24..b8b8e601 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPCommandlet.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPCommandlet.cpp @@ -14,13 +14,6 @@ int32 UBlueprintMCPCommandlet::Main(const FString& Params) { // The UMCPServer editor subsystem starts the server automatically. // We just need to tick it, since FTickableEditorObject doesn't tick in commandlet mode. - UMCPServer* Server = UMCPServer::Get(); - if (!Server) - { - UE_LOG(LogTemp, Error, TEXT("BlueprintMCP: Could not find MCP server subsystem")); - return 1; - } - double LastTime = FPlatformTime::Seconds(); while (!IsEngineExitRequested()) @@ -29,7 +22,7 @@ int32 UBlueprintMCPCommandlet::Main(const FString& Params) double DeltaTime = CurrentTime - LastTime; LastTime = CurrentTime; FTSTicker::GetCoreTicker().Tick(DeltaTime); - Server->Tick(DeltaTime); + UMCPServer::TickServer(DeltaTime); FPlatformProcess::Sleep(0.01f); } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPFetcher.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPFetcher.cpp index 3ae96d99..339016ae 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPFetcher.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPFetcher.cpp @@ -1,4 +1,5 @@ #include "MCPFetcher.h" +#include "MCPServer.h" #include "MCPUtils.h" #include "Engine/Blueprint.h" #include "EdGraph/EdGraph.h" @@ -14,10 +15,12 @@ #include "Engine/LevelScriptBlueprint.h" #include "Subsystems/AssetEditorSubsystem.h" -MCPFetcher& MCPFetcher::SetError(const FString& Msg) +void MCPFetcher::SetObj(UObject* InObj) { UMCPServer::AddTouchedObject(InObj); Obj = InObj; ResultPin = nullptr; } +void MCPFetcher::SetPin(UEdGraphPin* InPin) { ResultPin = InPin; Obj = nullptr; } + +MCPFetcher& MCPFetcher::SetError() { bError = true; - ErrorCB.SetError(Msg); return *this; } @@ -25,22 +28,22 @@ MCPFetcher& MCPFetcher::TypeMismatch(const TCHAR* Walker, const TCHAR* Expected) { bError = true; if (ResultPin) - ErrorCB.SetError(FString::Printf(TEXT("Input to '%s' is a pin, expected %s"), Walker, Expected)); + UMCPServer::Printf(TEXT("ERROR: Input to '%s' is a pin, expected %s\n"), Walker, Expected); else if (Obj) - ErrorCB.SetError(FString::Printf(TEXT("Input to '%s' is %s, expected %s"), Walker, *Obj->GetClass()->GetName(), Expected)); + UMCPServer::Printf(TEXT("ERROR: Input to '%s' is %s, expected %s\n"), Walker, *Obj->GetClass()->GetName(), Expected); else - ErrorCB.SetError(FString::Printf(TEXT("Input to '%s' is null, expected %s"), Walker, Expected)); + UMCPServer::Printf(TEXT("ERROR: Input to '%s' is null, expected %s\n"), Walker, Expected); return *this; } const TArray& MCPFetcher::GetWalkerTable() { - static const TArray Table = { - { TEXT("graph"), TEXT("Find a named UEdGraph (blank name for material graphs)"), &MCPFetcher::Graph }, - { TEXT("node"), TEXT("Find a named UEdGraphNode within a graph or blueprint"), &MCPFetcher::Node }, - { TEXT("pin"), TEXT("Find a named UEdGraphPin on a node"), &MCPFetcher::Pin }, - { TEXT("component"), TEXT("Find a named component in a Blueprint's SCS"), &MCPFetcher::Component }, - { TEXT("levelblueprint"), TEXT("Get the level blueprint from a UWorld"), &MCPFetcher::LevelBlueprint }, + static TArray Table = { + { TEXT("graph"), TEXT("Find a named UEdGraph (blank name for material graphs)"), &MCPFetcher::Graph }, + { TEXT("node"), TEXT("Find a named UEdGraphNode within a graph or blueprint"), &MCPFetcher::Node }, + { TEXT("pin"), TEXT("Find a named UEdGraphPin on a node"), &MCPFetcher::Pin }, + { TEXT("component"), TEXT("Find a named component in a Blueprint's SCS"), &MCPFetcher::Component }, + { TEXT("levelblueprint"), TEXT("Get the level blueprint from a UWorld"), &MCPFetcher::LevelBlueprint }, }; return Table; } @@ -53,8 +56,8 @@ MCPFetcher& MCPFetcher::Walk(const FString& Path) Path.ParseIntoArray(Segments, TEXT(",")); if (Segments.Num() == 0) { - SetError(TEXT("Empty path")); - return *this; + UMCPServer::Print(TEXT("ERROR: Empty path\n")); + return SetError(); } for (int32 i = 0; i < Segments.Num(); i++) @@ -73,8 +76,8 @@ MCPFetcher& MCPFetcher::Walk(const FString& Path) const FWalker* W = GetWalker(Key); if (!W) { - SetError(FString::Printf(TEXT("Unknown path step '%s'"), *Key)); - return *this; + UMCPServer::Printf(TEXT("ERROR: Unknown path step '%s'\n"), *Key); + return SetError(); } (this->*W->Func)(Value); if (bError) return *this; @@ -87,18 +90,27 @@ MCPFetcher& MCPFetcher::Asset(const FString& PackagePath) { SetObj(LoadObject(nullptr, *PackagePath)); if (!Obj) - return SetError(FString::Printf(TEXT("Could not load asset '%s'"), *PackagePath)); + { + UMCPServer::Printf(TEXT("ERROR: Could not load asset '%s'\n"), *PackagePath); + return SetError(); + } OriginalAsset = Obj; // Open the editor for this asset (or bring it to front if already open). UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem(); if (!Sub || !Sub->OpenEditorForAsset(Obj)) - return SetError(FString::Printf(TEXT("Could not open editor for '%s'"), *PackagePath)); + { + UMCPServer::Printf(TEXT("ERROR: Could not open editor for '%s'\n"), *PackagePath); + return SetError(); + } Editor = Sub->FindEditorForAsset(OriginalAsset, false); if (!Editor) - return SetError(FString::Printf(TEXT("Could not find editor instance for '%s'"), *PackagePath)); + { + UMCPServer::Printf(TEXT("ERROR: Could not find editor instance for '%s'\n"), *PackagePath); + return SetError(); + } // If this is a material, use the editor's transient copy. if (UMaterial* Mat = ::Cast(Obj)) @@ -114,9 +126,10 @@ bool MCPFetcher::CheckAssetIsA(UClass* StaticClass) if (bError) return false; if (!OriginalAsset || !OriginalAsset->IsA(StaticClass)) { - SetError(FString::Printf(TEXT("Asset is %s, expected %s"), + UMCPServer::Printf(TEXT("ERROR: Asset is %s, expected %s\n"), OriginalAsset ? *OriginalAsset->GetClass()->GetName() : TEXT("null"), - *StaticClass->GetName())); + *StaticClass->GetName()); + SetError(); return false; } return true; @@ -126,7 +139,7 @@ const MCPFetcher::FWalker* MCPFetcher::GetWalker(const FString& Key) { for (const FWalker& W : GetWalkerTable()) { - if (StrEq(Key, W.Key)) + if (Key.Equals(W.Key, ESearchCase::IgnoreCase)) return &W; } return nullptr; @@ -140,10 +153,16 @@ MCPFetcher& MCPFetcher::Graph(const FString& Value) if (UMaterial* Mat = ::Cast(Obj)) { if (!Value.IsEmpty()) - return SetError(FString::Printf(TEXT("Materials do not have named graphs (got '%s')"), *Value)); + { + UMCPServer::Printf(TEXT("ERROR: Materials do not have named graphs (got '%s')\n"), *Value); + return SetError(); + } MCPUtils::EnsureMaterialGraph(Mat); if (!Mat->MaterialGraph) - return SetError(FString::Printf(TEXT("Material '%s' has no material graph"), *Mat->GetName())); + { + UMCPServer::Printf(TEXT("ERROR: Material '%s' has no material graph\n"), *Mat->GetName()); + return SetError(); + } SetObj(Mat->MaterialGraph); return *this; } @@ -154,9 +173,15 @@ MCPFetcher& MCPFetcher::Graph(const FString& Value) TArray Matches = MCPUtils::AllGraphsNamed(BP, Value); if (Matches.Num() == 0) - return SetError(FString::Printf(TEXT("Graph '%s' not found in %s"), *Value, *BP->GetName())); + { + UMCPServer::Printf(TEXT("ERROR: Graph '%s' not found in %s\n"), *Value, *BP->GetName()); + return SetError(); + } if (Matches.Num() > 1) - return SetError(FString::Printf(TEXT("Ambiguous graph '%s' in %s — %d matches"), *Value, *BP->GetName(), Matches.Num())); + { + UMCPServer::Printf(TEXT("ERROR: Ambiguous graph '%s' in %s — %d matches\n"), *Value, *BP->GetName(), Matches.Num()); + return SetError(); + } SetObj(Matches[0]); return *this; @@ -175,11 +200,17 @@ MCPFetcher& MCPFetcher::Node(const FString& Value) if (!N || !MCPUtils::Identifies(Value, N)) continue; if (Found) - return SetError(FString::Printf(TEXT("Ambiguous node '%s' in graph %s"), *Value, *G->GetName())); + { + UMCPServer::Printf(TEXT("ERROR: Ambiguous node '%s' in graph %s\n"), *Value, *G->GetName()); + return SetError(); + } Found = N; } if (!Found) - return SetError(FString::Printf(TEXT("Node '%s' not found in graph %s"), *Value, *G->GetName())); + { + UMCPServer::Printf(TEXT("ERROR: Node '%s' not found in graph %s\n"), *Value, *G->GetName()); + return SetError(); + } SetObj(Found); return *this; } @@ -195,12 +226,18 @@ MCPFetcher& MCPFetcher::Node(const FString& Value) if (!N || !MCPUtils::Identifies(Value, N)) continue; if (Found) - return SetError(FString::Printf(TEXT("Ambiguous node '%s' in %s"), *Value, *BP->GetName())); + { + UMCPServer::Printf(TEXT("ERROR: Ambiguous node '%s' in %s\n"), *Value, *BP->GetName()); + return SetError(); + } Found = N; } } if (!Found) - return SetError(FString::Printf(TEXT("Node '%s' not found in %s"), *Value, *BP->GetName())); + { + UMCPServer::Printf(TEXT("ERROR: Node '%s' not found in %s\n"), *Value, *BP->GetName()); + return SetError(); + } SetObj(Found); return *this; } @@ -221,13 +258,19 @@ MCPFetcher& MCPFetcher::Pin(const FString& Value) if (!MCPUtils::Identifies(Value, P)) continue; if (Found) - return SetError(FString::Printf(TEXT("Ambiguous pin '%s' on node %s"), - *Value, *MCPUtils::FormatName(N))); + { + UMCPServer::Printf(TEXT("ERROR: Ambiguous pin '%s' on node %s\n"), + *Value, *MCPUtils::FormatName(N)); + return SetError(); + } Found = P; } if (!Found) - return SetError(FString::Printf(TEXT("Pin '%s' not found on node %s"), - *Value, *MCPUtils::FormatName(N))); + { + UMCPServer::Printf(TEXT("ERROR: Pin '%s' not found on node %s\n"), + *Value, *MCPUtils::FormatName(N)); + return SetError(); + } SetPin(Found); return *this; @@ -243,7 +286,10 @@ MCPFetcher& MCPFetcher::Component(const FString& Value) USimpleConstructionScript* SCS = BP->SimpleConstructionScript; if (!SCS) - return SetError(FString::Printf(TEXT("Blueprint %s has no SimpleConstructionScript (not an Actor Blueprint)"), *BP->GetName())); + { + UMCPServer::Printf(TEXT("ERROR: Blueprint %s has no SimpleConstructionScript (not an Actor Blueprint)\n"), *BP->GetName()); + return SetError(); + } FName SearchName(*Value); for (USCS_Node* SCSNode : SCS->GetAllNodes()) @@ -255,7 +301,8 @@ MCPFetcher& MCPFetcher::Component(const FString& Value) } } - return SetError(FString::Printf(TEXT("Component '%s' not found in %s"), *Value, *BP->GetName())); + UMCPServer::Printf(TEXT("ERROR: Component '%s' not found in %s\n"), *Value, *BP->GetName()); + return SetError(); } MCPFetcher& MCPFetcher::LevelBlueprint(const FString& Value) @@ -267,11 +314,17 @@ MCPFetcher& MCPFetcher::LevelBlueprint(const FString& Value) return TypeMismatch(TEXT("levelblueprint"), TEXT("World")); if (!World->PersistentLevel) - return SetError(TEXT("World has no PersistentLevel")); + { + UMCPServer::Print(TEXT("ERROR: World has no PersistentLevel\n")); + return SetError(); + } ULevelScriptBlueprint* LevelBP = World->PersistentLevel->GetLevelScriptBlueprint(true); if (!LevelBP) - return SetError(TEXT("World has no level blueprint")); + { + UMCPServer::Print(TEXT("ERROR: World has no level blueprint\n")); + return SetError(); + } SetObj(LevelBP); return *this; @@ -281,12 +334,18 @@ MCPFetcher& MCPFetcher::Template() { if (bError) return *this; if (!Obj) - return SetError(TEXT("Template: object is null")); + { + UMCPServer::Print(TEXT("ERROR: Template: object is null\n")); + return SetError(); + } if (UBlueprint* BP = ::Cast(Obj)) { if (!BP->GeneratedClass) - return SetError(FString::Printf(TEXT("Blueprint '%s' has no GeneratedClass"), *Obj->GetName())); + { + UMCPServer::Printf(TEXT("ERROR: Blueprint '%s' has no GeneratedClass\n"), *Obj->GetName()); + return SetError(); + } SetObj(BP->GeneratedClass->GetDefaultObject()); return *this; } @@ -303,10 +362,16 @@ MCPFetcher& MCPFetcher::ToBlueprint() if (UWorld* World = ::Cast(Obj)) { if (!World->PersistentLevel) - return SetError(TEXT("ToBlueprint: World has no PersistentLevel")); + { + UMCPServer::Print(TEXT("ERROR: ToBlueprint: World has no PersistentLevel\n")); + return SetError(); + } ULevelScriptBlueprint* LevelBP = World->PersistentLevel->GetLevelScriptBlueprint(true); if (!LevelBP) - return SetError(TEXT("ToBlueprint: World has no level blueprint")); + { + UMCPServer::Print(TEXT("ERROR: ToBlueprint: World has no level blueprint\n")); + return SetError(); + } SetObj(LevelBP); return *this; } @@ -323,11 +388,13 @@ MCPFetcher& MCPFetcher::ToGraph() { MCPUtils::EnsureMaterialGraph(Mat); if (!Mat->MaterialGraph) - return SetError(FString::Printf(TEXT("ToGraph: Material '%s' has no material graph"), *Mat->GetName())); + { + UMCPServer::Printf(TEXT("ERROR: ToGraph: Material '%s' has no material graph\n"), *Mat->GetName()); + return SetError(); + } SetObj(Mat->MaterialGraph); return *this; } return TypeMismatch(TEXT("ToGraph"), TEXT("Graph or Material")); } - diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPNotifier.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPNotifier.cpp index 0c44a723..a35abe90 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPNotifier.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPNotifier.cpp @@ -6,25 +6,17 @@ #include "Kismet2/BlueprintEditorUtils.h" #include "MaterialEditingLibrary.h" -void MCPNotifier::PreEditAddObject(UObject* Obj) +void MCPNotifier::AddTouchedObject(UObject* Obj) { if (!Obj) return; bool bAlreadyInSet = false; TouchedSet.Add(Obj, &bAlreadyInSet); if (bAlreadyInSet) return; TouchedArray.Add(Obj); - if (bInsidePrePost) - Obj->PreEditChange(nullptr); + Obj->PreEditChange(nullptr); } -void MCPNotifier::PreEdit() -{ - bInsidePrePost = true; - for (UObject* Obj : TouchedArray) - Obj->PreEditChange(nullptr); -} - -void MCPNotifier::PostEdit() +void MCPNotifier::SendNotifications() { TSet Nodes; TSet Graphs; @@ -61,5 +53,6 @@ void MCPNotifier::PostEdit() if (GEditor) GEditor->RedrawAllViewports(); - bInsidePrePost = false; + TouchedSet.Empty(); + TouchedArray.Empty(); } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPServer.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPServer.cpp index 165bf5e9..42edc863 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPServer.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPServer.cpp @@ -100,15 +100,7 @@ #include "AnimationGraph.h" #include "AnimationTransitionGraph.h" -// ============================================================ -// Get() — retrieve the active server via the editor subsystem -// ============================================================ - -UMCPServer* UMCPServer::Get() -{ - if (!GEditor) return nullptr; - return GEditor->GetEditorSubsystem(); -} +UMCPServer* UMCPServer::GMCPServer = nullptr; // ============================================================ // Initialization and Shutdown @@ -117,6 +109,7 @@ UMCPServer* UMCPServer::Get() void UMCPServer::Initialize(FSubsystemCollectionBase& Collection) { Super::Initialize(Collection); + GMCPServer = this; // Create TCP listen socket ISocketSubsystem* SocketSub = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM); @@ -210,6 +203,7 @@ void UMCPServer::Deinitialize() LogCapture.Uninstall(); bRunning = false; bShuttingDown = false; + GMCPServer = nullptr; UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Server stopped.")); Super::Deinitialize(); } @@ -245,6 +239,11 @@ void UMCPServer::Tick(float DeltaTime) } } +void UMCPServer::TickServer(float DeltaTime) +{ + if (GMCPServer) GMCPServer->Tick(DeltaTime); +} + bool UMCPServer::IsTickable() const { return bRunning; @@ -301,17 +300,19 @@ FString UMCPServer::HandleRequest(const FString& Line) // Invoke the handler with log capture. LogCapture.CapturedErrors.Empty(); LogCapture.bEnabled = true; - TStringBuilder<32768> TextResult; - Handler->Handle(TextResult); + HandlerOutput.Reset(); + Handler->Handle(HandlerOutput); + Notifier.SendNotifications(); LogCapture.bEnabled = false; for (const FString& Msg : LogCapture.CapturedErrors) { - TextResult.Append(TEXT("LOG: ")); - TextResult.Append(Msg); - TextResult.Append(TEXT("\n")); + HandlerOutput.Append(TEXT("LOG: ")); + HandlerOutput.Append(Msg); + HandlerOutput.Append(TEXT("\n")); } LogCapture.CapturedErrors.Empty(); - FString Result = TextResult.ToString(); + FString Result = HandlerOutput.ToString(); + HandlerOutput.Reset(); for (int32 i = 0; i < Result.Len(); ++i) { if (Result[i] == TEXT('\0')) Result[i] = TEXT(' '); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPUtils.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPUtils.cpp index e5ae8a6f..d18da4bb 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPUtils.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPUtils.cpp @@ -1322,10 +1322,6 @@ FString MCPUtils::GetHandlerName(UClass* HandlerClass) // Strip "MCP_" prefix if (Name.StartsWith(TEXT("MCP_"))) Name = Name.Mid(4); - // Strip the remaining underscore between group and action (e.g. "Blueprint_Create" -> "BlueprintCreate") - int32 UnderscoreIdx; - if (Name.FindChar(TEXT('_'), UnderscoreIdx)) - Name = Name.Left(UnderscoreIdx) + Name.Mid(UnderscoreIdx + 1); return Name; } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPFetcher.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPFetcher.h index b295f0a5..6aabcc7b 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPFetcher.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPFetcher.h @@ -2,7 +2,6 @@ #include "CoreMinimal.h" #include "MCPUtils.h" -#include "MCPNotifier.h" class UEdGraphPin; class IAssetEditorInstance; @@ -20,19 +19,17 @@ class IAssetEditorInstance; // /Game/Tangibles/TAN_Character,component:CharacterMesh0 // // Builder-style usage: -// MCPFetcher F(cb); +// MCPFetcher F; // if (!F.Walk(Path).Ok()) return; // -// MCPFetcher F(cb, ExistingObj); +// MCPFetcher F(ExistingObj); // if (!F.Graph("EventGraph").Node("MyNode").Ok()) return; // class MCPFetcher { public: - MCPFetcher(MCPErrorCallback CB) : ErrorCB(CB) {} - MCPFetcher(MCPErrorCallback CB, UObject* O) : ErrorCB(CB), Obj(O) {} - MCPFetcher(MCPErrorCallback CB, MCPNotifier& N) : ErrorCB(CB), Notifier(&N) {} - MCPFetcher(MCPErrorCallback CB, MCPNotifier& N, UObject* O) : ErrorCB(CB), Obj(O), Notifier(&N) {} + MCPFetcher() {} + MCPFetcher(UObject* O) : Obj(O) {} // Starting point is always Asset. MCPFetcher& Asset(const FString& PackagePath); @@ -90,15 +87,7 @@ public: return Result; } - void PreEditAddObject(UObject* Obj) { Notifier->PreEditAddObject(Obj); } - void PreEdit() { Notifier->PreEdit(); } - void PostEdit() { Notifier->PostEdit(); } - - private: - // The error callback is invoked whenever an error is detected. - MCPErrorCallback ErrorCB = nullptr; - // The Current Object or Pin UObject* Obj = nullptr; UEdGraphPin* ResultPin = nullptr; @@ -110,15 +99,10 @@ private: // True if an error has occurred. bool bError = false; - // Notifier for tracking touched objects. - MCPNotifier OwnedNotifier; - MCPNotifier* Notifier = &OwnedNotifier; - // Internal methods. - static bool StrEq(const FString& A, const TCHAR* B) { return A.Equals(B, ESearchCase::IgnoreCase); } - void SetObj(UObject* InObj) { Notifier->PreEditAddObject(InObj); Obj = InObj; ResultPin = nullptr; } - void SetPin(UEdGraphPin* InPin) { ResultPin = InPin; Obj = nullptr; } - MCPFetcher& SetError(const FString& Msg); + void SetObj(UObject* InObj); + void SetPin(UEdGraphPin* InPin); + MCPFetcher& SetError(); MCPFetcher& TypeMismatch(const TCHAR* Walker, const TCHAR* Expected); const FWalker* GetWalker(const FString& Key); bool CheckAssetIsA(UClass* StaticClass); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPNotifier.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPNotifier.h index b4cb5b89..21908cbe 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPNotifier.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPNotifier.h @@ -6,23 +6,13 @@ // Handles PreEditChange/PostEditChange, ReconstructNode, and other // notifications that need to happen after modifications. // -// Usage: -// MCPNotifier N; -// MCPFetcher F(Result, N); -// F.Walk(Path)... -// N.PreEdit(); -// // modify stuff -// N.PostEdit(); -// class MCPNotifier { public: - void PreEditAddObject(UObject* Obj); - void PreEdit(); - void PostEdit(); + void AddTouchedObject(UObject* Obj); + void SendNotifications(); private: - bool bInsidePrePost = false; TSet TouchedSet; TArray TouchedArray; }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPServer.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPServer.h index cc65548d..1155e97c 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPServer.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPServer.h @@ -7,6 +7,7 @@ #include "Async/Future.h" #include "Dom/JsonObject.h" #include "MCPUtils.h" +#include "MCPNotifier.h" #include "LogCapture.h" #include "MCPServer.generated.h" @@ -32,18 +33,34 @@ class UMCPServer : public UEditorSubsystem, public FTickableEditorObject GENERATED_BODY() public: - /** Get the active server instance via GEditor. */ - static UMCPServer* Get(); - // UEditorSubsystem virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; // FTickableEditorObject virtual void Tick(float DeltaTime) override; + + /** Static entry point for commandlet mode (no FTickableEditorObject). */ + static void TickServer(float DeltaTime); virtual bool IsTickable() const override; virtual TStatId GetStatId() const override; + /** Track an object that has been modified by the current handler. */ + static void AddTouchedObject(UObject* Obj) { GMCPServer->Notifier.AddTouchedObject(Obj); } + + /** Send notifications now (also called automatically after the handler returns). */ + static void SendNotifications() { GMCPServer->Notifier.SendNotifications(); } + + /** Print text to the handler output buffer. */ + static void Print(const TCHAR* Text) { GMCPServer->HandlerOutput.Append(Text); } + + /** Print formatted text to the handler output buffer. */ + template + static void Printf(const FmtType& Fmt, ArgTypes&&... Args) + { + GMCPServer->HandlerOutput.Appendf(Fmt, Forward(Args)...); + } + /** Whether the server is currently listening. */ bool IsRunning() const { return bRunning; } @@ -51,8 +68,11 @@ public: int32 GetPort() const { return Port; } private: + static UMCPServer* GMCPServer; // ----- Tool dispatch ----- + MCPNotifier Notifier; + TStringBuilder<16384> HandlerOutput; FLogCaptureOutputDevice LogCapture; // installed once at startup, enabled per-request TMap MCPHandlerRegistry; // tool name -> UMCPHandler subclass void BuildMCPHandlerRegistry();