Remove stringbuilder parameter for handlers

This commit is contained in:
2026-03-13 23:41:59 -04:00
parent 7cfe73eca8
commit 2ffc493e91
76 changed files with 539 additions and 467 deletions

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -37,7 +38,7 @@ public:
return TEXT("Create a new Animation Blueprint asset with a specified skeleton."); return TEXT("Create a new Animation Blueprint asset with a specified skeleton.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPPackageMaker Maker(AssetPath); MCPPackageMaker Maker(AssetPath);
if (!Maker.Ok()) return; if (!Maker.Ok()) return;
@@ -94,14 +95,14 @@ public:
FKismetEditorUtilities::CompileBlueprint(NewAnimBP); FKismetEditorUtilities::CompileBlueprint(NewAnimBP);
bool bSaved = MCPUtils::SaveBlueprintPackage(NewAnimBP); bool bSaved = MCPUtils::SaveBlueprintPackage(NewAnimBP);
Result.Appendf(TEXT("Created: %s\n"), *AssetPath); UMCPServer::Printf(TEXT("Created: %s\n"), *AssetPath);
Result.Appendf(TEXT("ParentClass: %s\n"), *MCPUtils::FormatName(ParentClassObj)); UMCPServer::Printf(TEXT("ParentClass: %s\n"), *MCPUtils::FormatName(ParentClassObj));
Result.Appendf(TEXT("Saved: %s\n"), bSaved ? TEXT("true") : TEXT("false")); UMCPServer::Printf(TEXT("Saved: %s\n"), bSaved ? TEXT("true") : TEXT("false"));
TArray<UEdGraph*> Graphs = MCPUtils::AllGraphs(NewAnimBP); TArray<UEdGraph*> Graphs = MCPUtils::AllGraphs(NewAnimBP);
for (UEdGraph* Graph : Graphs) for (UEdGraph* Graph : Graphs)
{ {
Result.Appendf(TEXT("Graph: %s\n"), *MCPUtils::FormatName(Graph)); UMCPServer::Printf(TEXT("Graph: %s\n"), *MCPUtils::FormatName(Graph));
} }
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -27,7 +28,7 @@ public:
return TEXT("List all animation slot names used in an Animation Blueprint."); return TEXT("List all animation slot names used in an Animation Blueprint.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPAssets<UAnimBlueprint> Assets; MCPAssets<UAnimBlueprint> Assets;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
@@ -52,12 +53,12 @@ public:
for (const FString& Slot : SlotNames) for (const FString& Slot : SlotNames)
{ {
Result.Appendf(TEXT("%s\n"), *Slot); UMCPServer::Printf(TEXT("%s\n"), *Slot);
} }
if (SlotNames.Num() == 0) if (SlotNames.Num() == 0)
{ {
Result.Append(TEXT("No animation slot names found.\n")); UMCPServer::Print(TEXT("No animation slot names found.\n"));
} }
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -27,7 +28,7 @@ public:
return TEXT("List all sync group names used in an Animation Blueprint."); return TEXT("List all sync group names used in an Animation Blueprint.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UAnimBlueprint* AnimBP = F.Walk(Path).Cast<UAnimBlueprint>(); UAnimBlueprint* AnimBP = F.Walk(Path).Cast<UAnimBlueprint>();
@@ -52,13 +53,13 @@ public:
if (SyncGroupNames.Num() == 0) if (SyncGroupNames.Num() == 0)
{ {
Result.Append(TEXT("No sync groups found.\n")); UMCPServer::Print(TEXT("No sync groups found.\n"));
return; return;
} }
for (const FString& Group : SyncGroupNames) for (const FString& Group : SyncGroupNames)
{ {
Result.Appendf(TEXT("%s\n"), *Group); UMCPServer::Printf(TEXT("%s\n"), *Group);
} }
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
@@ -65,7 +66,7 @@ public:
"Replaces all existing samples."); "Replaces all existing samples.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
// Load the blend space // Load the blend space
MCPAssets<UBlendSpace> Assets; MCPAssets<UBlendSpace> Assets;
@@ -128,10 +129,10 @@ public:
// Save // Save
bool bSaved = MCPUtils::SaveGenericPackage(BS); bool bSaved = MCPUtils::SaveGenericPackage(BS);
Result.Appendf(TEXT("Set %d samples on %s\n"), SamplesSet, *MCPUtils::FormatName(BS)); UMCPServer::Printf(TEXT("Set %d samples on %s\n"), SamplesSet, *MCPUtils::FormatName(BS));
if (!bSaved) if (!bSaved)
{ {
Result.Append(TEXT("WARNING: package save failed\n")); UMCPServer::Print(TEXT("WARNING: package save failed\n"));
} }
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Misc/Paths.h" #include "Misc/Paths.h"
@@ -27,14 +28,14 @@ public:
return TEXT("Copy an asset's .uasset file to a .uasset.bak backup."); return TEXT("Copy an asset's .uasset file to a .uasset.bak backup.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
FString Filename = FPaths::ConvertRelativePathToFull( FString Filename = FPaths::ConvertRelativePathToFull(
FPackageName::LongPackageNameToFilename(AssetPath, FPackageName::GetAssetPackageExtension())); FPackageName::LongPackageNameToFilename(AssetPath, FPackageName::GetAssetPackageExtension()));
if (!IFileManager::Get().FileExists(*Filename)) if (!IFileManager::Get().FileExists(*Filename))
{ {
Result.Appendf(TEXT("ERROR: Asset file not found: %s\n"), *Filename); UMCPServer::Printf(TEXT("ERROR: Asset file not found: %s\n"), *Filename);
return; return;
} }
@@ -42,10 +43,10 @@ public:
uint32 CopyResult = IFileManager::Get().Copy(*BackupFilename, *Filename, true); uint32 CopyResult = IFileManager::Get().Copy(*BackupFilename, *Filename, true);
if (CopyResult != COPY_OK) if (CopyResult != COPY_OK)
{ {
Result.Appendf(TEXT("ERROR: Failed to copy %s to %s\n"), *Filename, *BackupFilename); UMCPServer::Printf(TEXT("ERROR: Failed to copy %s to %s\n"), *Filename, *BackupFilename);
return; return;
} }
Result.Appendf(TEXT("Backed up to %s\n"), *BackupFilename); UMCPServer::Printf(TEXT("Backed up to %s\n"), *BackupFilename);
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -34,7 +35,7 @@ public:
"Use force=true to skip the reference check."); "Use force=true to skip the reference check.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
// Verify the asset file exists on disk // Verify the asset file exists on disk
FString PackageFilename = FPackageName::LongPackageNameToFilename( FString PackageFilename = FPackageName::LongPackageNameToFilename(
@@ -43,7 +44,7 @@ public:
if (!IFileManager::Get().FileExists(*PackageFilename)) if (!IFileManager::Get().FileExists(*PackageFilename))
{ {
Result.Appendf(TEXT("ERROR: Asset file not found on disk: %s\n"), *PackageFilename); UMCPServer::Printf(TEXT("ERROR: Asset file not found on disk: %s\n"), *PackageFilename);
return; return;
} }
@@ -59,22 +60,22 @@ public:
if (Referencers.Num() > 0 && !Force) if (Referencers.Num() > 0 && !Force)
{ {
Result.Appendf(TEXT("ERROR: Asset is still referenced by %d package(s):\n"), Referencers.Num()); UMCPServer::Printf(TEXT("ERROR: Asset is still referenced by %d package(s):\n"), Referencers.Num());
for (const FName& Ref : Referencers) for (const FName& Ref : Referencers)
{ {
FString RefStr = Ref.ToString(); FString RefStr = Ref.ToString();
UPackage* RefPackage = FindPackage(nullptr, *RefStr); UPackage* RefPackage = FindPackage(nullptr, *RefStr);
Result.Appendf(TEXT(" %s%s\n"), *RefStr, UMCPServer::Printf(TEXT(" %s%s\n"), *RefStr,
RefPackage ? TEXT(" (loaded)") : TEXT(" (on-disk only)")); RefPackage ? TEXT(" (loaded)") : TEXT(" (on-disk only)"));
} }
Result.Append(TEXT("Use force=true to skip the reference check.\n")); UMCPServer::Print(TEXT("Use force=true to skip the reference check.\n"));
return; return;
} }
// Force delete: unload the package from memory first // Force delete: unload the package from memory first
if (Force && Referencers.Num() > 0) if (Force && Referencers.Num() > 0)
{ {
Result.Appendf(TEXT("WARNING: Force-deleting despite %d referencer(s).\n"), Referencers.Num()); UMCPServer::Printf(TEXT("WARNING: Force-deleting despite %d referencer(s).\n"), Referencers.Num());
} }
if (Force) if (Force)
@@ -110,7 +111,7 @@ public:
if (!bDeleted) if (!bDeleted)
{ {
Result.Appendf(TEXT("ERROR: Failed to delete file from disk: %s\n"), *PackageFilename); UMCPServer::Printf(TEXT("ERROR: Failed to delete file from disk: %s\n"), *PackageFilename);
return; return;
} }
@@ -123,6 +124,6 @@ public:
Registry.ScanPathsSynchronous({PackageDir}, true); Registry.ScanPathsSynchronous({PackageDir}, true);
} }
Result.Appendf(TEXT("Deleted %s\n"), *AssetPath); UMCPServer::Printf(TEXT("Deleted %s\n"), *AssetPath);
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "AssetRegistry/AssetData.h" #include "AssetRegistry/AssetData.h"
@@ -26,7 +27,7 @@ public:
return TEXT("Find all assets that reference a given asset."); return TEXT("Find all assets that reference a given asset.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
IAssetRegistry& Registry = *IAssetRegistry::Get(); IAssetRegistry& Registry = *IAssetRegistry::Get();
@@ -34,7 +35,7 @@ public:
FAssetData AssetData = Registry.GetAssetByObjectPath(FSoftObjectPath(AssetPath)); FAssetData AssetData = Registry.GetAssetByObjectPath(FSoftObjectPath(AssetPath));
if (!AssetData.IsValid()) if (!AssetData.IsValid())
{ {
Result.Appendf(TEXT("ERROR: Asset not found: %s\n"), *AssetPath); UMCPServer::Printf(TEXT("ERROR: Asset not found: %s\n"), *AssetPath);
return; return;
} }
@@ -43,7 +44,7 @@ public:
if (Referencers.Num() == 0) if (Referencers.Num() == 0)
{ {
Result.Append(TEXT("No referencers found.\n")); UMCPServer::Print(TEXT("No referencers found.\n"));
return; return;
} }
@@ -55,13 +56,13 @@ public:
Registry.GetAssetsByPackageName(Ref, RefAssets); Registry.GetAssetsByPackageName(Ref, RefAssets);
if (RefAssets.Num() > 0) if (RefAssets.Num() > 0)
{ {
Result.Appendf(TEXT("%s %s\n"), UMCPServer::Printf(TEXT("%s %s\n"),
*MCPUtils::FormatName(RefAssets[0].GetClass()), *MCPUtils::FormatName(RefAssets[0].GetClass()),
*RefStr); *RefStr);
} }
else else
{ {
Result.Appendf(TEXT("Unknown %s\n"), *RefStr); UMCPServer::Printf(TEXT("Unknown %s\n"), *RefStr);
} }
} }
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -30,7 +31,7 @@ public:
return TEXT("Rename or move an asset with reference fixup."); return TEXT("Rename or move an asset with reference fixup.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
// Load the asset // Load the asset
MCPAssets<UObject> Assets; MCPAssets<UObject> Assets;
@@ -47,7 +48,7 @@ public:
NewAssetName = NewPath; NewAssetName = NewPath;
if (NewPackagePath.IsEmpty()) if (NewPackagePath.IsEmpty())
{ {
Result.Appendf(TEXT("ERROR: Cannot determine directory from AssetPath '%s'\n"), *AssetPath); UMCPServer::Printf(TEXT("ERROR: Cannot determine directory from AssetPath '%s'\n"), *AssetPath);
return; return;
} }
} }
@@ -61,10 +62,10 @@ public:
if (!AssetTools.RenameAssets(RenameData)) if (!AssetTools.RenameAssets(RenameData))
{ {
Result.Append(TEXT("ERROR: Rename failed. The target path may be invalid or a conflicting asset may exist.\n")); UMCPServer::Print(TEXT("ERROR: Rename failed. The target path may be invalid or a conflicting asset may exist.\n"));
return; return;
} }
Result.Appendf(TEXT("Renamed to %s/%s\n"), *NewPackagePath, *NewAssetName); UMCPServer::Printf(TEXT("Renamed to %s/%s\n"), *NewPackagePath, *NewAssetName);
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Misc/PackageName.h" #include "Misc/PackageName.h"
@@ -28,7 +29,7 @@ public:
return TEXT("Restore a .uasset file from its .uasset.bak backup, reloading it in the editor."); return TEXT("Restore a .uasset file from its .uasset.bak backup, reloading it in the editor.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
FString Filename = FPaths::ConvertRelativePathToFull( FString Filename = FPaths::ConvertRelativePathToFull(
FPackageName::LongPackageNameToFilename(AssetPath, FPackageName::GetAssetPackageExtension())); FPackageName::LongPackageNameToFilename(AssetPath, FPackageName::GetAssetPackageExtension()));
@@ -36,7 +37,7 @@ public:
if (!IFileManager::Get().FileExists(*BackupFilename)) if (!IFileManager::Get().FileExists(*BackupFilename))
{ {
Result.Appendf(TEXT("ERROR: Backup file not found: %s\n"), *BackupFilename); UMCPServer::Printf(TEXT("ERROR: Backup file not found: %s\n"), *BackupFilename);
return; return;
} }
@@ -51,7 +52,7 @@ public:
uint32 CopyResult = IFileManager::Get().Copy(*Filename, *BackupFilename, true); uint32 CopyResult = IFileManager::Get().Copy(*Filename, *BackupFilename, true);
if (CopyResult != COPY_OK) if (CopyResult != COPY_OK)
{ {
Result.Appendf(TEXT("ERROR: Failed to copy backup over %s\n"), *AssetPath); UMCPServer::Printf(TEXT("ERROR: Failed to copy backup over %s\n"), *AssetPath);
return; return;
} }
@@ -63,12 +64,12 @@ public:
UEditorLoadingAndSavingUtils::ReloadPackages({Package}, bReloaded, ErrorMessage, EReloadPackagesInteractionMode::AssumePositive); UEditorLoadingAndSavingUtils::ReloadPackages({Package}, bReloaded, ErrorMessage, EReloadPackagesInteractionMode::AssumePositive);
if (!bReloaded) if (!bReloaded)
{ {
Result.Appendf(TEXT("WARNING: Restored %s but reload failed: %s\n"), UMCPServer::Printf(TEXT("WARNING: Restored %s but reload failed: %s\n"),
*AssetPath, *ErrorMessage.ToString()); *AssetPath, *ErrorMessage.ToString());
return; return;
} }
} }
Result.Appendf(TEXT("Restored %s from backup\n"), *AssetPath); UMCPServer::Printf(TEXT("Restored %s from backup\n"), *AssetPath);
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -31,11 +32,11 @@ public:
return TEXT("Search for assets by name and/or type. At least one of Query or Type must be specified."); return TEXT("Search for assets by name and/or type. At least one of Query or Type must be specified.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
if (Query.IsEmpty() && Type.IsEmpty()) if (Query.IsEmpty() && Type.IsEmpty())
{ {
Result.Append(TEXT("ERROR: At least one of Query or Type must be specified\n")); UMCPServer::Print(TEXT("ERROR: At least one of Query or Type must be specified\n"));
return; return;
} }
@@ -47,7 +48,7 @@ public:
UClass* TypeClass = MCPUtils::FindClassByName(Type); UClass* TypeClass = MCPUtils::FindClassByName(Type);
if (!TypeClass) if (!TypeClass)
{ {
Result.Appendf(TEXT("ERROR: Unknown asset type '%s'\n"), *Type); UMCPServer::Printf(TEXT("ERROR: Unknown asset type '%s'\n"), *Type);
return; return;
} }
Assets.NoScans().Scan(TypeClass); Assets.NoScans().Scan(TypeClass);
@@ -63,18 +64,18 @@ public:
const TArray<FAssetData>& AllData = Assets.AllData(); const TArray<FAssetData>& AllData = Assets.AllData();
for (const FAssetData& Data : AllData) for (const FAssetData& Data : AllData)
{ {
Result.Appendf(TEXT("%s %s\n"), UMCPServer::Printf(TEXT("%s %s\n"),
*MCPUtils::FormatName(Data.GetClass()), *MCPUtils::FormatName(Data.GetClass()),
*Data.PackageName.ToString()); *Data.PackageName.ToString());
} }
if (AllData.Num() == 0) if (AllData.Num() == 0)
{ {
Result.Append(TEXT("No assets found.\n")); UMCPServer::Print(TEXT("No assets found.\n"));
} }
else if (AllData.Num() >= Limit) else if (AllData.Num() >= Limit)
{ {
Result.Appendf(TEXT("WARNING: You reached the limit of %d, to raise it, specify the Limit parameter.\n"), Limit); UMCPServer::Printf(TEXT("WARNING: You reached the limit of %d, to raise it, specify the Limit parameter.\n"), Limit);
} }
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -31,7 +32,7 @@ public:
return TEXT("Create a new 2D Blend Space asset with a specified skeleton."); return TEXT("Create a new 2D Blend Space asset with a specified skeleton.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPPackageMaker Maker(AssetPath); MCPPackageMaker Maker(AssetPath);
if (!Maker.Ok()) return; if (!Maker.Ok()) return;
@@ -46,7 +47,7 @@ public:
UBlendSpace* NewBS = NewObject<UBlendSpace>(Maker.Package(), FName(*Maker.Name()), RF_Public | RF_Standalone); UBlendSpace* NewBS = NewObject<UBlendSpace>(Maker.Package(), FName(*Maker.Name()), RF_Public | RF_Standalone);
if (!NewBS) if (!NewBS)
{ {
Result.Append(TEXT("ERROR: Failed to create Blend Space object\n")); UMCPServer::Print(TEXT("ERROR: Failed to create Blend Space object\n"));
return; return;
} }
@@ -56,9 +57,9 @@ public:
NewBS->MarkPackageDirty(); NewBS->MarkPackageDirty();
bool bSaved = MCPUtils::SaveGenericPackage(NewBS); bool bSaved = MCPUtils::SaveGenericPackage(NewBS);
Result.Appendf(TEXT("Created %s\n"), *NewBS->GetPathName()); UMCPServer::Printf(TEXT("Created %s\n"), *NewBS->GetPathName());
Result.Appendf(TEXT("Skeleton: %s\n"), *SkeletonObj->GetPathName()); UMCPServer::Printf(TEXT("Skeleton: %s\n"), *SkeletonObj->GetPathName());
if (!bSaved) if (!bSaved)
Result.Append(TEXT("WARNING: Package save failed\n")); UMCPServer::Print(TEXT("WARNING: Package save failed\n"));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -37,11 +38,11 @@ public:
return TEXT("Create a new function, macro, or custom event graph in a Blueprint."); return TEXT("Create a new function, macro, or custom event graph in a Blueprint.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
if (GraphType != TEXT("function") && GraphType != TEXT("macro") && GraphType != TEXT("customEvent")) if (GraphType != TEXT("function") && GraphType != TEXT("macro") && GraphType != TEXT("customEvent"))
{ {
Result.Appendf(TEXT("ERROR: Invalid GraphType '%s'. Valid values: function, macro, customEvent\n"), *GraphType); UMCPServer::Printf(TEXT("ERROR: Invalid GraphType '%s'. Valid values: function, macro, customEvent\n"), *GraphType);
return; return;
} }
@@ -52,7 +53,7 @@ public:
// Check graph name uniqueness // Check graph name uniqueness
if (!MCPUtils::AllGraphsNamed(BP, Graph).IsEmpty()) if (!MCPUtils::AllGraphsNamed(BP, Graph).IsEmpty())
{ {
Result.Appendf(TEXT("ERROR: A graph named '%s' already exists in %s\n"), *Graph, *MCPUtils::FormatName(BP)); UMCPServer::Printf(TEXT("ERROR: A graph named '%s' already exists in %s\n"), *Graph, *MCPUtils::FormatName(BP));
return; return;
} }
@@ -63,7 +64,7 @@ public:
{ {
if (CE->CustomFunctionName == FName(*Graph)) if (CE->CustomFunctionName == FName(*Graph))
{ {
Result.Appendf(TEXT("ERROR: A custom event named '%s' already exists in %s\n"), *Graph, *MCPUtils::FormatName(BP)); UMCPServer::Printf(TEXT("ERROR: A custom event named '%s' already exists in %s\n"), *Graph, *MCPUtils::FormatName(BP));
return; return;
} }
} }
@@ -75,11 +76,11 @@ public:
UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
if (!NewGraph) if (!NewGraph)
{ {
Result.Append(TEXT("ERROR: Failed to create function graph\n")); UMCPServer::Print(TEXT("ERROR: Failed to create function graph\n"));
return; return;
} }
FBlueprintEditorUtils::AddFunctionGraph(BP, NewGraph, /*bIsUserCreated=*/true, /*SignatureFromObject=*/static_cast<UClass*>(nullptr)); FBlueprintEditorUtils::AddFunctionGraph(BP, NewGraph, /*bIsUserCreated=*/true, /*SignatureFromObject=*/static_cast<UClass*>(nullptr));
Result.Appendf(TEXT("Created function graph: %s\n"), *MCPUtils::FormatName(NewGraph)); UMCPServer::Printf(TEXT("Created function graph: %s\n"), *MCPUtils::FormatName(NewGraph));
} }
else if (GraphType == TEXT("macro")) else if (GraphType == TEXT("macro"))
{ {
@@ -87,11 +88,11 @@ public:
UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
if (!NewGraph) if (!NewGraph)
{ {
Result.Append(TEXT("ERROR: Failed to create macro graph\n")); UMCPServer::Print(TEXT("ERROR: Failed to create macro graph\n"));
return; return;
} }
FBlueprintEditorUtils::AddMacroGraph(BP, NewGraph, /*bIsUserCreated=*/true, /*SignatureFromClass=*/nullptr); FBlueprintEditorUtils::AddMacroGraph(BP, NewGraph, /*bIsUserCreated=*/true, /*SignatureFromClass=*/nullptr);
Result.Appendf(TEXT("Created macro graph: %s\n"), *MCPUtils::FormatName(NewGraph)); UMCPServer::Printf(TEXT("Created macro graph: %s\n"), *MCPUtils::FormatName(NewGraph));
} }
else // customEvent else // customEvent
{ {
@@ -100,7 +101,7 @@ public:
EventGraph = BP->UbergraphPages[0]; EventGraph = BP->UbergraphPages[0];
if (!EventGraph) if (!EventGraph)
{ {
Result.Append(TEXT("ERROR: Blueprint has no EventGraph to add a custom event to\n")); UMCPServer::Print(TEXT("ERROR: Blueprint has no EventGraph to add a custom event to\n"));
return; return;
} }
@@ -111,7 +112,7 @@ public:
NewEvent->CreateNewGuid(); NewEvent->CreateNewGuid();
NewEvent->PostPlacedNewNode(); NewEvent->PostPlacedNewNode();
NewEvent->AllocateDefaultPins(); NewEvent->AllocateDefaultPins();
Result.Appendf(TEXT("Created custom event: %s\n"), *MCPUtils::FormatName(NewEvent)); UMCPServer::Printf(TEXT("Created custom event: %s\n"), *MCPUtils::FormatName(NewEvent));
} }
MCPUtils::SaveBlueprintPackage(BP); MCPUtils::SaveBlueprintPackage(BP);

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -31,7 +32,7 @@ public:
return TEXT("Delete a function or macro graph from a Blueprint. Cannot delete EventGraph pages."); return TEXT("Delete a function or macro graph from a Blueprint. Cannot delete EventGraph pages.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
F.Walk(Path); F.Walk(Path);
@@ -73,12 +74,12 @@ public:
{ {
if (G && MCPUtils::Identifies(Graph, G)) if (G && MCPUtils::Identifies(Graph, G))
{ {
Result.Appendf(TEXT("ERROR: Cannot delete UbergraphPage '%s'. EventGraph pages cannot be deleted.\n"), UMCPServer::Printf(TEXT("ERROR: Cannot delete UbergraphPage '%s'. EventGraph pages cannot be deleted.\n"),
*MCPUtils::FormatName(G)); *MCPUtils::FormatName(G));
return; return;
} }
} }
Result.Appendf(TEXT("ERROR: Graph '%s' not found in blueprint %s\n"), UMCPServer::Printf(TEXT("ERROR: Graph '%s' not found in blueprint %s\n"),
*Graph, *MCPUtils::FormatName(BP)); *Graph, *MCPUtils::FormatName(BP));
return; return;
} }
@@ -88,8 +89,8 @@ public:
FBlueprintEditorUtils::RemoveGraph(BP, TargetGraph, EGraphRemoveFlags::Default); FBlueprintEditorUtils::RemoveGraph(BP, TargetGraph, EGraphRemoveFlags::Default);
bool bSaved = MCPUtils::SaveBlueprintPackage(BP); bool bSaved = MCPUtils::SaveBlueprintPackage(BP);
Result.Appendf(TEXT("Deleted %s graph %s\n"), *GraphType, *GraphName); UMCPServer::Printf(TEXT("Deleted %s graph %s\n"), *GraphType, *GraphName);
if (!bSaved) if (!bSaved)
Result.Append(TEXT("WARNING: Package save failed.\n")); UMCPServer::Print(TEXT("WARNING: Package save failed.\n"));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -31,7 +32,7 @@ public:
return TEXT("Rename a function or macro graph in a Blueprint. Cannot rename EventGraph pages."); return TEXT("Rename a function or macro graph in a Blueprint. Cannot rename EventGraph pages.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>(); UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>();
@@ -40,14 +41,14 @@ public:
UBlueprint* BP = Cast<UBlueprint>(TargetGraph->GetOuter()); UBlueprint* BP = Cast<UBlueprint>(TargetGraph->GetOuter());
if (!BP) if (!BP)
{ {
Result.Appendf(TEXT("Error: Graph '%s' is not owned by a Blueprint.\n"), *Graph); UMCPServer::Printf(TEXT("Error: Graph '%s' is not owned by a Blueprint.\n"), *Graph);
return; return;
} }
// Check if it's an UbergraphPage -- disallow rename // Check if it's an UbergraphPage -- disallow rename
if (BP->UbergraphPages.Contains(TargetGraph)) if (BP->UbergraphPages.Contains(TargetGraph))
{ {
Result.Appendf(TEXT("Error: Cannot rename UbergraphPage '%s'. EventGraph pages cannot be renamed.\n"), UMCPServer::Printf(TEXT("Error: Cannot rename UbergraphPage '%s'. EventGraph pages cannot be renamed.\n"),
*MCPUtils::FormatName(TargetGraph)); *MCPUtils::FormatName(TargetGraph));
return; return;
} }
@@ -57,7 +58,7 @@ public:
bool bIsMacro = BP->MacroGraphs.Contains(TargetGraph); bool bIsMacro = BP->MacroGraphs.Contains(TargetGraph);
if (!bIsFunction && !bIsMacro) if (!bIsFunction && !bIsMacro)
{ {
Result.Appendf(TEXT("Error: Graph '%s' is not a function or macro graph.\n"), UMCPServer::Printf(TEXT("Error: Graph '%s' is not a function or macro graph.\n"),
*MCPUtils::FormatName(TargetGraph)); *MCPUtils::FormatName(TargetGraph));
return; return;
} }
@@ -67,7 +68,7 @@ public:
{ {
if (Existing != TargetGraph) if (Existing != TargetGraph)
{ {
Result.Appendf(TEXT("Error: A graph named '%s' already exists in '%s'.\n"), UMCPServer::Printf(TEXT("Error: A graph named '%s' already exists in '%s'.\n"),
*NewName, *MCPUtils::FormatName(BP)); *NewName, *MCPUtils::FormatName(BP));
return; return;
} }
@@ -76,7 +77,7 @@ public:
FBlueprintEditorUtils::RenameGraph(TargetGraph, NewName); FBlueprintEditorUtils::RenameGraph(TargetGraph, NewName);
MCPUtils::SaveBlueprintPackage(BP); MCPUtils::SaveBlueprintPackage(BP);
Result.Appendf(TEXT("Renamed to %s %s\n"), UMCPServer::Printf(TEXT("Renamed to %s %s\n"),
bIsFunction ? TEXT("function") : TEXT("macro"), bIsFunction ? TEXT("function") : TEXT("macro"),
*MCPUtils::FormatName(TargetGraph)); *MCPUtils::FormatName(TargetGraph));
} }

View File

@@ -40,7 +40,7 @@ public:
"Optionally attach it to an existing parent component."); "Optionally attach it to an existing parent component.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPAssets<UBlueprint> Assets; MCPAssets<UBlueprint> Assets;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
@@ -123,13 +123,13 @@ public:
bool bSaved = MCPUtils::SaveBlueprintPackage(BP); bool bSaved = MCPUtils::SaveBlueprintPackage(BP);
Result.Appendf(TEXT("Added component %s (%s)"), UMCPServer::Printf(TEXT("Added component %s (%s)"),
*MCPUtils::FormatName(NewNode->ComponentTemplate), *MCPUtils::FormatName(NewNode->ComponentTemplate),
*MCPUtils::FormatName(ComponentClassObj)); *MCPUtils::FormatName(ComponentClassObj));
if (ParentSCSNode) if (ParentSCSNode)
{ {
Result.Appendf(TEXT(" under %s"), *MCPUtils::FormatName(ParentSCSNode->ComponentTemplate)); UMCPServer::Printf(TEXT(" under %s"), *MCPUtils::FormatName(ParentSCSNode->ComponentTemplate));
} }
Result.Appendf(TEXT("\nSaved: %s\n"), bSaved ? TEXT("true") : TEXT("false")); UMCPServer::Printf(TEXT("\nSaved: %s\n"), bSaved ? TEXT("true") : TEXT("false"));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -49,7 +50,7 @@ public:
return TEXT("Create a new multicast event dispatcher on a Blueprint, optionally with parameters."); return TEXT("Create a new multicast event dispatcher on a Blueprint, optionally with parameters.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UBlueprint* BP = F.Walk(Path).Cast<UBlueprint>(); UBlueprint* BP = F.Walk(Path).Cast<UBlueprint>();
@@ -62,7 +63,7 @@ public:
{ {
if (Var.VarName == DispatcherFName) if (Var.VarName == DispatcherFName)
{ {
Result.Appendf(TEXT("Error: A variable or dispatcher named '%s' already exists.\n"), *DispatcherName); UMCPServer::Printf(TEXT("Error: A variable or dispatcher named '%s' already exists.\n"), *DispatcherName);
return; return;
} }
} }
@@ -70,7 +71,7 @@ public:
// Check against existing graphs (functions, macros, etc.) // Check against existing graphs (functions, macros, etc.)
if (!MCPUtils::AllGraphsNamed(BP, DispatcherName).IsEmpty()) if (!MCPUtils::AllGraphsNamed(BP, DispatcherName).IsEmpty())
{ {
Result.Appendf(TEXT("Error: A graph named '%s' already exists.\n"), *DispatcherName); UMCPServer::Printf(TEXT("Error: A graph named '%s' already exists.\n"), *DispatcherName);
return; return;
} }
@@ -79,7 +80,7 @@ public:
DelegateType.PinCategory = UEdGraphSchema_K2::PC_MCDelegate; DelegateType.PinCategory = UEdGraphSchema_K2::PC_MCDelegate;
if (!FBlueprintEditorUtils::AddMemberVariable(BP, DispatcherFName, DelegateType)) if (!FBlueprintEditorUtils::AddMemberVariable(BP, DispatcherFName, DelegateType))
{ {
Result.Appendf(TEXT("Error: Failed to add delegate variable for '%s'.\n"), *DispatcherName); UMCPServer::Printf(TEXT("Error: Failed to add delegate variable for '%s'.\n"), *DispatcherName);
return; return;
} }
@@ -90,7 +91,7 @@ public:
UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
if (!SigGraph) if (!SigGraph)
{ {
Result.Append(TEXT("Error: Failed to create delegate signature graph.\n")); UMCPServer::Print(TEXT("Error: Failed to create delegate signature graph.\n"));
return; return;
} }
@@ -116,7 +117,7 @@ public:
if (!EntryNode) if (!EntryNode)
{ {
MCPUtils::SaveBlueprintPackage(BP); MCPUtils::SaveBlueprintPackage(BP);
Result.Append(TEXT("Error: Event dispatcher created but entry node not found — parameters could not be added.\n")); UMCPServer::Print(TEXT("Error: Event dispatcher created but entry node not found — parameters could not be added.\n"));
return; return;
} }
@@ -137,9 +138,9 @@ public:
MCPUtils::SaveBlueprintPackage(BP); MCPUtils::SaveBlueprintPackage(BP);
Result.Appendf(TEXT("Created event dispatcher '%s'"), *DispatcherName); UMCPServer::Printf(TEXT("Created event dispatcher '%s'"), *DispatcherName);
if (ParamCount > 0) if (ParamCount > 0)
Result.Appendf(TEXT(" with %d parameter(s)"), ParamCount); UMCPServer::Printf(TEXT(" with %d parameter(s)"), ParamCount);
Result.Append(TEXT(".\n")); UMCPServer::Print(TEXT(".\n"));
} }
}; };

