More work on MCP endpoints

This commit is contained in:
2026-03-06 04:18:16 -05:00
parent 7351756a71
commit 3ed7a33f69
5 changed files with 313 additions and 827 deletions

View File

@@ -367,23 +367,6 @@ static int32 TrySavePackageSEH(
}
}
static void RefreshAllNodesInner(UBlueprint* BP)
{
FBlueprintEditorUtils::RefreshAllNodes(BP);
}
int32 TryRefreshAllNodesSEH(UBlueprint* BP)
{
__try
{
RefreshAllNodesInner(BP);
return 0;
}
__except (1)
{
return -1;
}
}
// Inner: create expression, register in material, and trigger PostEditChange.
// All of this may crash for classes that are effectively abstract.
@@ -601,13 +584,13 @@ bool FBlueprintMCPServer::Start(int32 InPort, bool bEditorMode)
Router->BindRoute(FHttpPath(TEXT("/api/references")), EHttpServerRequestVerbs::VERB_GET,
QueuedHandler(TEXT("references")));
Router->BindRoute(FHttpPath(TEXT("/api/replace-function-calls")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("replaceFunctionCalls")));
QueuedHandler(TEXT("replace_function_calls")));
Router->BindRoute(FHttpPath(TEXT("/api/change-variable-type")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("changeVariableType")));
Router->BindRoute(FHttpPath(TEXT("/api/change-function-param-type")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("changeFunctionParamType")));
Router->BindRoute(FHttpPath(TEXT("/api/delete-asset")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("deleteAsset")));
QueuedHandler(TEXT("delete_asset")));
Router->BindRoute(FHttpPath(TEXT("/api/test-save")), EHttpServerRequestVerbs::VERB_GET,
QueuedHandler(TEXT("testSave")));
Router->BindRoute(FHttpPath(TEXT("/api/connect-pins")), EHttpServerRequestVerbs::VERB_POST,
@@ -615,7 +598,7 @@ bool FBlueprintMCPServer::Start(int32 InPort, bool bEditorMode)
Router->BindRoute(FHttpPath(TEXT("/api/disconnect-pin")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("disconnect_pin")));
Router->BindRoute(FHttpPath(TEXT("/api/refresh-all-nodes")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("refreshAllNodes")));
QueuedHandler(TEXT("refresh_all_nodes")));
Router->BindRoute(FHttpPath(TEXT("/api/set-pin-default")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("set_pin_default")));
Router->BindRoute(FHttpPath(TEXT("/api/move-node")), EHttpServerRequestVerbs::VERB_POST,
@@ -635,7 +618,7 @@ bool FBlueprintMCPServer::Start(int32 InPort, bool bEditorMode)
Router->BindRoute(FHttpPath(TEXT("/api/list-properties")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("listProperties")));
Router->BindRoute(FHttpPath(TEXT("/api/change-struct-node-type")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("changeStructNodeType")));
QueuedHandler(TEXT("change_struct_node_type")));
Router->BindRoute(FHttpPath(TEXT("/api/remove-function-parameter")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("removeFunctionParameter")));
Router->BindRoute(FHttpPath(TEXT("/api/delete-node")), EHttpServerRequestVerbs::VERB_POST,
@@ -648,18 +631,16 @@ bool FBlueprintMCPServer::Start(int32 InPort, bool bEditorMode)
QueuedHandler(TEXT("validateBlueprint")));
Router->BindRoute(FHttpPath(TEXT("/api/validate-all-blueprints")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("validateAllBlueprints")));
Router->BindRoute(FHttpPath(TEXT("/api/add-node")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("addNode")));
Router->BindRoute(FHttpPath(TEXT("/api/search-node-actions")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("searchNodeActions")));
Router->BindRoute(FHttpPath(TEXT("/api/search-node-actions")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("search_node_actions")));
Router->BindRoute(FHttpPath(TEXT("/api/spawn-node")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("spawn_node")));
Router->BindRoute(FHttpPath(TEXT("/api/rename-asset")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("renameAsset")));
QueuedHandler(TEXT("rename_asset")));
Router->BindRoute(FHttpPath(TEXT("/api/reparent-blueprint")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("reparentBlueprint")));
Router->BindRoute(FHttpPath(TEXT("/api/set-blueprint-default")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("setBlueprintDefault")));
QueuedHandler(TEXT("set_blueprint_default")));
Router->BindRoute(FHttpPath(TEXT("/api/create-blueprint")), EHttpServerRequestVerbs::VERB_POST,
QueuedHandler(TEXT("createBlueprint")));
Router->BindRoute(FHttpPath(TEXT("/api/create-graph")), EHttpServerRequestVerbs::VERB_POST,
@@ -977,25 +958,24 @@ void FBlueprintMCPServer::RegisterHandlers()
{
// Mutation endpoints — wrapped in undo transactions by ProcessOneRequest()
MutationEndpoints = {
TEXT("replaceFunctionCalls"),
TEXT("replace_function_calls"),
TEXT("changeVariableType"),
TEXT("changeFunctionParamType"),
TEXT("removeFunctionParameter"),
TEXT("deleteAsset"),
TEXT("delete_asset"),
TEXT("connect_pins"),
TEXT("disconnect_pin"),
TEXT("refreshAllNodes"),
TEXT("refresh_all_nodes"),
TEXT("set_pin_default"),
TEXT("move_node"),
TEXT("changeStructNodeType"),
TEXT("change_struct_node_type"),
TEXT("delete_node"),
TEXT("duplicate_nodes"),
TEXT("addNode"),
TEXT("spawn_node"),
TEXT("set_node_comment"),
TEXT("renameAsset"),
TEXT("rename_asset"),
TEXT("reparentBlueprint"),
TEXT("setBlueprintDefault"),
TEXT("set_blueprint_default"),
TEXT("createBlueprint"),
TEXT("createGraph"),
TEXT("deleteGraph"),
@@ -1050,14 +1030,14 @@ void FBlueprintMCPServer::RegisterHandlers()
H(TEXT("references"), &FBlueprintMCPServer::HandleFindReferences);
H(TEXT("testSave"), &FBlueprintMCPServer::HandleTestSave);
H(TEXT("searchByType"), &FBlueprintMCPServer::HandleSearchByType);
H(TEXT("replaceFunctionCalls"), &FBlueprintMCPServer::HandleReplaceFunctionCalls);
// replace_function_calls is now handled by UMCPHandler_ReplaceFunctionCalls (new-style registry)
H(TEXT("changeVariableType"), &FBlueprintMCPServer::HandleChangeVariableType);
H(TEXT("changeFunctionParamType"), &FBlueprintMCPServer::HandleChangeFunctionParamType);
H(TEXT("removeFunctionParameter"), &FBlueprintMCPServer::HandleRemoveFunctionParameter);
H(TEXT("deleteAsset"), &FBlueprintMCPServer::HandleDeleteAsset);
// delete_asset is now handled by UMCPHandler_DeleteAsset (new-style registry)
// connect_pins is now handled by UMCPHandler_ConnectPins (new-style registry)
// disconnect_pin is now handled by UMCPHandler_DisconnectPin (new-style registry)
H(TEXT("refreshAllNodes"), &FBlueprintMCPServer::HandleRefreshAllNodes);
// refresh_all_nodes is now handled by UMCPHandler_RefreshAllNodes (new-style registry)
// set_pin_default is now handled by UMCPHandler_SetPinDefault (new-style registry)
// move_node is now handled by UMCPHandler_MoveNode (new-style registry)
// get_node_comment is now handled by UMCPHandler_GetNodeComment (new-style registry)
@@ -1067,17 +1047,16 @@ void FBlueprintMCPServer::RegisterHandlers()
H(TEXT("listClasses"), &FBlueprintMCPServer::HandleListClasses);
H(TEXT("listFunctions"), &FBlueprintMCPServer::HandleListFunctions);
H(TEXT("listProperties"), &FBlueprintMCPServer::HandleListProperties);
H(TEXT("changeStructNodeType"), &FBlueprintMCPServer::HandleChangeStructNodeType);
// change_struct_node_type is now handled by UMCPHandler_ChangeStructNodeType (new-style registry)
// delete_node is now handled by UMCPHandler_DeleteNode (new-style registry)
// duplicate_nodes is now handled by UMCPHandler_DuplicateNodes (new-style registry)
H(TEXT("validateBlueprint"), &FBlueprintMCPServer::HandleValidateBlueprint);
H(TEXT("validateAllBlueprints"), &FBlueprintMCPServer::HandleValidateAllBlueprints);
H(TEXT("addNode"), &FBlueprintMCPServer::HandleAddNode);
H(TEXT("searchNodeActions"), &FBlueprintMCPServer::HandleSearchNodeActions);
// search_node_actions is now handled by UMCPHandler_SearchNodeActions (new-style registry)
// spawn_node is now handled by UMCPHandler_SpawnNode (new-style registry)
H(TEXT("renameAsset"), &FBlueprintMCPServer::HandleRenameAsset);
// rename_asset is now handled by UMCPHandler_RenameAsset (new-style registry)
H(TEXT("reparentBlueprint"), &FBlueprintMCPServer::HandleReparentBlueprint);
H(TEXT("setBlueprintDefault"), &FBlueprintMCPServer::HandleSetBlueprintDefault);
// set_blueprint_default is now handled by UMCPHandler_SetBlueprintDefault (new-style registry)
H(TEXT("createBlueprint"), &FBlueprintMCPServer::HandleCreateBlueprint);
H(TEXT("createGraph"), &FBlueprintMCPServer::HandleCreateGraph);
H(TEXT("deleteGraph"), &FBlueprintMCPServer::HandleDeleteGraph);

View File

@@ -285,3 +285,158 @@ public:
virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override;
};
UCLASS(meta=(ToolName="replace_function_calls"))
class UMCPHandler_ReplaceFunctionCalls : public UMCPHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Blueprint name or package path"))
FString Blueprint;
UPROPERTY(meta=(Description="Old class name to match"))
FString OldClass;
UPROPERTY(meta=(Description="New class name to redirect to"))
FString NewClass;
UPROPERTY(meta=(Optional, Description="If true, report what would change without modifying"))
bool DryRun = false;
virtual FString GetDescription() const override
{
return TEXT("Redirect function call nodes from one class to another. "
"Supports dry-run to preview impact before applying.");
}
virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override;
};
UCLASS(meta=(ToolName="delete_asset"))
class UMCPHandler_DeleteAsset : public UMCPHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Package path of the asset to delete"))
FString AssetPath;
UPROPERTY(meta=(Optional, Description="If true, skip reference check and force delete"))
bool Force = false;
virtual FString GetDescription() const override
{
return TEXT("Delete a .uasset after verifying no references. "
"Use force=true to skip the reference check.");
}
virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override;
};
UCLASS(meta=(ToolName="refresh_all_nodes"))
class UMCPHandler_RefreshAllNodes : public UMCPHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Blueprint name or package path"))
FString Blueprint;
virtual FString GetDescription() const override
{
return TEXT("Refresh all nodes in a Blueprint, removing orphaned pins. "
"Reports compiler warnings and errors.");
}
virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override;
};
UCLASS(meta=(ToolName="change_struct_node_type"))
class UMCPHandler_ChangeStructNodeType : public UMCPHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Blueprint name or package path"))
FString Blueprint;
UPROPERTY(meta=(Description="Node GUID of the BreakStruct or MakeStruct node"))
FString NodeId;
UPROPERTY(meta=(Description="New struct type name (e.g. 'FVector', 'Vector')"))
FString NewType;
virtual FString GetDescription() const override
{
return TEXT("Change the struct type on a BreakStruct or MakeStruct node. "
"Attempts to reconnect matching pins after the type change.");
}
virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override;
};
UCLASS(meta=(ToolName="rename_asset"))
class UMCPHandler_RenameAsset : public UMCPHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Current package path of the asset"))
FString AssetPath;
UPROPERTY(meta=(Description="New package path or new asset name"))
FString NewPath;
virtual FString GetDescription() const override
{
return TEXT("Rename or move an asset with reference fixup.");
}
virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override;
};
UCLASS(meta=(ToolName="set_blueprint_default"))
class UMCPHandler_SetBlueprintDefault : public UMCPHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Blueprint name or package path"))
FString Blueprint;
UPROPERTY(meta=(Description="Property name on the Class Default Object"))
FString Property;
UPROPERTY(meta=(Description="New value (parsed according to property type)"))
FString Value;
virtual FString GetDescription() const override
{
return TEXT("Set a default property value on a Blueprint's Class Default Object. "
"Handles class references, object references, and simple types.");
}
virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override;
};
UCLASS(meta=(ToolName="search_node_actions"))
class UMCPHandler_SearchNodeActions : public UMCPHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Search query string"))
FString Query;
UPROPERTY(meta=(Optional, Description="Maximum number of results (default 50, max 500)"))
int32 MaxResults = 50;
virtual FString GetDescription() const override
{
return TEXT("Search the Blueprint action database for node spawners matching a query. "
"Returns full action names for use with spawn_node.");
}
virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override;
};

