diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPServer.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPServer.cpp index 1248795a..61fbfad8 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPServer.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BlueprintMCPServer.cpp @@ -862,8 +862,9 @@ bool FBlueprintMCPServer::ProcessOneRequest() GEditor->BeginTransaction(FText::FromString(FString::Printf(TEXT("BlueprintMCP: %s"), *Req->Endpoint))); } - TStrongObjectPtr Handler(NewObject(GetTransientPackage(), *HandlerClass)); - FString PopulateError = PopulateFromJson(Handler->GetClass(), Handler.Get(), Params.Get()); + TStrongObjectPtr HandlerObj(NewObject(GetTransientPackage(), *HandlerClass)); + IMCPHandler* Handler = Cast(HandlerObj.Get()); + FString PopulateError = PopulateFromJson(HandlerObj->GetClass(), HandlerObj.Get(), Params.Get()); if (PopulateError.IsEmpty()) { Handler->Handle(Params.Get(), &*Result); @@ -1057,11 +1058,13 @@ void FBlueprintMCPServer::RegisterHandlers() void FBlueprintMCPServer::BuildMCPHandlerRegistry() { - TArray HandlerClasses; - GetDerivedClasses(UMCPHandler::StaticClass(), HandlerClasses); - - for (UClass* Class : HandlerClasses) + for (TObjectIterator It; It; ++It) { + UClass* Class = *It; + if (!Class->ImplementsInterface(UMCPHandler::StaticClass())) + { + continue; + } if (Class->HasAnyClassFlags(CLASS_Abstract)) { continue; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_DiffBlueprints.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_DiffBlueprints.h index 6d4ee4da..987858dc 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_DiffBlueprints.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_DiffBlueprints.h @@ -5,7 +5,7 @@ #include "BlueprintMCPHandlers_DiffBlueprints.generated.h" UCLASS(meta=(ToolName="diff_blueprints")) -class UMCPHandler_DiffBlueprints : public UMCPHandler +class UMCPHandler_DiffBlueprints : public UObject, public IMCPHandler { GENERATED_BODY() diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Interfaces.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Interfaces.h index 89ee7c8d..5c197666 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Interfaces.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Interfaces.h @@ -5,7 +5,7 @@ #include "BlueprintMCPHandlers_Interfaces.generated.h" UCLASS(meta=(ToolName="list_interfaces")) -class UMCPHandler_ListInterfaces : public UMCPHandler +class UMCPHandler_ListInterfaces : public UObject, public IMCPHandler { GENERATED_BODY() @@ -23,7 +23,7 @@ public: }; UCLASS(meta=(ToolName="add_interface")) -class UMCPHandler_AddInterface : public UMCPHandler +class UMCPHandler_AddInterface : public UObject, public IMCPHandler { GENERATED_BODY() @@ -44,7 +44,7 @@ public: }; UCLASS(meta=(ToolName="remove_interface")) -class UMCPHandler_RemoveInterface : public UMCPHandler +class UMCPHandler_RemoveInterface : public UObject, public IMCPHandler { GENERATED_BODY() diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Mutation.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Mutation.h index b92c6e76..e13eb8a9 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Mutation.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPHandlers_Mutation.h @@ -23,7 +23,7 @@ struct FSetPinDefaultEntry }; UCLASS(meta=(ToolName="set_pin_default")) -class UMCPHandler_SetPinDefault : public UMCPHandler +class UMCPHandler_SetPinDefault : public UObject, public IMCPHandler { GENERATED_BODY() @@ -55,7 +55,7 @@ struct FMoveNodeEntry }; UCLASS(meta=(ToolName="move_node")) -class UMCPHandler_MoveNode : public UMCPHandler +class UMCPHandler_MoveNode : public UObject, public IMCPHandler { GENERATED_BODY() @@ -75,7 +75,7 @@ public: }; UCLASS(meta=(ToolName="duplicate_nodes")) -class UMCPHandler_DuplicateNodes : public UMCPHandler +class UMCPHandler_DuplicateNodes : public UObject, public IMCPHandler { GENERATED_BODY() @@ -121,7 +121,7 @@ struct FSpawnNodeEntry }; UCLASS(meta=(ToolName="spawn_node")) -class UMCPHandler_SpawnNode : public UMCPHandler +class UMCPHandler_SpawnNode : public UObject, public IMCPHandler { GENERATED_BODY() @@ -146,7 +146,7 @@ public: }; UCLASS(meta=(ToolName="get_node_comment")) -class UMCPHandler_GetNodeComment : public UMCPHandler +class UMCPHandler_GetNodeComment : public UObject, public IMCPHandler { GENERATED_BODY() @@ -166,7 +166,7 @@ public: }; UCLASS(meta=(ToolName="set_node_comment")) -class UMCPHandler_SetNodeComment : public UMCPHandler +class UMCPHandler_SetNodeComment : public UObject, public IMCPHandler { GENERATED_BODY() @@ -189,7 +189,7 @@ public: }; UCLASS(meta=(ToolName="delete_node")) -class UMCPHandler_DeleteNode : public UMCPHandler +class UMCPHandler_DeleteNode : public UObject, public IMCPHandler { GENERATED_BODY() @@ -228,7 +228,7 @@ struct FConnectPinsEntry }; UCLASS(meta=(ToolName="connect_pins")) -class UMCPHandler_ConnectPins : public UMCPHandler +class UMCPHandler_ConnectPins : public UObject, public IMCPHandler { GENERATED_BODY() @@ -266,7 +266,7 @@ struct FDisconnectPinEntry }; UCLASS(meta=(ToolName="disconnect_pin")) -class UMCPHandler_DisconnectPin : public UMCPHandler +class UMCPHandler_DisconnectPin : public UObject, public IMCPHandler { GENERATED_BODY() @@ -287,7 +287,7 @@ public: }; UCLASS(meta=(ToolName="replace_function_calls")) -class UMCPHandler_ReplaceFunctionCalls : public UMCPHandler +class UMCPHandler_ReplaceFunctionCalls : public UObject, public IMCPHandler { GENERATED_BODY() @@ -314,7 +314,7 @@ public: }; UCLASS(meta=(ToolName="delete_asset")) -class UMCPHandler_DeleteAsset : public UMCPHandler +class UMCPHandler_DeleteAsset : public UObject, public IMCPHandler { GENERATED_BODY() @@ -335,7 +335,7 @@ public: }; UCLASS(meta=(ToolName="refresh_all_nodes")) -class UMCPHandler_RefreshAllNodes : public UMCPHandler +class UMCPHandler_RefreshAllNodes : public UObject, public IMCPHandler { GENERATED_BODY() @@ -353,7 +353,7 @@ public: }; UCLASS(meta=(ToolName="change_struct_node_type")) -class UMCPHandler_ChangeStructNodeType : public UMCPHandler +class UMCPHandler_ChangeStructNodeType : public UObject, public IMCPHandler { GENERATED_BODY() @@ -377,7 +377,7 @@ public: }; UCLASS(meta=(ToolName="rename_asset")) -class UMCPHandler_RenameAsset : public UMCPHandler +class UMCPHandler_RenameAsset : public UObject, public IMCPHandler { GENERATED_BODY() @@ -397,7 +397,7 @@ public: }; UCLASS(meta=(ToolName="set_blueprint_default")) -class UMCPHandler_SetBlueprintDefault : public UMCPHandler +class UMCPHandler_SetBlueprintDefault : public UObject, public IMCPHandler { GENERATED_BODY() @@ -421,7 +421,7 @@ public: }; UCLASS(meta=(ToolName="search_node_types")) -class UMCPHandler_SearchNodeTypes : public UMCPHandler +class UMCPHandler_SearchNodeTypes : public UObject, public IMCPHandler { GENERATED_BODY() diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPServer.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPServer.h index 3594c393..5bab8836 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPServer.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BlueprintMCPServer.h @@ -14,7 +14,7 @@ class UMaterial; class UMaterialInstanceConstant; class UMaterialFunction; class UMaterialExpression; -class UMCPHandler; +class IMCPHandler; // ----- Snapshot data structures ----- diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPHandler.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPHandler.h index aecaed57..3082d942 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPHandler.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPHandler.h @@ -23,9 +23,9 @@ struct FMCPJsonArray TArray> 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 // generate the tool's JSON Schema for MCP tools/list. // @@ -35,17 +35,20 @@ struct FMCPJsonArray // Property metadata: // Optional - marks a parameter as not required // -UCLASS(Abstract) -class BLUEPRINTMCP_API UMCPHandler : public UObject +UINTERFACE(MinimalAPI, meta=(CannotImplementInterfaceInBlueprint)) +class UMCPHandler : public UInterface +{ + GENERATED_BODY() +}; + +class BLUEPRINTMCP_API IMCPHandler { GENERATED_BODY() public: // Human-readable tool description for MCP tools/list. - // Override in each subclass. - virtual FString GetDescription() const PURE_VIRTUAL(UMCPHandler::GetDescription, return FString();); + virtual FString GetDescription() const = 0; // Called after parameter fields have been populated from JSON. - // Override in each subclass. - virtual void Handle(const FJsonObject* Json, FJsonObject* Result) PURE_VIRTUAL(UMCPHandler::Handle, ); + virtual void Handle(const FJsonObject* Json, FJsonObject* Result) = 0; };