Refactor MCPHandler to be a UINTERFACE

This commit is contained in:
2026-03-06 14:28:24 -05:00
parent 896df949f7
commit 7e3a4d2fea
6 changed files with 41 additions and 35 deletions

View File

@@ -862,8 +862,9 @@ bool FBlueprintMCPServer::ProcessOneRequest()
GEditor->BeginTransaction(FText::FromString(FString::Printf(TEXT("BlueprintMCP: %s"), *Req->Endpoint))); GEditor->BeginTransaction(FText::FromString(FString::Printf(TEXT("BlueprintMCP: %s"), *Req->Endpoint)));
} }
TStrongObjectPtr<UMCPHandler> Handler(NewObject<UMCPHandler>(GetTransientPackage(), *HandlerClass)); TStrongObjectPtr<UObject> HandlerObj(NewObject<UObject>(GetTransientPackage(), *HandlerClass));
FString PopulateError = PopulateFromJson(Handler->GetClass(), Handler.Get(), Params.Get()); IMCPHandler* Handler = Cast<IMCPHandler>(HandlerObj.Get());
FString PopulateError = PopulateFromJson(HandlerObj->GetClass(), HandlerObj.Get(), Params.Get());
if (PopulateError.IsEmpty()) if (PopulateError.IsEmpty())
{ {
Handler->Handle(Params.Get(), &*Result); Handler->Handle(Params.Get(), &*Result);
@@ -1057,11 +1058,13 @@ void FBlueprintMCPServer::RegisterHandlers()
void FBlueprintMCPServer::BuildMCPHandlerRegistry() void FBlueprintMCPServer::BuildMCPHandlerRegistry()
{ {
TArray<UClass*> HandlerClasses; for (TObjectIterator<UClass> It; It; ++It)
GetDerivedClasses(UMCPHandler::StaticClass(), HandlerClasses);
for (UClass* Class : HandlerClasses)
{ {
UClass* Class = *It;
if (!Class->ImplementsInterface(UMCPHandler::StaticClass()))
{
continue;
}
if (Class->HasAnyClassFlags(CLASS_Abstract)) if (Class->HasAnyClassFlags(CLASS_Abstract))
{ {
continue; continue;

View File

@@ -5,7 +5,7 @@
#include "BlueprintMCPHandlers_DiffBlueprints.generated.h" #include "BlueprintMCPHandlers_DiffBlueprints.generated.h"
UCLASS(meta=(ToolName="diff_blueprints")) UCLASS(meta=(ToolName="diff_blueprints"))
class UMCPHandler_DiffBlueprints : public UMCPHandler class UMCPHandler_DiffBlueprints : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()

View File

@@ -5,7 +5,7 @@
#include "BlueprintMCPHandlers_Interfaces.generated.h" #include "BlueprintMCPHandlers_Interfaces.generated.h"
UCLASS(meta=(ToolName="list_interfaces")) UCLASS(meta=(ToolName="list_interfaces"))
class UMCPHandler_ListInterfaces : public UMCPHandler class UMCPHandler_ListInterfaces : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -23,7 +23,7 @@ public:
}; };
UCLASS(meta=(ToolName="add_interface")) UCLASS(meta=(ToolName="add_interface"))
class UMCPHandler_AddInterface : public UMCPHandler class UMCPHandler_AddInterface : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -44,7 +44,7 @@ public:
}; };
UCLASS(meta=(ToolName="remove_interface")) UCLASS(meta=(ToolName="remove_interface"))
class UMCPHandler_RemoveInterface : public UMCPHandler class UMCPHandler_RemoveInterface : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()

View File

@@ -23,7 +23,7 @@ struct FSetPinDefaultEntry
}; };
UCLASS(meta=(ToolName="set_pin_default")) UCLASS(meta=(ToolName="set_pin_default"))
class UMCPHandler_SetPinDefault : public UMCPHandler class UMCPHandler_SetPinDefault : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -55,7 +55,7 @@ struct FMoveNodeEntry
}; };
UCLASS(meta=(ToolName="move_node")) UCLASS(meta=(ToolName="move_node"))
class UMCPHandler_MoveNode : public UMCPHandler class UMCPHandler_MoveNode : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -75,7 +75,7 @@ public:
}; };
UCLASS(meta=(ToolName="duplicate_nodes")) UCLASS(meta=(ToolName="duplicate_nodes"))
class UMCPHandler_DuplicateNodes : public UMCPHandler class UMCPHandler_DuplicateNodes : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -121,7 +121,7 @@ struct FSpawnNodeEntry
}; };
UCLASS(meta=(ToolName="spawn_node")) UCLASS(meta=(ToolName="spawn_node"))
class UMCPHandler_SpawnNode : public UMCPHandler class UMCPHandler_SpawnNode : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -146,7 +146,7 @@ public:
}; };
UCLASS(meta=(ToolName="get_node_comment")) UCLASS(meta=(ToolName="get_node_comment"))
class UMCPHandler_GetNodeComment : public UMCPHandler class UMCPHandler_GetNodeComment : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -166,7 +166,7 @@ public:
}; };
UCLASS(meta=(ToolName="set_node_comment")) UCLASS(meta=(ToolName="set_node_comment"))
class UMCPHandler_SetNodeComment : public UMCPHandler class UMCPHandler_SetNodeComment : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -189,7 +189,7 @@ public:
}; };
UCLASS(meta=(ToolName="delete_node")) UCLASS(meta=(ToolName="delete_node"))
class UMCPHandler_DeleteNode : public UMCPHandler class UMCPHandler_DeleteNode : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -228,7 +228,7 @@ struct FConnectPinsEntry
}; };
UCLASS(meta=(ToolName="connect_pins")) UCLASS(meta=(ToolName="connect_pins"))
class UMCPHandler_ConnectPins : public UMCPHandler class UMCPHandler_ConnectPins : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -266,7 +266,7 @@ struct FDisconnectPinEntry
}; };
UCLASS(meta=(ToolName="disconnect_pin")) UCLASS(meta=(ToolName="disconnect_pin"))
class UMCPHandler_DisconnectPin : public UMCPHandler class UMCPHandler_DisconnectPin : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -287,7 +287,7 @@ public:
}; };
UCLASS(meta=(ToolName="replace_function_calls")) UCLASS(meta=(ToolName="replace_function_calls"))
class UMCPHandler_ReplaceFunctionCalls : public UMCPHandler class UMCPHandler_ReplaceFunctionCalls : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -314,7 +314,7 @@ public:
}; };
UCLASS(meta=(ToolName="delete_asset")) UCLASS(meta=(ToolName="delete_asset"))
class UMCPHandler_DeleteAsset : public UMCPHandler class UMCPHandler_DeleteAsset : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -335,7 +335,7 @@ public:
}; };
UCLASS(meta=(ToolName="refresh_all_nodes")) UCLASS(meta=(ToolName="refresh_all_nodes"))
class UMCPHandler_RefreshAllNodes : public UMCPHandler class UMCPHandler_RefreshAllNodes : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -353,7 +353,7 @@ public:
}; };
UCLASS(meta=(ToolName="change_struct_node_type")) UCLASS(meta=(ToolName="change_struct_node_type"))
class UMCPHandler_ChangeStructNodeType : public UMCPHandler class UMCPHandler_ChangeStructNodeType : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -377,7 +377,7 @@ public:
}; };
UCLASS(meta=(ToolName="rename_asset")) UCLASS(meta=(ToolName="rename_asset"))
class UMCPHandler_RenameAsset : public UMCPHandler class UMCPHandler_RenameAsset : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -397,7 +397,7 @@ public:
}; };
UCLASS(meta=(ToolName="set_blueprint_default")) UCLASS(meta=(ToolName="set_blueprint_default"))
class UMCPHandler_SetBlueprintDefault : public UMCPHandler class UMCPHandler_SetBlueprintDefault : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -421,7 +421,7 @@ public:
}; };
UCLASS(meta=(ToolName="search_node_types")) UCLASS(meta=(ToolName="search_node_types"))
class UMCPHandler_SearchNodeTypes : public UMCPHandler class UMCPHandler_SearchNodeTypes : public UObject, public IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()