View File

@@ -42,7 +42,7 @@ public:
return TEXT("Add a new parameter to a function, custom event, or event dispatcher in a Blueprint."); return TEXT("Add a new parameter to a function, custom event, or event dispatcher in a Blueprint.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPAssets<UBlueprint> Assets; MCPAssets<UBlueprint> Assets;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
@@ -135,7 +135,7 @@ public:
EntryNode->CreateUserDefinedPin(FName(*ParamName), PinType, EGPD_Output); EntryNode->CreateUserDefinedPin(FName(*ParamName), PinType, EGPD_Output);
bool bSaved = MCPUtils::SaveBlueprintPackage(BP); bool bSaved = MCPUtils::SaveBlueprintPackage(BP);
Result.Appendf(TEXT("Added %s parameter '%s' to %s '%s'%s\n"), UMCPServer::Printf(TEXT("Added %s parameter '%s' to %s '%s'%s\n"),
*ParamType, *ParamName, *NodeType, *FunctionName, *ParamType, *ParamName, *NodeType, *FunctionName,
bSaved ? TEXT("") : TEXT(" (WARNING: save failed)")); bSaved ? TEXT("") : TEXT(" (WARNING: save failed)"));
} }

View File

@@ -33,14 +33,14 @@ public:
"Creates stub function graphs for each interface function."); "Creates stub function graphs for each interface function.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPAssets<UBlueprint> Assets; MCPAssets<UBlueprint> Assets;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
UBlueprint* BP = Assets.Object(); UBlueprint* BP = Assets.Object();
// Resolve the interface class // Resolve the interface class
UClass* InterfaceClass = FindInterfaceClass(InterfaceName, Result); UClass* InterfaceClass = FindInterfaceClass(InterfaceName);
if (!InterfaceClass) return; if (!InterfaceClass) return;
// Check for duplicates // Check for duplicates
@@ -64,15 +64,15 @@ public:
} }
// Collect stub function graph names from the newly added interface entry // Collect stub function graph names from the newly added interface entry
Result.Appendf(TEXT("Added interface %s\n"), *MCPUtils::FormatName(InterfaceClass)); UMCPServer::Printf(TEXT("Added interface %s\n"), *MCPUtils::FormatName(InterfaceClass));
Result.Appendf(TEXT("Function stubs:\n")); UMCPServer::Printf(TEXT("Function stubs:\n"));
for (const FBPInterfaceDescription& IfaceDesc : BP->ImplementedInterfaces) for (const FBPInterfaceDescription& IfaceDesc : BP->ImplementedInterfaces)
{ {
if (IfaceDesc.Interface != InterfaceClass) continue; if (IfaceDesc.Interface != InterfaceClass) continue;
for (const UEdGraph* Graph : IfaceDesc.Graphs) for (const UEdGraph* Graph : IfaceDesc.Graphs)
{ {
if (Graph) if (Graph)
Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Graph)); UMCPServer::Printf(TEXT(" %s\n"), *MCPUtils::FormatName(Graph));
} }
break; break;
} }
@@ -82,7 +82,7 @@ private:
// Resolve an interface name to a UClass. Tries loaded UInterface classes // Resolve an interface name to a UClass. Tries loaded UInterface classes
// first (for native interfaces), then falls back to loading a Blueprint // first (for native interfaces), then falls back to loading a Blueprint
// Interface asset. // Interface asset.
static UClass* FindInterfaceClass(const FString& Name, FStringBuilderBase& Result) static UClass* FindInterfaceClass(const FString& Name)
{ {
// Strategy 1: Search loaded UInterface classes by name // Strategy 1: Search loaded UInterface classes by name
for (TObjectIterator<UClass> It; It; ++It) for (TObjectIterator<UClass> It; It; ++It)

View File

@@ -43,7 +43,7 @@ public:
return TEXT("Add a new member variable to a Blueprint."); return TEXT("Add a new member variable to a Blueprint.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPAssets<UBlueprint> Assets; MCPAssets<UBlueprint> Assets;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
@@ -80,13 +80,13 @@ public:
bool bSaved = MCPUtils::SaveBlueprintPackage(BP); bool bSaved = MCPUtils::SaveBlueprintPackage(BP);
Result.Appendf(TEXT("Added %s %s to %s\n"), UMCPServer::Printf(TEXT("Added %s %s to %s\n"),
*VariableType, *VariableName, *MCPUtils::FormatName(BP)); *VariableType, *VariableName, *MCPUtils::FormatName(BP));
if (IsArray) if (IsArray)
Result.Append(TEXT("Container: Array\n")); UMCPServer::Print(TEXT("Container: Array\n"));
if (!Category.IsEmpty()) if (!Category.IsEmpty())
Result.Appendf(TEXT("Category: %s\n"), *Category); UMCPServer::Printf(TEXT("Category: %s\n"), *Category);
if (!bSaved) if (!bSaved)
Result.Append(TEXT("Warning: package save failed\n")); UMCPServer::Print(TEXT("Warning: package save failed\n"));
} }
}; };

View File

