Another batch of MCP handlers ported
This commit is contained in:
@@ -459,219 +459,212 @@ void FBlueprintMCPServer::HandleDeleteAsset(const FJsonObject* Json, FJsonObject
|
||||
// HandleConnectPins — wire two pins together
|
||||
// ============================================================
|
||||
|
||||
void FBlueprintMCPServer::HandleConnectPins(const FJsonObject* Json, FJsonObject* Result)
|
||||
// connect_pins is now handled by UMCPHandler_ConnectPins (new-style registry)
|
||||
|
||||
void UMCPHandler_ConnectPins::Handle(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString SourceNodeId = Json->GetStringField(TEXT("sourceNodeId"));
|
||||
FString SourcePinName = Json->GetStringField(TEXT("sourcePinName"));
|
||||
FString TargetNodeId = Json->GetStringField(TEXT("targetNodeId"));
|
||||
FString TargetPinName = Json->GetStringField(TEXT("targetPinName"));
|
||||
MCPHelper* Helper = MCPHelper::Get();
|
||||
|
||||
if (BlueprintName.IsEmpty() || SourceNodeId.IsEmpty() || SourcePinName.IsEmpty() ||
|
||||
TargetNodeId.IsEmpty() || TargetPinName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, sourceNodeId, sourcePinName, targetNodeId, targetPinName"));
|
||||
}
|
||||
|
||||
// Load Blueprint
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
return Helper->MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Find source node
|
||||
UEdGraph* SourceGraph = nullptr;
|
||||
UEdGraphNode* SourceNode = FindNodeByGuid(BP, SourceNodeId, &SourceGraph);
|
||||
if (!SourceNode)
|
||||
{
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Source node '%s' not found"), *SourceNodeId));
|
||||
}
|
||||
TArray<TSharedPtr<FJsonValue>> Results;
|
||||
int32 SuccessCount = 0;
|
||||
|
||||
// Find target node
|
||||
UEdGraphNode* TargetNode = FindNodeByGuid(BP, TargetNodeId);
|
||||
if (!TargetNode)
|
||||
for (const TSharedPtr<FJsonValue>& ConnVal : Connections.Array)
|
||||
{
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Target node '%s' not found"), *TargetNodeId));
|
||||
}
|
||||
TSharedRef<FJsonObject> EntryResult = MakeShared<FJsonObject>();
|
||||
Results.Add(MakeShared<FJsonValueObject>(EntryResult));
|
||||
|
||||
// Find source pin
|
||||
UEdGraphPin* SourcePin = SourceNode->FindPin(FName(*SourcePinName));
|
||||
if (!SourcePin)
|
||||
{
|
||||
// List available pins for debugging
|
||||
TArray<TSharedPtr<FJsonValue>> PinNames;
|
||||
for (UEdGraphPin* P : SourceNode->Pins)
|
||||
FConnectPinsEntry Entry;
|
||||
FString PopulateError = Helper->PopulateFromJson(FConnectPinsEntry::StaticStruct(), &Entry, ConnVal);
|
||||
if (!PopulateError.IsEmpty())
|
||||
{
|
||||
if (P) PinNames.Add(MakeShared<FJsonValueString>(
|
||||
FString::Printf(TEXT("%s (%s)"), *P->PinName.ToString(),
|
||||
P->Direction == EGPD_Input ? TEXT("Input") : TEXT("Output"))));
|
||||
EntryResult->SetStringField(TEXT("error"), PopulateError);
|
||||
continue;
|
||||
}
|
||||
MakeErrorJson(Result, FString::Printf(TEXT("Source pin '%s' not found on node '%s'"),
|
||||
*SourcePinName, *SourceNodeId));
|
||||
Result->SetArrayField(TEXT("availablePins"), PinNames);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find target pin
|
||||
UEdGraphPin* TargetPin = TargetNode->FindPin(FName(*TargetPinName));
|
||||
if (!TargetPin)
|
||||
{
|
||||
// List available pins for debugging
|
||||
TArray<TSharedPtr<FJsonValue>> PinNames;
|
||||
for (UEdGraphPin* P : TargetNode->Pins)
|
||||
EntryResult->SetStringField(TEXT("sourceNodeId"), Entry.SourceNodeId);
|
||||
EntryResult->SetStringField(TEXT("sourcePinName"), Entry.SourcePinName);
|
||||
EntryResult->SetStringField(TEXT("targetNodeId"), Entry.TargetNodeId);
|
||||
EntryResult->SetStringField(TEXT("targetPinName"), Entry.TargetPinName);
|
||||
|
||||
UEdGraph* SourceGraph = nullptr;
|
||||
UEdGraphNode* SourceNode = Helper->FindNodeByGuid(BP, Entry.SourceNodeId, &SourceGraph);
|
||||
if (!SourceNode)
|
||||
{
|
||||
if (P) PinNames.Add(MakeShared<FJsonValueString>(
|
||||
FString::Printf(TEXT("%s (%s)"), *P->PinName.ToString(),
|
||||
P->Direction == EGPD_Input ? TEXT("Input") : TEXT("Output"))));
|
||||
EntryResult->SetStringField(TEXT("error"), FString::Printf(TEXT("Source node '%s' not found"), *Entry.SourceNodeId));
|
||||
continue;
|
||||
}
|
||||
MakeErrorJson(Result, FString::Printf(TEXT("Target pin '%s' not found on node '%s'"),
|
||||
*TargetPinName, *TargetNodeId));
|
||||
Result->SetArrayField(TEXT("availablePins"), PinNames);
|
||||
return;
|
||||
|
||||
UEdGraphNode* TargetNode = Helper->FindNodeByGuid(BP, Entry.TargetNodeId);
|
||||
if (!TargetNode)
|
||||
{
|
||||
EntryResult->SetStringField(TEXT("error"), FString::Printf(TEXT("Target node '%s' not found"), *Entry.TargetNodeId));
|
||||
continue;
|
||||
}
|
||||
|
||||
UEdGraphPin* SourcePin = SourceNode->FindPin(FName(*Entry.SourcePinName));
|
||||
if (!SourcePin)
|
||||
{
|
||||
EntryResult->SetStringField(TEXT("error"), FString::Printf(TEXT("Source pin '%s' not found on node '%s'"), *Entry.SourcePinName, *Entry.SourceNodeId));
|
||||
continue;
|
||||
}
|
||||
|
||||
UEdGraphPin* TargetPin = TargetNode->FindPin(FName(*Entry.TargetPinName));
|
||||
if (!TargetPin)
|
||||
{
|
||||
EntryResult->SetStringField(TEXT("error"), FString::Printf(TEXT("Target pin '%s' not found on node '%s'"), *Entry.TargetPinName, *Entry.TargetNodeId));
|
||||
continue;
|
||||
}
|
||||
|
||||
const UEdGraphSchema* Schema = SourceGraph->GetSchema();
|
||||
if (!Schema)
|
||||
{
|
||||
EntryResult->SetStringField(TEXT("error"), TEXT("Graph schema not found"));
|
||||
continue;
|
||||
}
|
||||
|
||||
bool bConnected = Schema->TryCreateConnection(SourcePin, TargetPin);
|
||||
if (!bConnected)
|
||||
{
|
||||
EntryResult->SetStringField(TEXT("error"), FString::Printf(
|
||||
TEXT("Cannot connect %s (%s) to %s (%s) — types are incompatible"),
|
||||
*Entry.SourcePinName, *SourcePin->PinType.PinCategory.ToString(),
|
||||
*Entry.TargetPinName, *TargetPin->PinType.PinCategory.ToString()));
|
||||
continue;
|
||||
}
|
||||
|
||||
EntryResult->SetBoolField(TEXT("success"), true);
|
||||
SuccessCount++;
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Connecting %s.%s -> %s.%s"),
|
||||
*SourceNode->GetNodeTitle(ENodeTitleType::FullTitle).ToString(), *SourcePinName,
|
||||
*TargetNode->GetNodeTitle(ENodeTitleType::FullTitle).ToString(), *TargetPinName);
|
||||
|
||||
// Try type-validated connection via the schema
|
||||
const UEdGraphSchema* Schema = SourceGraph->GetSchema();
|
||||
if (!Schema)
|
||||
if (SuccessCount > 0)
|
||||
{
|
||||
return MakeErrorJson(Result, TEXT("Graph schema not found"));
|
||||
}
|
||||
bool bConnected = Schema->TryCreateConnection(SourcePin, TargetPin);
|
||||
|
||||
Result->SetBoolField(TEXT("success"), bConnected);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("sourcePinType"), SourcePin->PinType.PinCategory.ToString());
|
||||
if (SourcePin->PinType.PinSubCategoryObject.IsValid())
|
||||
Result->SetStringField(TEXT("sourcePinSubtype"), SourcePin->PinType.PinSubCategoryObject->GetName());
|
||||
Result->SetStringField(TEXT("targetPinType"), TargetPin->PinType.PinCategory.ToString());
|
||||
if (TargetPin->PinType.PinSubCategoryObject.IsValid())
|
||||
Result->SetStringField(TEXT("targetPinSubtype"), TargetPin->PinType.PinSubCategoryObject->GetName());
|
||||
|
||||
if (!bConnected)
|
||||
{
|
||||
// Provide type mismatch details
|
||||
FString Reason = FString::Printf(TEXT("Cannot connect %s (%s) to %s (%s) — types are incompatible"),
|
||||
*SourcePinName, *SourcePin->PinType.PinCategory.ToString(),
|
||||
*TargetPinName, *TargetPin->PinType.PinCategory.ToString());
|
||||
return MakeErrorJson(Result, Reason);
|
||||
FBlueprintEditorUtils::MarkBlueprintAsModified(BP);
|
||||
}
|
||||
|
||||
// Save
|
||||
bool bSaved = SaveBlueprintPackage(BP);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: ConnectPins — %d/%d succeeded in '%s'"),
|
||||
SuccessCount, Connections.Array.Num(), *Blueprint);
|
||||
|
||||
// Return updated node state for both source and target
|
||||
TSharedPtr<FJsonObject> SourceNodeState = SerializeNode(SourceNode);
|
||||
TSharedPtr<FJsonObject> TargetNodeState = SerializeNode(TargetNode);
|
||||
if (SourceNodeState.IsValid())
|
||||
Result->SetObjectField(TEXT("updatedSourceNode"), SourceNodeState);
|
||||
if (TargetNodeState.IsValid())
|
||||
Result->SetObjectField(TEXT("updatedTargetNode"), TargetNodeState);
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Connection %s, save %s"),
|
||||
bConnected ? TEXT("succeeded") : TEXT("failed"),
|
||||
bSaved ? TEXT("succeeded") : TEXT("failed"));
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), Blueprint);
|
||||
Result->SetNumberField(TEXT("successCount"), SuccessCount);
|
||||
Result->SetNumberField(TEXT("totalCount"), Connections.Array.Num());
|
||||
Result->SetArrayField(TEXT("results"), Results);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleDisconnectPin — break connections on a pin
|
||||
// ============================================================
|
||||
|
||||
void FBlueprintMCPServer::HandleDisconnectPin(const FJsonObject* Json, FJsonObject* Result)
|
||||
// disconnect_pin is now handled by UMCPHandler_DisconnectPin (new-style registry)
|
||||
|
||||
void UMCPHandler_DisconnectPin::Handle(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString NodeId = Json->GetStringField(TEXT("nodeId"));
|
||||
FString PinName = Json->GetStringField(TEXT("pinName"));
|
||||
MCPHelper* Helper = MCPHelper::Get();
|
||||
|
||||
if (BlueprintName.IsEmpty() || NodeId.IsEmpty() || PinName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, nodeId, pinName"));
|
||||
}
|
||||
|
||||
// Optional: specific target to disconnect from
|
||||
FString TargetNodeId = Json->GetStringField(TEXT("targetNodeId"));
|
||||
FString TargetPinName = Json->GetStringField(TEXT("targetPinName"));
|
||||
|
||||
// Load Blueprint
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
return Helper->MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Find source node
|
||||
UEdGraphNode* Node = FindNodeByGuid(BP, NodeId);
|
||||
if (!Node)
|
||||
{
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Node '%s' not found"), *NodeId));
|
||||
}
|
||||
TArray<TSharedPtr<FJsonValue>> Results;
|
||||
int32 SuccessCount = 0;
|
||||
int32 TotalDisconnected = 0;
|
||||
|
||||
// Find pin
|
||||
UEdGraphPin* Pin = Node->FindPin(FName(*PinName));
|
||||
if (!Pin)
|
||||
for (const TSharedPtr<FJsonValue>& DiscVal : Disconnections.Array)
|
||||
{
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Pin '%s' not found on node '%s'"), *PinName, *NodeId));
|
||||
}
|
||||
TSharedRef<FJsonObject> EntryResult = MakeShared<FJsonObject>();
|
||||
Results.Add(MakeShared<FJsonValueObject>(EntryResult));
|
||||
|
||||
int32 DisconnectedCount = 0;
|
||||
|
||||
if (!TargetNodeId.IsEmpty() && !TargetPinName.IsEmpty())
|
||||
{
|
||||
// Disconnect a single specific link
|
||||
UEdGraphNode* TargetNode = FindNodeByGuid(BP, TargetNodeId);
|
||||
if (!TargetNode)
|
||||
FDisconnectPinEntry Entry;
|
||||
FString PopulateError = Helper->PopulateFromJson(FDisconnectPinEntry::StaticStruct(), &Entry, DiscVal);
|
||||
if (!PopulateError.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Target node '%s' not found"), *TargetNodeId));
|
||||
EntryResult->SetStringField(TEXT("error"), PopulateError);
|
||||
continue;
|
||||
}
|
||||
|
||||
UEdGraphPin* TargetPin = TargetNode->FindPin(FName(*TargetPinName));
|
||||
if (!TargetPin)
|
||||
EntryResult->SetStringField(TEXT("nodeId"), Entry.NodeId);
|
||||
EntryResult->SetStringField(TEXT("pinName"), Entry.PinName);
|
||||
|
||||
UEdGraphNode* Node = Helper->FindNodeByGuid(BP, Entry.NodeId);
|
||||
if (!Node)
|
||||
{
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Target pin '%s' not found on node '%s'"),
|
||||
*TargetPinName, *TargetNodeId));
|
||||
EntryResult->SetStringField(TEXT("error"), FString::Printf(TEXT("Node '%s' not found"), *Entry.NodeId));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Pin->LinkedTo.Contains(TargetPin))
|
||||
UEdGraphPin* Pin = Node->FindPin(FName(*Entry.PinName));
|
||||
if (!Pin)
|
||||
{
|
||||
EntryResult->SetStringField(TEXT("error"), FString::Printf(TEXT("Pin '%s' not found on node '%s'"), *Entry.PinName, *Entry.NodeId));
|
||||
continue;
|
||||
}
|
||||
|
||||
int32 DisconnectedCount = 0;
|
||||
|
||||
if (!Entry.TargetNodeId.IsEmpty() && !Entry.TargetPinName.IsEmpty())
|
||||
{
|
||||
UEdGraphNode* TargetNode = Helper->FindNodeByGuid(BP, Entry.TargetNodeId);
|
||||
if (!TargetNode)
|
||||
{
|
||||
EntryResult->SetStringField(TEXT("error"), FString::Printf(TEXT("Target node '%s' not found"), *Entry.TargetNodeId));
|
||||
continue;
|
||||
}
|
||||
|
||||
UEdGraphPin* TargetPin = TargetNode->FindPin(FName(*Entry.TargetPinName));
|
||||
if (!TargetPin)
|
||||
{
|
||||
EntryResult->SetStringField(TEXT("error"), FString::Printf(TEXT("Target pin '%s' not found on node '%s'"), *Entry.TargetPinName, *Entry.TargetNodeId));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Pin->LinkedTo.Contains(TargetPin))
|
||||
{
|
||||
EntryResult->SetStringField(TEXT("error"), TEXT("The specified pins are not connected to each other"));
|
||||
continue;
|
||||
}
|
||||
|
||||
Pin->BreakLinkTo(TargetPin);
|
||||
DisconnectedCount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return MakeErrorJson(Result, TEXT("The specified pins are not connected to each other"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Disconnect all links on this pin
|
||||
DisconnectedCount = Pin->LinkedTo.Num();
|
||||
if (DisconnectedCount > 0)
|
||||
{
|
||||
Pin->BreakAllPinLinks(true);
|
||||
DisconnectedCount = Pin->LinkedTo.Num();
|
||||
if (DisconnectedCount > 0)
|
||||
{
|
||||
Pin->BreakAllPinLinks(true);
|
||||
}
|
||||
}
|
||||
|
||||
EntryResult->SetBoolField(TEXT("success"), true);
|
||||
EntryResult->SetNumberField(TEXT("disconnectedCount"), DisconnectedCount);
|
||||
SuccessCount++;
|
||||
TotalDisconnected += DisconnectedCount;
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Disconnected %d link(s) from %s.%s"),
|
||||
DisconnectedCount, *Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString(), *PinName);
|
||||
|
||||
// Save
|
||||
bool bSaved = false;
|
||||
if (DisconnectedCount > 0)
|
||||
if (TotalDisconnected > 0)
|
||||
{
|
||||
bSaved = SaveBlueprintPackage(BP);
|
||||
FBlueprintEditorUtils::MarkBlueprintAsModified(BP);
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: DisconnectPin — %d/%d succeeded, %d links broken in '%s'"),
|
||||
SuccessCount, Disconnections.Array.Num(), TotalDisconnected, *Blueprint);
|
||||
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetNumberField(TEXT("disconnectedCount"), DisconnectedCount);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
Result->SetStringField(TEXT("blueprint"), Blueprint);
|
||||
Result->SetNumberField(TEXT("successCount"), SuccessCount);
|
||||
Result->SetNumberField(TEXT("totalCount"), Disconnections.Array.Num());
|
||||
Result->SetNumberField(TEXT("totalDisconnected"), TotalDisconnected);
|
||||
Result->SetArrayField(TEXT("results"), Results);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -1128,34 +1121,28 @@ void FBlueprintMCPServer::HandleChangeStructNodeType(const FJsonObject* Json, FJ
|
||||
// HandleDeleteNode — remove a node from a blueprint graph
|
||||
// ============================================================
|
||||
|
||||
void FBlueprintMCPServer::HandleDeleteNode(const FJsonObject* Json, FJsonObject* Result)
|
||||
// delete_node is now handled by UMCPHandler_DeleteNode (new-style registry)
|
||||
|
||||
void UMCPHandler_DeleteNode::Handle(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString NodeId = Json->GetStringField(TEXT("nodeId"));
|
||||
MCPHelper* Helper = MCPHelper::Get();
|
||||
|
||||
if (BlueprintName.IsEmpty() || NodeId.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, nodeId"));
|
||||
}
|
||||
|
||||
// Load Blueprint
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
return Helper->MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
// Find node
|
||||
UEdGraph* Graph = nullptr;
|
||||
UEdGraphNode* Node = FindNodeByGuid(BP, NodeId, &Graph);
|
||||
UEdGraphNode* Node = Helper->FindNodeByGuid(BP, NodeId, &Graph);
|
||||
if (!Node)
|
||||
{
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Node '%s' not found"), *NodeId));
|
||||
return Helper->MakeErrorJson(Result, FString::Printf(TEXT("Node '%s' not found"), *NodeId));
|
||||
}
|
||||
if (!Graph)
|
||||
{
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Graph not found for node '%s'"), *NodeId));
|
||||
return Helper->MakeErrorJson(Result, FString::Printf(TEXT("Graph not found for node '%s'"), *NodeId));
|
||||
}
|
||||
|
||||
FString NodeClass = Node->GetClass()->GetName();
|
||||
@@ -1167,7 +1154,7 @@ void FBlueprintMCPServer::HandleDeleteNode(const FJsonObject* Json, FJsonObject*
|
||||
// without recreating the entire function/event.
|
||||
if (Cast<UK2Node_FunctionEntry>(Node))
|
||||
{
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
return Helper->MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Cannot delete FunctionEntry node '%s' in graph '%s'. ")
|
||||
TEXT("This is the root node of the function — removing it would leave an empty, uncompilable graph. ")
|
||||
TEXT("To remove the entire function, delete it from the Blueprint editor."),
|
||||
@@ -1175,41 +1162,35 @@ void FBlueprintMCPServer::HandleDeleteNode(const FJsonObject* Json, FJsonObject*
|
||||
}
|
||||
if (Cast<UK2Node_Event>(Node))
|
||||
{
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
return Helper->MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Cannot delete event entry node '%s' in graph '%s'. ")
|
||||
TEXT("This is the root node of the event handler — removing it would leave an empty, uncompilable graph."),
|
||||
*NodeTitle, *GraphName));
|
||||
}
|
||||
if (Cast<UK2Node_CustomEvent>(Node))
|
||||
{
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
return Helper->MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Cannot delete CustomEvent entry node '%s' in graph '%s'. ")
|
||||
TEXT("This is the root node of the custom event — removing it would leave an empty, uncompilable graph."),
|
||||
*NodeTitle, *GraphName));
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Deleting node '%s' (%s) from graph '%s' in '%s'"),
|
||||
*NodeId, *NodeTitle, *GraphName, *BlueprintName);
|
||||
*NodeId, *NodeTitle, *GraphName, *Blueprint);
|
||||
|
||||
// Disconnect all pins
|
||||
Node->BreakAllNodeLinks();
|
||||
|
||||
// Remove the node from the graph
|
||||
Graph->RemoveNode(Node);
|
||||
|
||||
// Save (which also compiles)
|
||||
bool bSaved = SaveBlueprintPackage(BP);
|
||||
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(BP);
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Node deleted, save %s"),
|
||||
bSaved ? TEXT("succeeded") : TEXT("failed"));
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Node deleted"));
|
||||
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("blueprint"), Blueprint);
|
||||
Result->SetStringField(TEXT("nodeId"), NodeId);
|
||||
Result->SetStringField(TEXT("nodeClass"), NodeClass);
|
||||
Result->SetStringField(TEXT("nodeTitle"), NodeTitle);
|
||||
Result->SetStringField(TEXT("graph"), GraphName);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -2221,31 +2202,27 @@ void UMCPHandler_DuplicateNodes::Handle(const FJsonObject* Json, FJsonObject* Re
|
||||
// HandleGetNodeComment — read a node's comment text
|
||||
// ============================================================
|
||||
|
||||
void FBlueprintMCPServer::HandleGetNodeComment(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString NodeId = Json->GetStringField(TEXT("nodeId"));
|
||||
// get_node_comment is now handled by UMCPHandler_GetNodeComment (new-style registry)
|
||||
|
||||
if (BlueprintName.IsEmpty() || NodeId.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, nodeId"));
|
||||
}
|
||||
void UMCPHandler_GetNodeComment::Handle(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
MCPHelper* Helper = MCPHelper::Get();
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
return Helper->MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UEdGraphNode* Node = FindNodeByGuid(BP, NodeId);
|
||||
UEdGraphNode* Node = Helper->FindNodeByGuid(BP, NodeId);
|
||||
if (!Node)
|
||||
{
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Node '%s' not found"), *NodeId));
|
||||
return Helper->MakeErrorJson(Result, FString::Printf(TEXT("Node '%s' not found"), *NodeId));
|
||||
}
|
||||
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("blueprint"), Blueprint);
|
||||
Result->SetStringField(TEXT("nodeId"), NodeId);
|
||||
Result->SetStringField(TEXT("comment"), Node->NodeComment);
|
||||
Result->SetBoolField(TEXT("commentBubbleVisible"), Node->bCommentBubbleVisible);
|
||||
@@ -2255,34 +2232,23 @@ void FBlueprintMCPServer::HandleGetNodeComment(const FJsonObject* Json, FJsonObj
|
||||
// HandleSetNodeComment — set a node's comment text
|
||||
// ============================================================
|
||||
|
||||
void FBlueprintMCPServer::HandleSetNodeComment(const FJsonObject* Json, FJsonObject* Result)
|
||||
// set_node_comment is now handled by UMCPHandler_SetNodeComment (new-style registry)
|
||||
|
||||
void UMCPHandler_SetNodeComment::Handle(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString NodeId = Json->GetStringField(TEXT("nodeId"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || NodeId.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, nodeId"));
|
||||
}
|
||||
|
||||
if (!Json->HasField(TEXT("comment")))
|
||||
{
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: comment"));
|
||||
}
|
||||
|
||||
FString Comment = Json->GetStringField(TEXT("comment"));
|
||||
MCPHelper* Helper = MCPHelper::Get();
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
UBlueprint* BP = Helper->LoadBlueprintByName(Blueprint, LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
return Helper->MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UEdGraphNode* Node = FindNodeByGuid(BP, NodeId);
|
||||
UEdGraphNode* Node = Helper->FindNodeByGuid(BP, NodeId);
|
||||
if (!Node)
|
||||
{
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Node '%s' not found"), *NodeId));
|
||||
return Helper->MakeErrorJson(Result, FString::Printf(TEXT("Node '%s' not found"), *NodeId));
|
||||
}
|
||||
|
||||
FString OldComment = Node->NodeComment;
|
||||
@@ -2296,17 +2262,15 @@ void FBlueprintMCPServer::HandleSetNodeComment(const FJsonObject* Json, FJsonObj
|
||||
}
|
||||
|
||||
FBlueprintEditorUtils::MarkBlueprintAsModified(BP);
|
||||
bool bSaved = SaveBlueprintPackage(BP);
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Set comment on node '%s' in '%s', save %s"),
|
||||
*NodeId, *BlueprintName, bSaved ? TEXT("succeeded") : TEXT("failed"));
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Set comment on node '%s' in '%s'"),
|
||||
*NodeId, *Blueprint);
|
||||
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetStringField(TEXT("blueprint"), Blueprint);
|
||||
Result->SetStringField(TEXT("nodeId"), NodeId);
|
||||
Result->SetStringField(TEXT("oldComment"), OldComment);
|
||||
Result->SetStringField(TEXT("newComment"), Comment);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
||||
@@ -611,9 +611,9 @@ bool FBlueprintMCPServer::Start(int32 InPort, bool bEditorMode)
|
||||
Router->BindRoute(FHttpPath(TEXT("/api/test-save")), EHttpServerRequestVerbs::VERB_GET,
|
||||
QueuedHandler(TEXT("testSave")));
|
||||
Router->BindRoute(FHttpPath(TEXT("/api/connect-pins")), EHttpServerRequestVerbs::VERB_POST,
|
||||
QueuedHandler(TEXT("connectPins")));
|
||||
QueuedHandler(TEXT("connect_pins")));
|
||||
Router->BindRoute(FHttpPath(TEXT("/api/disconnect-pin")), EHttpServerRequestVerbs::VERB_POST,
|
||||
QueuedHandler(TEXT("disconnectPin")));
|
||||
QueuedHandler(TEXT("disconnect_pin")));
|
||||
Router->BindRoute(FHttpPath(TEXT("/api/refresh-all-nodes")), EHttpServerRequestVerbs::VERB_POST,
|
||||
QueuedHandler(TEXT("refreshAllNodes")));
|
||||
Router->BindRoute(FHttpPath(TEXT("/api/set-pin-default")), EHttpServerRequestVerbs::VERB_POST,
|
||||
@@ -621,9 +621,9 @@ bool FBlueprintMCPServer::Start(int32 InPort, bool bEditorMode)
|
||||
Router->BindRoute(FHttpPath(TEXT("/api/move-node")), EHttpServerRequestVerbs::VERB_POST,
|
||||
QueuedHandler(TEXT("move_node")));
|
||||
Router->BindRoute(FHttpPath(TEXT("/api/get-node-comment")), EHttpServerRequestVerbs::VERB_POST,
|
||||
QueuedHandler(TEXT("getNodeComment")));
|
||||
QueuedHandler(TEXT("get_node_comment")));
|
||||
Router->BindRoute(FHttpPath(TEXT("/api/set-node-comment")), EHttpServerRequestVerbs::VERB_POST,
|
||||
QueuedHandler(TEXT("setNodeComment")));
|
||||
QueuedHandler(TEXT("set_node_comment")));
|
||||
Router->BindRoute(FHttpPath(TEXT("/api/get-pin-info")), EHttpServerRequestVerbs::VERB_POST,
|
||||
QueuedHandler(TEXT("getPinInfo")));
|
||||
Router->BindRoute(FHttpPath(TEXT("/api/check-pin-compatibility")), EHttpServerRequestVerbs::VERB_POST,
|
||||
@@ -639,7 +639,7 @@ bool FBlueprintMCPServer::Start(int32 InPort, bool bEditorMode)
|
||||
Router->BindRoute(FHttpPath(TEXT("/api/remove-function-parameter")), EHttpServerRequestVerbs::VERB_POST,
|
||||
QueuedHandler(TEXT("removeFunctionParameter")));
|
||||
Router->BindRoute(FHttpPath(TEXT("/api/delete-node")), EHttpServerRequestVerbs::VERB_POST,
|
||||
QueuedHandler(TEXT("deleteNode")));
|
||||
QueuedHandler(TEXT("delete_node")));
|
||||
Router->BindRoute(FHttpPath(TEXT("/api/duplicate-nodes")), EHttpServerRequestVerbs::VERB_POST,
|
||||
QueuedHandler(TEXT("duplicate_nodes")));
|
||||
Router->BindRoute(FHttpPath(TEXT("/api/search-by-type")), EHttpServerRequestVerbs::VERB_GET,
|
||||
@@ -982,17 +982,17 @@ void FBlueprintMCPServer::RegisterHandlers()
|
||||
TEXT("changeFunctionParamType"),
|
||||
TEXT("removeFunctionParameter"),
|
||||
TEXT("deleteAsset"),
|
||||
TEXT("connectPins"),
|
||||
TEXT("disconnectPin"),
|
||||
TEXT("connect_pins"),
|
||||
TEXT("disconnect_pin"),
|
||||
TEXT("refreshAllNodes"),
|
||||
TEXT("set_pin_default"),
|
||||
TEXT("move_node"),
|
||||
TEXT("changeStructNodeType"),
|
||||
TEXT("deleteNode"),
|
||||
TEXT("delete_node"),
|
||||
TEXT("duplicate_nodes"),
|
||||
TEXT("addNode"),
|
||||
TEXT("spawn_node"),
|
||||
TEXT("setNodeComment"),
|
||||
TEXT("set_node_comment"),
|
||||
TEXT("renameAsset"),
|
||||
TEXT("reparentBlueprint"),
|
||||
TEXT("setBlueprintDefault"),
|
||||
@@ -1055,20 +1055,20 @@ void FBlueprintMCPServer::RegisterHandlers()
|
||||
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);
|
||||
// connect_pins is now handled by UMCPHandler_ConnectPins (new-style registry)
|
||||
// disconnect_pin is now handled by UMCPHandler_DisconnectPin (new-style registry)
|
||||
H(TEXT("refreshAllNodes"), &FBlueprintMCPServer::HandleRefreshAllNodes);
|
||||
// set_pin_default is now handled by UMCPHandler_SetPinDefault (new-style registry)
|
||||
// move_node is now handled by UMCPHandler_MoveNode (new-style registry)
|
||||
H(TEXT("getNodeComment"), &FBlueprintMCPServer::HandleGetNodeComment);
|
||||
H(TEXT("setNodeComment"), &FBlueprintMCPServer::HandleSetNodeComment);
|
||||
// get_node_comment is now handled by UMCPHandler_GetNodeComment (new-style registry)
|
||||
// set_node_comment is now handled by UMCPHandler_SetNodeComment (new-style registry)
|
||||
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);
|
||||
// delete_node is now handled by UMCPHandler_DeleteNode (new-style registry)
|
||||
// duplicate_nodes is now handled by UMCPHandler_DuplicateNodes (new-style registry)
|
||||
H(TEXT("validateBlueprint"), &FBlueprintMCPServer::HandleValidateBlueprint);
|
||||
H(TEXT("validateAllBlueprints"), &FBlueprintMCPServer::HandleValidateAllBlueprints);
|
||||
|
||||
Reference in New Issue
Block a user