Skip to main content
Glama

UnrealBlueprintMCP

by BestDev
MCPBlueprintManager.cpp27.2 kB
// Copyright Epic Games, Inc. All Rights Reserved. // MCPBlueprintManager - Implementation of blueprint creation and property modification #include "MCPBlueprintManager.h" #include "MCPSettings.h" // Editor-only includes (wrapped in WITH_EDITOR) #if WITH_EDITOR #include "Kismet2/KismetEditorUtilities.h" #include "Kismet2/BlueprintEditorUtils.h" #include "Engine/SimpleConstructionScript.h" #include "Engine/SCS_Node.h" #include "AssetRegistry/AssetRegistryModule.h" #include "Engine/Blueprint.h" #include "UObject/Package.h" #include "Engine/World.h" #include "GameFramework/Actor.h" #include "GameFramework/Pawn.h" #include "GameFramework/Character.h" #include "Components/ActorComponent.h" #include "Blueprint/UserWidget.h" #include "AssetToolsModule.h" #include "IAssetTools.h" #include "PackageTools.h" #include "FileHelpers.h" #endif // JSON includes #include "Dom/JsonObject.h" #include "Serialization/JsonSerializer.h" #include "Serialization/JsonWriter.h" // Unreal includes #include "Engine/Engine.h" #include "Misc/DateTime.h" #include "Misc/Guid.h" DEFINE_LOG_CATEGORY_STATIC(LogMCPBlueprintManager, Log, All); // Static member definitions UMCPBlueprintManager* UMCPBlueprintManager::SingletonInstance = nullptr; const FString UMCPBlueprintManager::DefaultAssetPath = TEXT("/Game/Blueprints/"); // Supported parent classes for blueprint creation const TArray<FString> UMCPBlueprintManager::SupportedParentClasses = { TEXT("Actor"), TEXT("Pawn"), TEXT("Character"), TEXT("ActorComponent"), TEXT("SceneComponent"), TEXT("UserWidget"), TEXT("Object") }; UMCPBlueprintManager::UMCPBlueprintManager() : MCPSettings(nullptr) , bIsInitialized(false) , AssetNameCounter(0) { } UMCPBlueprintManager::~UMCPBlueprintManager() { // Clear singleton reference if (SingletonInstance == this) { SingletonInstance = nullptr; } } void UMCPBlueprintManager::BeginDestroy() { // Cleanup before destruction bIsInitialized = false; Super::BeginDestroy(); } UMCPBlueprintManager* UMCPBlueprintManager::Get() { if (!SingletonInstance) { SingletonInstance = NewObject<UMCPBlueprintManager>(); SingletonInstance->AddToRoot(); // Prevent garbage collection } return SingletonInstance; } bool UMCPBlueprintManager::Initialize(UMCPSettings* InSettings) { // Use provided settings or get default instance MCPSettings = InSettings ? InSettings : UMCPSettings::Get(); if (!MCPSettings) { LogMessage(TEXT("Failed to initialize MCPBlueprintManager: No valid settings found"), ELogVerbosity::Error); return false; } #if !WITH_EDITOR LogMessage(TEXT("MCPBlueprintManager requires editor environment"), ELogVerbosity::Error); return false; #endif bIsInitialized = true; LogMessage(TEXT("MCPBlueprintManager initialized successfully")); return true; } FMCPBlueprintOperationResult UMCPBlueprintManager::CreateBlueprint(const FMCPBlueprintCreateParams& CreateParams) { #if WITH_EDITOR // Validate parameters FString ErrorMessage; if (!ValidateCreateParams(CreateParams, ErrorMessage)) { LogMessage(FString::Printf(TEXT("Blueprint creation validation failed: %s"), *ErrorMessage), ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Find parent class UClass* ParentClass = FindClassByName(CreateParams.ParentClassName); if (!ParentClass) { ErrorMessage = FString::Printf(TEXT("Parent class '%s' not found"), *CreateParams.ParentClassName); LogMessage(ErrorMessage, ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Check if class is blueprintable if (!IsClassBluepritable(ParentClass)) { ErrorMessage = FString::Printf(TEXT("Class '%s' is not blueprintable"), *CreateParams.ParentClassName); LogMessage(ErrorMessage, ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Create full asset path FString FullAssetPath = CreateParams.AssetPath; if (!FullAssetPath.EndsWith(TEXT("/"))) { FullAssetPath += TEXT("/"); } FullAssetPath += CreateParams.BlueprintName; // Create package UPackage* Package = CreateBlueprintPackage(FullAssetPath); if (!Package) { ErrorMessage = FString::Printf(TEXT("Failed to create package for path: %s"), *FullAssetPath); LogMessage(ErrorMessage, ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Create blueprint UBlueprint* NewBlueprint = FKismetEditorUtilities::CreateBlueprint( ParentClass, Package, *CreateParams.BlueprintName, BPTYPE_Normal, UBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass() ); if (!NewBlueprint) { ErrorMessage = FString::Printf(TEXT("Failed to create blueprint: %s"), *CreateParams.BlueprintName); LogMessage(ErrorMessage, ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Register asset and mark package dirty RegisterBlueprintAsset(NewBlueprint); Package->MarkPackageDirty(); // Log success LogMessage(FString::Printf(TEXT("Successfully created blueprint: %s"), *FullAssetPath)); return FMCPBlueprintOperationResult(true, TEXT(""), FullAssetPath); #else LogMessage(TEXT("Blueprint creation requires editor environment"), ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, TEXT("Editor environment required")); #endif } FMCPBlueprintOperationResult UMCPBlueprintManager::SetBlueprintProperty(const FMCPBlueprintPropertyParams& PropertyParams) { #if WITH_EDITOR // Validate parameters FString ErrorMessage; if (!ValidatePropertyParams(PropertyParams, ErrorMessage)) { LogMessage(FString::Printf(TEXT("Property modification validation failed: %s"), *ErrorMessage), ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Load blueprint asset UBlueprint* Blueprint = LoadBlueprintAsset(PropertyParams.BlueprintPath); if (!Blueprint) { ErrorMessage = FString::Printf(TEXT("Failed to load blueprint: %s"), *PropertyParams.BlueprintPath); LogMessage(ErrorMessage, ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Get CDO UObject* CDO = GetBlueprintCDO(Blueprint); if (!CDO) { ErrorMessage = FString::Printf(TEXT("Failed to get CDO for blueprint: %s"), *PropertyParams.BlueprintPath); LogMessage(ErrorMessage, ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Set property if (!SetPropertyValue(CDO, PropertyParams.PropertyName, PropertyParams.PropertyValue, PropertyParams.PropertyType)) { ErrorMessage = FString::Printf(TEXT("Failed to set property '%s' on blueprint: %s"), *PropertyParams.PropertyName, *PropertyParams.BlueprintPath); LogMessage(ErrorMessage, ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Mark blueprint as modified Blueprint->MarkPackageDirty(); // Log success LogMessage(FString::Printf(TEXT("Successfully set property '%s' = '%s' on blueprint: %s"), *PropertyParams.PropertyName, *PropertyParams.PropertyValue, *PropertyParams.BlueprintPath)); return FMCPBlueprintOperationResult(true, TEXT(""), PropertyParams.BlueprintPath); #else LogMessage(TEXT("Property modification requires editor environment"), ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, TEXT("Editor environment required")); #endif } FString UMCPBlueprintManager::ProcessCreateBlueprintCommand(const FString& JsonCommand) { FMCPBlueprintCreateParams CreateParams; // Parse JSON command if (!ParseCreateBlueprintJson(JsonCommand, CreateParams)) { FMCPBlueprintOperationResult ErrorResult(false, TEXT("Failed to parse JSON command")); return CreateJsonResponse(ErrorResult); } // Execute blueprint creation FMCPBlueprintOperationResult Result = CreateBlueprint(CreateParams); // Return JSON response return CreateJsonResponse(Result); } FString UMCPBlueprintManager::ProcessSetPropertyCommand(const FString& JsonCommand) { FMCPBlueprintPropertyParams PropertyParams; // Parse JSON command if (!ParseSetPropertyJson(JsonCommand, PropertyParams)) { FMCPBlueprintOperationResult ErrorResult(false, TEXT("Failed to parse JSON command")); return CreateJsonResponse(ErrorResult); } // Execute property modification FMCPBlueprintOperationResult Result = SetBlueprintProperty(PropertyParams); // Return JSON response return CreateJsonResponse(Result); } TArray<FString> UMCPBlueprintManager::GetAvailableParentClasses() const { return SupportedParentClasses; } bool UMCPBlueprintManager::ValidateCreateParams(const FMCPBlueprintCreateParams& CreateParams, FString& OutErrorMessage) const { // Check blueprint name if (CreateParams.BlueprintName.IsEmpty()) { OutErrorMessage = TEXT("Blueprint name cannot be empty"); return false; } if (!IsValidBlueprintName(CreateParams.BlueprintName)) { OutErrorMessage = TEXT("Blueprint name contains invalid characters"); return false; } // Check parent class name if (CreateParams.ParentClassName.IsEmpty()) { OutErrorMessage = TEXT("Parent class name cannot be empty"); return false; } // Check asset path if (!IsValidAssetPath(CreateParams.AssetPath)) { OutErrorMessage = TEXT("Invalid asset path"); return false; } return true; } bool UMCPBlueprintManager::ValidatePropertyParams(const FMCPBlueprintPropertyParams& PropertyParams, FString& OutErrorMessage) const { // Check blueprint path if (PropertyParams.BlueprintPath.IsEmpty()) { OutErrorMessage = TEXT("Blueprint path cannot be empty"); return false; } // Check property name if (PropertyParams.PropertyName.IsEmpty()) { OutErrorMessage = TEXT("Property name cannot be empty"); return false; } // Property value can be empty (for clearing values) // Property type is optional for basic validation return true; } // Private helper implementations UClass* UMCPBlueprintManager::FindClassByName(const FString& ClassName) const { #if WITH_EDITOR // Common class mappings if (ClassName == TEXT("Actor")) { return AActor::StaticClass(); } else if (ClassName == TEXT("Pawn")) { return APawn::StaticClass(); } else if (ClassName == TEXT("Character")) { return ACharacter::StaticClass(); } else if (ClassName == TEXT("ActorComponent")) { return UActorComponent::StaticClass(); } else if (ClassName == TEXT("SceneComponent")) { return USceneComponent::StaticClass(); } else if (ClassName == TEXT("UserWidget")) { return UUserWidget::StaticClass(); } else if (ClassName == TEXT("Object")) { return UObject::StaticClass(); } // Try to find class by name using reflection UClass* FoundClass = FindObject<UClass>(ANY_PACKAGE, *ClassName); return FoundClass; #else return nullptr; #endif } UPackage* UMCPBlueprintManager::CreateBlueprintPackage(const FString& AssetPath) const { #if WITH_EDITOR // Create package UPackage* Package = CreatePackage(*AssetPath); if (Package) { Package->FullyLoad(); } return Package; #else return nullptr; #endif } void UMCPBlueprintManager::RegisterBlueprintAsset(UBlueprint* Blueprint) const { #if WITH_EDITOR if (Blueprint) { FAssetRegistryModule::AssetCreated(Blueprint); } #endif } UBlueprint* UMCPBlueprintManager::LoadBlueprintAsset(const FString& BlueprintPath) const { #if WITH_EDITOR return LoadObject<UBlueprint>(nullptr, *BlueprintPath); #else return nullptr; #endif } UObject* UMCPBlueprintManager::GetBlueprintCDO(UBlueprint* Blueprint) const { #if WITH_EDITOR if (Blueprint && Blueprint->GeneratedClass) { return Blueprint->GeneratedClass->GetDefaultObject(); } #endif return nullptr; } bool UMCPBlueprintManager::SetPropertyValue(UObject* Object, const FString& PropertyName, const FString& PropertyValue, const FString& PropertyType) const { #if WITH_EDITOR if (!Object) { return false; } // Find property by name const FProperty* Property = Object->GetClass()->FindPropertyByName(*PropertyName); if (!Property) { LogMessage(FString::Printf(TEXT("Property '%s' not found in class '%s'"), *PropertyName, *Object->GetClass()->GetName()), ELogVerbosity::Warning); return false; } // Convert string value to property data uint8* PropertyData = Property->ContainerPtrToValuePtr<uint8>(Object); return ConvertStringToPropertyValue(Property, PropertyValue, PropertyData); #else return false; #endif } bool UMCPBlueprintManager::ConvertStringToPropertyValue(const FProperty* Property, const FString& StringValue, void* OutData) const { #if WITH_EDITOR if (!Property || !OutData) { return false; } // Handle different property types if (const FIntProperty* IntProp = CastField<FIntProperty>(Property)) { int32 Value = FCString::Atoi(*StringValue); IntProp->SetPropertyValue(OutData, Value); return true; } else if (const FFloatProperty* FloatProp = CastField<FFloatProperty>(Property)) { float Value = FCString::Atof(*StringValue); FloatProp->SetPropertyValue(OutData, Value); return true; } else if (const FBoolProperty* BoolProp = CastField<FBoolProperty>(Property)) { bool Value = StringValue.ToBool(); BoolProp->SetPropertyValue(OutData, Value); return true; } else if (const FStrProperty* StrProp = CastField<FStrProperty>(Property)) { StrProp->SetPropertyValue(OutData, StringValue); return true; } else if (const FNameProperty* NameProp = CastField<FNameProperty>(Property)) { FName Value(*StringValue); NameProp->SetPropertyValue(OutData, Value); return true; } else if (const FStructProperty* StructProp = CastField<FStructProperty>(Property)) { // Handle common struct types if (StructProp->Struct == TBaseStructure<FVector>::Get()) { FVector Vector; if (Vector.InitFromString(StringValue)) { FVector* VectorPtr = StructProp->ContainerPtrToValuePtr<FVector>(OutData); if (VectorPtr) { *VectorPtr = Vector; } return true; } } else if (StructProp->Struct == TBaseStructure<FRotator>::Get()) { FRotator Rotator; if (Rotator.InitFromString(StringValue)) { FRotator* RotatorPtr = StructProp->ContainerPtrToValuePtr<FRotator>(OutData); if (RotatorPtr) { *RotatorPtr = Rotator; } return true; } } } LogMessage(FString::Printf(TEXT("Unsupported property type: %s"), *Property->GetClass()->GetName()), ELogVerbosity::Warning); return false; #else return false; #endif } // JSON helper implementations bool UMCPBlueprintManager::ParseCreateBlueprintJson(const FString& JsonCommand, FMCPBlueprintCreateParams& OutParams) const { TSharedPtr<FJsonObject> JsonObject; TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonCommand); if (!FJsonSerializer::Deserialize(Reader, JsonObject) || !JsonObject.IsValid()) { return false; } // Parse parameters OutParams.BlueprintName = JsonObject->GetStringField(TEXT("blueprint_name")); OutParams.ParentClassName = JsonObject->GetStringField(TEXT("parent_class")); FString AssetPath = JsonObject->GetStringField(TEXT("asset_path")); if (!AssetPath.IsEmpty()) { OutParams.AssetPath = AssetPath; } return true; } bool UMCPBlueprintManager::ParseSetPropertyJson(const FString& JsonCommand, FMCPBlueprintPropertyParams& OutParams) const { TSharedPtr<FJsonObject> JsonObject; TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonCommand); if (!FJsonSerializer::Deserialize(Reader, JsonObject) || !JsonObject.IsValid()) { return false; } // Parse parameters OutParams.BlueprintPath = JsonObject->GetStringField(TEXT("blueprint_path")); OutParams.PropertyName = JsonObject->GetStringField(TEXT("property_name")); OutParams.PropertyValue = JsonObject->GetStringField(TEXT("property_value")); OutParams.PropertyType = JsonObject->GetStringField(TEXT("property_type")); return true; } FString UMCPBlueprintManager::CreateJsonResponse(const FMCPBlueprintOperationResult& Result) const { TSharedPtr<FJsonObject> ResponseObject = MakeShareable(new FJsonObject); ResponseObject->SetBoolField(TEXT("success"), Result.bSuccess); ResponseObject->SetStringField(TEXT("error_message"), Result.ErrorMessage); ResponseObject->SetStringField(TEXT("blueprint_path"), Result.BlueprintPath); ResponseObject->SetStringField(TEXT("result_data"), Result.ResultData); ResponseObject->SetStringField(TEXT("timestamp"), FDateTime::Now().ToString()); FString OutputString; TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutputString); if (!FJsonSerializer::Serialize(ResponseObject.ToSharedRef(), Writer)) { UE_LOG(LogMCPBlueprintManager, Warning, TEXT("Failed to serialize JSON response")); } return OutputString; } // Validation helper implementations bool UMCPBlueprintManager::IsValidAssetPath(const FString& AssetPath) const { // Basic validation - should start with /Game/ if (!AssetPath.StartsWith(TEXT("/Game/"))) { return false; } // Check for invalid characters const FString InvalidChars = TEXT("<>:\"|?*"); for (const TCHAR& Char : InvalidChars) { if (AssetPath.Contains(FString::Chr(Char))) { return false; } } return true; } bool UMCPBlueprintManager::IsValidBlueprintName(const FString& BlueprintName) const { // Basic name validation if (BlueprintName.IsEmpty() || BlueprintName.Len() > 64) { return false; } // Should start with letter or underscore TCHAR FirstChar = BlueprintName[0]; if (!FChar::IsAlpha(FirstChar) && FirstChar != TEXT('_')) { return false; } // Should contain only alphanumeric characters and underscores for (const TCHAR& Char : BlueprintName) { if (!FChar::IsAlnum(Char) && Char != TEXT('_')) { return false; } } return true; } bool UMCPBlueprintManager::IsClassBluepritable(UClass* Class) const { #if WITH_EDITOR if (!Class) { return false; } // Check if class is blueprintable (not abstract, deprecated, or interface) return !Class->HasAnyClassFlags(CLASS_Abstract | CLASS_Deprecated | CLASS_Interface); #else return false; #endif } void UMCPBlueprintManager::LogMessage(const FString& Message, ELogVerbosity::Type Verbosity) const { // Use appropriate log level based on verbosity switch (Verbosity) { case ELogVerbosity::Error: UE_LOG(LogMCPBlueprintManager, Error, TEXT("[MCPBlueprintManager] %s"), *Message); break; case ELogVerbosity::Warning: UE_LOG(LogMCPBlueprintManager, Warning, TEXT("[MCPBlueprintManager] %s"), *Message); break; default: UE_LOG(LogMCPBlueprintManager, Log, TEXT("[MCPBlueprintManager] %s"), *Message); break; } } FString UMCPBlueprintManager::ProcessAddComponentCommand(const FString& JsonCommand) { TSharedPtr<FJsonObject> JsonObject; TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonCommand); if (!FJsonSerializer::Deserialize(Reader, JsonObject) || !JsonObject.IsValid()) { FMCPBlueprintOperationResult ErrorResult(false, TEXT("Failed to parse JSON command")); return CreateJsonResponse(ErrorResult); } // Parse parameters FString BlueprintPath = JsonObject->GetStringField(TEXT("blueprint_path")); FString ComponentType = JsonObject->GetStringField(TEXT("component_type")); FString ComponentName = JsonObject->GetStringField(TEXT("component_name")); // Execute component addition FMCPBlueprintOperationResult Result = AddComponentToBlueprint(BlueprintPath, ComponentType, ComponentName); // Return JSON response return CreateJsonResponse(Result); } FString UMCPBlueprintManager::ProcessCompileBlueprintCommand(const FString& JsonCommand) { TSharedPtr<FJsonObject> JsonObject; TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonCommand); if (!FJsonSerializer::Deserialize(Reader, JsonObject) || !JsonObject.IsValid()) { FMCPBlueprintOperationResult ErrorResult(false, TEXT("Failed to parse JSON command")); return CreateJsonResponse(ErrorResult); } // Parse parameters FString BlueprintPath = JsonObject->GetStringField(TEXT("blueprint_path")); // Execute blueprint compilation FMCPBlueprintOperationResult Result = CompileBlueprint(BlueprintPath); // Return JSON response return CreateJsonResponse(Result); } FString UMCPBlueprintManager::ProcessGetServerStatusCommand(const FString& JsonCommand) { // Return server status information return GetServerStatus(); } FMCPBlueprintOperationResult UMCPBlueprintManager::AddComponentToBlueprint(const FString& BlueprintPath, const FString& ComponentType, const FString& ComponentName) { #if WITH_EDITOR // Validate inputs if (BlueprintPath.IsEmpty() || ComponentType.IsEmpty() || ComponentName.IsEmpty()) { FString ErrorMessage = TEXT("Blueprint path, component type, and component name cannot be empty"); LogMessage(ErrorMessage, ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Load blueprint asset UBlueprint* Blueprint = LoadBlueprintAsset(BlueprintPath); if (!Blueprint) { FString ErrorMessage = FString::Printf(TEXT("Failed to load blueprint: %s"), *BlueprintPath); LogMessage(ErrorMessage, ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Find component class UClass* ComponentClass = FindObject<UClass>(ANY_PACKAGE, *ComponentType); if (!ComponentClass) { // Try with "Component" suffix if not found FString ComponentTypeWithSuffix = ComponentType + TEXT("Component"); ComponentClass = FindObject<UClass>(ANY_PACKAGE, *ComponentTypeWithSuffix); } if (!ComponentClass) { FString ErrorMessage = FString::Printf(TEXT("Component class not found: %s"), *ComponentType); LogMessage(ErrorMessage, ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Check if it's a valid component class if (!ComponentClass->IsChildOf(UActorComponent::StaticClass())) { FString ErrorMessage = FString::Printf(TEXT("Class %s is not a component class"), *ComponentType); LogMessage(ErrorMessage, ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Get or create the Simple Construction Script USimpleConstructionScript* SCS = Blueprint->SimpleConstructionScript; if (!SCS) { SCS = NewObject<USimpleConstructionScript>(Blueprint); Blueprint->SimpleConstructionScript = SCS; } // Create new SCS node for the component USCS_Node* NewNode = SCS->CreateNode(ComponentClass, FName(*ComponentName)); if (!NewNode) { FString ErrorMessage = FString::Printf(TEXT("Failed to create SCS node for component %s in blueprint %s"), *ComponentName, *BlueprintPath); LogMessage(ErrorMessage, ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Add the node to the construction script SCS->AddNode(NewNode); // Get the component template from the node UActorComponent* NewComponent = NewNode->ComponentTemplate; // Mark blueprint as modified Blueprint->MarkPackageDirty(); // Log success LogMessage(FString::Printf(TEXT("Successfully added component '%s' of type '%s' to blueprint: %s"), *ComponentName, *ComponentType, *BlueprintPath)); return FMCPBlueprintOperationResult(true, TEXT(""), BlueprintPath); #else LogMessage(TEXT("Component addition requires editor environment"), ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, TEXT("Editor environment required")); #endif } FMCPBlueprintOperationResult UMCPBlueprintManager::CompileBlueprint(const FString& BlueprintPath) { #if WITH_EDITOR // Validate input if (BlueprintPath.IsEmpty()) { FString ErrorMessage = TEXT("Blueprint path cannot be empty"); LogMessage(ErrorMessage, ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Load blueprint asset UBlueprint* Blueprint = LoadBlueprintAsset(BlueprintPath); if (!Blueprint) { FString ErrorMessage = FString::Printf(TEXT("Failed to load blueprint: %s"), *BlueprintPath); LogMessage(ErrorMessage, ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Compile the blueprint FKismetEditorUtilities::CompileBlueprint(Blueprint); // Check for compilation errors if (Blueprint->Status == BS_Error) { FString ErrorMessage = FString::Printf(TEXT("Blueprint compilation failed: %s"), *BlueprintPath); LogMessage(ErrorMessage, ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, ErrorMessage); } // Log success LogMessage(FString::Printf(TEXT("Successfully compiled blueprint: %s"), *BlueprintPath)); return FMCPBlueprintOperationResult(true, TEXT(""), BlueprintPath); #else LogMessage(TEXT("Blueprint compilation requires editor environment"), ELogVerbosity::Error); return FMCPBlueprintOperationResult(false, TEXT("Editor environment required")); #endif } FString UMCPBlueprintManager::GetServerStatus() const { TSharedPtr<FJsonObject> StatusObject = MakeShareable(new FJsonObject); // Basic server information StatusObject->SetBoolField(TEXT("online"), true); StatusObject->SetStringField(TEXT("version"), TEXT("1.0.0")); StatusObject->SetStringField(TEXT("plugin_name"), TEXT("UnrealBlueprintMCP")); StatusObject->SetStringField(TEXT("timestamp"), FDateTime::Now().ToString()); StatusObject->SetBoolField(TEXT("editor_available"), WITH_EDITOR ? true : false); StatusObject->SetBoolField(TEXT("initialized"), bIsInitialized); // Supported operations TArray<TSharedPtr<FJsonValue>> SupportedOperations; SupportedOperations.Add(MakeShareable(new FJsonValueString(TEXT("create_blueprint")))); SupportedOperations.Add(MakeShareable(new FJsonValueString(TEXT("set_property")))); SupportedOperations.Add(MakeShareable(new FJsonValueString(TEXT("add_component")))); SupportedOperations.Add(MakeShareable(new FJsonValueString(TEXT("compile_blueprint")))); SupportedOperations.Add(MakeShareable(new FJsonValueString(TEXT("get_server_status")))); StatusObject->SetArrayField(TEXT("supported_operations"), SupportedOperations); // Supported parent classes TArray<TSharedPtr<FJsonValue>> ParentClasses; for (const FString& ClassName : SupportedParentClasses) { ParentClasses.Add(MakeShareable(new FJsonValueString(ClassName))); } StatusObject->SetArrayField(TEXT("supported_parent_classes"), ParentClasses); // Serialize to string FString OutputString; TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutputString); if (!FJsonSerializer::Serialize(StatusObject.ToSharedRef(), Writer)) { UE_LOG(LogMCPBlueprintManager, Warning, TEXT("Failed to serialize server status JSON")); } return OutputString; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/BestDev/unreal_bp_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server