Begining refactor of BlueprintMCP
This commit is contained in:
@@ -38,14 +38,8 @@
|
||||
// HandleCreateAnimBlueprint — create a new Animation Blueprint
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleCreateAnimBlueprint(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleCreateAnimBlueprint(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString Name = Json->GetStringField(TEXT("name"));
|
||||
FString PackagePath = Json->GetStringField(TEXT("packagePath"));
|
||||
FString SkeletonName = Json->GetStringField(TEXT("skeleton"));
|
||||
@@ -53,19 +47,19 @@ FString FBlueprintMCPServer::HandleCreateAnimBlueprint(const FString& Body)
|
||||
|
||||
if (Name.IsEmpty() || PackagePath.IsEmpty() || SkeletonName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: name, packagePath, skeleton"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: name, packagePath, skeleton"));
|
||||
}
|
||||
|
||||
if (!PackagePath.StartsWith(TEXT("/Game")))
|
||||
{
|
||||
return MakeErrorJson(TEXT("packagePath must start with '/Game'"));
|
||||
return MakeErrorJson(Result, TEXT("packagePath must start with '/Game'"));
|
||||
}
|
||||
|
||||
// Check if asset already exists
|
||||
FString FullAssetPath = PackagePath / Name;
|
||||
if (FindBlueprintAsset(Name) || FindBlueprintAsset(FullAssetPath))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Blueprint '%s' already exists. Use a different name or delete the existing asset first."),
|
||||
*Name));
|
||||
}
|
||||
@@ -120,7 +114,7 @@ FString FBlueprintMCPServer::HandleCreateAnimBlueprint(const FString& Body)
|
||||
|
||||
if (!Skeleton)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Skeleton '%s' not found. Provide the skeleton asset name or path. Use '__create_test_skeleton__' for testing."),
|
||||
*SkeletonName));
|
||||
}
|
||||
@@ -147,7 +141,7 @@ FString FBlueprintMCPServer::HandleCreateAnimBlueprint(const FString& Body)
|
||||
UPackage* Package = CreatePackage(*FullPackagePath);
|
||||
if (!Package)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Failed to create package at '%s'"), *FullPackagePath));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Failed to create package at '%s'"), *FullPackagePath));
|
||||
}
|
||||
|
||||
// Create the Animation Blueprint
|
||||
@@ -163,7 +157,7 @@ FString FBlueprintMCPServer::HandleCreateAnimBlueprint(const FString& Body)
|
||||
|
||||
if (!NewAnimBP)
|
||||
{
|
||||
return MakeErrorJson(TEXT("FKismetEditorUtilities::CreateBlueprint returned null for AnimBlueprint"));
|
||||
return MakeErrorJson(Result, TEXT("FKismetEditorUtilities::CreateBlueprint returned null for AnimBlueprint"));
|
||||
}
|
||||
|
||||
// Set target skeleton
|
||||
@@ -195,7 +189,6 @@ FString FBlueprintMCPServer::HandleCreateAnimBlueprint(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Created AnimBlueprint '%s' with %d graphs (saved: %s)"),
|
||||
*Name, GraphNames.Num(), bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprintName"), Name);
|
||||
Result->SetStringField(TEXT("packagePath"), PackagePath);
|
||||
@@ -205,7 +198,6 @@ FString FBlueprintMCPServer::HandleCreateAnimBlueprint(const FString& Body)
|
||||
Result->SetBoolField(TEXT("isAnimBlueprint"), true);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
Result->SetArrayField(TEXT("graphs"), GraphNames);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -267,43 +259,40 @@ static UAnimStateTransitionNode* FindTransition(UAnimationStateMachineGraph* SMG
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleAddAnimState(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleAddAnimState(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graph"));
|
||||
FString StateName = Json->GetStringField(TEXT("stateName"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || GraphName.IsEmpty() || StateName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, graph, stateName"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, graph, stateName"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
UAnimationStateMachineGraph* SMGraph = FindStateMachineGraph(BP, GraphName);
|
||||
if (!SMGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
}
|
||||
|
||||
// Check for duplicate state name
|
||||
if (FindStateByName(SMGraph, StateName))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State '%s' already exists in graph '%s'"), *StateName, *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State '%s' already exists in graph '%s'"), *StateName, *GraphName));
|
||||
}
|
||||
|
||||
// Get position
|
||||
@@ -365,52 +354,47 @@ FString FBlueprintMCPServer::HandleAddAnimState(const FString& Body)
|
||||
FKismetEditorUtilities::CompileBlueprint(AnimBP);
|
||||
bool bSaved = SaveBlueprintPackage(AnimBP);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("stateName"), StateName);
|
||||
Result->SetStringField(TEXT("graph"), GraphName);
|
||||
Result->SetStringField(TEXT("nodeId"), NewState->NodeGuid.ToString());
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleRemoveAnimState(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleRemoveAnimState(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graph"));
|
||||
FString StateName = Json->GetStringField(TEXT("stateName"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || GraphName.IsEmpty() || StateName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, graph, stateName"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, graph, stateName"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
UAnimationStateMachineGraph* SMGraph = FindStateMachineGraph(BP, GraphName);
|
||||
if (!SMGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
}
|
||||
|
||||
UAnimStateNode* StateNode = FindStateByName(SMGraph, StateName);
|
||||
if (!StateNode)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State '%s' not found in graph '%s'"), *StateName, *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State '%s' not found in graph '%s'"), *StateName, *GraphName));
|
||||
}
|
||||
|
||||
// Collect and remove transitions connected to this state
|
||||
@@ -441,22 +425,14 @@ FString FBlueprintMCPServer::HandleRemoveAnimState(const FString& Body)
|
||||
FKismetEditorUtilities::CompileBlueprint(AnimBP);
|
||||
bool bSaved = SaveBlueprintPackage(AnimBP);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("removedState"), StateName);
|
||||
Result->SetNumberField(TEXT("removedTransitions"), RemovedTransitions);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleAddAnimTransition(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleAddAnimTransition(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graph"));
|
||||
FString FromStateName = Json->GetStringField(TEXT("fromState"));
|
||||
@@ -464,35 +440,38 @@ FString FBlueprintMCPServer::HandleAddAnimTransition(const FString& Body)
|
||||
|
||||
if (BlueprintName.IsEmpty() || GraphName.IsEmpty() || FromStateName.IsEmpty() || ToStateName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, graph, fromState, toState"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, graph, fromState, toState"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
UAnimationStateMachineGraph* SMGraph = FindStateMachineGraph(BP, GraphName);
|
||||
if (!SMGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
}
|
||||
|
||||
UAnimStateNode* FromState = FindStateByName(SMGraph, FromStateName);
|
||||
if (!FromState)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("From state '%s' not found"), *FromStateName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("From state '%s' not found"), *FromStateName));
|
||||
}
|
||||
|
||||
UAnimStateNode* ToState = FindStateByName(SMGraph, ToStateName);
|
||||
if (!ToState)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("To state '%s' not found"), *ToStateName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("To state '%s' not found"), *ToStateName));
|
||||
}
|
||||
|
||||
// Create transition node
|
||||
@@ -529,7 +508,6 @@ FString FBlueprintMCPServer::HandleAddAnimTransition(const FString& Body)
|
||||
FKismetEditorUtilities::CompileBlueprint(AnimBP);
|
||||
bool bSaved = SaveBlueprintPackage(AnimBP);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("fromState"), FromStateName);
|
||||
Result->SetStringField(TEXT("toState"), ToStateName);
|
||||
@@ -538,17 +516,10 @@ FString FBlueprintMCPServer::HandleAddAnimTransition(const FString& Body)
|
||||
Result->SetNumberField(TEXT("priorityOrder"), TransNode->PriorityOrder);
|
||||
Result->SetBoolField(TEXT("bBidirectional"), TransNode->Bidirectional);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleSetTransitionRule(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleSetTransitionRule(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graph"));
|
||||
FString FromStateName = Json->GetStringField(TEXT("fromState"));
|
||||
@@ -556,29 +527,32 @@ FString FBlueprintMCPServer::HandleSetTransitionRule(const FString& Body)
|
||||
|
||||
if (BlueprintName.IsEmpty() || GraphName.IsEmpty() || FromStateName.IsEmpty() || ToStateName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, graph, fromState, toState"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, graph, fromState, toState"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
UAnimationStateMachineGraph* SMGraph = FindStateMachineGraph(BP, GraphName);
|
||||
if (!SMGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
}
|
||||
|
||||
UAnimStateTransitionNode* TransNode = FindTransition(SMGraph, FromStateName, ToStateName);
|
||||
if (!TransNode)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Transition from '%s' to '%s' not found in graph '%s'"),
|
||||
*FromStateName, *ToStateName, *GraphName));
|
||||
}
|
||||
@@ -614,14 +588,13 @@ FString FBlueprintMCPServer::HandleSetTransitionRule(const FString& Body)
|
||||
|
||||
if (ChangedCount == 0)
|
||||
{
|
||||
return MakeErrorJson(TEXT("No properties to update. Provide at least one of: crossfadeDuration, blendMode, priorityOrder, logicType, bBidirectional"));
|
||||
return MakeErrorJson(Result, TEXT("No properties to update. Provide at least one of: crossfadeDuration, blendMode, priorityOrder, logicType, bBidirectional"));
|
||||
}
|
||||
|
||||
// Compile and save
|
||||
FKismetEditorUtilities::CompileBlueprint(AnimBP);
|
||||
bool bSaved = SaveBlueprintPackage(AnimBP);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("fromState"), FromStateName);
|
||||
Result->SetStringField(TEXT("toState"), ToStateName);
|
||||
@@ -632,38 +605,34 @@ FString FBlueprintMCPServer::HandleSetTransitionRule(const FString& Body)
|
||||
Result->SetNumberField(TEXT("logicType"), (int32)TransNode->LogicType.GetValue());
|
||||
Result->SetBoolField(TEXT("bBidirectional"), TransNode->Bidirectional);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Tier 3: AnimGraph Blend Tree Mutation
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleAddAnimNode(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleAddAnimNode(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graph"));
|
||||
FString NodeType = Json->GetStringField(TEXT("nodeType"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || NodeType.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, nodeType"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, nodeType"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
// Find target graph (default to AnimGraph if not specified)
|
||||
@@ -686,7 +655,7 @@ FString FBlueprintMCPServer::HandleAddAnimNode(const FString& Body)
|
||||
|
||||
if (!TargetGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Graph '%s' not found"), *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Graph '%s' not found"), *GraphName));
|
||||
}
|
||||
|
||||
int32 PosX = Json->HasField(TEXT("posX")) ? (int32)Json->GetNumberField(TEXT("posX")) : 0;
|
||||
@@ -766,14 +735,14 @@ FString FBlueprintMCPServer::HandleAddAnimNode(const FString& Body)
|
||||
}
|
||||
else
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Unsupported nodeType '%s'. Supported: SequencePlayer, BlendSpacePlayer, StateMachine"),
|
||||
*NodeType));
|
||||
}
|
||||
|
||||
if (!NewNode)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Failed to create anim node"));
|
||||
return MakeErrorJson(Result, TEXT("Failed to create anim node"));
|
||||
}
|
||||
|
||||
NewNode->NodePosX = PosX;
|
||||
@@ -785,7 +754,6 @@ FString FBlueprintMCPServer::HandleAddAnimNode(const FString& Body)
|
||||
FKismetEditorUtilities::CompileBlueprint(AnimBP);
|
||||
bool bSaved = SaveBlueprintPackage(AnimBP);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("nodeType"), NodeType);
|
||||
Result->SetStringField(TEXT("nodeId"), NewNode->NodeGuid.ToString());
|
||||
@@ -803,28 +771,20 @@ FString FBlueprintMCPServer::HandleAddAnimNode(const FString& Body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Tier 4: Advanced Operations
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleAddStateMachine(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleAddStateMachine(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString MachineName = Json->GetStringField(TEXT("name"));
|
||||
|
||||
if (BlueprintName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: blueprint"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: blueprint"));
|
||||
}
|
||||
|
||||
// Default name
|
||||
@@ -843,21 +803,11 @@ FString FBlueprintMCPServer::HandleAddStateMachine(const FString& Body)
|
||||
if (Json->HasField(TEXT("posY")))
|
||||
ForwardJson->SetNumberField(TEXT("posY"), Json->GetNumberField(TEXT("posY")));
|
||||
|
||||
FString ForwardBody;
|
||||
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&ForwardBody);
|
||||
FJsonSerializer::Serialize(ForwardJson, Writer);
|
||||
|
||||
return HandleAddAnimNode(ForwardBody);
|
||||
HandleAddAnimNode(&*ForwardJson, Result);
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleSetStateAnimation(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleSetStateAnimation(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graph"));
|
||||
FString StateName = Json->GetStringField(TEXT("stateName"));
|
||||
@@ -865,35 +815,38 @@ FString FBlueprintMCPServer::HandleSetStateAnimation(const FString& Body)
|
||||
|
||||
if (BlueprintName.IsEmpty() || GraphName.IsEmpty() || StateName.IsEmpty() || AnimAssetName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, graph, stateName, animationAsset"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, graph, stateName, animationAsset"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
UAnimationStateMachineGraph* SMGraph = FindStateMachineGraph(BP, GraphName);
|
||||
if (!SMGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
}
|
||||
|
||||
UAnimStateNode* StateNode = FindStateByName(SMGraph, StateName);
|
||||
if (!StateNode)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State '%s' not found in graph '%s'"), *StateName, *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State '%s' not found in graph '%s'"), *StateName, *GraphName));
|
||||
}
|
||||
|
||||
UEdGraph* InnerGraph = StateNode->GetBoundGraph();
|
||||
if (!InnerGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State '%s' has no bound graph"), *StateName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State '%s' has no bound graph"), *StateName));
|
||||
}
|
||||
|
||||
// Find the animation asset
|
||||
@@ -913,7 +866,7 @@ FString FBlueprintMCPServer::HandleSetStateAnimation(const FString& Body)
|
||||
|
||||
if (!AnimSeq)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Animation asset '%s' not found"), *AnimAssetName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Animation asset '%s' not found"), *AnimAssetName));
|
||||
}
|
||||
|
||||
// Find existing SequencePlayer or create one
|
||||
@@ -943,37 +896,32 @@ FString FBlueprintMCPServer::HandleSetStateAnimation(const FString& Body)
|
||||
FKismetEditorUtilities::CompileBlueprint(AnimBP);
|
||||
bool bSaved = SaveBlueprintPackage(AnimBP);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("stateName"), StateName);
|
||||
Result->SetStringField(TEXT("animationAsset"), AnimSeq->GetName());
|
||||
Result->SetBoolField(TEXT("createdNewNode"), bCreatedNew);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleListAnimSlots(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleListAnimSlots(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
if (BlueprintName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: blueprint"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: blueprint"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
// Walk all anim nodes to collect slot names
|
||||
@@ -1008,36 +956,31 @@ FString FBlueprintMCPServer::HandleListAnimSlots(const FString& Body)
|
||||
SlotsArr.Add(MakeShared<FJsonValueString>(Slot));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetArrayField(TEXT("slots"), SlotsArr);
|
||||
Result->SetNumberField(TEXT("count"), SlotsArr.Num());
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleListSyncGroups(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleListSyncGroups(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
if (BlueprintName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: blueprint"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: blueprint"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
// Walk all anim nodes to collect sync group names
|
||||
@@ -1072,38 +1015,30 @@ FString FBlueprintMCPServer::HandleListSyncGroups(const FString& Body)
|
||||
GroupsArr.Add(MakeShared<FJsonValueString>(Group));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetArrayField(TEXT("syncGroups"), GroupsArr);
|
||||
Result->SetNumberField(TEXT("count"), GroupsArr.Num());
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleCreateBlendSpace — create a new 2D Blend Space asset
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleCreateBlendSpace(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleCreateBlendSpace(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString Name = Json->GetStringField(TEXT("name"));
|
||||
FString PackagePath = Json->GetStringField(TEXT("packagePath"));
|
||||
FString SkeletonName = Json->GetStringField(TEXT("skeleton"));
|
||||
|
||||
if (Name.IsEmpty() || PackagePath.IsEmpty() || SkeletonName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: name, packagePath, skeleton"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: name, packagePath, skeleton"));
|
||||
}
|
||||
|
||||
if (!PackagePath.StartsWith(TEXT("/Game")))
|
||||
{
|
||||
return MakeErrorJson(TEXT("packagePath must start with '/Game'"));
|
||||
return MakeErrorJson(Result, TEXT("packagePath must start with '/Game'"));
|
||||
}
|
||||
|
||||
// Check if asset already exists
|
||||
@@ -1116,7 +1051,7 @@ FString FBlueprintMCPServer::HandleCreateBlendSpace(const FString& Body)
|
||||
{
|
||||
if (Asset.AssetName.ToString() == Name || Asset.GetObjectPathString() == FullAssetPath)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Blend Space '%s' already exists. Use a different name or delete the existing asset first."),
|
||||
*Name));
|
||||
}
|
||||
@@ -1171,7 +1106,7 @@ FString FBlueprintMCPServer::HandleCreateBlendSpace(const FString& Body)
|
||||
|
||||
if (!Skeleton)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Skeleton '%s' not found. Provide the skeleton asset name or path. Use '__create_test_skeleton__' for testing."),
|
||||
*SkeletonName));
|
||||
}
|
||||
@@ -1184,14 +1119,14 @@ FString FBlueprintMCPServer::HandleCreateBlendSpace(const FString& Body)
|
||||
UPackage* Package = CreatePackage(*FullPackagePath);
|
||||
if (!Package)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Failed to create package at '%s'"), *FullPackagePath));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Failed to create package at '%s'"), *FullPackagePath));
|
||||
}
|
||||
|
||||
// Create the Blend Space
|
||||
UBlendSpace* NewBS = NewObject<UBlendSpace>(Package, FName(*Name), RF_Public | RF_Standalone);
|
||||
if (!NewBS)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Failed to create Blend Space object"));
|
||||
return MakeErrorJson(Result, TEXT("Failed to create Blend Space object"));
|
||||
}
|
||||
|
||||
// Set skeleton
|
||||
@@ -1204,30 +1139,22 @@ FString FBlueprintMCPServer::HandleCreateBlendSpace(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Created Blend Space '%s' (saved: %s)"),
|
||||
*Name, bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("assetPath"), FullAssetPath);
|
||||
Result->SetStringField(TEXT("skeleton"), Skeleton->GetName());
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleSetBlendSpaceSamples — add animation samples to a Blend Space
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleSetBlendSpaceSamples(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleSetBlendSpaceSamples(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlendSpaceName = Json->GetStringField(TEXT("blendSpace"));
|
||||
if (BlendSpaceName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: blendSpace"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: blendSpace"));
|
||||
}
|
||||
|
||||
// Load the blend space
|
||||
@@ -1263,7 +1190,7 @@ FString FBlueprintMCPServer::HandleSetBlendSpaceSamples(const FString& Body)
|
||||
|
||||
if (!BS)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Blend Space '%s' not found"), *BlendSpaceName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Blend Space '%s' not found"), *BlendSpaceName));
|
||||
}
|
||||
|
||||
// Set axis parameters
|
||||
@@ -1366,26 +1293,18 @@ FString FBlueprintMCPServer::HandleSetBlendSpaceSamples(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Set %d samples on Blend Space '%s' (saved: %s)"),
|
||||
SamplesSet, *BS->GetName(), bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blendSpace"), BS->GetPathName());
|
||||
Result->SetNumberField(TEXT("samplesSet"), SamplesSet);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleSetStateBlendSpace — place a BlendSpacePlayer in a state
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleSetStateBlendSpace(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleSetStateBlendSpace(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graph"));
|
||||
FString StateName = Json->GetStringField(TEXT("stateName"));
|
||||
@@ -1395,35 +1314,38 @@ FString FBlueprintMCPServer::HandleSetStateBlendSpace(const FString& Body)
|
||||
|
||||
if (BlueprintName.IsEmpty() || GraphName.IsEmpty() || StateName.IsEmpty() || BlendSpaceName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, graph, stateName, blendSpace"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, graph, stateName, blendSpace"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
UAnimationStateMachineGraph* SMGraph = FindStateMachineGraph(BP, GraphName);
|
||||
if (!SMGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
}
|
||||
|
||||
UAnimStateNode* StateNode = FindStateByName(SMGraph, StateName);
|
||||
if (!StateNode)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State '%s' not found in graph '%s'"), *StateName, *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State '%s' not found in graph '%s'"), *StateName, *GraphName));
|
||||
}
|
||||
|
||||
UEdGraph* InnerGraph = StateNode->GetBoundGraph();
|
||||
if (!InnerGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State '%s' has no bound graph"), *StateName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State '%s' has no bound graph"), *StateName));
|
||||
}
|
||||
|
||||
// Find the blend space asset
|
||||
@@ -1459,7 +1381,7 @@ FString FBlueprintMCPServer::HandleSetStateBlendSpace(const FString& Body)
|
||||
|
||||
if (!BlendSpaceAsset)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Blend Space '%s' not found"), *BlendSpaceName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Blend Space '%s' not found"), *BlendSpaceName));
|
||||
}
|
||||
|
||||
// Find existing BlendSpacePlayer or create one
|
||||
@@ -1609,11 +1531,9 @@ FString FBlueprintMCPServer::HandleSetStateBlendSpace(const FString& Body)
|
||||
FKismetEditorUtilities::CompileBlueprint(AnimBP);
|
||||
bool bSaved = SaveBlueprintPackage(AnimBP);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("stateName"), StateName);
|
||||
Result->SetStringField(TEXT("blendSpace"), BlendSpaceAsset->GetName());
|
||||
Result->SetStringField(TEXT("nodeId"), BSNode->NodeGuid.ToString());
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,31 +13,25 @@
|
||||
// HandleListComponents — list all components in a Blueprint's SCS
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleListComponents(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleListComponents(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
if (BlueprintName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: blueprint"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: blueprint"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
USimpleConstructionScript* SCS = BP->SimpleConstructionScript;
|
||||
if (!SCS)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Blueprint '%s' does not have a SimpleConstructionScript (not an Actor Blueprint)"),
|
||||
*BlueprintName));
|
||||
}
|
||||
@@ -95,32 +89,24 @@ FString FBlueprintMCPServer::HandleListComponents(const FString& Body)
|
||||
ComponentsArr.Add(MakeShared<FJsonValueObject>(CompObj));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetNumberField(TEXT("count"), ComponentsArr.Num());
|
||||
Result->SetArrayField(TEXT("components"), ComponentsArr);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleAddComponent — add a component to a Blueprint's SCS
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleAddComponent(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleAddComponent(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString ComponentClassName = Json->GetStringField(TEXT("componentClass"));
|
||||
FString ComponentName = Json->GetStringField(TEXT("name"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || ComponentClassName.IsEmpty() || ComponentName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, componentClass, name"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, componentClass, name"));
|
||||
}
|
||||
|
||||
FString ParentComponentName;
|
||||
@@ -133,13 +119,13 @@ FString FBlueprintMCPServer::HandleAddComponent(const FString& Body)
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
USimpleConstructionScript* SCS = BP->SimpleConstructionScript;
|
||||
if (!SCS)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Blueprint '%s' does not have a SimpleConstructionScript (not an Actor Blueprint)"),
|
||||
*BlueprintName));
|
||||
}
|
||||
@@ -150,7 +136,7 @@ FString FBlueprintMCPServer::HandleAddComponent(const FString& Body)
|
||||
{
|
||||
if (Existing && Existing->GetVariableName().ToString().Equals(ComponentName, ESearchCase::IgnoreCase))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("A component named '%s' already exists in Blueprint '%s'"),
|
||||
*ComponentName, *BlueprintName));
|
||||
}
|
||||
@@ -197,7 +183,7 @@ FString FBlueprintMCPServer::HandleAddComponent(const FString& Body)
|
||||
|
||||
if (!ComponentClass)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Component class '%s' not found or is not a subclass of UActorComponent. "
|
||||
"Common classes: StaticMeshComponent, SkeletalMeshComponent, AudioComponent, "
|
||||
"SceneComponent, BoxCollisionComponent, SphereCollisionComponent, CapsuleComponent, "
|
||||
@@ -221,7 +207,7 @@ FString FBlueprintMCPServer::HandleAddComponent(const FString& Body)
|
||||
|
||||
if (!ParentSCSNode)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Parent component '%s' not found in Blueprint '%s'"),
|
||||
*ParentComponentName, *BlueprintName));
|
||||
}
|
||||
@@ -234,7 +220,7 @@ FString FBlueprintMCPServer::HandleAddComponent(const FString& Body)
|
||||
USCS_Node* NewNode = SCS->CreateNode(ComponentClass, FName(*ComponentName));
|
||||
if (!NewNode)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Failed to create SCS node for component '%s' with class '%s'"),
|
||||
*ComponentName, *ComponentClass->GetName()));
|
||||
}
|
||||
@@ -257,7 +243,6 @@ FString FBlueprintMCPServer::HandleAddComponent(const FString& Body)
|
||||
ParentSCSNode ? *ParentComponentName : TEXT("(root)"),
|
||||
bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("name"), NewNode->GetVariableName().ToString());
|
||||
@@ -267,40 +252,33 @@ FString FBlueprintMCPServer::HandleAddComponent(const FString& Body)
|
||||
Result->SetStringField(TEXT("parentComponent"), ParentSCSNode->GetVariableName().ToString());
|
||||
}
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleRemoveComponent — remove a component from a Blueprint's SCS
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleRemoveComponent(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleRemoveComponent(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString ComponentName = Json->GetStringField(TEXT("name"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || ComponentName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, name"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, name"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
USimpleConstructionScript* SCS = BP->SimpleConstructionScript;
|
||||
if (!SCS)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Blueprint '%s' does not have a SimpleConstructionScript (not an Actor Blueprint)"),
|
||||
*BlueprintName));
|
||||
}
|
||||
@@ -329,19 +307,18 @@ FString FBlueprintMCPServer::HandleRemoveComponent(const FString& Body)
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> ErrorResult = MakeShared<FJsonObject>();
|
||||
ErrorResult->SetStringField(TEXT("error"), FString::Printf(
|
||||
MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Component '%s' not found in Blueprint '%s'"),
|
||||
*ComponentName, *BlueprintName));
|
||||
ErrorResult->SetArrayField(TEXT("existingComponents"), CompList);
|
||||
return JsonToString(ErrorResult);
|
||||
Result->SetArrayField(TEXT("existingComponents"), CompList);
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent removing the root scene component if it has children
|
||||
const TArray<USCS_Node*>& RootNodes = SCS->GetRootNodes();
|
||||
if (RootNodes.Contains(NodeToRemove) && NodeToRemove->GetChildNodes().Num() > 0)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Cannot remove component '%s' because it is a root component with %d child(ren). "
|
||||
"Remove or re-parent the children first."),
|
||||
*ComponentName, NodeToRemove->GetChildNodes().Num()));
|
||||
@@ -359,10 +336,8 @@ FString FBlueprintMCPServer::HandleRemoveComponent(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Removed component '%s' from '%s' (saved: %s)"),
|
||||
*ComponentName, *BlueprintName, bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("name"), ComponentName);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
@@ -17,30 +17,24 @@
|
||||
// HandleDiffBlueprints — structural diff between two Blueprints
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleDiffBlueprints(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleDiffBlueprints(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintA = Json->GetStringField(TEXT("blueprintA"));
|
||||
FString BlueprintB = Json->GetStringField(TEXT("blueprintB"));
|
||||
FString GraphFilter = Json->GetStringField(TEXT("graph"));
|
||||
|
||||
if (BlueprintA.IsEmpty() || BlueprintB.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprintA, blueprintB"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprintA, blueprintB"));
|
||||
}
|
||||
|
||||
// Load both blueprints
|
||||
FString LoadErrorA, LoadErrorB;
|
||||
UBlueprint* BPA = LoadBlueprintByName(BlueprintA, LoadErrorA);
|
||||
if (!BPA) return MakeErrorJson(FString::Printf(TEXT("blueprintA: %s"), *LoadErrorA));
|
||||
if (!BPA) { MakeErrorJson(Result, FString::Printf(TEXT("blueprintA: %s"), *LoadErrorA)); return; }
|
||||
|
||||
UBlueprint* BPB = LoadBlueprintByName(BlueprintB, LoadErrorB);
|
||||
if (!BPB) return MakeErrorJson(FString::Printf(TEXT("blueprintB: %s"), *LoadErrorB));
|
||||
if (!BPB) { MakeErrorJson(Result, FString::Printf(TEXT("blueprintB: %s"), *LoadErrorB)); return; }
|
||||
|
||||
// Helper to gather graphs from a Blueprint
|
||||
auto GatherGraphs = [&GraphFilter](UBlueprint* BP) -> TArray<UEdGraph*>
|
||||
@@ -245,7 +239,6 @@ FString FBlueprintMCPServer::HandleDiffBlueprints(const FString& Body)
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprintA"), BlueprintA);
|
||||
Result->SetStringField(TEXT("blueprintB"), BlueprintB);
|
||||
@@ -264,6 +257,4 @@ FString FBlueprintMCPServer::HandleDiffBlueprints(const FString& Body)
|
||||
}
|
||||
TotalDiffs += VarsOnlyInA.Num() + VarsOnlyInB.Num();
|
||||
Result->SetNumberField(TEXT("totalDifferences"), TotalDiffs);
|
||||
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
@@ -15,35 +15,29 @@
|
||||
// HandleGetPinInfo — detailed information about a specific pin
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleGetPinInfo(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleGetPinInfo(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString NodeId = Json->GetStringField(TEXT("nodeId"));
|
||||
FString PinName = Json->GetStringField(TEXT("pinName"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || NodeId.IsEmpty() || PinName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, nodeId, pinName"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, nodeId, pinName"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UEdGraph* Graph = nullptr;
|
||||
UEdGraphNode* Node = FindNodeByGuid(BP, NodeId, &Graph);
|
||||
if (!Node)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Node '%s' not found"), *NodeId));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Node '%s' not found"), *NodeId));
|
||||
}
|
||||
|
||||
UEdGraphPin* Pin = Node->FindPin(FName(*PinName));
|
||||
@@ -62,13 +56,11 @@ FString FBlueprintMCPServer::HandleGetPinInfo(const FString& Body)
|
||||
AvailPins.Add(MakeShared<FJsonValueObject>(PinObj));
|
||||
}
|
||||
}
|
||||
TSharedRef<FJsonObject> E = MakeShared<FJsonObject>();
|
||||
E->SetStringField(TEXT("error"), FString::Printf(TEXT("Pin '%s' not found on node '%s'"), *PinName, *NodeId));
|
||||
E->SetArrayField(TEXT("availablePins"), AvailPins);
|
||||
return JsonToString(E);
|
||||
MakeErrorJson(Result, FString::Printf(TEXT("Pin '%s' not found on node '%s'"), *PinName, *NodeId));
|
||||
Result->SetArrayField(TEXT("availablePins"), AvailPins);
|
||||
return;
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("nodeId"), NodeId);
|
||||
@@ -119,22 +111,14 @@ FString FBlueprintMCPServer::HandleGetPinInfo(const FString& Body)
|
||||
}
|
||||
Result->SetArrayField(TEXT("connectedTo"), Conns);
|
||||
}
|
||||
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleCheckPinCompatibility — pre-flight check for connect_pins
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleCheckPinCompatibility(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleCheckPinCompatibility(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString SourceNodeId = Json->GetStringField(TEXT("sourceNodeId"));
|
||||
FString SourcePinName = Json->GetStringField(TEXT("sourcePinName"));
|
||||
@@ -144,51 +128,50 @@ FString FBlueprintMCPServer::HandleCheckPinCompatibility(const FString& Body)
|
||||
if (BlueprintName.IsEmpty() || SourceNodeId.IsEmpty() || SourcePinName.IsEmpty() ||
|
||||
TargetNodeId.IsEmpty() || TargetPinName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, sourceNodeId, sourcePinName, targetNodeId, targetPinName"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, sourceNodeId, sourcePinName, targetNodeId, targetPinName"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UEdGraph* SourceGraph = nullptr;
|
||||
UEdGraphNode* SourceNode = FindNodeByGuid(BP, SourceNodeId, &SourceGraph);
|
||||
if (!SourceNode)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Source node '%s' not found"), *SourceNodeId));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Source node '%s' not found"), *SourceNodeId));
|
||||
}
|
||||
|
||||
UEdGraphNode* TargetNode = FindNodeByGuid(BP, TargetNodeId);
|
||||
if (!TargetNode)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Target node '%s' not found"), *TargetNodeId));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Target node '%s' not found"), *TargetNodeId));
|
||||
}
|
||||
|
||||
UEdGraphPin* SourcePin = SourceNode->FindPin(FName(*SourcePinName));
|
||||
if (!SourcePin)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Source pin '%s' not found on node '%s'"), *SourcePinName, *SourceNodeId));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Source pin '%s' not found on node '%s'"), *SourcePinName, *SourceNodeId));
|
||||
}
|
||||
|
||||
UEdGraphPin* TargetPin = TargetNode->FindPin(FName(*TargetPinName));
|
||||
if (!TargetPin)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Target pin '%s' not found on node '%s'"), *TargetPinName, *TargetNodeId));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Target pin '%s' not found on node '%s'"), *TargetPinName, *TargetNodeId));
|
||||
}
|
||||
|
||||
const UEdGraphSchema* Schema = SourceGraph ? SourceGraph->GetSchema() : nullptr;
|
||||
if (!Schema)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Graph schema not found"));
|
||||
return MakeErrorJson(Result, TEXT("Graph schema not found"));
|
||||
}
|
||||
|
||||
// Check compatibility using the schema
|
||||
const FPinConnectionResponse Response = Schema->CanCreateConnection(SourcePin, TargetPin);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
|
||||
@@ -236,22 +219,14 @@ FString FBlueprintMCPServer::HandleCheckPinCompatibility(const FString& Body)
|
||||
Result->SetStringField(TEXT("targetPinType"), TargetPin->PinType.PinCategory.ToString());
|
||||
if (TargetPin->PinType.PinSubCategoryObject.IsValid())
|
||||
Result->SetStringField(TEXT("targetPinSubtype"), TargetPin->PinType.PinSubCategoryObject->GetName());
|
||||
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleListClasses — discover available UClasses
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleListClasses(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleListClasses(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString Filter = Json->GetStringField(TEXT("filter"));
|
||||
FString ParentClassName = Json->GetStringField(TEXT("parentClass"));
|
||||
int32 Limit = 100;
|
||||
@@ -273,7 +248,7 @@ FString FBlueprintMCPServer::HandleListClasses(const FString& Body)
|
||||
}
|
||||
if (!ParentClass)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Parent class '%s' not found"), *ParentClassName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Parent class '%s' not found"), *ParentClassName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +311,6 @@ FString FBlueprintMCPServer::HandleListClasses(const FString& Body)
|
||||
ClassList.Add(MakeShared<FJsonValueObject>(ClassObj));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetNumberField(TEXT("count"), ClassList.Num());
|
||||
Result->SetNumberField(TEXT("totalMatched"), TotalMatched);
|
||||
@@ -346,27 +320,20 @@ FString FBlueprintMCPServer::HandleListClasses(const FString& Body)
|
||||
Result->SetNumberField(TEXT("limit"), Limit);
|
||||
}
|
||||
Result->SetArrayField(TEXT("classes"), ClassList);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleListFunctions — list Blueprint-callable functions on a class
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleListFunctions(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleListFunctions(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString ClassName = Json->GetStringField(TEXT("className"));
|
||||
FString Filter = Json->GetStringField(TEXT("filter"));
|
||||
|
||||
if (ClassName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: className"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: className"));
|
||||
}
|
||||
|
||||
// Find the class
|
||||
@@ -381,7 +348,7 @@ FString FBlueprintMCPServer::HandleListFunctions(const FString& Body)
|
||||
}
|
||||
if (!FoundClass)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Class '%s' not found"), *ClassName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Class '%s' not found"), *ClassName));
|
||||
}
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> FuncList;
|
||||
@@ -453,32 +420,24 @@ FString FBlueprintMCPServer::HandleListFunctions(const FString& Body)
|
||||
FuncList.Add(MakeShared<FJsonValueObject>(FuncObj));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("className"), FoundClass->GetName());
|
||||
Result->SetNumberField(TEXT("count"), FuncList.Num());
|
||||
Result->SetArrayField(TEXT("functions"), FuncList);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleListProperties — list properties on a class
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleListProperties(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleListProperties(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString ClassName = Json->GetStringField(TEXT("className"));
|
||||
FString Filter = Json->GetStringField(TEXT("filter"));
|
||||
|
||||
if (ClassName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: className"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: className"));
|
||||
}
|
||||
|
||||
// Find the class
|
||||
@@ -493,7 +452,7 @@ FString FBlueprintMCPServer::HandleListProperties(const FString& Body)
|
||||
}
|
||||
if (!FoundClass)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Class '%s' not found"), *ClassName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Class '%s' not found"), *ClassName));
|
||||
}
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> PropList;
|
||||
@@ -540,10 +499,8 @@ FString FBlueprintMCPServer::HandleListProperties(const FString& Body)
|
||||
PropList.Add(MakeShared<FJsonValueObject>(PropObj));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("className"), FoundClass->GetName());
|
||||
Result->SetNumberField(TEXT("count"), PropList.Num());
|
||||
Result->SetArrayField(TEXT("properties"), PropList);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
@@ -14,20 +14,14 @@
|
||||
// HandleAddEventDispatcher — create a multicast delegate on a Blueprint
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleAddEventDispatcher(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleAddEventDispatcher(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString DispatcherName = Json->GetStringField(TEXT("dispatcherName"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || DispatcherName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, dispatcherName"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, dispatcherName"));
|
||||
}
|
||||
|
||||
// Load Blueprint
|
||||
@@ -35,7 +29,7 @@ FString FBlueprintMCPServer::HandleAddEventDispatcher(const FString& Body)
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
FName DispatcherFName(*DispatcherName);
|
||||
@@ -45,7 +39,7 @@ FString FBlueprintMCPServer::HandleAddEventDispatcher(const FString& Body)
|
||||
{
|
||||
if (Var.VarName == DispatcherFName)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("A variable or dispatcher named '%s' already exists in Blueprint '%s'"),
|
||||
*DispatcherName, *BlueprintName));
|
||||
}
|
||||
@@ -58,7 +52,7 @@ FString FBlueprintMCPServer::HandleAddEventDispatcher(const FString& Body)
|
||||
{
|
||||
if (Existing && Existing->GetName().Equals(DispatcherName, ESearchCase::IgnoreCase))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("A graph named '%s' already exists in Blueprint '%s'"),
|
||||
*DispatcherName, *BlueprintName));
|
||||
}
|
||||
@@ -73,7 +67,7 @@ FString FBlueprintMCPServer::HandleAddEventDispatcher(const FString& Body)
|
||||
bool bVarAdded = FBlueprintEditorUtils::AddMemberVariable(BP, DispatcherFName, DelegateType);
|
||||
if (!bVarAdded)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Failed to add delegate variable for '%s'"), *DispatcherName));
|
||||
}
|
||||
|
||||
@@ -84,7 +78,7 @@ FString FBlueprintMCPServer::HandleAddEventDispatcher(const FString& Body)
|
||||
UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
|
||||
if (!SigGraph)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Failed to create delegate signature graph"));
|
||||
return MakeErrorJson(Result, TEXT("Failed to create delegate signature graph"));
|
||||
}
|
||||
|
||||
K2Schema->CreateDefaultNodesForGraph(*SigGraph);
|
||||
@@ -121,7 +115,7 @@ FString FBlueprintMCPServer::HandleAddEventDispatcher(const FString& Body)
|
||||
// Still save what we have
|
||||
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(BP);
|
||||
SaveBlueprintPackage(BP);
|
||||
return MakeErrorJson(TEXT("Event dispatcher created but entry node not found — parameters could not be added"));
|
||||
return MakeErrorJson(Result, TEXT("Event dispatcher created but entry node not found — parameters could not be added"));
|
||||
}
|
||||
|
||||
for (const TSharedPtr<FJsonValue>& ParamVal : ParamsArr)
|
||||
@@ -138,7 +132,7 @@ FString FBlueprintMCPServer::HandleAddEventDispatcher(const FString& Body)
|
||||
FString TypeError;
|
||||
if (!ResolveTypeFromString(ParamType, PinType, TypeError))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Parameter '%s': %s"), *ParamName, *TypeError));
|
||||
}
|
||||
|
||||
@@ -157,38 +151,30 @@ FString FBlueprintMCPServer::HandleAddEventDispatcher(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Added event dispatcher '%s' to '%s' with %d params (saved: %s)"),
|
||||
*DispatcherName, *BlueprintName, AddedParamsJson.Num(), bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("dispatcherName"), DispatcherName);
|
||||
Result->SetArrayField(TEXT("parameters"), AddedParamsJson);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleListEventDispatchers — list all event dispatchers on a Blueprint
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleListEventDispatchers(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleListEventDispatchers(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
if (BlueprintName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: blueprint"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: blueprint"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
TSet<FName> DelegateNameSet;
|
||||
@@ -237,9 +223,7 @@ FString FBlueprintMCPServer::HandleListEventDispatchers(const FString& Body)
|
||||
DispatchersArr.Add(MakeShared<FJsonValueObject>(DispObj));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetNumberField(TEXT("count"), DispatchersArr.Num());
|
||||
Result->SetArrayField(TEXT("dispatchers"), DispatchersArr);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
@@ -19,20 +19,14 @@
|
||||
// HandleReparentBlueprint — change a Blueprint's parent class
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleReparentBlueprint(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleReparentBlueprint(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString NewParentName = Json->GetStringField(TEXT("newParentClass"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || NewParentName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, newParentClass"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, newParentClass"));
|
||||
}
|
||||
|
||||
// Load Blueprint
|
||||
@@ -40,7 +34,7 @@ FString FBlueprintMCPServer::HandleReparentBlueprint(const FString& Body)
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
FString OldParentName = BP->ParentClass ? BP->ParentClass->GetName() : TEXT("None");
|
||||
@@ -72,7 +66,7 @@ FString FBlueprintMCPServer::HandleReparentBlueprint(const FString& Body)
|
||||
|
||||
if (!NewParentClass)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Could not find class '%s'. Provide a C++ class name (e.g. 'WebUIHUD') or Blueprint name."),
|
||||
*NewParentName));
|
||||
}
|
||||
@@ -107,27 +101,19 @@ FString FBlueprintMCPServer::HandleReparentBlueprint(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Reparent complete, save %s"),
|
||||
bSaved ? TEXT("succeeded") : TEXT("failed"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("oldParentClass"), OldParentName);
|
||||
Result->SetStringField(TEXT("newParentClass"), NewParentActualName);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleCreateBlueprint — create a new Blueprint asset
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleCreateBlueprint(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleCreateBlueprint(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprintName"));
|
||||
FString PackagePath = Json->GetStringField(TEXT("packagePath"));
|
||||
FString ParentClassName = Json->GetStringField(TEXT("parentClass"));
|
||||
@@ -135,20 +121,20 @@ FString FBlueprintMCPServer::HandleCreateBlueprint(const FString& Body)
|
||||
|
||||
if (BlueprintName.IsEmpty() || PackagePath.IsEmpty() || ParentClassName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprintName, packagePath, parentClass"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprintName, packagePath, parentClass"));
|
||||
}
|
||||
|
||||
// Validate packagePath starts with /Game
|
||||
if (!PackagePath.StartsWith(TEXT("/Game")))
|
||||
{
|
||||
return MakeErrorJson(TEXT("packagePath must start with '/Game'"));
|
||||
return MakeErrorJson(Result, TEXT("packagePath must start with '/Game'"));
|
||||
}
|
||||
|
||||
// Check if asset already exists
|
||||
FString FullAssetPath = PackagePath / BlueprintName;
|
||||
if (FindBlueprintAsset(BlueprintName) || FindBlueprintAsset(FullAssetPath))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Blueprint '%s' already exists. Use a different name or delete the existing asset first."),
|
||||
*BlueprintName));
|
||||
}
|
||||
@@ -177,7 +163,7 @@ FString FBlueprintMCPServer::HandleCreateBlueprint(const FString& Body)
|
||||
|
||||
if (!ParentClass)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Could not find parent class '%s'. Provide a C++ class name (e.g. 'Actor', 'Pawn') or Blueprint name."),
|
||||
*ParentClassName));
|
||||
}
|
||||
@@ -200,7 +186,7 @@ FString FBlueprintMCPServer::HandleCreateBlueprint(const FString& Body)
|
||||
}
|
||||
else if (BlueprintTypeStr != TEXT("Normal"))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Invalid blueprintType '%s'. Valid values: Normal, Interface, FunctionLibrary, MacroLibrary"),
|
||||
*BlueprintTypeStr));
|
||||
}
|
||||
@@ -221,7 +207,7 @@ FString FBlueprintMCPServer::HandleCreateBlueprint(const FString& Body)
|
||||
UPackage* Package = CreatePackage(*FullPackagePath);
|
||||
if (!Package)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Failed to create package at '%s'"), *FullPackagePath));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Failed to create package at '%s'"), *FullPackagePath));
|
||||
}
|
||||
|
||||
// Create the Blueprint
|
||||
@@ -236,7 +222,7 @@ FString FBlueprintMCPServer::HandleCreateBlueprint(const FString& Body)
|
||||
|
||||
if (!NewBP)
|
||||
{
|
||||
return MakeErrorJson(TEXT("FKismetEditorUtilities::CreateBlueprint returned null"));
|
||||
return MakeErrorJson(Result, TEXT("FKismetEditorUtilities::CreateBlueprint returned null"));
|
||||
}
|
||||
|
||||
// Compile
|
||||
@@ -268,7 +254,6 @@ FString FBlueprintMCPServer::HandleCreateBlueprint(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Created Blueprint '%s' with %d graphs (saved: %s)"),
|
||||
*BlueprintName, GraphNames.Num(), bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprintName"), BlueprintName);
|
||||
Result->SetStringField(TEXT("packagePath"), PackagePath);
|
||||
@@ -277,33 +262,26 @@ FString FBlueprintMCPServer::HandleCreateBlueprint(const FString& Body)
|
||||
Result->SetStringField(TEXT("blueprintType"), BlueprintTypeStr.IsEmpty() ? TEXT("Normal") : BlueprintTypeStr);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
Result->SetArrayField(TEXT("graphs"), GraphNames);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleCreateGraph — create a new function, macro, or custom event graph
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleCreateGraph(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleCreateGraph(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graphName"));
|
||||
FString GraphType = Json->GetStringField(TEXT("graphType"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || GraphName.IsEmpty() || GraphType.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, graphName, graphType"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, graphName, graphType"));
|
||||
}
|
||||
|
||||
if (GraphType != TEXT("function") && GraphType != TEXT("macro") && GraphType != TEXT("customEvent"))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Invalid graphType '%s'. Valid values: function, macro, customEvent"), *GraphType));
|
||||
}
|
||||
|
||||
@@ -312,7 +290,7 @@ FString FBlueprintMCPServer::HandleCreateGraph(const FString& Body)
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Check graph name uniqueness
|
||||
@@ -322,7 +300,7 @@ FString FBlueprintMCPServer::HandleCreateGraph(const FString& Body)
|
||||
{
|
||||
if (Existing && Existing->GetName().Equals(GraphName, ESearchCase::IgnoreCase))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("A graph named '%s' already exists in Blueprint '%s'"), *GraphName, *BlueprintName));
|
||||
}
|
||||
}
|
||||
@@ -339,7 +317,7 @@ FString FBlueprintMCPServer::HandleCreateGraph(const FString& Body)
|
||||
{
|
||||
if (CE->CustomFunctionName == FName(*GraphName))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("A custom event named '%s' already exists in Blueprint '%s'"), *GraphName, *BlueprintName));
|
||||
}
|
||||
}
|
||||
@@ -358,7 +336,7 @@ FString FBlueprintMCPServer::HandleCreateGraph(const FString& Body)
|
||||
UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
|
||||
if (!NewGraph)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Failed to create function graph"));
|
||||
return MakeErrorJson(Result, TEXT("Failed to create function graph"));
|
||||
}
|
||||
FBlueprintEditorUtils::AddFunctionGraph(BP, NewGraph, /*bIsUserCreated=*/true, /*SignatureFromObject=*/static_cast<UClass*>(nullptr));
|
||||
}
|
||||
@@ -368,7 +346,7 @@ FString FBlueprintMCPServer::HandleCreateGraph(const FString& Body)
|
||||
UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
|
||||
if (!NewGraph)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Failed to create macro graph"));
|
||||
return MakeErrorJson(Result, TEXT("Failed to create macro graph"));
|
||||
}
|
||||
FBlueprintEditorUtils::AddMacroGraph(BP, NewGraph, /*bIsUserCreated=*/true, /*SignatureFromClass=*/nullptr);
|
||||
}
|
||||
@@ -382,7 +360,7 @@ FString FBlueprintMCPServer::HandleCreateGraph(const FString& Body)
|
||||
}
|
||||
if (!EventGraph)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Blueprint has no EventGraph to add a custom event to"));
|
||||
return MakeErrorJson(Result, TEXT("Blueprint has no EventGraph to add a custom event to"));
|
||||
}
|
||||
|
||||
// Create a custom event node in the EventGraph
|
||||
@@ -402,7 +380,6 @@ FString FBlueprintMCPServer::HandleCreateGraph(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Created %s graph '%s' in '%s' (saved: %s)"),
|
||||
*GraphType, *GraphName, *BlueprintName, bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("graphName"), GraphName);
|
||||
@@ -412,27 +389,28 @@ FString FBlueprintMCPServer::HandleCreateGraph(const FString& Body)
|
||||
{
|
||||
Result->SetStringField(TEXT("nodeId"), CreatedNodeId);
|
||||
}
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleDeleteGraph — delete a function or macro graph
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleDeleteGraph(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleDeleteGraph(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid()) return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graphName"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || GraphName.IsEmpty())
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, graphName"));
|
||||
{
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, graphName"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Find the graph
|
||||
UEdGraph* TargetGraph = nullptr;
|
||||
@@ -467,12 +445,12 @@ FString FBlueprintMCPServer::HandleDeleteGraph(const FString& Body)
|
||||
{
|
||||
if (Graph && Graph->GetName().Equals(GraphName, ESearchCase::IgnoreCase))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Cannot delete UbergraphPage '%s'. EventGraph and other Ubergraph pages cannot be deleted."),
|
||||
*GraphName));
|
||||
}
|
||||
}
|
||||
return MakeErrorJson(FString::Printf(TEXT("Graph '%s' not found in Blueprint '%s'"), *GraphName, *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Graph '%s' not found in Blueprint '%s'"), *GraphName, *BlueprintName));
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Deleting %s graph '%s' from Blueprint '%s'"),
|
||||
@@ -490,42 +468,42 @@ FString FBlueprintMCPServer::HandleDeleteGraph(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Deleted graph '%s' (%d nodes), save %s"),
|
||||
*GraphName, NodeCount, bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("graphName"), GraphName);
|
||||
Result->SetStringField(TEXT("graphType"), GraphType);
|
||||
Result->SetNumberField(TEXT("nodeCount"), NodeCount);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleRenameGraph — rename a function or macro graph
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleRenameGraph(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleRenameGraph(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid()) return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graphName"));
|
||||
FString NewName = Json->GetStringField(TEXT("newName"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || GraphName.IsEmpty() || NewName.IsEmpty())
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, graphName, newName"));
|
||||
{
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, graphName, newName"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Check if it's an UbergraphPage — disallow rename
|
||||
for (UEdGraph* Graph : BP->UbergraphPages)
|
||||
{
|
||||
if (Graph && Graph->GetName().Equals(GraphName, ESearchCase::IgnoreCase))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Cannot rename UbergraphPage '%s'. EventGraph and other Ubergraph pages cannot be renamed."),
|
||||
*GraphName));
|
||||
}
|
||||
@@ -558,7 +536,9 @@ FString FBlueprintMCPServer::HandleRenameGraph(const FString& Body)
|
||||
}
|
||||
|
||||
if (!TargetGraph)
|
||||
return MakeErrorJson(FString::Printf(TEXT("Graph '%s' not found in Blueprint '%s'"), *GraphName, *BlueprintName));
|
||||
{
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Graph '%s' not found in Blueprint '%s'"), *GraphName, *BlueprintName));
|
||||
}
|
||||
|
||||
// Check for name collision
|
||||
TArray<UEdGraph*> AllGraphs;
|
||||
@@ -567,7 +547,7 @@ FString FBlueprintMCPServer::HandleRenameGraph(const FString& Body)
|
||||
{
|
||||
if (Existing && Existing != TargetGraph && Existing->GetName().Equals(NewName, ESearchCase::IgnoreCase))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("A graph named '%s' already exists in Blueprint '%s'"), *NewName, *BlueprintName));
|
||||
}
|
||||
}
|
||||
@@ -583,12 +563,10 @@ FString FBlueprintMCPServer::HandleRenameGraph(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Renamed graph '%s' to '%s', save %s"),
|
||||
*GraphName, *NewName, bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("oldName"), GraphName);
|
||||
Result->SetStringField(TEXT("newName"), TargetGraph->GetName());
|
||||
Result->SetStringField(TEXT("graphType"), GraphType);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
@@ -12,25 +12,19 @@
|
||||
// HandleListInterfaces — list implemented interfaces on a Blueprint
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleListInterfaces(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleListInterfaces(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
if (BlueprintName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: blueprint"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: blueprint"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> InterfacesArr;
|
||||
@@ -59,38 +53,30 @@ FString FBlueprintMCPServer::HandleListInterfaces(const FString& Body)
|
||||
InterfacesArr.Add(MakeShared<FJsonValueObject>(IfaceObj));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetNumberField(TEXT("count"), InterfacesArr.Num());
|
||||
Result->SetArrayField(TEXT("interfaces"), InterfacesArr);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleAddInterface — add a Blueprint Interface implementation
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleAddInterface(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleAddInterface(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString InterfaceName = Json->GetStringField(TEXT("interfaceName"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || InterfaceName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, interfaceName"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, interfaceName"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Resolve the interface class
|
||||
@@ -137,7 +123,7 @@ FString FBlueprintMCPServer::HandleAddInterface(const FString& Body)
|
||||
|
||||
if (!InterfaceClass)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Interface '%s' not found. Provide a Blueprint Interface asset name (e.g. 'BPI_MyInterface') or a native UInterface class name."),
|
||||
*InterfaceName));
|
||||
}
|
||||
@@ -147,7 +133,7 @@ FString FBlueprintMCPServer::HandleAddInterface(const FString& Body)
|
||||
{
|
||||
if (IfaceDesc.Interface == InterfaceClass)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Interface '%s' is already implemented by Blueprint '%s'"),
|
||||
*InterfaceName, *BlueprintName));
|
||||
}
|
||||
@@ -162,7 +148,7 @@ FString FBlueprintMCPServer::HandleAddInterface(const FString& Body)
|
||||
bool bAdded = FBlueprintEditorUtils::ImplementNewInterface(BP, InterfacePath);
|
||||
if (!bAdded)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("FBlueprintEditorUtils::ImplementNewInterface failed for interface '%s' on Blueprint '%s'"),
|
||||
*InterfaceName, *BlueprintName));
|
||||
}
|
||||
@@ -190,7 +176,6 @@ FString FBlueprintMCPServer::HandleAddInterface(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Added interface '%s' to '%s' (%d function stubs, saved: %s)"),
|
||||
*InterfaceClass->GetName(), *BlueprintName, AddedFunctions.Num(), bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("interfaceName"), InterfaceClass->GetName());
|
||||
@@ -203,27 +188,20 @@ FString FBlueprintMCPServer::HandleAddInterface(const FString& Body)
|
||||
}
|
||||
Result->SetArrayField(TEXT("functionGraphsAdded"), FuncArr);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleRemoveInterface — remove a Blueprint Interface implementation
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleRemoveInterface(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleRemoveInterface(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString InterfaceName = Json->GetStringField(TEXT("interfaceName"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || InterfaceName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, interfaceName"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, interfaceName"));
|
||||
}
|
||||
|
||||
bool bPreserveFunctions = false;
|
||||
@@ -236,7 +214,7 @@ FString FBlueprintMCPServer::HandleRemoveInterface(const FString& Body)
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Find the interface in ImplementedInterfaces by name (case-insensitive)
|
||||
@@ -279,12 +257,11 @@ FString FBlueprintMCPServer::HandleRemoveInterface(const FString& Body)
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> ErrorResult = MakeShared<FJsonObject>();
|
||||
ErrorResult->SetStringField(TEXT("error"), FString::Printf(
|
||||
MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Interface '%s' is not implemented by Blueprint '%s'"),
|
||||
*InterfaceName, *BlueprintName));
|
||||
ErrorResult->SetArrayField(TEXT("implementedInterfaces"), IfaceList);
|
||||
return JsonToString(ErrorResult);
|
||||
Result->SetArrayField(TEXT("implementedInterfaces"), IfaceList);
|
||||
return;
|
||||
}
|
||||
|
||||
FTopLevelAssetPath InterfacePath = FoundInterface->GetClassPathName();
|
||||
@@ -300,11 +277,9 @@ FString FBlueprintMCPServer::HandleRemoveInterface(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Removed interface '%s' from '%s' (saved: %s)"),
|
||||
*FoundInterface->GetName(), *BlueprintName, bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("interfaceName"), FoundInterface->GetName());
|
||||
Result->SetBoolField(TEXT("preservedFunctions"), bPreserveFunctions);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
@@ -20,34 +20,28 @@
|
||||
// HandleCreateMaterialInstance — create a new Material Instance Constant
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleCreateMaterialInstance(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleCreateMaterialInstance(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString Name = Json->GetStringField(TEXT("name"));
|
||||
FString PackagePath = Json->GetStringField(TEXT("packagePath"));
|
||||
FString ParentMaterialName = Json->GetStringField(TEXT("parentMaterial"));
|
||||
|
||||
if (Name.IsEmpty() || PackagePath.IsEmpty() || ParentMaterialName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: name, packagePath, parentMaterial"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: name, packagePath, parentMaterial"));
|
||||
}
|
||||
|
||||
// Validate packagePath starts with /Game
|
||||
if (!PackagePath.StartsWith(TEXT("/Game")))
|
||||
{
|
||||
return MakeErrorJson(TEXT("packagePath must start with '/Game'"));
|
||||
return MakeErrorJson(Result, TEXT("packagePath must start with '/Game'"));
|
||||
}
|
||||
|
||||
// Check if asset already exists
|
||||
FString FullAssetPath = PackagePath / Name;
|
||||
if (FindMaterialInstanceAsset(Name) || FindMaterialInstanceAsset(FullAssetPath))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Material Instance '%s' already exists. Use a different name or delete the existing asset first."),
|
||||
*Name));
|
||||
}
|
||||
@@ -80,7 +74,7 @@ FString FBlueprintMCPServer::HandleCreateMaterialInstance(const FString& Body)
|
||||
|
||||
if (!ParentMaterial)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Parent material '%s' not found. Provide a Material or Material Instance name/path."),
|
||||
*ParentMaterialName));
|
||||
}
|
||||
@@ -95,13 +89,13 @@ FString FBlueprintMCPServer::HandleCreateMaterialInstance(const FString& Body)
|
||||
UObject* NewAsset = AssetTools.CreateAsset(Name, PackagePath, UMaterialInstanceConstant::StaticClass(), Factory);
|
||||
if (!NewAsset)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Failed to create Material Instance asset '%s' in '%s'"), *Name, *PackagePath));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Failed to create Material Instance asset '%s' in '%s'"), *Name, *PackagePath));
|
||||
}
|
||||
|
||||
UMaterialInstanceConstant* MI = Cast<UMaterialInstanceConstant>(NewAsset);
|
||||
if (!MI)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Created asset is not a UMaterialInstanceConstant"));
|
||||
return MakeErrorJson(Result, TEXT("Created asset is not a UMaterialInstanceConstant"));
|
||||
}
|
||||
|
||||
// Set parent
|
||||
@@ -120,38 +114,30 @@ FString FBlueprintMCPServer::HandleCreateMaterialInstance(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Created Material Instance '%s' with parent '%s' (saved: %s)"),
|
||||
*Name, *ParentMaterial->GetName(), bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("name"), Name);
|
||||
Result->SetStringField(TEXT("path"), MI->GetPathName());
|
||||
Result->SetStringField(TEXT("parent"), ParentMaterial->GetPathName());
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleSetMaterialInstanceParameter — set a parameter override on an MI
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleSetMaterialInstanceParameter(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleSetMaterialInstanceParameter(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString MIName = Json->GetStringField(TEXT("materialInstance"));
|
||||
FString ParamName = Json->GetStringField(TEXT("parameterName"));
|
||||
|
||||
if (MIName.IsEmpty() || ParamName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: materialInstance, parameterName"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: materialInstance, parameterName"));
|
||||
}
|
||||
|
||||
if (!Json->HasField(TEXT("value")))
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: value"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: value"));
|
||||
}
|
||||
|
||||
bool bDryRun = false;
|
||||
@@ -165,7 +151,7 @@ FString FBlueprintMCPServer::HandleSetMaterialInstanceParameter(const FString& B
|
||||
UMaterialInstanceConstant* MI = LoadMaterialInstanceByName(MIName, LoadError);
|
||||
if (!MI)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Determine the parameter type — explicit or auto-detect from parent
|
||||
@@ -237,7 +223,7 @@ FString FBlueprintMCPServer::HandleSetMaterialInstanceParameter(const FString& B
|
||||
|
||||
if (TypeStr.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Could not determine parameter type for '%s'. Specify the 'type' field explicitly (scalar, vector, texture, staticSwitch)."),
|
||||
*ParamName));
|
||||
}
|
||||
@@ -266,7 +252,7 @@ FString FBlueprintMCPServer::HandleSetMaterialInstanceParameter(const FString& B
|
||||
const TSharedPtr<FJsonObject>* ValueObj = nullptr;
|
||||
if (!Json->TryGetObjectField(TEXT("value"), ValueObj) || !ValueObj || !(*ValueObj).IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("For vector parameters, 'value' must be an object with r, g, b (and optional a) fields."));
|
||||
return MakeErrorJson(Result, TEXT("For vector parameters, 'value' must be an object with r, g, b (and optional a) fields."));
|
||||
}
|
||||
|
||||
double R = (*ValueObj)->GetNumberField(TEXT("r"));
|
||||
@@ -288,13 +274,13 @@ FString FBlueprintMCPServer::HandleSetMaterialInstanceParameter(const FString& B
|
||||
FString TexturePath = Json->GetStringField(TEXT("value"));
|
||||
if (TexturePath.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("For texture parameters, 'value' must be a texture asset path string."));
|
||||
return MakeErrorJson(Result, TEXT("For texture parameters, 'value' must be a texture asset path string."));
|
||||
}
|
||||
|
||||
UTexture* TextureObj = LoadObject<UTexture>(nullptr, *TexturePath);
|
||||
if (!TextureObj)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Could not load texture at path '%s'"), *TexturePath));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Could not load texture at path '%s'"), *TexturePath));
|
||||
}
|
||||
|
||||
if (!bDryRun)
|
||||
@@ -342,7 +328,7 @@ FString FBlueprintMCPServer::HandleSetMaterialInstanceParameter(const FString& B
|
||||
}
|
||||
else
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Unknown parameter type '%s'. Valid types: scalar, vector, texture, staticSwitch"),
|
||||
*TypeStr));
|
||||
}
|
||||
@@ -359,7 +345,6 @@ FString FBlueprintMCPServer::HandleSetMaterialInstanceParameter(const FString& B
|
||||
bDryRun ? TEXT("[DRY RUN] Would set") : TEXT("Set"),
|
||||
*ParamName, *NewValueDescription, *MIName);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("materialInstance"), MIName);
|
||||
Result->SetStringField(TEXT("parameterName"), ParamName);
|
||||
@@ -369,29 +354,27 @@ FString FBlueprintMCPServer::HandleSetMaterialInstanceParameter(const FString& B
|
||||
{
|
||||
Result->SetBoolField(TEXT("dryRun"), true);
|
||||
}
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleGetMaterialInstanceParameters — list all parameters on an MI
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleGetMaterialInstanceParameters(const TMap<FString, FString>& Params)
|
||||
void FBlueprintMCPServer::HandleGetMaterialInstanceParameters(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
const FString* NameParam = Params.Find(TEXT("name"));
|
||||
if (!NameParam || NameParam->IsEmpty())
|
||||
FString NameParam = Json->GetStringField(TEXT("name"));
|
||||
if (NameParam.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required query parameter: name"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required query parameter: name"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UMaterialInstanceConstant* MI = LoadMaterialInstanceByName(*NameParam, LoadError);
|
||||
UMaterialInstanceConstant* MI = LoadMaterialInstanceByName(NameParam, LoadError);
|
||||
if (!MI)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetStringField(TEXT("name"), MI->GetName());
|
||||
Result->SetStringField(TEXT("path"), MI->GetPathName());
|
||||
|
||||
@@ -610,28 +593,20 @@ FString FBlueprintMCPServer::HandleGetMaterialInstanceParameters(const TMap<FStr
|
||||
Result->SetArrayField(TEXT("vectorParameters"), VectorArr);
|
||||
Result->SetArrayField(TEXT("textureParameters"), TextureArr);
|
||||
Result->SetArrayField(TEXT("staticSwitchParameters"), StaticSwitchArr);
|
||||
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleReparentMaterialInstance — change parent of an MI
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleReparentMaterialInstance(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleReparentMaterialInstance(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString MIName = Json->GetStringField(TEXT("materialInstance"));
|
||||
FString NewParentName = Json->GetStringField(TEXT("newParent"));
|
||||
|
||||
if (MIName.IsEmpty() || NewParentName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: materialInstance, newParent"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: materialInstance, newParent"));
|
||||
}
|
||||
|
||||
bool bDryRun = false;
|
||||
@@ -645,7 +620,7 @@ FString FBlueprintMCPServer::HandleReparentMaterialInstance(const FString& Body)
|
||||
UMaterialInstanceConstant* MI = LoadMaterialInstanceByName(MIName, LoadError);
|
||||
if (!MI)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Capture old parent
|
||||
@@ -679,7 +654,7 @@ FString FBlueprintMCPServer::HandleReparentMaterialInstance(const FString& Body)
|
||||
|
||||
if (!NewParent)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("New parent material '%s' not found. Provide a Material or Material Instance name/path."),
|
||||
*NewParentName));
|
||||
}
|
||||
@@ -691,7 +666,7 @@ FString FBlueprintMCPServer::HandleReparentMaterialInstance(const FString& Body)
|
||||
{
|
||||
if (Check == MI)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Cannot reparent '%s' to '%s' — this would create a circular parent chain."),
|
||||
*MIName, *NewParentName));
|
||||
}
|
||||
@@ -723,7 +698,6 @@ FString FBlueprintMCPServer::HandleReparentMaterialInstance(const FString& Body)
|
||||
*MIName, bSaved ? TEXT("true") : TEXT("false"));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("materialInstance"), MIName);
|
||||
Result->SetStringField(TEXT("oldParent"), OldParentPath);
|
||||
@@ -732,5 +706,4 @@ FString FBlueprintMCPServer::HandleReparentMaterialInstance(const FString& Body)
|
||||
{
|
||||
Result->SetBoolField(TEXT("dryRun"), true);
|
||||
}
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,13 +34,13 @@
|
||||
// HandleListMaterials — list Material and MaterialInstance assets
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleListMaterials(const TMap<FString, FString>& Params)
|
||||
void FBlueprintMCPServer::HandleListMaterials(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
const FString* Filter = Params.Find(TEXT("filter"));
|
||||
const FString* TypeFilter = Params.Find(TEXT("type"));
|
||||
FString Filter = Json->GetStringField(TEXT("filter"));
|
||||
FString TypeFilter = Json->GetStringField(TEXT("type"));
|
||||
|
||||
bool bIncludeMaterials = !TypeFilter || TypeFilter->IsEmpty() || *TypeFilter == TEXT("all") || *TypeFilter == TEXT("material");
|
||||
bool bIncludeInstances = !TypeFilter || TypeFilter->IsEmpty() || *TypeFilter == TEXT("all") || *TypeFilter == TEXT("instance");
|
||||
bool bIncludeMaterials = TypeFilter.IsEmpty() || TypeFilter == TEXT("all") || TypeFilter == TEXT("material");
|
||||
bool bIncludeInstances = TypeFilter.IsEmpty() || TypeFilter == TEXT("all") || TypeFilter == TEXT("instance");
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> Entries;
|
||||
|
||||
@@ -51,10 +51,10 @@ FString FBlueprintMCPServer::HandleListMaterials(const TMap<FString, FString>& P
|
||||
FString Name = Asset.AssetName.ToString();
|
||||
FString Path = Asset.PackageName.ToString();
|
||||
|
||||
if (Filter && !Filter->IsEmpty())
|
||||
if (!Filter.IsEmpty())
|
||||
{
|
||||
if (!Name.Contains(*Filter, ESearchCase::IgnoreCase) &&
|
||||
!Path.Contains(*Filter, ESearchCase::IgnoreCase))
|
||||
if (!Name.Contains(Filter, ESearchCase::IgnoreCase) &&
|
||||
!Path.Contains(Filter, ESearchCase::IgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -75,10 +75,10 @@ FString FBlueprintMCPServer::HandleListMaterials(const TMap<FString, FString>& P
|
||||
FString Name = Asset.AssetName.ToString();
|
||||
FString Path = Asset.PackageName.ToString();
|
||||
|
||||
if (Filter && !Filter->IsEmpty())
|
||||
if (!Filter.IsEmpty())
|
||||
{
|
||||
if (!Name.Contains(*Filter, ESearchCase::IgnoreCase) &&
|
||||
!Path.Contains(*Filter, ESearchCase::IgnoreCase))
|
||||
if (!Name.Contains(Filter, ESearchCase::IgnoreCase) &&
|
||||
!Path.Contains(Filter, ESearchCase::IgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -94,26 +94,24 @@ FString FBlueprintMCPServer::HandleListMaterials(const TMap<FString, FString>& P
|
||||
|
||||
int32 Total = AllMaterialAssets.Num() + AllMaterialInstanceAssets.Num();
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetNumberField(TEXT("count"), Entries.Num());
|
||||
Result->SetNumberField(TEXT("total"), Total);
|
||||
Result->SetArrayField(TEXT("materials"), Entries);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleGetMaterial — detailed info about a material or instance
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleGetMaterial(const TMap<FString, FString>& Params)
|
||||
void FBlueprintMCPServer::HandleGetMaterial(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
const FString* Name = Params.Find(TEXT("name"));
|
||||
if (!Name || Name->IsEmpty())
|
||||
FString Name = Json->GetStringField(TEXT("name"));
|
||||
if (Name.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing 'name' parameter"));
|
||||
return MakeErrorJson(Result, TEXT("Missing 'name' parameter"));
|
||||
}
|
||||
|
||||
FString DecodedName = UrlDecode(*Name);
|
||||
FString DecodedName = UrlDecode(Name);
|
||||
|
||||
// Try loading as UMaterial first
|
||||
FString LoadError;
|
||||
@@ -122,7 +120,6 @@ FString FBlueprintMCPServer::HandleGetMaterial(const TMap<FString, FString>& Par
|
||||
{
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: GetMaterial — loaded material '%s'"), *Material->GetName());
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetStringField(TEXT("name"), Material->GetName());
|
||||
Result->SetStringField(TEXT("path"), Material->GetPathName());
|
||||
Result->SetStringField(TEXT("type"), TEXT("Material"));
|
||||
@@ -268,7 +265,7 @@ FString FBlueprintMCPServer::HandleGetMaterial(const TMap<FString, FString>& Par
|
||||
}
|
||||
Result->SetNumberField(TEXT("textureSampleCount"), TextureSampleCount);
|
||||
|
||||
return JsonToString(Result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try loading as MaterialInstance
|
||||
@@ -278,7 +275,6 @@ FString FBlueprintMCPServer::HandleGetMaterial(const TMap<FString, FString>& Par
|
||||
{
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: GetMaterial — loaded material instance '%s'"), *MI->GetName());
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetStringField(TEXT("name"), MI->GetName());
|
||||
Result->SetStringField(TEXT("path"), MI->GetPathName());
|
||||
Result->SetStringField(TEXT("type"), TEXT("MaterialInstance"));
|
||||
@@ -343,31 +339,31 @@ FString FBlueprintMCPServer::HandleGetMaterial(const TMap<FString, FString>& Par
|
||||
|
||||
Result->SetArrayField(TEXT("overriddenParameters"), OverriddenParams);
|
||||
|
||||
return JsonToString(Result);
|
||||
return;
|
||||
}
|
||||
|
||||
return MakeErrorJson(FString::Printf(TEXT("Material or MaterialInstance '%s' not found. Use list_materials to see available assets."), *DecodedName));
|
||||
MakeErrorJson(Result, FString::Printf(TEXT("Material or MaterialInstance '%s' not found. Use list_materials to see available assets."), *DecodedName));
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleGetMaterialGraph — serialized graph for a material
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleGetMaterialGraph(const TMap<FString, FString>& Params)
|
||||
void FBlueprintMCPServer::HandleGetMaterialGraph(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
const FString* Name = Params.Find(TEXT("name"));
|
||||
if (!Name || Name->IsEmpty())
|
||||
FString Name = Json->GetStringField(TEXT("name"));
|
||||
if (Name.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing 'name' parameter"));
|
||||
return MakeErrorJson(Result, TEXT("Missing 'name' parameter"));
|
||||
}
|
||||
|
||||
FString DecodedName = UrlDecode(*Name);
|
||||
FString DecodedName = UrlDecode(Name);
|
||||
|
||||
FString LoadError;
|
||||
UMaterial* Material = LoadMaterialByName(DecodedName, LoadError);
|
||||
if (!Material)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: GetMaterialGraph — material '%s'"), *Material->GetName());
|
||||
@@ -385,45 +381,39 @@ FString FBlueprintMCPServer::HandleGetMaterialGraph(const TMap<FString, FString>
|
||||
|
||||
if (!Material->MaterialGraph)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Could not build MaterialGraph for this material"));
|
||||
return MakeErrorJson(Result, TEXT("Could not build MaterialGraph for this material"));
|
||||
}
|
||||
|
||||
TSharedPtr<FJsonObject> GraphJson = SerializeGraph(Material->MaterialGraph);
|
||||
if (!GraphJson.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Failed to serialize material graph"));
|
||||
return MakeErrorJson(Result, TEXT("Failed to serialize material graph"));
|
||||
}
|
||||
|
||||
// Add material name context
|
||||
GraphJson->SetStringField(TEXT("material"), Material->GetName());
|
||||
GraphJson->SetStringField(TEXT("materialPath"), Material->GetPathName());
|
||||
CopyJsonFields(GraphJson.Get(), Result);
|
||||
|
||||
return JsonToString(GraphJson.ToSharedRef());
|
||||
// Add material name context
|
||||
Result->SetStringField(TEXT("material"), Material->GetName());
|
||||
Result->SetStringField(TEXT("materialPath"), Material->GetPathName());
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleDescribeMaterial — human-readable material description
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleDescribeMaterial(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleDescribeMaterial(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString MaterialName = Json->GetStringField(TEXT("material"));
|
||||
if (MaterialName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: material"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: material"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UMaterial* Material = LoadMaterialByName(MaterialName, LoadError);
|
||||
if (!Material)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: DescribeMaterial — '%s'"), *Material->GetName());
|
||||
@@ -439,7 +429,7 @@ FString FBlueprintMCPServer::HandleDescribeMaterial(const FString& Body)
|
||||
|
||||
if (!Material->MaterialGraph)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Could not build MaterialGraph for this material"));
|
||||
return MakeErrorJson(Result, TEXT("Could not build MaterialGraph for this material"));
|
||||
}
|
||||
|
||||
// Recursive helper: trace backwards from a pin and build a description string
|
||||
@@ -559,7 +549,7 @@ FString FBlueprintMCPServer::HandleDescribeMaterial(const FString& Body)
|
||||
|
||||
if (!RootNode)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Could not find root node in material graph"));
|
||||
return MakeErrorJson(Result, TEXT("Could not find root node in material graph"));
|
||||
}
|
||||
|
||||
for (UEdGraphPin* Pin : RootNode->Pins)
|
||||
@@ -585,7 +575,6 @@ FString FBlueprintMCPServer::HandleDescribeMaterial(const FString& Body)
|
||||
InputDescriptions.Add(MakeShared<FJsonValueObject>(InputObj));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("material"), Material->GetName());
|
||||
Result->SetStringField(TEXT("materialPath"), Material->GetPathName());
|
||||
@@ -609,28 +598,26 @@ FString FBlueprintMCPServer::HandleDescribeMaterial(const FString& Body)
|
||||
{
|
||||
Result->SetStringField(TEXT("description"), TextDesc);
|
||||
}
|
||||
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleSearchMaterials — search expressions and parameters
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleSearchMaterials(const TMap<FString, FString>& Params)
|
||||
void FBlueprintMCPServer::HandleSearchMaterials(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
const FString* Query = Params.Find(TEXT("query"));
|
||||
if (!Query || Query->IsEmpty())
|
||||
FString Query = Json->GetStringField(TEXT("query"));
|
||||
if (Query.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing 'query' parameter"));
|
||||
return MakeErrorJson(Result, TEXT("Missing 'query' parameter"));
|
||||
}
|
||||
|
||||
FString DecodedQuery = UrlDecode(*Query);
|
||||
FString DecodedQuery = UrlDecode(Query);
|
||||
|
||||
int32 MaxResults = 50;
|
||||
if (const FString* M = Params.Find(TEXT("maxResults")))
|
||||
if (Json->HasField(TEXT("maxResults")))
|
||||
{
|
||||
MaxResults = FMath::Clamp(FCString::Atoi(**M), 1, 200);
|
||||
MaxResults = FMath::Clamp((int32)Json->GetNumberField(TEXT("maxResults")), 1, 200);
|
||||
}
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> Results;
|
||||
@@ -698,29 +685,21 @@ FString FBlueprintMCPServer::HandleSearchMaterials(const TMap<FString, FString>&
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetStringField(TEXT("query"), DecodedQuery);
|
||||
Result->SetNumberField(TEXT("resultCount"), Results.Num());
|
||||
Result->SetArrayField(TEXT("results"), Results);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleFindMaterialReferences — find assets referencing a material
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleFindMaterialReferences(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleFindMaterialReferences(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString MaterialName = Json->GetStringField(TEXT("material"));
|
||||
if (MaterialName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: material"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: material"));
|
||||
}
|
||||
|
||||
// Try to find the material's package path
|
||||
@@ -742,7 +721,7 @@ FString FBlueprintMCPServer::HandleFindMaterialReferences(const FString& Body)
|
||||
|
||||
if (PackagePath.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Material '%s' not found. Use list_materials to see available assets."), *MaterialName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Material '%s' not found. Use list_materials to see available assets."), *MaterialName));
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: FindMaterialReferences — '%s' (package: %s)"), *MaterialName, *PackagePath);
|
||||
@@ -761,22 +740,20 @@ FString FBlueprintMCPServer::HandleFindMaterialReferences(const FString& Body)
|
||||
RefArray.Add(MakeShared<FJsonValueString>(RefStr));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("material"), MaterialName);
|
||||
Result->SetStringField(TEXT("packagePath"), PackagePath);
|
||||
Result->SetNumberField(TEXT("totalReferencers"), RefArray.Num());
|
||||
Result->SetArrayField(TEXT("referencers"), RefArray);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleListMaterialFunctions — list MaterialFunction assets
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleListMaterialFunctions(const TMap<FString, FString>& Params)
|
||||
void FBlueprintMCPServer::HandleListMaterialFunctions(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
const FString* Filter = Params.Find(TEXT("filter"));
|
||||
FString Filter = Json->GetStringField(TEXT("filter"));
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> Entries;
|
||||
|
||||
@@ -785,10 +762,10 @@ FString FBlueprintMCPServer::HandleListMaterialFunctions(const TMap<FString, FSt
|
||||
FString Name = Asset.AssetName.ToString();
|
||||
FString Path = Asset.PackageName.ToString();
|
||||
|
||||
if (Filter && !Filter->IsEmpty())
|
||||
if (!Filter.IsEmpty())
|
||||
{
|
||||
if (!Name.Contains(*Filter, ESearchCase::IgnoreCase) &&
|
||||
!Path.Contains(*Filter, ESearchCase::IgnoreCase))
|
||||
if (!Name.Contains(Filter, ESearchCase::IgnoreCase) &&
|
||||
!Path.Contains(Filter, ESearchCase::IgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -800,37 +777,34 @@ FString FBlueprintMCPServer::HandleListMaterialFunctions(const TMap<FString, FSt
|
||||
Entries.Add(MakeShared<FJsonValueObject>(Entry));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetNumberField(TEXT("count"), Entries.Num());
|
||||
Result->SetNumberField(TEXT("total"), AllMaterialFunctionAssets.Num());
|
||||
Result->SetArrayField(TEXT("functions"), Entries);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleGetMaterialFunction — detailed info about a material function
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleGetMaterialFunction(const TMap<FString, FString>& Params)
|
||||
void FBlueprintMCPServer::HandleGetMaterialFunction(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
const FString* Name = Params.Find(TEXT("name"));
|
||||
if (!Name || Name->IsEmpty())
|
||||
FString Name = Json->GetStringField(TEXT("name"));
|
||||
if (Name.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing 'name' parameter"));
|
||||
return MakeErrorJson(Result, TEXT("Missing 'name' parameter"));
|
||||
}
|
||||
|
||||
FString DecodedName = UrlDecode(*Name);
|
||||
FString DecodedName = UrlDecode(Name);
|
||||
|
||||
FString LoadError;
|
||||
UMaterialFunction* MF = LoadMaterialFunctionByName(DecodedName, LoadError);
|
||||
if (!MF)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: GetMaterialFunction — '%s'"), *MF->GetName());
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetStringField(TEXT("name"), MF->GetName());
|
||||
Result->SetStringField(TEXT("path"), MF->GetPathName());
|
||||
Result->SetStringField(TEXT("description"), MF->GetDescription());
|
||||
@@ -891,26 +865,18 @@ FString FBlueprintMCPServer::HandleGetMaterialFunction(const TMap<FString, FStri
|
||||
Result->SetObjectField(TEXT("graph"), GraphJson);
|
||||
}
|
||||
}
|
||||
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleValidateMaterial — force recompile and check for errors
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleValidateMaterial(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleValidateMaterial(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString MaterialName = Json->GetStringField(TEXT("material"));
|
||||
if (MaterialName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: material"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: material"));
|
||||
}
|
||||
|
||||
// Load material
|
||||
@@ -918,7 +884,7 @@ FString FBlueprintMCPServer::HandleValidateMaterial(const FString& Body)
|
||||
UMaterial* Material = LoadMaterialByName(MaterialName, LoadError);
|
||||
if (!Material)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Validating material '%s'"), *Material->GetName());
|
||||
@@ -962,7 +928,6 @@ FString FBlueprintMCPServer::HandleValidateMaterial(const FString& Body)
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("valid"), bValid);
|
||||
Result->SetStringField(TEXT("material"), Material->GetName());
|
||||
Result->SetStringField(TEXT("materialPath"), Material->GetPathName());
|
||||
@@ -973,6 +938,4 @@ FString FBlueprintMCPServer::HandleValidateMaterial(const FString& Body)
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Material '%s' validation %s (%d errors)"),
|
||||
*Material->GetName(), bValid ? TEXT("passed") : TEXT("failed"), ErrorArray.Num());
|
||||
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,14 +13,8 @@
|
||||
#include "Serialization/JsonSerializer.h"
|
||||
#include "UObject/UObjectIterator.h"
|
||||
|
||||
FString FBlueprintMCPServer::HandleChangeFunctionParamType(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleChangeFunctionParamType(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString FunctionName = Json->GetStringField(TEXT("functionName"));
|
||||
FString ParamName = Json->GetStringField(TEXT("paramName"));
|
||||
@@ -28,7 +22,7 @@ FString FBlueprintMCPServer::HandleChangeFunctionParamType(const FString& Body)
|
||||
|
||||
if (BlueprintName.IsEmpty() || FunctionName.IsEmpty() || ParamName.IsEmpty() || NewTypeName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, functionName, paramName, newType"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, functionName, paramName, newType"));
|
||||
}
|
||||
|
||||
// Load Blueprint
|
||||
@@ -36,7 +30,7 @@ FString FBlueprintMCPServer::HandleChangeFunctionParamType(const FString& Body)
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Resolve the new type using the shared resolver (supports primitives, structs, enums, and object references)
|
||||
@@ -44,7 +38,7 @@ FString FBlueprintMCPServer::HandleChangeFunctionParamType(const FString& Body)
|
||||
FString TypeError;
|
||||
if (!ResolveTypeFromString(NewTypeName, NewPinType, TypeError))
|
||||
{
|
||||
return MakeErrorJson(TypeError);
|
||||
return MakeErrorJson(Result, TypeError);
|
||||
}
|
||||
|
||||
// Find the entry node: K2Node_FunctionEntry in a function graph,
|
||||
@@ -117,12 +111,11 @@ FString FBlueprintMCPServer::HandleChangeFunctionParamType(const FString& Body)
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> E = MakeShared<FJsonObject>();
|
||||
E->SetStringField(TEXT("error"), FString::Printf(
|
||||
MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Function or custom event '%s' not found in Blueprint '%s'"),
|
||||
*FunctionName, *BlueprintName));
|
||||
E->SetArrayField(TEXT("availableFunctionsAndEvents"), Available);
|
||||
return JsonToString(E);
|
||||
Result->SetArrayField(TEXT("availableFunctionsAndEvents"), Available);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the UserDefinedPin matching paramName
|
||||
@@ -149,12 +142,11 @@ FString FBlueprintMCPServer::HandleChangeFunctionParamType(const FString& Body)
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> E = MakeShared<FJsonObject>();
|
||||
E->SetStringField(TEXT("error"), FString::Printf(
|
||||
MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Parameter '%s' not found in %s '%s'"),
|
||||
*ParamName, *FoundNodeType, *FunctionName));
|
||||
E->SetArrayField(TEXT("availableParams"), ParamNames);
|
||||
return JsonToString(E);
|
||||
Result->SetArrayField(TEXT("availableParams"), ParamNames);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for dry run
|
||||
@@ -189,7 +181,6 @@ FString FBlueprintMCPServer::HandleChangeFunctionParamType(const FString& Body)
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("dryRun"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("functionName"), FunctionName);
|
||||
@@ -199,7 +190,7 @@ FString FBlueprintMCPServer::HandleChangeFunctionParamType(const FString& Body)
|
||||
Result->SetStringField(TEXT("nodeId"), EntryNode->NodeGuid.ToString());
|
||||
Result->SetNumberField(TEXT("connectionsAtRisk"), AffectedPins.Num());
|
||||
Result->SetArrayField(TEXT("affectedPins"), AffectedPins);
|
||||
return JsonToString(Result);
|
||||
return;
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Changing param '%s' in %s '%s' of '%s' to %s"),
|
||||
@@ -223,7 +214,6 @@ FString FBlueprintMCPServer::HandleChangeFunctionParamType(const FString& Body)
|
||||
// Serialize the updated entry node state
|
||||
TSharedPtr<FJsonObject> UpdatedNodeState = SerializeNode(EntryNode);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("functionName"), FunctionName);
|
||||
@@ -236,28 +226,21 @@ FString FBlueprintMCPServer::HandleChangeFunctionParamType(const FString& Body)
|
||||
{
|
||||
Result->SetObjectField(TEXT("updatedNode"), UpdatedNodeState);
|
||||
}
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleRemoveFunctionParameter
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleRemoveFunctionParameter(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleRemoveFunctionParameter(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString FunctionName = Json->GetStringField(TEXT("functionName"));
|
||||
FString ParamName = Json->GetStringField(TEXT("paramName"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || FunctionName.IsEmpty() || ParamName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, functionName, paramName"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, functionName, paramName"));
|
||||
}
|
||||
|
||||
// Load Blueprint
|
||||
@@ -265,7 +248,7 @@ FString FBlueprintMCPServer::HandleRemoveFunctionParameter(const FString& Body)
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Find the entry node
|
||||
@@ -337,12 +320,11 @@ FString FBlueprintMCPServer::HandleRemoveFunctionParameter(const FString& Body)
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> E = MakeShared<FJsonObject>();
|
||||
E->SetStringField(TEXT("error"), FString::Printf(
|
||||
MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Function or custom event '%s' not found in Blueprint '%s'"),
|
||||
*FunctionName, *BlueprintName));
|
||||
E->SetArrayField(TEXT("availableFunctionsAndEvents"), Available);
|
||||
return JsonToString(E);
|
||||
Result->SetArrayField(TEXT("availableFunctionsAndEvents"), Available);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find and remove the UserDefinedPin matching paramName
|
||||
@@ -369,12 +351,11 @@ FString FBlueprintMCPServer::HandleRemoveFunctionParameter(const FString& Body)
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> E = MakeShared<FJsonObject>();
|
||||
E->SetStringField(TEXT("error"), FString::Printf(
|
||||
MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Parameter '%s' not found in %s '%s'"),
|
||||
*ParamName, *FoundNodeType, *FunctionName));
|
||||
E->SetArrayField(TEXT("availableParams"), ParamNames);
|
||||
return JsonToString(E);
|
||||
Result->SetArrayField(TEXT("availableParams"), ParamNames);
|
||||
return;
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Removing param '%s' from %s '%s' in '%s'"),
|
||||
@@ -398,7 +379,6 @@ FString FBlueprintMCPServer::HandleRemoveFunctionParameter(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Parameter removed, save %s"),
|
||||
bSaved ? TEXT("succeeded") : TEXT("failed"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("functionName"), FunctionName);
|
||||
@@ -406,21 +386,14 @@ FString FBlueprintMCPServer::HandleRemoveFunctionParameter(const FString& Body)
|
||||
Result->SetStringField(TEXT("nodeType"), FoundNodeType);
|
||||
Result->SetStringField(TEXT("nodeId"), EntryNode->NodeGuid.ToString());
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleAddFunctionParameter
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleAddFunctionParameter(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleAddFunctionParameter(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString FunctionName = Json->GetStringField(TEXT("functionName"));
|
||||
FString ParamName = Json->GetStringField(TEXT("paramName"));
|
||||
@@ -428,7 +401,7 @@ FString FBlueprintMCPServer::HandleAddFunctionParameter(const FString& Body)
|
||||
|
||||
if (BlueprintName.IsEmpty() || FunctionName.IsEmpty() || ParamName.IsEmpty() || ParamType.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, functionName, paramName, paramType"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, functionName, paramName, paramType"));
|
||||
}
|
||||
|
||||
// Load Blueprint
|
||||
@@ -436,7 +409,7 @@ FString FBlueprintMCPServer::HandleAddFunctionParameter(const FString& Body)
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Resolve param type
|
||||
@@ -444,7 +417,7 @@ FString FBlueprintMCPServer::HandleAddFunctionParameter(const FString& Body)
|
||||
FString TypeError;
|
||||
if (!ResolveTypeFromString(ParamType, PinType, TypeError))
|
||||
{
|
||||
return MakeErrorJson(TypeError);
|
||||
return MakeErrorJson(Result, TypeError);
|
||||
}
|
||||
|
||||
// Find the entry node using 3 strategies
|
||||
@@ -559,12 +532,11 @@ FString FBlueprintMCPServer::HandleAddFunctionParameter(const FString& Body)
|
||||
FString::Printf(TEXT("%s (event dispatcher)"), *DN.ToString())));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> ErrorResult = MakeShared<FJsonObject>();
|
||||
ErrorResult->SetStringField(TEXT("error"), FString::Printf(
|
||||
MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Function, custom event, or event dispatcher '%s' not found in Blueprint '%s'"),
|
||||
*FunctionName, *BlueprintName));
|
||||
ErrorResult->SetArrayField(TEXT("availableFunctions"), AvailFuncs);
|
||||
return JsonToString(ErrorResult);
|
||||
Result->SetArrayField(TEXT("availableFunctions"), AvailFuncs);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for duplicate parameter name
|
||||
@@ -572,7 +544,7 @@ FString FBlueprintMCPServer::HandleAddFunctionParameter(const FString& Body)
|
||||
{
|
||||
if (Existing.IsValid() && Existing->PinName.ToString().Equals(ParamName, ESearchCase::IgnoreCase))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Parameter '%s' already exists on '%s'"), *ParamName, *FunctionName));
|
||||
}
|
||||
}
|
||||
@@ -589,7 +561,6 @@ FString FBlueprintMCPServer::HandleAddFunctionParameter(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Added parameter '%s' to '%s' in '%s' (saved: %s)"),
|
||||
*ParamName, *FunctionName, *BlueprintName, bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("functionName"), FunctionName);
|
||||
@@ -597,5 +568,4 @@ FString FBlueprintMCPServer::HandleAddFunctionParameter(const FString& Body)
|
||||
Result->SetStringField(TEXT("paramType"), ParamType);
|
||||
Result->SetStringField(TEXT("nodeType"), NodeType);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
@@ -24,14 +24,14 @@
|
||||
// Request handlers
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleList(const TMap<FString, FString>& Params)
|
||||
void FBlueprintMCPServer::HandleList(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
const FString* Filter = Params.Find(TEXT("filter"));
|
||||
const FString* ParentClassFilter = Params.Find(TEXT("parentClass"));
|
||||
const FString* TypeFilter = Params.Find(TEXT("type"));
|
||||
FString Filter = Json->GetStringField(TEXT("filter"));
|
||||
FString ParentClassFilter = Json->GetStringField(TEXT("parentClass"));
|
||||
FString TypeFilter = Json->GetStringField(TEXT("type"));
|
||||
// type: "all" (default), "regular", "level"
|
||||
bool bIncludeRegular = !TypeFilter || TypeFilter->IsEmpty() || *TypeFilter == TEXT("all") || *TypeFilter == TEXT("regular");
|
||||
bool bIncludeLevel = !TypeFilter || TypeFilter->IsEmpty() || *TypeFilter == TEXT("all") || *TypeFilter == TEXT("level");
|
||||
bool bIncludeRegular = TypeFilter.IsEmpty() || TypeFilter == TEXT("all") || TypeFilter == TEXT("regular");
|
||||
bool bIncludeLevel = TypeFilter.IsEmpty() || TypeFilter == TEXT("all") || TypeFilter == TEXT("level");
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> Entries;
|
||||
if (bIncludeRegular)
|
||||
@@ -40,10 +40,10 @@ FString FBlueprintMCPServer::HandleList(const TMap<FString, FString>& Params)
|
||||
FString Name = Asset.AssetName.ToString();
|
||||
FString Path = Asset.PackageName.ToString();
|
||||
|
||||
if (Filter && !Filter->IsEmpty())
|
||||
if (!Filter.IsEmpty())
|
||||
{
|
||||
if (!Name.Contains(*Filter, ESearchCase::IgnoreCase) &&
|
||||
!Path.Contains(*Filter, ESearchCase::IgnoreCase))
|
||||
if (!Name.Contains(Filter, ESearchCase::IgnoreCase) &&
|
||||
!Path.Contains(Filter, ESearchCase::IgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -58,9 +58,9 @@ FString FBlueprintMCPServer::HandleList(const TMap<FString, FString>& Params)
|
||||
ParentClass = ParentClass.Mid(DotIndex + 1);
|
||||
}
|
||||
|
||||
if (ParentClassFilter && !ParentClassFilter->IsEmpty())
|
||||
if (!ParentClassFilter.IsEmpty())
|
||||
{
|
||||
if (!ParentClass.Contains(*ParentClassFilter, ESearchCase::IgnoreCase))
|
||||
if (!ParentClass.Contains(ParentClassFilter, ESearchCase::IgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -80,19 +80,19 @@ FString FBlueprintMCPServer::HandleList(const TMap<FString, FString>& Params)
|
||||
FString Name = Asset.AssetName.ToString();
|
||||
FString Path = Asset.PackageName.ToString();
|
||||
|
||||
if (Filter && !Filter->IsEmpty())
|
||||
if (!Filter.IsEmpty())
|
||||
{
|
||||
if (!Name.Contains(*Filter, ESearchCase::IgnoreCase) &&
|
||||
!Path.Contains(*Filter, ESearchCase::IgnoreCase))
|
||||
if (!Name.Contains(Filter, ESearchCase::IgnoreCase) &&
|
||||
!Path.Contains(Filter, ESearchCase::IgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// No parent class filter for level blueprints
|
||||
if (ParentClassFilter && !ParentClassFilter->IsEmpty())
|
||||
if (!ParentClassFilter.IsEmpty())
|
||||
{
|
||||
if (!FString(TEXT("LevelScriptActor")).Contains(*ParentClassFilter, ESearchCase::IgnoreCase))
|
||||
if (!FString(TEXT("LevelScriptActor")).Contains(ParentClassFilter, ESearchCase::IgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -106,48 +106,47 @@ FString FBlueprintMCPServer::HandleList(const TMap<FString, FString>& Params)
|
||||
Entries.Add(MakeShared<FJsonValueObject>(Entry));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetNumberField(TEXT("count"), Entries.Num());
|
||||
Result->SetNumberField(TEXT("total"), AllBlueprintAssets.Num() + AllMapAssets.Num());
|
||||
Result->SetArrayField(TEXT("blueprints"), Entries);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleGetBlueprint(const TMap<FString, FString>& Params)
|
||||
void FBlueprintMCPServer::HandleGetBlueprint(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
const FString* Name = Params.Find(TEXT("name"));
|
||||
if (!Name || Name->IsEmpty())
|
||||
FString Name = Json->GetStringField(TEXT("name"));
|
||||
if (Name.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing 'name' parameter"));
|
||||
return MakeErrorJson(Result, TEXT("Missing 'name' parameter"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(*Name, LoadError);
|
||||
UBlueprint* BP = LoadBlueprintByName(Name, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
return JsonToString(SerializeBlueprint(BP));
|
||||
TSharedRef<FJsonObject> Tmp = SerializeBlueprint(BP);
|
||||
CopyJsonFields(&*Tmp, Result);
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleGetGraph(const TMap<FString, FString>& Params)
|
||||
void FBlueprintMCPServer::HandleGetGraph(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
const FString* Name = Params.Find(TEXT("name"));
|
||||
const FString* GraphName = Params.Find(TEXT("graph"));
|
||||
if (!Name || Name->IsEmpty() || !GraphName || GraphName->IsEmpty())
|
||||
FString Name = Json->GetStringField(TEXT("name"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graph"));
|
||||
if (Name.IsEmpty() || GraphName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing 'name' or 'graph' parameter"));
|
||||
return MakeErrorJson(Result, TEXT("Missing 'name' or 'graph' parameter"));
|
||||
}
|
||||
|
||||
// URL-decode graph name to handle spaces encoded as '+' or '%20'
|
||||
FString DecodedGraphName = UrlDecode(*GraphName);
|
||||
FString DecodedGraphName = UrlDecode(GraphName);
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(*Name, LoadError);
|
||||
UBlueprint* BP = LoadBlueprintByName(Name, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
TArray<UEdGraph*> AllGraphs;
|
||||
@@ -160,7 +159,8 @@ FString FBlueprintMCPServer::HandleGetGraph(const TMap<FString, FString>& Params
|
||||
TSharedPtr<FJsonObject> GraphJson = SerializeGraph(Graph);
|
||||
if (GraphJson.IsValid())
|
||||
{
|
||||
return JsonToString(GraphJson.ToSharedRef());
|
||||
CopyJsonFields(GraphJson.Get(), Result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,28 +174,24 @@ FString FBlueprintMCPServer::HandleGetGraph(const TMap<FString, FString>& Params
|
||||
GraphNames.Add(MakeShared<FJsonValueString>(Graph->GetName()));
|
||||
}
|
||||
}
|
||||
TSharedRef<FJsonObject> E = MakeShared<FJsonObject>();
|
||||
E->SetStringField(TEXT("error"), FString::Printf(TEXT("Graph '%s' not found"), *DecodedGraphName));
|
||||
E->SetArrayField(TEXT("availableGraphs"), GraphNames);
|
||||
return JsonToString(E);
|
||||
MakeErrorJson(Result, FString::Printf(TEXT("Graph '%s' not found"), *DecodedGraphName));
|
||||
Result->SetArrayField(TEXT("availableGraphs"), GraphNames);
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleSearch(const TMap<FString, FString>& Params)
|
||||
void FBlueprintMCPServer::HandleSearch(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
const FString* Query = Params.Find(TEXT("query"));
|
||||
if (!Query || Query->IsEmpty())
|
||||
FString Query = Json->GetStringField(TEXT("query"));
|
||||
if (Query.IsEmpty())
|
||||
{
|
||||
TSharedRef<FJsonObject> E = MakeShared<FJsonObject>();
|
||||
E->SetStringField(TEXT("error"), TEXT("Missing 'query' parameter"));
|
||||
return JsonToString(E);
|
||||
return MakeErrorJson(Result, TEXT("Missing 'query' parameter"));
|
||||
}
|
||||
|
||||
const FString* PathFilter = Params.Find(TEXT("path"));
|
||||
FString PathFilter = Json->GetStringField(TEXT("path"));
|
||||
|
||||
int32 MaxResults = 50;
|
||||
if (const FString* M = Params.Find(TEXT("maxResults")))
|
||||
if (Json->HasField(TEXT("maxResults")))
|
||||
{
|
||||
MaxResults = FMath::Clamp(FCString::Atoi(**M), 1, 200);
|
||||
MaxResults = FMath::Clamp(FCString::Atoi(*Json->GetStringField(TEXT("maxResults"))), 1, 200);
|
||||
}
|
||||
|
||||
// Build a combined list of all searchable blueprints (regular + level)
|
||||
@@ -236,10 +232,10 @@ FString FBlueprintMCPServer::HandleSearch(const TMap<FString, FString>& Params)
|
||||
VarName = VS->GetVarName().ToString();
|
||||
}
|
||||
|
||||
bool bMatch = Title.Contains(*Query, ESearchCase::IgnoreCase) ||
|
||||
(!FuncName.IsEmpty() && FuncName.Contains(*Query, ESearchCase::IgnoreCase)) ||
|
||||
(!EventName.IsEmpty() && EventName.Contains(*Query, ESearchCase::IgnoreCase)) ||
|
||||
(!VarName.IsEmpty() && VarName.Contains(*Query, ESearchCase::IgnoreCase));
|
||||
bool bMatch = Title.Contains(Query, ESearchCase::IgnoreCase) ||
|
||||
(!FuncName.IsEmpty() && FuncName.Contains(Query, ESearchCase::IgnoreCase)) ||
|
||||
(!EventName.IsEmpty() && EventName.Contains(Query, ESearchCase::IgnoreCase)) ||
|
||||
(!VarName.IsEmpty() && VarName.Contains(Query, ESearchCase::IgnoreCase));
|
||||
|
||||
if (bMatch)
|
||||
{
|
||||
@@ -264,7 +260,7 @@ FString FBlueprintMCPServer::HandleSearch(const TMap<FString, FString>& Params)
|
||||
if (Results.Num() >= MaxResults) break;
|
||||
|
||||
FString Path = Asset.PackageName.ToString();
|
||||
if (PathFilter && !PathFilter->IsEmpty() && !Path.Contains(*PathFilter, ESearchCase::IgnoreCase))
|
||||
if (!PathFilter.IsEmpty() && !Path.Contains(PathFilter, ESearchCase::IgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -281,7 +277,7 @@ FString FBlueprintMCPServer::HandleSearch(const TMap<FString, FString>& Params)
|
||||
if (Results.Num() >= MaxResults) break;
|
||||
|
||||
FString Path = MapAsset.PackageName.ToString();
|
||||
if (PathFilter && !PathFilter->IsEmpty() && !Path.Contains(*PathFilter, ESearchCase::IgnoreCase))
|
||||
if (!PathFilter.IsEmpty() && !Path.Contains(PathFilter, ESearchCase::IgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -300,32 +296,30 @@ FString FBlueprintMCPServer::HandleSearch(const TMap<FString, FString>& Params)
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetStringField(TEXT("query"), *Query);
|
||||
Result->SetStringField(TEXT("query"), Query);
|
||||
Result->SetNumberField(TEXT("resultCount"), Results.Num());
|
||||
Result->SetArrayField(TEXT("results"), Results);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleTestSave — load a Blueprint and save it unmodified (diagnostic)
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleTestSave(const TMap<FString, FString>& Params)
|
||||
void FBlueprintMCPServer::HandleTestSave(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
const FString* Name = Params.Find(TEXT("name"));
|
||||
if (!Name || Name->IsEmpty())
|
||||
FString Name = Json->GetStringField(TEXT("name"));
|
||||
if (Name.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing 'name' query parameter"));
|
||||
return MakeErrorJson(Result, TEXT("Missing 'name' query parameter"));
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: test-save requested for '%s'"), **Name);
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: test-save requested for '%s'"), *Name);
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(*Name, LoadError);
|
||||
UBlueprint* BP = LoadBlueprintByName(Name, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: test-save — loaded '%s', GeneratedClass=%s"),
|
||||
@@ -335,30 +329,28 @@ FString FBlueprintMCPServer::HandleTestSave(const TMap<FString, FString>& Params
|
||||
// Attempt save with NO modifications
|
||||
bool bSaved = SaveBlueprintPackage(BP);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetStringField(TEXT("blueprint"), *Name);
|
||||
Result->SetStringField(TEXT("blueprint"), Name);
|
||||
Result->SetStringField(TEXT("packagePath"), BP->GetPackage()->GetName());
|
||||
Result->SetBoolField(TEXT("hasGeneratedClass"), BP->GeneratedClass != nullptr);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleFindReferences — find all Blueprints referencing an asset
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleFindReferences(const TMap<FString, FString>& Params)
|
||||
void FBlueprintMCPServer::HandleFindReferences(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
const FString* AssetPath = Params.Find(TEXT("assetPath"));
|
||||
if (!AssetPath || AssetPath->IsEmpty())
|
||||
FString AssetPath = Json->GetStringField(TEXT("assetPath"));
|
||||
if (AssetPath.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing 'assetPath' query parameter"));
|
||||
return MakeErrorJson(Result, TEXT("Missing 'assetPath' query parameter"));
|
||||
}
|
||||
|
||||
IAssetRegistry& Registry = *IAssetRegistry::Get();
|
||||
|
||||
TArray<FName> Referencers;
|
||||
Registry.GetReferencers(FName(**AssetPath), Referencers);
|
||||
Registry.GetReferencers(FName(*AssetPath), Referencers);
|
||||
|
||||
// Build set of known Blueprint package names for filtering
|
||||
TSet<FString> BlueprintPackages;
|
||||
@@ -382,36 +374,34 @@ FString FBlueprintMCPServer::HandleFindReferences(const TMap<FString, FString>&
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetStringField(TEXT("assetPath"), **AssetPath);
|
||||
Result->SetStringField(TEXT("assetPath"), AssetPath);
|
||||
Result->SetNumberField(TEXT("totalReferencers"), Referencers.Num());
|
||||
Result->SetNumberField(TEXT("blueprintReferencerCount"), BPRefs.Num());
|
||||
Result->SetArrayField(TEXT("blueprintReferencers"), BPRefs);
|
||||
Result->SetNumberField(TEXT("otherReferencerCount"), OtherRefs.Num());
|
||||
Result->SetArrayField(TEXT("otherReferencers"), OtherRefs);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleSearchByType — find all usages of a type across blueprints
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleSearchByType(const TMap<FString, FString>& Params)
|
||||
void FBlueprintMCPServer::HandleSearchByType(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
const FString* TypeNamePtr = Params.Find(TEXT("typeName"));
|
||||
if (!TypeNamePtr || TypeNamePtr->IsEmpty())
|
||||
FString TypeNameRaw = Json->GetStringField(TEXT("typeName"));
|
||||
if (TypeNameRaw.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing 'typeName' query parameter"));
|
||||
return MakeErrorJson(Result, TEXT("Missing 'typeName' query parameter"));
|
||||
}
|
||||
|
||||
FString TypeName = UrlDecode(*TypeNamePtr);
|
||||
const FString* Filter = Params.Find(TEXT("filter"));
|
||||
FString FilterStr = Filter ? UrlDecode(*Filter) : FString();
|
||||
FString TypeName = UrlDecode(TypeNameRaw);
|
||||
FString FilterRaw = Json->GetStringField(TEXT("filter"));
|
||||
FString FilterStr = FilterRaw.IsEmpty() ? FString() : UrlDecode(FilterRaw);
|
||||
|
||||
int32 MaxResults = 200;
|
||||
if (const FString* M = Params.Find(TEXT("maxResults")))
|
||||
if (Json->HasField(TEXT("maxResults")))
|
||||
{
|
||||
MaxResults = FMath::Clamp(FCString::Atoi(**M), 1, 500);
|
||||
MaxResults = FMath::Clamp(FCString::Atoi(*Json->GetStringField(TEXT("maxResults"))), 1, 500);
|
||||
}
|
||||
|
||||
// Strip F/E/U prefix for comparison
|
||||
@@ -636,9 +626,7 @@ FString FBlueprintMCPServer::HandleSearchByType(const TMap<FString, FString>& Pa
|
||||
SearchOneBlueprint(MapName, Path, LevelBP, true);
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetStringField(TEXT("typeName"), TypeName);
|
||||
Result->SetNumberField(TEXT("resultCount"), Results.Num());
|
||||
Result->SetArrayField(TEXT("results"), Results);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
@@ -254,18 +254,12 @@ bool FBlueprintMCPServer::LoadSnapshotFromDisk(const FString& SnapshotId, FGraph
|
||||
// HandleSnapshotGraph
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleSnapshotGraph(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleSnapshotGraph(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
if (BlueprintName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: blueprint"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: blueprint"));
|
||||
}
|
||||
|
||||
FString GraphFilter;
|
||||
@@ -276,7 +270,7 @@ FString FBlueprintMCPServer::HandleSnapshotGraph(const FString& Body)
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Creating snapshot for blueprint '%s'"), *BlueprintName);
|
||||
@@ -305,7 +299,7 @@ FString FBlueprintMCPServer::HandleSnapshotGraph(const FString& Body)
|
||||
|
||||
if (GraphsToCapture.Num() == 0 && !GraphFilter.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Graph '%s' not found in blueprint '%s'"), *GraphFilter, *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Graph '%s' not found in blueprint '%s'"), *GraphFilter, *BlueprintName));
|
||||
}
|
||||
|
||||
int32 TotalConnections = 0;
|
||||
@@ -336,32 +330,24 @@ FString FBlueprintMCPServer::HandleSnapshotGraph(const FString& Body)
|
||||
*Snapshot.SnapshotId, GraphsToCapture.Num(), TotalConnections);
|
||||
|
||||
// Build response
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetStringField(TEXT("status"), TEXT("ok"));
|
||||
Result->SetStringField(TEXT("snapshotId"), Snapshot.SnapshotId);
|
||||
Result->SetStringField(TEXT("blueprint"), BP->GetName());
|
||||
Result->SetArrayField(TEXT("graphs"), GraphSummaries);
|
||||
Result->SetNumberField(TEXT("totalConnections"), TotalConnections);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleDiffGraph
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleDiffGraph(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleDiffGraph(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString SnapshotId = Json->GetStringField(TEXT("snapshotId"));
|
||||
if (BlueprintName.IsEmpty() || SnapshotId.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, snapshotId"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, snapshotId"));
|
||||
}
|
||||
|
||||
FString GraphFilter;
|
||||
@@ -374,7 +360,7 @@ FString FBlueprintMCPServer::HandleDiffGraph(const FString& Body)
|
||||
{
|
||||
if (!LoadSnapshotFromDisk(SnapshotId, LoadedSnapshot))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Snapshot '%s' not found in memory or on disk"), *SnapshotId));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Snapshot '%s' not found in memory or on disk"), *SnapshotId));
|
||||
}
|
||||
SnapshotPtr = &LoadedSnapshot;
|
||||
}
|
||||
@@ -384,7 +370,7 @@ FString FBlueprintMCPServer::HandleDiffGraph(const FString& Body)
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Diffing blueprint '%s' against snapshot '%s'"), *BlueprintName, *SnapshotId);
|
||||
@@ -545,7 +531,6 @@ FString FBlueprintMCPServer::HandleDiffGraph(const FString& Body)
|
||||
}
|
||||
|
||||
// Build result
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetStringField(TEXT("status"), TEXT("ok"));
|
||||
Result->SetStringField(TEXT("blueprint"), BP->GetName());
|
||||
Result->SetStringField(TEXT("snapshotId"), SnapshotId);
|
||||
@@ -563,27 +548,19 @@ FString FBlueprintMCPServer::HandleDiffGraph(const FString& Body)
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Diff complete — %d severed, %d new, %d type changes, %d missing nodes"),
|
||||
SeveredArr.Num(), NewConnsArr.Num(), TypeChangesArr.Num(), MissingNodesArr.Num());
|
||||
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleRestoreGraph
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleRestoreGraph(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleRestoreGraph(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString SnapshotId = Json->GetStringField(TEXT("snapshotId"));
|
||||
if (BlueprintName.IsEmpty() || SnapshotId.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, snapshotId"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, snapshotId"));
|
||||
}
|
||||
|
||||
FString GraphFilter;
|
||||
@@ -602,7 +579,7 @@ FString FBlueprintMCPServer::HandleRestoreGraph(const FString& Body)
|
||||
{
|
||||
if (!LoadSnapshotFromDisk(SnapshotId, LoadedSnapshot))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Snapshot '%s' not found in memory or on disk"), *SnapshotId));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Snapshot '%s' not found in memory or on disk"), *SnapshotId));
|
||||
}
|
||||
SnapshotPtr = &LoadedSnapshot;
|
||||
}
|
||||
@@ -612,7 +589,7 @@ FString FBlueprintMCPServer::HandleRestoreGraph(const FString& Body)
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Restoring connections from snapshot '%s' for blueprint '%s' (dryRun=%s)"),
|
||||
@@ -775,28 +752,20 @@ FString FBlueprintMCPServer::HandleRestoreGraph(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Restore complete — %d reconnected, %d failed, saved=%s"),
|
||||
Reconnected, Failed, bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetStringField(TEXT("status"), TEXT("ok"));
|
||||
Result->SetNumberField(TEXT("reconnected"), Reconnected);
|
||||
Result->SetNumberField(TEXT("failed"), Failed);
|
||||
Result->SetArrayField(TEXT("details"), DetailsArr);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
Result->SetBoolField(TEXT("dryRun"), bDryRun);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleFindDisconnectedPins
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleFindDisconnectedPins(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleFindDisconnectedPins(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName;
|
||||
Json->TryGetStringField(TEXT("blueprint"), BlueprintName);
|
||||
|
||||
@@ -808,7 +777,7 @@ FString FBlueprintMCPServer::HandleFindDisconnectedPins(const FString& Body)
|
||||
|
||||
if (BlueprintName.IsEmpty() && PathFilter.IsEmpty() && SnapshotId.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Provide at least one of: blueprint, filter, or snapshotId"));
|
||||
return MakeErrorJson(Result, TEXT("Provide at least one of: blueprint, filter, or snapshotId"));
|
||||
}
|
||||
|
||||
// Optionally load snapshot for definite-break detection
|
||||
@@ -1086,7 +1055,6 @@ FString FBlueprintMCPServer::HandleFindDisconnectedPins(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: FindDisconnectedPins complete — %d HIGH, %d MEDIUM, %d total across %d blueprints"),
|
||||
HighCount, MediumCount, ResultsArr.Num(), BlueprintsScanned);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetArrayField(TEXT("results"), ResultsArr);
|
||||
|
||||
TSharedRef<FJsonObject> Summary = MakeShared<FJsonObject>();
|
||||
@@ -1095,26 +1063,18 @@ FString FBlueprintMCPServer::HandleFindDisconnectedPins(const FString& Body)
|
||||
Summary->SetNumberField(TEXT("total"), ResultsArr.Num());
|
||||
Summary->SetNumberField(TEXT("blueprintsScanned"), BlueprintsScanned);
|
||||
Result->SetObjectField(TEXT("summary"), Summary);
|
||||
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleAnalyzeRebuildImpact
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleAnalyzeRebuildImpact(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleAnalyzeRebuildImpact(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString ModuleName = Json->GetStringField(TEXT("moduleName"));
|
||||
if (ModuleName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: moduleName"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: moduleName"));
|
||||
}
|
||||
|
||||
// Optional struct name filter
|
||||
@@ -1359,7 +1319,6 @@ FString FBlueprintMCPServer::HandleAnalyzeRebuildImpact(const FString& Body)
|
||||
AffectedBlueprints.Num(), TotalBreakMakeNodes, TotalConnectionsAtRisk);
|
||||
|
||||
// Build response
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetStringField(TEXT("moduleName"), ModuleName);
|
||||
Result->SetArrayField(TEXT("typesFound"), TypesFoundArr);
|
||||
|
||||
@@ -1384,6 +1343,4 @@ FString FBlueprintMCPServer::HandleAnalyzeRebuildImpact(const FString& Body)
|
||||
Summary->SetNumberField(TEXT("totalBreakMakeNodes"), TotalBreakMakeNodes);
|
||||
Summary->SetNumberField(TEXT("totalConnectionsAtRisk"), TotalConnectionsAtRisk);
|
||||
Result->SetObjectField(TEXT("summary"), Summary);
|
||||
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
@@ -20,18 +20,12 @@
|
||||
// HandleCreateStruct — create a new UserDefinedStruct asset
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleCreateStruct(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleCreateStruct(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString AssetPath = Json->GetStringField(TEXT("assetPath"));
|
||||
if (AssetPath.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: assetPath (e.g. '/Game/DataTypes/S_MyStruct')"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: assetPath (e.g. '/Game/DataTypes/S_MyStruct')"));
|
||||
}
|
||||
|
||||
// Split path into package path and asset name
|
||||
@@ -44,12 +38,12 @@ FString FBlueprintMCPServer::HandleCreateStruct(const FString& Body)
|
||||
}
|
||||
else
|
||||
{
|
||||
return MakeErrorJson(TEXT("assetPath must be a full path (e.g. '/Game/DataTypes/S_MyStruct')"));
|
||||
return MakeErrorJson(Result, TEXT("assetPath must be a full path (e.g. '/Game/DataTypes/S_MyStruct')"));
|
||||
}
|
||||
|
||||
if (AssetName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid asset name in assetPath"));
|
||||
return MakeErrorJson(Result, TEXT("Invalid asset name in assetPath"));
|
||||
}
|
||||
|
||||
// Check if asset already exists
|
||||
@@ -57,7 +51,7 @@ FString FBlueprintMCPServer::HandleCreateStruct(const FString& Body)
|
||||
FAssetData ExistingAsset = ARM.Get().GetAssetByObjectPath(FSoftObjectPath(AssetPath + TEXT(".") + AssetName));
|
||||
if (ExistingAsset.IsValid())
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Asset already exists at '%s'"), *AssetPath));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Asset already exists at '%s'"), *AssetPath));
|
||||
}
|
||||
|
||||
// Create the struct using the AssetTools factory
|
||||
@@ -69,13 +63,13 @@ FString FBlueprintMCPServer::HandleCreateStruct(const FString& Body)
|
||||
|
||||
if (!NewAsset)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Failed to create UserDefinedStruct asset"));
|
||||
return MakeErrorJson(Result, TEXT("Failed to create UserDefinedStruct asset"));
|
||||
}
|
||||
|
||||
UUserDefinedStruct* NewStruct = Cast<UUserDefinedStruct>(NewAsset);
|
||||
if (!NewStruct)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Created asset is not a UserDefinedStruct"));
|
||||
return MakeErrorJson(Result, TEXT("Created asset is not a UserDefinedStruct"));
|
||||
}
|
||||
|
||||
// Add properties if specified
|
||||
@@ -139,31 +133,23 @@ FString FBlueprintMCPServer::HandleCreateStruct(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Created UserDefinedStruct '%s' with %d properties, save %s"),
|
||||
*AssetPath, PropsAdded, bSaved ? TEXT("succeeded") : TEXT("failed"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("assetPath"), AssetPath);
|
||||
Result->SetStringField(TEXT("assetName"), AssetName);
|
||||
Result->SetNumberField(TEXT("propertiesAdded"), PropsAdded);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleCreateEnum — create a new UserDefinedEnum asset
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleCreateEnum(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleCreateEnum(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString AssetPath = Json->GetStringField(TEXT("assetPath"));
|
||||
if (AssetPath.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: assetPath (e.g. '/Game/DataTypes/E_MyEnum')"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: assetPath (e.g. '/Game/DataTypes/E_MyEnum')"));
|
||||
}
|
||||
|
||||
// Split path
|
||||
@@ -176,19 +162,19 @@ FString FBlueprintMCPServer::HandleCreateEnum(const FString& Body)
|
||||
}
|
||||
else
|
||||
{
|
||||
return MakeErrorJson(TEXT("assetPath must be a full path (e.g. '/Game/DataTypes/E_MyEnum')"));
|
||||
return MakeErrorJson(Result, TEXT("assetPath must be a full path (e.g. '/Game/DataTypes/E_MyEnum')"));
|
||||
}
|
||||
|
||||
if (AssetName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid asset name in assetPath"));
|
||||
return MakeErrorJson(Result, TEXT("Invalid asset name in assetPath"));
|
||||
}
|
||||
|
||||
// Get values
|
||||
const TArray<TSharedPtr<FJsonValue>>* ValuesArray = nullptr;
|
||||
if (!Json->TryGetArrayField(TEXT("values"), ValuesArray) || !ValuesArray || ValuesArray->Num() == 0)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing or empty required field: values (array of strings)"));
|
||||
return MakeErrorJson(Result, TEXT("Missing or empty required field: values (array of strings)"));
|
||||
}
|
||||
|
||||
TArray<FString> EnumValues;
|
||||
@@ -199,7 +185,7 @@ FString FBlueprintMCPServer::HandleCreateEnum(const FString& Body)
|
||||
}
|
||||
if (EnumValues.Num() == 0)
|
||||
{
|
||||
return MakeErrorJson(TEXT("No valid enum values provided"));
|
||||
return MakeErrorJson(Result, TEXT("No valid enum values provided"));
|
||||
}
|
||||
|
||||
// Create the enum using AssetTools
|
||||
@@ -211,13 +197,13 @@ FString FBlueprintMCPServer::HandleCreateEnum(const FString& Body)
|
||||
|
||||
if (!NewAsset)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Failed to create UserDefinedEnum asset"));
|
||||
return MakeErrorJson(Result, TEXT("Failed to create UserDefinedEnum asset"));
|
||||
}
|
||||
|
||||
UUserDefinedEnum* NewEnum = Cast<UUserDefinedEnum>(NewAsset);
|
||||
if (!NewEnum)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Created asset is not a UserDefinedEnum"));
|
||||
return MakeErrorJson(Result, TEXT("Created asset is not a UserDefinedEnum"));
|
||||
}
|
||||
|
||||
// Add enum values — UUserDefinedEnum starts with a MAX value.
|
||||
@@ -241,34 +227,26 @@ FString FBlueprintMCPServer::HandleCreateEnum(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Created UserDefinedEnum '%s' with %d values, save %s"),
|
||||
*AssetPath, EnumValues.Num(), bSaved ? TEXT("succeeded") : TEXT("failed"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("assetPath"), AssetPath);
|
||||
Result->SetStringField(TEXT("assetName"), AssetName);
|
||||
Result->SetNumberField(TEXT("valueCount"), EnumValues.Num());
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleAddStructProperty — add a property to UserDefinedStruct
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleAddStructProperty(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleAddStructProperty(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString AssetPath = Json->GetStringField(TEXT("assetPath"));
|
||||
FString PropName = Json->GetStringField(TEXT("name"));
|
||||
FString PropType = Json->GetStringField(TEXT("type"));
|
||||
|
||||
if (AssetPath.IsEmpty() || PropName.IsEmpty() || PropType.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: assetPath, name, type"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: assetPath, name, type"));
|
||||
}
|
||||
|
||||
// Find the struct
|
||||
@@ -281,7 +259,7 @@ FString FBlueprintMCPServer::HandleAddStructProperty(const FString& Body)
|
||||
}
|
||||
if (!Struct)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("UserDefinedStruct not found at '%s'"), *AssetPath));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("UserDefinedStruct not found at '%s'"), *AssetPath));
|
||||
}
|
||||
|
||||
// Resolve type
|
||||
@@ -289,7 +267,7 @@ FString FBlueprintMCPServer::HandleAddStructProperty(const FString& Body)
|
||||
FString TypeError;
|
||||
if (!ResolveTypeFromString(PropType, PinType, TypeError))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Cannot resolve type '%s': %s"), *PropType, *TypeError));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Cannot resolve type '%s': %s"), *PropType, *TypeError));
|
||||
}
|
||||
|
||||
// Snapshot existing GUIDs so we can find the newly added one
|
||||
@@ -302,7 +280,7 @@ FString FBlueprintMCPServer::HandleAddStructProperty(const FString& Body)
|
||||
bool bAdded = FStructureEditorUtils::AddVariable(Struct, PinType);
|
||||
if (!bAdded)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Failed to add property to struct"));
|
||||
return MakeErrorJson(Result, TEXT("Failed to add property to struct"));
|
||||
}
|
||||
|
||||
// Find the new variable by diffing GUID sets and rename it
|
||||
@@ -325,33 +303,25 @@ FString FBlueprintMCPServer::HandleAddStructProperty(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Added property '%s' (%s) to struct '%s', save %s"),
|
||||
*PropName, *PropType, *AssetPath, bSaved ? TEXT("succeeded") : TEXT("failed"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("assetPath"), AssetPath);
|
||||
Result->SetStringField(TEXT("propertyName"), PropName);
|
||||
Result->SetStringField(TEXT("propertyType"), PropType);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleRemoveStructProperty — remove a property from UserDefinedStruct
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleRemoveStructProperty(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleRemoveStructProperty(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString AssetPath = Json->GetStringField(TEXT("assetPath"));
|
||||
FString PropName = Json->GetStringField(TEXT("name"));
|
||||
|
||||
if (AssetPath.IsEmpty() || PropName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: assetPath, name"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: assetPath, name"));
|
||||
}
|
||||
|
||||
// Find the struct
|
||||
@@ -363,7 +333,7 @@ FString FBlueprintMCPServer::HandleRemoveStructProperty(const FString& Body)
|
||||
}
|
||||
if (!Struct)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("UserDefinedStruct not found at '%s'"), *AssetPath));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("UserDefinedStruct not found at '%s'"), *AssetPath));
|
||||
}
|
||||
|
||||
// Find the property GUID by name
|
||||
@@ -387,16 +357,15 @@ FString FBlueprintMCPServer::HandleRemoveStructProperty(const FString& Body)
|
||||
{
|
||||
AvailProps.Add(MakeShared<FJsonValueString>(Var.FriendlyName));
|
||||
}
|
||||
TSharedRef<FJsonObject> E = MakeShared<FJsonObject>();
|
||||
E->SetStringField(TEXT("error"), FString::Printf(TEXT("Property '%s' not found in struct '%s'"), *PropName, *AssetPath));
|
||||
E->SetArrayField(TEXT("availableProperties"), AvailProps);
|
||||
return JsonToString(E);
|
||||
MakeErrorJson(Result, FString::Printf(TEXT("Property '%s' not found in struct '%s'"), *PropName, *AssetPath));
|
||||
Result->SetArrayField(TEXT("availableProperties"), AvailProps);
|
||||
return;
|
||||
}
|
||||
|
||||
bool bRemoved = FStructureEditorUtils::RemoveVariable(Struct, TargetGuid);
|
||||
if (!bRemoved)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Failed to remove property '%s'"), *PropName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Failed to remove property '%s'"), *PropName));
|
||||
}
|
||||
|
||||
// Save
|
||||
@@ -409,10 +378,8 @@ FString FBlueprintMCPServer::HandleRemoveStructProperty(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Removed property '%s' from struct '%s', save %s"),
|
||||
*PropName, *AssetPath, bSaved ? TEXT("succeeded") : TEXT("failed"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("assetPath"), AssetPath);
|
||||
Result->SetStringField(TEXT("removedProperty"), PropName);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
@@ -175,18 +175,12 @@ static TSharedRef<FJsonObject> ValidateSingleBlueprint(UBlueprint* BP, const FSt
|
||||
// HandleValidateBlueprint — compile without saving, report errors + captured log messages
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleValidateBlueprint(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleValidateBlueprint(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
if (BlueprintName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: blueprint"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: blueprint"));
|
||||
}
|
||||
|
||||
// Load Blueprint
|
||||
@@ -194,36 +188,25 @@ FString FBlueprintMCPServer::HandleValidateBlueprint(const FString& Body)
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Validating blueprint '%s'"), *BlueprintName);
|
||||
|
||||
TSharedRef<FJsonObject> Result = ValidateSingleBlueprint(BP, BlueprintName);
|
||||
return JsonToString(Result);
|
||||
TSharedRef<FJsonObject> ValidationResult = ValidateSingleBlueprint(BP, BlueprintName);
|
||||
CopyJsonFields(&*ValidationResult, Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleValidateAllBlueprints — bulk validation
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleValidateAllBlueprints(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleValidateAllBlueprints(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
// Body is optional — empty body means validate all
|
||||
|
||||
FString Filter;
|
||||
bool bCountOnly = false;
|
||||
int32 Offset = 0;
|
||||
int32 Limit = 0;
|
||||
|
||||
if (Json.IsValid())
|
||||
{
|
||||
Filter = Json->GetStringField(TEXT("filter"));
|
||||
bCountOnly = Json->GetBoolField(TEXT("countOnly"));
|
||||
Offset = (int32)Json->GetNumberField(TEXT("offset"));
|
||||
Limit = (int32)Json->GetNumberField(TEXT("limit"));
|
||||
}
|
||||
FString Filter = Json->GetStringField(TEXT("filter"));
|
||||
bool bCountOnly = Json->GetBoolField(TEXT("countOnly"));
|
||||
int32 Offset = (int32)Json->GetNumberField(TEXT("offset"));
|
||||
int32 Limit = (int32)Json->GetNumberField(TEXT("limit"));
|
||||
|
||||
// First pass: collect matching asset indices (string comparisons only, no GetAsset())
|
||||
TArray<int32> MatchingIndices;
|
||||
@@ -247,13 +230,12 @@ FString FBlueprintMCPServer::HandleValidateAllBlueprints(const FString& Body)
|
||||
// countOnly: return count without compiling anything
|
||||
if (bCountOnly)
|
||||
{
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetNumberField(TEXT("totalMatching"), TotalMatching);
|
||||
if (!Filter.IsEmpty())
|
||||
{
|
||||
Result->SetStringField(TEXT("filter"), Filter);
|
||||
}
|
||||
return JsonToString(Result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute range
|
||||
@@ -284,13 +266,13 @@ FString FBlueprintMCPServer::HandleValidateAllBlueprints(const FString& Body)
|
||||
|
||||
TotalChecked++;
|
||||
|
||||
TSharedRef<FJsonObject> Result = ValidateSingleBlueprint(BP, AssetName);
|
||||
TSharedRef<FJsonObject> ValidationResult = ValidateSingleBlueprint(BP, AssetName);
|
||||
|
||||
bool bValid = Result->GetBoolField(TEXT("isValid"));
|
||||
int32 Errors = (int32)Result->GetNumberField(TEXT("errorCount"));
|
||||
int32 Warnings = (int32)Result->GetNumberField(TEXT("warningCount"));
|
||||
bool bValid = ValidationResult->GetBoolField(TEXT("isValid"));
|
||||
int32 Errors = (int32)ValidationResult->GetNumberField(TEXT("errorCount"));
|
||||
int32 Warnings = (int32)ValidationResult->GetNumberField(TEXT("warningCount"));
|
||||
|
||||
if (Result->HasField(TEXT("compileWarning")))
|
||||
if (ValidationResult->HasField(TEXT("compileWarning")))
|
||||
{
|
||||
TotalCrashed++;
|
||||
}
|
||||
@@ -303,8 +285,8 @@ FString FBlueprintMCPServer::HandleValidateAllBlueprints(const FString& Body)
|
||||
{
|
||||
TotalFailed++;
|
||||
// Include path for context in bulk results
|
||||
Result->SetStringField(TEXT("path"), PackagePath);
|
||||
FailedArr.Add(MakeShared<FJsonValueObject>(Result));
|
||||
ValidationResult->SetStringField(TEXT("path"), PackagePath);
|
||||
FailedArr.Add(MakeShared<FJsonValueObject>(ValidationResult));
|
||||
}
|
||||
|
||||
// Log progress every 50 blueprints
|
||||
@@ -318,7 +300,6 @@ FString FBlueprintMCPServer::HandleValidateAllBlueprints(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Bulk validation complete — %d checked, %d passed, %d failed, %d crashed"),
|
||||
TotalChecked, TotalPassed, TotalFailed, TotalCrashed);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetNumberField(TEXT("totalMatching"), TotalMatching);
|
||||
Result->SetNumberField(TEXT("totalChecked"), TotalChecked);
|
||||
Result->SetNumberField(TEXT("totalPassed"), TotalPassed);
|
||||
@@ -332,5 +313,4 @@ FString FBlueprintMCPServer::HandleValidateAllBlueprints(const FString& Body)
|
||||
{
|
||||
Result->SetStringField(TEXT("filter"), Filter);
|
||||
}
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
@@ -15,14 +15,8 @@
|
||||
// HandleChangeVariableType — change a Blueprint member variable's type
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleChangeVariableType(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleChangeVariableType(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString VariableName = Json->GetStringField(TEXT("variable"));
|
||||
FString NewTypeName = Json->GetStringField(TEXT("newType"));
|
||||
@@ -34,7 +28,7 @@ FString FBlueprintMCPServer::HandleChangeVariableType(const FString& Body)
|
||||
|
||||
if (BlueprintName.IsEmpty() || VariableName.IsEmpty() || NewTypeName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, variable, newType"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, variable, newType"));
|
||||
}
|
||||
|
||||
// Load Blueprint
|
||||
@@ -42,7 +36,7 @@ FString FBlueprintMCPServer::HandleChangeVariableType(const FString& Body)
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Verify variable exists
|
||||
@@ -57,7 +51,7 @@ FString FBlueprintMCPServer::HandleChangeVariableType(const FString& Body)
|
||||
}
|
||||
if (!bVarFound)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Variable '%s' not found in Blueprint '%s'"), *VariableName, *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Variable '%s' not found in Blueprint '%s'"), *VariableName, *BlueprintName));
|
||||
}
|
||||
|
||||
// Build the new pin type using shared resolver
|
||||
@@ -75,7 +69,7 @@ FString FBlueprintMCPServer::HandleChangeVariableType(const FString& Body)
|
||||
FString TypeError;
|
||||
if (!ResolveTypeFromString(ResolveInput, NewPinType, TypeError))
|
||||
{
|
||||
return MakeErrorJson(TypeError);
|
||||
return MakeErrorJson(Result, TypeError);
|
||||
}
|
||||
|
||||
// Derive typeCategory from the resolved pin type for the response
|
||||
@@ -170,7 +164,6 @@ FString FBlueprintMCPServer::HandleChangeVariableType(const FString& Body)
|
||||
|
||||
if (bDryRun)
|
||||
{
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("dryRun"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("variable"), VariableName);
|
||||
@@ -178,7 +171,7 @@ FString FBlueprintMCPServer::HandleChangeVariableType(const FString& Body)
|
||||
Result->SetStringField(TEXT("typeCategory"), TypeCategory);
|
||||
Result->SetNumberField(TEXT("affectedNodeCount"), AffectedNodes.Num());
|
||||
Result->SetArrayField(TEXT("affectedNodes"), AffectedNodes);
|
||||
return JsonToString(Result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Directly modify the variable type in the description array.
|
||||
@@ -211,7 +204,6 @@ FString FBlueprintMCPServer::HandleChangeVariableType(const FString& Body)
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("variable"), VariableName);
|
||||
@@ -220,28 +212,21 @@ FString FBlueprintMCPServer::HandleChangeVariableType(const FString& Body)
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
Result->SetObjectField(TEXT("updatedVariable"), UpdatedVar);
|
||||
Result->SetArrayField(TEXT("affectedNodes"), AffectedNodes);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleAddVariable — add a new member variable to a Blueprint
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleAddVariable(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleAddVariable(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString VariableName = Json->GetStringField(TEXT("variableName"));
|
||||
FString VariableType = Json->GetStringField(TEXT("variableType"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || VariableName.IsEmpty() || VariableType.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, variableName, variableType"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, variableName, variableType"));
|
||||
}
|
||||
|
||||
FString Category;
|
||||
@@ -267,7 +252,7 @@ FString FBlueprintMCPServer::HandleAddVariable(const FString& Body)
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Check for duplicate variable name
|
||||
@@ -276,7 +261,7 @@ FString FBlueprintMCPServer::HandleAddVariable(const FString& Body)
|
||||
{
|
||||
if (Var.VarName == VarFName)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Variable '%s' already exists in Blueprint '%s'"), *VariableName, *BlueprintName));
|
||||
}
|
||||
}
|
||||
@@ -286,7 +271,7 @@ FString FBlueprintMCPServer::HandleAddVariable(const FString& Body)
|
||||
FString TypeError;
|
||||
if (!ResolveTypeFromString(VariableType, PinType, TypeError))
|
||||
{
|
||||
return MakeErrorJson(TypeError);
|
||||
return MakeErrorJson(Result, TypeError);
|
||||
}
|
||||
|
||||
// Set container type for arrays
|
||||
@@ -302,7 +287,7 @@ FString FBlueprintMCPServer::HandleAddVariable(const FString& Body)
|
||||
bool bSuccess = FBlueprintEditorUtils::AddMemberVariable(BP, VarFName, PinType, DefaultValue);
|
||||
if (!bSuccess)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("FBlueprintEditorUtils::AddMemberVariable failed for '%s'"), *VariableName));
|
||||
}
|
||||
|
||||
@@ -318,7 +303,6 @@ FString FBlueprintMCPServer::HandleAddVariable(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Added variable '%s' to '%s' (saved: %s)"),
|
||||
*VariableName, *BlueprintName, bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("variableName"), VariableName);
|
||||
@@ -329,27 +313,20 @@ FString FBlueprintMCPServer::HandleAddVariable(const FString& Body)
|
||||
}
|
||||
Result->SetBoolField(TEXT("isArray"), bIsArray);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleRemoveVariable — remove a member variable from a Blueprint
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleRemoveVariable(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleRemoveVariable(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString VariableName = Json->GetStringField(TEXT("variableName"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || VariableName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, variableName"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, variableName"));
|
||||
}
|
||||
|
||||
// Load Blueprint
|
||||
@@ -357,7 +334,7 @@ FString FBlueprintMCPServer::HandleRemoveVariable(const FString& Body)
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Find variable by name (case-insensitive)
|
||||
@@ -382,11 +359,10 @@ FString FBlueprintMCPServer::HandleRemoveVariable(const FString& Body)
|
||||
AvailVars.Add(MakeShared<FJsonValueString>(Var.VarName.ToString()));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> ErrorResult = MakeShared<FJsonObject>();
|
||||
ErrorResult->SetStringField(TEXT("error"), FString::Printf(
|
||||
MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Variable '%s' not found in Blueprint '%s'"), *VariableName, *BlueprintName));
|
||||
ErrorResult->SetArrayField(TEXT("availableVariables"), AvailVars);
|
||||
return JsonToString(ErrorResult);
|
||||
Result->SetArrayField(TEXT("availableVariables"), AvailVars);
|
||||
return;
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Removing variable '%s' from Blueprint '%s'"),
|
||||
@@ -401,32 +377,24 @@ FString FBlueprintMCPServer::HandleRemoveVariable(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Removed variable '%s' from '%s' (saved: %s)"),
|
||||
*VariableName, *BlueprintName, bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("variableName"), VariableName);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleSetVariableMetadata — set variable properties (category, tooltip, replication, etc.)
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleSetVariableMetadata(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleSetVariableMetadata(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString VariableName = Json->GetStringField(TEXT("variable"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || VariableName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, variable"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, variable"));
|
||||
}
|
||||
|
||||
// Load Blueprint
|
||||
@@ -434,7 +402,7 @@ FString FBlueprintMCPServer::HandleSetVariableMetadata(const FString& Body)
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(LoadError);
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Find the variable
|
||||
@@ -456,11 +424,10 @@ FString FBlueprintMCPServer::HandleSetVariableMetadata(const FString& Body)
|
||||
{
|
||||
AvailableVars.Add(MakeShared<FJsonValueString>(Var.VarName.ToString()));
|
||||
}
|
||||
TSharedRef<FJsonObject> ErrResult = MakeShared<FJsonObject>();
|
||||
ErrResult->SetStringField(TEXT("error"), FString::Printf(
|
||||
MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Variable '%s' not found in Blueprint '%s'"), *VariableName, *BlueprintName));
|
||||
ErrResult->SetArrayField(TEXT("availableVariables"), AvailableVars);
|
||||
return JsonToString(ErrResult);
|
||||
Result->SetArrayField(TEXT("availableVariables"), AvailableVars);
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> Changes;
|
||||
@@ -521,7 +488,7 @@ FString FBlueprintMCPServer::HandleSetVariableMetadata(const FString& Body)
|
||||
}
|
||||
else
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Invalid replication value '%s'. Valid: none, replicated, repNotify"), *ReplicationStr));
|
||||
}
|
||||
|
||||
@@ -590,7 +557,7 @@ FString FBlueprintMCPServer::HandleSetVariableMetadata(const FString& Body)
|
||||
}
|
||||
else
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Invalid editability value '%s'. Valid: editAnywhere, editDefaultsOnly, editInstanceOnly, none"),
|
||||
*Editability));
|
||||
}
|
||||
@@ -603,7 +570,7 @@ FString FBlueprintMCPServer::HandleSetVariableMetadata(const FString& Body)
|
||||
|
||||
if (Changes.Num() == 0)
|
||||
{
|
||||
return MakeErrorJson(TEXT("No metadata fields specified. Provide at least one of: category, tooltip, replication, exposeOnSpawn, isPrivate, editability"));
|
||||
return MakeErrorJson(Result, TEXT("No metadata fields specified. Provide at least one of: category, tooltip, replication, exposeOnSpawn, isPrivate, editability"));
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: SetVariableMetadata on '%s.%s' — %d field(s) changed"),
|
||||
@@ -612,11 +579,9 @@ FString FBlueprintMCPServer::HandleSetVariableMetadata(const FString& Body)
|
||||
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(BP);
|
||||
bool bSaved = SaveBlueprintPackage(BP);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("variable"), VariableName);
|
||||
Result->SetArrayField(TEXT("changes"), Changes);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
@@ -210,6 +210,20 @@ FString FBlueprintMCPServer::MakeErrorJson(const FString& Message)
|
||||
return JsonToString(E);
|
||||
}
|
||||
|
||||
void FBlueprintMCPServer::MakeErrorJson(FJsonObject* Result, const FString& Message)
|
||||
{
|
||||
Result->Values.Empty();
|
||||
Result->SetStringField(TEXT("error"), Message);
|
||||
}
|
||||
|
||||
void FBlueprintMCPServer::CopyJsonFields(const FJsonObject* Source, FJsonObject* Dest)
|
||||
{
|
||||
for (const auto& KV : Source->Values)
|
||||
{
|
||||
Dest->SetField(KV.Key, KV.Value);
|
||||
}
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::UrlDecode(const FString& EncodedString)
|
||||
{
|
||||
FString Result;
|
||||
@@ -547,9 +561,15 @@ bool FBlueprintMCPServer::Start(int32 InPort, bool bEditorMode)
|
||||
FHttpRequestHandler::CreateLambda(
|
||||
[this](const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete)
|
||||
{
|
||||
FString Resp = HandleList(Request.QueryParams);
|
||||
TSharedRef<FJsonObject> Params = MakeShared<FJsonObject>();
|
||||
for (const auto& KV : Request.QueryParams)
|
||||
{
|
||||
Params->SetStringField(KV.Key, KV.Value);
|
||||
}
|
||||
TSharedRef<FJsonObject> ListResult = MakeShared<FJsonObject>();
|
||||
HandleList(&*Params, &*ListResult);
|
||||
TUniquePtr<FHttpServerResponse> R = FHttpServerResponse::Create(
|
||||
Resp, TEXT("application/json"));
|
||||
JsonToString(ListResult), TEXT("application/json"));
|
||||
OnComplete(MoveTemp(R));
|
||||
return true;
|
||||
}));
|
||||
@@ -693,9 +713,15 @@ bool FBlueprintMCPServer::Start(int32 InPort, bool bEditorMode)
|
||||
FHttpRequestHandler::CreateLambda(
|
||||
[this](const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete)
|
||||
{
|
||||
FString Resp = HandleListMaterials(Request.QueryParams);
|
||||
TSharedRef<FJsonObject> Params = MakeShared<FJsonObject>();
|
||||
for (const auto& KV : Request.QueryParams)
|
||||
{
|
||||
Params->SetStringField(KV.Key, KV.Value);
|
||||
}
|
||||
TSharedRef<FJsonObject> ListResult = MakeShared<FJsonObject>();
|
||||
HandleListMaterials(&*Params, &*ListResult);
|
||||
TUniquePtr<FHttpServerResponse> R = FHttpServerResponse::Create(
|
||||
Resp, TEXT("application/json"));
|
||||
JsonToString(ListResult), TEXT("application/json"));
|
||||
OnComplete(MoveTemp(R));
|
||||
return true;
|
||||
}));
|
||||
@@ -743,9 +769,15 @@ bool FBlueprintMCPServer::Start(int32 InPort, bool bEditorMode)
|
||||
FHttpRequestHandler::CreateLambda(
|
||||
[this](const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete)
|
||||
{
|
||||
FString Resp = HandleListMaterialFunctions(Request.QueryParams);
|
||||
TSharedRef<FJsonObject> Params = MakeShared<FJsonObject>();
|
||||
for (const auto& KV : Request.QueryParams)
|
||||
{
|
||||
Params->SetStringField(KV.Key, KV.Value);
|
||||
}
|
||||
TSharedRef<FJsonObject> ListResult = MakeShared<FJsonObject>();
|
||||
HandleListMaterialFunctions(&*Params, &*ListResult);
|
||||
TUniquePtr<FHttpServerResponse> R = FHttpServerResponse::Create(
|
||||
Resp, TEXT("application/json"));
|
||||
JsonToString(ListResult), TEXT("application/json"));
|
||||
OnComplete(MoveTemp(R));
|
||||
return true;
|
||||
}));
|
||||
@@ -852,7 +884,23 @@ bool FBlueprintMCPServer::ProcessOneRequest()
|
||||
return false;
|
||||
}
|
||||
|
||||
FString Response;
|
||||
// Build Params JSON: either from POST body or from GET query params
|
||||
TSharedPtr<FJsonObject> Params;
|
||||
if (!Req->Body.IsEmpty())
|
||||
{
|
||||
Params = ParseBodyJson(Req->Body);
|
||||
}
|
||||
if (!Params.IsValid())
|
||||
{
|
||||
Params = MakeShared<FJsonObject>();
|
||||
for (const auto& KV : Req->QueryParams)
|
||||
{
|
||||
Params->SetStringField(KV.Key, KV.Value);
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
|
||||
if (FRequestHandler* Handler = HandlerMap.Find(Req->Endpoint))
|
||||
{
|
||||
// Wrap mutation endpoints in an undo transaction so users can Ctrl+Z
|
||||
@@ -862,7 +910,7 @@ bool FBlueprintMCPServer::ProcessOneRequest()
|
||||
GEditor->BeginTransaction(FText::FromString(FString::Printf(TEXT("BlueprintMCP: %s"), *Req->Endpoint)));
|
||||
}
|
||||
|
||||
Response = (*Handler)(Req->QueryParams, Req->Body);
|
||||
(*Handler)(Params.Get(), &*Result);
|
||||
|
||||
if (bIsMutation && GEditor)
|
||||
{
|
||||
@@ -871,10 +919,11 @@ bool FBlueprintMCPServer::ProcessOneRequest()
|
||||
}
|
||||
else
|
||||
{
|
||||
Response = MakeErrorJson(FString::Printf(TEXT("Unknown endpoint: %s"), *Req->Endpoint));
|
||||
MakeErrorJson(&*Result, FString::Printf(TEXT("Unknown endpoint: %s"), *Req->Endpoint));
|
||||
}
|
||||
|
||||
// Send the response back via the HTTP callback (non-blocking)
|
||||
FString Response = JsonToString(Result);
|
||||
TUniquePtr<FHttpServerResponse> HttpResp = FHttpServerResponse::Create(
|
||||
Response, TEXT("application/json"));
|
||||
Req->OnComplete(MoveTemp(HttpResp));
|
||||
@@ -946,121 +995,116 @@ void FBlueprintMCPServer::RegisterHandlers()
|
||||
TEXT("setStateAnimation"),
|
||||
};
|
||||
|
||||
// GET handlers (use QueryParams, ignore Body)
|
||||
HandlerMap.Add(TEXT("blueprint"), [this](const TMap<FString, FString>& P, const FString&) { return HandleGetBlueprint(P); });
|
||||
HandlerMap.Add(TEXT("graph"), [this](const TMap<FString, FString>& P, const FString&) { return HandleGetGraph(P); });
|
||||
HandlerMap.Add(TEXT("search"), [this](const TMap<FString, FString>& P, const FString&) { return HandleSearch(P); });
|
||||
HandlerMap.Add(TEXT("references"), [this](const TMap<FString, FString>& P, const FString&) { return HandleFindReferences(P); });
|
||||
HandlerMap.Add(TEXT("testSave"), [this](const TMap<FString, FString>& P, const FString&) { return HandleTestSave(P); });
|
||||
HandlerMap.Add(TEXT("searchByType"), [this](const TMap<FString, FString>& P, const FString&) { return HandleSearchByType(P); });
|
||||
// All handlers have uniform signature: void(const FJsonObject&, FJsonObject&)
|
||||
auto H = [this](const TCHAR* Name, void(FBlueprintMCPServer::*Fn)(const FJsonObject*, FJsonObject*))
|
||||
{
|
||||
HandlerMap.Add(Name, [this, Fn](const FJsonObject* Json, FJsonObject* Result) { (this->*Fn)(Json, Result); });
|
||||
};
|
||||
|
||||
// Rescan handler (game thread, no body needed)
|
||||
HandlerMap.Add(TEXT("rescan"), [this](const TMap<FString, FString>&, const FString&) { return HandleRescan(); });
|
||||
|
||||
// POST handlers (use Body, ignore QueryParams)
|
||||
HandlerMap.Add(TEXT("replaceFunctionCalls"), [this](const TMap<FString, FString>&, const FString& B) { return HandleReplaceFunctionCalls(B); });
|
||||
HandlerMap.Add(TEXT("changeVariableType"), [this](const TMap<FString, FString>&, const FString& B) { return HandleChangeVariableType(B); });
|
||||
HandlerMap.Add(TEXT("changeFunctionParamType"), [this](const TMap<FString, FString>&, const FString& B) { return HandleChangeFunctionParamType(B); });
|
||||
HandlerMap.Add(TEXT("removeFunctionParameter"), [this](const TMap<FString, FString>&, const FString& B) { return HandleRemoveFunctionParameter(B); });
|
||||
HandlerMap.Add(TEXT("deleteAsset"), [this](const TMap<FString, FString>&, const FString& B) { return HandleDeleteAsset(B); });
|
||||
HandlerMap.Add(TEXT("connectPins"), [this](const TMap<FString, FString>&, const FString& B) { return HandleConnectPins(B); });
|
||||
HandlerMap.Add(TEXT("disconnectPin"), [this](const TMap<FString, FString>&, const FString& B) { return HandleDisconnectPin(B); });
|
||||
HandlerMap.Add(TEXT("refreshAllNodes"), [this](const TMap<FString, FString>&, const FString& B) { return HandleRefreshAllNodes(B); });
|
||||
HandlerMap.Add(TEXT("setPinDefault"), [this](const TMap<FString, FString>&, const FString& B) { return HandleSetPinDefault(B); });
|
||||
HandlerMap.Add(TEXT("moveNode"), [this](const TMap<FString, FString>&, const FString& B) { return HandleMoveNode(B); });
|
||||
HandlerMap.Add(TEXT("getNodeComment"), [this](const TMap<FString, FString>&, const FString& B) { return HandleGetNodeComment(B); });
|
||||
HandlerMap.Add(TEXT("setNodeComment"), [this](const TMap<FString, FString>&, const FString& B) { return HandleSetNodeComment(B); });
|
||||
HandlerMap.Add(TEXT("getPinInfo"), [this](const TMap<FString, FString>&, const FString& B) { return HandleGetPinInfo(B); });
|
||||
HandlerMap.Add(TEXT("checkPinCompatibility"), [this](const TMap<FString, FString>&, const FString& B) { return HandleCheckPinCompatibility(B); });
|
||||
HandlerMap.Add(TEXT("listClasses"), [this](const TMap<FString, FString>&, const FString& B) { return HandleListClasses(B); });
|
||||
HandlerMap.Add(TEXT("listFunctions"), [this](const TMap<FString, FString>&, const FString& B) { return HandleListFunctions(B); });
|
||||
HandlerMap.Add(TEXT("listProperties"), [this](const TMap<FString, FString>&, const FString& B) { return HandleListProperties(B); });
|
||||
HandlerMap.Add(TEXT("changeStructNodeType"), [this](const TMap<FString, FString>&, const FString& B) { return HandleChangeStructNodeType(B); });
|
||||
HandlerMap.Add(TEXT("deleteNode"), [this](const TMap<FString, FString>&, const FString& B) { return HandleDeleteNode(B); });
|
||||
HandlerMap.Add(TEXT("duplicateNodes"), [this](const TMap<FString, FString>&, const FString& B) { return HandleDuplicateNodes(B); });
|
||||
HandlerMap.Add(TEXT("validateBlueprint"), [this](const TMap<FString, FString>&, const FString& B) { return HandleValidateBlueprint(B); });
|
||||
HandlerMap.Add(TEXT("validateAllBlueprints"), [this](const TMap<FString, FString>&, const FString& B) { return HandleValidateAllBlueprints(B); });
|
||||
HandlerMap.Add(TEXT("addNode"), [this](const TMap<FString, FString>&, const FString& B) { return HandleAddNode(B); });
|
||||
HandlerMap.Add(TEXT("searchNodeActions"), [this](const TMap<FString, FString>&, const FString& B) { return HandleSearchNodeActions(B); });
|
||||
HandlerMap.Add(TEXT("spawnNode"), [this](const TMap<FString, FString>&, const FString& B) { return HandleSpawnNode(B); });
|
||||
HandlerMap.Add(TEXT("renameAsset"), [this](const TMap<FString, FString>&, const FString& B) { return HandleRenameAsset(B); });
|
||||
HandlerMap.Add(TEXT("reparentBlueprint"), [this](const TMap<FString, FString>&, const FString& B) { return HandleReparentBlueprint(B); });
|
||||
HandlerMap.Add(TEXT("setBlueprintDefault"), [this](const TMap<FString, FString>&, const FString& B) { return HandleSetBlueprintDefault(B); });
|
||||
HandlerMap.Add(TEXT("createBlueprint"), [this](const TMap<FString, FString>&, const FString& B) { return HandleCreateBlueprint(B); });
|
||||
HandlerMap.Add(TEXT("createGraph"), [this](const TMap<FString, FString>&, const FString& B) { return HandleCreateGraph(B); });
|
||||
HandlerMap.Add(TEXT("deleteGraph"), [this](const TMap<FString, FString>&, const FString& B) { return HandleDeleteGraph(B); });
|
||||
HandlerMap.Add(TEXT("renameGraph"), [this](const TMap<FString, FString>&, const FString& B) { return HandleRenameGraph(B); });
|
||||
HandlerMap.Add(TEXT("addVariable"), [this](const TMap<FString, FString>&, const FString& B) { return HandleAddVariable(B); });
|
||||
HandlerMap.Add(TEXT("removeVariable"), [this](const TMap<FString, FString>&, const FString& B) { return HandleRemoveVariable(B); });
|
||||
HandlerMap.Add(TEXT("setVariableMetadata"), [this](const TMap<FString, FString>&, const FString& B) { return HandleSetVariableMetadata(B); });
|
||||
HandlerMap.Add(TEXT("addInterface"), [this](const TMap<FString, FString>&, const FString& B) { return HandleAddInterface(B); });
|
||||
HandlerMap.Add(TEXT("removeInterface"), [this](const TMap<FString, FString>&, const FString& B) { return HandleRemoveInterface(B); });
|
||||
HandlerMap.Add(TEXT("listInterfaces"), [this](const TMap<FString, FString>&, const FString& B) { return HandleListInterfaces(B); });
|
||||
HandlerMap.Add(TEXT("addEventDispatcher"), [this](const TMap<FString, FString>&, const FString& B) { return HandleAddEventDispatcher(B); });
|
||||
HandlerMap.Add(TEXT("listEventDispatchers"), [this](const TMap<FString, FString>&, const FString& B) { return HandleListEventDispatchers(B); });
|
||||
HandlerMap.Add(TEXT("addFunctionParameter"), [this](const TMap<FString, FString>&, const FString& B) { return HandleAddFunctionParameter(B); });
|
||||
HandlerMap.Add(TEXT("addComponent"), [this](const TMap<FString, FString>&, const FString& B) { return HandleAddComponent(B); });
|
||||
HandlerMap.Add(TEXT("removeComponent"), [this](const TMap<FString, FString>&, const FString& B) { return HandleRemoveComponent(B); });
|
||||
HandlerMap.Add(TEXT("listComponents"), [this](const TMap<FString, FString>&, const FString& B) { return HandleListComponents(B); });
|
||||
HandlerMap.Add(TEXT("snapshotGraph"), [this](const TMap<FString, FString>&, const FString& B) { return HandleSnapshotGraph(B); });
|
||||
HandlerMap.Add(TEXT("diffGraph"), [this](const TMap<FString, FString>&, const FString& B) { return HandleDiffGraph(B); });
|
||||
HandlerMap.Add(TEXT("restoreGraph"), [this](const TMap<FString, FString>&, const FString& B) { return HandleRestoreGraph(B); });
|
||||
HandlerMap.Add(TEXT("findDisconnectedPins"), [this](const TMap<FString, FString>&, const FString& B) { return HandleFindDisconnectedPins(B); });
|
||||
HandlerMap.Add(TEXT("analyzeRebuildImpact"), [this](const TMap<FString, FString>&, const FString& B) { return HandleAnalyzeRebuildImpact(B); });
|
||||
HandlerMap.Add(TEXT("diffBlueprints"), [this](const TMap<FString, FString>&, const FString& B) { return HandleDiffBlueprints(B); });
|
||||
HandlerMap.Add(TEXT("createStruct"), [this](const TMap<FString, FString>&, const FString& B) { return HandleCreateStruct(B); });
|
||||
HandlerMap.Add(TEXT("createEnum"), [this](const TMap<FString, FString>&, const FString& B) { return HandleCreateEnum(B); });
|
||||
HandlerMap.Add(TEXT("addStructProperty"), [this](const TMap<FString, FString>&, const FString& B) { return HandleAddStructProperty(B); });
|
||||
HandlerMap.Add(TEXT("removeStructProperty"), [this](const TMap<FString, FString>&, const FString& B) { return HandleRemoveStructProperty(B); });
|
||||
|
||||
// Material GET handlers
|
||||
HandlerMap.Add(TEXT("getMaterial"), [this](const TMap<FString, FString>& P, const FString&) { return HandleGetMaterial(P); });
|
||||
HandlerMap.Add(TEXT("getMaterialGraph"), [this](const TMap<FString, FString>& P, const FString&) { return HandleGetMaterialGraph(P); });
|
||||
HandlerMap.Add(TEXT("searchMaterials"), [this](const TMap<FString, FString>& P, const FString&) { return HandleSearchMaterials(P); });
|
||||
HandlerMap.Add(TEXT("getMaterialInstanceParams"), [this](const TMap<FString, FString>& P, const FString&) { return HandleGetMaterialInstanceParameters(P); });
|
||||
HandlerMap.Add(TEXT("getMaterialFunction"), [this](const TMap<FString, FString>& P, const FString&) { return HandleGetMaterialFunction(P); });
|
||||
|
||||
// Material POST handlers
|
||||
HandlerMap.Add(TEXT("describeMaterial"), [this](const TMap<FString, FString>&, const FString& B) { return HandleDescribeMaterial(B); });
|
||||
HandlerMap.Add(TEXT("findMaterialReferences"), [this](const TMap<FString, FString>&, const FString& B) { return HandleFindMaterialReferences(B); });
|
||||
HandlerMap.Add(TEXT("createMaterial"), [this](const TMap<FString, FString>&, const FString& B) { return HandleCreateMaterial(B); });
|
||||
HandlerMap.Add(TEXT("setMaterialProperty"), [this](const TMap<FString, FString>&, const FString& B) { return HandleSetMaterialProperty(B); });
|
||||
HandlerMap.Add(TEXT("addMaterialExpression"), [this](const TMap<FString, FString>&, const FString& B) { return HandleAddMaterialExpression(B); });
|
||||
HandlerMap.Add(TEXT("deleteMaterialExpression"),[this](const TMap<FString, FString>&, const FString& B) { return HandleDeleteMaterialExpression(B); });
|
||||
HandlerMap.Add(TEXT("connectMaterialPins"), [this](const TMap<FString, FString>&, const FString& B) { return HandleConnectMaterialPins(B); });
|
||||
HandlerMap.Add(TEXT("disconnectMaterialPin"), [this](const TMap<FString, FString>&, const FString& B) { return HandleDisconnectMaterialPin(B); });
|
||||
HandlerMap.Add(TEXT("setExpressionValue"), [this](const TMap<FString, FString>&, const FString& B) { return HandleSetExpressionValue(B); });
|
||||
HandlerMap.Add(TEXT("moveMaterialExpression"), [this](const TMap<FString, FString>&, const FString& B) { return HandleMoveMaterialExpression(B); });
|
||||
HandlerMap.Add(TEXT("createMaterialInstance"), [this](const TMap<FString, FString>&, const FString& B) { return HandleCreateMaterialInstance(B); });
|
||||
HandlerMap.Add(TEXT("setMaterialInstanceParameter"), [this](const TMap<FString, FString>&, const FString& B) { return HandleSetMaterialInstanceParameter(B); });
|
||||
HandlerMap.Add(TEXT("reparentMaterialInstance"),[this](const TMap<FString, FString>&, const FString& B) { return HandleReparentMaterialInstance(B); });
|
||||
HandlerMap.Add(TEXT("createMaterialFunction"), [this](const TMap<FString, FString>&, const FString& B) { return HandleCreateMaterialFunction(B); });
|
||||
HandlerMap.Add(TEXT("snapshotMaterialGraph"), [this](const TMap<FString, FString>&, const FString& B) { return HandleSnapshotMaterialGraph(B); });
|
||||
HandlerMap.Add(TEXT("diffMaterialGraph"), [this](const TMap<FString, FString>&, const FString& B) { return HandleDiffMaterialGraph(B); });
|
||||
HandlerMap.Add(TEXT("restoreMaterialGraph"), [this](const TMap<FString, FString>&, const FString& B) { return HandleRestoreMaterialGraph(B); });
|
||||
HandlerMap.Add(TEXT("validateMaterial"), [this](const TMap<FString, FString>&, const FString& B) { return HandleValidateMaterial(B); });
|
||||
|
||||
// Animation Blueprint handlers
|
||||
HandlerMap.Add(TEXT("createAnimBlueprint"), [this](const TMap<FString, FString>&, const FString& B) { return HandleCreateAnimBlueprint(B); });
|
||||
HandlerMap.Add(TEXT("addAnimState"), [this](const TMap<FString, FString>&, const FString& B) { return HandleAddAnimState(B); });
|
||||
HandlerMap.Add(TEXT("removeAnimState"), [this](const TMap<FString, FString>&, const FString& B) { return HandleRemoveAnimState(B); });
|
||||
HandlerMap.Add(TEXT("addAnimTransition"), [this](const TMap<FString, FString>&, const FString& B) { return HandleAddAnimTransition(B); });
|
||||
HandlerMap.Add(TEXT("setTransitionRule"), [this](const TMap<FString, FString>&, const FString& B) { return HandleSetTransitionRule(B); });
|
||||
HandlerMap.Add(TEXT("addAnimNode"), [this](const TMap<FString, FString>&, const FString& B) { return HandleAddAnimNode(B); });
|
||||
HandlerMap.Add(TEXT("addStateMachine"), [this](const TMap<FString, FString>&, const FString& B) { return HandleAddStateMachine(B); });
|
||||
HandlerMap.Add(TEXT("setStateAnimation"), [this](const TMap<FString, FString>&, const FString& B) { return HandleSetStateAnimation(B); });
|
||||
HandlerMap.Add(TEXT("listAnimSlots"), [this](const TMap<FString, FString>&, const FString& B) { return HandleListAnimSlots(B); });
|
||||
HandlerMap.Add(TEXT("listSyncGroups"), [this](const TMap<FString, FString>&, const FString& B) { return HandleListSyncGroups(B); });
|
||||
HandlerMap.Add(TEXT("createBlendSpace"), [this](const TMap<FString, FString>&, const FString& B) { return HandleCreateBlendSpace(B); });
|
||||
HandlerMap.Add(TEXT("setBlendSpaceSamples"), [this](const TMap<FString, FString>&, const FString& B) { return HandleSetBlendSpaceSamples(B); });
|
||||
HandlerMap.Add(TEXT("setStateBlendSpace"), [this](const TMap<FString, FString>&, const FString& B) { return HandleSetStateBlendSpace(B); });
|
||||
H(TEXT("rescan"), &FBlueprintMCPServer::HandleRescan);
|
||||
H(TEXT("blueprint"), &FBlueprintMCPServer::HandleGetBlueprint);
|
||||
H(TEXT("graph"), &FBlueprintMCPServer::HandleGetGraph);
|
||||
H(TEXT("search"), &FBlueprintMCPServer::HandleSearch);
|
||||
H(TEXT("references"), &FBlueprintMCPServer::HandleFindReferences);
|
||||
H(TEXT("testSave"), &FBlueprintMCPServer::HandleTestSave);
|
||||
H(TEXT("searchByType"), &FBlueprintMCPServer::HandleSearchByType);
|
||||
H(TEXT("replaceFunctionCalls"), &FBlueprintMCPServer::HandleReplaceFunctionCalls);
|
||||
H(TEXT("changeVariableType"), &FBlueprintMCPServer::HandleChangeVariableType);
|
||||
H(TEXT("changeFunctionParamType"), &FBlueprintMCPServer::HandleChangeFunctionParamType);
|
||||
H(TEXT("removeFunctionParameter"), &FBlueprintMCPServer::HandleRemoveFunctionParameter);
|
||||
H(TEXT("deleteAsset"), &FBlueprintMCPServer::HandleDeleteAsset);
|
||||
H(TEXT("connectPins"), &FBlueprintMCPServer::HandleConnectPins);
|
||||
H(TEXT("disconnectPin"), &FBlueprintMCPServer::HandleDisconnectPin);
|
||||
H(TEXT("refreshAllNodes"), &FBlueprintMCPServer::HandleRefreshAllNodes);
|
||||
H(TEXT("setPinDefault"), &FBlueprintMCPServer::HandleSetPinDefault);
|
||||
H(TEXT("moveNode"), &FBlueprintMCPServer::HandleMoveNode);
|
||||
H(TEXT("getNodeComment"), &FBlueprintMCPServer::HandleGetNodeComment);
|
||||
H(TEXT("setNodeComment"), &FBlueprintMCPServer::HandleSetNodeComment);
|
||||
H(TEXT("getPinInfo"), &FBlueprintMCPServer::HandleGetPinInfo);
|
||||
H(TEXT("checkPinCompatibility"), &FBlueprintMCPServer::HandleCheckPinCompatibility);
|
||||
H(TEXT("listClasses"), &FBlueprintMCPServer::HandleListClasses);
|
||||
H(TEXT("listFunctions"), &FBlueprintMCPServer::HandleListFunctions);
|
||||
H(TEXT("listProperties"), &FBlueprintMCPServer::HandleListProperties);
|
||||
H(TEXT("changeStructNodeType"), &FBlueprintMCPServer::HandleChangeStructNodeType);
|
||||
H(TEXT("deleteNode"), &FBlueprintMCPServer::HandleDeleteNode);
|
||||
H(TEXT("duplicateNodes"), &FBlueprintMCPServer::HandleDuplicateNodes);
|
||||
H(TEXT("validateBlueprint"), &FBlueprintMCPServer::HandleValidateBlueprint);
|
||||
H(TEXT("validateAllBlueprints"), &FBlueprintMCPServer::HandleValidateAllBlueprints);
|
||||
H(TEXT("addNode"), &FBlueprintMCPServer::HandleAddNode);
|
||||
H(TEXT("searchNodeActions"), &FBlueprintMCPServer::HandleSearchNodeActions);
|
||||
H(TEXT("spawnNode"), &FBlueprintMCPServer::HandleSpawnNode);
|
||||
H(TEXT("renameAsset"), &FBlueprintMCPServer::HandleRenameAsset);
|
||||
H(TEXT("reparentBlueprint"), &FBlueprintMCPServer::HandleReparentBlueprint);
|
||||
H(TEXT("setBlueprintDefault"), &FBlueprintMCPServer::HandleSetBlueprintDefault);
|
||||
H(TEXT("createBlueprint"), &FBlueprintMCPServer::HandleCreateBlueprint);
|
||||
H(TEXT("createGraph"), &FBlueprintMCPServer::HandleCreateGraph);
|
||||
H(TEXT("deleteGraph"), &FBlueprintMCPServer::HandleDeleteGraph);
|
||||
H(TEXT("renameGraph"), &FBlueprintMCPServer::HandleRenameGraph);
|
||||
H(TEXT("addVariable"), &FBlueprintMCPServer::HandleAddVariable);
|
||||
H(TEXT("removeVariable"), &FBlueprintMCPServer::HandleRemoveVariable);
|
||||
H(TEXT("setVariableMetadata"), &FBlueprintMCPServer::HandleSetVariableMetadata);
|
||||
H(TEXT("addInterface"), &FBlueprintMCPServer::HandleAddInterface);
|
||||
H(TEXT("removeInterface"), &FBlueprintMCPServer::HandleRemoveInterface);
|
||||
H(TEXT("listInterfaces"), &FBlueprintMCPServer::HandleListInterfaces);
|
||||
H(TEXT("addEventDispatcher"), &FBlueprintMCPServer::HandleAddEventDispatcher);
|
||||
H(TEXT("listEventDispatchers"), &FBlueprintMCPServer::HandleListEventDispatchers);
|
||||
H(TEXT("addFunctionParameter"), &FBlueprintMCPServer::HandleAddFunctionParameter);
|
||||
H(TEXT("addComponent"), &FBlueprintMCPServer::HandleAddComponent);
|
||||
H(TEXT("removeComponent"), &FBlueprintMCPServer::HandleRemoveComponent);
|
||||
H(TEXT("listComponents"), &FBlueprintMCPServer::HandleListComponents);
|
||||
H(TEXT("snapshotGraph"), &FBlueprintMCPServer::HandleSnapshotGraph);
|
||||
H(TEXT("diffGraph"), &FBlueprintMCPServer::HandleDiffGraph);
|
||||
H(TEXT("restoreGraph"), &FBlueprintMCPServer::HandleRestoreGraph);
|
||||
H(TEXT("findDisconnectedPins"), &FBlueprintMCPServer::HandleFindDisconnectedPins);
|
||||
H(TEXT("analyzeRebuildImpact"), &FBlueprintMCPServer::HandleAnalyzeRebuildImpact);
|
||||
H(TEXT("diffBlueprints"), &FBlueprintMCPServer::HandleDiffBlueprints);
|
||||
H(TEXT("createStruct"), &FBlueprintMCPServer::HandleCreateStruct);
|
||||
H(TEXT("createEnum"), &FBlueprintMCPServer::HandleCreateEnum);
|
||||
H(TEXT("addStructProperty"), &FBlueprintMCPServer::HandleAddStructProperty);
|
||||
H(TEXT("removeStructProperty"), &FBlueprintMCPServer::HandleRemoveStructProperty);
|
||||
H(TEXT("getMaterial"), &FBlueprintMCPServer::HandleGetMaterial);
|
||||
H(TEXT("getMaterialGraph"), &FBlueprintMCPServer::HandleGetMaterialGraph);
|
||||
H(TEXT("searchMaterials"), &FBlueprintMCPServer::HandleSearchMaterials);
|
||||
H(TEXT("getMaterialInstanceParams"),&FBlueprintMCPServer::HandleGetMaterialInstanceParameters);
|
||||
H(TEXT("getMaterialFunction"), &FBlueprintMCPServer::HandleGetMaterialFunction);
|
||||
H(TEXT("describeMaterial"), &FBlueprintMCPServer::HandleDescribeMaterial);
|
||||
H(TEXT("findMaterialReferences"), &FBlueprintMCPServer::HandleFindMaterialReferences);
|
||||
H(TEXT("createMaterial"), &FBlueprintMCPServer::HandleCreateMaterial);
|
||||
H(TEXT("setMaterialProperty"), &FBlueprintMCPServer::HandleSetMaterialProperty);
|
||||
H(TEXT("addMaterialExpression"), &FBlueprintMCPServer::HandleAddMaterialExpression);
|
||||
H(TEXT("deleteMaterialExpression"), &FBlueprintMCPServer::HandleDeleteMaterialExpression);
|
||||
H(TEXT("connectMaterialPins"), &FBlueprintMCPServer::HandleConnectMaterialPins);
|
||||
H(TEXT("disconnectMaterialPin"), &FBlueprintMCPServer::HandleDisconnectMaterialPin);
|
||||
H(TEXT("setExpressionValue"), &FBlueprintMCPServer::HandleSetExpressionValue);
|
||||
H(TEXT("moveMaterialExpression"), &FBlueprintMCPServer::HandleMoveMaterialExpression);
|
||||
H(TEXT("createMaterialInstance"), &FBlueprintMCPServer::HandleCreateMaterialInstance);
|
||||
H(TEXT("setMaterialInstanceParameter"), &FBlueprintMCPServer::HandleSetMaterialInstanceParameter);
|
||||
H(TEXT("reparentMaterialInstance"), &FBlueprintMCPServer::HandleReparentMaterialInstance);
|
||||
H(TEXT("createMaterialFunction"), &FBlueprintMCPServer::HandleCreateMaterialFunction);
|
||||
H(TEXT("snapshotMaterialGraph"), &FBlueprintMCPServer::HandleSnapshotMaterialGraph);
|
||||
H(TEXT("diffMaterialGraph"), &FBlueprintMCPServer::HandleDiffMaterialGraph);
|
||||
H(TEXT("restoreMaterialGraph"), &FBlueprintMCPServer::HandleRestoreMaterialGraph);
|
||||
H(TEXT("validateMaterial"), &FBlueprintMCPServer::HandleValidateMaterial);
|
||||
H(TEXT("createAnimBlueprint"), &FBlueprintMCPServer::HandleCreateAnimBlueprint);
|
||||
H(TEXT("addAnimState"), &FBlueprintMCPServer::HandleAddAnimState);
|
||||
H(TEXT("removeAnimState"), &FBlueprintMCPServer::HandleRemoveAnimState);
|
||||
H(TEXT("addAnimTransition"), &FBlueprintMCPServer::HandleAddAnimTransition);
|
||||
H(TEXT("setTransitionRule"), &FBlueprintMCPServer::HandleSetTransitionRule);
|
||||
H(TEXT("addAnimNode"), &FBlueprintMCPServer::HandleAddAnimNode);
|
||||
H(TEXT("addStateMachine"), &FBlueprintMCPServer::HandleAddStateMachine);
|
||||
H(TEXT("setStateAnimation"), &FBlueprintMCPServer::HandleSetStateAnimation);
|
||||
H(TEXT("listAnimSlots"), &FBlueprintMCPServer::HandleListAnimSlots);
|
||||
H(TEXT("listSyncGroups"), &FBlueprintMCPServer::HandleListSyncGroups);
|
||||
H(TEXT("createBlendSpace"), &FBlueprintMCPServer::HandleCreateBlendSpace);
|
||||
H(TEXT("setBlendSpaceSamples"), &FBlueprintMCPServer::HandleSetBlendSpaceSamples);
|
||||
H(TEXT("setStateBlendSpace"), &FBlueprintMCPServer::HandleSetStateBlendSpace);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleRescan — refresh cached asset lists from asset registry
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleRescan()
|
||||
void FBlueprintMCPServer::HandleRescan(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Rescanning asset registry..."));
|
||||
|
||||
@@ -1092,13 +1136,12 @@ FString FBlueprintMCPServer::HandleRescan()
|
||||
OldMI, AllMaterialInstanceAssets.Num(),
|
||||
OldMF, AllMaterialFunctionAssets.Num());
|
||||
|
||||
TSharedRef<FJsonObject> J = MakeShared<FJsonObject>();
|
||||
J->SetStringField(TEXT("status"), TEXT("ok"));
|
||||
J->SetNumberField(TEXT("blueprintCount"), AllBlueprintAssets.Num());
|
||||
J->SetNumberField(TEXT("mapCount"), AllMapAssets.Num());
|
||||
J->SetNumberField(TEXT("materialCount"), AllMaterialAssets.Num());
|
||||
J->SetNumberField(TEXT("materialInstanceCount"), AllMaterialInstanceAssets.Num());
|
||||
J->SetNumberField(TEXT("materialFunctionCount"), AllMaterialFunctionAssets.Num());
|
||||
Result->SetStringField(TEXT("status"), TEXT("ok"));
|
||||
Result->SetNumberField(TEXT("blueprintCount"), AllBlueprintAssets.Num());
|
||||
Result->SetNumberField(TEXT("mapCount"), AllMapAssets.Num());
|
||||
Result->SetNumberField(TEXT("materialCount"), AllMaterialAssets.Num());
|
||||
Result->SetNumberField(TEXT("materialInstanceCount"), AllMaterialInstanceAssets.Num());
|
||||
Result->SetNumberField(TEXT("materialFunctionCount"), AllMaterialFunctionAssets.Num());
|
||||
|
||||
TSharedRef<FJsonObject> Delta = MakeShared<FJsonObject>();
|
||||
Delta->SetNumberField(TEXT("blueprints"), AllBlueprintAssets.Num() - OldBP);
|
||||
@@ -1106,9 +1149,7 @@ FString FBlueprintMCPServer::HandleRescan()
|
||||
Delta->SetNumberField(TEXT("materials"), AllMaterialAssets.Num() - OldMat);
|
||||
Delta->SetNumberField(TEXT("materialInstances"), AllMaterialInstanceAssets.Num() - OldMI);
|
||||
Delta->SetNumberField(TEXT("materialFunctions"), AllMaterialFunctionAssets.Num() - OldMF);
|
||||
J->SetObjectField(TEXT("delta"), Delta);
|
||||
|
||||
return JsonToString(J);
|
||||
Result->SetObjectField(TEXT("delta"), Delta);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
||||
Reference in New Issue
Block a user