@@ -45,7 +45,7 @@ public:
return TEXT("Change the type of an existing parameter on a function or custom event in a Blueprint."); return TEXT("Change the type of an existing parameter on a function or custom event in a Blueprint.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPAssets<UBlueprint> Assets; MCPAssets<UBlueprint> Assets;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
@@ -123,14 +123,14 @@ public:
for (UEdGraphPin* Linked : Pin->LinkedTo) for (UEdGraphPin* Linked : Pin->LinkedTo)
{ {
if (!Linked || !Linked->GetOwningNode()) continue; if (!Linked || !Linked->GetOwningNode()) continue;
Result.Appendf(TEXT("Connection at risk: %s -> %s on %s\n"), UMCPServer::Printf(TEXT("Connection at risk: %s -> %s on %s\n"),
*MCPUtils::FormatName(Pin), *MCPUtils::FormatName(Pin),
*MCPUtils::FormatName(Linked), *MCPUtils::FormatName(Linked),
*MCPUtils::FormatName(Linked->GetOwningNode())); *MCPUtils::FormatName(Linked->GetOwningNode()));
AtRisk++; AtRisk++;
} }
} }
Result.Appendf(TEXT("Dry run: %d connection(s) at risk.\n"), AtRisk); UMCPServer::Printf(TEXT("Dry run: %d connection(s) at risk.\n"), AtRisk);
return; return;
} }
@@ -152,7 +152,7 @@ public:
// Save // Save
bool bSaved = MCPUtils::SaveBlueprintPackage(BP); bool bSaved = MCPUtils::SaveBlueprintPackage(BP);
Result.Appendf(TEXT("Changed '%s' to %s. Save %s.\n"), UMCPServer::Printf(TEXT("Changed '%s' to %s. Save %s.\n"),
*ParamName, *MCPUtils::FormatPinType(NewPinType), *ParamName, *MCPUtils::FormatPinType(NewPinType),
bSaved ? TEXT("succeeded") : TEXT("failed")); bSaved ? TEXT("succeeded") : TEXT("failed"));
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPAssets.h" #include "MCPAssets.h"
@@ -43,7 +44,7 @@ public:
"Supports dry-run mode to preview affected nodes before committing."); "Supports dry-run mode to preview affected nodes before committing.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
@@ -62,10 +63,10 @@ public:
} }
if (!Found) if (!Found)
{ {
Result.Appendf(TEXT("ERROR: Variable '%s' not found in %s.\nAvailable variables:\n"), UMCPServer::Printf(TEXT("ERROR: Variable '%s' not found in %s.\nAvailable variables:\n"),
*Variable, *MCPUtils::FormatName(BP)); *Variable, *MCPUtils::FormatName(BP));
for (const FBPVariableDescription& Var : BP->NewVariables) for (const FBPVariableDescription& Var : BP->NewVariables)
Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Var)); UMCPServer::Printf(TEXT(" %s\n"), *MCPUtils::FormatName(Var));
return; return;
} }
@@ -91,14 +92,14 @@ public:
for (auto* VarNode : NodeArray) for (auto* VarNode : NodeArray)
{ {
if (VarNode->GetVarName() != VarFName) continue; if (VarNode->GetVarName() != VarFName) continue;
Result.Appendf(TEXT(" %s %s in %s\n"), NodeType, UMCPServer::Printf(TEXT(" %s %s in %s\n"), NodeType,
*MCPUtils::FormatName(static_cast<UEdGraphNode*>(VarNode)), *MCPUtils::FormatName(static_cast<UEdGraphNode*>(VarNode)),
*MCPUtils::FormatName(VarNode->GetGraph())); *MCPUtils::FormatName(VarNode->GetGraph()));
for (UEdGraphPin* Pin : VarNode->Pins) for (UEdGraphPin* Pin : VarNode->Pins)
{ {
if (!Pin || Pin->LinkedTo.Num() == 0) continue; if (!Pin || Pin->LinkedTo.Num() == 0) continue;
if (NodeType[0] == 'G' && Pin->Direction != EGPD_Output) continue; // Get nodes: only output pins if (NodeType[0] == 'G' && Pin->Direction != EGPD_Output) continue; // Get nodes: only output pins
Result.Appendf(TEXT(" %s connected to %d pin(s)\n"), UMCPServer::Printf(TEXT(" %s connected to %d pin(s)\n"),
*MCPUtils::FormatName(Pin), Pin->LinkedTo.Num()); *MCPUtils::FormatName(Pin), Pin->LinkedTo.Num());
} }
} }
@@ -114,13 +115,13 @@ public:
if (DryRun) if (DryRun)
{ {
Result.Appendf(TEXT("Dry run: would change %s from %s to %s\n"), UMCPServer::Printf(TEXT("Dry run: would change %s from %s to %s\n"),
*MCPUtils::FormatName(*Found), *MCPUtils::FormatName(*Found),
*MCPUtils::FormatPinType(Found->VarType), *MCPUtils::FormatPinType(Found->VarType),
*MCPUtils::FormatPinType(NewPinType)); *MCPUtils::FormatPinType(NewPinType));
if (bHasAffected) if (bHasAffected)
{ {
Result.Append(TEXT("Affected nodes:\n")); UMCPServer::Print(TEXT("Affected nodes:\n"));
AppendAffectedNodes(GetNodes, TEXT("Get")); AppendAffectedNodes(GetNodes, TEXT("Get"));
AppendAffectedNodes(SetNodes, TEXT("Set")); AppendAffectedNodes(SetNodes, TEXT("Set"));
} }
@@ -131,14 +132,14 @@ public:
Found->VarType = NewPinType; Found->VarType = NewPinType;
bool bSaved = MCPUtils::SaveBlueprintPackage(BP); bool bSaved = MCPUtils::SaveBlueprintPackage(BP);
Result.Appendf(TEXT("Changed %s to %s.%s\n"), UMCPServer::Printf(TEXT("Changed %s to %s.%s\n"),
*MCPUtils::FormatName(*Found), *MCPUtils::FormatName(*Found),
*MCPUtils::FormatPinType(NewPinType), *MCPUtils::FormatPinType(NewPinType),
bSaved ? TEXT("") : TEXT(" WARNING: save failed.")); bSaved ? TEXT("") : TEXT(" WARNING: save failed."));
if (bHasAffected) if (bHasAffected)
{ {
Result.Append(TEXT("Affected nodes:\n")); UMCPServer::Print(TEXT("Affected nodes:\n"));
AppendAffectedNodes(GetNodes, TEXT("Get")); AppendAffectedNodes(GetNodes, TEXT("Get"));
AppendAffectedNodes(SetNodes, TEXT("Set")); AppendAffectedNodes(SetNodes, TEXT("Set"));
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -43,7 +44,7 @@ public:
// Compile a single blueprint and append results to Out. // Compile a single blueprint and append results to Out.
// Returns true if the blueprint compiled cleanly (no errors). // Returns true if the blueprint compiled cleanly (no errors).
static bool CompileAndReport(UBlueprint* BP, FStringBuilderBase& Out) static bool CompileAndReport(UBlueprint* BP)
{ {
EBlueprintCompileOptions CompileOpts = EBlueprintCompileOptions CompileOpts =
EBlueprintCompileOptions::SkipSave | EBlueprintCompileOptions::SkipSave |
@@ -61,7 +62,7 @@ public:
if (!Node->bHasCompilerMessage) continue; if (!Node->bHasCompilerMessage) continue;
bool bIsError = (Node->ErrorType == EMessageSeverity::Error); bool bIsError = (Node->ErrorType == EMessageSeverity::Error);
if (bIsError) ErrorCount++; else WarningCount++; if (bIsError) ErrorCount++; else WarningCount++;
Out.Appendf(TEXT(" %s: [%s] %s > %s: %s\n"), UMCPServer::Printf(TEXT(" %s: [%s] %s > %s: %s\n"),
bIsError ? TEXT("ERROR") : TEXT("WARNING"), bIsError ? TEXT("ERROR") : TEXT("WARNING"),
*MCPUtils::FormatName(Node->GetGraph()), *MCPUtils::FormatName(Node->GetGraph()),
*MCPUtils::FormatName(Node), *MCPUtils::FormatName(Node),
@@ -74,18 +75,18 @@ public:
if (bIsValid && WarningCount == 0) if (bIsValid && WarningCount == 0)
{ {
Out.Appendf(TEXT(" OK (status: %s)\n"), *StatusStr); UMCPServer::Printf(TEXT(" OK (status: %s)\n"), *StatusStr);
} }
else else
{ {
Out.Appendf(TEXT(" status: %s, errors: %d, warnings: %d\n"), UMCPServer::Printf(TEXT(" status: %s, errors: %d, warnings: %d\n"),
*StatusStr, ErrorCount, WarningCount); *StatusStr, ErrorCount, WarningCount);
} }
return bIsValid; return bIsValid;
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPAssets<UBlueprint> Finder; MCPAssets<UBlueprint> Finder;
Finder.Scan<UBlueprint>().Scan<UWorld>(); Finder.Scan<UBlueprint>().Scan<UWorld>();
@@ -104,7 +105,7 @@ public:
// countOnly: return count without compiling anything // countOnly: return count without compiling anything
if (CountOnly) if (CountOnly)
{ {
Result.Appendf(TEXT("Matching blueprints: %d\n"), TotalMatching); UMCPServer::Printf(TEXT("Matching blueprints: %d\n"), TotalMatching);
return; return;
} }
@@ -131,12 +132,12 @@ public:
UBlueprint* BP = Loader.Object(); UBlueprint* BP = Loader.Object();
TotalChecked++; TotalChecked++;
Result.Appendf(TEXT("%s:\n"), *MCPUtils::FormatName(BP)); UMCPServer::Printf(TEXT("%s:\n"), *MCPUtils::FormatName(BP));
bool bValid = CompileAndReport(BP, Result); bool bValid = CompileAndReport(BP);
if (bValid) TotalPassed++; else TotalFailed++; if (bValid) TotalPassed++; else TotalFailed++;
} }
Result.Appendf(TEXT("\nSummary: %d checked, %d passed, %d failed (of %d matching)\n"), UMCPServer::Printf(TEXT("\nSummary: %d checked, %d passed, %d failed (of %d matching)\n"),
TotalChecked, TotalPassed, TotalFailed, TotalMatching); TotalChecked, TotalPassed, TotalFailed, TotalMatching);
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -34,7 +35,7 @@ public:
return TEXT("Create a new Blueprint asset with a specified parent class and type."); return TEXT("Create a new Blueprint asset with a specified parent class and type.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPPackageMaker Maker(AssetPath); MCPPackageMaker Maker(AssetPath);
if (!Maker.Ok()) return; if (!Maker.Ok()) return;
@@ -93,11 +94,11 @@ public:
bool bSaved = MCPUtils::SaveBlueprintPackage(NewBP); bool bSaved = MCPUtils::SaveBlueprintPackage(NewBP);
// Report result // Report result
Result.Appendf(TEXT("Created: %s\n"), *MCPUtils::FormatName(NewBP)); UMCPServer::Printf(TEXT("Created: %s\n"), *MCPUtils::FormatName(NewBP));
Result.Appendf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentClassObj)); UMCPServer::Printf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentClassObj));
if (!bSaved) if (!bSaved)
Result.Append(TEXT("Warning: save failed\n")); UMCPServer::Print(TEXT("Warning: save failed\n"));
for (UEdGraph* Graph : MCPUtils::AllGraphs(NewBP)) for (UEdGraph* Graph : MCPUtils::AllGraphs(NewBP))
Result.Appendf(TEXT("Graph: %s\n"), *MCPUtils::FormatName(Graph)); UMCPServer::Printf(TEXT("Graph: %s\n"), *MCPUtils::FormatName(Graph));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -37,7 +38,7 @@ public:
"finding divergence after copy-paste, or auditing consistency."); "finding divergence after copy-paste, or auditing consistency.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
// Load both blueprints // Load both blueprints
MCPAssets<UBlueprint> AssetsA; MCPAssets<UBlueprint> AssetsA;
@@ -89,13 +90,13 @@ public:
if (!pGA) if (!pGA)
{ {
Result.Appendf(TEXT("Graph %s: only in B (%d nodes)\n"), *GraphName, (*pGB)->Nodes.Num()); UMCPServer::Printf(TEXT("Graph %s: only in B (%d nodes)\n"), *GraphName, (*pGB)->Nodes.Num());
TotalDiffs++; TotalDiffs++;
continue; continue;
} }
if (!pGB) if (!pGB)
{ {
Result.Appendf(TEXT("Graph %s: only in A (%d nodes)\n"), *GraphName, (*pGA)->Nodes.Num()); UMCPServer::Printf(TEXT("Graph %s: only in A (%d nodes)\n"), *GraphName, (*pGA)->Nodes.Num());
TotalDiffs++; TotalDiffs++;
continue; continue;
} }
@@ -181,32 +182,32 @@ public:
if (bIdentical) if (bIdentical)
{ {
Result.Appendf(TEXT("Graph %s: identical (%d nodes)\n"), *GraphName, GA->Nodes.Num()); UMCPServer::Printf(TEXT("Graph %s: identical (%d nodes)\n"), *GraphName, GA->Nodes.Num());
continue; continue;
} }
TotalDiffs++; TotalDiffs++;
Result.Appendf(TEXT("Graph %s: different (A=%d nodes, B=%d nodes)\n"), *GraphName, GA->Nodes.Num(), GB->Nodes.Num()); UMCPServer::Printf(TEXT("Graph %s: different (A=%d nodes, B=%d nodes)\n"), *GraphName, GA->Nodes.Num(), GB->Nodes.Num());
if (!OnlyInA.IsEmpty()) if (!OnlyInA.IsEmpty())
{ {
Result.Append(TEXT(" Nodes only in A:\n")); UMCPServer::Print(TEXT(" Nodes only in A:\n"));
for (const FString& Line : OnlyInA) Result.Appendf(TEXT(" %s\n"), *Line); for (const FString& Line : OnlyInA) UMCPServer::Printf(TEXT(" %s\n"), *Line);
} }
if (!OnlyInB.IsEmpty()) if (!OnlyInB.IsEmpty())
{ {
Result.Append(TEXT(" Nodes only in B:\n")); UMCPServer::Print(TEXT(" Nodes only in B:\n"));
for (const FString& Line : OnlyInB) Result.Appendf(TEXT(" %s\n"), *Line); for (const FString& Line : OnlyInB) UMCPServer::Printf(TEXT(" %s\n"), *Line);
} }
if (!ConnsOnlyInA.IsEmpty()) if (!ConnsOnlyInA.IsEmpty())
{ {
Result.Append(TEXT(" Connections only in A:\n")); UMCPServer::Print(TEXT(" Connections only in A:\n"));
for (const FString& Line : ConnsOnlyInA) Result.Appendf(TEXT(" %s\n"), *Line); for (const FString& Line : ConnsOnlyInA) UMCPServer::Printf(TEXT(" %s\n"), *Line);
} }
if (!ConnsOnlyInB.IsEmpty()) if (!ConnsOnlyInB.IsEmpty())
{ {
Result.Append(TEXT(" Connections only in B:\n")); UMCPServer::Print(TEXT(" Connections only in B:\n"));
for (const FString& Line : ConnsOnlyInB) Result.Appendf(TEXT(" %s\n"), *Line); for (const FString& Line : ConnsOnlyInB) UMCPServer::Printf(TEXT(" %s\n"), *Line);
} }
} }
@@ -225,17 +226,17 @@ public:
if (!VarsOnlyInA.IsEmpty()) if (!VarsOnlyInA.IsEmpty())
{ {
Result.Append(TEXT("Variables only in A:\n")); UMCPServer::Print(TEXT("Variables only in A:\n"));
for (const FString& Name : VarsOnlyInA) Result.Appendf(TEXT(" %s\n"), *Name); for (const FString& Name : VarsOnlyInA) UMCPServer::Printf(TEXT(" %s\n"), *Name);
TotalDiffs += VarsOnlyInA.Num(); TotalDiffs += VarsOnlyInA.Num();
} }
if (!VarsOnlyInB.IsEmpty()) if (!VarsOnlyInB.IsEmpty())
{ {
Result.Append(TEXT("Variables only in B:\n")); UMCPServer::Print(TEXT("Variables only in B:\n"));
for (const FString& Name : VarsOnlyInB) Result.Appendf(TEXT(" %s\n"), *Name); for (const FString& Name : VarsOnlyInB) UMCPServer::Printf(TEXT(" %s\n"), *Name);
TotalDiffs += VarsOnlyInB.Num(); TotalDiffs += VarsOnlyInB.Num();
} }
Result.Appendf(TEXT("Total differences: %d\n"), TotalDiffs); UMCPServer::Printf(TEXT("Total differences: %d\n"), TotalDiffs);
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -31,46 +32,46 @@ public:
"and graph names. Does not include graph contents (use DumpGraphs for that)."); "and graph names. Does not include graph contents (use DumpGraphs for that).");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return; if (!BP) return;
// Header // Header
Result.Appendf(TEXT("Blueprint: %s\n"), *MCPUtils::FormatName(BP)); UMCPServer::Printf(TEXT("Blueprint: %s\n"), *MCPUtils::FormatName(BP));
Result.Appendf(TEXT("Parent: %s\n"), BP->ParentClass ? *MCPUtils::FormatName(BP->ParentClass) : TEXT("None")); UMCPServer::Printf(TEXT("Parent: %s\n"), BP->ParentClass ? *MCPUtils::FormatName(BP->ParentClass) : TEXT("None"));
Result.Appendf(TEXT("Type: %s\n"), UMCPServer::Printf(TEXT("Type: %s\n"),
*MCPUtils::EnumToString(BP->BlueprintType)); *MCPUtils::EnumToString(BP->BlueprintType));
// Animation Blueprint // Animation Blueprint
if (UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP)) if (UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP))
{ {
if (AnimBP->TargetSkeleton) if (AnimBP->TargetSkeleton)
Result.Appendf(TEXT("TargetSkeleton: %s\n"), *AnimBP->TargetSkeleton->GetPathName()); UMCPServer::Printf(TEXT("TargetSkeleton: %s\n"), *AnimBP->TargetSkeleton->GetPathName());
} }
// Interfaces // Interfaces
for (const FBPInterfaceDescription& I : BP->ImplementedInterfaces) for (const FBPInterfaceDescription& I : BP->ImplementedInterfaces)
{ {
if (I.Interface) if (I.Interface)
Result.Appendf(TEXT("Interface: %s\n"), *MCPUtils::FormatName(I.Interface)); UMCPServer::Printf(TEXT("Interface: %s\n"), *MCPUtils::FormatName(I.Interface));
} }
// Variables // Variables
if (!BP->NewVariables.IsEmpty()) if (!BP->NewVariables.IsEmpty())
{ {
Result.Append(TEXT("\nVariables:\n")); UMCPServer::Print(TEXT("\nVariables:\n"));
for (const FBPVariableDescription& V : BP->NewVariables) for (const FBPVariableDescription& V : BP->NewVariables)
{ {
Result.Appendf(TEXT(" %s %s"), UMCPServer::Printf(TEXT(" %s %s"),
*MCPUtils::FormatPinType(V.VarType), *MCPUtils::FormatPinType(V.VarType),
*MCPUtils::FormatName(V)); *MCPUtils::FormatName(V));
if (!V.DefaultValue.IsEmpty()) if (!V.DefaultValue.IsEmpty())
Result.Appendf(TEXT(" = %s"), *V.DefaultValue); UMCPServer::Printf(TEXT(" = %s"), *V.DefaultValue);
if (!V.Category.IsEmpty() && V.Category.ToString() != TEXT("Default")) if (!V.Category.IsEmpty() && V.Category.ToString() != TEXT("Default"))
Result.Appendf(TEXT(" [%s]"), *V.Category.ToString()); UMCPServer::Printf(TEXT(" [%s]"), *V.Category.ToString());
Result.Append(TEXT("\n")); UMCPServer::Print(TEXT("\n"));
} }
} }
@@ -80,16 +81,16 @@ public:
const TArray<USCS_Node*>& AllNodes = SCS->GetAllNodes(); const TArray<USCS_Node*>& AllNodes = SCS->GetAllNodes();
if (!AllNodes.IsEmpty()) if (!AllNodes.IsEmpty())
{ {
Result.Append(TEXT("\nComponents:\n")); UMCPServer::Print(TEXT("\nComponents:\n"));
for (USCS_Node* Node : AllNodes) for (USCS_Node* Node : AllNodes)
{ {
if (!Node || !Node->ComponentTemplate) continue; if (!Node || !Node->ComponentTemplate) continue;
Result.Appendf(TEXT(" %s (%s)"), UMCPServer::Printf(TEXT(" %s (%s)"),
*MCPUtils::FormatName(Node->ComponentTemplate), *MCPUtils::FormatName(Node->ComponentTemplate),
*MCPUtils::FormatName(Node->ComponentClass)); *MCPUtils::FormatName(Node->ComponentClass));
if (Node->ParentComponentOrVariableName != NAME_None) if (Node->ParentComponentOrVariableName != NAME_None)
Result.Appendf(TEXT(" parent=%s"), *Node->ParentComponentOrVariableName.ToString()); UMCPServer::Printf(TEXT(" parent=%s"), *Node->ParentComponentOrVariableName.ToString());
Result.Append(TEXT("\n")); UMCPServer::Print(TEXT("\n"));
} }
} }
} }
@@ -98,9 +99,9 @@ public:
TArray<UEdGraph*> Graphs = MCPUtils::AllGraphs(BP); TArray<UEdGraph*> Graphs = MCPUtils::AllGraphs(BP);
if (!Graphs.IsEmpty()) if (!Graphs.IsEmpty())
{ {
Result.Append(TEXT("\nGraphs:\n")); UMCPServer::Print(TEXT("\nGraphs:\n"));
for (UEdGraph* Graph : Graphs) for (UEdGraph* Graph : Graphs)
Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Graph)); UMCPServer::Printf(TEXT(" %s\n"), *MCPUtils::FormatName(Graph));
} }
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -29,7 +30,7 @@ public:
"showing hierarchy and component classes."); "showing hierarchy and component classes.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
F.Walk(Path); F.Walk(Path);
@@ -41,7 +42,7 @@ public:
USimpleConstructionScript* SCS = BP->SimpleConstructionScript; USimpleConstructionScript* SCS = BP->SimpleConstructionScript;
if (!SCS) if (!SCS)
{ {
Result.Append(TEXT("ERROR: Not an Actor Blueprint (no SimpleConstructionScript)\n")); UMCPServer::Print(TEXT("ERROR: Not an Actor Blueprint (no SimpleConstructionScript)\n"));
return; return;
} }
@@ -50,11 +51,11 @@ public:
if (AllNodes.Num() == 0) if (AllNodes.Num() == 0)
{ {
Result.Append(TEXT("No components.\n")); UMCPServer::Print(TEXT("No components.\n"));
return; return;
} }
Result.Append(TEXT("WARNING: This only lists components added in this blueprint's SCS. " UMCPServer::Print(TEXT("WARNING: This only lists components added in this blueprint's SCS. "
"It does not include inherited components from C++ parent classes " "It does not include inherited components from C++ parent classes "
"(available via the CDO's OwnedComponents) or from parent blueprint SCS nodes.\n")); "(available via the CDO's OwnedComponents) or from parent blueprint SCS nodes.\n"));
@@ -62,34 +63,34 @@ public:
for (USCS_Node* Root : RootNodes) for (USCS_Node* Root : RootNodes)
{ {
if (!Root) continue; if (!Root) continue;
EmitNode(Root, 0, Root == RootNodes[0], Result); EmitNode(Root, 0, Root == RootNodes[0]);
} }
} }
private: private:
void EmitNode(USCS_Node* Node, int32 Depth, bool bIsSceneRoot, FStringBuilderBase& Result) void EmitNode(USCS_Node* Node, int32 Depth, bool bIsSceneRoot)
{ {
// Indent to show hierarchy // Indent to show hierarchy
for (int32 i = 0; i < Depth; i++) for (int32 i = 0; i < Depth; i++)
Result.Append(TEXT(" ")); UMCPServer::Print(TEXT(" "));
FString ClassName = Node->ComponentClass FString ClassName = Node->ComponentClass
? MCPUtils::FormatName(Node->ComponentClass) ? MCPUtils::FormatName(Node->ComponentClass)
: TEXT("None"); : TEXT("None");
Result.Appendf(TEXT("%s %s"), UMCPServer::Printf(TEXT("%s %s"),
*ClassName, *ClassName,
*MCPUtils::FormatName(Node->ComponentTemplate)); *MCPUtils::FormatName(Node->ComponentTemplate));
if (bIsSceneRoot && Depth == 0) if (bIsSceneRoot && Depth == 0)
Result.Append(TEXT(" [SceneRoot]")); UMCPServer::Print(TEXT(" [SceneRoot]"));
Result.Append(TEXT("\n")); UMCPServer::Print(TEXT("\n"));
for (USCS_Node* Child : Node->GetChildNodes()) for (USCS_Node* Child : Node->GetChildNodes())
{ {
if (!Child) continue; if (!Child) continue;
EmitNode(Child, Depth + 1, false, Result); EmitNode(Child, Depth + 1, false);
} }
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -31,7 +32,7 @@ public:
return TEXT("List all event dispatchers on a Blueprint, including their parameter signatures."); return TEXT("List all event dispatchers on a Blueprint, including their parameter signatures.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UBlueprint* BP = F.Walk(Path).Cast<UBlueprint>(); UBlueprint* BP = F.Walk(Path).Cast<UBlueprint>();
@@ -42,7 +43,7 @@ public:
for (const FName& DelegateName : DelegateNameSet) for (const FName& DelegateName : DelegateNameSet)
{ {
Result.Appendf(TEXT("%s("), *DelegateName.ToString()); UMCPServer::Printf(TEXT("%s("), *DelegateName.ToString());
UEdGraph* SigGraph = FBlueprintEditorUtils::GetDelegateSignatureGraphByName(BP, DelegateName); UEdGraph* SigGraph = FBlueprintEditorUtils::GetDelegateSignatureGraphByName(BP, DelegateName);
if (SigGraph) if (SigGraph)
@@ -53,9 +54,9 @@ public:
for (const TSharedPtr<FUserPinInfo>& PinInfo : FE->UserDefinedPins) for (const TSharedPtr<FUserPinInfo>& PinInfo : FE->UserDefinedPins)
{ {
if (!PinInfo.IsValid()) continue; if (!PinInfo.IsValid()) continue;
if (!bFirst) Result.Append(TEXT(", ")); if (!bFirst) UMCPServer::Print(TEXT(", "));
bFirst = false; bFirst = false;
Result.Appendf(TEXT("%s %s"), UMCPServer::Printf(TEXT("%s %s"),
*MCPUtils::FormatPinType(PinInfo->PinType), *MCPUtils::FormatPinType(PinInfo->PinType),
*PinInfo->PinName.ToString()); *PinInfo->PinName.ToString());
} }
@@ -63,12 +64,12 @@ public:
} }
} }
Result.Append(TEXT(")\n")); UMCPServer::Print(TEXT(")\n"));
} }
if (DelegateNameSet.Num() == 0) if (DelegateNameSet.Num() == 0)
{ {
Result.Append(TEXT("No event dispatchers found.\n")); UMCPServer::Print(TEXT("No event dispatchers found.\n"));
} }
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -27,7 +28,7 @@ public:
"including their function graphs."); "including their function graphs.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
F.Walk(Path); F.Walk(Path);
@@ -41,17 +42,17 @@ public:
if (!IfaceDesc.Interface) continue; if (!IfaceDesc.Interface) continue;
bAny = true; bAny = true;
Result.Appendf(TEXT("Interface: %s\n"), *MCPUtils::FormatName(IfaceDesc.Interface)); UMCPServer::Printf(TEXT("Interface: %s\n"), *MCPUtils::FormatName(IfaceDesc.Interface));
for (const UEdGraph* Graph : IfaceDesc.Graphs) for (const UEdGraph* Graph : IfaceDesc.Graphs)
{ {
if (!Graph) continue; if (!Graph) continue;
Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Graph)); UMCPServer::Printf(TEXT(" %s\n"), *MCPUtils::FormatName(Graph));
} }
} }
if (!bAny) if (!bAny)
{ {
Result.Append(TEXT("No interfaces implemented.\n")); UMCPServer::Print(TEXT("No interfaces implemented.\n"));
} }
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -31,7 +32,7 @@ public:
"Reports compiler warnings and errors."); "Reports compiler warnings and errors.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
// Load Blueprint // Load Blueprint
MCPAssets<UBlueprint> Assets; MCPAssets<UBlueprint> Assets;
@@ -61,24 +62,24 @@ public:
} }
// Summary // Summary
Result.Appendf(TEXT("Refreshed %s: %d graphs, %d nodes"), *MCPUtils::FormatName(BP), GraphCount, NodeCount); UMCPServer::Printf(TEXT("Refreshed %s: %d graphs, %d nodes"), *MCPUtils::FormatName(BP), GraphCount, NodeCount);
if (OrphanedPinsRemoved > 0) if (OrphanedPinsRemoved > 0)
{ {
Result.Appendf(TEXT(", %d orphaned pins removed"), OrphanedPinsRemoved); UMCPServer::Printf(TEXT(", %d orphaned pins removed"), OrphanedPinsRemoved);
} }
Result.Append(TEXT("\n")); UMCPServer::Print(TEXT("\n"));
// Collect compiler warnings and errors // Collect compiler warnings and errors
if (BP->Status == BS_Error) if (BP->Status == BS_Error)
{ {
Result.Append(TEXT("ERROR: Blueprint has compiler errors after refresh\n")); UMCPServer::Print(TEXT("ERROR: Blueprint has compiler errors after refresh\n"));
} }
for (UEdGraphNode* Node : MCPUtils::AllNodes(BP)) for (UEdGraphNode* Node : MCPUtils::AllNodes(BP))
{ {
if (!Node->bHasCompilerMessage) continue; if (!Node->bHasCompilerMessage) continue;
const TCHAR* Prefix = (Node->ErrorType == EMessageSeverity::Error) ? TEXT("ERROR") : TEXT("WARNING"); const TCHAR* Prefix = (Node->ErrorType == EMessageSeverity::Error) ? TEXT("ERROR") : TEXT("WARNING");
Result.Appendf(TEXT("%s: [%s] %s: %s\n"), UMCPServer::Printf(TEXT("%s: [%s] %s: %s\n"),
Prefix, *MCPUtils::FormatName(Node->GetGraph()), Prefix, *MCPUtils::FormatName(Node->GetGraph()),
*MCPUtils::FormatName(Node), *Node->ErrorMsg); *MCPUtils::FormatName(Node), *Node->ErrorMsg);
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -31,7 +32,7 @@ public:
return TEXT("Remove a component from a Blueprint's SimpleConstructionScript."); return TEXT("Remove a component from a Blueprint's SimpleConstructionScript.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
@@ -40,7 +41,7 @@ public:
USimpleConstructionScript* SCS = BP->SimpleConstructionScript; USimpleConstructionScript* SCS = BP->SimpleConstructionScript;
if (!SCS) if (!SCS)
{ {
Result.Append(TEXT("ERROR: Not an Actor Blueprint (no SimpleConstructionScript).\n")); UMCPServer::Print(TEXT("ERROR: Not an Actor Blueprint (no SimpleConstructionScript).\n"));
return; return;
} }
@@ -59,12 +60,12 @@ public:
if (!NodeToRemove) if (!NodeToRemove)
{ {
Result.Appendf(TEXT("ERROR: Component '%s' not found.\nAvailable components:\n"), UMCPServer::Printf(TEXT("ERROR: Component '%s' not found.\nAvailable components:\n"),
*Component); *Component);
for (USCS_Node* Node : AllNodes) for (USCS_Node* Node : AllNodes)
{ {
if (Node && Node->ComponentTemplate) if (Node && Node->ComponentTemplate)
Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Node->ComponentTemplate)); UMCPServer::Printf(TEXT(" %s\n"), *MCPUtils::FormatName(Node->ComponentTemplate));
} }
return; return;
} }
@@ -73,7 +74,7 @@ public:
const TArray<USCS_Node*>& RootNodes = SCS->GetRootNodes(); const TArray<USCS_Node*>& RootNodes = SCS->GetRootNodes();
if (RootNodes.Contains(NodeToRemove) && NodeToRemove->GetChildNodes().Num() > 0) if (RootNodes.Contains(NodeToRemove) && NodeToRemove->GetChildNodes().Num() > 0)
{ {
Result.Appendf(TEXT("ERROR: Cannot remove '%s' — it is a root component with %d child(ren). " UMCPServer::Printf(TEXT("ERROR: Cannot remove '%s' — it is a root component with %d child(ren). "
"Remove or re-parent the children first.\n"), "Remove or re-parent the children first.\n"),
*MCPUtils::FormatName(NodeToRemove->ComponentTemplate), *MCPUtils::FormatName(NodeToRemove->ComponentTemplate),
NodeToRemove->GetChildNodes().Num()); NodeToRemove->GetChildNodes().Num());
@@ -87,7 +88,7 @@ public:
bool bSaved = MCPUtils::SaveBlueprintPackage(BP); bool bSaved = MCPUtils::SaveBlueprintPackage(BP);
Result.Appendf(TEXT("Removed component %s.%s\n"), UMCPServer::Printf(TEXT("Removed component %s.%s\n"),
*RemovedName, *RemovedName,
bSaved ? TEXT("") : TEXT(" WARNING: save failed.")); bSaved ? TEXT("") : TEXT(" WARNING: save failed."));
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPAssets.h" #include "MCPAssets.h"
@@ -36,7 +37,7 @@ public:
return TEXT("Remove a parameter from a function or custom event in a Blueprint."); return TEXT("Remove a parameter from a function or custom event in a Blueprint.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPAssets<UBlueprint> Assets; MCPAssets<UBlueprint> Assets;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
@@ -68,11 +69,11 @@ public:
if (!EntryNode) if (!EntryNode)
{ {
Result.Appendf(TEXT("Error: Function or event '%s' not found.\nAvailable:\n"), *FunctionName); UMCPServer::Printf(TEXT("Error: Function or event '%s' not found.\nAvailable:\n"), *FunctionName);
for (UK2Node_FunctionEntry* FE : MCPUtils::AllNodes<UK2Node_FunctionEntry>(BP)) for (UK2Node_FunctionEntry* FE : MCPUtils::AllNodes<UK2Node_FunctionEntry>(BP))
Result.Appendf(TEXT(" function: %s\n"), *MCPUtils::FormatName(FE->GetGraph())); UMCPServer::Printf(TEXT(" function: %s\n"), *MCPUtils::FormatName(FE->GetGraph()));
for (UK2Node_CustomEvent* CE : MCPUtils::AllNodes<UK2Node_CustomEvent>(BP)) for (UK2Node_CustomEvent* CE : MCPUtils::AllNodes<UK2Node_CustomEvent>(BP))
Result.Appendf(TEXT(" event: %s\n"), *MCPUtils::FormatName(CE)); UMCPServer::Printf(TEXT(" event: %s\n"), *MCPUtils::FormatName(CE));
return; return;
} }
@@ -90,11 +91,11 @@ public:
if (RemovedIndex == INDEX_NONE) if (RemovedIndex == INDEX_NONE)
{ {
Result.Appendf(TEXT("Error: Parameter '%s' not found on %s.\nAvailable:\n"), UMCPServer::Printf(TEXT("Error: Parameter '%s' not found on %s.\nAvailable:\n"),
*ParamName, *MCPUtils::FormatName(EntryNode)); *ParamName, *MCPUtils::FormatName(EntryNode));
for (const TSharedPtr<FUserPinInfo>& PinInfo : EntryNode->UserDefinedPins) for (const TSharedPtr<FUserPinInfo>& PinInfo : EntryNode->UserDefinedPins)
if (PinInfo.IsValid()) if (PinInfo.IsValid())
Result.Appendf(TEXT(" %s\n"), *PinInfo->PinName.ToString()); UMCPServer::Printf(TEXT(" %s\n"), *PinInfo->PinName.ToString());
return; return;
} }
@@ -108,8 +109,8 @@ public:
bool bSaved = MCPUtils::SaveBlueprintPackage(BP); bool bSaved = MCPUtils::SaveBlueprintPackage(BP);
Result.Appendf(TEXT("Removed parameter '%s' from %s.\n"), *ParamName, *MCPUtils::FormatName(EntryNode)); UMCPServer::Printf(TEXT("Removed parameter '%s' from %s.\n"), *ParamName, *MCPUtils::FormatName(EntryNode));
if (!bSaved) if (!bSaved)
Result.Append(TEXT("Warning: save failed.\n")); UMCPServer::Print(TEXT("Warning: save failed.\n"));
} }
}; };