View File

@@ -97,6 +97,13 @@ public:
/** Number of indexed Material Instance assets. */
int32 GetMaterialInstanceCount() const { return AllMaterialInstanceAssets.Num(); }
// ----- Cached asset lists (accessed by handlers) -----
TArray<FAssetData> AllBlueprintAssets;
TArray<FAssetData> AllMapAssets;
TArray<FAssetData> AllMaterialAssets;
TArray<FAssetData> AllMaterialInstanceAssets;
TArray<FAssetData> AllMaterialFunctionAssets;
private:
// ----- Request dispatch -----
using FRequestHandler = TFunction<void(const FJsonObject* Json, FJsonObject* Result)>;
@@ -115,11 +122,6 @@ private:
};
TQueue<TSharedPtr<FPendingRequest>> RequestQueue;
TArray<FAssetData> AllBlueprintAssets;
TArray<FAssetData> AllMapAssets;
TArray<FAssetData> AllMaterialAssets;
TArray<FAssetData> AllMaterialInstanceAssets;
TArray<FAssetData> AllMaterialFunctionAssets;
int32 Port = 9847;
bool bRunning = false;
bool bIsEditor = false;
@@ -136,21 +138,14 @@ private:
void HandleSearchByType(const FJsonObject* Json, FJsonObject* Result);
// ----- Request handlers (write) -----
void HandleReplaceFunctionCalls(const FJsonObject* Json, FJsonObject* Result);
void HandleChangeVariableType(const FJsonObject* Json, FJsonObject* Result);
void HandleChangeFunctionParamType(const FJsonObject* Json, FJsonObject* Result);
void HandleRemoveFunctionParameter(const FJsonObject* Json, FJsonObject* Result);
void HandleDeleteAsset(const FJsonObject* Json, FJsonObject* Result);
void HandleAddNode(const FJsonObject* Json, FJsonObject* Result);
void HandleRenameAsset(const FJsonObject* Json, FJsonObject* Result);
// ----- Validation (read-only, no save) -----
void HandleValidateBlueprint(const FJsonObject* Json, FJsonObject* Result);
void HandleValidateAllBlueprints(const FJsonObject* Json, FJsonObject* Result);
// ----- Pin manipulation (write) -----
void HandleRefreshAllNodes(const FJsonObject* Json, FJsonObject* Result);
// ----- Pin introspection (read-only) -----
void HandleGetPinInfo(const FJsonObject* Json, FJsonObject* Result);
void HandleCheckPinCompatibility(const FJsonObject* Json, FJsonObject* Result);
@@ -160,9 +155,6 @@ private:
void HandleListFunctions(const FJsonObject* Json, FJsonObject* Result);
void HandleListProperties(const FJsonObject* Json, FJsonObject* Result);
// ----- Struct node manipulation (write) -----
void HandleChangeStructNodeType(const FJsonObject* Json, FJsonObject* Result);
// ----- Reparent -----
void HandleReparentBlueprint(const FJsonObject* Json, FJsonObject* Result);
@@ -198,12 +190,6 @@ private:
void HandleRemoveComponent(const FJsonObject* Json, FJsonObject* Result);
void HandleListComponents(const FJsonObject* Json, FJsonObject* Result);
// ----- Property defaults -----
void HandleSetBlueprintDefault(const FJsonObject* Json, FJsonObject* Result);
// ----- Generic node spawning via action database -----
void HandleSearchNodeActions(const FJsonObject* Json, FJsonObject* Result);
// ----- Diagnostic -----
void HandleTestSave(const FJsonObject* Json, FJsonObject* Result);