From 2ffc493e9150902cf60ddc39040ea0eb13c7a17d Mon Sep 17 00:00:00 2001 From: jyelon Date: Fri, 13 Mar 2026 23:41:59 -0400 Subject: [PATCH] Remove stringbuilder parameter for handlers --- .../Handlers/AnimBlueprint_Create.h | 11 ++- .../Handlers/AnimBlueprint_ListSlotNames.h | 7 +- .../Handlers/AnimBlueprint_ListSyncGroups.h | 7 +- .../AnimBlueprint_SetBlendSpaceSamples.h | 7 +- .../BlueprintMCP/Handlers/Asset_Backup.h | 9 +- .../BlueprintMCP/Handlers/Asset_Delete.h | 17 ++-- .../Handlers/Asset_FindReferences.h | 11 ++- .../BlueprintMCP/Handlers/Asset_Rename.h | 9 +- .../BlueprintMCP/Handlers/Asset_Restore.h | 11 ++- .../BlueprintMCP/Handlers/Asset_Search.h | 13 +-- .../BlueprintMCP/Handlers/BlendSpace_Create.h | 11 ++- .../Handlers/BlueprintGraph_Create.h | 21 +++-- .../Handlers/BlueprintGraph_Delete.h | 11 ++- .../Handlers/BlueprintGraph_Rename.h | 13 +-- .../Handlers/Blueprint_AddComponent.h | 8 +- .../Handlers/Blueprint_AddEventDispatcher.h | 19 ++-- .../Handlers/Blueprint_AddFunctionParameter.h | 4 +- .../Handlers/Blueprint_AddInterface.h | 12 +-- .../Handlers/Blueprint_AddVariable.h | 10 +- .../Blueprint_ChangeFunctionParameterType.h | 8 +- .../Handlers/Blueprint_ChangeVariableType.h | 19 ++-- .../BlueprintMCP/Handlers/Blueprint_Compile.h | 19 ++-- .../BlueprintMCP/Handlers/Blueprint_Create.h | 11 ++- .../BlueprintMCP/Handlers/Blueprint_Diff.h | 37 ++++---- .../BlueprintMCP/Handlers/Blueprint_Dump.h | 35 +++---- .../Handlers/Blueprint_ListComponents.h | 23 ++--- .../Handlers/Blueprint_ListEventDispatchers.h | 13 +-- .../Handlers/Blueprint_ListInterfaces.h | 9 +- .../Handlers/Blueprint_RefreshAllNodes.h | 13 +-- .../Handlers/Blueprint_RemoveComponent.h | 13 +-- .../Blueprint_RemoveFunctionParameter.h | 17 ++-- .../Handlers/Blueprint_RemoveInterface.h | 6 +- .../Handlers/Blueprint_RemoveVariable.h | 9 +- .../Handlers/Blueprint_Reparent.h | 6 +- .../BlueprintMCP/Handlers/Blueprint_Search.h | 7 +- .../Handlers/Blueprint_SearchContents.h | 25 ++--- .../BlueprintMCP/Handlers/Class_Search.h | 19 ++-- .../Handlers/Class_ShowProperties.h | 17 ++-- .../Handlers/Editor_ListOpenAssets.h | 9 +- .../BlueprintMCP/Handlers/Editor_OpenAsset.h | 9 +- .../BlueprintMCP/Handlers/Enum_Create.h | 7 +- .../BlueprintMCP/Handlers/GraphNode_Create.h | 13 +-- .../BlueprintMCP/Handlers/GraphNode_Delete.h | 4 +- .../Handlers/GraphNode_Duplicate.h | 11 ++- .../Handlers/GraphNode_GetComment.h | 9 +- .../Handlers/GraphNode_SearchTypes.h | 9 +- .../Handlers/GraphNode_SetComment.h | 5 +- .../Handlers/GraphNode_SetDefaults.h | 20 ++-- .../Handlers/GraphNode_SetPositions.h | 7 +- .../BlueprintMCP/Handlers/GraphPin_Connect.h | 7 +- .../Handlers/GraphPin_Disconnect.h | 9 +- .../Source/BlueprintMCP/Handlers/Graph_Dump.h | 2 +- .../Handlers/MaterialFunction_Create.h | 7 +- .../MaterialInstance_ClearParameter.h | 7 +- .../Handlers/MaterialInstance_Create.h | 15 +-- .../MaterialInstance_DumpParameters.h | 11 ++- .../Handlers/MaterialInstance_SetParameter.h | 15 +-- .../BlueprintMCP/Handlers/Material_Compile.h | 9 +- .../BlueprintMCP/Handlers/Material_Create.h | 13 +-- .../Handlers/Material_DumpParameters.h | 4 +- .../BlueprintMCP/Handlers/Property_Dump.h | 11 ++- .../BlueprintMCP/Handlers/Property_Get.h | 7 +- .../BlueprintMCP/Handlers/Property_Set.h | 11 ++- .../BlueprintMCP/Handlers/ShowCommands.h | 44 +++++---- .../Handlers/StateMachine_AddState.h | 11 ++- .../Handlers/StateMachine_AddTransition.h | 7 +- .../Handlers/StateMachine_RemoveState.h | 7 +- .../Handlers/StateMachine_SetAnimation.h | 11 ++- .../Handlers/StateMachine_SetBlendSpace.h | 19 ++-- .../Handlers/StateMachine_SetTransitionRule.h | 9 +- .../BlueprintMCP/Handlers/Struct_Create.h | 9 +- .../Source/BlueprintMCP/Private/MCPServer.cpp | 93 ++++++++++--------- .../Source/BlueprintMCP/Private/MCPUtils.cpp | 40 ++++---- .../Source/BlueprintMCP/Public/MCPHandler.h | 2 +- .../Source/BlueprintMCP/Public/MCPServer.h | 2 + .../Source/BlueprintMCP/Public/MCPUtils.h | 4 +- 76 files changed, 539 insertions(+), 467 deletions(-) diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_Create.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_Create.h index 90e5063d..58ab3c4e 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_Create.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_Create.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" @@ -37,7 +38,7 @@ public: 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); if (!Maker.Ok()) return; @@ -94,14 +95,14 @@ public: FKismetEditorUtilities::CompileBlueprint(NewAnimBP); bool bSaved = MCPUtils::SaveBlueprintPackage(NewAnimBP); - Result.Appendf(TEXT("Created: %s\n"), *AssetPath); - Result.Appendf(TEXT("ParentClass: %s\n"), *MCPUtils::FormatName(ParentClassObj)); - Result.Appendf(TEXT("Saved: %s\n"), bSaved ? TEXT("true") : TEXT("false")); + UMCPServer::Printf(TEXT("Created: %s\n"), *AssetPath); + UMCPServer::Printf(TEXT("ParentClass: %s\n"), *MCPUtils::FormatName(ParentClassObj)); + UMCPServer::Printf(TEXT("Saved: %s\n"), bSaved ? TEXT("true") : TEXT("false")); TArray Graphs = MCPUtils::AllGraphs(NewAnimBP); for (UEdGraph* Graph : Graphs) { - Result.Appendf(TEXT("Graph: %s\n"), *MCPUtils::FormatName(Graph)); + UMCPServer::Printf(TEXT("Graph: %s\n"), *MCPUtils::FormatName(Graph)); } } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_ListSlotNames.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_ListSlotNames.h index 0f2a2a23..0cd8fc91 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_ListSlotNames.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_ListSlotNames.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" @@ -27,7 +28,7 @@ public: return TEXT("List all animation slot names used in an Animation Blueprint."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPAssets Assets; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; @@ -52,12 +53,12 @@ public: for (const FString& Slot : SlotNames) { - Result.Appendf(TEXT("%s\n"), *Slot); + UMCPServer::Printf(TEXT("%s\n"), *Slot); } if (SlotNames.Num() == 0) { - Result.Append(TEXT("No animation slot names found.\n")); + UMCPServer::Print(TEXT("No animation slot names found.\n")); } } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_ListSyncGroups.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_ListSyncGroups.h index f48b33ab..52757cf4 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_ListSyncGroups.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_ListSyncGroups.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -27,7 +28,7 @@ public: return TEXT("List all sync group names used in an Animation Blueprint."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UAnimBlueprint* AnimBP = F.Walk(Path).Cast(); @@ -52,13 +53,13 @@ public: if (SyncGroupNames.Num() == 0) { - Result.Append(TEXT("No sync groups found.\n")); + UMCPServer::Print(TEXT("No sync groups found.\n")); return; } for (const FString& Group : SyncGroupNames) { - Result.Appendf(TEXT("%s\n"), *Group); + UMCPServer::Printf(TEXT("%s\n"), *Group); } } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_SetBlendSpaceSamples.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_SetBlendSpaceSamples.h index ce814d23..57724411 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_SetBlendSpaceSamples.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/AnimBlueprint_SetBlendSpaceSamples.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPFetcher.h" @@ -65,7 +66,7 @@ public: "Replaces all existing samples."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { // Load the blend space MCPAssets Assets; @@ -128,10 +129,10 @@ public: // Save 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) { - Result.Append(TEXT("WARNING: package save failed\n")); + UMCPServer::Print(TEXT("WARNING: package save failed\n")); } } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Backup.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Backup.h index 845d1543..417f320e 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Backup.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Backup.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPUtils.h" #include "Misc/Paths.h" @@ -27,14 +28,14 @@ public: 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( FPackageName::LongPackageNameToFilename(AssetPath, FPackageName::GetAssetPackageExtension())); 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; } @@ -42,10 +43,10 @@ public: uint32 CopyResult = IFileManager::Get().Copy(*BackupFilename, *Filename, true); 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; } - Result.Appendf(TEXT("Backed up to %s\n"), *BackupFilename); + UMCPServer::Printf(TEXT("Backed up to %s\n"), *BackupFilename); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Delete.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Delete.h index e88d2c19..656c6f1c 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Delete.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Delete.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" @@ -34,7 +35,7 @@ public: "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 FString PackageFilename = FPackageName::LongPackageNameToFilename( @@ -43,7 +44,7 @@ public: 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; } @@ -59,22 +60,22 @@ public: 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) { FString RefStr = Ref.ToString(); 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)")); } - 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; } // Force delete: unload the package from memory first 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) @@ -110,7 +111,7 @@ public: 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; } @@ -123,6 +124,6 @@ public: Registry.ScanPathsSynchronous({PackageDir}, true); } - Result.Appendf(TEXT("Deleted %s\n"), *AssetPath); + UMCPServer::Printf(TEXT("Deleted %s\n"), *AssetPath); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_FindReferences.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_FindReferences.h index b489c473..f786889f 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_FindReferences.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_FindReferences.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPUtils.h" #include "AssetRegistry/AssetData.h" @@ -26,7 +27,7 @@ public: return TEXT("Find all assets that reference a given asset."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { IAssetRegistry& Registry = *IAssetRegistry::Get(); @@ -34,7 +35,7 @@ public: FAssetData AssetData = Registry.GetAssetByObjectPath(FSoftObjectPath(AssetPath)); if (!AssetData.IsValid()) { - Result.Appendf(TEXT("ERROR: Asset not found: %s\n"), *AssetPath); + UMCPServer::Printf(TEXT("ERROR: Asset not found: %s\n"), *AssetPath); return; } @@ -43,7 +44,7 @@ public: if (Referencers.Num() == 0) { - Result.Append(TEXT("No referencers found.\n")); + UMCPServer::Print(TEXT("No referencers found.\n")); return; } @@ -55,13 +56,13 @@ public: Registry.GetAssetsByPackageName(Ref, RefAssets); if (RefAssets.Num() > 0) { - Result.Appendf(TEXT("%s %s\n"), + UMCPServer::Printf(TEXT("%s %s\n"), *MCPUtils::FormatName(RefAssets[0].GetClass()), *RefStr); } else { - Result.Appendf(TEXT("Unknown %s\n"), *RefStr); + UMCPServer::Printf(TEXT("Unknown %s\n"), *RefStr); } } } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Rename.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Rename.h index 0b2df522..97a00c14 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Rename.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Rename.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" @@ -30,7 +31,7 @@ public: return TEXT("Rename or move an asset with reference fixup."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { // Load the asset MCPAssets Assets; @@ -47,7 +48,7 @@ public: NewAssetName = NewPath; 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; } } @@ -61,10 +62,10 @@ public: 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; } - Result.Appendf(TEXT("Renamed to %s/%s\n"), *NewPackagePath, *NewAssetName); + UMCPServer::Printf(TEXT("Renamed to %s/%s\n"), *NewPackagePath, *NewAssetName); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Restore.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Restore.h index c92b4147..5f9847e4 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Restore.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Restore.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPUtils.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."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { FString Filename = FPaths::ConvertRelativePathToFull( FPackageName::LongPackageNameToFilename(AssetPath, FPackageName::GetAssetPackageExtension())); @@ -36,7 +37,7 @@ public: 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; } @@ -51,7 +52,7 @@ public: uint32 CopyResult = IFileManager::Get().Copy(*Filename, *BackupFilename, true); 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; } @@ -63,12 +64,12 @@ public: UEditorLoadingAndSavingUtils::ReloadPackages({Package}, bReloaded, ErrorMessage, EReloadPackagesInteractionMode::AssumePositive); 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()); return; } } - Result.Appendf(TEXT("Restored %s from backup\n"), *AssetPath); + UMCPServer::Printf(TEXT("Restored %s from backup\n"), *AssetPath); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Search.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Search.h index 85834a95..1555dbf6 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Search.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Asset_Search.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.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."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { 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; } @@ -47,7 +48,7 @@ public: UClass* TypeClass = MCPUtils::FindClassByName(Type); if (!TypeClass) { - Result.Appendf(TEXT("ERROR: Unknown asset type '%s'\n"), *Type); + UMCPServer::Printf(TEXT("ERROR: Unknown asset type '%s'\n"), *Type); return; } Assets.NoScans().Scan(TypeClass); @@ -63,18 +64,18 @@ public: const TArray& AllData = Assets.AllData(); for (const FAssetData& Data : AllData) { - Result.Appendf(TEXT("%s %s\n"), + UMCPServer::Printf(TEXT("%s %s\n"), *MCPUtils::FormatName(Data.GetClass()), *Data.PackageName.ToString()); } if (AllData.Num() == 0) { - Result.Append(TEXT("No assets found.\n")); + UMCPServer::Print(TEXT("No assets found.\n")); } 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); } } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlendSpace_Create.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlendSpace_Create.h index b2129573..6d7759aa 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlendSpace_Create.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlendSpace_Create.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" @@ -31,7 +32,7 @@ public: 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); if (!Maker.Ok()) return; @@ -46,7 +47,7 @@ public: UBlendSpace* NewBS = NewObject(Maker.Package(), FName(*Maker.Name()), RF_Public | RF_Standalone); 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; } @@ -56,9 +57,9 @@ public: NewBS->MarkPackageDirty(); bool bSaved = MCPUtils::SaveGenericPackage(NewBS); - Result.Appendf(TEXT("Created %s\n"), *NewBS->GetPathName()); - Result.Appendf(TEXT("Skeleton: %s\n"), *SkeletonObj->GetPathName()); + UMCPServer::Printf(TEXT("Created %s\n"), *NewBS->GetPathName()); + UMCPServer::Printf(TEXT("Skeleton: %s\n"), *SkeletonObj->GetPathName()); if (!bSaved) - Result.Append(TEXT("WARNING: Package save failed\n")); + UMCPServer::Print(TEXT("WARNING: Package save failed\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Create.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Create.h index 2e69b9f2..01357cfe 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Create.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Create.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -37,11 +38,11 @@ public: 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")) { - 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; } @@ -52,7 +53,7 @@ public: // Check graph name uniqueness 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; } @@ -63,7 +64,7 @@ public: { 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; } } @@ -75,11 +76,11 @@ public: UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); if (!NewGraph) { - Result.Append(TEXT("ERROR: Failed to create function graph\n")); + UMCPServer::Print(TEXT("ERROR: Failed to create function graph\n")); return; } FBlueprintEditorUtils::AddFunctionGraph(BP, NewGraph, /*bIsUserCreated=*/true, /*SignatureFromObject=*/static_cast(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")) { @@ -87,11 +88,11 @@ public: UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); if (!NewGraph) { - Result.Append(TEXT("ERROR: Failed to create macro graph\n")); + UMCPServer::Print(TEXT("ERROR: Failed to create macro graph\n")); return; } 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 { @@ -100,7 +101,7 @@ public: EventGraph = BP->UbergraphPages[0]; 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; } @@ -111,7 +112,7 @@ public: NewEvent->CreateNewGuid(); NewEvent->PostPlacedNewNode(); 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); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Delete.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Delete.h index deafe890..bde6a147 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Delete.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Delete.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -31,7 +32,7 @@ public: 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; F.Walk(Path); @@ -73,12 +74,12 @@ public: { 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)); 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)); return; } @@ -88,8 +89,8 @@ public: FBlueprintEditorUtils::RemoveGraph(BP, TargetGraph, EGraphRemoveFlags::Default); 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) - Result.Append(TEXT("WARNING: Package save failed.\n")); + UMCPServer::Print(TEXT("WARNING: Package save failed.\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Rename.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Rename.h index f2e7a5a1..97e29be8 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Rename.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintGraph_Rename.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -31,7 +32,7 @@ public: 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; UEdGraph* TargetGraph = F.Walk(Graph).Cast(); @@ -40,14 +41,14 @@ public: UBlueprint* BP = Cast(TargetGraph->GetOuter()); 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; } // Check if it's an UbergraphPage -- disallow rename 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)); return; } @@ -57,7 +58,7 @@ public: bool bIsMacro = BP->MacroGraphs.Contains(TargetGraph); 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)); return; } @@ -67,7 +68,7 @@ public: { 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)); return; } @@ -76,7 +77,7 @@ public: FBlueprintEditorUtils::RenameGraph(TargetGraph, NewName); MCPUtils::SaveBlueprintPackage(BP); - Result.Appendf(TEXT("Renamed to %s %s\n"), + UMCPServer::Printf(TEXT("Renamed to %s %s\n"), bIsFunction ? TEXT("function") : TEXT("macro"), *MCPUtils::FormatName(TargetGraph)); } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddComponent.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddComponent.h index 7998f092..1ae4f21f 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddComponent.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddComponent.h @@ -40,7 +40,7 @@ public: "Optionally attach it to an existing parent component."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPAssets Assets; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; @@ -123,13 +123,13 @@ public: 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(ComponentClassObj)); 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")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddEventDispatcher.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddEventDispatcher.h index 2be41692..f4ef30b8 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddEventDispatcher.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddEventDispatcher.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -49,7 +50,7 @@ public: 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; UBlueprint* BP = F.Walk(Path).Cast(); @@ -62,7 +63,7 @@ public: { 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; } } @@ -70,7 +71,7 @@ public: // Check against existing graphs (functions, macros, etc.) 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; } @@ -79,7 +80,7 @@ public: DelegateType.PinCategory = UEdGraphSchema_K2::PC_MCDelegate; 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; } @@ -90,7 +91,7 @@ public: UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); 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; } @@ -116,7 +117,7 @@ public: if (!EntryNode) { 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; } @@ -137,9 +138,9 @@ public: MCPUtils::SaveBlueprintPackage(BP); - Result.Appendf(TEXT("Created event dispatcher '%s'"), *DispatcherName); + UMCPServer::Printf(TEXT("Created event dispatcher '%s'"), *DispatcherName); if (ParamCount > 0) - Result.Appendf(TEXT(" with %d parameter(s)"), ParamCount); - Result.Append(TEXT(".\n")); + UMCPServer::Printf(TEXT(" with %d parameter(s)"), ParamCount); + UMCPServer::Print(TEXT(".\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddFunctionParameter.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddFunctionParameter.h index 18903c4d..a7dd4521 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddFunctionParameter.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddFunctionParameter.h @@ -42,7 +42,7 @@ public: 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 Assets; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; @@ -135,7 +135,7 @@ public: EntryNode->CreateUserDefinedPin(FName(*ParamName), PinType, EGPD_Output); 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, bSaved ? TEXT("") : TEXT(" (WARNING: save failed)")); } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddInterface.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddInterface.h index 6e41cb6b..28405196 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddInterface.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddInterface.h @@ -33,14 +33,14 @@ public: "Creates stub function graphs for each interface function."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPAssets Assets; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; UBlueprint* BP = Assets.Object(); // Resolve the interface class - UClass* InterfaceClass = FindInterfaceClass(InterfaceName, Result); + UClass* InterfaceClass = FindInterfaceClass(InterfaceName); if (!InterfaceClass) return; // Check for duplicates @@ -64,15 +64,15 @@ public: } // Collect stub function graph names from the newly added interface entry - Result.Appendf(TEXT("Added interface %s\n"), *MCPUtils::FormatName(InterfaceClass)); - Result.Appendf(TEXT("Function stubs:\n")); + UMCPServer::Printf(TEXT("Added interface %s\n"), *MCPUtils::FormatName(InterfaceClass)); + UMCPServer::Printf(TEXT("Function stubs:\n")); for (const FBPInterfaceDescription& IfaceDesc : BP->ImplementedInterfaces) { if (IfaceDesc.Interface != InterfaceClass) continue; for (const UEdGraph* Graph : IfaceDesc.Graphs) { if (Graph) - Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Graph)); + UMCPServer::Printf(TEXT(" %s\n"), *MCPUtils::FormatName(Graph)); } break; } @@ -82,7 +82,7 @@ private: // Resolve an interface name to a UClass. Tries loaded UInterface classes // first (for native interfaces), then falls back to loading a Blueprint // Interface asset. - static UClass* FindInterfaceClass(const FString& Name, FStringBuilderBase& Result) + static UClass* FindInterfaceClass(const FString& Name) { // Strategy 1: Search loaded UInterface classes by name for (TObjectIterator It; It; ++It) diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddVariable.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddVariable.h index 85c5a79e..a6c1d6e8 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddVariable.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddVariable.h @@ -43,7 +43,7 @@ public: return TEXT("Add a new member variable to a Blueprint."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPAssets Assets; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; @@ -80,13 +80,13 @@ public: 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)); if (IsArray) - Result.Append(TEXT("Container: Array\n")); + UMCPServer::Print(TEXT("Container: Array\n")); if (!Category.IsEmpty()) - Result.Appendf(TEXT("Category: %s\n"), *Category); + UMCPServer::Printf(TEXT("Category: %s\n"), *Category); if (!bSaved) - Result.Append(TEXT("Warning: package save failed\n")); + UMCPServer::Print(TEXT("Warning: package save failed\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ChangeFunctionParameterType.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ChangeFunctionParameterType.h index 196a23d5..637952fb 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ChangeFunctionParameterType.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ChangeFunctionParameterType.h @@ -45,7 +45,7 @@ public: 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 Assets; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; @@ -123,14 +123,14 @@ public: for (UEdGraphPin* Linked : Pin->LinkedTo) { 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(Linked), *MCPUtils::FormatName(Linked->GetOwningNode())); 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; } @@ -152,7 +152,7 @@ public: // Save 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), bSaved ? TEXT("succeeded") : TEXT("failed")); } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ChangeVariableType.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ChangeVariableType.h index a98788c3..94d1a9a2 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ChangeVariableType.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ChangeVariableType.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPAssets.h" @@ -43,7 +44,7 @@ public: "Supports dry-run mode to preview affected nodes before committing."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UBlueprint* BP = F.Walk(Blueprint).Cast(); @@ -62,10 +63,10 @@ public: } 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)); for (const FBPVariableDescription& Var : BP->NewVariables) - Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Var)); + UMCPServer::Printf(TEXT(" %s\n"), *MCPUtils::FormatName(Var)); return; } @@ -91,14 +92,14 @@ public: for (auto* VarNode : NodeArray) { 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(VarNode)), *MCPUtils::FormatName(VarNode->GetGraph())); for (UEdGraphPin* Pin : VarNode->Pins) { if (!Pin || Pin->LinkedTo.Num() == 0) continue; 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()); } } @@ -114,13 +115,13 @@ public: 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::FormatPinType(Found->VarType), *MCPUtils::FormatPinType(NewPinType)); if (bHasAffected) { - Result.Append(TEXT("Affected nodes:\n")); + UMCPServer::Print(TEXT("Affected nodes:\n")); AppendAffectedNodes(GetNodes, TEXT("Get")); AppendAffectedNodes(SetNodes, TEXT("Set")); } @@ -131,14 +132,14 @@ public: Found->VarType = NewPinType; 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::FormatPinType(NewPinType), bSaved ? TEXT("") : TEXT(" WARNING: save failed.")); if (bHasAffected) { - Result.Append(TEXT("Affected nodes:\n")); + UMCPServer::Print(TEXT("Affected nodes:\n")); AppendAffectedNodes(GetNodes, TEXT("Get")); AppendAffectedNodes(SetNodes, TEXT("Set")); } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Compile.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Compile.h index 43420ef4..b67ff73d 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Compile.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Compile.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" @@ -43,7 +44,7 @@ public: // Compile a single blueprint and append results to Out. // Returns true if the blueprint compiled cleanly (no errors). - static bool CompileAndReport(UBlueprint* BP, FStringBuilderBase& Out) + static bool CompileAndReport(UBlueprint* BP) { EBlueprintCompileOptions CompileOpts = EBlueprintCompileOptions::SkipSave | @@ -61,7 +62,7 @@ public: if (!Node->bHasCompilerMessage) continue; bool bIsError = (Node->ErrorType == EMessageSeverity::Error); 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"), *MCPUtils::FormatName(Node->GetGraph()), *MCPUtils::FormatName(Node), @@ -74,18 +75,18 @@ public: if (bIsValid && WarningCount == 0) { - Out.Appendf(TEXT(" OK (status: %s)\n"), *StatusStr); + UMCPServer::Printf(TEXT(" OK (status: %s)\n"), *StatusStr); } else { - Out.Appendf(TEXT(" status: %s, errors: %d, warnings: %d\n"), + UMCPServer::Printf(TEXT(" status: %s, errors: %d, warnings: %d\n"), *StatusStr, ErrorCount, WarningCount); } return bIsValid; } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPAssets Finder; Finder.Scan().Scan(); @@ -104,7 +105,7 @@ public: // countOnly: return count without compiling anything if (CountOnly) { - Result.Appendf(TEXT("Matching blueprints: %d\n"), TotalMatching); + UMCPServer::Printf(TEXT("Matching blueprints: %d\n"), TotalMatching); return; } @@ -131,12 +132,12 @@ public: UBlueprint* BP = Loader.Object(); TotalChecked++; - Result.Appendf(TEXT("%s:\n"), *MCPUtils::FormatName(BP)); - bool bValid = CompileAndReport(BP, Result); + UMCPServer::Printf(TEXT("%s:\n"), *MCPUtils::FormatName(BP)); + bool bValid = CompileAndReport(BP); 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); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Create.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Create.h index 6725d461..10106d0c 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Create.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Create.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" @@ -34,7 +35,7 @@ public: 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); if (!Maker.Ok()) return; @@ -93,11 +94,11 @@ public: bool bSaved = MCPUtils::SaveBlueprintPackage(NewBP); // Report result - Result.Appendf(TEXT("Created: %s\n"), *MCPUtils::FormatName(NewBP)); - Result.Appendf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentClassObj)); + UMCPServer::Printf(TEXT("Created: %s\n"), *MCPUtils::FormatName(NewBP)); + UMCPServer::Printf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentClassObj)); if (!bSaved) - Result.Append(TEXT("Warning: save failed\n")); + UMCPServer::Print(TEXT("Warning: save failed\n")); for (UEdGraph* Graph : MCPUtils::AllGraphs(NewBP)) - Result.Appendf(TEXT("Graph: %s\n"), *MCPUtils::FormatName(Graph)); + UMCPServer::Printf(TEXT("Graph: %s\n"), *MCPUtils::FormatName(Graph)); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Diff.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Diff.h index 287e707c..8ac8a448 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Diff.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Diff.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" @@ -37,7 +38,7 @@ public: "finding divergence after copy-paste, or auditing consistency."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { // Load both blueprints MCPAssets AssetsA; @@ -89,13 +90,13 @@ public: 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++; continue; } 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++; continue; } @@ -181,32 +182,32 @@ public: 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; } 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()) { - Result.Append(TEXT(" Nodes only in A:\n")); - for (const FString& Line : OnlyInA) Result.Appendf(TEXT(" %s\n"), *Line); + UMCPServer::Print(TEXT(" Nodes only in A:\n")); + for (const FString& Line : OnlyInA) UMCPServer::Printf(TEXT(" %s\n"), *Line); } if (!OnlyInB.IsEmpty()) { - Result.Append(TEXT(" Nodes only in B:\n")); - for (const FString& Line : OnlyInB) Result.Appendf(TEXT(" %s\n"), *Line); + UMCPServer::Print(TEXT(" Nodes only in B:\n")); + for (const FString& Line : OnlyInB) UMCPServer::Printf(TEXT(" %s\n"), *Line); } if (!ConnsOnlyInA.IsEmpty()) { - Result.Append(TEXT(" Connections only in A:\n")); - for (const FString& Line : ConnsOnlyInA) Result.Appendf(TEXT(" %s\n"), *Line); + UMCPServer::Print(TEXT(" Connections only in A:\n")); + for (const FString& Line : ConnsOnlyInA) UMCPServer::Printf(TEXT(" %s\n"), *Line); } if (!ConnsOnlyInB.IsEmpty()) { - Result.Append(TEXT(" Connections only in B:\n")); - for (const FString& Line : ConnsOnlyInB) Result.Appendf(TEXT(" %s\n"), *Line); + UMCPServer::Print(TEXT(" Connections only in B:\n")); + for (const FString& Line : ConnsOnlyInB) UMCPServer::Printf(TEXT(" %s\n"), *Line); } } @@ -225,17 +226,17 @@ public: if (!VarsOnlyInA.IsEmpty()) { - Result.Append(TEXT("Variables only in A:\n")); - for (const FString& Name : VarsOnlyInA) Result.Appendf(TEXT(" %s\n"), *Name); + UMCPServer::Print(TEXT("Variables only in A:\n")); + for (const FString& Name : VarsOnlyInA) UMCPServer::Printf(TEXT(" %s\n"), *Name); TotalDiffs += VarsOnlyInA.Num(); } if (!VarsOnlyInB.IsEmpty()) { - Result.Append(TEXT("Variables only in B:\n")); - for (const FString& Name : VarsOnlyInB) Result.Appendf(TEXT(" %s\n"), *Name); + UMCPServer::Print(TEXT("Variables only in B:\n")); + for (const FString& Name : VarsOnlyInB) UMCPServer::Printf(TEXT(" %s\n"), *Name); TotalDiffs += VarsOnlyInB.Num(); } - Result.Appendf(TEXT("Total differences: %d\n"), TotalDiffs); + UMCPServer::Printf(TEXT("Total differences: %d\n"), TotalDiffs); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Dump.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Dump.h index e35d6d65..0d420429 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Dump.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Dump.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -31,46 +32,46 @@ public: "and graph names. Does not include graph contents (use DumpGraphs for that)."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UBlueprint* BP = F.Walk(Blueprint).Cast(); if (!BP) return; // Header - Result.Appendf(TEXT("Blueprint: %s\n"), *MCPUtils::FormatName(BP)); - Result.Appendf(TEXT("Parent: %s\n"), BP->ParentClass ? *MCPUtils::FormatName(BP->ParentClass) : TEXT("None")); - Result.Appendf(TEXT("Type: %s\n"), + UMCPServer::Printf(TEXT("Blueprint: %s\n"), *MCPUtils::FormatName(BP)); + UMCPServer::Printf(TEXT("Parent: %s\n"), BP->ParentClass ? *MCPUtils::FormatName(BP->ParentClass) : TEXT("None")); + UMCPServer::Printf(TEXT("Type: %s\n"), *MCPUtils::EnumToString(BP->BlueprintType)); // Animation Blueprint if (UAnimBlueprint* AnimBP = Cast(BP)) { if (AnimBP->TargetSkeleton) - Result.Appendf(TEXT("TargetSkeleton: %s\n"), *AnimBP->TargetSkeleton->GetPathName()); + UMCPServer::Printf(TEXT("TargetSkeleton: %s\n"), *AnimBP->TargetSkeleton->GetPathName()); } // Interfaces for (const FBPInterfaceDescription& I : BP->ImplementedInterfaces) { if (I.Interface) - Result.Appendf(TEXT("Interface: %s\n"), *MCPUtils::FormatName(I.Interface)); + UMCPServer::Printf(TEXT("Interface: %s\n"), *MCPUtils::FormatName(I.Interface)); } // Variables if (!BP->NewVariables.IsEmpty()) { - Result.Append(TEXT("\nVariables:\n")); + UMCPServer::Print(TEXT("\nVariables:\n")); for (const FBPVariableDescription& V : BP->NewVariables) { - Result.Appendf(TEXT(" %s %s"), + UMCPServer::Printf(TEXT(" %s %s"), *MCPUtils::FormatPinType(V.VarType), *MCPUtils::FormatName(V)); 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")) - Result.Appendf(TEXT(" [%s]"), *V.Category.ToString()); - Result.Append(TEXT("\n")); + UMCPServer::Printf(TEXT(" [%s]"), *V.Category.ToString()); + UMCPServer::Print(TEXT("\n")); } } @@ -80,16 +81,16 @@ public: const TArray& AllNodes = SCS->GetAllNodes(); if (!AllNodes.IsEmpty()) { - Result.Append(TEXT("\nComponents:\n")); + UMCPServer::Print(TEXT("\nComponents:\n")); for (USCS_Node* Node : AllNodes) { if (!Node || !Node->ComponentTemplate) continue; - Result.Appendf(TEXT(" %s (%s)"), + UMCPServer::Printf(TEXT(" %s (%s)"), *MCPUtils::FormatName(Node->ComponentTemplate), *MCPUtils::FormatName(Node->ComponentClass)); if (Node->ParentComponentOrVariableName != NAME_None) - Result.Appendf(TEXT(" parent=%s"), *Node->ParentComponentOrVariableName.ToString()); - Result.Append(TEXT("\n")); + UMCPServer::Printf(TEXT(" parent=%s"), *Node->ParentComponentOrVariableName.ToString()); + UMCPServer::Print(TEXT("\n")); } } } @@ -98,9 +99,9 @@ public: TArray Graphs = MCPUtils::AllGraphs(BP); if (!Graphs.IsEmpty()) { - Result.Append(TEXT("\nGraphs:\n")); + UMCPServer::Print(TEXT("\nGraphs:\n")); for (UEdGraph* Graph : Graphs) - Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Graph)); + UMCPServer::Printf(TEXT(" %s\n"), *MCPUtils::FormatName(Graph)); } } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListComponents.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListComponents.h index 50faab5e..bd63e76b 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListComponents.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListComponents.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -29,7 +30,7 @@ public: "showing hierarchy and component classes."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; F.Walk(Path); @@ -41,7 +42,7 @@ public: USimpleConstructionScript* SCS = BP->SimpleConstructionScript; 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; } @@ -50,11 +51,11 @@ public: if (AllNodes.Num() == 0) { - Result.Append(TEXT("No components.\n")); + UMCPServer::Print(TEXT("No components.\n")); 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 " "(available via the CDO's OwnedComponents) or from parent blueprint SCS nodes.\n")); @@ -62,34 +63,34 @@ public: for (USCS_Node* Root : RootNodes) { if (!Root) continue; - EmitNode(Root, 0, Root == RootNodes[0], Result); + EmitNode(Root, 0, Root == RootNodes[0]); } } 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 for (int32 i = 0; i < Depth; i++) - Result.Append(TEXT(" ")); + UMCPServer::Print(TEXT(" ")); FString ClassName = Node->ComponentClass ? MCPUtils::FormatName(Node->ComponentClass) : TEXT("None"); - Result.Appendf(TEXT("%s %s"), + UMCPServer::Printf(TEXT("%s %s"), *ClassName, *MCPUtils::FormatName(Node->ComponentTemplate)); 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()) { if (!Child) continue; - EmitNode(Child, Depth + 1, false, Result); + EmitNode(Child, Depth + 1, false); } } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListEventDispatchers.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListEventDispatchers.h index d5c0a404..ef54aad5 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListEventDispatchers.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListEventDispatchers.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -31,7 +32,7 @@ public: 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; UBlueprint* BP = F.Walk(Path).Cast(); @@ -42,7 +43,7 @@ public: for (const FName& DelegateName : DelegateNameSet) { - Result.Appendf(TEXT("%s("), *DelegateName.ToString()); + UMCPServer::Printf(TEXT("%s("), *DelegateName.ToString()); UEdGraph* SigGraph = FBlueprintEditorUtils::GetDelegateSignatureGraphByName(BP, DelegateName); if (SigGraph) @@ -53,9 +54,9 @@ public: for (const TSharedPtr& PinInfo : FE->UserDefinedPins) { if (!PinInfo.IsValid()) continue; - if (!bFirst) Result.Append(TEXT(", ")); + if (!bFirst) UMCPServer::Print(TEXT(", ")); bFirst = false; - Result.Appendf(TEXT("%s %s"), + UMCPServer::Printf(TEXT("%s %s"), *MCPUtils::FormatPinType(PinInfo->PinType), *PinInfo->PinName.ToString()); } @@ -63,12 +64,12 @@ public: } } - Result.Append(TEXT(")\n")); + UMCPServer::Print(TEXT(")\n")); } if (DelegateNameSet.Num() == 0) { - Result.Append(TEXT("No event dispatchers found.\n")); + UMCPServer::Print(TEXT("No event dispatchers found.\n")); } } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListInterfaces.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListInterfaces.h index ef6c1527..c2b356ca 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListInterfaces.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_ListInterfaces.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -27,7 +28,7 @@ public: "including their function graphs."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; F.Walk(Path); @@ -41,17 +42,17 @@ public: if (!IfaceDesc.Interface) continue; 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) { if (!Graph) continue; - Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Graph)); + UMCPServer::Printf(TEXT(" %s\n"), *MCPUtils::FormatName(Graph)); } } if (!bAny) { - Result.Append(TEXT("No interfaces implemented.\n")); + UMCPServer::Print(TEXT("No interfaces implemented.\n")); } } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RefreshAllNodes.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RefreshAllNodes.h index a0a96e26..3e67c265 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RefreshAllNodes.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RefreshAllNodes.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" @@ -31,7 +32,7 @@ public: "Reports compiler warnings and errors."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { // Load Blueprint MCPAssets Assets; @@ -61,24 +62,24 @@ public: } // 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) { - 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 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)) { if (!Node->bHasCompilerMessage) continue; 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()), *MCPUtils::FormatName(Node), *Node->ErrorMsg); } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveComponent.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveComponent.h index df9df8d4..e91cbd76 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveComponent.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveComponent.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -31,7 +32,7 @@ public: return TEXT("Remove a component from a Blueprint's SimpleConstructionScript."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UBlueprint* BP = F.Walk(Blueprint).Cast(); @@ -40,7 +41,7 @@ public: USimpleConstructionScript* SCS = BP->SimpleConstructionScript; 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; } @@ -59,12 +60,12 @@ public: 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); for (USCS_Node* Node : AllNodes) { if (Node && Node->ComponentTemplate) - Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Node->ComponentTemplate)); + UMCPServer::Printf(TEXT(" %s\n"), *MCPUtils::FormatName(Node->ComponentTemplate)); } return; } @@ -73,7 +74,7 @@ public: const TArray& RootNodes = SCS->GetRootNodes(); 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"), *MCPUtils::FormatName(NodeToRemove->ComponentTemplate), NodeToRemove->GetChildNodes().Num()); @@ -87,7 +88,7 @@ public: bool bSaved = MCPUtils::SaveBlueprintPackage(BP); - Result.Appendf(TEXT("Removed component %s.%s\n"), + UMCPServer::Printf(TEXT("Removed component %s.%s\n"), *RemovedName, bSaved ? TEXT("") : TEXT(" WARNING: save failed.")); } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveFunctionParameter.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveFunctionParameter.h index 4d6e4c79..62a171eb 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveFunctionParameter.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveFunctionParameter.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPAssets.h" @@ -36,7 +37,7 @@ public: 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 Assets; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; @@ -68,11 +69,11 @@ public: 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(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(BP)) - Result.Appendf(TEXT(" event: %s\n"), *MCPUtils::FormatName(CE)); + UMCPServer::Printf(TEXT(" event: %s\n"), *MCPUtils::FormatName(CE)); return; } @@ -90,11 +91,11 @@ public: 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)); for (const TSharedPtr& PinInfo : EntryNode->UserDefinedPins) if (PinInfo.IsValid()) - Result.Appendf(TEXT(" %s\n"), *PinInfo->PinName.ToString()); + UMCPServer::Printf(TEXT(" %s\n"), *PinInfo->PinName.ToString()); return; } @@ -108,8 +109,8 @@ public: 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) - Result.Append(TEXT("Warning: save failed.\n")); + UMCPServer::Print(TEXT("Warning: save failed.\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveInterface.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveInterface.h index ec686226..5fcef439 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveInterface.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveInterface.h @@ -35,7 +35,7 @@ public: "Optionally preserve the function graphs as regular functions."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPAssets Assets; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; @@ -67,8 +67,8 @@ public: FTopLevelAssetPath InterfacePath = FoundInterface->GetClassPathName(); 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) - Result.Append(TEXT("Function graphs preserved as regular functions.\n")); + UMCPServer::Print(TEXT("Function graphs preserved as regular functions.\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveVariable.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveVariable.h index 10166fdd..0d7df465 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveVariable.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_RemoveVariable.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -30,7 +31,7 @@ public: return TEXT("Remove a member variable from a Blueprint."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UBlueprint* BP = F.Walk(Blueprint).Cast(); @@ -50,11 +51,11 @@ public: 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)); for (const FBPVariableDescription& Var : BP->NewVariables) { - Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Var)); + UMCPServer::Printf(TEXT(" %s\n"), *MCPUtils::FormatName(Var)); } return; } @@ -65,7 +66,7 @@ public: FBlueprintEditorUtils::RemoveMemberVariable(BP, VarFName); 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), bSaved ? TEXT("") : TEXT(" WARNING: save failed.")); } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Reparent.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Reparent.h index 880100b5..5468f6a3 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Reparent.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Reparent.h @@ -33,7 +33,7 @@ public: 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 MCPAssets Assets; @@ -66,9 +66,9 @@ public: FKismetEditorUtilities::CompileBlueprint(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)); if (!bSaved) - Result.Append(TEXT("Warning: save failed\n")); + UMCPServer::Print(TEXT("Warning: save failed\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Search.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Search.h index dc0fe44d..ba11fe15 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Search.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_Search.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.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."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPAssets Assets; 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++; } if (Count == 0) { - Result.Append(TEXT("No blueprint assets found.\n")); + UMCPServer::Print(TEXT("No blueprint assets found.\n")); } } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_SearchContents.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_SearchContents.h index 137f8d28..75533945 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_SearchContents.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_SearchContents.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" @@ -41,7 +42,7 @@ public: 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 Count = 0; @@ -85,15 +86,15 @@ public: if (!bMatch) continue; Count++; - Result.Appendf(TEXT("blueprint: %s\n"), *MCPUtils::FormatName(BP)); - Result.Appendf(TEXT(" graph: %s\n"), *MCPUtils::FormatName(Node->GetGraph())); - Result.Appendf(TEXT(" node: %s\n"), *MCPUtils::FormatName(Node)); - Result.Appendf(TEXT(" class: %s\n"), *MCPUtils::FormatName(Node->GetClass())); - if (!FuncName.IsEmpty()) Result.Appendf(TEXT(" function: %s\n"), *FuncName); - if (!EventName.IsEmpty()) Result.Appendf(TEXT(" event: %s\n"), *EventName); - if (!VarName.IsEmpty()) Result.Appendf(TEXT(" variable: %s\n"), *VarName); - if (bIsLevelBP) Result.Append(TEXT(" level-blueprint: true\n")); - Result.Append(TEXT("\n")); + UMCPServer::Printf(TEXT("blueprint: %s\n"), *MCPUtils::FormatName(BP)); + UMCPServer::Printf(TEXT(" graph: %s\n"), *MCPUtils::FormatName(Node->GetGraph())); + UMCPServer::Printf(TEXT(" node: %s\n"), *MCPUtils::FormatName(Node)); + UMCPServer::Printf(TEXT(" class: %s\n"), *MCPUtils::FormatName(Node->GetClass())); + if (!FuncName.IsEmpty()) UMCPServer::Printf(TEXT(" function: %s\n"), *FuncName); + if (!EventName.IsEmpty()) UMCPServer::Printf(TEXT(" event: %s\n"), *EventName); + if (!VarName.IsEmpty()) UMCPServer::Printf(TEXT(" variable: %s\n"), *VarName); + if (bIsLevelBP) UMCPServer::Print(TEXT(" level-blueprint: true\n")); + UMCPServer::Print(TEXT("\n")); } }; @@ -120,7 +121,7 @@ public: SearchBlueprint(LevelBP, true); } - Result.Appendf(TEXT("Results: %d\n"), Count); - if (Count >= Limit) Result.Appendf(TEXT("(limit %d reached, use MaxResults to increase)\n"), Limit); + UMCPServer::Printf(TEXT("Results: %d\n"), Count); + if (Count >= Limit) UMCPServer::Printf(TEXT("(limit %d reached, use MaxResults to increase)\n"), Limit); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Class_Search.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Class_Search.h index 2a871402..8735a799 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Class_Search.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Class_Search.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPUtils.h" #include "UObject/UObjectIterator.h" @@ -36,7 +37,7 @@ public: "Returns class names, parent class, package, and flags."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { Limit = FMath::Clamp(Limit, 1, 500); @@ -53,7 +54,7 @@ public: } 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; } } @@ -78,16 +79,16 @@ public: } } - Result.Appendf(TEXT("Found %d classes"), TotalMatched); + UMCPServer::Printf(TEXT("Found %d classes"), TotalMatched); 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) { - Result.Appendf(TEXT(" %s"), *MCPUtils::FormatName(Class)); + UMCPServer::Printf(TEXT(" %s"), *MCPUtils::FormatName(Class)); // Flags TStringBuilder<64> Flags; @@ -97,15 +98,15 @@ public: if (Class->ClassGeneratedBy) Flags.Append(TEXT(" Blueprint")); 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()) { - Result.Appendf(TEXT(" : %s"), *MCPUtils::FormatName(Class->GetSuperClass())); + UMCPServer::Printf(TEXT(" : %s"), *MCPUtils::FormatName(Class->GetSuperClass())); } - Result.Append(TEXT("\n")); + UMCPServer::Print(TEXT("\n")); } } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Class_ShowProperties.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Class_ShowProperties.h index 4c3e04c4..82464db9 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Class_ShowProperties.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Class_ShowProperties.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPUtils.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."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { UClass* FoundClass = MCPUtils::FindClassByName(ClassName); if (!FoundClass) { - Result.Appendf(TEXT("ERROR: Class '%s' not found\n"), *ClassName); + UMCPServer::Printf(TEXT("ERROR: Class '%s' not found\n"), *ClassName); return; } - Result.Appendf(TEXT("Properties of %s:\n"), *MCPUtils::FormatName(FoundClass)); + UMCPServer::Printf(TEXT("Properties of %s:\n"), *MCPUtils::FormatName(FoundClass)); int32 Count = 0; for (TFieldIterator PropIt(FoundClass); PropIt; ++PropIt) @@ -61,18 +62,18 @@ public: if (Prop->HasAnyPropertyFlags(CPF_RepNotify)) Flags.Append(TEXT(" RepNotify")); 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) - Result.Appendf(TEXT(" [%s]"), *MCPUtils::FormatName(OwnerClass)); + UMCPServer::Printf(TEXT(" [%s]"), *MCPUtils::FormatName(OwnerClass)); if (Flags.Len() > 0) - Result.Appendf(TEXT(" (%s)"), Flags.ToString() + 1); // skip leading space - Result.Append(TEXT("\n")); + UMCPServer::Printf(TEXT(" (%s)"), Flags.ToString() + 1); // skip leading space + UMCPServer::Print(TEXT("\n")); Count++; } if (Count == 0) { - Result.Append(TEXT("No properties found.\n")); + UMCPServer::Print(TEXT("No properties found.\n")); } } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Editor_ListOpenAssets.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Editor_ListOpenAssets.h index f6f91a98..a4f25c07 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Editor_ListOpenAssets.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Editor_ListOpenAssets.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "Editor.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."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem(); if (!Sub) { - Result.Append(TEXT("Error: AssetEditorSubsystem not available\n")); + UMCPServer::Print(TEXT("Error: AssetEditorSubsystem not available\n")); return; } TArray EditedAssets = Sub->GetAllEditedAssets(); if (EditedAssets.IsEmpty()) { - Result.Append(TEXT("No asset editors are open.\n")); + UMCPServer::Print(TEXT("No asset editors are open.\n")); return; } @@ -42,7 +43,7 @@ public: { bool bDirty = Asset->GetOutermost()->IsDirty(); - Result.Appendf(TEXT(" %s%s\n"), + UMCPServer::Printf(TEXT(" %s%s\n"), bDirty ? TEXT("[unsaved] ") : TEXT(""), *Asset->GetPathName()); } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Editor_OpenAsset.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Editor_OpenAsset.h index 1057a272..980708ec 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Editor_OpenAsset.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Editor_OpenAsset.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "Editor.h" @@ -26,7 +27,7 @@ public: 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; UObject* Obj = F.Walk(Path).Cast(); @@ -35,13 +36,13 @@ public: UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem(); if (!Sub) { - Result.Append(TEXT("Error: AssetEditorSubsystem not available\n")); + UMCPServer::Print(TEXT("Error: AssetEditorSubsystem not available\n")); return; } 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 - 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()); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Enum_Create.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Enum_Create.h index 1fd8bfd0..75425f94 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Enum_Create.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Enum_Create.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" @@ -32,7 +33,7 @@ public: return TEXT("Create a new UserDefinedEnum asset with the specified values."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPPackageMaker Maker(AssetPath); if (!Maker.Ok()) return; @@ -64,8 +65,8 @@ public: 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) - Result.Append(TEXT("WARNING: Package save failed\n")); + UMCPServer::Print(TEXT("WARNING: Package save failed\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Create.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Create.h index 47b67105..66e85046 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Create.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Create.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -49,7 +50,7 @@ public: "Use GraphNodeSearchTypes first to find the exact action name."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UEdGraph* TargetGraph = F.Walk(Graph).Cast(); @@ -68,13 +69,13 @@ public: TArray> Matches = MCPUtils::SearchGraphActions(TargetGraph, Entry.ActionName, 0, /*ExactMatch=*/true); 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); continue; } 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); continue; } @@ -84,18 +85,18 @@ public: UEdGraphNode* NewNode = Matches[0]->PerformAction(TargetGraph, nullptr, Location, /*bSelectNewNode=*/false); 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; } if (!NewNode->NodeGuid.IsValid()) NewNode->CreateNewGuid(); - Result.Appendf(TEXT("Spawned: %s (%s)\n"), + UMCPServer::Printf(TEXT("Spawned: %s (%s)\n"), *MCPUtils::FormatName(NewNode), *MCPUtils::FormatName(NewNode->GetClass())); SuccessCount++; } - Result.Appendf(TEXT("Spawned %d/%d nodes.\n"), SuccessCount, TotalCount); + UMCPServer::Printf(TEXT("Spawned %d/%d nodes.\n"), SuccessCount, TotalCount); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Delete.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Delete.h index e6ab128f..743de1dd 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Delete.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Delete.h @@ -32,7 +32,7 @@ public: "Cannot delete undeletable nodes (entry points, root nodes, etc)."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UEdGraphNode* FoundNode = F.Walk(Node).Cast(); @@ -63,6 +63,6 @@ public: 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); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Duplicate.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Duplicate.h index 42ea1545..61f3400b 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Duplicate.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_Duplicate.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -39,7 +40,7 @@ public: "Connections are not preserved on the duplicates."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UEdGraph* TargetGraph = F.Walk(Graph).Cast(); @@ -47,7 +48,7 @@ public: if (Nodes.Array.Num() == 0) { - Result.Append(TEXT("ERROR: Nodes array is empty\n")); + UMCPServer::Print(TEXT("ERROR: Nodes array is empty\n")); return; } @@ -67,7 +68,7 @@ public: } 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; } SourceNodes.Add(Found); @@ -81,7 +82,7 @@ public: UEdGraphNode* NewNode = DuplicateObject(SourceNode, TargetGraph); 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; } @@ -96,7 +97,7 @@ public: } 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)); } } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_GetComment.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_GetComment.h index 2da53aa7..57b853eb 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_GetComment.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_GetComment.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -26,14 +27,14 @@ public: return TEXT("Get the comment text and bubble visibility of a node."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UEdGraphNode* FoundNode = F.Walk(Node).Cast(); if (!FoundNode) return; - Result.Appendf(TEXT("Node: %s\n"), *MCPUtils::FormatName(FoundNode)); - Result.Appendf(TEXT("Comment: %s\n"), *FoundNode->NodeComment); - Result.Appendf(TEXT("BubbleVisible: %s\n"), FoundNode->bCommentBubbleVisible ? TEXT("true") : TEXT("false")); + UMCPServer::Printf(TEXT("Node: %s\n"), *MCPUtils::FormatName(FoundNode)); + UMCPServer::Printf(TEXT("Comment: %s\n"), *FoundNode->NodeComment); + UMCPServer::Printf(TEXT("BubbleVisible: %s\n"), FoundNode->bCommentBubbleVisible ? TEXT("true") : TEXT("false")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SearchTypes.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SearchTypes.h index 671cde1d..a999f288 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SearchTypes.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SearchTypes.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -35,7 +36,7 @@ public: "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); @@ -47,16 +48,16 @@ public: for (const TSharedPtr& Action : Actions) { - Result.Appendf(TEXT("%s\n"), *MCPUtils::ActionFullName(Action)); + UMCPServer::Printf(TEXT("%s\n"), *MCPUtils::ActionFullName(Action)); } 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) { - 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); } } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetComment.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetComment.h index 2ddf7b3b..b2f6f6d1 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetComment.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetComment.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.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."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UEdGraphNode* FoundNode = F.Walk(Node).Cast(); @@ -44,6 +45,6 @@ public: 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)); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetDefaults.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetDefaults.h index 9d447ab3..290c7328 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetDefaults.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetDefaults.h @@ -53,8 +53,7 @@ public: // K2 graphs: set pin default values. // ----------------------------------------------------------------------- - void HandleK2Entry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj, const UEdGraphSchema_K2* K2Schema, - FStringBuilderBase& Result) + void HandleK2Entry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj, const UEdGraphSchema_K2* K2Schema) { MCPFetcher F(GraphObj); UEdGraphPin* Pin = F.Node(Entry.Node).Pin(Entry.Name).Cast(); @@ -64,7 +63,7 @@ public: 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; } @@ -77,7 +76,7 @@ public: FString Error = K2Schema->IsPinDefaultValid(Pin, UseDefaultValue, UseDefaultObject, UseDefaultText); 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; } UMCPServer::AddTouchedObject(Node); @@ -88,8 +87,7 @@ public: // Material graphs: set material expression properties. // ----------------------------------------------------------------------- - void HandleMaterialEntry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj, - FStringBuilderBase& Result) + void HandleMaterialEntry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj) { MCPFetcher F(GraphObj); UEdGraphNode* Node = F.Node(Entry.Node).Cast(); @@ -106,7 +104,7 @@ public: // ----------------------------------------------------------------------- - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { // Fetch the graph once. MCPFetcher GraphFetcher; @@ -119,7 +117,7 @@ public: 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; } @@ -130,11 +128,11 @@ public: continue; if (K2Schema) - HandleK2Entry(Entry, GraphObj, K2Schema, Result); + HandleK2Entry(Entry, GraphObj, K2Schema); else if (MGSchema) - HandleMaterialEntry(Entry, GraphObj, Result); + HandleMaterialEntry(Entry, GraphObj); } - Result.Appendf(TEXT("Done.\n")); + UMCPServer::Printf(TEXT("Done.\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetPositions.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetPositions.h index 24186943..0294a6fb 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetPositions.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphNode_SetPositions.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -46,7 +47,7 @@ public: return TEXT("Reposition one or more nodes in a Blueprint graph."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UBlueprint* BP = F.Walk(Blueprint).Cast(); @@ -65,10 +66,10 @@ public: Node->NodePosX = Entry.X; 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++; } - Result.Appendf(TEXT("Moved %d/%d nodes.\n"), SuccessCount, Nodes.Array.Num()); + UMCPServer::Printf(TEXT("Moved %d/%d nodes.\n"), SuccessCount, Nodes.Array.Num()); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphPin_Connect.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphPin_Connect.h index 359f343e..7f6b94d3 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphPin_Connect.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphPin_Connect.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -45,7 +46,7 @@ public: return TEXT("Connect pins between nodes in a graph (Blueprint or Material)."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UEdGraph* G = F.Walk(Graph).ToGraph().Cast(); @@ -72,7 +73,7 @@ public: const FPinConnectionResponse Response = Schema->CanCreateConnection(SourcePin, TargetPin); 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(TargetPin->GetOwningNode()), *MCPUtils::FormatName(TargetPin), *Response.Message.ToString()); @@ -83,6 +84,6 @@ public: SuccessCount++; } - Result.Appendf(TEXT("Connected %d/%d pins.\n"), SuccessCount, TotalCount); + UMCPServer::Printf(TEXT("Connected %d/%d pins.\n"), SuccessCount, TotalCount); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphPin_Disconnect.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphPin_Disconnect.h index 749c5c69..ce180615 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphPin_Disconnect.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/GraphPin_Disconnect.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -45,7 +46,7 @@ public: "Can disconnect a specific link or all links on a pin."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UEdGraph* G = F.Walk(Graph).ToGraph().Cast(); @@ -73,7 +74,7 @@ public: 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(Target->GetOwningNode()), *MCPUtils::FormatName(Target)); 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, *MCPUtils::FormatName(Pin->GetOwningNode()), *MCPUtils::FormatName(Pin)); SuccessCount++; 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); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Graph_Dump.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Graph_Dump.h index a061b3f9..ff352f62 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Graph_Dump.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Graph_Dump.h @@ -32,7 +32,7 @@ public: "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; F.Walk(Path); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialFunction_Create.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialFunction_Create.h index 5161f27e..1ef493ea 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialFunction_Create.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialFunction_Create.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" @@ -31,7 +32,7 @@ public: return TEXT("Create a new UMaterialFunction asset with an optional description."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPPackageMaker Maker(AssetPath); if (!Maker.Ok()) return; @@ -46,8 +47,8 @@ public: bool bSaved = MCPUtils::SaveGenericPackage(MF); - Result.Appendf(TEXT("Created %s\n"), *MF->GetPathName()); + UMCPServer::Printf(TEXT("Created %s\n"), *MF->GetPathName()); if (!bSaved) - Result.Append(TEXT("WARNING: Package save failed\n")); + UMCPServer::Print(TEXT("WARNING: Package save failed\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_ClearParameter.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_ClearParameter.h index f10bdc99..46471b32 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_ClearParameter.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_ClearParameter.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.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."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UMaterialInstanceConstant* MI = F.Asset(Path).Cast(); @@ -66,13 +67,13 @@ public: 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)); return; } 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)); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_Create.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_Create.h index 81e7fc8b..a96f71fc 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_Create.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_Create.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" @@ -33,7 +34,7 @@ public: 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); if (!Maker.Ok()) return; @@ -63,7 +64,7 @@ public: 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; } @@ -77,14 +78,14 @@ public: // Save. 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(ParentMaterialObj)) - Result.Appendf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentMI)); + UMCPServer::Printf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentMI)); else if (UMaterial* ParentMat = Cast(ParentMaterialObj)) - Result.Appendf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentMat)); + UMCPServer::Printf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentMat)); else - Result.Appendf(TEXT("Parent: %s\n"), *ParentMaterialObj->GetPathName()); + UMCPServer::Printf(TEXT("Parent: %s\n"), *ParentMaterialObj->GetPathName()); if (!bSaved) - Result.Append(TEXT("WARNING: Package save failed\n")); + UMCPServer::Print(TEXT("WARNING: Package save failed\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_DumpParameters.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_DumpParameters.h index 3034d6d7..e4977d63 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_DumpParameters.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_DumpParameters.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.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."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UMaterialInstanceConstant* MI = F.Asset(Path).Cast(); @@ -40,8 +41,8 @@ public: for (auto& [Info, Meta] : AllParams) { if (!Meta.bOverride) continue; - if (!bHasOverrides) { Result.Append(TEXT("\nOverridden Parameters:\n")); bHasOverrides = true; } - MCPUtils::FormatMaterialParameter(Result, Info, Meta); + if (!bHasOverrides) { UMCPServer::Print(TEXT("\nOverridden Parameters:\n")); bHasOverrides = true; } + MCPUtils::FormatMaterialParameter(Info, Meta); } // Inherited (non-overridden) parameters. @@ -49,8 +50,8 @@ public: for (auto& [Info, Meta] : AllParams) { if (Meta.bOverride) continue; - if (!bHasInherited) { Result.Append(TEXT("\nInherited Parameters (not overridden):\n")); bHasInherited = true; } - MCPUtils::FormatMaterialParameter(Result, Info, Meta); + if (!bHasInherited) { UMCPServer::Print(TEXT("\nInherited Parameters (not overridden):\n")); bHasInherited = true; } + MCPUtils::FormatMaterialParameter(Info, Meta); } } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_SetParameter.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_SetParameter.h index 29be29f4..f7b46179 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_SetParameter.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/MaterialInstance_SetParameter.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -40,7 +41,7 @@ public: return TEXT("Set a parameter override on a Material Instance."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UMaterialInstanceConstant* MI = F.Asset(Path).Cast(); @@ -59,13 +60,13 @@ public: FMaterialParameterMetadata* Found = AllParams.Find(ParamID); 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); return; } 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; } @@ -78,7 +79,7 @@ public: float 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; } MI->SetScalarParameterValueEditorOnly(ParamID, ScalarValue); @@ -89,19 +90,19 @@ public: FLinearColor Color; 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; } MI->SetVectorParameterValueEditorOnly(ParamID, Color); break; } 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; } 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)); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_Compile.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_Compile.h index d93b83cb..212d5b39 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_Compile.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_Compile.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -26,7 +27,7 @@ public: return TEXT("Force recompile a material and check for compilation errors."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { // Load material MCPFetcher F; @@ -47,14 +48,14 @@ public: if (Errors.IsEmpty()) { - Result.Appendf(TEXT("%s compiled successfully.\n"), *MCPUtils::FormatName(MaterialObj)); + UMCPServer::Printf(TEXT("%s compiled successfully.\n"), *MCPUtils::FormatName(MaterialObj)); } 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) { - Result.Appendf(TEXT(" %s\n"), *Err); + UMCPServer::Printf(TEXT(" %s\n"), *Err); } } } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_Create.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_Create.h index a1b7e41f..09fe3b68 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_Create.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_Create.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.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."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPPackageMaker Maker(AssetPath); if (!Maker.Ok()) return; @@ -73,11 +74,11 @@ public: bool bSaved = MCPUtils::SaveGenericPackage(MaterialObj); - Result.Appendf(TEXT("Created %s\n"), *MaterialObj->GetPathName()); - Result.Appendf(TEXT("Domain: %s\n"), *MCPUtils::EnumToString(MaterialObj->MaterialDomain, TEXT("MD_"))); - Result.Appendf(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("Created %s\n"), *MaterialObj->GetPathName()); + UMCPServer::Printf(TEXT("Domain: %s\n"), *MCPUtils::EnumToString(MaterialObj->MaterialDomain, TEXT("MD_"))); + UMCPServer::Printf(TEXT("BlendMode: %s\n"), *MCPUtils::EnumToString(MaterialObj->BlendMode, TEXT("BLEND_"))); + UMCPServer::Printf(TEXT("TwoSided: %s\n"), MaterialObj->TwoSided ? TEXT("true") : TEXT("false")); if (!bSaved) - Result.Append(TEXT("WARNING: Package save failed\n")); + UMCPServer::Print(TEXT("WARNING: Package save failed\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_DumpParameters.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_DumpParameters.h index 790181a3..59ed1dbd 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_DumpParameters.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Material_DumpParameters.h @@ -27,7 +27,7 @@ public: return TEXT("List all parameters on a Material, showing their default values."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UMaterial* Mat = F.Asset(Path).Cast(); @@ -37,7 +37,7 @@ public: for (auto& [Info, Meta] : AllParams) { - MCPUtils::FormatMaterialParameter(Result, Info, Meta); + MCPUtils::FormatMaterialParameter(Info, Meta); } } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Dump.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Dump.h index 4ebe274f..3dddd397 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Dump.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Dump.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPProperty.h" @@ -36,7 +37,7 @@ public: "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. MCPFetcher F; @@ -70,9 +71,9 @@ public: for (const FString& Category : Categories) { if (Category.IsEmpty()) - Result.Append(TEXT("\nUncategorized:\n")); + UMCPServer::Print(TEXT("\nUncategorized:\n")); else - Result.Appendf(TEXT("\n%s:\n"), *Category); + UMCPServer::Printf(TEXT("\n%s:\n"), *Category); for (MCPProperty& P : ByCategory[Category]) { @@ -83,7 +84,7 @@ public: ValueStr = ValueStr.Left(80) + TEXT("..."); 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"), *MCPUtils::FormatPropertyType(P.Prop), *PropName, @@ -92,6 +93,6 @@ public: } if (Props.IsEmpty()) - Result.Append(TEXT(" (no blueprint-visible properties found)\n")); + UMCPServer::Print(TEXT(" (no blueprint-visible properties found)\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Get.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Get.h index c021412d..acbac0cd 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Get.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Get.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.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."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPFetcher F; UObject* Template = F.Walk(Path).Template().Cast(); @@ -38,7 +39,7 @@ public: MCPProperty P = MCPProperty::GetOneExactMatch(Template, CPF_Edit, Property); if (!P) return; - Result.Append(P.GetText()); - Result.Append(TEXT("\n")); + UMCPServer::Print(P.GetText()); + UMCPServer::Print(TEXT("\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Set.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Set.h index 67f4a250..361fe147 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Set.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Property_Set.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPProperty.h" @@ -30,7 +31,7 @@ public: "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. MCPFetcher F; @@ -39,7 +40,7 @@ public: 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; } @@ -53,7 +54,7 @@ public: FString 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; } Resolved.Emplace(P, ValueStr); @@ -71,8 +72,8 @@ public: // Save. 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) - Result.Append(TEXT("Warning: Save failed\n")); + UMCPServer::Print(TEXT("Warning: Save failed\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/ShowCommands.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/ShowCommands.h index 37660b38..bdcc3f39 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/ShowCommands.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/ShowCommands.h @@ -3,6 +3,7 @@ #include "CoreMinimal.h" #include "MCPHandler.h" #include "MCPFetcher.h" +#include "MCPServer.h" #include "MCPUtils.h" #include "ShowCommands.generated.h" @@ -23,7 +24,7 @@ public: return TEXT("List all available commands with their descriptions."); } - virtual void Handle(FStringBuilderBase& Result) override + void EmitCommandList() { FString QueryLower = Query.ToLower(); FString PrevGroup; @@ -39,40 +40,47 @@ public: if (Group != PrevGroup) { if (!PrevGroup.IsEmpty()) - Result.Append(TEXT("\n")); + UMCPServer::Print(TEXT("\n")); PrevGroup = Group; } if (Verbose) { - MCPUtils::FormatCommandHelp(Class, Result); + MCPUtils::FormatCommandHelp(Class); continue; } - Result.Append(ToolName); - Result.Append(TEXT("(")); + UMCPServer::Print(ToolName); + UMCPServer::Print(TEXT("(")); bool bFirst = true; for (TFieldIterator PropIt(Class, EFieldIterationFlags::None); PropIt; ++PropIt) { - if (!bFirst) Result.Append(TEXT(",")); + if (!bFirst) UMCPServer::Print(TEXT(",")); bFirst = false; - if (PropIt->HasMetaData(TEXT("Optional"))) Result.Append(TEXT("?")); - Result.Append(MCPUtils::FormatPropertyType(*PropIt)); - Result.Append(TEXT(" ")); - Result.Append(MCPUtils::PropertyNameToJsonKey(PropIt->GetName())); + if (PropIt->HasMetaData(TEXT("Optional"))) UMCPServer::Print(TEXT("?")); + UMCPServer::Print(MCPUtils::FormatPropertyType(*PropIt)); + UMCPServer::Print(TEXT(" ")); + 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. - Result.Append(TEXT("\n")); - Result.Append(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")); - Result.Append(TEXT("more comma-separated steps that navigate into the asset:\n")); - Result.Append(TEXT("\n")); + UMCPServer::Print(TEXT("\n")); + UMCPServer::Print(TEXT("Some commands take a Path parameter. A Path starts with an asset\n")); + UMCPServer::Print(TEXT("package path (e.g. /Game/Widgets/WB_Hotkeys), followed by zero or\n")); + UMCPServer::Print(TEXT("more comma-separated steps that navigate into the asset:\n")); + UMCPServer::Print(TEXT("\n")); 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")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_AddState.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_AddState.h index b63ca0b5..57122881 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_AddState.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_AddState.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPFetcher.h" @@ -49,7 +50,7 @@ public: "Optionally assign an animation asset to the state."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { // Resolve the anim blueprint MCPFetcher F; @@ -60,14 +61,14 @@ public: UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph); 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; } // Check for duplicate state name 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; } @@ -110,7 +111,7 @@ public: FKismetEditorUtilities::CompileBlueprint(AnimBP); MCPUtils::SaveBlueprintPackage(AnimBP); - Result.Appendf(TEXT("Created state '%s' in %s\n"), *StateName, *MCPUtils::FormatName(SMGraph)); - Result.Appendf(TEXT(" node: %s\n"), *MCPUtils::FormatName(NewState)); + UMCPServer::Printf(TEXT("Created state '%s' in %s\n"), *StateName, *MCPUtils::FormatName(SMGraph)); + UMCPServer::Printf(TEXT(" node: %s\n"), *MCPUtils::FormatName(NewState)); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_AddTransition.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_AddTransition.h index a8d3f93a..b609bb14 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_AddTransition.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_AddTransition.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" @@ -48,7 +49,7 @@ public: 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 Assets; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; @@ -57,7 +58,7 @@ public: UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph); 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; } @@ -92,7 +93,7 @@ public: FKismetEditorUtilities::CompileBlueprint(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)); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_RemoveState.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_RemoveState.h index 5827b0ca..6b89a393 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_RemoveState.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_RemoveState.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPFetcher.h" #include "MCPUtils.h" @@ -33,7 +34,7 @@ public: 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 MCPFetcher F; @@ -47,7 +48,7 @@ public: UBlueprint* BP = Cast(SMGraph->GetOuter()->GetOuter()); if (!BP) { - Result.Append(TEXT("ERROR: Could not find owning blueprint.\n")); + UMCPServer::Print(TEXT("ERROR: Could not find owning blueprint.\n")); return; } @@ -75,7 +76,7 @@ public: FKismetEditorUtilities::CompileBlueprint(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); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetAnimation.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetAnimation.h index 725a1804..bd80bfdc 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetAnimation.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetAnimation.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.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."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { // Resolve the anim blueprint MCPFetcher F; @@ -54,7 +55,7 @@ public: UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph); 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; } @@ -65,7 +66,7 @@ public: UEdGraph* InnerGraph = StateNode->GetBoundGraph(); 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; } @@ -102,8 +103,8 @@ public: MCPUtils::SaveBlueprintPackage(AnimBP); 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 - 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)); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetBlendSpace.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetBlendSpace.h index fbcbacc6..524e4bfd 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetBlendSpace.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetBlendSpace.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPFetcher.h" @@ -53,7 +54,7 @@ public: "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 MCPAssets Assets; @@ -62,13 +63,13 @@ public: // Find the state machine graph and state 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); if (!StateNode) return; 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 MCPAssets BlendSpaceAssets; @@ -100,17 +101,17 @@ public: ConnectToOutputPose(BSNode, InnerGraph); // Wire X and Y variables if provided - WireVariable(AnimBP, InnerGraph, BSNode, XVariable, TEXT("X"), Result); - WireVariable(AnimBP, InnerGraph, BSNode, YVariable, TEXT("Y"), Result); + WireVariable(AnimBP, InnerGraph, BSNode, XVariable, TEXT("X")); + WireVariable(AnimBP, InnerGraph, BSNode, YVariable, TEXT("Y")); // Compile and save FKismetEditorUtilities::CompileBlueprint(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); if (!bSaved) - Result.Append(TEXT("WARNING: Failed to save package\n")); + UMCPServer::Print(TEXT("WARNING: Failed to save package\n")); } private: @@ -160,7 +161,7 @@ private: void WireVariable(UAnimBlueprint* AnimBP, UEdGraph* InnerGraph, UAnimGraphNode_BlendSpacePlayer* BSNode, const FString& VarName, - const TCHAR* PinName, FStringBuilderBase& Result) + const TCHAR* PinName) { if (VarName.IsEmpty()) return; @@ -185,7 +186,7 @@ private: } 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; } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetTransitionRule.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetTransitionRule.h index b5724783..6fb5cf55 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetTransitionRule.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/StateMachine_SetTransitionRule.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.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."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPAssets Assets; if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return; @@ -62,14 +63,14 @@ public: UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph); 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; } UAnimStateTransitionNode* TransNode = MCPUtils::FindTransition(SMGraph, FromState, ToState); 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); return; } @@ -85,7 +86,7 @@ public: FKismetEditorUtilities::CompileBlueprint(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)); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Struct_Create.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Struct_Create.h index bc2aa623..1479d6a5 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Struct_Create.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Struct_Create.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MCPServer.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" @@ -45,7 +46,7 @@ public: return TEXT("Create a new UserDefinedStruct asset with optional initial properties."); } - virtual void Handle(FStringBuilderBase& Result) override + virtual void Handle() override { MCPPackageMaker Maker(AssetPath); if (!Maker.Ok()) return; @@ -88,10 +89,10 @@ public: bool bSaved = MCPUtils::SaveGenericPackage(NewStruct); - Result.Appendf(TEXT("Created %s\n"), *NewStruct->GetPathName()); + UMCPServer::Printf(TEXT("Created %s\n"), *NewStruct->GetPathName()); if (PropsAdded > 0) - Result.Appendf(TEXT("Properties added: %d\n"), PropsAdded); + UMCPServer::Printf(TEXT("Properties added: %d\n"), PropsAdded); if (!bSaved) - Result.Append(TEXT("WARNING: Package save failed\n")); + UMCPServer::Print(TEXT("WARNING: Package save failed\n")); } }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPServer.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPServer.cpp index f825d912..4ea42b77 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPServer.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPServer.cpp @@ -260,57 +260,17 @@ TStatId UMCPServer::GetStatId() const FString UMCPServer::HandleRequest(const FString& Line) { - // Turn the request string into a JSON tree. - TSharedPtr Request; - TSharedRef> 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 HandlerObj(NewObject(GetTransientPackage(), *HandlerClass)); - IMCPHandler* Handler = Cast(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.bEnabled = true; HandlerOutput.Reset(); - Handler->Handle(HandlerOutput); + + TryCallHandler(Line); + Notifier.SendNotifications(); LogCapture.bEnabled = false; for (const FString& Msg : LogCapture.CapturedErrors) { - HandlerOutput.Append(TEXT("LOG: ")); - HandlerOutput.Append(Msg); - HandlerOutput.Append(TEXT("\n")); + UMCPServer::Printf(TEXT("UE_LOG: %s\n"), *Msg); } LogCapture.CapturedErrors.Empty(); FString Result = HandlerOutput.ToString(); @@ -322,6 +282,51 @@ FString UMCPServer::HandleRequest(const FString& Line) return Result; } +void UMCPServer::TryCallHandler(const FString &Line) +{ + // Turn the request string into a JSON tree. + TSharedPtr Request; + TSharedRef> 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 HandlerObj(NewObject(GetTransientPackage(), *HandlerClass)); + IMCPHandler* Handler = Cast(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 // ============================================================ diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPUtils.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPUtils.cpp index f781cd5a..ea056ff7 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPUtils.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPUtils.cpp @@ -813,7 +813,7 @@ bool MCPUtils::ParseMaterialParameterAssociation(const FString& Str, EMaterialPa 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. FString Prefix; @@ -825,35 +825,35 @@ void MCPUtils::FormatMaterialParameter(FStringBuilderBase& Result, const FMateri switch (Meta.Value.Type) { 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; case EMaterialParameterType::Vector: { 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); break; } case EMaterialParameterType::DoubleVector: { 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); break; } case EMaterialParameterType::Texture: { UTexture* Tex = Cast(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")); break; } 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")); break; 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; } } @@ -1398,29 +1398,29 @@ TArray MCPUtils::SearchProperties(UObject* Obj, const FString& Query // FormatCommandHelp — verbose description of one handler command // ============================================================ -void MCPUtils::FormatCommandHelp(UClass* HandlerClass, FStringBuilderBase& Result) +void MCPUtils::FormatCommandHelp(UClass* HandlerClass) { const IMCPHandler* Handler = Cast(HandlerClass->GetDefaultObject()); if (!Handler) return; FString ToolName = GetHandlerName(HandlerClass); - Result.Append(TEXT("\n")); - Result.Append(WrapText(Handler->GetDescription(), 80, TEXT("// "))); - Result.Append(TEXT("\n")); + UMCPServer::Print(TEXT("\n")); + UMCPServer::Print(WrapText(Handler->GetDescription(), 80, TEXT("// "))); + UMCPServer::Print(TEXT("\n")); // Command signature line - Result.Append(ToolName); - Result.Append(TEXT("(")); + UMCPServer::Print(ToolName); + UMCPServer::Print(TEXT("(")); bool bFirst = true; for (TFieldIterator PropIt(HandlerClass, EFieldIterationFlags::None); PropIt; ++PropIt) { - if (!bFirst) Result.Append(TEXT(",")); + if (!bFirst) UMCPServer::Print(TEXT(",")); bFirst = false; - if (PropIt->HasMetaData(TEXT("Optional"))) Result.Append(TEXT("?")); - Result.Append(PropertyNameToJsonKey(PropIt->GetName())); + if (PropIt->HasMetaData(TEXT("Optional"))) UMCPServer::Print(TEXT("?")); + UMCPServer::Print(PropertyNameToJsonKey(PropIt->GetName())); } - Result.Append(TEXT(")\n")); + UMCPServer::Print(TEXT(")\n")); // parameter details for (TFieldIterator PropIt(HandlerClass, EFieldIterationFlags::None); PropIt; ++PropIt) @@ -1431,10 +1431,10 @@ void MCPUtils::FormatCommandHelp(UClass* HandlerClass, FStringBuilderBase& Resul bool bOptional = Prop->HasMetaData(TEXT("Optional")); 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("")); if (!Desc.IsEmpty()) - Result.Appendf(TEXT(" — %s"), *Desc); - Result.Append(TEXT("\n")); + UMCPServer::Printf(TEXT(" — %s"), *Desc); + UMCPServer::Print(TEXT("\n")); } } diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPHandler.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPHandler.h index ae5d3554..c155f84f 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPHandler.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPHandler.h @@ -51,5 +51,5 @@ public: virtual FString GetDescription() const = 0; // Called after parameter fields have been populated from JSON. - virtual void Handle(FStringBuilderBase& Result) {} + virtual void Handle() {} }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPServer.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPServer.h index f3583f6a..287b72e9 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPServer.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPServer.h @@ -53,6 +53,7 @@ public: /** Print text to the handler output buffer. */ 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. */ template @@ -79,6 +80,7 @@ private: // Handle a complete JSON line and return the response JSON FString HandleRequest(const FString& Line); + void TryCallHandler(const FString &Line); // ----- TCP server ----- FSocket* ListenSocket = nullptr; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPUtils.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPUtils.h index 92f3c09e..3fd1316b 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPUtils.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPUtils.h @@ -162,7 +162,7 @@ public: // ----- Material Parameters ----- static TMap GetMaterialParameters(UMaterialInterface* Material); 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 ----- static TArray SearchProperties(UObject* Obj, const FString& Query, EPropertyFlags Flags, bool bLocal); @@ -183,7 +183,7 @@ public: static TArray CollectHandlerClasses(); static FString GetHandlerName(UClass* HandlerClass); static FString GetHandlerGroup(UClass* HandlerClass); - static void FormatCommandHelp(UClass* HandlerClass, FStringBuilderBase& Result); + static void FormatCommandHelp(UClass* HandlerClass); private: