From d85b62027c3d9a5f50d2ab78797e16c452f13215 Mon Sep 17 00:00:00 2001 From: jyelon Date: Fri, 6 Mar 2026 00:33:07 -0500 Subject: [PATCH] Ported a few MCP handlers to the new registration system --- .../BlueprintMCPHandlers_DiffBlueprints.cpp | 45 +++----- .../BlueprintMCPHandlers_Interfaces.cpp | 103 +++++++----------- .../Private/BlueprintMCPHandlers_Mutation.cpp | 1 + .../Private/BlueprintMCPServer.cpp | 18 ++- .../BlueprintMCPHandlers_DiffBlueprints.h | 30 +++++ .../Public/BlueprintMCPHandlers_Interfaces.h | 74 +++++++++++++ .../Public/BlueprintMCPHandlers_Mutation.h | 3 + .../BlueprintMCP/Public/BlueprintMCPServer.h | 6 - 8 files changed, 166 insertions(+), 114 deletions(-) create mode 100644 Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_DiffBlueprints.h create mode 100644 Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Interfaces.h diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_DiffBlueprints.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_DiffBlueprints.cpp index bda9b624..7bf3b835 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_DiffBlueprints.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_DiffBlueprints.cpp @@ -1,55 +1,36 @@ +#include "BlueprintMCPHandlers_DiffBlueprints.h" #include "BlueprintMCPServer.h" #include "Engine/Blueprint.h" #include "EdGraph/EdGraph.h" #include "EdGraph/EdGraphNode.h" #include "EdGraph/EdGraphPin.h" -#include "K2Node_BreakStruct.h" -#include "K2Node_MakeStruct.h" -#include "K2Node_CallFunction.h" -#include "K2Node_VariableGet.h" -#include "K2Node_VariableSet.h" -#include "Kismet2/BlueprintEditorUtils.h" -#include "Serialization/JsonReader.h" -#include "Serialization/JsonWriter.h" -#include "Serialization/JsonSerializer.h" -// ============================================================ -// HandleDiffBlueprints — structural diff between two Blueprints -// ============================================================ - -void FBlueprintMCPServer::HandleDiffBlueprints(const FJsonObject* Json, FJsonObject* Result) +void UMCPHandler_DiffBlueprints::Handle(const FJsonObject* Json, FJsonObject* Result) { - FString BlueprintA = Json->GetStringField(TEXT("blueprintA")); - FString BlueprintB = Json->GetStringField(TEXT("blueprintB")); - FString GraphFilter = Json->GetStringField(TEXT("graph")); - - if (BlueprintA.IsEmpty() || BlueprintB.IsEmpty()) - { - return MakeErrorJson(Result, TEXT("Missing required fields: blueprintA, blueprintB")); - } + MCPHelper* Helper = MCPHelper::Get(); // Load both blueprints FString LoadErrorA, LoadErrorB; - UBlueprint* BPA = LoadBlueprintByName(BlueprintA, LoadErrorA); - if (!BPA) { MakeErrorJson(Result, FString::Printf(TEXT("blueprintA: %s"), *LoadErrorA)); return; } + UBlueprint* BPA = Helper->LoadBlueprintByName(BlueprintA, LoadErrorA); + if (!BPA) { Helper->MakeErrorJson(Result, FString::Printf(TEXT("blueprintA: %s"), *LoadErrorA)); return; } - UBlueprint* BPB = LoadBlueprintByName(BlueprintB, LoadErrorB); - if (!BPB) { MakeErrorJson(Result, FString::Printf(TEXT("blueprintB: %s"), *LoadErrorB)); return; } + UBlueprint* BPB = Helper->LoadBlueprintByName(BlueprintB, LoadErrorB); + if (!BPB) { Helper->MakeErrorJson(Result, FString::Printf(TEXT("blueprintB: %s"), *LoadErrorB)); return; } // Helper to gather graphs from a Blueprint - auto GatherGraphs = [&GraphFilter](UBlueprint* BP) -> TArray + auto GatherGraphs = [this](UBlueprint* BP) -> TArray { TArray Graphs; for (UEdGraph* G : BP->UbergraphPages) { if (!G) continue; - if (!GraphFilter.IsEmpty() && G->GetName() != GraphFilter) continue; + if (!Graph.IsEmpty() && (G->GetName() != Graph)) continue; Graphs.Add(G); } for (UEdGraph* G : BP->FunctionGraphs) { if (!G) continue; - if (!GraphFilter.IsEmpty() && G->GetName() != GraphFilter) continue; + if (!Graph.IsEmpty() && (G->GetName() != Graph)) continue; Graphs.Add(G); } return Graphs; @@ -167,7 +148,7 @@ void FBlueprintMCPServer::HandleDiffBlueprints(const FJsonObject* Json, FJsonObj if (!N) continue; for (UEdGraphPin* Pin : N->Pins) { - if (!Pin || Pin->Direction != EGPD_Output) continue; + if (!Pin || (Pin->Direction != EGPD_Output)) continue; for (UEdGraphPin* Linked : Pin->LinkedTo) { if (!Linked || !Linked->GetOwningNode()) continue; @@ -180,7 +161,7 @@ void FBlueprintMCPServer::HandleDiffBlueprints(const FJsonObject* Json, FJsonObj if (!N) continue; for (UEdGraphPin* Pin : N->Pins) { - if (!Pin || Pin->Direction != EGPD_Output) continue; + if (!Pin || (Pin->Direction != EGPD_Output)) continue; for (UEdGraphPin* Linked : Pin->LinkedTo) { if (!Linked || !Linked->GetOwningNode()) continue; @@ -205,7 +186,7 @@ void FBlueprintMCPServer::HandleDiffBlueprints(const FJsonObject* Json, FJsonObj } } - bool bIdentical = OnlyInA.Num() == 0 && OnlyInB.Num() == 0 && ConnsOnlyInA.Num() == 0 && ConnsOnlyInB.Num() == 0; + bool bIdentical = (OnlyInA.Num() == 0) && (OnlyInB.Num() == 0) && (ConnsOnlyInA.Num() == 0) && (ConnsOnlyInB.Num() == 0); GD->SetStringField(TEXT("status"), bIdentical ? TEXT("identical") : TEXT("different")); GD->SetNumberField(TEXT("nodeCountA"), GA->Nodes.Num()); GD->SetNumberField(TEXT("nodeCountB"), GB->Nodes.Num()); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Interfaces.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Interfaces.cpp index 302c99d7..e8b27616 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Interfaces.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Interfaces.cpp @@ -1,30 +1,23 @@ +#include "BlueprintMCPHandlers_Interfaces.h" #include "BlueprintMCPServer.h" #include "Engine/Blueprint.h" #include "EdGraph/EdGraph.h" #include "Kismet2/BlueprintEditorUtils.h" -#include "Kismet2/KismetEditorUtilities.h" -#include "Serialization/JsonReader.h" -#include "Serialization/JsonWriter.h" -#include "Serialization/JsonSerializer.h" #include "UObject/UObjectIterator.h" // ============================================================ -// HandleListInterfaces — list implemented interfaces on a Blueprint +// ListInterfaces // ============================================================ -void FBlueprintMCPServer::HandleListInterfaces(const FJsonObject* Json, FJsonObject* Result) +void UMCPHandler_ListInterfaces::Handle(const FJsonObject* Json, FJsonObject* Result) { - FString BlueprintName = Json->GetStringField(TEXT("blueprint")); - if (BlueprintName.IsEmpty()) - { - return MakeErrorJson(Result, TEXT("Missing required field: blueprint")); - } + MCPHelper* Helper = MCPHelper::Get(); FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); if (!BP) { - return MakeErrorJson(Result, LoadError); + return Helper->MakeErrorJson(Result, LoadError); } TArray> InterfacesArr; @@ -39,7 +32,6 @@ void FBlueprintMCPServer::HandleListInterfaces(const FJsonObject* Json, FJsonObj IfaceObj->SetStringField(TEXT("name"), IfaceDesc.Interface->GetName()); IfaceObj->SetStringField(TEXT("classPath"), IfaceDesc.Interface->GetPathName()); - // Collect function graph names from the interface TArray> FuncArr; for (const UEdGraph* Graph : IfaceDesc.Graphs) { @@ -53,30 +45,24 @@ void FBlueprintMCPServer::HandleListInterfaces(const FJsonObject* Json, FJsonObj InterfacesArr.Add(MakeShared(IfaceObj)); } - Result->SetStringField(TEXT("blueprint"), BlueprintName); + Result->SetStringField(TEXT("blueprint"), Blueprint); Result->SetNumberField(TEXT("count"), InterfacesArr.Num()); Result->SetArrayField(TEXT("interfaces"), InterfacesArr); } // ============================================================ -// HandleAddInterface — add a Blueprint Interface implementation +// AddInterface // ============================================================ -void FBlueprintMCPServer::HandleAddInterface(const FJsonObject* Json, FJsonObject* Result) +void UMCPHandler_AddInterface::Handle(const FJsonObject* Json, FJsonObject* Result) { - FString BlueprintName = Json->GetStringField(TEXT("blueprint")); - FString InterfaceName = Json->GetStringField(TEXT("interfaceName")); - - if (BlueprintName.IsEmpty() || InterfaceName.IsEmpty()) - { - return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, interfaceName")); - } + MCPHelper* Helper = MCPHelper::Get(); FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); if (!BP) { - return MakeErrorJson(Result, LoadError); + return Helper->MakeErrorJson(Result, LoadError); } // Resolve the interface class @@ -114,7 +100,7 @@ void FBlueprintMCPServer::HandleAddInterface(const FJsonObject* Json, FJsonObjec if (!InterfaceClass) { FString IfaceLoadError; - UBlueprint* IfaceBP = LoadBlueprintByName(InterfaceName, IfaceLoadError); + UBlueprint* IfaceBP = Helper->LoadBlueprintByName(InterfaceName, IfaceLoadError); if (IfaceBP && IfaceBP->GeneratedClass && IfaceBP->GeneratedClass->IsChildOf(UInterface::StaticClass())) { InterfaceClass = IfaceBP->GeneratedClass; @@ -123,7 +109,7 @@ void FBlueprintMCPServer::HandleAddInterface(const FJsonObject* Json, FJsonObjec if (!InterfaceClass) { - return MakeErrorJson(Result, FString::Printf( + return Helper->MakeErrorJson(Result, FString::Printf( TEXT("Interface '%s' not found. Provide a Blueprint Interface asset name (e.g. 'BPI_MyInterface') or a native UInterface class name."), *InterfaceName)); } @@ -133,24 +119,23 @@ void FBlueprintMCPServer::HandleAddInterface(const FJsonObject* Json, FJsonObjec { if (IfaceDesc.Interface == InterfaceClass) { - return MakeErrorJson(Result, FString::Printf( + return Helper->MakeErrorJson(Result, FString::Printf( TEXT("Interface '%s' is already implemented by Blueprint '%s'"), - *InterfaceName, *BlueprintName)); + *InterfaceName, *Blueprint)); } } - // Get interface class path for the non-deprecated overload FTopLevelAssetPath InterfacePath = InterfaceClass->GetClassPathName(); UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Adding interface '%s' to Blueprint '%s'"), - *InterfaceClass->GetName(), *BlueprintName); + *InterfaceClass->GetName(), *Blueprint); bool bAdded = FBlueprintEditorUtils::ImplementNewInterface(BP, InterfacePath); if (!bAdded) { - return MakeErrorJson(Result, FString::Printf( + return Helper->MakeErrorJson(Result, FString::Printf( TEXT("FBlueprintEditorUtils::ImplementNewInterface failed for interface '%s' on Blueprint '%s'"), - *InterfaceName, *BlueprintName)); + *InterfaceName, *Blueprint)); } // Collect stub function graph names from the newly added interface entry @@ -171,13 +156,13 @@ void FBlueprintMCPServer::HandleAddInterface(const FJsonObject* Json, FJsonObjec } FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(BP); - bool bSaved = SaveBlueprintPackage(BP); + if (Save) Helper->SaveBlueprintPackage(BP); - UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Added interface '%s' to '%s' (%d function stubs, saved: %s)"), - *InterfaceClass->GetName(), *BlueprintName, AddedFunctions.Num(), bSaved ? TEXT("true") : TEXT("false")); + UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Added interface '%s' to '%s' (%d function stubs)"), + *InterfaceClass->GetName(), *Blueprint, AddedFunctions.Num()); Result->SetBoolField(TEXT("success"), true); - Result->SetStringField(TEXT("blueprint"), BlueprintName); + Result->SetStringField(TEXT("blueprint"), Blueprint); Result->SetStringField(TEXT("interfaceName"), InterfaceClass->GetName()); Result->SetStringField(TEXT("interfacePath"), InterfaceClass->GetPathName()); @@ -187,34 +172,21 @@ void FBlueprintMCPServer::HandleAddInterface(const FJsonObject* Json, FJsonObjec FuncArr.Add(MakeShared(FuncName)); } Result->SetArrayField(TEXT("functionGraphsAdded"), FuncArr); - Result->SetBoolField(TEXT("saved"), bSaved); } // ============================================================ -// HandleRemoveInterface — remove a Blueprint Interface implementation +// RemoveInterface // ============================================================ -void FBlueprintMCPServer::HandleRemoveInterface(const FJsonObject* Json, FJsonObject* Result) +void UMCPHandler_RemoveInterface::Handle(const FJsonObject* Json, FJsonObject* Result) { - FString BlueprintName = Json->GetStringField(TEXT("blueprint")); - FString InterfaceName = Json->GetStringField(TEXT("interfaceName")); - - if (BlueprintName.IsEmpty() || InterfaceName.IsEmpty()) - { - return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, interfaceName")); - } - - bool bPreserveFunctions = false; - if (Json->HasField(TEXT("preserveFunctions"))) - { - bPreserveFunctions = Json->GetBoolField(TEXT("preserveFunctions")); - } + MCPHelper* Helper = MCPHelper::Get(); FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); if (!BP) { - return MakeErrorJson(Result, LoadError); + return Helper->MakeErrorJson(Result, LoadError); } // Find the interface in ImplementedInterfaces by name (case-insensitive) @@ -257,9 +229,9 @@ void FBlueprintMCPServer::HandleRemoveInterface(const FJsonObject* Json, FJsonOb } } - MakeErrorJson(Result, FString::Printf( + Helper->MakeErrorJson(Result, FString::Printf( TEXT("Interface '%s' is not implemented by Blueprint '%s'"), - *InterfaceName, *BlueprintName)); + *InterfaceName, *Blueprint)); Result->SetArrayField(TEXT("implementedInterfaces"), IfaceList); return; } @@ -267,19 +239,18 @@ void FBlueprintMCPServer::HandleRemoveInterface(const FJsonObject* Json, FJsonOb FTopLevelAssetPath InterfacePath = FoundInterface->GetClassPathName(); UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Removing interface '%s' from Blueprint '%s' (preserveFunctions: %s)"), - *FoundInterface->GetName(), *BlueprintName, bPreserveFunctions ? TEXT("true") : TEXT("false")); + *FoundInterface->GetName(), *Blueprint, PreserveFunctions ? TEXT("true") : TEXT("false")); - FBlueprintEditorUtils::RemoveInterface(BP, InterfacePath, bPreserveFunctions); + FBlueprintEditorUtils::RemoveInterface(BP, InterfacePath, PreserveFunctions); FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(BP); - bool bSaved = SaveBlueprintPackage(BP); + if (Save) Helper->SaveBlueprintPackage(BP); - UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Removed interface '%s' from '%s' (saved: %s)"), - *FoundInterface->GetName(), *BlueprintName, bSaved ? TEXT("true") : TEXT("false")); + UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Removed interface '%s' from '%s'"), + *FoundInterface->GetName(), *Blueprint); Result->SetBoolField(TEXT("success"), true); - Result->SetStringField(TEXT("blueprint"), BlueprintName); + Result->SetStringField(TEXT("blueprint"), Blueprint); Result->SetStringField(TEXT("interfaceName"), FoundInterface->GetName()); - Result->SetBoolField(TEXT("preservedFunctions"), bPreserveFunctions); - Result->SetBoolField(TEXT("saved"), bSaved); + Result->SetBoolField(TEXT("preservedFunctions"), PreserveFunctions); } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Mutation.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Mutation.cpp index 5aa4a05b..5e2e40ee 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Mutation.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Mutation.cpp @@ -2667,6 +2667,7 @@ void UMCPHandler_SpawnNode::Handle(const FJsonObject* Json, FJsonObject* Result) } FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(BP); + if (Save) Helper->SaveBlueprintPackage(BP); UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Spawned node '%s' (class %s) via action '%s' in graph '%s' of '%s'"), *NewNode->NodeGuid.ToString(), diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPServer.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPServer.cpp index 9af1dac3..ecd536f7 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPServer.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPServer.cpp @@ -684,11 +684,11 @@ bool FBlueprintMCPServer::Start(int32 InPort, bool bEditorMode) // Interface tools Router->BindRoute(FHttpPath(TEXT("/api/add-interface")), EHttpServerRequestVerbs::VERB_POST, - QueuedHandler(TEXT("addInterface"))); + QueuedHandler(TEXT("add_interface"))); Router->BindRoute(FHttpPath(TEXT("/api/remove-interface")), EHttpServerRequestVerbs::VERB_POST, - QueuedHandler(TEXT("removeInterface"))); + QueuedHandler(TEXT("remove_interface"))); Router->BindRoute(FHttpPath(TEXT("/api/list-interfaces")), EHttpServerRequestVerbs::VERB_POST, - QueuedHandler(TEXT("listInterfaces"))); + QueuedHandler(TEXT("list_interfaces"))); // Event Dispatcher tools Router->BindRoute(FHttpPath(TEXT("/api/add-event-dispatcher")), EHttpServerRequestVerbs::VERB_POST, @@ -720,7 +720,7 @@ bool FBlueprintMCPServer::Start(int32 InPort, bool bEditorMode) Router->BindRoute(FHttpPath(TEXT("/api/analyze-rebuild-impact")), EHttpServerRequestVerbs::VERB_POST, QueuedHandler(TEXT("analyzeRebuildImpact"))); Router->BindRoute(FHttpPath(TEXT("/api/diff-blueprints")), EHttpServerRequestVerbs::VERB_POST, - QueuedHandler(TEXT("diffBlueprints"))); + QueuedHandler(TEXT("diff_blueprints"))); // Material read-only tools (Phase 1) Router->BindRoute(FHttpPath(TEXT("/api/materials")), EHttpServerRequestVerbs::VERB_GET, @@ -997,8 +997,8 @@ void FBlueprintMCPServer::RegisterHandlers() TEXT("addVariable"), TEXT("removeVariable"), TEXT("setVariableMetadata"), - TEXT("addInterface"), - TEXT("removeInterface"), + TEXT("add_interface"), + TEXT("remove_interface"), TEXT("addEventDispatcher"), TEXT("addFunctionParameter"), TEXT("addComponent"), @@ -1079,9 +1079,7 @@ void FBlueprintMCPServer::RegisterHandlers() H(TEXT("addVariable"), &FBlueprintMCPServer::HandleAddVariable); H(TEXT("removeVariable"), &FBlueprintMCPServer::HandleRemoveVariable); H(TEXT("setVariableMetadata"), &FBlueprintMCPServer::HandleSetVariableMetadata); - H(TEXT("addInterface"), &FBlueprintMCPServer::HandleAddInterface); - H(TEXT("removeInterface"), &FBlueprintMCPServer::HandleRemoveInterface); - H(TEXT("listInterfaces"), &FBlueprintMCPServer::HandleListInterfaces); + // add_interface, remove_interface, list_interfaces now handled by new-style registry H(TEXT("addEventDispatcher"), &FBlueprintMCPServer::HandleAddEventDispatcher); H(TEXT("listEventDispatchers"), &FBlueprintMCPServer::HandleListEventDispatchers); H(TEXT("addFunctionParameter"), &FBlueprintMCPServer::HandleAddFunctionParameter); @@ -1093,7 +1091,7 @@ void FBlueprintMCPServer::RegisterHandlers() H(TEXT("restoreGraph"), &FBlueprintMCPServer::HandleRestoreGraph); H(TEXT("findDisconnectedPins"), &FBlueprintMCPServer::HandleFindDisconnectedPins); H(TEXT("analyzeRebuildImpact"), &FBlueprintMCPServer::HandleAnalyzeRebuildImpact); - H(TEXT("diffBlueprints"), &FBlueprintMCPServer::HandleDiffBlueprints); + // diff_blueprints is now handled by UMCPHandler_DiffBlueprints (new-style registry) H(TEXT("createStruct"), &FBlueprintMCPServer::HandleCreateStruct); H(TEXT("createEnum"), &FBlueprintMCPServer::HandleCreateEnum); H(TEXT("addStructProperty"), &FBlueprintMCPServer::HandleAddStructProperty); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_DiffBlueprints.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_DiffBlueprints.h new file mode 100644 index 00000000..6d4ee4da --- /dev/null +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_DiffBlueprints.h @@ -0,0 +1,30 @@ +#pragma once + +#include "CoreMinimal.h" +#include "MCPHandler.h" +#include "BlueprintMCPHandlers_DiffBlueprints.generated.h" + +UCLASS(meta=(ToolName="diff_blueprints")) +class UMCPHandler_DiffBlueprints : public UMCPHandler +{ + GENERATED_BODY() + +public: + UPROPERTY(meta=(Description="First blueprint name or package path")) + FString BlueprintA; + + UPROPERTY(meta=(Description="Second blueprint name or package path")) + FString BlueprintB; + + UPROPERTY(meta=(Optional, Description="Filter to a specific graph name")) + FString Graph; + + virtual FString GetDescription() const override + { + return TEXT("Structural diff between two different Blueprints. Compares nodes, " + "connections, and variables across graphs. Use for comparing variants, " + "finding divergence after copy-paste, or auditing consistency."); + } + + virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override; +}; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Interfaces.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Interfaces.h new file mode 100644 index 00000000..e124f8ff --- /dev/null +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Interfaces.h @@ -0,0 +1,74 @@ +#pragma once + +#include "CoreMinimal.h" +#include "MCPHandler.h" +#include "BlueprintMCPHandlers_Interfaces.generated.h" + +UCLASS(meta=(ToolName="list_interfaces")) +class UMCPHandler_ListInterfaces : public UMCPHandler +{ + GENERATED_BODY() + +public: + UPROPERTY(meta=(Description="Blueprint name or package path")) + FString Blueprint; + + virtual FString GetDescription() const override + { + return TEXT("List all Blueprint Interfaces implemented by a Blueprint, " + "including their function graphs."); + } + + virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override; +}; + +UCLASS(meta=(ToolName="add_interface")) +class UMCPHandler_AddInterface : public UMCPHandler +{ + GENERATED_BODY() + +public: + UPROPERTY(meta=(Description="Blueprint name or package path")) + FString Blueprint; + + UPROPERTY(meta=(Description="Interface name (e.g. 'BPI_MyInterface') or native UInterface class name")) + FString InterfaceName; + + UPROPERTY(meta=(Optional, Description="Save the blueprint after adding the interface")) + bool Save = false; + + virtual FString GetDescription() const override + { + return TEXT("Add a Blueprint Interface implementation to a Blueprint. " + "Creates stub function graphs for each interface function."); + } + + virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override; +}; + +UCLASS(meta=(ToolName="remove_interface")) +class UMCPHandler_RemoveInterface : public UMCPHandler +{ + GENERATED_BODY() + +public: + UPROPERTY(meta=(Description="Blueprint name or package path")) + FString Blueprint; + + UPROPERTY(meta=(Description="Interface name to remove")) + FString InterfaceName; + + UPROPERTY(meta=(Optional, Description="If true, keep the function graphs as regular functions")) + bool PreserveFunctions = false; + + UPROPERTY(meta=(Optional, Description="Save the blueprint after removing the interface")) + bool Save = false; + + virtual FString GetDescription() const override + { + return TEXT("Remove a Blueprint Interface implementation from a Blueprint. " + "Optionally preserve the function graphs as regular functions."); + } + + virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override; +}; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Mutation.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Mutation.h index 91a8cf44..b8961cb5 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Mutation.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Mutation.h @@ -25,6 +25,9 @@ public: UPROPERTY(meta=(Optional, Description="Y position in the graph")) int32 PosY = 0; + UPROPERTY(meta=(Optional, Description="Save the blueprint after spawning the node")) + bool Save = false; + virtual FString GetDescription() const override { return TEXT("Create a node in a Blueprint graph using the editor's action database. " diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPServer.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPServer.h index 883918fe..343a857f 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPServer.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPServer.h @@ -193,10 +193,6 @@ private: void HandleRemoveVariable(const FJsonObject* Json, FJsonObject* Result); void HandleSetVariableMetadata(const FJsonObject* Json, FJsonObject* Result); - // ----- Interfaces ----- - void HandleAddInterface(const FJsonObject* Json, FJsonObject* Result); - void HandleRemoveInterface(const FJsonObject* Json, FJsonObject* Result); - void HandleListInterfaces(const FJsonObject* Json, FJsonObject* Result); // ----- Event Dispatchers ----- void HandleAddEventDispatcher(const FJsonObject* Json, FJsonObject* Result); @@ -226,8 +222,6 @@ private: void HandleFindDisconnectedPins(const FJsonObject* Json, FJsonObject* Result); void HandleAnalyzeRebuildImpact(const FJsonObject* Json, FJsonObject* Result); - // ----- Cross-Blueprint comparison (read-only) ----- - void HandleDiffBlueprints(const FJsonObject* Json, FJsonObject* Result); // ----- Material read-only handlers (Phase 1) ----- void HandleListMaterials(const FJsonObject* Json, FJsonObject* Result);