MCP Backup Restore and remove undo
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
#include "AssetRegistry/IAssetRegistry.h"
|
#include "AssetRegistry/IAssetRegistry.h"
|
||||||
#include "AssetToolsModule.h"
|
#include "AssetToolsModule.h"
|
||||||
#include "IAssetTools.h"
|
#include "IAssetTools.h"
|
||||||
|
#include "FileHelpers.h"
|
||||||
#include "MCPHandlers_AssetMutation.generated.h"
|
#include "MCPHandlers_AssetMutation.generated.h"
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -241,3 +242,97 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
UCLASS(meta=(ToolName="backup_asset"))
|
||||||
|
class UMCPHandler_BackupAsset : public UObject, public IMCPHandler
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UPROPERTY(meta=(Description="Full package path of the asset (e.g. /Game/Widgets/WB_Hotkeys)"))
|
||||||
|
FString AssetPath;
|
||||||
|
|
||||||
|
virtual FString GetDescription() const override
|
||||||
|
{
|
||||||
|
return TEXT("Copy an asset's .uasset file to a .uasset.bak backup.");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override
|
||||||
|
{
|
||||||
|
FString Filename = FPaths::ConvertRelativePathToFull(
|
||||||
|
FPackageName::LongPackageNameToFilename(AssetPath, FPackageName::GetAssetPackageExtension()));
|
||||||
|
|
||||||
|
if (!IFileManager::Get().FileExists(*Filename))
|
||||||
|
{
|
||||||
|
return MCPUtils::MakeErrorJson(Result, FString::Printf(TEXT("Asset file not found: %s"), *Filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
FString BackupFilename = Filename + TEXT(".bak");
|
||||||
|
uint32 CopyResult = IFileManager::Get().Copy(*BackupFilename, *Filename, true);
|
||||||
|
if (CopyResult != COPY_OK)
|
||||||
|
{
|
||||||
|
return MCPUtils::MakeErrorJson(Result, FString::Printf(TEXT("Failed to back up %s"), *Filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result->SetStringField(TEXT("backupFile"), BackupFilename);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
UCLASS(meta=(ToolName="restore_asset"))
|
||||||
|
class UMCPHandler_RestoreAsset : public UObject, public IMCPHandler
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UPROPERTY(meta=(Description="Full package path of the asset (e.g. /Game/Widgets/WB_Hotkeys)"))
|
||||||
|
FString AssetPath;
|
||||||
|
|
||||||
|
virtual FString GetDescription() const override
|
||||||
|
{
|
||||||
|
return TEXT("Restore a .uasset file from its .uasset.bak backup, reloading it in the editor.");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override
|
||||||
|
{
|
||||||
|
FString Filename = FPaths::ConvertRelativePathToFull(
|
||||||
|
FPackageName::LongPackageNameToFilename(AssetPath, FPackageName::GetAssetPackageExtension()));
|
||||||
|
FString BackupFilename = Filename + TEXT(".bak");
|
||||||
|
|
||||||
|
if (!IFileManager::Get().FileExists(*BackupFilename))
|
||||||
|
{
|
||||||
|
return MCPUtils::MakeErrorJson(Result, FString::Printf(TEXT("Backup file not found: %s"), *BackupFilename));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release file handles if the package is loaded
|
||||||
|
UPackage* Package = FindPackage(nullptr, *AssetPath);
|
||||||
|
if (Package)
|
||||||
|
{
|
||||||
|
ResetLoaders(Package);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy backup over the original
|
||||||
|
uint32 CopyResult = IFileManager::Get().Copy(*Filename, *BackupFilename, true);
|
||||||
|
if (CopyResult != COPY_OK)
|
||||||
|
{
|
||||||
|
return MCPUtils::MakeErrorJson(Result, FString::Printf(TEXT("Failed to restore %s"), *Filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload the package if it was loaded
|
||||||
|
if (Package)
|
||||||
|
{
|
||||||
|
bool bReloaded = false;
|
||||||
|
FText ErrorMessage;
|
||||||
|
UEditorLoadingAndSavingUtils::ReloadPackages({Package}, bReloaded, ErrorMessage, EReloadPackagesInteractionMode::AssumePositive);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result->SetStringField(TEXT("restoredFrom"), BackupFilename);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -253,23 +253,12 @@ void FMCPServer::DispatchToolCall(const FString& ToolName, const FJsonObject* Pa
|
|||||||
{
|
{
|
||||||
if (UClass** HandlerClass = MCPHandlerRegistry.Find(ToolName))
|
if (UClass** HandlerClass = MCPHandlerRegistry.Find(ToolName))
|
||||||
{
|
{
|
||||||
const bool bIsMutation = MutationEndpoints.Contains(ToolName);
|
|
||||||
if (bIsMutation && GEditor)
|
|
||||||
{
|
|
||||||
GEditor->BeginTransaction(FText::FromString(FString::Printf(TEXT("BlueprintMCP: %s"), *ToolName)));
|
|
||||||
}
|
|
||||||
|
|
||||||
TStrongObjectPtr<UObject> HandlerObj(NewObject<UObject>(GetTransientPackage(), *HandlerClass));
|
TStrongObjectPtr<UObject> HandlerObj(NewObject<UObject>(GetTransientPackage(), *HandlerClass));
|
||||||
IMCPHandler* Handler = Cast<IMCPHandler>(HandlerObj.Get());
|
IMCPHandler* Handler = Cast<IMCPHandler>(HandlerObj.Get());
|
||||||
if (MCPUtils::PopulateFromJson(HandlerObj->GetClass(), HandlerObj.Get(), Params, Result))
|
if (MCPUtils::PopulateFromJson(HandlerObj->GetClass(), HandlerObj.Get(), Params, Result))
|
||||||
{
|
{
|
||||||
Handler->Handle(Params, Result);
|
Handler->Handle(Params, Result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bIsMutation && GEditor)
|
|
||||||
{
|
|
||||||
GEditor->EndTransaction();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -316,7 +305,6 @@ bool FMCPServer::Start(int32 InPort, bool bEditorMode)
|
|||||||
|
|
||||||
// Register handlers
|
// Register handlers
|
||||||
BuildMCPHandlerRegistry();
|
BuildMCPHandlerRegistry();
|
||||||
RegisterHandlers();
|
|
||||||
|
|
||||||
// Create TCP listen socket
|
// Create TCP listen socket
|
||||||
ISocketSubsystem* SocketSub = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
|
ISocketSubsystem* SocketSub = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
|
||||||
@@ -535,65 +523,9 @@ bool FMCPServer::ProcessOneRequest()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// RegisterHandlers / BuildMCPHandlerRegistry
|
// BuildMCPHandlerRegistry
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
void FMCPServer::RegisterHandlers()
|
|
||||||
{
|
|
||||||
// Mutation endpoints — wrapped in undo transactions by DispatchToolCall()
|
|
||||||
MutationEndpoints = {
|
|
||||||
TEXT("replace_function_calls_in_blueprint"),
|
|
||||||
TEXT("change_blueprint_variable_type"),
|
|
||||||
TEXT("change_function_parameter_type"),
|
|
||||||
TEXT("remove_function_parameter"),
|
|
||||||
TEXT("delete_asset"),
|
|
||||||
TEXT("connect_blueprint_pins"),
|
|
||||||
TEXT("disconnect_blueprint_pins"),
|
|
||||||
TEXT("refresh_all_nodes_in_graph"),
|
|
||||||
TEXT("set_pin_default_values"),
|
|
||||||
TEXT("set_node_positions"),
|
|
||||||
TEXT("change_struct_node_type"),
|
|
||||||
TEXT("delete_node_from_graph"),
|
|
||||||
TEXT("duplicate_nodes_in_graph"),
|
|
||||||
TEXT("spawn_nodes_in_graph"),
|
|
||||||
TEXT("set_node_comment"),
|
|
||||||
TEXT("rename_asset"),
|
|
||||||
TEXT("reparent_blueprint"),
|
|
||||||
TEXT("set_class_default_value"),
|
|
||||||
TEXT("create_blueprint_asset"),
|
|
||||||
TEXT("create_blueprint_graph"),
|
|
||||||
TEXT("delete_blueprint_graph"),
|
|
||||||
TEXT("rename_blueprint_graph"),
|
|
||||||
TEXT("add_blueprint_variable"),
|
|
||||||
TEXT("remove_blueprint_variable"),
|
|
||||||
TEXT("set_blueprint_variable_metadata"),
|
|
||||||
TEXT("add_blueprint_interface"),
|
|
||||||
TEXT("remove_blueprint_interface"),
|
|
||||||
TEXT("add_event_dispatcher"),
|
|
||||||
TEXT("add_function_parameter"),
|
|
||||||
TEXT("add_blueprint_component"),
|
|
||||||
TEXT("remove_blueprint_component"),
|
|
||||||
TEXT("create_material_asset"),
|
|
||||||
TEXT("set_material_property"),
|
|
||||||
TEXT("add_material_expression"),
|
|
||||||
TEXT("delete_material_expression"),
|
|
||||||
TEXT("connect_material_expression_pins"),
|
|
||||||
TEXT("disconnect_material_expression_pin"),
|
|
||||||
TEXT("set_material_expression_property"),
|
|
||||||
TEXT("set_material_expression_position"),
|
|
||||||
TEXT("create_material_instance_asset"),
|
|
||||||
TEXT("set_material_instance_parameter"),
|
|
||||||
TEXT("reparent_material_instance"),
|
|
||||||
TEXT("create_material_function_asset"),
|
|
||||||
TEXT("add_anim_state_to_machine"),
|
|
||||||
TEXT("remove_anim_state_from_machine"),
|
|
||||||
TEXT("add_anim_state_transition"),
|
|
||||||
TEXT("set_anim_transition_rule"),
|
|
||||||
TEXT("set_anim_state_animation"),
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void FMCPServer::BuildMCPHandlerRegistry()
|
void FMCPServer::BuildMCPHandlerRegistry()
|
||||||
{
|
{
|
||||||
for (TObjectIterator<UClass> It; It; ++It)
|
for (TObjectIterator<UClass> It; It; ++It)
|
||||||
|
|||||||
@@ -51,8 +51,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
// ----- Tool dispatch -----
|
// ----- Tool dispatch -----
|
||||||
TMap<FString, UClass*> MCPHandlerRegistry; // tool name -> UMCPHandler subclass
|
TMap<FString, UClass*> MCPHandlerRegistry; // tool name -> UMCPHandler subclass
|
||||||
TSet<FString> MutationEndpoints;
|
|
||||||
void RegisterHandlers();
|
|
||||||
void BuildMCPHandlerRegistry();
|
void BuildMCPHandlerRegistry();
|
||||||
|
|
||||||
// Dispatch a tool call to the appropriate handler
|
// Dispatch a tool call to the appropriate handler
|
||||||
|
|||||||
Reference in New Issue
Block a user