View File

@@ -35,7 +35,7 @@ public:
"Optionally preserve the function graphs as regular functions."); "Optionally preserve the function graphs as regular functions.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPAssets<UBlueprint> Assets; MCPAssets<UBlueprint> Assets;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
@@ -67,8 +67,8 @@ public:
FTopLevelAssetPath InterfacePath = FoundInterface->GetClassPathName(); FTopLevelAssetPath InterfacePath = FoundInterface->GetClassPathName();
FBlueprintEditorUtils::RemoveInterface(BP, InterfacePath, PreserveFunctions); FBlueprintEditorUtils::RemoveInterface(BP, InterfacePath, PreserveFunctions);
Result.Appendf(TEXT("Removed interface %s\n"), *MCPUtils::FormatName(FoundInterface)); UMCPServer::Printf(TEXT("Removed interface %s\n"), *MCPUtils::FormatName(FoundInterface));
if (PreserveFunctions) if (PreserveFunctions)
Result.Append(TEXT("Function graphs preserved as regular functions.\n")); UMCPServer::Print(TEXT("Function graphs preserved as regular functions.\n"));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -30,7 +31,7 @@ public:
return TEXT("Remove a member variable from a Blueprint."); return TEXT("Remove a member variable from a Blueprint.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
@@ -50,11 +51,11 @@ public:
if (!Found) if (!Found)
{ {
Result.Appendf(TEXT("ERROR: Variable '%s' not found in %s.\nAvailable variables:\n"), UMCPServer::Printf(TEXT("ERROR: Variable '%s' not found in %s.\nAvailable variables:\n"),
*VariableName, *MCPUtils::FormatName(BP)); *VariableName, *MCPUtils::FormatName(BP));
for (const FBPVariableDescription& Var : BP->NewVariables) for (const FBPVariableDescription& Var : BP->NewVariables)
{ {
Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Var)); UMCPServer::Printf(TEXT(" %s\n"), *MCPUtils::FormatName(Var));
} }
return; return;
} }
@@ -65,7 +66,7 @@ public:
FBlueprintEditorUtils::RemoveMemberVariable(BP, VarFName); FBlueprintEditorUtils::RemoveMemberVariable(BP, VarFName);
bool bSaved = MCPUtils::SaveBlueprintPackage(BP); bool bSaved = MCPUtils::SaveBlueprintPackage(BP);
Result.Appendf(TEXT("Removed variable %s from %s.%s\n"), UMCPServer::Printf(TEXT("Removed variable %s from %s.%s\n"),
*VarFName.ToString(), *MCPUtils::FormatName(BP), *VarFName.ToString(), *MCPUtils::FormatName(BP),
bSaved ? TEXT("") : TEXT(" WARNING: save failed.")); bSaved ? TEXT("") : TEXT(" WARNING: save failed."));
} }

View File

