ue-mcp 1.2.1 → 1.2.2
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/context-cli.js +16 -16
- package/dist/engine-observer.js +68 -68
- package/dist/flow/guard.d.ts +26 -37
- package/dist/flow/guard.js +40 -25
- package/dist/flow/guard.js.map +1 -1
- package/dist/flow/guarded-bridge.d.ts +0 -10
- package/dist/flow/guarded-bridge.js +18 -23
- package/dist/flow/guarded-bridge.js.map +1 -1
- package/dist/flow/schema.d.ts +276 -16
- package/dist/flow/task-guards.d.ts +1 -1
- package/dist/flow/task-guards.js +44 -68
- package/dist/flow/task-guards.js.map +1 -1
- package/dist/instructions.js +130 -130
- package/dist/plugin-cli.js +299 -299
- package/dist/tool-counts.json +2 -2
- package/package.json +2 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/EngineStatusHooks.cpp +76 -76
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/EngineStatusHooks.h +27 -27
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerRegistry.cpp +128 -128
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerRegistry.h +63 -63
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRig.cpp +230 -230
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Struct.cpp +323 -323
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Internal.h +20 -20
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/ChooserHandlers_Nested.cpp +537 -537
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.h +77 -77
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_Build.cpp +353 -353
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.h +18 -18
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_InputRuntime.cpp +246 -246
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.cpp +597 -597
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.h +51 -51
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.cpp +878 -878
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers_Sculpt.cpp +449 -449
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers_Function.cpp +275 -275
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers_Graph.cpp +551 -551
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NetworkingHandlers.cpp +531 -531
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NetworkingHandlers.h +28 -28
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.cpp +677 -677
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.cpp +822 -822
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.h +22 -22
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.cpp +218 -218
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.h +17 -17
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/VolumeHelpers_Internal.cpp +46 -46
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/VolumeHelpers_Internal.h +23 -23
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/MCPExternalRegistry.cpp +83 -83
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/UE_MCP_BridgeModule.h +14 -14
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/UE_MCP_BridgeStatusModule.cpp +37 -37
- package/dist/manifest-signature.d.ts +0 -1
- package/dist/manifest-signature.js +0 -53
- package/dist/manifest-signature.js.map +0 -1
- package/dist/pie/schema.d.ts +0 -791
- package/dist/pie/schema.js +0 -151
- package/dist/pie/schema.js.map +0 -1
- package/dist/watch-cli.js.map +0 -1
- /package/plugin/ue_mcp_bridge/{ue_mcp_bridge.uplugin → UE_MCP_Bridge.uplugin} +0 -0
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
#include "EngineStatusHooks.h"
|
|
2
|
-
#include "MCPEngineStatus.h"
|
|
3
|
-
#include "UE_MCP_BridgeModule.h"
|
|
4
|
-
#include "GameThreadExecutor.h"
|
|
5
|
-
#include "Handlers/DialogHandlers.h"
|
|
6
|
-
|
|
7
|
-
#include "Framework/Application/SlateApplication.h"
|
|
8
|
-
|
|
9
|
-
#include "AssetCompilingManager.h"
|
|
10
|
-
#include "ShaderCompiler.h"
|
|
11
|
-
|
|
12
|
-
namespace
|
|
13
|
-
{
|
|
14
|
-
FDelegateHandle GPreTickHandle;
|
|
15
|
-
FDelegateHandle GModalLoopHandle;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
void FMCPEngineStatusHooks::Install()
|
|
19
|
-
{
|
|
20
|
-
FMCPEngineStatus& Status = FMCPEngineStatus::Get();
|
|
21
|
-
|
|
22
|
-
Status.SetModalProvider([](FString& OutTitle, FString& OutMessage, TArray<FString>& OutButtons)
|
|
23
|
-
{
|
|
24
|
-
return FDialogHandlers::DescribeActiveModal(OutTitle, OutMessage, OutButtons);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
Status.SetCompileProvider([](int32& OutShaderJobs, int32& OutAssetCompiles)
|
|
28
|
-
{
|
|
29
|
-
OutShaderJobs = GShaderCompilingManager ? GShaderCompilingManager->GetNumRemainingJobs() : 0;
|
|
30
|
-
OutAssetCompiles = FAssetCompilingManager::Get().GetNumRemainingAssets();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
if (!FSlateApplication::IsInitialized())
|
|
34
|
-
{
|
|
35
|
-
UE_LOG(LogMCPBridge, Warning, TEXT("[UE-MCP] Slate not initialized at bridge startup - engine status keeps its core-only hooks"));
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Slate's pre-tick keeps firing while a long operation pumps the UI to draw
|
|
40
|
-
// its progress bar, which is exactly the window where the core ticker is
|
|
41
|
-
// suspended and every bridge request times out.
|
|
42
|
-
GPreTickHandle = FSlateApplication::Get().OnPreTick().AddLambda([](float)
|
|
43
|
-
{
|
|
44
|
-
FMCPEngineStatus::Get().CaptureNow();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
GModalLoopHandle = FSlateApplication::Get().GetOnModalLoopTickEvent().AddLambda([](float)
|
|
48
|
-
{
|
|
49
|
-
FMCPEngineStatus::Get().CaptureNow();
|
|
50
|
-
// Seeing the dialog is only half of it: the call that could answer it
|
|
51
|
-
// is queued behind the same blocked game thread. Modal-safe handlers
|
|
52
|
-
// run here so a dialog can be cleared from the outside.
|
|
53
|
-
FMCPGameThreadExecutor::DrainModalSafeQueue();
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
void FMCPEngineStatusHooks::Remove()
|
|
58
|
-
{
|
|
59
|
-
FMCPEngineStatus& Status = FMCPEngineStatus::Get();
|
|
60
|
-
Status.SetModalProvider(nullptr);
|
|
61
|
-
Status.SetCompileProvider(nullptr);
|
|
62
|
-
|
|
63
|
-
if (FSlateApplication::IsInitialized())
|
|
64
|
-
{
|
|
65
|
-
if (GPreTickHandle.IsValid())
|
|
66
|
-
{
|
|
67
|
-
FSlateApplication::Get().OnPreTick().Remove(GPreTickHandle);
|
|
68
|
-
GPreTickHandle.Reset();
|
|
69
|
-
}
|
|
70
|
-
if (GModalLoopHandle.IsValid())
|
|
71
|
-
{
|
|
72
|
-
FSlateApplication::Get().GetOnModalLoopTickEvent().Remove(GModalLoopHandle);
|
|
73
|
-
GModalLoopHandle.Reset();
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
1
|
+
#include "EngineStatusHooks.h"
|
|
2
|
+
#include "MCPEngineStatus.h"
|
|
3
|
+
#include "UE_MCP_BridgeModule.h"
|
|
4
|
+
#include "GameThreadExecutor.h"
|
|
5
|
+
#include "Handlers/DialogHandlers.h"
|
|
6
|
+
|
|
7
|
+
#include "Framework/Application/SlateApplication.h"
|
|
8
|
+
|
|
9
|
+
#include "AssetCompilingManager.h"
|
|
10
|
+
#include "ShaderCompiler.h"
|
|
11
|
+
|
|
12
|
+
namespace
|
|
13
|
+
{
|
|
14
|
+
FDelegateHandle GPreTickHandle;
|
|
15
|
+
FDelegateHandle GModalLoopHandle;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
void FMCPEngineStatusHooks::Install()
|
|
19
|
+
{
|
|
20
|
+
FMCPEngineStatus& Status = FMCPEngineStatus::Get();
|
|
21
|
+
|
|
22
|
+
Status.SetModalProvider([](FString& OutTitle, FString& OutMessage, TArray<FString>& OutButtons)
|
|
23
|
+
{
|
|
24
|
+
return FDialogHandlers::DescribeActiveModal(OutTitle, OutMessage, OutButtons);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
Status.SetCompileProvider([](int32& OutShaderJobs, int32& OutAssetCompiles)
|
|
28
|
+
{
|
|
29
|
+
OutShaderJobs = GShaderCompilingManager ? GShaderCompilingManager->GetNumRemainingJobs() : 0;
|
|
30
|
+
OutAssetCompiles = FAssetCompilingManager::Get().GetNumRemainingAssets();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if (!FSlateApplication::IsInitialized())
|
|
34
|
+
{
|
|
35
|
+
UE_LOG(LogMCPBridge, Warning, TEXT("[UE-MCP] Slate not initialized at bridge startup - engine status keeps its core-only hooks"));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Slate's pre-tick keeps firing while a long operation pumps the UI to draw
|
|
40
|
+
// its progress bar, which is exactly the window where the core ticker is
|
|
41
|
+
// suspended and every bridge request times out.
|
|
42
|
+
GPreTickHandle = FSlateApplication::Get().OnPreTick().AddLambda([](float)
|
|
43
|
+
{
|
|
44
|
+
FMCPEngineStatus::Get().CaptureNow();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
GModalLoopHandle = FSlateApplication::Get().GetOnModalLoopTickEvent().AddLambda([](float)
|
|
48
|
+
{
|
|
49
|
+
FMCPEngineStatus::Get().CaptureNow();
|
|
50
|
+
// Seeing the dialog is only half of it: the call that could answer it
|
|
51
|
+
// is queued behind the same blocked game thread. Modal-safe handlers
|
|
52
|
+
// run here so a dialog can be cleared from the outside.
|
|
53
|
+
FMCPGameThreadExecutor::DrainModalSafeQueue();
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
void FMCPEngineStatusHooks::Remove()
|
|
58
|
+
{
|
|
59
|
+
FMCPEngineStatus& Status = FMCPEngineStatus::Get();
|
|
60
|
+
Status.SetModalProvider(nullptr);
|
|
61
|
+
Status.SetCompileProvider(nullptr);
|
|
62
|
+
|
|
63
|
+
if (FSlateApplication::IsInitialized())
|
|
64
|
+
{
|
|
65
|
+
if (GPreTickHandle.IsValid())
|
|
66
|
+
{
|
|
67
|
+
FSlateApplication::Get().OnPreTick().Remove(GPreTickHandle);
|
|
68
|
+
GPreTickHandle.Reset();
|
|
69
|
+
}
|
|
70
|
+
if (GModalLoopHandle.IsValid())
|
|
71
|
+
{
|
|
72
|
+
FSlateApplication::Get().GetOnModalLoopTickEvent().Remove(GModalLoopHandle);
|
|
73
|
+
GModalLoopHandle.Reset();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
|
|
3
|
-
#include "CoreMinimal.h"
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Wires the editor-only sensors into FMCPEngineStatus, which lives in the
|
|
7
|
-
* UE_MCP_BridgeStatus module and has been publishing since PostConfigInit on
|
|
8
|
-
* Core-only hooks.
|
|
9
|
-
*
|
|
10
|
-
* Split this way because the snapshot has to exist long before Slate, the
|
|
11
|
-
* asset compiler or the shader compiler do. Once the bridge module loads, the
|
|
12
|
-
* same snapshot gains:
|
|
13
|
-
*
|
|
14
|
-
* - Slate's pre-tick, which keeps firing while a long operation pumps the UI;
|
|
15
|
-
* - the modal loop tick, which fires while a dialog owns the main loop (and
|
|
16
|
-
* is also where modal-safe handlers get their chance to run);
|
|
17
|
-
* - a description of the active modal dialog, from the Slate widget walk;
|
|
18
|
-
* - remaining shader jobs and asset compiles.
|
|
19
|
-
*/
|
|
20
|
-
namespace FMCPEngineStatusHooks
|
|
21
|
-
{
|
|
22
|
-
/** Register the Slate hooks and the Slate/Engine-backed providers. */
|
|
23
|
-
void Install();
|
|
24
|
-
|
|
25
|
-
/** Unregister everything registered by Install. Safe to call twice. */
|
|
26
|
-
void Remove();
|
|
27
|
-
}
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "CoreMinimal.h"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Wires the editor-only sensors into FMCPEngineStatus, which lives in the
|
|
7
|
+
* UE_MCP_BridgeStatus module and has been publishing since PostConfigInit on
|
|
8
|
+
* Core-only hooks.
|
|
9
|
+
*
|
|
10
|
+
* Split this way because the snapshot has to exist long before Slate, the
|
|
11
|
+
* asset compiler or the shader compiler do. Once the bridge module loads, the
|
|
12
|
+
* same snapshot gains:
|
|
13
|
+
*
|
|
14
|
+
* - Slate's pre-tick, which keeps firing while a long operation pumps the UI;
|
|
15
|
+
* - the modal loop tick, which fires while a dialog owns the main loop (and
|
|
16
|
+
* is also where modal-safe handlers get their chance to run);
|
|
17
|
+
* - a description of the active modal dialog, from the Slate widget walk;
|
|
18
|
+
* - remaining shader jobs and asset compiles.
|
|
19
|
+
*/
|
|
20
|
+
namespace FMCPEngineStatusHooks
|
|
21
|
+
{
|
|
22
|
+
/** Register the Slate hooks and the Slate/Engine-backed providers. */
|
|
23
|
+
void Install();
|
|
24
|
+
|
|
25
|
+
/** Unregister everything registered by Install. Safe to call twice. */
|
|
26
|
+
void Remove();
|
|
27
|
+
}
|
|
@@ -1,128 +1,128 @@
|
|
|
1
|
-
#include "HandlerRegistry.h"
|
|
2
|
-
#include "HAL/PlatformFileManager.h"
|
|
3
|
-
#include "Misc/FileHelper.h"
|
|
4
|
-
#include "Misc/Paths.h"
|
|
5
|
-
#include "HandlerUtils.h"
|
|
6
|
-
#include "MCPHandlerRegistration.h"
|
|
7
|
-
|
|
8
|
-
FMCPHandlerRegistry::FMCPHandlerRegistry()
|
|
9
|
-
{
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
FMCPHandlerRegistry::~FMCPHandlerRegistry()
|
|
13
|
-
{
|
|
14
|
-
Clear();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
void FMCPHandlerRegistry::RegisterHandler(const FString& MethodName, FHandlerFunction Handler)
|
|
18
|
-
{
|
|
19
|
-
CppHandlers.Add(MethodName, Handler);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
void FMCPHandlerRegistry::RegisterHandlerWithTimeout(const FString& MethodName, FHandlerFunction Handler, float TimeoutSeconds)
|
|
23
|
-
{
|
|
24
|
-
CppHandlers.Add(MethodName, Handler);
|
|
25
|
-
if (TimeoutSeconds > 0.0f)
|
|
26
|
-
{
|
|
27
|
-
HandlerTimeouts.Add(MethodName, TimeoutSeconds);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
float FMCPHandlerRegistry::GetHandlerTimeout(const FString& MethodName) const
|
|
32
|
-
{
|
|
33
|
-
if (const float* V = HandlerTimeouts.Find(MethodName))
|
|
34
|
-
{
|
|
35
|
-
return *V;
|
|
36
|
-
}
|
|
37
|
-
// External (plugin-contributed) handlers may register their own timeout.
|
|
38
|
-
UEMCP::FExternalHandlerFn Unused;
|
|
39
|
-
float ExternalTimeout = 0.0f;
|
|
40
|
-
if (UEMCP::LookupExternalHandler(MethodName, Unused, ExternalTimeout) && ExternalTimeout > 0.0f)
|
|
41
|
-
{
|
|
42
|
-
return ExternalTimeout;
|
|
43
|
-
}
|
|
44
|
-
return 0.0f;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
void FMCPHandlerRegistry::RegisterPythonHandler(const FString& MethodName, const FString& PythonScriptPath)
|
|
48
|
-
{
|
|
49
|
-
FPythonHandlerInfo Info;
|
|
50
|
-
Info.ScriptPath = PythonScriptPath;
|
|
51
|
-
Info.HandlerName = MethodName;
|
|
52
|
-
PythonHandlers.Add(MethodName, Info);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
TSharedPtr<FJsonValue> FMCPHandlerRegistry::ExecuteHandler(const FString& MethodName, const TSharedPtr<FJsonObject>& Params)
|
|
56
|
-
{
|
|
57
|
-
// Try C++ handler first
|
|
58
|
-
if (CppHandlers.Contains(MethodName))
|
|
59
|
-
{
|
|
60
|
-
return CppHandlers[MethodName](Params);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Try Python handler
|
|
64
|
-
if (PythonHandlers.Contains(MethodName))
|
|
65
|
-
{
|
|
66
|
-
return ExecutePythonHandler(MethodName, Params);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Plugin-contributed external handler (registered via UEMCP::RegisterExternalHandler).
|
|
70
|
-
{
|
|
71
|
-
UEMCP::FExternalHandlerFn External;
|
|
72
|
-
float Unused = 0.0f;
|
|
73
|
-
if (UEMCP::LookupExternalHandler(MethodName, External, Unused))
|
|
74
|
-
{
|
|
75
|
-
return External(Params);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Handler not found - return nullptr so BridgeServer sends "Unknown method" error
|
|
80
|
-
return nullptr;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
bool FMCPHandlerRegistry::HasHandler(const FString& MethodName) const
|
|
84
|
-
{
|
|
85
|
-
if (CppHandlers.Contains(MethodName) || PythonHandlers.Contains(MethodName))
|
|
86
|
-
{
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
UEMCP::FExternalHandlerFn Unused;
|
|
90
|
-
float UnusedTimeout = 0.0f;
|
|
91
|
-
return UEMCP::LookupExternalHandler(MethodName, Unused, UnusedTimeout);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
TArray<FString> FMCPHandlerRegistry::GetHandlerNames() const
|
|
95
|
-
{
|
|
96
|
-
TArray<FString> Names;
|
|
97
|
-
CppHandlers.GetKeys(Names);
|
|
98
|
-
|
|
99
|
-
TArray<FString> PythonNames;
|
|
100
|
-
PythonHandlers.GetKeys(PythonNames);
|
|
101
|
-
Names.Append(PythonNames);
|
|
102
|
-
|
|
103
|
-
Names.Append(UEMCP::GetExternalHandlerNames());
|
|
104
|
-
|
|
105
|
-
return Names;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
void FMCPHandlerRegistry::Clear()
|
|
109
|
-
{
|
|
110
|
-
CppHandlers.Empty();
|
|
111
|
-
PythonHandlers.Empty();
|
|
112
|
-
HandlerTimeouts.Empty();
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
TSharedPtr<FJsonValue> FMCPHandlerRegistry::ExecutePythonHandler(const FString& MethodName, const TSharedPtr<FJsonObject>& /*Params*/)
|
|
116
|
-
{
|
|
117
|
-
// Python handler dispatch is not implemented. Prior behaviour returned an
|
|
118
|
-
// empty JSON object, which callers could not distinguish from a real
|
|
119
|
-
// empty-success result. Return a typed error instead so callers see the
|
|
120
|
-
// gap clearly; use `execute_python` for ad-hoc Python until the dispatch
|
|
121
|
-
// pipeline lands.
|
|
122
|
-
TSharedPtr<FJsonObject> Err = MakeShared<FJsonObject>();
|
|
123
|
-
Err->SetBoolField(TEXT("success"), false);
|
|
124
|
-
Err->SetStringField(TEXT("error"), FString::Printf(
|
|
125
|
-
TEXT("Python handler '%s' is registered but Python dispatch is not implemented. Use the 'execute_python' action instead."),
|
|
126
|
-
*MethodName));
|
|
127
|
-
return MakeShared<FJsonValueObject>(Err);
|
|
128
|
-
}
|
|
1
|
+
#include "HandlerRegistry.h"
|
|
2
|
+
#include "HAL/PlatformFileManager.h"
|
|
3
|
+
#include "Misc/FileHelper.h"
|
|
4
|
+
#include "Misc/Paths.h"
|
|
5
|
+
#include "HandlerUtils.h"
|
|
6
|
+
#include "MCPHandlerRegistration.h"
|
|
7
|
+
|
|
8
|
+
FMCPHandlerRegistry::FMCPHandlerRegistry()
|
|
9
|
+
{
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
FMCPHandlerRegistry::~FMCPHandlerRegistry()
|
|
13
|
+
{
|
|
14
|
+
Clear();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
void FMCPHandlerRegistry::RegisterHandler(const FString& MethodName, FHandlerFunction Handler)
|
|
18
|
+
{
|
|
19
|
+
CppHandlers.Add(MethodName, Handler);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
void FMCPHandlerRegistry::RegisterHandlerWithTimeout(const FString& MethodName, FHandlerFunction Handler, float TimeoutSeconds)
|
|
23
|
+
{
|
|
24
|
+
CppHandlers.Add(MethodName, Handler);
|
|
25
|
+
if (TimeoutSeconds > 0.0f)
|
|
26
|
+
{
|
|
27
|
+
HandlerTimeouts.Add(MethodName, TimeoutSeconds);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
float FMCPHandlerRegistry::GetHandlerTimeout(const FString& MethodName) const
|
|
32
|
+
{
|
|
33
|
+
if (const float* V = HandlerTimeouts.Find(MethodName))
|
|
34
|
+
{
|
|
35
|
+
return *V;
|
|
36
|
+
}
|
|
37
|
+
// External (plugin-contributed) handlers may register their own timeout.
|
|
38
|
+
UEMCP::FExternalHandlerFn Unused;
|
|
39
|
+
float ExternalTimeout = 0.0f;
|
|
40
|
+
if (UEMCP::LookupExternalHandler(MethodName, Unused, ExternalTimeout) && ExternalTimeout > 0.0f)
|
|
41
|
+
{
|
|
42
|
+
return ExternalTimeout;
|
|
43
|
+
}
|
|
44
|
+
return 0.0f;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
void FMCPHandlerRegistry::RegisterPythonHandler(const FString& MethodName, const FString& PythonScriptPath)
|
|
48
|
+
{
|
|
49
|
+
FPythonHandlerInfo Info;
|
|
50
|
+
Info.ScriptPath = PythonScriptPath;
|
|
51
|
+
Info.HandlerName = MethodName;
|
|
52
|
+
PythonHandlers.Add(MethodName, Info);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
TSharedPtr<FJsonValue> FMCPHandlerRegistry::ExecuteHandler(const FString& MethodName, const TSharedPtr<FJsonObject>& Params)
|
|
56
|
+
{
|
|
57
|
+
// Try C++ handler first
|
|
58
|
+
if (CppHandlers.Contains(MethodName))
|
|
59
|
+
{
|
|
60
|
+
return CppHandlers[MethodName](Params);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Try Python handler
|
|
64
|
+
if (PythonHandlers.Contains(MethodName))
|
|
65
|
+
{
|
|
66
|
+
return ExecutePythonHandler(MethodName, Params);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Plugin-contributed external handler (registered via UEMCP::RegisterExternalHandler).
|
|
70
|
+
{
|
|
71
|
+
UEMCP::FExternalHandlerFn External;
|
|
72
|
+
float Unused = 0.0f;
|
|
73
|
+
if (UEMCP::LookupExternalHandler(MethodName, External, Unused))
|
|
74
|
+
{
|
|
75
|
+
return External(Params);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Handler not found - return nullptr so BridgeServer sends "Unknown method" error
|
|
80
|
+
return nullptr;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
bool FMCPHandlerRegistry::HasHandler(const FString& MethodName) const
|
|
84
|
+
{
|
|
85
|
+
if (CppHandlers.Contains(MethodName) || PythonHandlers.Contains(MethodName))
|
|
86
|
+
{
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
UEMCP::FExternalHandlerFn Unused;
|
|
90
|
+
float UnusedTimeout = 0.0f;
|
|
91
|
+
return UEMCP::LookupExternalHandler(MethodName, Unused, UnusedTimeout);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
TArray<FString> FMCPHandlerRegistry::GetHandlerNames() const
|
|
95
|
+
{
|
|
96
|
+
TArray<FString> Names;
|
|
97
|
+
CppHandlers.GetKeys(Names);
|
|
98
|
+
|
|
99
|
+
TArray<FString> PythonNames;
|
|
100
|
+
PythonHandlers.GetKeys(PythonNames);
|
|
101
|
+
Names.Append(PythonNames);
|
|
102
|
+
|
|
103
|
+
Names.Append(UEMCP::GetExternalHandlerNames());
|
|
104
|
+
|
|
105
|
+
return Names;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
void FMCPHandlerRegistry::Clear()
|
|
109
|
+
{
|
|
110
|
+
CppHandlers.Empty();
|
|
111
|
+
PythonHandlers.Empty();
|
|
112
|
+
HandlerTimeouts.Empty();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
TSharedPtr<FJsonValue> FMCPHandlerRegistry::ExecutePythonHandler(const FString& MethodName, const TSharedPtr<FJsonObject>& /*Params*/)
|
|
116
|
+
{
|
|
117
|
+
// Python handler dispatch is not implemented. Prior behaviour returned an
|
|
118
|
+
// empty JSON object, which callers could not distinguish from a real
|
|
119
|
+
// empty-success result. Return a typed error instead so callers see the
|
|
120
|
+
// gap clearly; use `execute_python` for ad-hoc Python until the dispatch
|
|
121
|
+
// pipeline lands.
|
|
122
|
+
TSharedPtr<FJsonObject> Err = MakeShared<FJsonObject>();
|
|
123
|
+
Err->SetBoolField(TEXT("success"), false);
|
|
124
|
+
Err->SetStringField(TEXT("error"), FString::Printf(
|
|
125
|
+
TEXT("Python handler '%s' is registered but Python dispatch is not implemented. Use the 'execute_python' action instead."),
|
|
126
|
+
*MethodName));
|
|
127
|
+
return MakeShared<FJsonValueObject>(Err);
|
|
128
|
+
}
|
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
|
|
3
|
-
#include "CoreMinimal.h"
|
|
4
|
-
#include "Dom/JsonValue.h"
|
|
5
|
-
#include "Dom/JsonObject.h"
|
|
6
|
-
|
|
7
|
-
class FMCPHandlerRegistry
|
|
8
|
-
{
|
|
9
|
-
public:
|
|
10
|
-
// Handler function signature: takes params JSON object, returns result JSON value
|
|
11
|
-
using FHandlerFunction = TFunction<TSharedPtr<FJsonValue>(const TSharedPtr<FJsonObject>& Params)>;
|
|
12
|
-
|
|
13
|
-
// Python handler info
|
|
14
|
-
struct FPythonHandlerInfo
|
|
15
|
-
{
|
|
16
|
-
FString ScriptPath;
|
|
17
|
-
FString HandlerName;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
FMCPHandlerRegistry();
|
|
21
|
-
~FMCPHandlerRegistry();
|
|
22
|
-
|
|
23
|
-
// Register a C++ handler
|
|
24
|
-
void RegisterHandler(const FString& MethodName, FHandlerFunction Handler);
|
|
25
|
-
|
|
26
|
-
// Register a C++ handler with a non-default game-thread execution timeout.
|
|
27
|
-
// Most handlers finish in milliseconds, but a few (create_cpp_class
|
|
28
|
-
// regenerates IDE project files; build-related ops) legitimately need
|
|
29
|
-
// minutes. Pass TimeoutSeconds explicitly for those.
|
|
30
|
-
void RegisterHandlerWithTimeout(const FString& MethodName, FHandlerFunction Handler, float TimeoutSeconds);
|
|
31
|
-
|
|
32
|
-
// Look up a per-handler timeout. Returns 0 if no override registered,
|
|
33
|
-
// in which case the caller should use its default.
|
|
34
|
-
float GetHandlerTimeout(const FString& MethodName) const;
|
|
35
|
-
|
|
36
|
-
// Register a Python handler
|
|
37
|
-
void RegisterPythonHandler(const FString& MethodName, const FString& PythonScriptPath);
|
|
38
|
-
|
|
39
|
-
// Execute a handler
|
|
40
|
-
TSharedPtr<FJsonValue> ExecuteHandler(const FString& MethodName, const TSharedPtr<FJsonObject>& Params);
|
|
41
|
-
|
|
42
|
-
// Check if handler exists
|
|
43
|
-
bool HasHandler(const FString& MethodName) const;
|
|
44
|
-
|
|
45
|
-
// Get all registered handler names
|
|
46
|
-
TArray<FString> GetHandlerNames() const;
|
|
47
|
-
|
|
48
|
-
// Clear all handlers
|
|
49
|
-
void Clear();
|
|
50
|
-
|
|
51
|
-
private:
|
|
52
|
-
// C++ handlers
|
|
53
|
-
TMap<FString, FHandlerFunction> CppHandlers;
|
|
54
|
-
|
|
55
|
-
// Python handlers
|
|
56
|
-
TMap<FString, FPythonHandlerInfo> PythonHandlers;
|
|
57
|
-
|
|
58
|
-
// Per-handler game-thread timeouts (seconds). Absent = use default.
|
|
59
|
-
TMap<FString, float> HandlerTimeouts;
|
|
60
|
-
|
|
61
|
-
// Execute Python handler
|
|
62
|
-
TSharedPtr<FJsonValue> ExecutePythonHandler(const FString& MethodName, const TSharedPtr<FJsonObject>& Params);
|
|
63
|
-
};
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "CoreMinimal.h"
|
|
4
|
+
#include "Dom/JsonValue.h"
|
|
5
|
+
#include "Dom/JsonObject.h"
|
|
6
|
+
|
|
7
|
+
class FMCPHandlerRegistry
|
|
8
|
+
{
|
|
9
|
+
public:
|
|
10
|
+
// Handler function signature: takes params JSON object, returns result JSON value
|
|
11
|
+
using FHandlerFunction = TFunction<TSharedPtr<FJsonValue>(const TSharedPtr<FJsonObject>& Params)>;
|
|
12
|
+
|
|
13
|
+
// Python handler info
|
|
14
|
+
struct FPythonHandlerInfo
|
|
15
|
+
{
|
|
16
|
+
FString ScriptPath;
|
|
17
|
+
FString HandlerName;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
FMCPHandlerRegistry();
|
|
21
|
+
~FMCPHandlerRegistry();
|
|
22
|
+
|
|
23
|
+
// Register a C++ handler
|
|
24
|
+
void RegisterHandler(const FString& MethodName, FHandlerFunction Handler);
|
|
25
|
+
|
|
26
|
+
// Register a C++ handler with a non-default game-thread execution timeout.
|
|
27
|
+
// Most handlers finish in milliseconds, but a few (create_cpp_class
|
|
28
|
+
// regenerates IDE project files; build-related ops) legitimately need
|
|
29
|
+
// minutes. Pass TimeoutSeconds explicitly for those.
|
|
30
|
+
void RegisterHandlerWithTimeout(const FString& MethodName, FHandlerFunction Handler, float TimeoutSeconds);
|
|
31
|
+
|
|
32
|
+
// Look up a per-handler timeout. Returns 0 if no override registered,
|
|
33
|
+
// in which case the caller should use its default.
|
|
34
|
+
float GetHandlerTimeout(const FString& MethodName) const;
|
|
35
|
+
|
|
36
|
+
// Register a Python handler
|
|
37
|
+
void RegisterPythonHandler(const FString& MethodName, const FString& PythonScriptPath);
|
|
38
|
+
|
|
39
|
+
// Execute a handler
|
|
40
|
+
TSharedPtr<FJsonValue> ExecuteHandler(const FString& MethodName, const TSharedPtr<FJsonObject>& Params);
|
|
41
|
+
|
|
42
|
+
// Check if handler exists
|
|
43
|
+
bool HasHandler(const FString& MethodName) const;
|
|
44
|
+
|
|
45
|
+
// Get all registered handler names
|
|
46
|
+
TArray<FString> GetHandlerNames() const;
|
|
47
|
+
|
|
48
|
+
// Clear all handlers
|
|
49
|
+
void Clear();
|
|
50
|
+
|
|
51
|
+
private:
|
|
52
|
+
// C++ handlers
|
|
53
|
+
TMap<FString, FHandlerFunction> CppHandlers;
|
|
54
|
+
|
|
55
|
+
// Python handlers
|
|
56
|
+
TMap<FString, FPythonHandlerInfo> PythonHandlers;
|
|
57
|
+
|
|
58
|
+
// Per-handler game-thread timeouts (seconds). Absent = use default.
|
|
59
|
+
TMap<FString, float> HandlerTimeouts;
|
|
60
|
+
|
|
61
|
+
// Execute Python handler
|
|
62
|
+
TSharedPtr<FJsonValue> ExecutePythonHandler(const FString& MethodName, const TSharedPtr<FJsonObject>& Params);
|
|
63
|
+
};
|