#pragma once #include "CoreMinimal.h" #include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" #include "Engine/Blueprint.h" #include "Engine/World.h" #include "Engine/Level.h" #include "Engine/LevelScriptBlueprint.h" #include "EdGraph/EdGraphNode.h" #include "K2Node_CallFunction.h" #include "K2Node_Event.h" #include "K2Node_CustomEvent.h" #include "K2Node_VariableGet.h" #include "K2Node_VariableSet.h" #include "Blueprint_SearchContents.generated.h" // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- UCLASS() class UMCP_Blueprint_SearchContents : public UObject, public IMCPHandler { GENERATED_BODY() public: UPROPERTY(meta=(Description="Search query string to match against node titles, function names, event names, and variable names")) FString Query; UPROPERTY(meta=(Optional, Description="Filter results to blueprints whose path contains this substring")) FString Path; UPROPERTY(meta=(Optional, Description="Maximum number of results to return (default 50, max 200)")) int32 MaxResults = 0; virtual FString GetDescription() const override { return TEXT("Search across all Blueprint graphs for nodes matching a query string."); } virtual void Handle() override { int32 Limit = (MaxResults > 0) ? FMath::Clamp(MaxResults, 1, 200) : 50; int32 Count = 0; // Search one blueprint's nodes for the query string. auto SearchBlueprint = [&](UBlueprint* BP, bool bIsLevelBP) { for (UEdGraphNode* Node : MCPUtils::AllNodes(BP)) { if (Count >= Limit) return; FString Title = Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString(); FString FuncName, EventName, VarName; if (auto* CF = Cast(Node)) { FuncName = CF->FunctionReference.GetMemberName().ToString(); } else if (auto* Ev = Cast(Node)) { EventName = Ev->EventReference.GetMemberName().ToString(); } else if (auto* CE = Cast(Node)) { EventName = CE->CustomFunctionName.ToString(); } else if (auto* VG = Cast(Node)) { VarName = VG->GetVarName().ToString(); } else if (auto* VS = Cast(Node)) { VarName = VS->GetVarName().ToString(); } bool bMatch = Title.Contains(Query, ESearchCase::IgnoreCase) || (!FuncName.IsEmpty() && FuncName.Contains(Query, ESearchCase::IgnoreCase)) || (!EventName.IsEmpty() && EventName.Contains(Query, ESearchCase::IgnoreCase)) || (!VarName.IsEmpty() && VarName.Contains(Query, ESearchCase::IgnoreCase)); if (!bMatch) continue; Count++; UMCPServer::Printf(TEXT("blueprint: %s\n"), *MCPUtils::FormatName(BP)); UMCPServer::Printf(TEXT(" graph: %s\n"), *MCPUtils::FormatName(Node->GetGraph())); UMCPServer::Printf(TEXT(" node: %s\n"), *MCPUtils::FormatName(Node)); UMCPServer::Printf(TEXT(" class: %s\n"), *MCPUtils::FormatName(Node->GetClass())); if (!FuncName.IsEmpty()) UMCPServer::Printf(TEXT(" function: %s\n"), *FuncName); if (!EventName.IsEmpty()) UMCPServer::Printf(TEXT(" event: %s\n"), *EventName); if (!VarName.IsEmpty()) UMCPServer::Printf(TEXT(" variable: %s\n"), *VarName); if (bIsLevelBP) UMCPServer::Print(TEXT(" level-blueprint: true\n")); UMCPServer::Print(TEXT("\n")); } }; // Search regular blueprints MCPAssets AllBlueprints; if (!Path.IsEmpty()) AllBlueprints.Substring(Path); AllBlueprints.Load(); for (UBlueprint* BP : AllBlueprints.Objects()) { if (Count >= Limit) break; SearchBlueprint(BP, false); } // Search level blueprints MCPAssets AllWorlds; if (!Path.IsEmpty()) AllWorlds.Substring(Path); AllWorlds.Load(); for (UWorld* World : AllWorlds.Objects()) { if (Count >= Limit) break; if (!World || !World->PersistentLevel) continue; ULevelScriptBlueprint* LevelBP = World->PersistentLevel->GetLevelScriptBlueprint(false); if (!LevelBP) continue; SearchBlueprint(LevelBP, true); } UMCPServer::Printf(TEXT("Results: %d\n"), Count); if (Count >= Limit) UMCPServer::Printf(TEXT("(limit %d reached, use MaxResults to increase)\n"), Limit); } };