@@ -33,7 +33,7 @@ public:
return TEXT("Change a Blueprint's parent class. Accepts C++ class names or Blueprint names."); return TEXT("Change a Blueprint's parent class. Accepts C++ class names or Blueprint names.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
// Load Blueprint // Load Blueprint
MCPAssets<UBlueprint> Assets; MCPAssets<UBlueprint> Assets;
@@ -66,9 +66,9 @@ public:
FKismetEditorUtilities::CompileBlueprint(BP); FKismetEditorUtilities::CompileBlueprint(BP);
bool bSaved = MCPUtils::SaveBlueprintPackage(BP); bool bSaved = MCPUtils::SaveBlueprintPackage(BP);
Result.Appendf(TEXT("Reparented %s: %s -> %s\n"), UMCPServer::Printf(TEXT("Reparented %s: %s -> %s\n"),
*MCPUtils::FormatName(BP), *OldParentName, *MCPUtils::FormatName(NewParentClassObj)); *MCPUtils::FormatName(BP), *OldParentName, *MCPUtils::FormatName(NewParentClassObj));
if (!bSaved) if (!bSaved)
Result.Append(TEXT("Warning: save failed\n")); UMCPServer::Print(TEXT("Warning: save failed\n"));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -36,7 +37,7 @@ public:
return TEXT("List all Blueprint assets in the project, with optional filtering by name, parent class, or type."); return TEXT("List all Blueprint assets in the project, with optional filtering by name, parent class, or type.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPAssets<UObject> Assets; MCPAssets<UObject> Assets;
Assets.NoScans().Substring(Query).Limit(500); Assets.NoScans().Substring(Query).Limit(500);
@@ -73,13 +74,13 @@ public:
} }
} }
Result.Appendf(TEXT("%30s %s\n"), *ParentClassName, *Asset.PackageName.ToString()); UMCPServer::Printf(TEXT("%30s %s\n"), *ParentClassName, *Asset.PackageName.ToString());
Count++; Count++;
} }
if (Count == 0) if (Count == 0)
{ {
Result.Append(TEXT("No blueprint assets found.\n")); UMCPServer::Print(TEXT("No blueprint assets found.\n"));
} }
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -41,7 +42,7 @@ public:
return TEXT("Search across all Blueprint graphs for nodes matching a query string."); return TEXT("Search across all Blueprint graphs for nodes matching a query string.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
int32 Limit = (MaxResults > 0) ? FMath::Clamp(MaxResults, 1, 200) : 50; int32 Limit = (MaxResults > 0) ? FMath::Clamp(MaxResults, 1, 200) : 50;
int32 Count = 0; int32 Count = 0;
@@ -85,15 +86,15 @@ public:
if (!bMatch) continue; if (!bMatch) continue;
Count++; Count++;
Result.Appendf(TEXT("blueprint: %s\n"), *MCPUtils::FormatName(BP)); UMCPServer::Printf(TEXT("blueprint: %s\n"), *MCPUtils::FormatName(BP));
Result.Appendf(TEXT(" graph: %s\n"), *MCPUtils::FormatName(Node->GetGraph())); UMCPServer::Printf(TEXT(" graph: %s\n"), *MCPUtils::FormatName(Node->GetGraph()));
Result.Appendf(TEXT(" node: %s\n"), *MCPUtils::FormatName(Node)); UMCPServer::Printf(TEXT(" node: %s\n"), *MCPUtils::FormatName(Node));
Result.Appendf(TEXT(" class: %s\n"), *MCPUtils::FormatName(Node->GetClass())); UMCPServer::Printf(TEXT(" class: %s\n"), *MCPUtils::FormatName(Node->GetClass()));
if (!FuncName.IsEmpty()) Result.Appendf(TEXT(" function: %s\n"), *FuncName); if (!FuncName.IsEmpty()) UMCPServer::Printf(TEXT(" function: %s\n"), *FuncName);
if (!EventName.IsEmpty()) Result.Appendf(TEXT(" event: %s\n"), *EventName); if (!EventName.IsEmpty()) UMCPServer::Printf(TEXT(" event: %s\n"), *EventName);
if (!VarName.IsEmpty()) Result.Appendf(TEXT(" variable: %s\n"), *VarName); if (!VarName.IsEmpty()) UMCPServer::Printf(TEXT(" variable: %s\n"), *VarName);
if (bIsLevelBP) Result.Append(TEXT(" level-blueprint: true\n")); if (bIsLevelBP) UMCPServer::Print(TEXT(" level-blueprint: true\n"));
Result.Append(TEXT("\n")); UMCPServer::Print(TEXT("\n"));
} }
}; };
@@ -120,7 +121,7 @@ public:
SearchBlueprint(LevelBP, true); SearchBlueprint(LevelBP, true);
} }
Result.Appendf(TEXT("Results: %d\n"), Count); UMCPServer::Printf(TEXT("Results: %d\n"), Count);
if (Count >= Limit) Result.Appendf(TEXT("(limit %d reached, use MaxResults to increase)\n"), Limit); if (Count >= Limit) UMCPServer::Printf(TEXT("(limit %d reached, use MaxResults to increase)\n"), Limit);
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "UObject/UObjectIterator.h" #include "UObject/UObjectIterator.h"
@@ -36,7 +37,7 @@ public:
"Returns class names, parent class, package, and flags."); "Returns class names, parent class, package, and flags.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
Limit = FMath::Clamp(Limit, 1, 500); Limit = FMath::Clamp(Limit, 1, 500);
@@ -53,7 +54,7 @@ public:
} }
if (!ParentClassObj) if (!ParentClassObj)
{ {
Result.Appendf(TEXT("Error: Parent class '%s' not found\n"), *ParentClass); UMCPServer::Printf(TEXT("Error: Parent class '%s' not found\n"), *ParentClass);
return; return;
} }
} }
@@ -78,16 +79,16 @@ public:
} }
} }
Result.Appendf(TEXT("Found %d classes"), TotalMatched); UMCPServer::Printf(TEXT("Found %d classes"), TotalMatched);
if (TotalMatched > Limit) if (TotalMatched > Limit)
{ {
Result.Appendf(TEXT(" (showing %d)"), Limit); UMCPServer::Printf(TEXT(" (showing %d)"), Limit);
} }
Result.Append(TEXT("\n")); UMCPServer::Print(TEXT("\n"));
for (UClass* Class : Matches) for (UClass* Class : Matches)
{ {
Result.Appendf(TEXT(" %s"), *MCPUtils::FormatName(Class)); UMCPServer::Printf(TEXT(" %s"), *MCPUtils::FormatName(Class));
// Flags // Flags
TStringBuilder<64> Flags; TStringBuilder<64> Flags;
@@ -97,15 +98,15 @@ public:
if (Class->ClassGeneratedBy) Flags.Append(TEXT(" Blueprint")); if (Class->ClassGeneratedBy) Flags.Append(TEXT(" Blueprint"));
if (Flags.Len() > 0) if (Flags.Len() > 0)
{ {
Result.Appendf(TEXT(" [%s]"), Flags.ToString() + 1); // skip leading space UMCPServer::Printf(TEXT(" [%s]"), Flags.ToString() + 1); // skip leading space
} }
if (Class->GetSuperClass()) if (Class->GetSuperClass())
{ {
Result.Appendf(TEXT(" : %s"), *MCPUtils::FormatName(Class->GetSuperClass())); UMCPServer::Printf(TEXT(" : %s"), *MCPUtils::FormatName(Class->GetSuperClass()));
} }
Result.Append(TEXT("\n")); UMCPServer::Print(TEXT("\n"));
} }
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Class_ShowProperties.generated.h" #include "Class_ShowProperties.generated.h"
@@ -27,16 +28,16 @@ public:
return TEXT("List properties on a UClass, including type, owning class, and property flags."); return TEXT("List properties on a UClass, including type, owning class, and property flags.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
UClass* FoundClass = MCPUtils::FindClassByName(ClassName); UClass* FoundClass = MCPUtils::FindClassByName(ClassName);
if (!FoundClass) if (!FoundClass)
{ {
Result.Appendf(TEXT("ERROR: Class '%s' not found\n"), *ClassName); UMCPServer::Printf(TEXT("ERROR: Class '%s' not found\n"), *ClassName);
return; return;
} }
Result.Appendf(TEXT("Properties of %s:\n"), *MCPUtils::FormatName(FoundClass)); UMCPServer::Printf(TEXT("Properties of %s:\n"), *MCPUtils::FormatName(FoundClass));
int32 Count = 0; int32 Count = 0;
for (TFieldIterator<FProperty> PropIt(FoundClass); PropIt; ++PropIt) for (TFieldIterator<FProperty> PropIt(FoundClass); PropIt; ++PropIt)
@@ -61,18 +62,18 @@ public:
if (Prop->HasAnyPropertyFlags(CPF_RepNotify)) Flags.Append(TEXT(" RepNotify")); if (Prop->HasAnyPropertyFlags(CPF_RepNotify)) Flags.Append(TEXT(" RepNotify"));
UClass* OwnerClass = Prop->GetOwnerClass(); UClass* OwnerClass = Prop->GetOwnerClass();
Result.Appendf(TEXT(" %s %s"), *MCPUtils::FormatPropertyType(Prop), *PropName); UMCPServer::Printf(TEXT(" %s %s"), *MCPUtils::FormatPropertyType(Prop), *PropName);
if (OwnerClass && OwnerClass != FoundClass) if (OwnerClass && OwnerClass != FoundClass)
Result.Appendf(TEXT(" [%s]"), *MCPUtils::FormatName(OwnerClass)); UMCPServer::Printf(TEXT(" [%s]"), *MCPUtils::FormatName(OwnerClass));
if (Flags.Len() > 0) if (Flags.Len() > 0)
Result.Appendf(TEXT(" (%s)"), Flags.ToString() + 1); // skip leading space UMCPServer::Printf(TEXT(" (%s)"), Flags.ToString() + 1); // skip leading space
Result.Append(TEXT("\n")); UMCPServer::Print(TEXT("\n"));
Count++; Count++;
} }
if (Count == 0) if (Count == 0)
{ {
Result.Append(TEXT("No properties found.\n")); UMCPServer::Print(TEXT("No properties found.\n"));
} }
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "Editor.h" #include "Editor.h"
#include "Subsystems/AssetEditorSubsystem.h" #include "Subsystems/AssetEditorSubsystem.h"
@@ -22,19 +23,19 @@ public:
return TEXT("List all currently open asset editors, showing which has focus and whether they have unsaved changes."); return TEXT("List all currently open asset editors, showing which has focus and whether they have unsaved changes.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>(); UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
if (!Sub) if (!Sub)
{ {
Result.Append(TEXT("Error: AssetEditorSubsystem not available\n")); UMCPServer::Print(TEXT("Error: AssetEditorSubsystem not available\n"));
return; return;
} }
TArray<UObject*> EditedAssets = Sub->GetAllEditedAssets(); TArray<UObject*> EditedAssets = Sub->GetAllEditedAssets();
if (EditedAssets.IsEmpty()) if (EditedAssets.IsEmpty())
{ {
Result.Append(TEXT("No asset editors are open.\n")); UMCPServer::Print(TEXT("No asset editors are open.\n"));
return; return;
} }
@@ -42,7 +43,7 @@ public:
{ {
bool bDirty = Asset->GetOutermost()->IsDirty(); bool bDirty = Asset->GetOutermost()->IsDirty();
Result.Appendf(TEXT(" %s%s\n"), UMCPServer::Printf(TEXT(" %s%s\n"),
bDirty ? TEXT("[unsaved] ") : TEXT(""), bDirty ? TEXT("[unsaved] ") : TEXT(""),
*Asset->GetPathName()); *Asset->GetPathName());
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "Editor.h" #include "Editor.h"
@@ -26,7 +27,7 @@ public:
return TEXT("Open an asset in its editor and bring it to focus."); return TEXT("Open an asset in its editor and bring it to focus.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UObject* Obj = F.Walk(Path).Cast<UObject>(); UObject* Obj = F.Walk(Path).Cast<UObject>();
@@ -35,13 +36,13 @@ public:
UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>(); UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
if (!Sub) if (!Sub)
{ {
Result.Append(TEXT("Error: AssetEditorSubsystem not available\n")); UMCPServer::Print(TEXT("Error: AssetEditorSubsystem not available\n"));
return; return;
} }
if (Sub->OpenEditorForAsset(Obj)) if (Sub->OpenEditorForAsset(Obj))
Result.Appendf(TEXT("Opened editor for %s\n"), *Obj->GetPathName()); UMCPServer::Printf(TEXT("Opened editor for %s\n"), *Obj->GetPathName());
else else
Result.Appendf(TEXT("Error: Could not open editor for %s\n"), *Obj->GetPathName()); UMCPServer::Printf(TEXT("Error: Could not open editor for %s\n"), *Obj->GetPathName());
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -32,7 +33,7 @@ public:
return TEXT("Create a new UserDefinedEnum asset with the specified values."); return TEXT("Create a new UserDefinedEnum asset with the specified values.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPPackageMaker Maker(AssetPath); MCPPackageMaker Maker(AssetPath);
if (!Maker.Ok()) return; if (!Maker.Ok()) return;
@@ -64,8 +65,8 @@ public:
bool bSaved = MCPUtils::SaveGenericPackage(NewEnum); bool bSaved = MCPUtils::SaveGenericPackage(NewEnum);
Result.Appendf(TEXT("Created %s with %d values\n"), *NewEnum->GetPathName(), EnumValues.Num()); UMCPServer::Printf(TEXT("Created %s with %d values\n"), *NewEnum->GetPathName(), EnumValues.Num());
if (!bSaved) if (!bSaved)
Result.Append(TEXT("WARNING: Package save failed\n")); UMCPServer::Print(TEXT("WARNING: Package save failed\n"));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -49,7 +50,7 @@ public:
"Use GraphNodeSearchTypes first to find the exact action name."); "Use GraphNodeSearchTypes first to find the exact action name.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>(); UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>();
@@ -68,13 +69,13 @@ public:
TArray<TSharedPtr<FEdGraphSchemaAction>> Matches = MCPUtils::SearchGraphActions(TargetGraph, Entry.ActionName, 0, /*ExactMatch=*/true); TArray<TSharedPtr<FEdGraphSchemaAction>> Matches = MCPUtils::SearchGraphActions(TargetGraph, Entry.ActionName, 0, /*ExactMatch=*/true);
if (Matches.Num() == 0) if (Matches.Num() == 0)
{ {
Result.Appendf(TEXT("ERROR: No action found matching '%s'. Use GraphNodeSearchTypes to find available actions.\n"), UMCPServer::Printf(TEXT("ERROR: No action found matching '%s'. Use GraphNodeSearchTypes to find available actions.\n"),
*Entry.ActionName); *Entry.ActionName);
continue; continue;
} }
if (Matches.Num() > 1) if (Matches.Num() > 1)
{ {
Result.Appendf(TEXT("ERROR: Ambiguous: %d actions match '%s'.\n"), UMCPServer::Printf(TEXT("ERROR: Ambiguous: %d actions match '%s'.\n"),
Matches.Num(), *Entry.ActionName); Matches.Num(), *Entry.ActionName);
continue; continue;
} }
@@ -84,18 +85,18 @@ public:
UEdGraphNode* NewNode = Matches[0]->PerformAction(TargetGraph, nullptr, Location, /*bSelectNewNode=*/false); UEdGraphNode* NewNode = Matches[0]->PerformAction(TargetGraph, nullptr, Location, /*bSelectNewNode=*/false);
if (!NewNode) if (!NewNode)
{ {
Result.Appendf(TEXT("ERROR: PerformAction returned null for '%s'.\n"), *Entry.ActionName); UMCPServer::Printf(TEXT("ERROR: PerformAction returned null for '%s'.\n"), *Entry.ActionName);
continue; continue;
} }
if (!NewNode->NodeGuid.IsValid()) if (!NewNode->NodeGuid.IsValid())
NewNode->CreateNewGuid(); NewNode->CreateNewGuid();
Result.Appendf(TEXT("Spawned: %s (%s)\n"), UMCPServer::Printf(TEXT("Spawned: %s (%s)\n"),
*MCPUtils::FormatName(NewNode), *MCPUtils::FormatName(NewNode->GetClass())); *MCPUtils::FormatName(NewNode), *MCPUtils::FormatName(NewNode->GetClass()));
SuccessCount++; SuccessCount++;
} }
Result.Appendf(TEXT("Spawned %d/%d nodes.\n"), SuccessCount, TotalCount); UMCPServer::Printf(TEXT("Spawned %d/%d nodes.\n"), SuccessCount, TotalCount);
} }
}; };

View File

@@ -32,7 +32,7 @@ public:
"Cannot delete undeletable nodes (entry points, root nodes, etc)."); "Cannot delete undeletable nodes (entry points, root nodes, etc).");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>(); UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>();
@@ -63,6 +63,6 @@ public:
Graph->RemoveNode(FoundNode); Graph->RemoveNode(FoundNode);
} }
Result.Appendf(TEXT("Deleted node '%s' from graph '%s'.\n"), *NodeTitle, *GraphName); UMCPServer::Printf(TEXT("Deleted node '%s' from graph '%s'.\n"), *NodeTitle, *GraphName);
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -39,7 +40,7 @@ public:
"Connections are not preserved on the duplicates."); "Connections are not preserved on the duplicates.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>(); UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>();
@@ -47,7 +48,7 @@ public:
if (Nodes.Array.Num() == 0) if (Nodes.Array.Num() == 0)
{ {
Result.Append(TEXT("ERROR: Nodes array is empty\n")); UMCPServer::Print(TEXT("ERROR: Nodes array is empty\n"));
return; return;
} }
@@ -67,7 +68,7 @@ public:
} }
if (!Found) if (!Found)
{ {
Result.Appendf(TEXT("ERROR: Node '%s' not found in graph\n"), *Name); UMCPServer::Printf(TEXT("ERROR: Node '%s' not found in graph\n"), *Name);
continue; continue;
} }
SourceNodes.Add(Found); SourceNodes.Add(Found);
@@ -81,7 +82,7 @@ public:
UEdGraphNode* NewNode = DuplicateObject<UEdGraphNode>(SourceNode, TargetGraph); UEdGraphNode* NewNode = DuplicateObject<UEdGraphNode>(SourceNode, TargetGraph);
if (!NewNode) if (!NewNode)
{ {
Result.Appendf(TEXT("ERROR: Failed to duplicate %s\n"), *MCPUtils::FormatName(SourceNode)); UMCPServer::Printf(TEXT("ERROR: Failed to duplicate %s\n"), *MCPUtils::FormatName(SourceNode));
continue; continue;
} }
@@ -96,7 +97,7 @@ public:
} }
TargetGraph->AddNode(NewNode, false, false); TargetGraph->AddNode(NewNode, false, false);
Result.Appendf(TEXT("Duplicated: %s -> %s\n"), *MCPUtils::FormatName(SourceNode), *MCPUtils::FormatName(NewNode)); UMCPServer::Printf(TEXT("Duplicated: %s -> %s\n"), *MCPUtils::FormatName(SourceNode), *MCPUtils::FormatName(NewNode));
} }
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -26,14 +27,14 @@ public:
return TEXT("Get the comment text and bubble visibility of a node."); return TEXT("Get the comment text and bubble visibility of a node.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>(); UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>();
if (!FoundNode) return; if (!FoundNode) return;
Result.Appendf(TEXT("Node: %s\n"), *MCPUtils::FormatName(FoundNode)); UMCPServer::Printf(TEXT("Node: %s\n"), *MCPUtils::FormatName(FoundNode));
Result.Appendf(TEXT("Comment: %s\n"), *FoundNode->NodeComment); UMCPServer::Printf(TEXT("Comment: %s\n"), *FoundNode->NodeComment);
Result.Appendf(TEXT("BubbleVisible: %s\n"), FoundNode->bCommentBubbleVisible ? TEXT("true") : TEXT("false")); UMCPServer::Printf(TEXT("BubbleVisible: %s\n"), FoundNode->bCommentBubbleVisible ? TEXT("true") : TEXT("false"));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -35,7 +36,7 @@ public:
"Returns full action names for use with GraphNodeCreate."); "Returns full action names for use with GraphNodeCreate.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
int32 ClampedMax = FMath::Clamp(MaxResults, 1, 500); int32 ClampedMax = FMath::Clamp(MaxResults, 1, 500);
@@ -47,16 +48,16 @@ public:
for (const TSharedPtr<FEdGraphSchemaAction>& Action : Actions) for (const TSharedPtr<FEdGraphSchemaAction>& Action : Actions)
{ {
Result.Appendf(TEXT("%s\n"), *MCPUtils::ActionFullName(Action)); UMCPServer::Printf(TEXT("%s\n"), *MCPUtils::ActionFullName(Action));
} }
if (Actions.Num() == 0) if (Actions.Num() == 0)
{ {
Result.Append(TEXT("No matching node types found.\n")); UMCPServer::Print(TEXT("No matching node types found.\n"));
} }
else if (Actions.Num() >= ClampedMax) else if (Actions.Num() >= ClampedMax)
{ {
Result.Appendf(TEXT("WARNING: Reached limit of %d results. Refine your query or increase MaxResults.\n"), ClampedMax); UMCPServer::Printf(TEXT("WARNING: Reached limit of %d results. Refine your query or increase MaxResults.\n"), ClampedMax);
} }
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -29,7 +30,7 @@ public:
return TEXT("Set a node's comment text. Makes the comment bubble visible if non-empty."); return TEXT("Set a node's comment text. Makes the comment bubble visible if non-empty.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>(); UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>();
@@ -44,6 +45,6 @@ public:
FoundNode->bCommentBubblePinned = true; FoundNode->bCommentBubblePinned = true;
} }
Result.Appendf(TEXT("Comment set on %s\n"), *MCPUtils::FormatName(FoundNode)); UMCPServer::Printf(TEXT("Comment set on %s\n"), *MCPUtils::FormatName(FoundNode));
} }
}; };