View File

@@ -14,7 +14,7 @@ class UMaterial;
class UMaterialInstanceConstant; class UMaterialInstanceConstant;
class UMaterialFunction; class UMaterialFunction;
class UMaterialExpression; class UMaterialExpression;
class UMCPHandler; class IMCPHandler;
// ----- Snapshot data structures ----- // ----- Snapshot data structures -----

View File

@@ -23,9 +23,9 @@ struct FMCPJsonArray
TArray<TSharedPtr<FJsonValue>> Array; TArray<TSharedPtr<FJsonValue>> Array;
}; };
// Base class for self-registering MCP tool handlers. // Interface for self-registering MCP tool handlers.
// //
// Subclasses declare their parameters as UPROPERTY fields, which are // Implementing classes declare their parameters as UPROPERTY fields, which are
// automatically populated from the incoming JSON request and used to // automatically populated from the incoming JSON request and used to
// generate the tool's JSON Schema for MCP tools/list. // generate the tool's JSON Schema for MCP tools/list.
// //
@@ -35,17 +35,20 @@ struct FMCPJsonArray
// Property metadata: // Property metadata:
// Optional - marks a parameter as not required // Optional - marks a parameter as not required
// //
UCLASS(Abstract) UINTERFACE(MinimalAPI, meta=(CannotImplementInterfaceInBlueprint))
class BLUEPRINTMCP_API UMCPHandler : public UObject class UMCPHandler : public UInterface
{
GENERATED_BODY()
};
class BLUEPRINTMCP_API IMCPHandler
{ {
GENERATED_BODY() GENERATED_BODY()
public: public:
// Human-readable tool description for MCP tools/list. // Human-readable tool description for MCP tools/list.
// Override in each subclass. virtual FString GetDescription() const = 0;
virtual FString GetDescription() const PURE_VIRTUAL(UMCPHandler::GetDescription, return FString(););
// Called after parameter fields have been populated from JSON. // Called after parameter fields have been populated from JSON.
// Override in each subclass. virtual void Handle(const FJsonObject* Json, FJsonObject* Result) = 0;
virtual void Handle(const FJsonObject* Json, FJsonObject* Result) PURE_VIRTUAL(UMCPHandler::Handle, );
}; };