ultimate-unreal-engine-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +729 -0
  2. package/dist/build/error-parser.js +51 -0
  3. package/dist/build/fix-suggester.js +84 -0
  4. package/dist/build/ubt-runner.js +146 -0
  5. package/dist/cli.js +13 -0
  6. package/dist/config.js +8 -0
  7. package/dist/docs/data/ue57-api.js +228 -0
  8. package/dist/docs/doc-index.js +110 -0
  9. package/dist/docs/types.js +4 -0
  10. package/dist/generators/class-generator.js +363 -0
  11. package/dist/generators/file-modifier.js +276 -0
  12. package/dist/generators/uht-validator.js +177 -0
  13. package/dist/index.js +89 -0
  14. package/dist/parsers/cpp-class-index.js +230 -0
  15. package/dist/parsers/cpp-parser.js +369 -0
  16. package/dist/parsers/ini-parser.js +216 -0
  17. package/dist/parsers/uproject-parser.js +130 -0
  18. package/dist/plugin-bridge/client.js +217 -0
  19. package/dist/plugin-bridge/protocol.js +6 -0
  20. package/dist/plugin-bridge/retry.js +23 -0
  21. package/dist/setup.js +209 -0
  22. package/dist/tools/ai-systems/index.js +247 -0
  23. package/dist/tools/ai-systems/types.js +4 -0
  24. package/dist/tools/animation/index.js +241 -0
  25. package/dist/tools/animation/types.js +4 -0
  26. package/dist/tools/audio/index.js +204 -0
  27. package/dist/tools/audio/types.js +4 -0
  28. package/dist/tools/blueprint/index.js +495 -0
  29. package/dist/tools/blueprint/types.js +4 -0
  30. package/dist/tools/build/index.js +163 -0
  31. package/dist/tools/chaos/index.js +230 -0
  32. package/dist/tools/chaos/types.js +4 -0
  33. package/dist/tools/collision-physics/index.js +211 -0
  34. package/dist/tools/config/index.js +288 -0
  35. package/dist/tools/cpp/index.js +305 -0
  36. package/dist/tools/docs/index.js +251 -0
  37. package/dist/tools/editor/index.js +242 -0
  38. package/dist/tools/gas/index.js +222 -0
  39. package/dist/tools/gas/types.js +5 -0
  40. package/dist/tools/import-export/index.js +218 -0
  41. package/dist/tools/input/index.js +146 -0
  42. package/dist/tools/known-issues/index.js +88 -0
  43. package/dist/tools/known-issues/middleware.js +55 -0
  44. package/dist/tools/known-issues/store.js +125 -0
  45. package/dist/tools/livelink/index.js +203 -0
  46. package/dist/tools/livelink/types.js +4 -0
  47. package/dist/tools/material/index.js +190 -0
  48. package/dist/tools/motion-design/index.js +251 -0
  49. package/dist/tools/motion-design/types.js +6 -0
  50. package/dist/tools/movie-render/index.js +220 -0
  51. package/dist/tools/networking/index.js +149 -0
  52. package/dist/tools/pcg/index.js +164 -0
  53. package/dist/tools/selection/index.js +180 -0
  54. package/dist/tools/sequencer/index.js +218 -0
  55. package/dist/tools/validation/index.js +183 -0
  56. package/dist/tools/validation/types.js +4 -0
  57. package/dist/tools/viewport/index.js +310 -0
  58. package/dist/tools/worldpartition/index.js +226 -0
  59. package/dist/tools/worldpartition/types.js +4 -0
  60. package/dist/utils/execFileNoThrow.js +40 -0
  61. package/dist/utils/logger.js +27 -0
  62. package/dist/utils/path-guard.js +26 -0
  63. package/package.json +40 -0
  64. package/unreal-plugin/MCPBridge/MCPBridge.uplugin +29 -0
  65. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/MCPBridgeEditor.Build.cs +68 -0
  66. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.cpp +919 -0
  67. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.h +23 -0
  68. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.cpp +415 -0
  69. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.h +16 -0
  70. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.cpp +653 -0
  71. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.h +24 -0
  72. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.cpp +290 -0
  73. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.h +17 -0
  74. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.cpp +624 -0
  75. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.h +22 -0
  76. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.cpp +616 -0
  77. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.h +25 -0
  78. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.cpp +744 -0
  79. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.h +24 -0
  80. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeEditor.cpp +23 -0
  81. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.cpp +149 -0
  82. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.h +38 -0
  83. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.cpp +771 -0
  84. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.h +22 -0
  85. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.cpp +749 -0
  86. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.h +22 -0
  87. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.cpp +172 -0
  88. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.h +16 -0
  89. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.cpp +715 -0
  90. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.h +22 -0
  91. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.cpp +679 -0
  92. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.h +22 -0
  93. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.cpp +381 -0
  94. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.h +24 -0
  95. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.cpp +504 -0
  96. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.h +22 -0
  97. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.cpp +511 -0
  98. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.h +22 -0
  99. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.cpp +1110 -0
  100. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.h +28 -0
  101. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.cpp +590 -0
  102. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.h +16 -0
  103. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.cpp +482 -0
  104. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.h +16 -0
  105. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.cpp +338 -0
  106. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.h +16 -0
  107. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.cpp +677 -0
  108. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.h +22 -0
  109. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.cpp +721 -0
  110. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.h +16 -0
  111. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.cpp +368 -0
  112. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.h +22 -0
  113. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.cpp +1208 -0
  114. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.h +29 -0
  115. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.cpp +822 -0
  116. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.h +23 -0
  117. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Public/MCPBridgeEditor.h +14 -0
  118. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/MCPBridgeRuntime.Build.cs +28 -0
  119. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPBridgeRuntime.cpp +22 -0
  120. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPCommandRouter.cpp +118 -0
  121. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPTcpServer.cpp +196 -0
  122. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPBridgeRuntime.h +15 -0
  123. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPCommandRouter.h +55 -0
  124. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPTcpServer.h +59 -0