View File

@@ -53,8 +53,7 @@ public:
// K2 graphs: set pin default values. // K2 graphs: set pin default values.
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
void HandleK2Entry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj, const UEdGraphSchema_K2* K2Schema, void HandleK2Entry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj, const UEdGraphSchema_K2* K2Schema)
FStringBuilderBase& Result)
{ {
MCPFetcher F(GraphObj); MCPFetcher F(GraphObj);
UEdGraphPin* Pin = F.Node(Entry.Node).Pin(Entry.Name).Cast<UEdGraphPin>(); UEdGraphPin* Pin = F.Node(Entry.Node).Pin(Entry.Name).Cast<UEdGraphPin>();
@@ -64,7 +63,7 @@ public:
if (Pin->Direction != EGPD_Input) if (Pin->Direction != EGPD_Input)
{ {
Result.Appendf(TEXT("error: %s is an output pin\n"), *MCPUtils::FormatName(Pin)); UMCPServer::Printf(TEXT("error: %s is an output pin\n"), *MCPUtils::FormatName(Pin));
return; return;
} }
@@ -77,7 +76,7 @@ public:
FString Error = K2Schema->IsPinDefaultValid(Pin, UseDefaultValue, UseDefaultObject, UseDefaultText); FString Error = K2Schema->IsPinDefaultValid(Pin, UseDefaultValue, UseDefaultObject, UseDefaultText);
if (!Error.IsEmpty()) if (!Error.IsEmpty())
{ {
Result.Appendf(TEXT("error: %s: %s\n"), *MCPUtils::FormatName(Pin), *Error); UMCPServer::Printf(TEXT("error: %s: %s\n"), *MCPUtils::FormatName(Pin), *Error);
return; return;
} }
UMCPServer::AddTouchedObject(Node); UMCPServer::AddTouchedObject(Node);
@@ -88,8 +87,7 @@ public:
// Material graphs: set material expression properties. // Material graphs: set material expression properties.
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
void HandleMaterialEntry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj, void HandleMaterialEntry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj)
FStringBuilderBase& Result)
{ {
MCPFetcher F(GraphObj); MCPFetcher F(GraphObj);
UEdGraphNode* Node = F.Node(Entry.Node).Cast<UEdGraphNode>(); UEdGraphNode* Node = F.Node(Entry.Node).Cast<UEdGraphNode>();
@@ -106,7 +104,7 @@ public:
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
// Fetch the graph once. // Fetch the graph once.
MCPFetcher GraphFetcher; MCPFetcher GraphFetcher;
@@ -119,7 +117,7 @@ public:
if (!K2Schema && !MGSchema) if (!K2Schema && !MGSchema)
{ {
Result.Appendf(TEXT("error: unsupported graph schema %s\n"), *Schema->GetClass()->GetName()); UMCPServer::Printf(TEXT("error: unsupported graph schema %s\n"), *Schema->GetClass()->GetName());
return; return;
} }
@@ -130,11 +128,11 @@ public:
continue; continue;
if (K2Schema) if (K2Schema)
HandleK2Entry(Entry, GraphObj, K2Schema, Result); HandleK2Entry(Entry, GraphObj, K2Schema);
else if (MGSchema) else if (MGSchema)
HandleMaterialEntry(Entry, GraphObj, Result); HandleMaterialEntry(Entry, GraphObj);
} }
Result.Appendf(TEXT("Done.\n")); UMCPServer::Printf(TEXT("Done.\n"));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -46,7 +47,7 @@ public:
return TEXT("Reposition one or more nodes in a Blueprint graph."); return TEXT("Reposition one or more nodes in a Blueprint graph.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
@@ -65,10 +66,10 @@ public:
Node->NodePosX = Entry.X; Node->NodePosX = Entry.X;
Node->NodePosY = Entry.Y; Node->NodePosY = Entry.Y;
Result.Appendf(TEXT("Moved %s to (%d, %d)\n"), *MCPUtils::FormatName(Node), Entry.X, Entry.Y); UMCPServer::Printf(TEXT("Moved %s to (%d, %d)\n"), *MCPUtils::FormatName(Node), Entry.X, Entry.Y);
SuccessCount++; SuccessCount++;
} }
Result.Appendf(TEXT("Moved %d/%d nodes.\n"), SuccessCount, Nodes.Array.Num()); UMCPServer::Printf(TEXT("Moved %d/%d nodes.\n"), SuccessCount, Nodes.Array.Num());
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -45,7 +46,7 @@ public:
return TEXT("Connect pins between nodes in a graph (Blueprint or Material)."); return TEXT("Connect pins between nodes in a graph (Blueprint or Material).");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UEdGraph* G = F.Walk(Graph).ToGraph().Cast<UEdGraph>(); UEdGraph* G = F.Walk(Graph).ToGraph().Cast<UEdGraph>();
@@ -72,7 +73,7 @@ public:
const FPinConnectionResponse Response = Schema->CanCreateConnection(SourcePin, TargetPin); const FPinConnectionResponse Response = Schema->CanCreateConnection(SourcePin, TargetPin);
if (Response.Response == CONNECT_RESPONSE_DISALLOW) if (Response.Response == CONNECT_RESPONSE_DISALLOW)
{ {
Result.Appendf(TEXT("error: Cannot connect %s.%s to %s.%s: %s\n"), UMCPServer::Printf(TEXT("error: Cannot connect %s.%s to %s.%s: %s\n"),
*MCPUtils::FormatName(SourcePin->GetOwningNode()), *MCPUtils::FormatName(SourcePin), *MCPUtils::FormatName(SourcePin->GetOwningNode()), *MCPUtils::FormatName(SourcePin),
*MCPUtils::FormatName(TargetPin->GetOwningNode()), *MCPUtils::FormatName(TargetPin), *MCPUtils::FormatName(TargetPin->GetOwningNode()), *MCPUtils::FormatName(TargetPin),
*Response.Message.ToString()); *Response.Message.ToString());
@@ -83,6 +84,6 @@ public:
SuccessCount++; SuccessCount++;
} }
Result.Appendf(TEXT("Connected %d/%d pins.\n"), SuccessCount, TotalCount); UMCPServer::Printf(TEXT("Connected %d/%d pins.\n"), SuccessCount, TotalCount);
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -45,7 +46,7 @@ public:
"Can disconnect a specific link or all links on a pin."); "Can disconnect a specific link or all links on a pin.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UEdGraph* G = F.Walk(Graph).ToGraph().Cast<UEdGraph>(); UEdGraph* G = F.Walk(Graph).ToGraph().Cast<UEdGraph>();
@@ -73,7 +74,7 @@ public:
if (!Pin->LinkedTo.Contains(Target)) if (!Pin->LinkedTo.Contains(Target))
{ {
Result.Appendf(TEXT("Error: %s.%s is not connected to %s.%s\n"), UMCPServer::Printf(TEXT("Error: %s.%s is not connected to %s.%s\n"),
*MCPUtils::FormatName(Pin->GetOwningNode()), *MCPUtils::FormatName(Pin), *MCPUtils::FormatName(Pin->GetOwningNode()), *MCPUtils::FormatName(Pin),
*MCPUtils::FormatName(Target->GetOwningNode()), *MCPUtils::FormatName(Target)); *MCPUtils::FormatName(Target->GetOwningNode()), *MCPUtils::FormatName(Target));
continue; continue;
@@ -91,14 +92,14 @@ public:
} }
} }
Result.Appendf(TEXT("Disconnected %d link(s) from %s.%s\n"), UMCPServer::Printf(TEXT("Disconnected %d link(s) from %s.%s\n"),
DisconnectedCount, DisconnectedCount,
*MCPUtils::FormatName(Pin->GetOwningNode()), *MCPUtils::FormatName(Pin)); *MCPUtils::FormatName(Pin->GetOwningNode()), *MCPUtils::FormatName(Pin));
SuccessCount++; SuccessCount++;
TotalDisconnected += DisconnectedCount; TotalDisconnected += DisconnectedCount;
} }
Result.Appendf(TEXT("Done: %d/%d succeeded, %d links broken.\n"), UMCPServer::Printf(TEXT("Done: %d/%d succeeded, %d links broken.\n"),
SuccessCount, Disconnections.Array.Num(), TotalDisconnected); SuccessCount, Disconnections.Array.Num(), TotalDisconnected);
} }
}; };

View File

@@ -32,7 +32,7 @@ public:
"If given a blueprint or material, dumps all graphs. If given a specific graph, dumps only that one."); "If given a blueprint or material, dumps all graphs. If given a specific graph, dumps only that one.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
F.Walk(Path); F.Walk(Path);

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -31,7 +32,7 @@ public:
return TEXT("Create a new UMaterialFunction asset with an optional description."); return TEXT("Create a new UMaterialFunction asset with an optional description.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPPackageMaker Maker(AssetPath); MCPPackageMaker Maker(AssetPath);
if (!Maker.Ok()) return; if (!Maker.Ok()) return;
@@ -46,8 +47,8 @@ public:
bool bSaved = MCPUtils::SaveGenericPackage(MF); bool bSaved = MCPUtils::SaveGenericPackage(MF);
Result.Appendf(TEXT("Created %s\n"), *MF->GetPathName()); UMCPServer::Printf(TEXT("Created %s\n"), *MF->GetPathName());
if (!bSaved) if (!bSaved)
Result.Append(TEXT("WARNING: Package save failed\n")); UMCPServer::Print(TEXT("WARNING: Package save failed\n"));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -36,7 +37,7 @@ public:
return TEXT("Remove a parameter override from a Material Instance, reverting it to the parent material's value."); return TEXT("Remove a parameter override from a Material Instance, reverting it to the parent material's value.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UMaterialInstanceConstant* MI = F.Asset(Path).Cast<UMaterialInstanceConstant>(); UMaterialInstanceConstant* MI = F.Asset(Path).Cast<UMaterialInstanceConstant>();
@@ -66,13 +67,13 @@ public:
if (Removed == 0) if (Removed == 0)
{ {
Result.Appendf(TEXT("No override found for parameter '%s' (association=%s layer=%d) on %s"), UMCPServer::Printf(TEXT("No override found for parameter '%s' (association=%s layer=%d) on %s"),
*Parameter, *ParameterAssociation, ParameterLayer, *MCPUtils::FormatName(MI)); *Parameter, *ParameterAssociation, ParameterLayer, *MCPUtils::FormatName(MI));
return; return;
} }
MCPUtils::SaveGenericPackage(MI); MCPUtils::SaveGenericPackage(MI);
Result.Appendf(TEXT("Cleared override for '%s' on %s\n"), UMCPServer::Printf(TEXT("Cleared override for '%s' on %s\n"),
*Parameter, *MCPUtils::FormatName(MI)); *Parameter, *MCPUtils::FormatName(MI));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -33,7 +34,7 @@ public:
return TEXT("Create a new Material Instance Constant asset with a specified parent material."); return TEXT("Create a new Material Instance Constant asset with a specified parent material.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPPackageMaker Maker(AssetPath); MCPPackageMaker Maker(AssetPath);
if (!Maker.Ok()) return; if (!Maker.Ok()) return;
@@ -63,7 +64,7 @@ public:
if (!ParentMaterialObj) if (!ParentMaterialObj)
{ {
Result.Appendf(TEXT("ERROR: Parent material '%s' not found\n"), *ParentMaterial); UMCPServer::Printf(TEXT("ERROR: Parent material '%s' not found\n"), *ParentMaterial);
return; return;
} }
@@ -77,14 +78,14 @@ public:
// Save. // Save.
bool bSaved = MCPUtils::SaveGenericPackage(MI); bool bSaved = MCPUtils::SaveGenericPackage(MI);
Result.Appendf(TEXT("Created %s\n"), *MI->GetPathName()); UMCPServer::Printf(TEXT("Created %s\n"), *MI->GetPathName());
if (UMaterialInstance* ParentMI = Cast<UMaterialInstance>(ParentMaterialObj)) if (UMaterialInstance* ParentMI = Cast<UMaterialInstance>(ParentMaterialObj))
Result.Appendf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentMI)); UMCPServer::Printf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentMI));
else if (UMaterial* ParentMat = Cast<UMaterial>(ParentMaterialObj)) else if (UMaterial* ParentMat = Cast<UMaterial>(ParentMaterialObj))
Result.Appendf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentMat)); UMCPServer::Printf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentMat));
else else
Result.Appendf(TEXT("Parent: %s\n"), *ParentMaterialObj->GetPathName()); UMCPServer::Printf(TEXT("Parent: %s\n"), *ParentMaterialObj->GetPathName());
if (!bSaved) if (!bSaved)
Result.Append(TEXT("WARNING: Package save failed\n")); UMCPServer::Print(TEXT("WARNING: Package save failed\n"));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -27,7 +28,7 @@ public:
return TEXT("List all parameters on a Material Instance, showing current values and which are overridden."); return TEXT("List all parameters on a Material Instance, showing current values and which are overridden.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UMaterialInstanceConstant* MI = F.Asset(Path).Cast<UMaterialInstanceConstant>(); UMaterialInstanceConstant* MI = F.Asset(Path).Cast<UMaterialInstanceConstant>();
@@ -40,8 +41,8 @@ public:
for (auto& [Info, Meta] : AllParams) for (auto& [Info, Meta] : AllParams)
{ {
if (!Meta.bOverride) continue; if (!Meta.bOverride) continue;
if (!bHasOverrides) { Result.Append(TEXT("\nOverridden Parameters:\n")); bHasOverrides = true; } if (!bHasOverrides) { UMCPServer::Print(TEXT("\nOverridden Parameters:\n")); bHasOverrides = true; }
MCPUtils::FormatMaterialParameter(Result, Info, Meta); MCPUtils::FormatMaterialParameter(Info, Meta);
} }
// Inherited (non-overridden) parameters. // Inherited (non-overridden) parameters.
@@ -49,8 +50,8 @@ public:
for (auto& [Info, Meta] : AllParams) for (auto& [Info, Meta] : AllParams)
{ {
if (Meta.bOverride) continue; if (Meta.bOverride) continue;
if (!bHasInherited) { Result.Append(TEXT("\nInherited Parameters (not overridden):\n")); bHasInherited = true; } if (!bHasInherited) { UMCPServer::Print(TEXT("\nInherited Parameters (not overridden):\n")); bHasInherited = true; }
MCPUtils::FormatMaterialParameter(Result, Info, Meta); MCPUtils::FormatMaterialParameter(Info, Meta);
} }
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -40,7 +41,7 @@ public:
return TEXT("Set a parameter override on a Material Instance."); return TEXT("Set a parameter override on a Material Instance.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UMaterialInstanceConstant* MI = F.Asset(Path).Cast<UMaterialInstanceConstant>(); UMaterialInstanceConstant* MI = F.Asset(Path).Cast<UMaterialInstanceConstant>();
@@ -59,13 +60,13 @@ public:
FMaterialParameterMetadata* Found = AllParams.Find(ParamID); FMaterialParameterMetadata* Found = AllParams.Find(ParamID);
if (!Found) if (!Found)
{ {
Result.Appendf(TEXT("No parameter named '%s' with association=%s layer=%d"), UMCPServer::Printf(TEXT("No parameter named '%s' with association=%s layer=%d"),
*Parameter, *ParameterAssociation, ParameterLayer); *Parameter, *ParameterAssociation, ParameterLayer);
return; return;
} }
if (Found->PrimitiveDataIndex != INDEX_NONE) if (Found->PrimitiveDataIndex != INDEX_NONE)
{ {
Result.Appendf(TEXT("Parameter '%s' uses custom primitive data and cannot be set on a material instance"), *Parameter); UMCPServer::Printf(TEXT("Parameter '%s' uses custom primitive data and cannot be set on a material instance"), *Parameter);
return; return;
} }
@@ -78,7 +79,7 @@ public:
float ScalarValue; float ScalarValue;
if (!FDefaultValueHelper::ParseFloat(Value, ScalarValue)) if (!FDefaultValueHelper::ParseFloat(Value, ScalarValue))
{ {
Result.Appendf(TEXT("Failed to parse '%s' as a float"), *Value); UMCPServer::Printf(TEXT("Failed to parse '%s' as a float"), *Value);
return; return;
} }
MI->SetScalarParameterValueEditorOnly(ParamID, ScalarValue); MI->SetScalarParameterValueEditorOnly(ParamID, ScalarValue);
@@ -89,19 +90,19 @@ public:
FLinearColor Color; FLinearColor Color;
if (!Color.InitFromString(Value)) if (!Color.InitFromString(Value))
{ {
Result.Appendf(TEXT("Failed to parse '%s' as a color/vector (expected format: '(R=1,G=0,B=0,A=1)')"), *Value); UMCPServer::Printf(TEXT("Failed to parse '%s' as a color/vector (expected format: '(R=1,G=0,B=0,A=1)')"), *Value);
return; return;
} }
MI->SetVectorParameterValueEditorOnly(ParamID, Color); MI->SetVectorParameterValueEditorOnly(ParamID, Color);
break; break;
} }
default: default:
Result.Appendf(TEXT("Parameters of type %d (see EMaterialParameterType) are not implemented"), (int)Type); UMCPServer::Printf(TEXT("Parameters of type %d (see EMaterialParameterType) are not implemented"), (int)Type);
return; return;
} }
MCPUtils::SaveGenericPackage(MI); MCPUtils::SaveGenericPackage(MI);
Result.Appendf(TEXT("Set '%s' = %s on %s\n"), UMCPServer::Printf(TEXT("Set '%s' = %s on %s\n"),
*Parameter, *Value, *MCPUtils::FormatName(MI)); *Parameter, *Value, *MCPUtils::FormatName(MI));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -26,7 +27,7 @@ public:
return TEXT("Force recompile a material and check for compilation errors."); return TEXT("Force recompile a material and check for compilation errors.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
// Load material // Load material
MCPFetcher F; MCPFetcher F;
@@ -47,14 +48,14 @@ public:
if (Errors.IsEmpty()) if (Errors.IsEmpty())
{ {
Result.Appendf(TEXT("%s compiled successfully.\n"), *MCPUtils::FormatName(MaterialObj)); UMCPServer::Printf(TEXT("%s compiled successfully.\n"), *MCPUtils::FormatName(MaterialObj));
} }
else else
{ {
Result.Appendf(TEXT("%s compiled with %d error(s):\n"), *MCPUtils::FormatName(MaterialObj), Errors.Num()); UMCPServer::Printf(TEXT("%s compiled with %d error(s):\n"), *MCPUtils::FormatName(MaterialObj), Errors.Num());
for (const FString& Err : Errors) for (const FString& Err : Errors)
{ {
Result.Appendf(TEXT(" %s\n"), *Err); UMCPServer::Printf(TEXT(" %s\n"), *Err);
} }
} }
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -38,7 +39,7 @@ public:
return TEXT("Create a new UMaterial asset with optional domain, blend mode, and two-sided settings."); return TEXT("Create a new UMaterial asset with optional domain, blend mode, and two-sided settings.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPPackageMaker Maker(AssetPath); MCPPackageMaker Maker(AssetPath);
if (!Maker.Ok()) return; if (!Maker.Ok()) return;
@@ -73,11 +74,11 @@ public:
bool bSaved = MCPUtils::SaveGenericPackage(MaterialObj); bool bSaved = MCPUtils::SaveGenericPackage(MaterialObj);
Result.Appendf(TEXT("Created %s\n"), *MaterialObj->GetPathName()); UMCPServer::Printf(TEXT("Created %s\n"), *MaterialObj->GetPathName());
Result.Appendf(TEXT("Domain: %s\n"), *MCPUtils::EnumToString(MaterialObj->MaterialDomain, TEXT("MD_"))); UMCPServer::Printf(TEXT("Domain: %s\n"), *MCPUtils::EnumToString(MaterialObj->MaterialDomain, TEXT("MD_")));
Result.Appendf(TEXT("BlendMode: %s\n"), *MCPUtils::EnumToString(MaterialObj->BlendMode, TEXT("BLEND_"))); UMCPServer::Printf(TEXT("BlendMode: %s\n"), *MCPUtils::EnumToString(MaterialObj->BlendMode, TEXT("BLEND_")));
Result.Appendf(TEXT("TwoSided: %s\n"), MaterialObj->TwoSided ? TEXT("true") : TEXT("false")); UMCPServer::Printf(TEXT("TwoSided: %s\n"), MaterialObj->TwoSided ? TEXT("true") : TEXT("false"));
if (!bSaved) if (!bSaved)
Result.Append(TEXT("WARNING: Package save failed\n")); UMCPServer::Print(TEXT("WARNING: Package save failed\n"));
} }
}; };

