From 2c6b5d1ee8bed544e061453a0b2ab4021ad88904 Mon Sep 17 00:00:00 2001 From: jyelon Date: Fri, 6 Mar 2026 17:01:14 -0500 Subject: [PATCH] More MCP refactors, including creation of MCPAssetFinder. --- .../BlueprintMCPHandlers_AnimMutation.cpp | 25 +- .../BlueprintMCPHandlers_Components.cpp | 7 +- .../BlueprintMCPHandlers_DiffBlueprints.cpp | 6 +- .../BlueprintMCPHandlers_DiffBlueprints.h | 0 .../BlueprintMCPHandlers_Discovery.cpp | 5 +- .../BlueprintMCPHandlers_Dispatchers.cpp | 5 +- .../Private/BlueprintMCPHandlers_Graphs.cpp | 19 +- .../BlueprintMCPHandlers_Interfaces.cpp | 12 +- .../BlueprintMCPHandlers_Interfaces.h | 0 .../BlueprintMCPHandlers_MaterialInstance.cpp | 21 +- .../BlueprintMCPHandlers_MaterialMutation.cpp | 45 ++- .../BlueprintMCPHandlers_MaterialRead.cpp | 29 +- .../Private/BlueprintMCPHandlers_Mutation.cpp | 63 ++-- .../BlueprintMCPHandlers_Mutation.h | 0 .../Private/BlueprintMCPHandlers_Params.cpp | 7 +- .../Private/BlueprintMCPHandlers_Read.cpp | 23 +- .../Private/BlueprintMCPHandlers_Snapshot.cpp | 15 +- .../BlueprintMCPHandlers_Validation.cpp | 9 +- .../BlueprintMCPHandlers_Variables.cpp | 9 +- .../Private/BlueprintMCPServer.cpp | 271 +----------------- .../BlueprintMCP/Private/MCPAssetFinder.cpp | 259 +++++++++++++++++ .../BlueprintMCP/Public/BlueprintMCPServer.h | 78 +---- .../BlueprintMCP/Public/MCPAssetFinder.h | 66 +++++ .../Source/BlueprintMCP/Public/MCPUtils.h | 33 +++ 24 files changed, 496 insertions(+), 511 deletions(-) rename Plugins/BlueprintMCP/Source/BlueprintMCP/{Public => Private}/BlueprintMCPHandlers_DiffBlueprints.h (100%) rename Plugins/BlueprintMCP/Source/BlueprintMCP/{Public => Private}/BlueprintMCPHandlers_Interfaces.h (100%) rename Plugins/BlueprintMCP/Source/BlueprintMCP/{Public => Private}/BlueprintMCPHandlers_Mutation.h (100%) create mode 100644 Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPAssetFinder.cpp create mode 100644 Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPAssetFinder.h diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_AnimMutation.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_AnimMutation.cpp index d45fce6e..205000f5 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_AnimMutation.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_AnimMutation.cpp @@ -1,3 +1,4 @@ +#include "MCPAssetFinder.h" #include "BlueprintMCPServer.h" #include "MCPUtils.h" #include "Engine/Blueprint.h" @@ -58,7 +59,7 @@ void FBlueprintMCPServer::HandleCreateAnimBlueprint(const FJsonObject* Json, FJs // Check if asset already exists FString FullAssetPath = PackagePath / Name; - if (FindBlueprintAsset(Name) || FindBlueprintAsset(FullAssetPath)) + if (UMCPAssetFinder::FindBlueprintAsset(Name) || UMCPAssetFinder::FindBlueprintAsset(FullAssetPath)) { return MCPUtils::MakeErrorJson(Result, FString::Printf( TEXT("Blueprint '%s' already exists. Use a different name or delete the existing asset first."), @@ -170,10 +171,6 @@ void FBlueprintMCPServer::HandleCreateAnimBlueprint(const FJsonObject* Json, FJs // Save bool bSaved = MCPUtils::SaveBlueprintPackage(NewAnimBP); - // Refresh asset cache - FAssetRegistryModule& ARM = FModuleManager::LoadModuleChecked("AssetRegistry"); - AllBlueprintAssets.Empty(); - ARM.Get().GetAssetsByClass(UBlueprint::StaticClass()->GetClassPathName(), AllBlueprintAssets, true); // Collect graph names TArray> GraphNames; @@ -217,7 +214,7 @@ void FBlueprintMCPServer::HandleAddAnimState(const FJsonObject* Json, FJsonObjec } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -319,7 +316,7 @@ void FBlueprintMCPServer::HandleRemoveAnimState(const FJsonObject* Json, FJsonOb } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -390,7 +387,7 @@ void FBlueprintMCPServer::HandleAddAnimTransition(const FJsonObject* Json, FJson } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -477,7 +474,7 @@ void FBlueprintMCPServer::HandleSetTransitionRule(const FJsonObject* Json, FJson } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -569,7 +566,7 @@ void FBlueprintMCPServer::HandleAddAnimNode(const FJsonObject* Json, FJsonObject } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -765,7 +762,7 @@ void FBlueprintMCPServer::HandleSetStateAnimation(const FJsonObject* Json, FJson } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -858,7 +855,7 @@ void FBlueprintMCPServer::HandleListAnimSlots(const FJsonObject* Json, FJsonObje } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -917,7 +914,7 @@ void FBlueprintMCPServer::HandleListSyncGroups(const FJsonObject* Json, FJsonObj } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -1264,7 +1261,7 @@ void FBlueprintMCPServer::HandleSetStateBlendSpace(const FJsonObject* Json, FJso } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Components.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Components.cpp index ff1eeca2..d0065502 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Components.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Components.cpp @@ -1,3 +1,4 @@ +#include "MCPAssetFinder.h" #include "BlueprintMCPServer.h" #include "MCPUtils.h" #include "Engine/Blueprint.h" @@ -23,7 +24,7 @@ void FBlueprintMCPServer::HandleListComponents(const FJsonObject* Json, FJsonObj } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -117,7 +118,7 @@ void FBlueprintMCPServer::HandleAddComponent(const FJsonObject* Json, FJsonObjec } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -270,7 +271,7 @@ void FBlueprintMCPServer::HandleRemoveComponent(const FJsonObject* Json, FJsonOb } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_DiffBlueprints.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_DiffBlueprints.cpp index 3b0e3f02..eea77c7e 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_DiffBlueprints.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_DiffBlueprints.cpp @@ -1,4 +1,5 @@ #include "BlueprintMCPHandlers_DiffBlueprints.h" +#include "MCPAssetFinder.h" #include "BlueprintMCPServer.h" #include "MCPUtils.h" #include "Engine/Blueprint.h" @@ -8,14 +9,13 @@ void UMCPHandler_DiffTwoBlueprints::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); // Load both blueprints FString LoadErrorA, LoadErrorB; - UBlueprint* BPA = Helper->LoadBlueprintByName(BlueprintA, LoadErrorA); + UBlueprint* BPA = UMCPAssetFinder::LoadBlueprintByName(BlueprintA, LoadErrorA); if (!BPA) { MCPUtils::MakeErrorJson(Result, FString::Printf(TEXT("blueprintA: %s"), *LoadErrorA)); return; } - UBlueprint* BPB = Helper->LoadBlueprintByName(BlueprintB, LoadErrorB); + UBlueprint* BPB = UMCPAssetFinder::LoadBlueprintByName(BlueprintB, LoadErrorB); if (!BPB) { MCPUtils::MakeErrorJson(Result, FString::Printf(TEXT("blueprintB: %s"), *LoadErrorB)); return; } // Helper to gather graphs from a Blueprint diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_DiffBlueprints.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_DiffBlueprints.h similarity index 100% rename from Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_DiffBlueprints.h rename to Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_DiffBlueprints.h diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Discovery.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Discovery.cpp index 92f01d9f..f9d5d2ba 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Discovery.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Discovery.cpp @@ -1,3 +1,4 @@ +#include "MCPAssetFinder.h" #include "BlueprintMCPServer.h" #include "MCPUtils.h" #include "Engine/Blueprint.h" @@ -28,7 +29,7 @@ void FBlueprintMCPServer::HandleGetPinInfo(const FJsonObject* Json, FJsonObject* } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -133,7 +134,7 @@ void FBlueprintMCPServer::HandleCheckPinCompatibility(const FJsonObject* Json, F } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Dispatchers.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Dispatchers.cpp index 08949be3..e9cb54a2 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Dispatchers.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Dispatchers.cpp @@ -1,3 +1,4 @@ +#include "MCPAssetFinder.h" #include "BlueprintMCPServer.h" #include "MCPUtils.h" #include "Engine/Blueprint.h" @@ -27,7 +28,7 @@ void FBlueprintMCPServer::HandleAddEventDispatcher(const FJsonObject* Json, FJso // Load Blueprint FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -172,7 +173,7 @@ void FBlueprintMCPServer::HandleListEventDispatchers(const FJsonObject* Json, FJ } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Graphs.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Graphs.cpp index 300a384e..52c9d297 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Graphs.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Graphs.cpp @@ -1,3 +1,4 @@ +#include "MCPAssetFinder.h" #include "BlueprintMCPServer.h" #include "MCPUtils.h" #include "Engine/Blueprint.h" @@ -32,7 +33,7 @@ void FBlueprintMCPServer::HandleReparentBlueprint(const FJsonObject* Json, FJson // Load Blueprint FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -58,7 +59,7 @@ void FBlueprintMCPServer::HandleReparentBlueprint(const FJsonObject* Json, FJson if (!NewParentClass) { FString ParentLoadError; - UBlueprint* ParentBP = LoadBlueprintByName(NewParentName, ParentLoadError); + UBlueprint* ParentBP = UMCPAssetFinder::LoadBlueprintByName(NewParentName, ParentLoadError); if (ParentBP && ParentBP->GeneratedClass) { NewParentClass = ParentBP->GeneratedClass; @@ -133,7 +134,7 @@ void FBlueprintMCPServer::HandleCreateBlueprint(const FJsonObject* Json, FJsonOb // Check if asset already exists FString FullAssetPath = PackagePath / BlueprintName; - if (FindBlueprintAsset(BlueprintName) || FindBlueprintAsset(FullAssetPath)) + if (UMCPAssetFinder::FindBlueprintAsset(BlueprintName) || UMCPAssetFinder::FindBlueprintAsset(FullAssetPath)) { return MCPUtils::MakeErrorJson(Result, FString::Printf( TEXT("Blueprint '%s' already exists. Use a different name or delete the existing asset first."), @@ -155,7 +156,7 @@ void FBlueprintMCPServer::HandleCreateBlueprint(const FJsonObject* Json, FJsonOb if (!ParentClass) { FString ParentLoadError; - UBlueprint* ParentBP = LoadBlueprintByName(ParentClassName, ParentLoadError); + UBlueprint* ParentBP = UMCPAssetFinder::LoadBlueprintByName(ParentClassName, ParentLoadError); if (ParentBP && ParentBP->GeneratedClass) { ParentClass = ParentBP->GeneratedClass; @@ -232,10 +233,6 @@ void FBlueprintMCPServer::HandleCreateBlueprint(const FJsonObject* Json, FJsonOb // Save bool bSaved = MCPUtils::SaveBlueprintPackage(NewBP); - // Refresh asset cache - FAssetRegistryModule& ARM = FModuleManager::LoadModuleChecked("AssetRegistry"); - AllBlueprintAssets.Empty(); - ARM.Get().GetAssetsByClass(UBlueprint::StaticClass()->GetClassPathName(), AllBlueprintAssets, true); // Collect graph names TArray> GraphNames; @@ -288,7 +285,7 @@ void FBlueprintMCPServer::HandleCreateGraph(const FJsonObject* Json, FJsonObject // Load Blueprint FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -407,7 +404,7 @@ void FBlueprintMCPServer::HandleDeleteGraph(const FJsonObject* Json, FJsonObject } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -493,7 +490,7 @@ void FBlueprintMCPServer::HandleRenameGraph(const FJsonObject* Json, FJsonObject } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Interfaces.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Interfaces.cpp index b0469e60..5813357f 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Interfaces.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Interfaces.cpp @@ -1,4 +1,5 @@ #include "BlueprintMCPHandlers_Interfaces.h" +#include "MCPAssetFinder.h" #include "BlueprintMCPServer.h" #include "MCPUtils.h" #include "Engine/Blueprint.h" @@ -12,10 +13,9 @@ void UMCPHandler_ListBlueprintInterfaces::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); FString LoadError; - UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Blueprint, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -57,10 +57,9 @@ void UMCPHandler_ListBlueprintInterfaces::Handle(const FJsonObject* Json, FJsonO void UMCPHandler_AddBlueprintInterface::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); FString LoadError; - UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Blueprint, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -101,7 +100,7 @@ void UMCPHandler_AddBlueprintInterface::Handle(const FJsonObject* Json, FJsonObj if (!InterfaceClass) { FString IfaceLoadError; - UBlueprint* IfaceBP = Helper->LoadBlueprintByName(InterfaceName, IfaceLoadError); + UBlueprint* IfaceBP = UMCPAssetFinder::LoadBlueprintByName(InterfaceName, IfaceLoadError); if (IfaceBP && IfaceBP->GeneratedClass && IfaceBP->GeneratedClass->IsChildOf(UInterface::StaticClass())) { InterfaceClass = IfaceBP->GeneratedClass; @@ -181,10 +180,9 @@ void UMCPHandler_AddBlueprintInterface::Handle(const FJsonObject* Json, FJsonObj void UMCPHandler_RemoveBlueprintInterface::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); FString LoadError; - UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Blueprint, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Interfaces.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Interfaces.h similarity index 100% rename from Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Interfaces.h rename to Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Interfaces.h diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_MaterialInstance.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_MaterialInstance.cpp index ec254bf5..6d29ff4e 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_MaterialInstance.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_MaterialInstance.cpp @@ -1,3 +1,4 @@ +#include "MCPAssetFinder.h" #include "BlueprintMCPServer.h" #include "MCPUtils.h" #include "Materials/Material.h" @@ -40,7 +41,7 @@ void FBlueprintMCPServer::HandleCreateMaterialInstance(const FJsonObject* Json, // Check if asset already exists FString FullAssetPath = PackagePath / Name; - if (FindMaterialInstanceAsset(Name) || FindMaterialInstanceAsset(FullAssetPath)) + if (UMCPAssetFinder::FindMaterialInstanceAsset(Name) || UMCPAssetFinder::FindMaterialInstanceAsset(FullAssetPath)) { return MCPUtils::MakeErrorJson(Result, FString::Printf( TEXT("Material Instance '%s' already exists. Use a different name or delete the existing asset first."), @@ -51,7 +52,7 @@ void FBlueprintMCPServer::HandleCreateMaterialInstance(const FJsonObject* Json, UMaterialInterface* ParentMaterial = nullptr; { FString LoadError; - UMaterial* ParentMat = LoadMaterialByName(ParentMaterialName, LoadError); + UMaterial* ParentMat = UMCPAssetFinder::LoadMaterialByName(ParentMaterialName, LoadError); if (ParentMat) { ParentMaterial = ParentMat; @@ -59,7 +60,7 @@ void FBlueprintMCPServer::HandleCreateMaterialInstance(const FJsonObject* Json, else { FString MILoadError; - UMaterialInstanceConstant* ParentMI = LoadMaterialInstanceByName(ParentMaterialName, MILoadError); + UMaterialInstanceConstant* ParentMI = UMCPAssetFinder::LoadMaterialInstanceByName(ParentMaterialName, MILoadError); if (ParentMI) { ParentMaterial = ParentMI; @@ -107,10 +108,6 @@ void FBlueprintMCPServer::HandleCreateMaterialInstance(const FJsonObject* Json, // Save bool bSaved = MCPUtils::SaveGenericPackage(MI); - // Refresh asset cache - FAssetRegistryModule& ARM = FModuleManager::LoadModuleChecked("AssetRegistry"); - AllMaterialInstanceAssets.Empty(); - ARM.Get().GetAssetsByClass(UMaterialInstanceConstant::StaticClass()->GetClassPathName(), AllMaterialInstanceAssets, false); UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Created Material Instance '%s' with parent '%s' (saved: %s)"), *Name, *ParentMaterial->GetName(), bSaved ? TEXT("true") : TEXT("false")); @@ -149,7 +146,7 @@ void FBlueprintMCPServer::HandleSetMaterialInstanceParameter(const FJsonObject* // Load the Material Instance FString LoadError; - UMaterialInstanceConstant* MI = LoadMaterialInstanceByName(MIName, LoadError); + UMaterialInstanceConstant* MI = UMCPAssetFinder::LoadMaterialInstanceByName(MIName, LoadError); if (!MI) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -370,7 +367,7 @@ void FBlueprintMCPServer::HandleGetMaterialInstanceParameters(const FJsonObject* } FString LoadError; - UMaterialInstanceConstant* MI = LoadMaterialInstanceByName(NameParam, LoadError); + UMaterialInstanceConstant* MI = UMCPAssetFinder::LoadMaterialInstanceByName(NameParam, LoadError); if (!MI) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -618,7 +615,7 @@ void FBlueprintMCPServer::HandleReparentMaterialInstance(const FJsonObject* Json // Load the Material Instance FString LoadError; - UMaterialInstanceConstant* MI = LoadMaterialInstanceByName(MIName, LoadError); + UMaterialInstanceConstant* MI = UMCPAssetFinder::LoadMaterialInstanceByName(MIName, LoadError); if (!MI) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -631,7 +628,7 @@ void FBlueprintMCPServer::HandleReparentMaterialInstance(const FJsonObject* Json UMaterialInterface* NewParent = nullptr; { FString MatLoadError; - UMaterial* NewParentMat = LoadMaterialByName(NewParentName, MatLoadError); + UMaterial* NewParentMat = UMCPAssetFinder::LoadMaterialByName(NewParentName, MatLoadError); if (NewParentMat) { NewParent = NewParentMat; @@ -639,7 +636,7 @@ void FBlueprintMCPServer::HandleReparentMaterialInstance(const FJsonObject* Json else { FString MILoadError; - UMaterialInstanceConstant* NewParentMI = LoadMaterialInstanceByName(NewParentName, MILoadError); + UMaterialInstanceConstant* NewParentMI = UMCPAssetFinder::LoadMaterialInstanceByName(NewParentName, MILoadError); if (NewParentMI) { NewParent = NewParentMI; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_MaterialMutation.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_MaterialMutation.cpp index 8d4f5f78..3b226d44 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_MaterialMutation.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_MaterialMutation.cpp @@ -1,3 +1,4 @@ +#include "MCPAssetFinder.h" #include "BlueprintMCPServer.h" #include "MCPUtils.h" #include "Materials/Material.h" @@ -74,7 +75,7 @@ void FBlueprintMCPServer::HandleCreateMaterial(const FJsonObject* Json, FJsonObj // Check if asset already exists FString FullAssetPath = PackagePath / Name; - if (FindMaterialAsset(Name) || FindMaterialAsset(FullAssetPath)) + if (UMCPAssetFinder::FindMaterialAsset(Name) || UMCPAssetFinder::FindMaterialAsset(FullAssetPath)) { return MCPUtils::MakeErrorJson(Result, FString::Printf( TEXT("Material '%s' already exists. Use a different name or delete the existing asset first."), @@ -153,10 +154,6 @@ void FBlueprintMCPServer::HandleCreateMaterial(const FJsonObject* Json, FJsonObj // Save bool bSaved = MCPUtils::SaveMaterialPackage(Material); - // Refresh asset cache - FAssetRegistryModule& ARM = FModuleManager::LoadModuleChecked("AssetRegistry"); - AllMaterialAssets.Empty(); - ARM.Get().GetAssetsByClass(UMaterial::StaticClass()->GetClassPathName(), AllMaterialAssets, false); // Map domain back to string for response auto DomainToString = [](EMaterialDomain Domain) -> FString @@ -222,7 +219,7 @@ void FBlueprintMCPServer::HandleSetMaterialProperty(const FJsonObject* Json, FJs // Load material FString LoadError; - UMaterial* Material = LoadMaterialByName(MaterialName, LoadError); + UMaterial* Material = UMCPAssetFinder::LoadMaterialByName(MaterialName, LoadError); if (!Material) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -566,7 +563,7 @@ void FBlueprintMCPServer::HandleAddMaterialExpression(const FJsonObject* Json, F return MCPUtils::MakeErrorJson(Result, TEXT("Specify either 'material' or 'materialFunction', not both")); } FString LoadError; - MatFunc = LoadMaterialFunctionByName(MaterialFunctionName, LoadError); + MatFunc = UMCPAssetFinder::LoadMaterialFunctionByName(MaterialFunctionName, LoadError); if (!MatFunc) { MCPUtils::MakeErrorJson(Result, LoadError); return; } Owner = MatFunc; AssetDisplayName = MatFunc->GetName(); @@ -574,7 +571,7 @@ void FBlueprintMCPServer::HandleAddMaterialExpression(const FJsonObject* Json, F else { FString LoadError; - Material = LoadMaterialByName(MaterialName, LoadError); + Material = UMCPAssetFinder::LoadMaterialByName(MaterialName, LoadError); if (!Material) { MCPUtils::MakeErrorJson(Result, LoadError); return; } Owner = Material; AssetDisplayName = Material->GetName(); @@ -704,14 +701,14 @@ void FBlueprintMCPServer::HandleDeleteMaterialExpression(const FJsonObject* Json if (!MaterialFunctionName.IsEmpty()) { FString LoadError; - MatFunc = LoadMaterialFunctionByName(MaterialFunctionName, LoadError); + MatFunc = UMCPAssetFinder::LoadMaterialFunctionByName(MaterialFunctionName, LoadError); if (!MatFunc) { MCPUtils::MakeErrorJson(Result, LoadError); return; } AssetDisplayName = MatFunc->GetName(); } else { FString LoadError; - Material = LoadMaterialByName(MaterialName, LoadError); + Material = UMCPAssetFinder::LoadMaterialByName(MaterialName, LoadError); if (!Material) { MCPUtils::MakeErrorJson(Result, LoadError); return; } AssetDisplayName = Material->GetName(); } @@ -831,14 +828,14 @@ void FBlueprintMCPServer::HandleConnectMaterialPins(const FJsonObject* Json, FJs if (!MaterialFunctionName.IsEmpty()) { FString LoadError; - MatFunc = LoadMaterialFunctionByName(MaterialFunctionName, LoadError); + MatFunc = UMCPAssetFinder::LoadMaterialFunctionByName(MaterialFunctionName, LoadError); if (!MatFunc) { MCPUtils::MakeErrorJson(Result, LoadError); return; } AssetDisplayName = MatFunc->GetName(); } else { FString LoadError; - Material = LoadMaterialByName(MaterialName, LoadError); + Material = UMCPAssetFinder::LoadMaterialByName(MaterialName, LoadError); if (!Material) { MCPUtils::MakeErrorJson(Result, LoadError); return; } AssetDisplayName = Material->GetName(); } @@ -982,14 +979,14 @@ void FBlueprintMCPServer::HandleDisconnectMaterialPin(const FJsonObject* Json, F if (!MaterialFunctionName.IsEmpty()) { FString LoadError; - MatFunc = LoadMaterialFunctionByName(MaterialFunctionName, LoadError); + MatFunc = UMCPAssetFinder::LoadMaterialFunctionByName(MaterialFunctionName, LoadError); if (!MatFunc) { MCPUtils::MakeErrorJson(Result, LoadError); return; } AssetDisplayName = MatFunc->GetName(); } else { FString LoadError; - Material = LoadMaterialByName(MaterialName, LoadError); + Material = UMCPAssetFinder::LoadMaterialByName(MaterialName, LoadError); if (!Material) { MCPUtils::MakeErrorJson(Result, LoadError); return; } AssetDisplayName = Material->GetName(); } @@ -1104,14 +1101,14 @@ void FBlueprintMCPServer::HandleSetExpressionValue(const FJsonObject* Json, FJso if (!MaterialFunctionName.IsEmpty()) { FString LoadError; - MatFunc = LoadMaterialFunctionByName(MaterialFunctionName, LoadError); + MatFunc = UMCPAssetFinder::LoadMaterialFunctionByName(MaterialFunctionName, LoadError); if (!MatFunc) { MCPUtils::MakeErrorJson(Result, LoadError); return; } AssetDisplayName = MatFunc->GetName(); } else { FString LoadError; - Material = LoadMaterialByName(MaterialName, LoadError); + Material = UMCPAssetFinder::LoadMaterialByName(MaterialName, LoadError); if (!Material) { MCPUtils::MakeErrorJson(Result, LoadError); return; } AssetDisplayName = Material->GetName(); } @@ -1381,14 +1378,14 @@ void FBlueprintMCPServer::HandleMoveMaterialExpression(const FJsonObject* Json, if (!MaterialFunctionName.IsEmpty()) { FString LoadError; - MatFunc = LoadMaterialFunctionByName(MaterialFunctionName, LoadError); + MatFunc = UMCPAssetFinder::LoadMaterialFunctionByName(MaterialFunctionName, LoadError); if (!MatFunc) { MCPUtils::MakeErrorJson(Result, LoadError); return; } AssetDisplayName = MatFunc->GetName(); } else { FString LoadError; - Material = LoadMaterialByName(MaterialName, LoadError); + Material = UMCPAssetFinder::LoadMaterialByName(MaterialName, LoadError); if (!Material) { MCPUtils::MakeErrorJson(Result, LoadError); return; } AssetDisplayName = Material->GetName(); } @@ -1485,7 +1482,7 @@ void FBlueprintMCPServer::HandleCreateMaterialFunction(const FJsonObject* Json, // Check if asset already exists FString FullAssetPath = PackagePath / Name; - if (FindMaterialFunctionAsset(Name) || FindMaterialFunctionAsset(FullAssetPath)) + if (UMCPAssetFinder::FindMaterialFunctionAsset(Name) || UMCPAssetFinder::FindMaterialFunctionAsset(FullAssetPath)) { return MCPUtils::MakeErrorJson(Result, FString::Printf( TEXT("Material Function '%s' already exists. Use a different name or delete the existing asset first."), @@ -1522,10 +1519,6 @@ void FBlueprintMCPServer::HandleCreateMaterialFunction(const FJsonObject* Json, // Save bool bSaved = MCPUtils::SaveGenericPackage(MF); - // Refresh asset cache - FAssetRegistryModule& ARM = FModuleManager::LoadModuleChecked("AssetRegistry"); - AllMaterialFunctionAssets.Empty(); - ARM.Get().GetAssetsByClass(UMaterialFunction::StaticClass()->GetClassPathName(), AllMaterialFunctionAssets, false); UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Created Material Function '%s' (saved: %s)"), *Name, bSaved ? TEXT("true") : TEXT("false")); @@ -1558,7 +1551,7 @@ void FBlueprintMCPServer::HandleSnapshotMaterialGraph(const FJsonObject* Json, F // Load material FString LoadError; - UMaterial* Material = LoadMaterialByName(MaterialName, LoadError); + UMaterial* Material = UMCPAssetFinder::LoadMaterialByName(MaterialName, LoadError); if (!Material) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -1657,7 +1650,7 @@ void FBlueprintMCPServer::HandleDiffMaterialGraph(const FJsonObject* Json, FJson // Load material FString LoadError; - UMaterial* Material = LoadMaterialByName(MaterialName, LoadError); + UMaterial* Material = UMCPAssetFinder::LoadMaterialByName(MaterialName, LoadError); if (!Material) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -1823,7 +1816,7 @@ void FBlueprintMCPServer::HandleRestoreMaterialGraph(const FJsonObject* Json, FJ // Load material FString LoadError; - UMaterial* Material = LoadMaterialByName(MaterialName, LoadError); + UMaterial* Material = UMCPAssetFinder::LoadMaterialByName(MaterialName, LoadError); if (!Material) { return MCPUtils::MakeErrorJson(Result, LoadError); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_MaterialRead.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_MaterialRead.cpp index ed450576..217e4f07 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_MaterialRead.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_MaterialRead.cpp @@ -1,3 +1,4 @@ +#include "MCPAssetFinder.h" #include "BlueprintMCPServer.h" #include "MCPUtils.h" #include "Materials/Material.h" @@ -47,7 +48,7 @@ void FBlueprintMCPServer::HandleListMaterials(const FJsonObject* Json, FJsonObje if (bIncludeMaterials) { - for (const FAssetData& Asset : AllMaterialAssets) + for (const FAssetData& Asset : UMCPAssetFinder::GetMaterialAssets()) { FString Name = Asset.AssetName.ToString(); FString Path = Asset.PackageName.ToString(); @@ -71,7 +72,7 @@ void FBlueprintMCPServer::HandleListMaterials(const FJsonObject* Json, FJsonObje if (bIncludeInstances) { - for (const FAssetData& Asset : AllMaterialInstanceAssets) + for (const FAssetData& Asset : UMCPAssetFinder::GetMaterialInstanceAssets()) { FString Name = Asset.AssetName.ToString(); FString Path = Asset.PackageName.ToString(); @@ -93,7 +94,7 @@ void FBlueprintMCPServer::HandleListMaterials(const FJsonObject* Json, FJsonObje } } - int32 Total = AllMaterialAssets.Num() + AllMaterialInstanceAssets.Num(); + int32 Total = UMCPAssetFinder::GetMaterialAssets().Num() + UMCPAssetFinder::GetMaterialInstanceAssets().Num(); Result->SetNumberField(TEXT("count"), Entries.Num()); Result->SetNumberField(TEXT("total"), Total); @@ -116,7 +117,7 @@ void FBlueprintMCPServer::HandleGetMaterial(const FJsonObject* Json, FJsonObject // Try loading as UMaterial first FString LoadError; - UMaterial* Material = LoadMaterialByName(DecodedName, LoadError); + UMaterial* Material = UMCPAssetFinder::LoadMaterialByName(DecodedName, LoadError); if (Material) { UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: GetMaterial — loaded material '%s'"), *Material->GetName()); @@ -271,7 +272,7 @@ void FBlueprintMCPServer::HandleGetMaterial(const FJsonObject* Json, FJsonObject // Try loading as MaterialInstance FString MILoadError; - UMaterialInstanceConstant* MI = LoadMaterialInstanceByName(DecodedName, MILoadError); + UMaterialInstanceConstant* MI = UMCPAssetFinder::LoadMaterialInstanceByName(DecodedName, MILoadError); if (MI) { UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: GetMaterial — loaded material instance '%s'"), *MI->GetName()); @@ -361,7 +362,7 @@ void FBlueprintMCPServer::HandleGetMaterialGraph(const FJsonObject* Json, FJsonO FString DecodedName = MCPUtils::UrlDecode(Name); FString LoadError; - UMaterial* Material = LoadMaterialByName(DecodedName, LoadError); + UMaterial* Material = UMCPAssetFinder::LoadMaterialByName(DecodedName, LoadError); if (!Material) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -411,7 +412,7 @@ void FBlueprintMCPServer::HandleDescribeMaterial(const FJsonObject* Json, FJsonO } FString LoadError; - UMaterial* Material = LoadMaterialByName(MaterialName, LoadError); + UMaterial* Material = UMCPAssetFinder::LoadMaterialByName(MaterialName, LoadError); if (!Material) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -623,7 +624,7 @@ void FBlueprintMCPServer::HandleSearchMaterials(const FJsonObject* Json, FJsonOb TArray> Results; - for (const FAssetData& Asset : AllMaterialAssets) + for (const FAssetData& Asset : UMCPAssetFinder::GetMaterialAssets()) { if (Results.Num() >= MaxResults) break; @@ -705,7 +706,7 @@ void FBlueprintMCPServer::HandleFindMaterialReferences(const FJsonObject* Json, // Try to find the material's package path FString PackagePath; - FAssetData* MatAsset = FindMaterialAsset(MaterialName); + FAssetData* MatAsset = UMCPAssetFinder::FindMaterialAsset(MaterialName); if (MatAsset) { PackagePath = MatAsset->PackageName.ToString(); @@ -713,7 +714,7 @@ void FBlueprintMCPServer::HandleFindMaterialReferences(const FJsonObject* Json, else { // Try material instance - FAssetData* MIAsset = FindMaterialInstanceAsset(MaterialName); + FAssetData* MIAsset = UMCPAssetFinder::FindMaterialInstanceAsset(MaterialName); if (MIAsset) { PackagePath = MIAsset->PackageName.ToString(); @@ -758,7 +759,7 @@ void FBlueprintMCPServer::HandleListMaterialFunctions(const FJsonObject* Json, F TArray> Entries; - for (const FAssetData& Asset : AllMaterialFunctionAssets) + for (const FAssetData& Asset : UMCPAssetFinder::GetMaterialFunctionAssets()) { FString Name = Asset.AssetName.ToString(); FString Path = Asset.PackageName.ToString(); @@ -779,7 +780,7 @@ void FBlueprintMCPServer::HandleListMaterialFunctions(const FJsonObject* Json, F } Result->SetNumberField(TEXT("count"), Entries.Num()); - Result->SetNumberField(TEXT("total"), AllMaterialFunctionAssets.Num()); + Result->SetNumberField(TEXT("total"), UMCPAssetFinder::GetMaterialFunctionAssets().Num()); Result->SetArrayField(TEXT("functions"), Entries); } @@ -798,7 +799,7 @@ void FBlueprintMCPServer::HandleGetMaterialFunction(const FJsonObject* Json, FJs FString DecodedName = MCPUtils::UrlDecode(Name); FString LoadError; - UMaterialFunction* MF = LoadMaterialFunctionByName(DecodedName, LoadError); + UMaterialFunction* MF = UMCPAssetFinder::LoadMaterialFunctionByName(DecodedName, LoadError); if (!MF) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -882,7 +883,7 @@ void FBlueprintMCPServer::HandleValidateMaterial(const FJsonObject* Json, FJsonO // Load material FString LoadError; - UMaterial* Material = LoadMaterialByName(MaterialName, LoadError); + UMaterial* Material = UMCPAssetFinder::LoadMaterialByName(MaterialName, LoadError); if (!Material) { return MCPUtils::MakeErrorJson(Result, LoadError); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Mutation.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Mutation.cpp index 8055014d..94754879 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Mutation.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Mutation.cpp @@ -1,4 +1,5 @@ #include "BlueprintMCPHandlers_Mutation.h" +#include "MCPAssetFinder.h" #include "BlueprintMCPServer.h" #include "MCPUtils.h" #include "Engine/Blueprint.h" @@ -52,11 +53,10 @@ void UMCPHandler_ReplaceFunctionCallsInBlueprint::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); // Load Blueprint FString LoadError; - UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Blueprint, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -289,7 +289,6 @@ void UMCPHandler_ReplaceFunctionCallsInBlueprint::Handle(const FJsonObject* Json void UMCPHandler_DeleteAsset::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); // Check if asset file exists on disk FString PackageFilename = FPackageName::LongPackageNameToFilename( @@ -391,11 +390,6 @@ void UMCPHandler_DeleteAsset::Handle(const FJsonObject* Json, FJsonObject* Resul if (bDeleted) { - // Remove from our cached asset list if present - Helper->AllBlueprintAssets.RemoveAll([this](const FAssetData& Asset) { - return Asset.PackageName.ToString() == AssetPath; - }); - // Trigger an asset registry rescan so it notices the deletion TArray PathsToScan; int32 LastSlash; @@ -431,10 +425,9 @@ void UMCPHandler_DeleteAsset::Handle(const FJsonObject* Json, FJsonObject* Resul void UMCPHandler_ConnectBlueprintPins::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); FString LoadError; - UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Blueprint, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -534,10 +527,9 @@ void UMCPHandler_ConnectBlueprintPins::Handle(const FJsonObject* Json, FJsonObje void UMCPHandler_DisconnectBlueprintPins::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); FString LoadError; - UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Blueprint, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -641,11 +633,10 @@ void UMCPHandler_DisconnectBlueprintPins::Handle(const FJsonObject* Json, FJsonO void UMCPHandler_RefreshAllNodesInGraph::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); // Load Blueprint FString LoadError; - UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Blueprint, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -748,7 +739,6 @@ void UMCPHandler_RefreshAllNodesInGraph::Handle(const FJsonObject* Json, FJsonOb void UMCPHandler_SetPinDefaultValues::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); TArray> Results; int32 SuccessCount = 0; @@ -773,7 +763,7 @@ void UMCPHandler_SetPinDefaultValues::Handle(const FJsonObject* Json, FJsonObjec EntryResult->SetStringField(TEXT("pinName"), Entry.PinName); FString LoadError; - UBlueprint* BP = Helper->LoadBlueprintByName(Entry.Blueprint, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Entry.Blueprint, LoadError); if (!BP) { EntryResult->SetStringField(TEXT("error"), LoadError); @@ -849,11 +839,10 @@ void UMCPHandler_SetPinDefaultValues::Handle(const FJsonObject* Json, FJsonObjec void UMCPHandler_ChangeStructNodeType::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); // Load Blueprint FString LoadError; - UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Blueprint, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -1063,10 +1052,9 @@ void UMCPHandler_ChangeStructNodeType::Handle(const FJsonObject* Json, FJsonObje void UMCPHandler_DeleteNodeFromGraph::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); FString LoadError; - UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Blueprint, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -1139,7 +1127,6 @@ void UMCPHandler_DeleteNodeFromGraph::Handle(const FJsonObject* Json, FJsonObjec void UMCPHandler_RenameAsset::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Renaming asset '%s' -> '%s'"), *AssetPath, *NewPath); @@ -1151,7 +1138,7 @@ void UMCPHandler_RenameAsset::Handle(const FJsonObject* Json, FJsonObject* Resul TArray RenameData; // We need to load the asset to get the object - FAssetData* FoundAsset = Helper->FindAnyAsset(AssetPath); + FAssetData* FoundAsset = UMCPAssetFinder::FindAnyAsset(AssetPath); if (!FoundAsset) { return MCPUtils::MakeErrorJson(Result, FString::Printf(TEXT("Asset '%s' not found. Checked Blueprints, Materials, Material Instances, and Material Functions."), *AssetPath)); @@ -1190,16 +1177,6 @@ void UMCPHandler_RenameAsset::Handle(const FJsonObject* Json, FJsonObject* Resul if (bSuccess) { - // Update all cached asset lists — re-scan to pick up the new path - FAssetRegistryModule& ARM = FModuleManager::LoadModuleChecked("AssetRegistry"); - Helper->AllBlueprintAssets.Empty(); - ARM.Get().GetAssetsByClass(UBlueprint::StaticClass()->GetClassPathName(), Helper->AllBlueprintAssets, true); - Helper->AllMaterialAssets.Empty(); - ARM.Get().GetAssetsByClass(UMaterial::StaticClass()->GetClassPathName(), Helper->AllMaterialAssets, true); - Helper->AllMaterialInstanceAssets.Empty(); - ARM.Get().GetAssetsByClass(UMaterialInstanceConstant::StaticClass()->GetClassPathName(), Helper->AllMaterialInstanceAssets, true); - Helper->AllMaterialFunctionAssets.Empty(); - ARM.Get().GetAssetsByClass(UMaterialFunction::StaticClass()->GetClassPathName(), Helper->AllMaterialFunctionAssets, true); } UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Rename %s"), bSuccess ? TEXT("succeeded") : TEXT("failed")); @@ -1221,11 +1198,10 @@ void UMCPHandler_RenameAsset::Handle(const FJsonObject* Json, FJsonObject* Resul void UMCPHandler_SetClassDefaultValue::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); // Load Blueprint FString LoadError; - UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Blueprint, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -1277,7 +1253,7 @@ void UMCPHandler_SetClassDefaultValue::Handle(const FJsonObject* Json, FJsonObje if (!ResolvedClass) { FString BPLoadError; - UBlueprint* ValueBP = Helper->LoadBlueprintByName(Value, BPLoadError); + UBlueprint* ValueBP = UMCPAssetFinder::LoadBlueprintByName(Value, BPLoadError); if (ValueBP && ValueBP->GeneratedClass) { ResolvedClass = ValueBP->GeneratedClass; @@ -1317,7 +1293,7 @@ void UMCPHandler_SetClassDefaultValue::Handle(const FJsonObject* Json, FJsonObje // Try loading as a Blueprint asset FString ObjLoadError; - UBlueprint* ValueBP = Helper->LoadBlueprintByName(Value, ObjLoadError); + UBlueprint* ValueBP = UMCPAssetFinder::LoadBlueprintByName(Value, ObjLoadError); if (ValueBP && ValueBP->GeneratedClass) { ResolvedObj = ValueBP->GeneratedClass->GetDefaultObject(); @@ -1379,10 +1355,9 @@ void UMCPHandler_SetClassDefaultValue::Handle(const FJsonObject* Json, FJsonObje void UMCPHandler_SetNodePositions::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); FString LoadError; - UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Blueprint, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -1443,10 +1418,9 @@ void UMCPHandler_SetNodePositions::Handle(const FJsonObject* Json, FJsonObject* void UMCPHandler_DuplicateNodesInGraph::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); FString LoadError; - UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Blueprint, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -1582,10 +1556,9 @@ void UMCPHandler_DuplicateNodesInGraph::Handle(const FJsonObject* Json, FJsonObj void UMCPHandler_GetNodeComment::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); FString LoadError; - UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Blueprint, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -1612,10 +1585,9 @@ void UMCPHandler_GetNodeComment::Handle(const FJsonObject* Json, FJsonObject* Re void UMCPHandler_SetNodeComment::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); FString LoadError; - UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Blueprint, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -1680,11 +1652,10 @@ void UMCPHandler_SearchSpawnableNodeTypes::Handle(const FJsonObject* Json, FJson void UMCPHandler_SpawnNodesInGraph::Handle(const FJsonObject* Json, FJsonObject* Result) { - MCPHelper* Helper = MCPHelper::Get(); // Load Blueprint FString LoadError; - UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Blueprint, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Mutation.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Mutation.h similarity index 100% rename from Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Mutation.h rename to Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Mutation.h diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Params.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Params.cpp index 9076f561..12d0a80b 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Params.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Params.cpp @@ -1,3 +1,4 @@ +#include "MCPAssetFinder.h" #include "BlueprintMCPServer.h" #include "MCPUtils.h" #include "Engine/Blueprint.h" @@ -28,7 +29,7 @@ void FBlueprintMCPServer::HandleChangeFunctionParamType(const FJsonObject* Json, // Load Blueprint FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -246,7 +247,7 @@ void FBlueprintMCPServer::HandleRemoveFunctionParameter(const FJsonObject* Json, // Load Blueprint FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -407,7 +408,7 @@ void FBlueprintMCPServer::HandleAddFunctionParameter(const FJsonObject* Json, FJ // Load Blueprint FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Read.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Read.cpp index 97b18fb0..d3e89546 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Read.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Read.cpp @@ -1,3 +1,4 @@ +#include "MCPAssetFinder.h" #include "BlueprintMCPServer.h" #include "MCPUtils.h" #include "Engine/Blueprint.h" @@ -36,7 +37,7 @@ void FBlueprintMCPServer::HandleList(const FJsonObject* Json, FJsonObject* Resul TArray> Entries; if (bIncludeRegular) - for (const FAssetData& Asset : AllBlueprintAssets) + for (const FAssetData& Asset : UMCPAssetFinder::GetBlueprintAssets()) { FString Name = Asset.AssetName.ToString(); FString Path = Asset.PackageName.ToString(); @@ -76,7 +77,7 @@ void FBlueprintMCPServer::HandleList(const FJsonObject* Json, FJsonObject* Resul // Also include level blueprints from maps if (bIncludeLevel) - for (const FAssetData& Asset : AllMapAssets) + for (const FAssetData& Asset : UMCPAssetFinder::GetMapAssets()) { FString Name = Asset.AssetName.ToString(); FString Path = Asset.PackageName.ToString(); @@ -108,7 +109,7 @@ void FBlueprintMCPServer::HandleList(const FJsonObject* Json, FJsonObject* Resul } Result->SetNumberField(TEXT("count"), Entries.Num()); - Result->SetNumberField(TEXT("total"), AllBlueprintAssets.Num() + AllMapAssets.Num()); + Result->SetNumberField(TEXT("total"), UMCPAssetFinder::GetBlueprintAssets().Num() + UMCPAssetFinder::GetMapAssets().Num()); Result->SetArrayField(TEXT("blueprints"), Entries); } @@ -121,7 +122,7 @@ void FBlueprintMCPServer::HandleGetBlueprint(const FJsonObject* Json, FJsonObjec } FString LoadError; - UBlueprint* BP = LoadBlueprintByName(Name, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Name, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -144,7 +145,7 @@ void FBlueprintMCPServer::HandleGetGraph(const FJsonObject* Json, FJsonObject* R FString DecodedGraphName = MCPUtils::UrlDecode(GraphName); FString LoadError; - UBlueprint* BP = LoadBlueprintByName(Name, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Name, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -256,7 +257,7 @@ void FBlueprintMCPServer::HandleSearch(const FJsonObject* Json, FJsonObject* Res }; TArray> Results; - for (const FAssetData& Asset : AllBlueprintAssets) + for (const FAssetData& Asset : UMCPAssetFinder::GetBlueprintAssets()) { if (Results.Num() >= MaxResults) break; @@ -273,7 +274,7 @@ void FBlueprintMCPServer::HandleSearch(const FJsonObject* Json, FJsonObject* Res } // Also search level blueprints - for (FAssetData& MapAsset : AllMapAssets) + for (const FAssetData& MapAsset : UMCPAssetFinder::GetMapAssets()) { if (Results.Num() >= MaxResults) break; @@ -317,7 +318,7 @@ void FBlueprintMCPServer::HandleTestSave(const FJsonObject* Json, FJsonObject* R UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: test-save requested for '%s'"), *Name); FString LoadError; - UBlueprint* BP = LoadBlueprintByName(Name, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Name, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -355,7 +356,7 @@ void FBlueprintMCPServer::HandleFindReferences(const FJsonObject* Json, FJsonObj // Build set of known Blueprint package names for filtering TSet BlueprintPackages; - for (const FAssetData& Asset : AllBlueprintAssets) + for (const FAssetData& Asset : UMCPAssetFinder::GetBlueprintAssets()) { BlueprintPackages.Add(Asset.PackageName.ToString()); } @@ -586,7 +587,7 @@ void FBlueprintMCPServer::HandleSearchByType(const FJsonObject* Json, FJsonObjec }; // Search regular blueprints - for (const FAssetData& Asset : AllBlueprintAssets) + for (const FAssetData& Asset : UMCPAssetFinder::GetBlueprintAssets()) { if (Results.Num() >= MaxResults) break; @@ -606,7 +607,7 @@ void FBlueprintMCPServer::HandleSearchByType(const FJsonObject* Json, FJsonObjec } // Search level blueprints from maps - for (FAssetData& MapAsset : AllMapAssets) + for (const FAssetData& MapAsset : UMCPAssetFinder::GetMapAssets()) { if (Results.Num() >= MaxResults) break; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Snapshot.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Snapshot.cpp index 7e0ae0b9..79e29e22 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Snapshot.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Snapshot.cpp @@ -1,3 +1,4 @@ +#include "MCPAssetFinder.h" #include "BlueprintMCPServer.h" #include "MCPUtils.h" #include "Engine/Blueprint.h" @@ -268,7 +269,7 @@ void FBlueprintMCPServer::HandleSnapshotGraph(const FJsonObject* Json, FJsonObje // Load Blueprint FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -368,7 +369,7 @@ void FBlueprintMCPServer::HandleDiffGraph(const FJsonObject* Json, FJsonObject* // Load the current blueprint FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -587,7 +588,7 @@ void FBlueprintMCPServer::HandleRestoreGraph(const FJsonObject* Json, FJsonObjec // Load the current blueprint FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -820,7 +821,7 @@ void FBlueprintMCPServer::HandleFindDisconnectedPins(const FJsonObject* Json, FJ } else if (!PathFilter.IsEmpty()) { - for (const FAssetData& Asset : AllBlueprintAssets) + for (const FAssetData& Asset : UMCPAssetFinder::GetBlueprintAssets()) { if (Asset.PackageName.ToString().Contains(PathFilter) || Asset.AssetName.ToString().Contains(PathFilter)) { @@ -844,7 +845,7 @@ void FBlueprintMCPServer::HandleFindDisconnectedPins(const FJsonObject* Json, FJ for (const FString& BPName : BlueprintsToScan) { FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BPName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BPName, LoadError); if (!BP) continue; BlueprintsScanned++; @@ -1205,10 +1206,10 @@ void FBlueprintMCPServer::HandleAnalyzeRebuildImpact(const FJsonObject* Json, FJ int32 TotalBreakMakeNodes = 0; int32 TotalConnectionsAtRisk = 0; - for (const FAssetData& Asset : AllBlueprintAssets) + for (const FAssetData& Asset : UMCPAssetFinder::GetBlueprintAssets()) { FString LoadError; - UBlueprint* BP = LoadBlueprintByName(Asset.AssetName.ToString(), LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(Asset.AssetName.ToString(), LoadError); if (!BP) continue; FBlueprintImpact Impact; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Validation.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Validation.cpp index 2dc9be82..423f8fb5 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Validation.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Validation.cpp @@ -1,3 +1,4 @@ +#include "MCPAssetFinder.h" #include "BlueprintMCPServer.h" #include "MCPUtils.h" #include "Engine/Blueprint.h" @@ -186,7 +187,7 @@ void FBlueprintMCPServer::HandleValidateBlueprint(const FJsonObject* Json, FJson // Load Blueprint FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -211,9 +212,9 @@ void FBlueprintMCPServer::HandleValidateAllBlueprints(const FJsonObject* Json, F // First pass: collect matching asset indices (string comparisons only, no GetAsset()) TArray MatchingIndices; - for (int32 i = 0; i < AllBlueprintAssets.Num(); i++) + for (int32 i = 0; i < UMCPAssetFinder::GetBlueprintAssets().Num(); i++) { - const FAssetData& Asset = AllBlueprintAssets[i]; + const FAssetData& Asset = UMCPAssetFinder::GetBlueprintAssets()[i]; if (!Filter.IsEmpty()) { FString AssetName = Asset.AssetName.ToString(); @@ -254,7 +255,7 @@ void FBlueprintMCPServer::HandleValidateAllBlueprints(const FJsonObject* Json, F for (int32 Idx = StartIdx; Idx < EndIdx; Idx++) { - const FAssetData& Asset = AllBlueprintAssets[MatchingIndices[Idx]]; + const FAssetData& Asset = UMCPAssetFinder::GetBlueprintAssets()[MatchingIndices[Idx]]; FString AssetName = Asset.AssetName.ToString(); FString PackagePath = Asset.PackageName.ToString(); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Variables.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Variables.cpp index 5ab19cd8..5b3770bb 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Variables.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPHandlers_Variables.cpp @@ -1,3 +1,4 @@ +#include "MCPAssetFinder.h" #include "BlueprintMCPServer.h" #include "MCPUtils.h" #include "Engine/Blueprint.h" @@ -34,7 +35,7 @@ void FBlueprintMCPServer::HandleChangeVariableType(const FJsonObject* Json, FJso // Load Blueprint FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -250,7 +251,7 @@ void FBlueprintMCPServer::HandleAddVariable(const FJsonObject* Json, FJsonObject // Load Blueprint FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -332,7 +333,7 @@ void FBlueprintMCPServer::HandleRemoveVariable(const FJsonObject* Json, FJsonObj // Load Blueprint FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); @@ -400,7 +401,7 @@ void FBlueprintMCPServer::HandleSetVariableMetadata(const FJsonObject* Json, FJs // Load Blueprint FString LoadError; - UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError); + UBlueprint* BP = UMCPAssetFinder::LoadBlueprintByName(BlueprintName, LoadError); if (!BP) { return MCPUtils::MakeErrorJson(Result, LoadError); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPServer.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPServer.cpp index 6030944d..64c5f226 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPServer.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPServer.cpp @@ -1,6 +1,7 @@ #include "BlueprintMCPServer.h" #include "MCPHandler.h" #include "MCPUtils.h" +#include "MCPAssetFinder.h" #include "UObject/StrongObjectPtr.h" #include "Materials/MaterialExpression.h" #include "AssetRegistry/AssetRegistryModule.h" @@ -112,164 +113,6 @@ FBlueprintMCPServer* FBlueprintMCPServer::Get() return Sub ? Sub->GetServer() : nullptr; } -// ============================================================ -// Helpers -// ============================================================ - -FAssetData* FBlueprintMCPServer::FindAnyAsset(const FString& NameOrPath) -{ - FAssetData* Asset = FindBlueprintAsset(NameOrPath); - if (!Asset) - Asset = FindMaterialAsset(NameOrPath); - if (!Asset) - Asset = FindMaterialInstanceAsset(NameOrPath); - if (!Asset) - Asset = FindMaterialFunctionAsset(NameOrPath); - return Asset; -} - -FAssetData* FBlueprintMCPServer::FindBlueprintAsset(const FString& NameOrPath) -{ - for (FAssetData& Asset : AllBlueprintAssets) - { - if (Asset.AssetName.ToString() == NameOrPath || Asset.PackageName.ToString() == NameOrPath) - { - return &Asset; - } - } - // Case-insensitive fallback - for (FAssetData& Asset : AllBlueprintAssets) - { - if (Asset.AssetName.ToString().Equals(NameOrPath, ESearchCase::IgnoreCase) || - Asset.PackageName.ToString().Equals(NameOrPath, ESearchCase::IgnoreCase)) - { - return &Asset; - } - } - return nullptr; -} - -FAssetData* FBlueprintMCPServer::FindMapAsset(const FString& NameOrPath) -{ - for (FAssetData& Asset : AllMapAssets) - { - if (Asset.AssetName.ToString() == NameOrPath || Asset.PackageName.ToString() == NameOrPath) - { - return &Asset; - } - } - // Case-insensitive fallback - for (FAssetData& Asset : AllMapAssets) - { - if (Asset.AssetName.ToString().Equals(NameOrPath, ESearchCase::IgnoreCase) || - Asset.PackageName.ToString().Equals(NameOrPath, ESearchCase::IgnoreCase)) - { - return &Asset; - } - } - return nullptr; -} - -UBlueprint* FBlueprintMCPServer::LoadBlueprintByName(const FString& NameOrPath, FString& OutError) -{ - // Strategy 1: Try as a regular Blueprint asset - FAssetData* Asset = FindBlueprintAsset(NameOrPath); - if (Asset) - { - UBlueprint* BP = Cast(Asset->GetAsset()); - if (BP) return BP; - } - - // Strategy 2: Try as a level blueprint (from a .umap) - FAssetData* MapAsset = FindMapAsset(NameOrPath); - if (MapAsset) - { - UWorld* World = Cast(MapAsset->GetAsset()); - if (World && World->PersistentLevel) - { - ULevelScriptBlueprint* LevelBP = World->PersistentLevel->GetLevelScriptBlueprint(true); - if (LevelBP) - { - UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Loaded level blueprint from map '%s'"), - *NameOrPath); - return LevelBP; - } - } - OutError = FString::Printf(TEXT("Map '%s' loaded but its level blueprint could not be retrieved. The map may not contain a level blueprint."), *NameOrPath); - return nullptr; - } - - OutError = FString::Printf(TEXT("Blueprint or map '%s' not found. Use list_blueprints to see available assets. Level blueprints are referenced by their map name (e.g. 'MAP_Ward')."), *NameOrPath); - return nullptr; -} - -UMaterial* FBlueprintMCPServer::LoadMaterialByName(const FString& NameOrPath, FString& OutError) -{ - FAssetData* Asset = FindMaterialAsset(NameOrPath); - if (Asset) - { - UMaterial* Mat = Cast(Asset->GetAsset()); - if (Mat) return Mat; - } - OutError = FString::Printf(TEXT("Material '%s' not found. Use list_materials to see available assets."), *NameOrPath); - return nullptr; -} - -FAssetData* FBlueprintMCPServer::FindMaterialInstanceAsset(const FString& NameOrPath) -{ - for (FAssetData& Asset : AllMaterialInstanceAssets) - { - if (Asset.AssetName.ToString() == NameOrPath || Asset.PackageName.ToString() == NameOrPath) - return &Asset; - } - for (FAssetData& Asset : AllMaterialInstanceAssets) - { - if (Asset.AssetName.ToString().Equals(NameOrPath, ESearchCase::IgnoreCase) || - Asset.PackageName.ToString().Equals(NameOrPath, ESearchCase::IgnoreCase)) - return &Asset; - } - return nullptr; -} - -UMaterialInstanceConstant* FBlueprintMCPServer::LoadMaterialInstanceByName(const FString& NameOrPath, FString& OutError) -{ - FAssetData* Asset = FindMaterialInstanceAsset(NameOrPath); - if (Asset) - { - UMaterialInstanceConstant* MI = Cast(Asset->GetAsset()); - if (MI) return MI; - } - OutError = FString::Printf(TEXT("Material Instance '%s' not found. Use list_materials to see available assets."), *NameOrPath); - return nullptr; -} - -FAssetData* FBlueprintMCPServer::FindMaterialFunctionAsset(const FString& NameOrPath) -{ - for (FAssetData& Asset : AllMaterialFunctionAssets) - { - if (Asset.AssetName.ToString() == NameOrPath || Asset.PackageName.ToString() == NameOrPath) - return &Asset; - } - for (FAssetData& Asset : AllMaterialFunctionAssets) - { - if (Asset.AssetName.ToString().Equals(NameOrPath, ESearchCase::IgnoreCase) || - Asset.PackageName.ToString().Equals(NameOrPath, ESearchCase::IgnoreCase)) - return &Asset; - } - return nullptr; -} - -UMaterialFunction* FBlueprintMCPServer::LoadMaterialFunctionByName(const FString& NameOrPath, FString& OutError) -{ - FAssetData* Asset = FindMaterialFunctionAsset(NameOrPath); - if (Asset) - { - UMaterialFunction* MF = Cast(Asset->GetAsset()); - if (MF) return MF; - } - OutError = FString::Printf(TEXT("Material Function '%s' not found. Use list_material_functions to see available assets."), *NameOrPath); - return nullptr; -} // ============================================================ // SEH wrappers for crash-safe compilation and saving. @@ -415,29 +258,6 @@ bool FBlueprintMCPServer::Start(int32 InPort, bool bEditorMode) Port = InPort; bIsEditor = bEditorMode; - // Scan asset registry - UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Scanning asset registry...")); - FAssetRegistryModule& ARM = FModuleManager::LoadModuleChecked("AssetRegistry"); - ARM.Get().SearchAllAssets(true); - ARM.Get().GetAssetsByClass(UBlueprint::StaticClass()->GetClassPathName(), AllBlueprintAssets, true); - UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Found %d Blueprint assets."), AllBlueprintAssets.Num()); - - // Also scan for map assets (level blueprints live inside .umap packages) - ARM.Get().GetAssetsByClass(UWorld::StaticClass()->GetClassPathName(), AllMapAssets, false); - UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Found %d Map assets (potential level blueprints)."), AllMapAssets.Num()); - - // Scan for Material assets - ARM.Get().GetAssetsByClass(UMaterial::StaticClass()->GetClassPathName(), AllMaterialAssets, false); - UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Found %d Material assets."), AllMaterialAssets.Num()); - - // Scan for Material Instance assets - ARM.Get().GetAssetsByClass(UMaterialInstanceConstant::StaticClass()->GetClassPathName(), AllMaterialInstanceAssets, false); - UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Found %d Material Instance assets."), AllMaterialInstanceAssets.Num()); - - // Scan for Material Function assets - ARM.Get().GetAssetsByClass(UMaterialFunction::StaticClass()->GetClassPathName(), AllMaterialFunctionAssets, false); - UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Found %d Material Function assets."), AllMaterialFunctionAssets.Num()); - // Start HTTP server FHttpServerModule& HttpModule = FModuleManager::LoadModuleChecked("HTTPServer"); TSharedPtr Router = HttpModule.GetHttpRouter(Port); @@ -477,11 +297,11 @@ bool FBlueprintMCPServer::Start(int32 InPort, bool bEditorMode) TSharedRef J = MakeShared(); J->SetStringField(TEXT("status"), TEXT("ok")); J->SetStringField(TEXT("mode"), bIsEditor ? TEXT("editor") : TEXT("commandlet")); - J->SetNumberField(TEXT("blueprintCount"), AllBlueprintAssets.Num()); - J->SetNumberField(TEXT("mapCount"), AllMapAssets.Num()); - J->SetNumberField(TEXT("materialCount"), AllMaterialAssets.Num()); - J->SetNumberField(TEXT("materialInstanceCount"), AllMaterialInstanceAssets.Num()); - J->SetNumberField(TEXT("materialFunctionCount"), AllMaterialFunctionAssets.Num()); + J->SetNumberField(TEXT("blueprintCount"), UMCPAssetFinder::GetBlueprintAssets().Num()); + J->SetNumberField(TEXT("mapCount"), UMCPAssetFinder::GetMapAssets().Num()); + J->SetNumberField(TEXT("materialCount"), UMCPAssetFinder::GetMaterialAssets().Num()); + J->SetNumberField(TEXT("materialInstanceCount"), UMCPAssetFinder::GetMaterialInstanceAssets().Num()); + J->SetNumberField(TEXT("materialFunctionCount"), UMCPAssetFinder::GetMaterialFunctionAssets().Num()); TUniquePtr R = FHttpServerResponse::Create( MCPUtils::JsonToString(J), TEXT("application/json")); OnComplete(MoveTemp(R)); @@ -509,9 +329,6 @@ bool FBlueprintMCPServer::Start(int32 InPort, bool bEditorMode) return true; })); - // /api/rescan — re-scan asset registry and refresh cached asset lists (game thread) - Router->BindRoute(FHttpPath(TEXT("/api/rescan_asset_registry")), EHttpServerRequestVerbs::VERB_POST, - QueuedHandler(TEXT("rescan_asset_registry"))); // /api/list — answered directly (only reads immutable asset list) Router->BindRoute(FHttpPath(TEXT("/api/list_blueprint_assets")), EHttpServerRequestVerbs::VERB_GET, @@ -937,7 +754,6 @@ void FBlueprintMCPServer::RegisterHandlers() HandlerMap.Add(Name, [this, Fn](const FJsonObject* Json, FJsonObject* Result) { (this->*Fn)(Json, Result); }); }; - H(TEXT("rescan_asset_registry"), &FBlueprintMCPServer::HandleRescan); H(TEXT("dump_blueprint"), &FBlueprintMCPServer::HandleGetBlueprint); H(TEXT("dump_blueprint_graph"), &FBlueprintMCPServer::HandleGetGraph); H(TEXT("search_within_blueprints"), &FBlueprintMCPServer::HandleSearch); @@ -1046,79 +862,4 @@ void FBlueprintMCPServer::BuildMCPHandlerRegistry() UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: %d new-style handlers registered."), MCPHandlerRegistry.Num()); } -// ============================================================ -// HandleRescan — refresh cached asset lists from asset registry -// ============================================================ - -void FBlueprintMCPServer::HandleRescan(const FJsonObject* Json, FJsonObject* Result) -{ - UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Rescanning asset registry...")); - - FAssetRegistryModule& ARM = FModuleManager::LoadModuleChecked("AssetRegistry"); - ARM.Get().SearchAllAssets(true); - - int32 OldBP = AllBlueprintAssets.Num(); - int32 OldMap = AllMapAssets.Num(); - int32 OldMat = AllMaterialAssets.Num(); - int32 OldMI = AllMaterialInstanceAssets.Num(); - int32 OldMF = AllMaterialFunctionAssets.Num(); - - AllBlueprintAssets.Empty(); - AllMapAssets.Empty(); - AllMaterialAssets.Empty(); - AllMaterialInstanceAssets.Empty(); - AllMaterialFunctionAssets.Empty(); - - ARM.Get().GetAssetsByClass(UBlueprint::StaticClass()->GetClassPathName(), AllBlueprintAssets, true); - ARM.Get().GetAssetsByClass(UWorld::StaticClass()->GetClassPathName(), AllMapAssets, false); - ARM.Get().GetAssetsByClass(UMaterial::StaticClass()->GetClassPathName(), AllMaterialAssets, false); - ARM.Get().GetAssetsByClass(UMaterialInstanceConstant::StaticClass()->GetClassPathName(), AllMaterialInstanceAssets, false); - ARM.Get().GetAssetsByClass(UMaterialFunction::StaticClass()->GetClassPathName(), AllMaterialFunctionAssets, false); - - UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Rescan complete — BP %d→%d, Map %d→%d, Mat %d→%d, MI %d→%d, MF %d→%d"), - OldBP, AllBlueprintAssets.Num(), - OldMap, AllMapAssets.Num(), - OldMat, AllMaterialAssets.Num(), - OldMI, AllMaterialInstanceAssets.Num(), - OldMF, AllMaterialFunctionAssets.Num()); - - Result->SetStringField(TEXT("status"), TEXT("ok")); - Result->SetNumberField(TEXT("blueprintCount"), AllBlueprintAssets.Num()); - Result->SetNumberField(TEXT("mapCount"), AllMapAssets.Num()); - Result->SetNumberField(TEXT("materialCount"), AllMaterialAssets.Num()); - Result->SetNumberField(TEXT("materialInstanceCount"), AllMaterialInstanceAssets.Num()); - Result->SetNumberField(TEXT("materialFunctionCount"), AllMaterialFunctionAssets.Num()); - - TSharedRef Delta = MakeShared(); - Delta->SetNumberField(TEXT("blueprints"), AllBlueprintAssets.Num() - OldBP); - Delta->SetNumberField(TEXT("maps"), AllMapAssets.Num() - OldMap); - Delta->SetNumberField(TEXT("materials"), AllMaterialAssets.Num() - OldMat); - Delta->SetNumberField(TEXT("materialInstances"), AllMaterialInstanceAssets.Num() - OldMI); - Delta->SetNumberField(TEXT("materialFunctions"), AllMaterialFunctionAssets.Num() - OldMF); - Result->SetObjectField(TEXT("delta"), Delta); -} - -// ============================================================ - -// (SerializeBlueprint, SerializeGraph, SerializeNode, SerializePin moved to MCPUtils.cpp) - -// ============================================================ -// Material helpers -// ============================================================ - -FAssetData* FBlueprintMCPServer::FindMaterialAsset(const FString& NameOrPath) -{ - for (FAssetData& Asset : AllMaterialAssets) - { - if (Asset.AssetName.ToString() == NameOrPath || Asset.PackageName.ToString() == NameOrPath) - return &Asset; - } - for (FAssetData& Asset : AllMaterialAssets) - { - if (Asset.AssetName.ToString().Equals(NameOrPath, ESearchCase::IgnoreCase) || - Asset.PackageName.ToString().Equals(NameOrPath, ESearchCase::IgnoreCase)) - return &Asset; - } - return nullptr; -} diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPAssetFinder.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPAssetFinder.cpp new file mode 100644 index 00000000..1e198119 --- /dev/null +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPAssetFinder.cpp @@ -0,0 +1,259 @@ +#include "MCPAssetFinder.h" +#include "Engine/Engine.h" +#include "Engine/Blueprint.h" +#include "Engine/World.h" +#include "Engine/Level.h" +#include "Engine/LevelScriptBlueprint.h" +#include "Materials/Material.h" +#include "Materials/MaterialInstanceConstant.h" +#include "Materials/MaterialFunction.h" +#include "AssetRegistry/AssetRegistryModule.h" +#include "AssetRegistry/IAssetRegistry.h" + +const TArray UMCPAssetFinder::EmptyAssetArray; + +// ============================================================ +// Initialize / Deinitialize — subscribe to asset registry events +// ============================================================ + +void UMCPAssetFinder::Initialize(FSubsystemCollectionBase& Collection) +{ + Super::Initialize(Collection); + + IAssetRegistry& AR = FModuleManager::LoadModuleChecked("AssetRegistry").Get(); + AR.OnAssetAdded().AddUObject(this, &UMCPAssetFinder::OnAssetEvent); + AR.OnAssetRemoved().AddUObject(this, &UMCPAssetFinder::OnAssetEvent); + AR.OnAssetUpdated().AddUObject(this, &UMCPAssetFinder::OnAssetEvent); + AR.OnAssetRenamed().AddUObject(this, &UMCPAssetFinder::OnAssetRenamed); +} + +void UMCPAssetFinder::Deinitialize() +{ + IAssetRegistry* AR = IAssetRegistry::Get(); + if (AR) + { + AR->OnAssetAdded().RemoveAll(this); + AR->OnAssetRemoved().RemoveAll(this); + AR->OnAssetUpdated().RemoveAll(this); + AR->OnAssetRenamed().RemoveAll(this); + } + Super::Deinitialize(); +} + +// ============================================================ +// GetUpdatedAssets — the gateway for all static API +// ============================================================ + +UMCPAssetFinder* UMCPAssetFinder::GetUpdatedAssets() +{ + checkf(IsInGameThread(), TEXT("MCPAssetFinder must only be accessed from the game thread")); + if (!GEngine) return nullptr; + UMCPAssetFinder* Self = GEngine->GetEngineSubsystem(); + if (!Self) return nullptr; + + IAssetRegistry& AR = FModuleManager::LoadModuleChecked("AssetRegistry").Get(); + + while (AR.IsLoadingAssets()) FPlatformProcess::Sleep(0.1f); + + if (!Self->bDirty) return Self; + + Self->AllBlueprintAssets.Empty(); + Self->AllMapAssets.Empty(); + Self->AllMaterialAssets.Empty(); + Self->AllMaterialInstanceAssets.Empty(); + Self->AllMaterialFunctionAssets.Empty(); + + AR.GetAssetsByClass(UBlueprint::StaticClass()->GetClassPathName(), Self->AllBlueprintAssets, true); + AR.GetAssetsByClass(UWorld::StaticClass()->GetClassPathName(), Self->AllMapAssets, false); + AR.GetAssetsByClass(UMaterial::StaticClass()->GetClassPathName(), Self->AllMaterialAssets, false); + AR.GetAssetsByClass(UMaterialInstanceConstant::StaticClass()->GetClassPathName(), Self->AllMaterialInstanceAssets, false); + AR.GetAssetsByClass(UMaterialFunction::StaticClass()->GetClassPathName(), Self->AllMaterialFunctionAssets, false); + + Self->bDirty = false; + + UE_LOG(LogTemp, Display, TEXT("MCPAssetFinder: Refreshed — BP %d, Map %d, Mat %d, MI %d, MF %d"), + Self->AllBlueprintAssets.Num(), Self->AllMapAssets.Num(), Self->AllMaterialAssets.Num(), + Self->AllMaterialInstanceAssets.Num(), Self->AllMaterialFunctionAssets.Num()); + + return Self; +} + +// ============================================================ +// Static asset list accessors +// ============================================================ + +const TArray& UMCPAssetFinder::GetBlueprintAssets() +{ + UMCPAssetFinder* Self = GetUpdatedAssets(); + return Self ? Self->AllBlueprintAssets : EmptyAssetArray; +} + +const TArray& UMCPAssetFinder::GetMapAssets() +{ + UMCPAssetFinder* Self = GetUpdatedAssets(); + return Self ? Self->AllMapAssets : EmptyAssetArray; +} + +const TArray& UMCPAssetFinder::GetMaterialAssets() +{ + UMCPAssetFinder* Self = GetUpdatedAssets(); + return Self ? Self->AllMaterialAssets : EmptyAssetArray; +} + +const TArray& UMCPAssetFinder::GetMaterialInstanceAssets() +{ + UMCPAssetFinder* Self = GetUpdatedAssets(); + return Self ? Self->AllMaterialInstanceAssets : EmptyAssetArray; +} + +const TArray& UMCPAssetFinder::GetMaterialFunctionAssets() +{ + UMCPAssetFinder* Self = GetUpdatedAssets(); + return Self ? Self->AllMaterialFunctionAssets : EmptyAssetArray; +} + +// ============================================================ +// Find helpers (search cached lists by name or path) +// ============================================================ + +namespace +{ + FAssetData* FindInList(const TArray& List, const FString& NameOrPath, FString* OutError) + { + bool IsPath = NameOrPath.Contains(TEXT("/")); + + // Short name match — check for duplicates + FAssetData* Found = nullptr; + + for (const FAssetData& Asset : List) + { + FName Name = IsPath ? Asset.PackageName : Asset.AssetName; + if (!Name.ToString().Equals(NameOrPath, ESearchCase::IgnoreCase)) continue; + if (!Found) + { + Found = const_cast(&Asset); + continue; + } + if (OutError) + { + *OutError = FString::Printf( + TEXT("Ambiguous asset name '%s' — matches both '%s' and '%s'. Use the full package path to disambiguate."), + *NameOrPath, *Found->PackageName.ToString(), *Asset.PackageName.ToString()); + } + return nullptr; + } + return Found; + } +} + +FAssetData* UMCPAssetFinder::FindBlueprintAsset(const FString& NameOrPath, FString* OutError) +{ + return FindInList(GetBlueprintAssets(), NameOrPath, OutError); +} + +FAssetData* UMCPAssetFinder::FindMapAsset(const FString& NameOrPath, FString* OutError) +{ + return FindInList(GetMapAssets(), NameOrPath, OutError); +} + +FAssetData* UMCPAssetFinder::FindMaterialAsset(const FString& NameOrPath, FString* OutError) +{ + return FindInList(GetMaterialAssets(), NameOrPath, OutError); +} + +FAssetData* UMCPAssetFinder::FindMaterialInstanceAsset(const FString& NameOrPath, FString* OutError) +{ + return FindInList(GetMaterialInstanceAssets(), NameOrPath, OutError); +} + +FAssetData* UMCPAssetFinder::FindMaterialFunctionAsset(const FString& NameOrPath, FString* OutError) +{ + return FindInList(GetMaterialFunctionAssets(), NameOrPath, OutError); +} + +FAssetData* UMCPAssetFinder::FindAnyAsset(const FString& NameOrPath, FString* OutError) +{ + FAssetData* Asset = FindBlueprintAsset(NameOrPath, OutError); + if (!Asset && (!OutError || OutError->IsEmpty())) Asset = FindMaterialAsset(NameOrPath, OutError); + if (!Asset && (!OutError || OutError->IsEmpty())) Asset = FindMaterialInstanceAsset(NameOrPath, OutError); + if (!Asset && (!OutError || OutError->IsEmpty())) Asset = FindMaterialFunctionAsset(NameOrPath, OutError); + return Asset; +} + +// ============================================================ +// Load helpers +// ============================================================ + +UBlueprint* UMCPAssetFinder::LoadBlueprintByName(const FString& NameOrPath, FString& OutError) +{ + // Strategy 1: Try as a regular Blueprint asset + FAssetData* Asset = FindBlueprintAsset(NameOrPath, &OutError); + if (Asset) + { + UBlueprint* BP = Cast(Asset->GetAsset()); + if (BP) return BP; + } + if (!OutError.IsEmpty()) return nullptr; + + // Strategy 2: Try as a level blueprint (from a .umap) + FAssetData* MapAsset = FindMapAsset(NameOrPath, &OutError); + if (MapAsset) + { + UWorld* World = Cast(MapAsset->GetAsset()); + if (World && World->PersistentLevel) + { + ULevelScriptBlueprint* LevelBP = World->PersistentLevel->GetLevelScriptBlueprint(true); + if (LevelBP) + { + UE_LOG(LogTemp, Display, TEXT("MCPAssetFinder: Loaded level blueprint from map '%s'"), + *NameOrPath); + return LevelBP; + } + } + OutError = FString::Printf(TEXT("Map '%s' loaded but its level blueprint could not be retrieved. The map may not contain a level blueprint."), *NameOrPath); + return nullptr; + } + if (!OutError.IsEmpty()) return nullptr; + + OutError = FString::Printf(TEXT("Blueprint or map '%s' not found. Use list_blueprints to see available assets. Level blueprints are referenced by their map name (e.g. 'MAP_Ward')."), *NameOrPath); + return nullptr; +} + +UMaterial* UMCPAssetFinder::LoadMaterialByName(const FString& NameOrPath, FString& OutError) +{ + FAssetData* Asset = FindMaterialAsset(NameOrPath, &OutError); + if (Asset) + { + UMaterial* Mat = Cast(Asset->GetAsset()); + if (Mat) return Mat; + } + if (OutError.IsEmpty()) + OutError = FString::Printf(TEXT("Material '%s' not found. Use list_materials to see available assets."), *NameOrPath); + return nullptr; +} + +UMaterialInstanceConstant* UMCPAssetFinder::LoadMaterialInstanceByName(const FString& NameOrPath, FString& OutError) +{ + FAssetData* Asset = FindMaterialInstanceAsset(NameOrPath, &OutError); + if (Asset) + { + UMaterialInstanceConstant* MI = Cast(Asset->GetAsset()); + if (MI) return MI; + } + if (OutError.IsEmpty()) + OutError = FString::Printf(TEXT("Material Instance '%s' not found. Use list_materials to see available assets."), *NameOrPath); + return nullptr; +} + +UMaterialFunction* UMCPAssetFinder::LoadMaterialFunctionByName(const FString& NameOrPath, FString& OutError) +{ + FAssetData* Asset = FindMaterialFunctionAsset(NameOrPath, &OutError); + if (Asset) + { + UMaterialFunction* MF = Cast(Asset->GetAsset()); + if (MF) return MF; + } + if (OutError.IsEmpty()) + OutError = FString::Printf(TEXT("Material Function '%s' not found. Use list_material_functions to see available assets."), *NameOrPath); + return nullptr; +} diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPServer.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPServer.h index aa6a7c9b..a293d7e8 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPServer.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPServer.h @@ -2,53 +2,11 @@ #include "CoreMinimal.h" #include "Dom/JsonObject.h" -#include "AssetRegistry/AssetData.h" #include "HttpResultCallback.h" -#include "EdGraph/EdGraphPin.h" +#include "MCPUtils.h" -class UEdGraph; -class UEdGraphNode; -class UEdGraphPin; -class UBlueprint; -class UMaterial; -class UMaterialInstanceConstant; -class UMaterialFunction; -class UMaterialExpression; class IMCPHandler; -// ----- Snapshot data structures ----- - -struct FPinConnectionRecord -{ - FString SourceNodeGuid; - FString SourcePinName; - FString TargetNodeGuid; - FString TargetPinName; -}; - -struct FNodeRecord -{ - FString NodeGuid; - FString NodeClass; - FString NodeTitle; - FString StructType; // for Break/Make nodes -}; - -struct FGraphSnapshotData -{ - TArray Nodes; - TArray Connections; -}; - -struct FGraphSnapshot -{ - FString SnapshotId; - FString BlueprintName; - FString BlueprintPath; - FDateTime CreatedAt; - TMap Graphs; // graphName -> data -}; - /** * FBlueprintMCPServer — plain C++ class (not a UCLASS) that owns all HTTP * serving logic for the Blueprint MCP protocol. @@ -85,24 +43,6 @@ public: /** Port the server is listening on. */ int32 GetPort() const { return Port; } - /** Number of indexed Blueprint assets. */ - int32 GetBlueprintCount() const { return AllBlueprintAssets.Num(); } - - /** Number of indexed Map assets. */ - int32 GetMapCount() const { return AllMapAssets.Num(); } - - /** Number of indexed Material assets. */ - int32 GetMaterialCount() const { return AllMaterialAssets.Num(); } - - /** Number of indexed Material Instance assets. */ - int32 GetMaterialInstanceCount() const { return AllMaterialInstanceAssets.Num(); } - - // ----- Cached asset lists (accessed by handlers) ----- - TArray AllBlueprintAssets; - TArray AllMapAssets; - TArray AllMaterialAssets; - TArray AllMaterialInstanceAssets; - TArray AllMaterialFunctionAssets; private: // ----- Request dispatch ----- @@ -126,9 +66,6 @@ private: bool bRunning = false; bool bIsEditor = false; - // ----- Asset registry rescan ----- - void HandleRescan(const FJsonObject* Json, FJsonObject* Result); - // ----- Request handlers (read-only) ----- void HandleList(const FJsonObject* Json, FJsonObject* Result); void HandleGetBlueprint(const FJsonObject* Json, FJsonObject* Result); @@ -254,19 +191,6 @@ private: void HandleSetStateBlendSpace(const FJsonObject* Json, FJsonObject* Result); public: - // ----- Helpers (stateful — use cached asset lists) ----- - FAssetData* FindAnyAsset(const FString& NameOrPath); - FAssetData* FindBlueprintAsset(const FString& NameOrPath); - FAssetData* FindMapAsset(const FString& NameOrPath); - UBlueprint* LoadBlueprintByName(const FString& NameOrPath, FString& OutError); - - // ----- Material helpers (stateful — use cached asset lists) ----- - FAssetData* FindMaterialAsset(const FString& NameOrPath); - UMaterial* LoadMaterialByName(const FString& NameOrPath, FString& OutError); - FAssetData* FindMaterialInstanceAsset(const FString& NameOrPath); - UMaterialInstanceConstant* LoadMaterialInstanceByName(const FString& NameOrPath, FString& OutError); - FAssetData* FindMaterialFunctionAsset(const FString& NameOrPath); - UMaterialFunction* LoadMaterialFunctionByName(const FString& NameOrPath, FString& OutError); // ----- Snapshot storage ----- TMap Snapshots; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPAssetFinder.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPAssetFinder.h new file mode 100644 index 00000000..dfcd884d --- /dev/null +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPAssetFinder.h @@ -0,0 +1,66 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Subsystems/EngineSubsystem.h" +#include "AssetRegistry/AssetData.h" +#include "MCPAssetFinder.generated.h" + +class UBlueprint; +class UMaterial; +class UMaterialInstanceConstant; +class UMaterialFunction; + +/** + * Engine subsystem that caches asset registry data for the BlueprintMCP server. + * All public API is static — callers never need to fetch the subsystem directly. + * Asset lists are auto-refreshed when the asset registry signals changes. + */ +UCLASS() +class UMCPAssetFinder : public UEngineSubsystem +{ + GENERATED_BODY() + +public: + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + virtual void Deinitialize() override; + // --- Static API: asset lists --- + static const TArray& GetBlueprintAssets(); + static const TArray& GetMapAssets(); + static const TArray& GetMaterialAssets(); + static const TArray& GetMaterialInstanceAssets(); + static const TArray& GetMaterialFunctionAssets(); + + // --- Static API: find/load helpers --- + // Find functions return nullptr if not found or if the short name is ambiguous. + // Pass OutError to get a descriptive message on failure. + static FAssetData* FindAnyAsset(const FString& NameOrPath, FString* OutError = nullptr); + static FAssetData* FindBlueprintAsset(const FString& NameOrPath, FString* OutError = nullptr); + static FAssetData* FindMapAsset(const FString& NameOrPath, FString* OutError = nullptr); + static UBlueprint* LoadBlueprintByName(const FString& NameOrPath, FString& OutError); + + static FAssetData* FindMaterialAsset(const FString& NameOrPath, FString* OutError = nullptr); + static UMaterial* LoadMaterialByName(const FString& NameOrPath, FString& OutError); + static FAssetData* FindMaterialInstanceAsset(const FString& NameOrPath, FString* OutError = nullptr); + static UMaterialInstanceConstant* LoadMaterialInstanceByName(const FString& NameOrPath, FString& OutError); + static FAssetData* FindMaterialFunctionAsset(const FString& NameOrPath, FString* OutError = nullptr); + static UMaterialFunction* LoadMaterialFunctionByName(const FString& NameOrPath, FString& OutError); + +private: + /** Get the subsystem, refreshing asset caches if stale. Returns nullptr if engine is not initialized. */ + static UMCPAssetFinder* GetUpdatedAssets(); + + // Cached asset lists + TArray AllBlueprintAssets; + TArray AllMapAssets; + TArray AllMaterialAssets; + TArray AllMaterialInstanceAssets; + TArray AllMaterialFunctionAssets; + + // Change detection — set true by asset registry delegates + bool bDirty = true; + void OnAssetEvent(const FAssetData&) { bDirty = true; } + void OnAssetRenamed(const FAssetData&, const FString&) { bDirty = true; } + + // Empty array returned when subsystem is unavailable + static const TArray EmptyAssetArray; +}; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPUtils.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPUtils.h index bc4ba3b3..939f0c0c 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPUtils.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPUtils.h @@ -15,6 +15,39 @@ class UAnimationStateMachineGraph; class UAnimStateNode; class UAnimStateTransitionNode; +// ----- Snapshot data structures ----- + +struct FPinConnectionRecord +{ + FString SourceNodeGuid; + FString SourcePinName; + FString TargetNodeGuid; + FString TargetPinName; +}; + +struct FNodeRecord +{ + FString NodeGuid; + FString NodeClass; + FString NodeTitle; + FString StructType; // for Break/Make nodes +}; + +struct FGraphSnapshotData +{ + TArray Nodes; + TArray Connections; +}; + +struct FGraphSnapshot +{ + FString SnapshotId; + FString BlueprintName; + FString BlueprintPath; + FDateTime CreatedAt; + TMap Graphs; // graphName -> data +}; + // Stateless utility functions used by MCP handlers and the MCP server. // This is effectively a namespace — all methods are static. class MCPUtils