flockbay 0.10.15 → 0.10.16
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.
- package/dist/codex/flockbayMcpStdioBridge.cjs +339 -0
- package/dist/codex/flockbayMcpStdioBridge.mjs +339 -0
- package/dist/{index--o4BPz5o.cjs → index-Cau-_Qvn.cjs} +2683 -609
- package/dist/{index-CUp3juDS.mjs → index-DtmFQzXY.mjs} +2684 -611
- package/dist/index.cjs +3 -5
- package/dist/index.mjs +3 -5
- package/dist/lib.cjs +7 -9
- package/dist/lib.d.cts +219 -531
- package/dist/lib.d.mts +219 -531
- package/dist/lib.mjs +7 -9
- package/dist/{runCodex-o6PCbHQ7.mjs → runCodex-Di9eHddq.mjs} +263 -42
- package/dist/{runCodex-D3eT-TvB.cjs → runCodex-DzP3VUa-.cjs} +264 -43
- package/dist/{runGemini-Bt0oEj_g.mjs → runGemini-BS6sBU_V.mjs} +63 -28
- package/dist/{runGemini-CBxZp6I7.cjs → runGemini-CpmehDQ2.cjs} +64 -29
- package/dist/{types-DGd6ea2Z.mjs → types-CwzNqYEx.mjs} +465 -1142
- package/dist/{types-C-jnUdn_.cjs → types-SUAKq-K0.cjs} +466 -1146
- package/package.json +1 -1
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPBlueprintCommands.cpp +195 -6
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPBlueprintNodeCommands.cpp +376 -5
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPCommandSchema.cpp +731 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPCommonUtils.cpp +476 -8
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPEditorCommands.cpp +1518 -94
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/MCPServerRunnable.cpp +7 -4
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/UnrealMCPBridge.cpp +150 -112
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPBlueprintCommands.h +2 -1
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPBlueprintNodeCommands.h +4 -1
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPCommandSchema.h +42 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPEditorCommands.h +21 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/UnrealMCP.Build.cs +4 -1
- package/dist/flockbayScreenshotGate-DJX3Is5d.mjs +0 -136
- package/dist/flockbayScreenshotGate-DkxU24cR.cjs +0 -138
|
@@ -56,7 +56,7 @@ uint32 FMCPServerRunnable::Run()
|
|
|
56
56
|
ClientSocket->SetSendBufferSize(SocketBufferSize, SocketBufferSize);
|
|
57
57
|
ClientSocket->SetReceiveBufferSize(SocketBufferSize, SocketBufferSize);
|
|
58
58
|
|
|
59
|
-
uint8 Buffer[8192];
|
|
59
|
+
uint8 Buffer[8192 + 1];
|
|
60
60
|
while (bRunning)
|
|
61
61
|
{
|
|
62
62
|
int32 BytesRead = 0;
|
|
@@ -69,7 +69,9 @@ uint32 FMCPServerRunnable::Run()
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// Convert received data to string
|
|
72
|
-
|
|
72
|
+
// Null-terminate safely (Recv can fill the entire buffer).
|
|
73
|
+
const int32 SafeIndex = FMath::Clamp(BytesRead, 0, 8192);
|
|
74
|
+
Buffer[SafeIndex] = '\0';
|
|
73
75
|
FString ReceivedText = UTF8_TO_TCHAR(Buffer);
|
|
74
76
|
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Received: %s"), *ReceivedText);
|
|
75
77
|
|
|
@@ -90,8 +92,9 @@ uint32 FMCPServerRunnable::Run()
|
|
|
90
92
|
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Sending response: %s"), *Response);
|
|
91
93
|
|
|
92
94
|
// Send response
|
|
95
|
+
FTCHARToUTF8 Utf8(*Response);
|
|
93
96
|
int32 BytesSent = 0;
|
|
94
|
-
if (!ClientSocket->Send((uint8*)
|
|
97
|
+
if (!ClientSocket->Send((uint8*)Utf8.Get(), Utf8.Length(), BytesSent))
|
|
95
98
|
{
|
|
96
99
|
UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Failed to send response"));
|
|
97
100
|
}
|
|
@@ -318,4 +321,4 @@ void FMCPServerRunnable::ProcessMessage(TSharedPtr<FSocket> Client, const FStrin
|
|
|
318
321
|
{
|
|
319
322
|
UE_LOG(LogTemp, Error, TEXT("MCPServerRunnable: Failed to send response"));
|
|
320
323
|
}
|
|
321
|
-
}
|
|
324
|
+
}
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
#include "Commands/UnrealMCPProjectCommands.h"
|
|
58
58
|
#include "Commands/UnrealMCPCommonUtils.h"
|
|
59
59
|
#include "Commands/UnrealMCPUMGCommands.h"
|
|
60
|
+
#include "Commands/UnrealMCPCommandSchema.h"
|
|
60
61
|
#include "Interfaces/IPluginManager.h"
|
|
61
62
|
|
|
62
63
|
// Default settings
|
|
@@ -215,15 +216,31 @@ FString UUnrealMCPBridge::ExecuteCommand(const FString& CommandType, const TShar
|
|
|
215
216
|
{
|
|
216
217
|
TSharedPtr<FJsonObject> ResponseJson = MakeShareable(new FJsonObject);
|
|
217
218
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
219
|
+
try
|
|
220
|
+
{
|
|
221
|
+
TSharedPtr<FJsonObject> ResultJson;
|
|
222
|
+
|
|
223
|
+
// Bridge-level helpers (keep these in the bridge so they always match the running plugin build).
|
|
224
|
+
if (CommandType == TEXT("list_capabilities"))
|
|
225
|
+
{
|
|
226
|
+
ResultJson = FUnrealMCPCommandSchemaRegistry::GetCapabilitiesResponse();
|
|
227
|
+
}
|
|
228
|
+
else if (CommandType == TEXT("get_command_schema"))
|
|
229
|
+
{
|
|
230
|
+
FString Requested;
|
|
231
|
+
if (Params.IsValid())
|
|
232
|
+
{
|
|
233
|
+
Params->TryGetStringField(TEXT("command"), Requested);
|
|
234
|
+
Params->TryGetStringField(TEXT("type"), Requested);
|
|
235
|
+
}
|
|
236
|
+
Requested = Requested.TrimStartAndEnd();
|
|
237
|
+
ResultJson = FUnrealMCPCommandSchemaRegistry::GetSchemaResponse(Requested);
|
|
238
|
+
}
|
|
239
|
+
else if (CommandType == TEXT("ping"))
|
|
240
|
+
{
|
|
241
|
+
ResultJson = MakeShareable(new FJsonObject);
|
|
242
|
+
ResultJson->SetStringField(TEXT("message"), TEXT("pong"));
|
|
243
|
+
}
|
|
227
244
|
else if (CommandType == TEXT("get_plugin_info"))
|
|
228
245
|
{
|
|
229
246
|
TSharedPtr<IPlugin> Plugin = IPluginManager::Get().FindPlugin(TEXT("UnrealMCP"));
|
|
@@ -245,71 +262,26 @@ FString UUnrealMCPBridge::ExecuteCommand(const FString& CommandType, const TShar
|
|
|
245
262
|
ResultJson->SetStringField(TEXT("name"), Plugin->GetName());
|
|
246
263
|
ResultJson->SetStringField(TEXT("baseDir"), Plugin->GetBaseDir());
|
|
247
264
|
ResultJson->SetStringField(TEXT("friendlyName"), Descriptor.FriendlyName);
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("delete_actor"))));
|
|
264
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("set_actor_transform"))));
|
|
265
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("get_actor_properties"))));
|
|
266
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("set_actor_property"))));
|
|
267
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("spawn_blueprint_actor"))));
|
|
268
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("focus_viewport"))));
|
|
269
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("take_screenshot"))));
|
|
270
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("get_play_in_editor_status"))));
|
|
271
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("play_in_editor"))));
|
|
272
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("play_in_editor_windowed"))));
|
|
273
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("stop_play_in_editor"))));
|
|
274
|
-
|
|
275
|
-
// Blueprint commands
|
|
276
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("create_blueprint"))));
|
|
277
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("add_component_to_blueprint"))));
|
|
278
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("set_component_property"))));
|
|
279
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("set_physics_properties"))));
|
|
280
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("compile_blueprint"))));
|
|
281
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("set_blueprint_property"))));
|
|
282
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("set_static_mesh_properties"))));
|
|
283
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("set_pawn_properties"))));
|
|
284
|
-
|
|
285
|
-
// Blueprint node commands
|
|
286
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("connect_blueprint_nodes"))));
|
|
287
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("add_blueprint_get_self_component_reference"))));
|
|
288
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("add_blueprint_self_reference"))));
|
|
289
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("find_blueprint_nodes"))));
|
|
290
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("add_blueprint_event_node"))));
|
|
291
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("add_blueprint_input_action_node"))));
|
|
292
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("add_blueprint_function_node"))));
|
|
293
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("add_blueprint_get_component_node"))));
|
|
294
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("add_blueprint_variable"))));
|
|
295
|
-
|
|
296
|
-
// Project commands
|
|
297
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("create_input_mapping"))));
|
|
298
|
-
|
|
299
|
-
// UMG commands
|
|
300
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("create_umg_widget_blueprint"))));
|
|
301
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("add_text_block_to_widget"))));
|
|
302
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("add_button_to_widget"))));
|
|
303
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("bind_widget_event"))));
|
|
304
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("set_text_block_binding"))));
|
|
305
|
-
Commands.Add(MakeShareable(new FJsonValueString(TEXT("add_widget_to_viewport"))));
|
|
306
|
-
|
|
307
|
-
ResultJson->SetArrayField(TEXT("commands"), Commands);
|
|
308
|
-
}
|
|
265
|
+
ResultJson->SetStringField(TEXT("createdBy"), Descriptor.CreatedBy);
|
|
266
|
+
ResultJson->SetStringField(TEXT("versionName"), Descriptor.VersionName);
|
|
267
|
+
ResultJson->SetNumberField(TEXT("version"), Descriptor.Version);
|
|
268
|
+
ResultJson->SetBoolField(TEXT("installed"), Descriptor.bInstalled);
|
|
269
|
+
ResultJson->SetNumberField(TEXT("schemaVersion"), FUnrealMCPCommandSchemaRegistry::SchemaVersion());
|
|
270
|
+
|
|
271
|
+
// Keep this list explicit so it always matches the exact build running in the editor.
|
|
272
|
+
TArray<TSharedPtr<FJsonValue>> Commands;
|
|
273
|
+
for (const FString& Cmd : FUnrealMCPCommandSchemaRegistry::GetSupportedCommands())
|
|
274
|
+
{
|
|
275
|
+
Commands.Add(MakeShareable(new FJsonValueString(Cmd)));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
ResultJson->SetArrayField(TEXT("commands"), Commands);
|
|
279
|
+
}
|
|
309
280
|
// Editor Commands (including actor manipulation)
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
281
|
+
else if (CommandType == TEXT("get_actors_in_level") ||
|
|
282
|
+
CommandType == TEXT("find_actors_by_name") ||
|
|
283
|
+
CommandType == TEXT("spawn_actor") ||
|
|
284
|
+
CommandType == TEXT("create_landscape") ||
|
|
313
285
|
CommandType == TEXT("create_actor") ||
|
|
314
286
|
CommandType == TEXT("delete_actor") ||
|
|
315
287
|
CommandType == TEXT("set_actor_transform") ||
|
|
@@ -318,53 +290,113 @@ FString UUnrealMCPBridge::ExecuteCommand(const FString& CommandType, const TShar
|
|
|
318
290
|
CommandType == TEXT("spawn_blueprint_actor") ||
|
|
319
291
|
CommandType == TEXT("focus_viewport") ||
|
|
320
292
|
CommandType == TEXT("take_screenshot") ||
|
|
293
|
+
CommandType == TEXT("save_all") ||
|
|
321
294
|
CommandType == TEXT("get_play_in_editor_status") ||
|
|
322
295
|
CommandType == TEXT("play_in_editor") ||
|
|
323
296
|
CommandType == TEXT("play_in_editor_windowed") ||
|
|
324
|
-
CommandType == TEXT("stop_play_in_editor")
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
297
|
+
CommandType == TEXT("stop_play_in_editor") ||
|
|
298
|
+
CommandType == TEXT("search_assets") ||
|
|
299
|
+
CommandType == TEXT("get_asset_info") ||
|
|
300
|
+
CommandType == TEXT("list_asset_packs") ||
|
|
301
|
+
CommandType == TEXT("place_asset") ||
|
|
302
|
+
CommandType == TEXT("map_check") ||
|
|
303
|
+
CommandType == TEXT("get_editor_context") ||
|
|
304
|
+
CommandType == TEXT("get_player_context") ||
|
|
305
|
+
CommandType == TEXT("raycast_from_camera") ||
|
|
306
|
+
CommandType == TEXT("raycast_down") ||
|
|
307
|
+
CommandType == TEXT("get_actor_transform") ||
|
|
308
|
+
CommandType == TEXT("get_actor_bounds"))
|
|
309
|
+
{
|
|
310
|
+
TSharedPtr<FJsonObject> NormalizedParams;
|
|
311
|
+
TSharedPtr<FJsonObject> SchemaError;
|
|
312
|
+
if (!FUnrealMCPCommandSchemaRegistry::NormalizeParams(CommandType, Params, NormalizedParams, SchemaError))
|
|
313
|
+
{
|
|
314
|
+
ResultJson = SchemaError;
|
|
315
|
+
}
|
|
316
|
+
else
|
|
317
|
+
{
|
|
318
|
+
ResultJson = EditorCommands->HandleCommand(CommandType, NormalizedParams);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
328
321
|
// Blueprint Commands
|
|
329
322
|
else if (CommandType == TEXT("create_blueprint") ||
|
|
330
323
|
CommandType == TEXT("add_component_to_blueprint") ||
|
|
331
324
|
CommandType == TEXT("set_component_property") ||
|
|
332
325
|
CommandType == TEXT("set_physics_properties") ||
|
|
333
326
|
CommandType == TEXT("compile_blueprint") ||
|
|
327
|
+
CommandType == TEXT("compile_blueprints_all") ||
|
|
334
328
|
CommandType == TEXT("set_blueprint_property") ||
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
329
|
+
CommandType == TEXT("set_static_mesh_properties") ||
|
|
330
|
+
CommandType == TEXT("set_pawn_properties"))
|
|
331
|
+
{
|
|
332
|
+
TSharedPtr<FJsonObject> NormalizedParams;
|
|
333
|
+
TSharedPtr<FJsonObject> SchemaError;
|
|
334
|
+
if (!FUnrealMCPCommandSchemaRegistry::NormalizeParams(CommandType, Params, NormalizedParams, SchemaError))
|
|
335
|
+
{
|
|
336
|
+
ResultJson = SchemaError;
|
|
337
|
+
}
|
|
338
|
+
else
|
|
339
|
+
{
|
|
340
|
+
ResultJson = BlueprintCommands->HandleCommand(CommandType, NormalizedParams);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
// Blueprint Node Commands
|
|
344
|
+
else if (CommandType == TEXT("connect_blueprint_nodes") ||
|
|
345
|
+
CommandType == TEXT("add_blueprint_get_self_component_reference") ||
|
|
346
|
+
CommandType == TEXT("add_blueprint_self_reference") ||
|
|
347
|
+
CommandType == TEXT("find_blueprint_nodes") ||
|
|
348
|
+
CommandType == TEXT("add_blueprint_event_node") ||
|
|
349
|
+
CommandType == TEXT("add_blueprint_input_action_node") ||
|
|
350
|
+
CommandType == TEXT("add_blueprint_function_node") ||
|
|
351
|
+
CommandType == TEXT("add_blueprint_get_component_node") ||
|
|
352
|
+
CommandType == TEXT("add_blueprint_variable") ||
|
|
353
|
+
CommandType == TEXT("search_blueprint_functions") ||
|
|
354
|
+
CommandType == TEXT("resolve_blueprint_function"))
|
|
355
|
+
{
|
|
356
|
+
TSharedPtr<FJsonObject> NormalizedParams;
|
|
357
|
+
TSharedPtr<FJsonObject> SchemaError;
|
|
358
|
+
if (!FUnrealMCPCommandSchemaRegistry::NormalizeParams(CommandType, Params, NormalizedParams, SchemaError))
|
|
359
|
+
{
|
|
360
|
+
ResultJson = SchemaError;
|
|
361
|
+
}
|
|
362
|
+
else
|
|
363
|
+
{
|
|
364
|
+
ResultJson = BlueprintNodeCommands->HandleCommand(CommandType, NormalizedParams);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// Project Commands
|
|
368
|
+
else if (CommandType == TEXT("create_input_mapping"))
|
|
369
|
+
{
|
|
370
|
+
TSharedPtr<FJsonObject> NormalizedParams;
|
|
371
|
+
TSharedPtr<FJsonObject> SchemaError;
|
|
372
|
+
if (!FUnrealMCPCommandSchemaRegistry::NormalizeParams(CommandType, Params, NormalizedParams, SchemaError))
|
|
373
|
+
{
|
|
374
|
+
ResultJson = SchemaError;
|
|
375
|
+
}
|
|
376
|
+
else
|
|
377
|
+
{
|
|
378
|
+
ResultJson = ProjectCommands->HandleCommand(CommandType, NormalizedParams);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
358
381
|
// UMG Commands
|
|
359
382
|
else if (CommandType == TEXT("create_umg_widget_blueprint") ||
|
|
360
383
|
CommandType == TEXT("add_text_block_to_widget") ||
|
|
361
384
|
CommandType == TEXT("add_button_to_widget") ||
|
|
362
385
|
CommandType == TEXT("bind_widget_event") ||
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
386
|
+
CommandType == TEXT("set_text_block_binding") ||
|
|
387
|
+
CommandType == TEXT("add_widget_to_viewport"))
|
|
388
|
+
{
|
|
389
|
+
TSharedPtr<FJsonObject> NormalizedParams;
|
|
390
|
+
TSharedPtr<FJsonObject> SchemaError;
|
|
391
|
+
if (!FUnrealMCPCommandSchemaRegistry::NormalizeParams(CommandType, Params, NormalizedParams, SchemaError))
|
|
392
|
+
{
|
|
393
|
+
ResultJson = SchemaError;
|
|
394
|
+
}
|
|
395
|
+
else
|
|
396
|
+
{
|
|
397
|
+
ResultJson = UMGCommands->HandleCommand(CommandType, NormalizedParams);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
368
400
|
else
|
|
369
401
|
{
|
|
370
402
|
ResponseJson->SetStringField(TEXT("status"), TEXT("error"));
|
|
@@ -396,13 +428,19 @@ FString UUnrealMCPBridge::ExecuteCommand(const FString& CommandType, const TShar
|
|
|
396
428
|
ResponseJson->SetStringField(TEXT("status"), TEXT("success"));
|
|
397
429
|
ResponseJson->SetObjectField(TEXT("result"), ResultJson);
|
|
398
430
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
431
|
+
else
|
|
432
|
+
{
|
|
433
|
+
// Set error status and include the error message
|
|
434
|
+
ResponseJson->SetStringField(TEXT("status"), TEXT("error"));
|
|
435
|
+
ResponseJson->SetStringField(TEXT("error"), ErrorMessage);
|
|
436
|
+
|
|
437
|
+
// Preserve structured error details when present.
|
|
438
|
+
if (ResultJson.IsValid() && ResultJson->HasTypedField<EJson::Object>(TEXT("details")))
|
|
439
|
+
{
|
|
440
|
+
ResponseJson->SetObjectField(TEXT("details"), ResultJson->GetObjectField(TEXT("details")));
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
406
444
|
catch (const std::exception& e)
|
|
407
445
|
{
|
|
408
446
|
ResponseJson->SetStringField(TEXT("status"), TEXT("error"));
|
|
@@ -21,6 +21,7 @@ private:
|
|
|
21
21
|
TSharedPtr<FJsonObject> HandleSetComponentProperty(const TSharedPtr<FJsonObject>& Params);
|
|
22
22
|
TSharedPtr<FJsonObject> HandleSetPhysicsProperties(const TSharedPtr<FJsonObject>& Params);
|
|
23
23
|
TSharedPtr<FJsonObject> HandleCompileBlueprint(const TSharedPtr<FJsonObject>& Params);
|
|
24
|
+
TSharedPtr<FJsonObject> HandleCompileBlueprintsAll(const TSharedPtr<FJsonObject>& Params);
|
|
24
25
|
TSharedPtr<FJsonObject> HandleSpawnBlueprintActor(const TSharedPtr<FJsonObject>& Params);
|
|
25
26
|
TSharedPtr<FJsonObject> HandleSetBlueprintProperty(const TSharedPtr<FJsonObject>& Params);
|
|
26
27
|
TSharedPtr<FJsonObject> HandleSetStaticMeshProperties(const TSharedPtr<FJsonObject>& Params);
|
|
@@ -31,4 +32,4 @@ private:
|
|
|
31
32
|
const FString& ComponentName, const FString& MeshType,
|
|
32
33
|
const TArray<float>& Location, const TArray<float>& Rotation,
|
|
33
34
|
const TArray<float>& Scale, const TSharedPtr<FJsonObject>& ComponentProperties);
|
|
34
|
-
};
|
|
35
|
+
};
|
|
@@ -18,10 +18,13 @@ private:
|
|
|
18
18
|
// Specific blueprint node command handlers
|
|
19
19
|
TSharedPtr<FJsonObject> HandleConnectBlueprintNodes(const TSharedPtr<FJsonObject>& Params);
|
|
20
20
|
TSharedPtr<FJsonObject> HandleAddBlueprintGetSelfComponentReference(const TSharedPtr<FJsonObject>& Params);
|
|
21
|
+
TSharedPtr<FJsonObject> HandleAddBlueprintGetComponentNode(const TSharedPtr<FJsonObject>& Params);
|
|
21
22
|
TSharedPtr<FJsonObject> HandleAddBlueprintEvent(const TSharedPtr<FJsonObject>& Params);
|
|
22
23
|
TSharedPtr<FJsonObject> HandleAddBlueprintFunctionCall(const TSharedPtr<FJsonObject>& Params);
|
|
23
24
|
TSharedPtr<FJsonObject> HandleAddBlueprintVariable(const TSharedPtr<FJsonObject>& Params);
|
|
24
25
|
TSharedPtr<FJsonObject> HandleAddBlueprintInputActionNode(const TSharedPtr<FJsonObject>& Params);
|
|
25
26
|
TSharedPtr<FJsonObject> HandleAddBlueprintSelfReference(const TSharedPtr<FJsonObject>& Params);
|
|
26
27
|
TSharedPtr<FJsonObject> HandleFindBlueprintNodes(const TSharedPtr<FJsonObject>& Params);
|
|
27
|
-
|
|
28
|
+
TSharedPtr<FJsonObject> HandleSearchBlueprintFunctions(const TSharedPtr<FJsonObject>& Params);
|
|
29
|
+
TSharedPtr<FJsonObject> HandleResolveBlueprintFunction(const TSharedPtr<FJsonObject>& Params);
|
|
30
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "CoreMinimal.h"
|
|
4
|
+
#include "Dom/JsonObject.h"
|
|
5
|
+
|
|
6
|
+
struct FUnrealMCPParamSchema
|
|
7
|
+
{
|
|
8
|
+
FString Name;
|
|
9
|
+
FString Type;
|
|
10
|
+
bool bRequired = false;
|
|
11
|
+
FString Description;
|
|
12
|
+
TArray<FString> Aliases;
|
|
13
|
+
TArray<FString> EnumValues;
|
|
14
|
+
TMap<FString, FString> ValueAliases;
|
|
15
|
+
|
|
16
|
+
TSharedPtr<FJsonObject> ToJson() const;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
struct FUnrealMCPCommandSchema
|
|
20
|
+
{
|
|
21
|
+
FString Command;
|
|
22
|
+
FString Title;
|
|
23
|
+
FString Description;
|
|
24
|
+
TArray<FUnrealMCPParamSchema> Params;
|
|
25
|
+
TSharedPtr<FJsonObject> ExampleParams;
|
|
26
|
+
|
|
27
|
+
TSharedPtr<FJsonObject> ToJson() const;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
class UNREALMCP_API FUnrealMCPCommandSchemaRegistry
|
|
31
|
+
{
|
|
32
|
+
public:
|
|
33
|
+
static int32 SchemaVersion();
|
|
34
|
+
static const FUnrealMCPCommandSchema* FindSchema(const FString& CommandType);
|
|
35
|
+
static TArray<FString> GetSupportedCommands();
|
|
36
|
+
|
|
37
|
+
// Returns { success:false, error, details:{...} } on validation issues.
|
|
38
|
+
static bool NormalizeParams(const FString& CommandType, const TSharedPtr<FJsonObject>& InParams, TSharedPtr<FJsonObject>& OutParams, TSharedPtr<FJsonObject>& OutError);
|
|
39
|
+
static TSharedPtr<FJsonObject> GetSchemaResponse(const FString& CommandTypeOrEmpty);
|
|
40
|
+
static TSharedPtr<FJsonObject> GetCapabilitiesResponse();
|
|
41
|
+
};
|
|
42
|
+
|
|
@@ -20,6 +20,7 @@ private:
|
|
|
20
20
|
TSharedPtr<FJsonObject> HandleGetActorsInLevel(const TSharedPtr<FJsonObject>& Params);
|
|
21
21
|
TSharedPtr<FJsonObject> HandleFindActorsByName(const TSharedPtr<FJsonObject>& Params);
|
|
22
22
|
TSharedPtr<FJsonObject> HandleSpawnActor(const TSharedPtr<FJsonObject>& Params);
|
|
23
|
+
TSharedPtr<FJsonObject> HandleCreateLandscape(const TSharedPtr<FJsonObject>& Params);
|
|
23
24
|
TSharedPtr<FJsonObject> HandleDeleteActor(const TSharedPtr<FJsonObject>& Params);
|
|
24
25
|
TSharedPtr<FJsonObject> HandleSetActorTransform(const TSharedPtr<FJsonObject>& Params);
|
|
25
26
|
TSharedPtr<FJsonObject> HandleGetActorProperties(const TSharedPtr<FJsonObject>& Params);
|
|
@@ -32,9 +33,29 @@ private:
|
|
|
32
33
|
TSharedPtr<FJsonObject> HandleFocusViewport(const TSharedPtr<FJsonObject>& Params);
|
|
33
34
|
TSharedPtr<FJsonObject> HandleTakeScreenshot(const TSharedPtr<FJsonObject>& Params);
|
|
34
35
|
|
|
36
|
+
// Editor save commands
|
|
37
|
+
TSharedPtr<FJsonObject> HandleSaveAll(const TSharedPtr<FJsonObject>& Params);
|
|
38
|
+
|
|
35
39
|
// Editor play/preview commands
|
|
36
40
|
TSharedPtr<FJsonObject> HandleGetPlayInEditorStatus(const TSharedPtr<FJsonObject>& Params);
|
|
37
41
|
TSharedPtr<FJsonObject> HandlePlayInEditor(const TSharedPtr<FJsonObject>& Params);
|
|
38
42
|
TSharedPtr<FJsonObject> HandlePlayInEditorWindowed(const TSharedPtr<FJsonObject>& Params);
|
|
39
43
|
TSharedPtr<FJsonObject> HandleStopPlayInEditor(const TSharedPtr<FJsonObject>& Params);
|
|
44
|
+
|
|
45
|
+
// Asset discovery + placement
|
|
46
|
+
TSharedPtr<FJsonObject> HandleSearchAssets(const TSharedPtr<FJsonObject>& Params);
|
|
47
|
+
TSharedPtr<FJsonObject> HandleGetAssetInfo(const TSharedPtr<FJsonObject>& Params);
|
|
48
|
+
TSharedPtr<FJsonObject> HandleListAssetPacks(const TSharedPtr<FJsonObject>& Params);
|
|
49
|
+
TSharedPtr<FJsonObject> HandlePlaceAsset(const TSharedPtr<FJsonObject>& Params);
|
|
50
|
+
|
|
51
|
+
// Verify (editor diagnostics)
|
|
52
|
+
TSharedPtr<FJsonObject> HandleMapCheck(const TSharedPtr<FJsonObject>& Params);
|
|
53
|
+
|
|
54
|
+
// Context + spatial primitives
|
|
55
|
+
TSharedPtr<FJsonObject> HandleGetEditorContext(const TSharedPtr<FJsonObject>& Params);
|
|
56
|
+
TSharedPtr<FJsonObject> HandleGetPlayerContext(const TSharedPtr<FJsonObject>& Params);
|
|
57
|
+
TSharedPtr<FJsonObject> HandleRaycastFromCamera(const TSharedPtr<FJsonObject>& Params);
|
|
58
|
+
TSharedPtr<FJsonObject> HandleRaycastDown(const TSharedPtr<FJsonObject>& Params);
|
|
59
|
+
TSharedPtr<FJsonObject> HandleGetActorTransform(const TSharedPtr<FJsonObject>& Params);
|
|
60
|
+
TSharedPtr<FJsonObject> HandleGetActorBounds(const TSharedPtr<FJsonObject>& Params);
|
|
40
61
|
};
|
|
@@ -44,8 +44,10 @@ public class UnrealMCP : ModuleRules
|
|
|
44
44
|
"UnrealEd",
|
|
45
45
|
"EditorScriptingUtilities",
|
|
46
46
|
"EditorSubsystem",
|
|
47
|
+
"Landscape",
|
|
47
48
|
"Slate",
|
|
48
49
|
"SlateCore",
|
|
50
|
+
"MessageLog",
|
|
49
51
|
"UMG",
|
|
50
52
|
"Kismet",
|
|
51
53
|
"KismetCompiler",
|
|
@@ -60,6 +62,7 @@ public class UnrealMCP : ModuleRules
|
|
|
60
62
|
PrivateDependencyModuleNames.AddRange(
|
|
61
63
|
new string[]
|
|
62
64
|
{
|
|
65
|
+
"LandscapeEditor",
|
|
63
66
|
"PropertyEditor", // For widget property editing
|
|
64
67
|
"ToolMenus", // For editor UI
|
|
65
68
|
"BlueprintEditorLibrary", // For Blueprint utilities
|
|
@@ -75,4 +78,4 @@ public class UnrealMCP : ModuleRules
|
|
|
75
78
|
}
|
|
76
79
|
);
|
|
77
80
|
}
|
|
78
|
-
}
|
|
81
|
+
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
|
|
3
|
-
function uniqPush(into, seen, value) {
|
|
4
|
-
const v = value.trim();
|
|
5
|
-
if (!v) return;
|
|
6
|
-
if (seen.has(v)) return;
|
|
7
|
-
seen.add(v);
|
|
8
|
-
into.push(v);
|
|
9
|
-
}
|
|
10
|
-
function normalizeFilePathToken(token) {
|
|
11
|
-
return token.trim().replace(/^['"`]+/, "").replace(/['"`]+$/, "").replace(/[),.;:'"`]+$/, "").trim();
|
|
12
|
-
}
|
|
13
|
-
function resolveCandidatePath(candidate, cwd) {
|
|
14
|
-
const raw = normalizeFilePathToken(candidate);
|
|
15
|
-
if (!raw) return raw;
|
|
16
|
-
if (raw.startsWith("~/")) {
|
|
17
|
-
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
18
|
-
if (home) return path.join(home, raw.slice(2));
|
|
19
|
-
}
|
|
20
|
-
return path.isAbsolute(raw) ? raw : path.resolve(cwd, raw);
|
|
21
|
-
}
|
|
22
|
-
function isImageBlock(block) {
|
|
23
|
-
if (!block || typeof block !== "object") return false;
|
|
24
|
-
if (block.type !== "image") return false;
|
|
25
|
-
if (typeof block.data === "string" && block.data.length > 0) return true;
|
|
26
|
-
if (block.source && typeof block.source === "object" && typeof block.source.data === "string" && block.source.data.length > 0) return true;
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
function tryParseJsonObjectWithViews(text) {
|
|
30
|
-
const trimmed = text.trim();
|
|
31
|
-
if (!trimmed) return null;
|
|
32
|
-
try {
|
|
33
|
-
const direct = JSON.parse(trimmed);
|
|
34
|
-
if (direct && typeof direct === "object" && Array.isArray(direct.views)) return direct;
|
|
35
|
-
} catch {
|
|
36
|
-
}
|
|
37
|
-
for (let i = trimmed.length - 1; i >= 0; i -= 1) {
|
|
38
|
-
if (trimmed[i] !== "{") continue;
|
|
39
|
-
const candidate = trimmed.slice(i);
|
|
40
|
-
try {
|
|
41
|
-
const parsed = JSON.parse(candidate);
|
|
42
|
-
if (parsed && typeof parsed === "object" && Array.isArray(parsed.views)) return parsed;
|
|
43
|
-
} catch {
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
function extractScreenshotViewsFromText(text, cwd) {
|
|
49
|
-
const out = [];
|
|
50
|
-
const seen = /* @__PURE__ */ new Set();
|
|
51
|
-
const re = /(?:^|[\s"'(])(?:-\s*)?([^\s"'()]*Saved[\\/]+Screenshots[\\/]+Flockbay[\\/]+[^\s"'()]+\.(?:png|jpg|jpeg))/gi;
|
|
52
|
-
for (const match of text.matchAll(re)) {
|
|
53
|
-
const token = String(match[1] || "");
|
|
54
|
-
const resolved = resolveCandidatePath(token, cwd);
|
|
55
|
-
if (!resolved) continue;
|
|
56
|
-
if (!resolved.includes(`${path.sep}Saved${path.sep}Screenshots${path.sep}Flockbay${path.sep}`) && !resolved.includes("Saved/Screenshots/Flockbay/") && !resolved.includes("Saved\\Screenshots\\Flockbay\\")) {
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
uniqPush(out, seen, resolved);
|
|
60
|
-
}
|
|
61
|
-
if (out.length === 0 && (text.includes("Saved/Screenshots/Flockbay") || text.includes("Saved\\Screenshots\\Flockbay"))) {
|
|
62
|
-
const filenameRe = /(?:^|[\s"'(\\/])(?:-\s*)?(Flockbay_[A-Za-z0-9_.-]+\.(?:png|jpg|jpeg))/gi;
|
|
63
|
-
for (const match of text.matchAll(filenameRe)) {
|
|
64
|
-
const filename = normalizeFilePathToken(String(match[1] || ""));
|
|
65
|
-
if (!filename) continue;
|
|
66
|
-
const rel = path.join("Saved", "Screenshots", "Flockbay", filename);
|
|
67
|
-
const resolved = resolveCandidatePath(rel, cwd);
|
|
68
|
-
uniqPush(out, seen, resolved);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return out;
|
|
72
|
-
}
|
|
73
|
-
function extractViewsFromParsedJson(parsed, cwd) {
|
|
74
|
-
const out = [];
|
|
75
|
-
const seen = /* @__PURE__ */ new Set();
|
|
76
|
-
const views = Array.isArray(parsed?.views) ? parsed.views : [];
|
|
77
|
-
for (const v of views) {
|
|
78
|
-
const p = typeof v?.path === "string" ? v.path : "";
|
|
79
|
-
if (!p.trim()) continue;
|
|
80
|
-
uniqPush(out, seen, resolveCandidatePath(p, cwd));
|
|
81
|
-
}
|
|
82
|
-
return out;
|
|
83
|
-
}
|
|
84
|
-
function extractFromContentBlocks(content, cwd) {
|
|
85
|
-
const texts = [];
|
|
86
|
-
let hasImages = false;
|
|
87
|
-
for (const block of content) {
|
|
88
|
-
if (block && typeof block === "object" && block.type === "text" && typeof block.text === "string") {
|
|
89
|
-
texts.push(block.text);
|
|
90
|
-
}
|
|
91
|
-
if (!hasImages && isImageBlock(block)) hasImages = true;
|
|
92
|
-
}
|
|
93
|
-
const text = texts.join("\n");
|
|
94
|
-
const parsed = tryParseJsonObjectWithViews(text);
|
|
95
|
-
const jsonPaths = parsed ? extractViewsFromParsedJson(parsed, cwd) : [];
|
|
96
|
-
const pathMatches = extractScreenshotViewsFromText(text, cwd);
|
|
97
|
-
const combined = [];
|
|
98
|
-
const seen = /* @__PURE__ */ new Set();
|
|
99
|
-
for (const p of jsonPaths) uniqPush(combined, seen, p);
|
|
100
|
-
for (const p of pathMatches) uniqPush(combined, seen, p);
|
|
101
|
-
return { paths: combined, hasImages };
|
|
102
|
-
}
|
|
103
|
-
function detectScreenshotsForGate(args) {
|
|
104
|
-
const cwd = args.cwd && args.cwd.trim().length > 0 ? args.cwd : process.cwd();
|
|
105
|
-
const output = args.output;
|
|
106
|
-
if (Array.isArray(output)) {
|
|
107
|
-
const extracted = extractFromContentBlocks(output, cwd);
|
|
108
|
-
return { paths: extracted.paths, hasImageBlocks: extracted.hasImages };
|
|
109
|
-
}
|
|
110
|
-
if (output && typeof output === "object" && Array.isArray(output.content)) {
|
|
111
|
-
const extracted = extractFromContentBlocks(output.content, cwd);
|
|
112
|
-
return { paths: extracted.paths, hasImageBlocks: extracted.hasImages };
|
|
113
|
-
}
|
|
114
|
-
if (output && typeof output === "object" && Array.isArray(output.views)) {
|
|
115
|
-
return { paths: extractViewsFromParsedJson(output, cwd), hasImageBlocks: false };
|
|
116
|
-
}
|
|
117
|
-
const candidates = [
|
|
118
|
-
typeof output === "string" ? output : null,
|
|
119
|
-
typeof output?.stdout === "string" ? output.stdout : null,
|
|
120
|
-
typeof output?.stderr === "string" ? output.stderr : null,
|
|
121
|
-
typeof output?.output === "string" ? output.output : null,
|
|
122
|
-
typeof output?.message === "string" ? output.message : null
|
|
123
|
-
];
|
|
124
|
-
const combinedText = candidates.filter((v) => typeof v === "string" && v.trim().length > 0).join("\n");
|
|
125
|
-
if (!combinedText) return { paths: [], hasImageBlocks: false };
|
|
126
|
-
const parsed = tryParseJsonObjectWithViews(combinedText);
|
|
127
|
-
const jsonPaths = parsed ? extractViewsFromParsedJson(parsed, cwd) : [];
|
|
128
|
-
const pathMatches = extractScreenshotViewsFromText(combinedText, cwd);
|
|
129
|
-
const out = [];
|
|
130
|
-
const seen = /* @__PURE__ */ new Set();
|
|
131
|
-
for (const p of jsonPaths) uniqPush(out, seen, p);
|
|
132
|
-
for (const p of pathMatches) uniqPush(out, seen, p);
|
|
133
|
-
return { paths: out, hasImageBlocks: false };
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export { detectScreenshotsForGate as d };
|