View File

@@ -27,7 +27,7 @@ public:
return TEXT("List all parameters on a Material, showing their default values."); return TEXT("List all parameters on a Material, showing their default values.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UMaterial* Mat = F.Asset(Path).Cast<UMaterial>(); UMaterial* Mat = F.Asset(Path).Cast<UMaterial>();
@@ -37,7 +37,7 @@ public:
for (auto& [Info, Meta] : AllParams) for (auto& [Info, Meta] : AllParams)
{ {
MCPUtils::FormatMaterialParameter(Result, Info, Meta); MCPUtils::FormatMaterialParameter(Info, Meta);
} }
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPProperty.h" #include "MCPProperty.h"
@@ -36,7 +37,7 @@ public:
"showing current values and which properties are editable."); "showing current values and which properties are editable.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
// Resolve the path to an object and get its editable template. // Resolve the path to an object and get its editable template.
MCPFetcher F; MCPFetcher F;
@@ -70,9 +71,9 @@ public:
for (const FString& Category : Categories) for (const FString& Category : Categories)
{ {
if (Category.IsEmpty()) if (Category.IsEmpty())
Result.Append(TEXT("\nUncategorized:\n")); UMCPServer::Print(TEXT("\nUncategorized:\n"));
else else
Result.Appendf(TEXT("\n%s:\n"), *Category); UMCPServer::Printf(TEXT("\n%s:\n"), *Category);
for (MCPProperty& P : ByCategory[Category]) for (MCPProperty& P : ByCategory[Category])
{ {
@@ -83,7 +84,7 @@ public:
ValueStr = ValueStr.Left(80) + TEXT("..."); ValueStr = ValueStr.Left(80) + TEXT("...");
bool bEditable = !P->HasAnyPropertyFlags(CPF_EditConst); bool bEditable = !P->HasAnyPropertyFlags(CPF_EditConst);
Result.Appendf(TEXT(" %s %s %s = %s\n"), UMCPServer::Printf(TEXT(" %s %s %s = %s\n"),
bEditable ? TEXT("editable") : TEXT("readonly"), bEditable ? TEXT("editable") : TEXT("readonly"),
*MCPUtils::FormatPropertyType(P.Prop), *MCPUtils::FormatPropertyType(P.Prop),
*PropName, *PropName,
@@ -92,6 +93,6 @@ public:
} }
if (Props.IsEmpty()) if (Props.IsEmpty())
Result.Append(TEXT(" (no blueprint-visible properties found)\n")); UMCPServer::Print(TEXT(" (no blueprint-visible properties found)\n"));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPProperty.h" #include "MCPProperty.h"
@@ -29,7 +30,7 @@ public:
return TEXT("Get the value of a single property on an object resolved via MCPFetcher path."); return TEXT("Get the value of a single property on an object resolved via MCPFetcher path.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPFetcher F; MCPFetcher F;
UObject* Template = F.Walk(Path).Template().Cast<UObject>(); UObject* Template = F.Walk(Path).Template().Cast<UObject>();
@@ -38,7 +39,7 @@ public:
MCPProperty P = MCPProperty::GetOneExactMatch(Template, CPF_Edit, Property); MCPProperty P = MCPProperty::GetOneExactMatch(Template, CPF_Edit, Property);
if (!P) return; if (!P) return;
Result.Append(P.GetText()); UMCPServer::Print(P.GetText());
Result.Append(TEXT("\n")); UMCPServer::Print(TEXT("\n"));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPProperty.h" #include "MCPProperty.h"
@@ -30,7 +31,7 @@ public:
"Properties is a JSON object like {\"TwoSided\": \"true\", \"BlendMode\": \"BLEND_Translucent\"}."); "Properties is a JSON object like {\"TwoSided\": \"true\", \"BlendMode\": \"BLEND_Translucent\"}.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
// Resolve the path to an object and get its editable template. // Resolve the path to an object and get its editable template.
MCPFetcher F; MCPFetcher F;
@@ -39,7 +40,7 @@ public:
if (!Properties.Json || Properties.Json->Values.Num() == 0) if (!Properties.Json || Properties.Json->Values.Num() == 0)
{ {
Result.Append(TEXT("Error: No properties specified\n")); UMCPServer::Print(TEXT("Error: No properties specified\n"));
return; return;
} }
@@ -53,7 +54,7 @@ public:
FString ValueStr; FString ValueStr;
if (!Pair.Value->TryGetString(ValueStr)) if (!Pair.Value->TryGetString(ValueStr))
{ {
Result.Appendf(TEXT("Error: Value for '%s' must be a string\n"), *Pair.Key); UMCPServer::Printf(TEXT("Error: Value for '%s' must be a string\n"), *Pair.Key);
return; return;
} }
Resolved.Emplace(P, ValueStr); Resolved.Emplace(P, ValueStr);
@@ -71,8 +72,8 @@ public:
// Save. // Save.
bool bSaved = MCPUtils::SaveGenericPackage(Template); bool bSaved = MCPUtils::SaveGenericPackage(Template);
Result.Appendf(TEXT("Set %d/%d properties.\n"), SuccessCount, Properties.Json->Values.Num()); UMCPServer::Printf(TEXT("Set %d/%d properties.\n"), SuccessCount, Properties.Json->Values.Num());
if (!bSaved) if (!bSaved)
Result.Append(TEXT("Warning: Save failed\n")); UMCPServer::Print(TEXT("Warning: Save failed\n"));
} }
}; };

View File

@@ -3,6 +3,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPServer.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "ShowCommands.generated.h" #include "ShowCommands.generated.h"
@@ -23,7 +24,7 @@ public:
return TEXT("List all available commands with their descriptions."); return TEXT("List all available commands with their descriptions.");
} }
virtual void Handle(FStringBuilderBase& Result) override void EmitCommandList()
{ {
FString QueryLower = Query.ToLower(); FString QueryLower = Query.ToLower();
FString PrevGroup; FString PrevGroup;
@@ -39,40 +40,47 @@ public:
if (Group != PrevGroup) if (Group != PrevGroup)
{ {
if (!PrevGroup.IsEmpty()) if (!PrevGroup.IsEmpty())
Result.Append(TEXT("\n")); UMCPServer::Print(TEXT("\n"));
PrevGroup = Group; PrevGroup = Group;
} }
if (Verbose) if (Verbose)
{ {
MCPUtils::FormatCommandHelp(Class, Result); MCPUtils::FormatCommandHelp(Class);
continue; continue;
} }
Result.Append(ToolName); UMCPServer::Print(ToolName);
Result.Append(TEXT("(")); UMCPServer::Print(TEXT("("));
bool bFirst = true; bool bFirst = true;
for (TFieldIterator<FProperty> PropIt(Class, EFieldIterationFlags::None); PropIt; ++PropIt) for (TFieldIterator<FProperty> PropIt(Class, EFieldIterationFlags::None); PropIt; ++PropIt)
{ {
if (!bFirst) Result.Append(TEXT(",")); if (!bFirst) UMCPServer::Print(TEXT(","));
bFirst = false; bFirst = false;
if (PropIt->HasMetaData(TEXT("Optional"))) Result.Append(TEXT("?")); if (PropIt->HasMetaData(TEXT("Optional"))) UMCPServer::Print(TEXT("?"));
Result.Append(MCPUtils::FormatPropertyType(*PropIt)); UMCPServer::Print(MCPUtils::FormatPropertyType(*PropIt));
Result.Append(TEXT(" ")); UMCPServer::Print(TEXT(" "));
Result.Append(MCPUtils::PropertyNameToJsonKey(PropIt->GetName())); UMCPServer::Print(MCPUtils::PropertyNameToJsonKey(PropIt->GetName()));
} }
Result.Append(TEXT(")\n")); UMCPServer::Print(TEXT(")\n"));
} }
}
virtual void Handle() override
{
UMCPServer::Printf(TEXT("\n"));
EmitCommandList();
// Append Path documentation. // Append Path documentation.
Result.Append(TEXT("\n")); UMCPServer::Print(TEXT("\n"));
Result.Append(TEXT("Some commands take a Path parameter. A Path starts with an asset\n")); UMCPServer::Print(TEXT("Some commands take a Path parameter. A Path starts with an asset\n"));
Result.Append(TEXT("package path (e.g. /Game/Widgets/WB_Hotkeys), followed by zero or\n")); UMCPServer::Print(TEXT("package path (e.g. /Game/Widgets/WB_Hotkeys), followed by zero or\n"));
Result.Append(TEXT("more comma-separated steps that navigate into the asset:\n")); UMCPServer::Print(TEXT("more comma-separated steps that navigate into the asset:\n"));
Result.Append(TEXT("\n")); UMCPServer::Print(TEXT("\n"));
for (const MCPFetcher::FWalker& W : MCPFetcher::GetWalkerTable()) for (const MCPFetcher::FWalker& W : MCPFetcher::GetWalkerTable())
{ {
Result.Appendf(TEXT(" %s — %s\n"), W.Key, W.Description); UMCPServer::Printf(TEXT(" %s — %s\n"), W.Key, W.Description);
} }
Result.Append(TEXT("\nExample: /Game/Widgets/WB_Hotkeys,graph:EventGraph,node:Self_Reference_03,pin:Result\n")); UMCPServer::Print(TEXT("\nExample: /Game/Widgets/WB_Hotkeys,graph:EventGraph,node:Self_Reference_03,pin:Result\n"));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
@@ -49,7 +50,7 @@ public:
"Optionally assign an animation asset to the state."); "Optionally assign an animation asset to the state.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
// Resolve the anim blueprint // Resolve the anim blueprint
MCPFetcher F; MCPFetcher F;
@@ -60,14 +61,14 @@ public:
UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph); UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph);
if (!SMGraph) if (!SMGraph)
{ {
Result.Appendf(TEXT("ERROR: State machine graph '%s' not found in %s\n"), *Graph, *MCPUtils::FormatName(AnimBP)); UMCPServer::Printf(TEXT("ERROR: State machine graph '%s' not found in %s\n"), *Graph, *MCPUtils::FormatName(AnimBP));
return; return;
} }
// Check for duplicate state name // Check for duplicate state name
if (MCPUtils::FindStateByName(SMGraph, StateName)) if (MCPUtils::FindStateByName(SMGraph, StateName))
{ {
Result.Appendf(TEXT("ERROR: State '%s' already exists in %s\n"), *StateName, *MCPUtils::FormatName(SMGraph)); UMCPServer::Printf(TEXT("ERROR: State '%s' already exists in %s\n"), *StateName, *MCPUtils::FormatName(SMGraph));
return; return;
} }
@@ -110,7 +111,7 @@ public:
FKismetEditorUtilities::CompileBlueprint(AnimBP); FKismetEditorUtilities::CompileBlueprint(AnimBP);
MCPUtils::SaveBlueprintPackage(AnimBP); MCPUtils::SaveBlueprintPackage(AnimBP);
Result.Appendf(TEXT("Created state '%s' in %s\n"), *StateName, *MCPUtils::FormatName(SMGraph)); UMCPServer::Printf(TEXT("Created state '%s' in %s\n"), *StateName, *MCPUtils::FormatName(SMGraph));
Result.Appendf(TEXT(" node: %s\n"), *MCPUtils::FormatName(NewState)); UMCPServer::Printf(TEXT(" node: %s\n"), *MCPUtils::FormatName(NewState));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -48,7 +49,7 @@ public:
return TEXT("Add a transition between two states in an animation state machine graph."); return TEXT("Add a transition between two states in an animation state machine graph.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPAssets<UAnimBlueprint> Assets; MCPAssets<UAnimBlueprint> Assets;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
@@ -57,7 +58,7 @@ public:
UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph); UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph);
if (!SMGraph) if (!SMGraph)
{ {
Result.Appendf(TEXT("ERROR: State machine graph '%s' not found in '%s'\n"), *Graph, *MCPUtils::FormatName(AnimBP)); UMCPServer::Printf(TEXT("ERROR: State machine graph '%s' not found in '%s'\n"), *Graph, *MCPUtils::FormatName(AnimBP));
return; return;
} }
@@ -92,7 +93,7 @@ public:
FKismetEditorUtilities::CompileBlueprint(AnimBP); FKismetEditorUtilities::CompileBlueprint(AnimBP);
MCPUtils::SaveBlueprintPackage(AnimBP); MCPUtils::SaveBlueprintPackage(AnimBP);
Result.Appendf(TEXT("Created transition %s -> %s: %s\n"), UMCPServer::Printf(TEXT("Created transition %s -> %s: %s\n"),
*FromState, *ToState, *MCPUtils::FormatName(TransNode)); *FromState, *ToState, *MCPUtils::FormatName(TransNode));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -33,7 +34,7 @@ public:
return TEXT("Remove a state and its connected transitions from an animation state machine graph."); return TEXT("Remove a state and its connected transitions from an animation state machine graph.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
// Fetch the state machine graph via MCPFetcher // Fetch the state machine graph via MCPFetcher
MCPFetcher F; MCPFetcher F;
@@ -47,7 +48,7 @@ public:
UBlueprint* BP = Cast<UBlueprint>(SMGraph->GetOuter()->GetOuter()); UBlueprint* BP = Cast<UBlueprint>(SMGraph->GetOuter()->GetOuter());
if (!BP) if (!BP)
{ {
Result.Append(TEXT("ERROR: Could not find owning blueprint.\n")); UMCPServer::Print(TEXT("ERROR: Could not find owning blueprint.\n"));
return; return;
} }
@@ -75,7 +76,7 @@ public:
FKismetEditorUtilities::CompileBlueprint(BP); FKismetEditorUtilities::CompileBlueprint(BP);
MCPUtils::SaveBlueprintPackage(BP); MCPUtils::SaveBlueprintPackage(BP);
Result.Appendf(TEXT("Removed state %s and %d transition(s).\n"), UMCPServer::Printf(TEXT("Removed state %s and %d transition(s).\n"),
*MCPUtils::FormatName(StateNode), RemovedTransitions); *MCPUtils::FormatName(StateNode), RemovedTransitions);
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
@@ -43,7 +44,7 @@ public:
return TEXT("Set or replace the animation sequence played by a state in an animation state machine."); return TEXT("Set or replace the animation sequence played by a state in an animation state machine.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
// Resolve the anim blueprint // Resolve the anim blueprint
MCPFetcher F; MCPFetcher F;
@@ -54,7 +55,7 @@ public:
UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph); UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph);
if (!SMGraph) if (!SMGraph)
{ {
Result.Appendf(TEXT("ERROR: State machine graph '%s' not found in %s\n"), *Graph, *MCPUtils::FormatName(AnimBP)); UMCPServer::Printf(TEXT("ERROR: State machine graph '%s' not found in %s\n"), *Graph, *MCPUtils::FormatName(AnimBP));
return; return;
} }
@@ -65,7 +66,7 @@ public:
UEdGraph* InnerGraph = StateNode->GetBoundGraph(); UEdGraph* InnerGraph = StateNode->GetBoundGraph();
if (!InnerGraph) if (!InnerGraph)
{ {
Result.Appendf(TEXT("ERROR: State '%s' has no bound graph\n"), *StateName); UMCPServer::Printf(TEXT("ERROR: State '%s' has no bound graph\n"), *StateName);
return; return;
} }
@@ -102,8 +103,8 @@ public:
MCPUtils::SaveBlueprintPackage(AnimBP); MCPUtils::SaveBlueprintPackage(AnimBP);
if (bCreatedNew) if (bCreatedNew)
Result.Appendf(TEXT("Created sequence player in state '%s', assigned %s\n"), *StateName, *MCPUtils::FormatName(AnimSeq)); UMCPServer::Printf(TEXT("Created sequence player in state '%s', assigned %s\n"), *StateName, *MCPUtils::FormatName(AnimSeq));
else else
Result.Appendf(TEXT("Updated sequence player in state '%s' to %s\n"), *StateName, *MCPUtils::FormatName(AnimSeq)); UMCPServer::Printf(TEXT("Updated sequence player in state '%s' to %s\n"), *StateName, *MCPUtils::FormatName(AnimSeq));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
@@ -53,7 +54,7 @@ public:
"and optionally wire blueprint variables to the X and Y axis inputs."); "and optionally wire blueprint variables to the X and Y axis inputs.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
// Load the anim blueprint // Load the anim blueprint
MCPAssets<UAnimBlueprint> Assets; MCPAssets<UAnimBlueprint> Assets;
@@ -62,13 +63,13 @@ public:
// Find the state machine graph and state // Find the state machine graph and state
UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph); UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph);
if (!SMGraph) { Result.Appendf(TEXT("ERROR: State machine graph '%s' not found\n"), *Graph); return; } if (!SMGraph) { UMCPServer::Printf(TEXT("ERROR: State machine graph '%s' not found\n"), *Graph); return; }
UAnimStateNode* StateNode = MCPUtils::FindStateByName(SMGraph, StateName); UAnimStateNode* StateNode = MCPUtils::FindStateByName(SMGraph, StateName);
if (!StateNode) return; if (!StateNode) return;
UEdGraph* InnerGraph = StateNode->GetBoundGraph(); UEdGraph* InnerGraph = StateNode->GetBoundGraph();
if (!InnerGraph) { Result.Appendf(TEXT("ERROR: State '%s' has no bound graph\n"), *StateName); return; } if (!InnerGraph) { UMCPServer::Printf(TEXT("ERROR: State '%s' has no bound graph\n"), *StateName); return; }
// Load the blend space asset // Load the blend space asset
MCPAssets<UBlendSpace> BlendSpaceAssets; MCPAssets<UBlendSpace> BlendSpaceAssets;
@@ -100,17 +101,17 @@ public:
ConnectToOutputPose(BSNode, InnerGraph); ConnectToOutputPose(BSNode, InnerGraph);
// Wire X and Y variables if provided // Wire X and Y variables if provided
WireVariable(AnimBP, InnerGraph, BSNode, XVariable, TEXT("X"), Result); WireVariable(AnimBP, InnerGraph, BSNode, XVariable, TEXT("X"));
WireVariable(AnimBP, InnerGraph, BSNode, YVariable, TEXT("Y"), Result); WireVariable(AnimBP, InnerGraph, BSNode, YVariable, TEXT("Y"));
// Compile and save // Compile and save
FKismetEditorUtilities::CompileBlueprint(AnimBP); FKismetEditorUtilities::CompileBlueprint(AnimBP);
bool bSaved = MCPUtils::SaveBlueprintPackage(AnimBP); bool bSaved = MCPUtils::SaveBlueprintPackage(AnimBP);
Result.Appendf(TEXT("BlendSpacePlayer %s placed in state %s\n"), UMCPServer::Printf(TEXT("BlendSpacePlayer %s placed in state %s\n"),
*MCPUtils::FormatName(BSNode), *StateName); *MCPUtils::FormatName(BSNode), *StateName);
if (!bSaved) if (!bSaved)
Result.Append(TEXT("WARNING: Failed to save package\n")); UMCPServer::Print(TEXT("WARNING: Failed to save package\n"));
} }
private: private:
@@ -160,7 +161,7 @@ private:
void WireVariable(UAnimBlueprint* AnimBP, UEdGraph* InnerGraph, void WireVariable(UAnimBlueprint* AnimBP, UEdGraph* InnerGraph,
UAnimGraphNode_BlendSpacePlayer* BSNode, const FString& VarName, UAnimGraphNode_BlendSpacePlayer* BSNode, const FString& VarName,
const TCHAR* PinName, FStringBuilderBase& Result) const TCHAR* PinName)
{ {
if (VarName.IsEmpty()) return; if (VarName.IsEmpty()) return;
@@ -185,7 +186,7 @@ private:
} }
if (!bVarFound) if (!bVarFound)
{ {
Result.Appendf(TEXT("WARNING: Variable '%s' not found, skipping %s wire\n"), *VarName, PinName); UMCPServer::Printf(TEXT("WARNING: Variable '%s' not found, skipping %s wire\n"), *VarName, PinName);
return; return;
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -53,7 +54,7 @@ public:
return TEXT("Update properties on an existing transition between two states in an animation state machine."); return TEXT("Update properties on an existing transition between two states in an animation state machine.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPAssets<UAnimBlueprint> Assets; MCPAssets<UAnimBlueprint> Assets;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
@@ -62,14 +63,14 @@ public:
UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph); UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph);
if (!SMGraph) if (!SMGraph)
{ {
Result.Appendf(TEXT("ERROR: State machine graph '%s' not found in '%s'\n"), *Graph, *MCPUtils::FormatName(AnimBP)); UMCPServer::Printf(TEXT("ERROR: State machine graph '%s' not found in '%s'\n"), *Graph, *MCPUtils::FormatName(AnimBP));
return; return;
} }
UAnimStateTransitionNode* TransNode = MCPUtils::FindTransition(SMGraph, FromState, ToState); UAnimStateTransitionNode* TransNode = MCPUtils::FindTransition(SMGraph, FromState, ToState);
if (!TransNode) if (!TransNode)
{ {
Result.Appendf(TEXT("ERROR: Transition from '%s' to '%s' not found in graph '%s'\n"), UMCPServer::Printf(TEXT("ERROR: Transition from '%s' to '%s' not found in graph '%s'\n"),
*FromState, *ToState, *Graph); *FromState, *ToState, *Graph);
return; return;
} }
@@ -85,7 +86,7 @@ public:
FKismetEditorUtilities::CompileBlueprint(AnimBP); FKismetEditorUtilities::CompileBlueprint(AnimBP);
MCPUtils::SaveBlueprintPackage(AnimBP); MCPUtils::SaveBlueprintPackage(AnimBP);
Result.Appendf(TEXT("Updated transition %s -> %s: %s\n"), UMCPServer::Printf(TEXT("Updated transition %s -> %s: %s\n"),
*FromState, *ToState, *MCPUtils::FormatName(TransNode)); *FromState, *ToState, *MCPUtils::FormatName(TransNode));
} }
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssets.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
@@ -45,7 +46,7 @@ public:
return TEXT("Create a new UserDefinedStruct asset with optional initial properties."); return TEXT("Create a new UserDefinedStruct asset with optional initial properties.");
} }
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle() override
{ {
MCPPackageMaker Maker(AssetPath); MCPPackageMaker Maker(AssetPath);
if (!Maker.Ok()) return; if (!Maker.Ok()) return;
@@ -88,10 +89,10 @@ public:
bool bSaved = MCPUtils::SaveGenericPackage(NewStruct); bool bSaved = MCPUtils::SaveGenericPackage(NewStruct);
Result.Appendf(TEXT("Created %s\n"), *NewStruct->GetPathName()); UMCPServer::Printf(TEXT("Created %s\n"), *NewStruct->GetPathName());
if (PropsAdded > 0) if (PropsAdded > 0)
Result.Appendf(TEXT("Properties added: %d\n"), PropsAdded); UMCPServer::Printf(TEXT("Properties added: %d\n"), PropsAdded);
if (!bSaved) if (!bSaved)
Result.Append(TEXT("WARNING: Package save failed\n")); UMCPServer::Print(TEXT("WARNING: Package save failed\n"));
} }
}; };