@@ -0,0 +1,16 @@
1
+ // MCPSequencerCommands.h
2
+ // Declares the registration function for all sequencer-related MCP command handlers.
3
+ // Handlers: sequencer.create, sequencer.tracks, sequencer.addTrack, sequencer.addKey, sequencer.playback
4
+ //
5
+ // Call RegisterSequencerCommands(*Router) in MCPBridgeSubsystem::Initialize()
6
+ // BEFORE the TCP server starts accepting connections.
7
+
8
+ #pragma once
9
+
10
+ #include "MCPCommandRouter.h"
11
+
12
+ /**
13
+ * Register all five sequencer command handlers into the given router.
14
+ * Must be called on the game thread before connections arrive.
15
+ */
16
+ void RegisterSequencerCommands(FMCPCommandRouter& Router);
@@ -0,0 +1,368 @@
1
+ // MCPValidationCommands.cpp
2
+ // Implements validate.asset, validate.folder, validate.project, validate.blueprint
3
+ // command handlers for the MCPBridge editor plugin.
4
+ // All handlers are registered on the FMCPCommandRouter and run on the game thread
5
+ // (enforced by FMCPCommandRouter::Dispatch via AsyncTask).
6
+ //
7
+ // Security mitigations per threat model (T-16-01 through T-16-02):
8
+ // T-16-01: asset_path validated via FPackageName::IsValidLongPackageName()
9
+ // before any IAssetRegistry or LoadObject call -- rejects traversal and bare paths
10
+ // T-16-02: Named error codes returned (e.g., "asset_not_found"), never raw UE internal strings;
11
+ // BP->ErrorMessage is user-authored content and is safe to surface
12
+
13
+ #include "MCPValidationCommands.h"
14
+
15
+ #include "MCPCommandRouter.h"
16
+
17
+ // Validation APIs
18
+ #include "DataValidation/EditorValidatorSubsystem.h"
19
+
20
+ // Blueprint APIs
21
+ #include "Engine/Blueprint.h"
22
+ #include "Kismet2/KismetEditorUtilities.h"
23
+
24
+ // Asset Registry APIs
25
+ #include "AssetRegistry/AssetRegistryModule.h"
26
+ #include "AssetRegistry/IAssetRegistry.h"
27
+
28
+ // Package path utilities
29
+ #include "Misc/PackageName.h"
30
+
31
+ // JSON APIs
32
+ #include "Dom/JsonObject.h"
33
+ #include "Serialization/JsonSerializer.h"
34
+ #include "Serialization/JsonWriter.h"
35
+
36
+ void RegisterValidationCommands(FMCPCommandRouter& Router)
37
+ {
38
+ // ---------------------------------------------------------------------------
39
+ // Shared response helpers -- identical pattern to MCPBlueprintWriteHandlers.cpp.
40
+ // ---------------------------------------------------------------------------
41
+
42
+ auto SendSuccess = [](FMCPResponseSender SendResponse,
43
+ const FString& CorrelationId,
44
+ TSharedPtr<FJsonObject> Data)
45
+ {
46
+ TSharedPtr<FJsonObject> Response = MakeShared<FJsonObject>();
47
+ Response->SetBoolField(TEXT("success"), true);
48
+ if (!CorrelationId.IsEmpty())
49
+ {
50
+ Response->SetStringField(TEXT("correlationId"), CorrelationId);
51
+ }
52
+ Response->SetObjectField(TEXT("data"), Data);
53
+
54
+ FString Output;
55
+ TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&Output);
56
+ FJsonSerializer::Serialize(Response.ToSharedRef(), Writer);
57
+ Output += TEXT("\n");
58
+ SendResponse(Output);
59
+ };
60
+
61
+ auto SendError = [](FMCPResponseSender SendResponse,
62
+ const FString& CorrelationId,
63
+ const FString& ErrorCode)
64
+ {
65
+ TSharedPtr<FJsonObject> Response = MakeShared<FJsonObject>();
66
+ Response->SetBoolField(TEXT("success"), false);
67
+ if (!CorrelationId.IsEmpty())
68
+ {
69
+ Response->SetStringField(TEXT("correlationId"), CorrelationId);
70
+ }
71
+ Response->SetStringField(TEXT("error"), ErrorCode);
72
+
73
+ FString Output;
74
+ TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&Output);
75
+ FJsonSerializer::Serialize(Response.ToSharedRef(), Writer);
76
+ Output += TEXT("\n");
77
+ SendResponse(Output);
78
+ };
79
+
80
+ // ---------------------------------------------------------------------------
81
+ // validate.asset (VAL-01)
82
+ // Input payload: { "asset_path": "/Game/MyAsset" }
83
+ // Returns: { "assetPath": string, "valid": bool, "numErrors": int, "numWarnings": int }
84
+ //
85
+ // Security: T-16-01 (path validation before IAssetRegistry call)
86
+ // ---------------------------------------------------------------------------
87
+ Router.RegisterHandler(TEXT("validate.asset"),
88
+ [SendSuccess, SendError](TSharedPtr<FJsonObject> Command, FMCPResponseSender SendResponse)
89
+ {
90
+ FString CorrelationId;
91
+ Command->TryGetStringField(TEXT("correlationId"), CorrelationId);
92
+
93
+ const TSharedPtr<FJsonObject>* PayloadObj = nullptr;
94
+ if (!Command->TryGetObjectField(TEXT("payload"), PayloadObj))
95
+ {
96
+ SendError(SendResponse, CorrelationId, TEXT("missing_payload"));
97
+ return;
98
+ }
99
+
100
+ FString AssetPath;
101
+ if (!(*PayloadObj)->TryGetStringField(TEXT("asset_path"), AssetPath) || AssetPath.IsEmpty())
102
+ {
103
+ SendError(SendResponse, CorrelationId, TEXT("missing_asset_path"));
104
+ return;
105
+ }
106
+
107
+ // T-16-01: Validate the asset path is a well-formed long package name.
108
+ // Rejects path traversal ("../") and bare filenames.
109
+ FString ValidationError;
110
+ if (!FPackageName::IsValidLongPackageName(AssetPath, /*bIncludeReadOnlyRoots=*/false, &ValidationError))
111
+ {
112
+ UE_LOG(LogTemp, Warning, TEXT("[MCPBridge] validate.asset: Invalid asset path '%s': %s"),
113
+ *AssetPath, *ValidationError);
114
+ SendError(SendResponse, CorrelationId, TEXT("invalid_asset_path"));
115
+ return;
116
+ }
117
+
118
+ IAssetRegistry& AssetRegistry = FAssetRegistryModule::GetRegistry();
119
+ FAssetData AssetData = AssetRegistry.GetAssetByObjectPath(FSoftObjectPath(AssetPath));
120
+ if (!AssetData.IsValid())
121
+ {
122
+ UE_LOG(LogTemp, Warning, TEXT("[MCPBridge] validate.asset: Asset not found at '%s'"), *AssetPath);
123
+ SendError(SendResponse, CorrelationId, TEXT("asset_not_found"));
124
+ return;
125
+ }
126
+
127
+ UEditorValidatorSubsystem* ValidatorSubsystem = GEditor->GetEditorSubsystem<UEditorValidatorSubsystem>();
128
+ if (!ValidatorSubsystem)
129
+ {
130
+ UE_LOG(LogTemp, Error, TEXT("[MCPBridge] validate.asset: UEditorValidatorSubsystem not available"));
131
+ SendError(SendResponse, CorrelationId, TEXT("validator_not_available"));
132
+ return;
133
+ }
134
+
135
+ TArray<FAssetData> AssetsToValidate;
136
+ AssetsToValidate.Add(AssetData);
137
+
138
+ FValidateAssetsSettings Settings;
139
+ Settings.bSkipExcludedDirectories = true;
140
+ Settings.bShowIfNoFailures = false;
141
+ Settings.ValidationUsecase = EDataValidationUsecase::Script;
142
+
143
+ FValidateAssetsResults Results;
144
+ ValidatorSubsystem->ValidateAssetsWithResults(AssetsToValidate, Settings, Results);
145
+
146
+ const bool bValid = (Results.NumInvalid == 0);
147
+
148
+ TSharedPtr<FJsonObject> Data = MakeShared<FJsonObject>();
149
+ Data->SetStringField(TEXT("assetPath"), AssetPath);
150
+ Data->SetBoolField(TEXT("valid"), bValid);
151
+ Data->SetNumberField(TEXT("numErrors"), Results.NumInvalid);
152
+ Data->SetNumberField(TEXT("numWarnings"), Results.NumWarnings);
153
+ SendSuccess(SendResponse, CorrelationId, Data);
154
+ });
155
+
156
+ // ---------------------------------------------------------------------------
157
+ // validate.folder (VAL-02)
158
+ // Input payload: { "folder_path": "/Game/MyFolder" }
159
+ // Returns: { "folderPath": string, "numAssets": int, "numValid": int,
160
+ // "numInvalid": int, "numWarnings": int }
161
+ //
162
+ // Security: T-16-01 (path validation before IAssetRegistry scan)
163
+ // ---------------------------------------------------------------------------
164
+ Router.RegisterHandler(TEXT("validate.folder"),
165
+ [SendSuccess, SendError](TSharedPtr<FJsonObject> Command, FMCPResponseSender SendResponse)
166
+ {
167
+ FString CorrelationId;
168
+ Command->TryGetStringField(TEXT("correlationId"), CorrelationId);
169
+
170
+ const TSharedPtr<FJsonObject>* PayloadObj = nullptr;
171
+ if (!Command->TryGetObjectField(TEXT("payload"), PayloadObj))
172
+ {
173
+ SendError(SendResponse, CorrelationId, TEXT("missing_payload"));
174
+ return;
175
+ }
176
+
177
+ FString FolderPath;
178
+ if (!(*PayloadObj)->TryGetStringField(TEXT("folder_path"), FolderPath) || FolderPath.IsEmpty())
179
+ {
180
+ SendError(SendResponse, CorrelationId, TEXT("missing_folder_path"));
181
+ return;
182
+ }
183
+
184
+ // T-16-01: Validate the folder path. IsValidLongPackageName rejects traversal paths.
185
+ // Note: folder paths (no asset name suffix) may fail the standard check, so we verify
186
+ // the path starts with a valid mount point prefix instead.
187
+ if (!FolderPath.StartsWith(TEXT("/")))
188
+ {
189
+ UE_LOG(LogTemp, Warning, TEXT("[MCPBridge] validate.folder: Invalid folder path '%s' (must start with '/')"),
190
+ *FolderPath);
191
+ SendError(SendResponse, CorrelationId, TEXT("invalid_folder_path"));
192
+ return;
193
+ }
194
+
195
+ // Additional check: ensure no path traversal sequences.
196
+ if (FolderPath.Contains(TEXT("..")) || FolderPath.Contains(TEXT("\\")))
197
+ {
198
+ UE_LOG(LogTemp, Warning, TEXT("[MCPBridge] validate.folder: Suspicious folder path '%s'"), *FolderPath);
199
+ SendError(SendResponse, CorrelationId, TEXT("invalid_folder_path"));
200
+ return;
201
+ }
202
+
203
+ IAssetRegistry& AssetRegistry = FAssetRegistryModule::GetRegistry();
204
+ TArray<FAssetData> OutAssets;
205
+ AssetRegistry.GetAssetsByPath(FName(*FolderPath), OutAssets, /*bRecursive=*/true);
206
+
207
+ if (OutAssets.Num() == 0)
208
+ {
209
+ UE_LOG(LogTemp, Warning, TEXT("[MCPBridge] validate.folder: No assets found under '%s'"), *FolderPath);
210
+ SendError(SendResponse, CorrelationId, TEXT("folder_not_found"));
211
+ return;
212
+ }
213
+
214
+ UEditorValidatorSubsystem* ValidatorSubsystem = GEditor->GetEditorSubsystem<UEditorValidatorSubsystem>();
215
+ if (!ValidatorSubsystem)
216
+ {
217
+ UE_LOG(LogTemp, Error, TEXT("[MCPBridge] validate.folder: UEditorValidatorSubsystem not available"));
218
+ SendError(SendResponse, CorrelationId, TEXT("validator_not_available"));
219
+ return;
220
+ }
221
+
222
+ FValidateAssetsSettings Settings;
223
+ Settings.bSkipExcludedDirectories = true;
224
+ Settings.bShowIfNoFailures = false;
225
+ Settings.ValidationUsecase = EDataValidationUsecase::Script;
226
+
227
+ FValidateAssetsResults Results;
228
+ ValidatorSubsystem->ValidateAssetsWithResults(OutAssets, Settings, Results);
229
+
230
+ TSharedPtr<FJsonObject> Data = MakeShared<FJsonObject>();
231
+ Data->SetStringField(TEXT("folderPath"), FolderPath);
232
+ Data->SetNumberField(TEXT("numAssets"), Results.NumRequested);
233
+ Data->SetNumberField(TEXT("numValid"), Results.NumValid);
234
+ Data->SetNumberField(TEXT("numInvalid"), Results.NumInvalid);
235
+ Data->SetNumberField(TEXT("numWarnings"), Results.NumWarnings);
236
+ SendSuccess(SendResponse, CorrelationId, Data);
237
+ });
238
+
239
+ // ---------------------------------------------------------------------------
240
+ // validate.project (VAL-03)
241
+ // No required payload fields.
242
+ // Returns: { "numRequested": int, "numValid": int, "numInvalid": int,
243
+ // "numWarnings": int, "numUnableToValidate": int }
244
+ // ---------------------------------------------------------------------------
245
+ Router.RegisterHandler(TEXT("validate.project"),
246
+ [SendSuccess, SendError](TSharedPtr<FJsonObject> Command, FMCPResponseSender SendResponse)
247
+ {
248
+ FString CorrelationId;
249
+ Command->TryGetStringField(TEXT("correlationId"), CorrelationId);
250
+
251
+ IAssetRegistry& AssetRegistry = FAssetRegistryModule::GetRegistry();
252
+ TArray<FAssetData> OutAssets;
253
+ AssetRegistry.GetAssetsByPath(FName(TEXT("/Game")), OutAssets, /*bRecursive=*/true);
254
+
255
+ UEditorValidatorSubsystem* ValidatorSubsystem = GEditor->GetEditorSubsystem<UEditorValidatorSubsystem>();
256
+ if (!ValidatorSubsystem)
257
+ {
258
+ UE_LOG(LogTemp, Error, TEXT("[MCPBridge] validate.project: UEditorValidatorSubsystem not available"));
259
+ SendError(SendResponse, CorrelationId, TEXT("validator_not_available"));
260
+ return;
261
+ }
262
+
263
+ FValidateAssetsSettings Settings;
264
+ Settings.bSkipExcludedDirectories = true;
265
+ Settings.bShowIfNoFailures = false;
266
+ Settings.ValidationUsecase = EDataValidationUsecase::Script;
267
+
268
+ FValidateAssetsResults Results;
269
+ ValidatorSubsystem->ValidateAssetsWithResults(OutAssets, Settings, Results);
270
+
271
+ TSharedPtr<FJsonObject> Data = MakeShared<FJsonObject>();
272
+ Data->SetNumberField(TEXT("numRequested"), Results.NumRequested);
273
+ Data->SetNumberField(TEXT("numValid"), Results.NumValid);
274
+ Data->SetNumberField(TEXT("numInvalid"), Results.NumInvalid);
275
+ Data->SetNumberField(TEXT("numWarnings"), Results.NumWarnings);
276
+ Data->SetNumberField(TEXT("numUnableToValidate"), Results.NumUnableToValidate);
277
+ SendSuccess(SendResponse, CorrelationId, Data);
278
+ });
279
+
280
+ // ---------------------------------------------------------------------------
281
+ // validate.blueprint (VAL-04)
282
+ // Input payload: { "asset_path": "/Game/Blueprints/BP_MyActor" }
283
+ // Returns: { "assetPath": string, "compiled": bool, "status": string, "errorMessage": string }
284
+ //
285
+ // status values: "up_to_date", "dirty", "error", "unknown"
286
+ // compiled = true only when status is "up_to_date"
287
+ //
288
+ // Security: T-16-01 (path validation before LoadObject)
289
+ // T-16-02 (BP->ErrorMessage is user-authored content, safe to surface)
290
+ // ---------------------------------------------------------------------------
291
+ Router.RegisterHandler(TEXT("validate.blueprint"),
292
+ [SendSuccess, SendError](TSharedPtr<FJsonObject> Command, FMCPResponseSender SendResponse)
293
+ {
294
+ FString CorrelationId;
295
+ Command->TryGetStringField(TEXT("correlationId"), CorrelationId);
296
+
297
+ const TSharedPtr<FJsonObject>* PayloadObj = nullptr;
298
+ if (!Command->TryGetObjectField(TEXT("payload"), PayloadObj))
299
+ {
300
+ SendError(SendResponse, CorrelationId, TEXT("missing_payload"));
301
+ return;
302
+ }
303
+
304
+ FString AssetPath;
305
+ if (!(*PayloadObj)->TryGetStringField(TEXT("asset_path"), AssetPath) || AssetPath.IsEmpty())
306
+ {
307
+ SendError(SendResponse, CorrelationId, TEXT("missing_asset_path"));
308
+ return;
309
+ }
310
+
311
+ // T-16-01: Validate the asset path before passing to LoadObject.
312
+ FString ValidationError;
313
+ if (!FPackageName::IsValidLongPackageName(AssetPath, /*bIncludeReadOnlyRoots=*/false, &ValidationError))
314
+ {
315
+ UE_LOG(LogTemp, Warning, TEXT("[MCPBridge] validate.blueprint: Invalid asset path '%s': %s"),
316
+ *AssetPath, *ValidationError);
317
+ SendError(SendResponse, CorrelationId, TEXT("invalid_asset_path"));
318
+ return;
319
+ }
320
+
321
+ UBlueprint* BP = LoadObject<UBlueprint>(nullptr, *AssetPath);
322
+ if (!BP)
323
+ {
324
+ UE_LOG(LogTemp, Warning, TEXT("[MCPBridge] validate.blueprint: Blueprint not found at '%s'"), *AssetPath);
325
+ SendError(SendResponse, CorrelationId, TEXT("asset_not_found"));
326
+ return;
327
+ }
328
+
329
+ // Compile the Blueprint synchronously.
330
+ FKismetEditorUtilities::CompileBlueprint(BP, EBlueprintCompileOptions::None);
331
+
332
+ // Map EBlueprintStatus to a human-readable string.
333
+ // T-16-02: We return named status strings, not raw enum values.
334
+ FString StatusString;
335
+ bool bCompiled = false;
336
+
337
+ switch (BP->Status)
338
+ {
339
+ case BS_UpToDate:
340
+ StatusString = TEXT("up_to_date");
341
+ bCompiled = true;
342
+ break;
343
+ case BS_Dirty:
344
+ StatusString = TEXT("dirty");
345
+ bCompiled = false;
346
+ break;
347
+ case BS_Error:
348
+ StatusString = TEXT("error");
349
+ bCompiled = false;
350
+ break;
351
+ default:
352
+ StatusString = TEXT("unknown");
353
+ bCompiled = false;
354
+ break;
355
+ }
356
+
357
+ // T-16-02: BP->ErrorMessage is the Blueprint's own error message authored during
358
+ // compilation -- safe to surface as it contains no internal UE system paths.
359
+ FString ErrorMessage = BP->ErrorMessage;
360
+
361
+ TSharedPtr<FJsonObject> Data = MakeShared<FJsonObject>();
362
+ Data->SetStringField(TEXT("assetPath"), AssetPath);
363
+ Data->SetBoolField(TEXT("compiled"), bCompiled);
364
+ Data->SetStringField(TEXT("status"), StatusString);
365
+ Data->SetStringField(TEXT("errorMessage"), ErrorMessage);
366
+ SendSuccess(SendResponse, CorrelationId, Data);
367
+ });
368
+ }
@@ -0,0 +1,22 @@
1
+ // MCPValidationCommands.h
2
+ // Declares handler registration for asset validation and Blueprint compile-check commands.
3
+ // Handles: validate.asset, validate.folder, validate.project, validate.blueprint
4
+ //
5
+ // Call RegisterValidationCommands(*Router) in MCPBridgeSubsystem::Initialize()
6
+ // BEFORE the TCP server starts accepting connections.
7
+
8
+ #pragma once
9
+
10
+ #include "MCPCommandRouter.h"
11
+
12
+ /**
13
+ * Register all validation command handlers into the given router.
14
+ * Must be called on the game thread before connections arrive.
15
+ *
16
+ * Registered commands:
17
+ * validate.asset -- run UEditorValidatorSubsystem on a single asset path (VAL-01)
18
+ * validate.folder -- run validation on all assets under a folder path (VAL-02)
19
+ * validate.project -- run validation across the full /Game/ tree (VAL-03)
20
+ * validate.blueprint -- compile a Blueprint and return compiler messages (VAL-04)
21
+ */
22
+ void RegisterValidationCommands(FMCPCommandRouter& Router);