View File

@@ -260,57 +260,17 @@ TStatId UMCPServer::GetStatId() const
FString UMCPServer::HandleRequest(const FString& Line) FString UMCPServer::HandleRequest(const FString& Line)
{ {
// Turn the request string into a JSON tree.
TSharedPtr<FJsonObject> Request;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Line);
FJsonSerializer::Deserialize(Reader, Request);
if (!Request.IsValid())
{
return TEXT("Request is not valid JSON");
}
// Extract the command from the request.
FString Command;
if (!Request->TryGetStringField(TEXT("command"), Command))
{
return TEXT("Request does not contain 'command' parameter");
}
Request->RemoveField(TEXT("command"));
// Find the handler UClass for the specified command.
UClass** HandlerClass = MCPHandlerRegistry.Find(Command);
if (!HandlerClass)
{
return FString::Printf(TEXT("Unknown command: %s"), *Command);
}
// Make an object of the handler class.
TStrongObjectPtr<UObject> HandlerObj(NewObject<UObject>(GetTransientPackage(), *HandlerClass));
IMCPHandler* Handler = Cast<IMCPHandler>(HandlerObj.Get());
// Populate the handler object with the request parameters.
HandlerOutput.Reset();
if (!MCPUtils::PopulateFromJson(HandlerObj->GetClass(), HandlerObj.Get(), &*Request))
{
HandlerOutput.Append(TEXT("\nUsage:\n"));
MCPUtils::FormatCommandHelp(*HandlerClass, HandlerOutput);
FString Result = HandlerOutput.ToString();
HandlerOutput.Reset();
return Result;
}
// Invoke the handler with log capture.
LogCapture.CapturedErrors.Empty(); LogCapture.CapturedErrors.Empty();
LogCapture.bEnabled = true; LogCapture.bEnabled = true;
HandlerOutput.Reset(); HandlerOutput.Reset();
Handler->Handle(HandlerOutput);
TryCallHandler(Line);
Notifier.SendNotifications(); Notifier.SendNotifications();
LogCapture.bEnabled = false; LogCapture.bEnabled = false;
for (const FString& Msg : LogCapture.CapturedErrors) for (const FString& Msg : LogCapture.CapturedErrors)
{ {
HandlerOutput.Append(TEXT("LOG: ")); UMCPServer::Printf(TEXT("UE_LOG: %s\n"), *Msg);
HandlerOutput.Append(Msg);
HandlerOutput.Append(TEXT("\n"));
} }
LogCapture.CapturedErrors.Empty(); LogCapture.CapturedErrors.Empty();
FString Result = HandlerOutput.ToString(); FString Result = HandlerOutput.ToString();
@@ -322,6 +282,51 @@ FString UMCPServer::HandleRequest(const FString& Line)
return Result; return Result;
} }
void UMCPServer::TryCallHandler(const FString &Line)
{
// Turn the request string into a JSON tree.
TSharedPtr<FJsonObject> Request;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Line);
FJsonSerializer::Deserialize(Reader, Request);
if (!Request.IsValid())
{
UMCPServer::Printf(TEXT("Request is not valid JSON"));
return;
}
// Extract the command from the request.
FString Command;
if (!Request->TryGetStringField(TEXT("command"), Command))
{
UMCPServer::Printf(TEXT("Request does not contain 'command' parameter"));
return;
}
Request->RemoveField(TEXT("command"));
// Find the handler UClass for the specified command.
UClass** HandlerClass = MCPHandlerRegistry.Find(Command);
if (!HandlerClass)
{
UMCPServer::Printf(TEXT("Unknown command: %s"), *Command);
return;
}
// Make an object of the handler class.
TStrongObjectPtr<UObject> HandlerObj(NewObject<UObject>(GetTransientPackage(), *HandlerClass));
IMCPHandler* Handler = Cast<IMCPHandler>(HandlerObj.Get());
// Populate the handler object with the request parameters.
if (!MCPUtils::PopulateFromJson(HandlerObj->GetClass(), HandlerObj.Get(), &*Request))
{
UMCPServer::Printf(TEXT("\nUsage:\n\n"));
MCPUtils::FormatCommandHelp(*HandlerClass);
return;
}
// Invoke the handler.
Handler->Handle();
}
// ============================================================ // ============================================================
// Connection Maintenance // Connection Maintenance
// ============================================================ // ============================================================

View File

@@ -813,7 +813,7 @@ bool MCPUtils::ParseMaterialParameterAssociation(const FString& Str, EMaterialPa
return true; return true;
} }
void MCPUtils::FormatMaterialParameter(FStringBuilderBase& Result, const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta) void MCPUtils::FormatMaterialParameter(const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta)
{ {
// Association prefix for layer/blend parameters. // Association prefix for layer/blend parameters.
FString Prefix; FString Prefix;
@@ -825,35 +825,35 @@ void MCPUtils::FormatMaterialParameter(FStringBuilderBase& Result, const FMateri
switch (Meta.Value.Type) switch (Meta.Value.Type)
{ {
case EMaterialParameterType::Scalar: case EMaterialParameterType::Scalar:
Result.Appendf(TEXT(" %sScalar \"%s\" = %g\n"), *Prefix, *Info.Name.ToString(), Meta.Value.AsScalar()); UMCPServer::Printf(TEXT(" %sScalar \"%s\" = %g\n"), *Prefix, *Info.Name.ToString(), Meta.Value.AsScalar());
break; break;
case EMaterialParameterType::Vector: case EMaterialParameterType::Vector:
{ {
FLinearColor C = Meta.Value.AsLinearColor(); FLinearColor C = Meta.Value.AsLinearColor();
Result.Appendf(TEXT(" %sVector \"%s\" = (R=%.3f, G=%.3f, B=%.3f, A=%.3f)\n"), UMCPServer::Printf(TEXT(" %sVector \"%s\" = (R=%.3f, G=%.3f, B=%.3f, A=%.3f)\n"),
*Prefix, *Info.Name.ToString(), C.R, C.G, C.B, C.A); *Prefix, *Info.Name.ToString(), C.R, C.G, C.B, C.A);
break; break;
} }
case EMaterialParameterType::DoubleVector: case EMaterialParameterType::DoubleVector:
{ {
FVector4d V = Meta.Value.AsVector4d(); FVector4d V = Meta.Value.AsVector4d();
Result.Appendf(TEXT(" %sDoubleVector \"%s\" = (%.3f, %.3f, %.3f, %.3f)\n"), UMCPServer::Printf(TEXT(" %sDoubleVector \"%s\" = (%.3f, %.3f, %.3f, %.3f)\n"),
*Prefix, *Info.Name.ToString(), V.X, V.Y, V.Z, V.W); *Prefix, *Info.Name.ToString(), V.X, V.Y, V.Z, V.W);
break; break;
} }
case EMaterialParameterType::Texture: case EMaterialParameterType::Texture:
{ {
UTexture* Tex = Cast<UTexture>(Meta.Value.AsTextureObject()); UTexture* Tex = Cast<UTexture>(Meta.Value.AsTextureObject());
Result.Appendf(TEXT(" %sTexture \"%s\" = %s\n"), UMCPServer::Printf(TEXT(" %sTexture \"%s\" = %s\n"),
*Prefix, *Info.Name.ToString(), Tex ? *MCPUtils::FormatName(Tex) : TEXT("None")); *Prefix, *Info.Name.ToString(), Tex ? *MCPUtils::FormatName(Tex) : TEXT("None"));
break; break;
} }
case EMaterialParameterType::StaticSwitch: case EMaterialParameterType::StaticSwitch:
Result.Appendf(TEXT(" %sStaticSwitch \"%s\" = %s\n"), UMCPServer::Printf(TEXT(" %sStaticSwitch \"%s\" = %s\n"),
*Prefix, *Info.Name.ToString(), Meta.Value.AsStaticSwitch() ? TEXT("true") : TEXT("false")); *Prefix, *Info.Name.ToString(), Meta.Value.AsStaticSwitch() ? TEXT("true") : TEXT("false"));
break; break;
default: default:
Result.Appendf(TEXT(" %sType%d \"%s\"\n"), *Prefix, (int)Meta.Value.Type, *Info.Name.ToString()); UMCPServer::Printf(TEXT(" %sType%d \"%s\"\n"), *Prefix, (int)Meta.Value.Type, *Info.Name.ToString());
break; break;
} }
} }
@@ -1398,29 +1398,29 @@ TArray<FProperty*> MCPUtils::SearchProperties(UObject* Obj, const FString& Query
// FormatCommandHelp — verbose description of one handler command // FormatCommandHelp — verbose description of one handler command
// ============================================================ // ============================================================
void MCPUtils::FormatCommandHelp(UClass* HandlerClass, FStringBuilderBase& Result) void MCPUtils::FormatCommandHelp(UClass* HandlerClass)
{ {
const IMCPHandler* Handler = Cast<IMCPHandler>(HandlerClass->GetDefaultObject()); const IMCPHandler* Handler = Cast<IMCPHandler>(HandlerClass->GetDefaultObject());
if (!Handler) return; if (!Handler) return;
FString ToolName = GetHandlerName(HandlerClass); FString ToolName = GetHandlerName(HandlerClass);
Result.Append(TEXT("\n")); UMCPServer::Print(TEXT("\n"));
Result.Append(WrapText(Handler->GetDescription(), 80, TEXT("// "))); UMCPServer::Print(WrapText(Handler->GetDescription(), 80, TEXT("// ")));
Result.Append(TEXT("\n")); UMCPServer::Print(TEXT("\n"));
// Command signature line // Command signature line
Result.Append(ToolName); UMCPServer::Print(ToolName);
Result.Append(TEXT("(")); UMCPServer::Print(TEXT("("));
bool bFirst = true; bool bFirst = true;
for (TFieldIterator<FProperty> PropIt(HandlerClass, EFieldIterationFlags::None); PropIt; ++PropIt) for (TFieldIterator<FProperty> PropIt(HandlerClass, EFieldIterationFlags::None); PropIt; ++PropIt)
{ {
if (!bFirst) Result.Append(TEXT(",")); if (!bFirst) UMCPServer::Print(TEXT(","));
bFirst = false; bFirst = false;
if (PropIt->HasMetaData(TEXT("Optional"))) Result.Append(TEXT("?")); if (PropIt->HasMetaData(TEXT("Optional"))) UMCPServer::Print(TEXT("?"));
Result.Append(PropertyNameToJsonKey(PropIt->GetName())); UMCPServer::Print(PropertyNameToJsonKey(PropIt->GetName()));
} }
Result.Append(TEXT(")\n")); UMCPServer::Print(TEXT(")\n"));
// parameter details // parameter details
for (TFieldIterator<FProperty> PropIt(HandlerClass, EFieldIterationFlags::None); PropIt; ++PropIt) for (TFieldIterator<FProperty> PropIt(HandlerClass, EFieldIterationFlags::None); PropIt; ++PropIt)
@@ -1431,10 +1431,10 @@ void MCPUtils::FormatCommandHelp(UClass* HandlerClass, FStringBuilderBase& Resul
bool bOptional = Prop->HasMetaData(TEXT("Optional")); bool bOptional = Prop->HasMetaData(TEXT("Optional"));
const FString& Desc = Prop->GetMetaData(TEXT("Description")); const FString& Desc = Prop->GetMetaData(TEXT("Description"));
Result.Appendf(TEXT(" %s %s%s"), UMCPServer::Printf(TEXT(" %s %s%s"),
*Type, *Name, bOptional ? TEXT(" (optional)") : TEXT("")); *Type, *Name, bOptional ? TEXT(" (optional)") : TEXT(""));
if (!Desc.IsEmpty()) if (!Desc.IsEmpty())
Result.Appendf(TEXT(" — %s"), *Desc); UMCPServer::Printf(TEXT(" — %s"), *Desc);
Result.Append(TEXT("\n")); UMCPServer::Print(TEXT("\n"));
} }
} }

View File

@@ -51,5 +51,5 @@ public:
virtual FString GetDescription() const = 0; virtual FString GetDescription() const = 0;
// Called after parameter fields have been populated from JSON. // Called after parameter fields have been populated from JSON.
virtual void Handle(FStringBuilderBase& Result) {} virtual void Handle() {}
}; };

View File

@@ -53,6 +53,7 @@ public:
/** Print text to the handler output buffer. */ /** Print text to the handler output buffer. */
static void Print(const TCHAR* Text) { GMCPServer->HandlerOutput.Append(Text); } static void Print(const TCHAR* Text) { GMCPServer->HandlerOutput.Append(Text); }
static void Print(const FString& Text) { GMCPServer->HandlerOutput.Append(Text); }
/** Print formatted text to the handler output buffer. */ /** Print formatted text to the handler output buffer. */
template <typename FmtType, typename... ArgTypes> template <typename FmtType, typename... ArgTypes>
@@ -79,6 +80,7 @@ private:
// Handle a complete JSON line and return the response JSON // Handle a complete JSON line and return the response JSON
FString HandleRequest(const FString& Line); FString HandleRequest(const FString& Line);
void TryCallHandler(const FString &Line);
// ----- TCP server ----- // ----- TCP server -----
FSocket* ListenSocket = nullptr; FSocket* ListenSocket = nullptr;

View File

@@ -162,7 +162,7 @@ public:
// ----- Material Parameters ----- // ----- Material Parameters -----
static TMap<FMaterialParameterInfo, FMaterialParameterMetadata> GetMaterialParameters(UMaterialInterface* Material); static TMap<FMaterialParameterInfo, FMaterialParameterMetadata> GetMaterialParameters(UMaterialInterface* Material);
static bool ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation); static bool ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation);
static void FormatMaterialParameter(FStringBuilderBase& Result, const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta); static void FormatMaterialParameter(const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta);
// ----- Editable template ----- // ----- Editable template -----
static TArray<FProperty*> SearchProperties(UObject* Obj, const FString& Query, EPropertyFlags Flags, bool bLocal); static TArray<FProperty*> SearchProperties(UObject* Obj, const FString& Query, EPropertyFlags Flags, bool bLocal);
@@ -183,7 +183,7 @@ public:
static TArray<UClass*> CollectHandlerClasses(); static TArray<UClass*> CollectHandlerClasses();
static FString GetHandlerName(UClass* HandlerClass); static FString GetHandlerName(UClass* HandlerClass);
static FString GetHandlerGroup(UClass* HandlerClass); static FString GetHandlerGroup(UClass* HandlerClass);
static void FormatCommandHelp(UClass* HandlerClass, FStringBuilderBase& Result); static void FormatCommandHelp(UClass* HandlerClass);
private: private: