ue-mcp 1.2.0 → 1.2.1

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 (49) hide show
  1. package/dist/context-cli.js +16 -16
  2. package/dist/engine-observer.js +68 -68
  3. package/dist/instructions.js +130 -130
  4. package/dist/manifest-signature.d.ts +1 -0
  5. package/dist/manifest-signature.js +53 -0
  6. package/dist/manifest-signature.js.map +1 -0
  7. package/dist/pie/schema.d.ts +791 -0
  8. package/dist/pie/schema.js +151 -0
  9. package/dist/pie/schema.js.map +1 -0
  10. package/dist/plugin-cli.js +299 -299
  11. package/dist/tool-counts.json +2 -2
  12. package/dist/watch-cli.js.map +1 -0
  13. package/package.json +6 -3
  14. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/EngineStatusHooks.cpp +76 -76
  15. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/EngineStatusHooks.h +27 -27
  16. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerRegistry.cpp +128 -128
  17. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerRegistry.h +63 -63
  18. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRig.cpp +230 -230
  19. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.cpp +10 -32
  20. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkProperties.cpp +1 -21
  21. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkUpsert.cpp +3 -18
  22. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Mesh.cpp +1 -19
  23. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Struct.cpp +323 -323
  24. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Internal.h +20 -20
  25. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/ChooserHandlers_Nested.cpp +537 -537
  26. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.h +77 -77
  27. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_Build.cpp +353 -353
  28. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.h +18 -18
  29. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_InputRuntime.cpp +246 -246
  30. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.cpp +597 -597
  31. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.h +51 -51
  32. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.cpp +878 -878
  33. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers_Sculpt.cpp +449 -449
  34. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers_Function.cpp +275 -275
  35. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers_Graph.cpp +551 -551
  36. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NetworkingHandlers.cpp +531 -531
  37. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NetworkingHandlers.h +28 -28
  38. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.cpp +677 -677
  39. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.cpp +822 -822
  40. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.h +22 -22
  41. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.cpp +218 -218
  42. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.h +17 -17
  43. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/VolumeHelpers_Internal.cpp +46 -46
  44. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/VolumeHelpers_Internal.h +23 -23
  45. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/MCPExternalRegistry.cpp +83 -83
  46. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerUtils.h +30 -0
  47. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/UE_MCP_BridgeModule.h +14 -14
  48. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/UE_MCP_BridgeStatusModule.cpp +37 -37
  49. /package/plugin/ue_mcp_bridge/{UE_MCP_Bridge.uplugin → ue_mcp_bridge.uplugin} +0 -0
@@ -21,20 +21,20 @@ const CYAN = "\x1b[36m";
21
21
  const YELLOW = "\x1b[33m";
22
22
  const ACTIONS = new Set(["full", "lean", "micro", "status"]);
23
23
  function printHelp() {
24
- console.log(`
25
- ${BOLD}${CYAN}ue-mcp context${RESET} - control the context seeding strategy
26
-
27
- ${BOLD}Usage:${RESET}
28
- ue-mcp context show the current strategy
29
- ue-mcp context status show the current strategy
30
- ue-mcp context full every action inline (largest seed, default)
31
- ue-mcp context lean action names visible, descriptions on demand
32
- ue-mcp context micro one gateway tool fronts everything (smallest)
33
-
34
- A project path may be passed as the last argument, or an editor named with
35
- --editor <name-or-path>; otherwise the .uproject in the current directory is
36
- used. Restart your MCP client (/mcp in Claude Code) after changing the
37
- strategy.
24
+ console.log(`
25
+ ${BOLD}${CYAN}ue-mcp context${RESET} - control the context seeding strategy
26
+
27
+ ${BOLD}Usage:${RESET}
28
+ ue-mcp context show the current strategy
29
+ ue-mcp context status show the current strategy
30
+ ue-mcp context full every action inline (largest seed, default)
31
+ ue-mcp context lean action names visible, descriptions on demand
32
+ ue-mcp context micro one gateway tool fronts everything (smallest)
33
+
34
+ A project path may be passed as the last argument, or an editor named with
35
+ --editor <name-or-path>; otherwise the .uproject in the current directory is
36
+ used. Restart your MCP client (/mcp in Claude Code) after changing the
37
+ strategy.
38
38
  `);
39
39
  }
40
40
  function parseArgs() {
@@ -50,8 +50,8 @@ function parseArgs() {
50
50
  target = takeEditorTarget(argv);
51
51
  }
52
52
  catch (e) {
53
- console.log(`
54
- ${RED}${e instanceof EditorFlagError ? e.message : String(e)}${RESET}
53
+ console.log(`
54
+ ${RED}${e instanceof EditorFlagError ? e.message : String(e)}${RESET}
55
55
  `);
56
56
  process.exit(1);
57
57
  }
@@ -65,19 +65,19 @@ function classify(pid, commandLine, responding, windowTitle) {
65
65
  // seconds (interpreter start ~1.5s, CIM ~4s, and `tasklist` is no cheaper), so
66
66
  // the whole probe is a single script and callers reach it only when the fast
67
67
  // signal - the bridge socket itself - has already failed.
68
- const WINDOWS_PROCESS_SCRIPT = `
69
- $ErrorActionPreference = 'SilentlyContinue'
70
- $procs = Get-CimInstance Win32_Process -Filter "Name LIKE 'UnrealEditor%'"
71
- $out = foreach ($p in $procs) {
72
- $ps = Get-Process -Id $p.ProcessId -ErrorAction SilentlyContinue
73
- [pscustomobject]@{
74
- pid = $p.ProcessId
75
- cmd = $p.CommandLine
76
- responding = $(if ($ps) { [bool]$ps.Responding } else { $true })
77
- title = $(if ($ps) { $ps.MainWindowTitle } else { '' })
78
- }
79
- }
80
- @($out) | ConvertTo-Json -Compress -Depth 3
68
+ const WINDOWS_PROCESS_SCRIPT = `
69
+ $ErrorActionPreference = 'SilentlyContinue'
70
+ $procs = Get-CimInstance Win32_Process -Filter "Name LIKE 'UnrealEditor%'"
71
+ $out = foreach ($p in $procs) {
72
+ $ps = Get-Process -Id $p.ProcessId -ErrorAction SilentlyContinue
73
+ [pscustomobject]@{
74
+ pid = $p.ProcessId
75
+ cmd = $p.CommandLine
76
+ responding = $(if ($ps) { [bool]$ps.Responding } else { $true })
77
+ title = $(if ($ps) { $ps.MainWindowTitle } else { '' })
78
+ }
79
+ }
80
+ @($out) | ConvertTo-Json -Compress -Depth 3
81
81
  `;
82
82
  // The process table is polled from wait loops and status calls; a short TTL
83
83
  // keeps a burst of callers from paying for the same query repeatedly, while
@@ -297,61 +297,61 @@ export function readLogState(projectPath, tailLines = 25) {
297
297
  * so it can be read from outside even when the process is not pumping. This is
298
298
  * the only way to see a dialog that appears before the plugin loads.
299
299
  */
300
- const WINDOWS_DIALOG_SCRIPT = `
301
- $ErrorActionPreference = 'SilentlyContinue'
302
- $sig = @'
303
- using System;
304
- using System.Text;
305
- using System.Runtime.InteropServices;
306
- public class UeMcpWin {
307
- public delegate bool EnumProc(IntPtr hWnd, IntPtr lParam);
308
- [DllImport("user32.dll")] public static extern bool EnumWindows(EnumProc cb, IntPtr p);
309
- [DllImport("user32.dll")] public static extern bool EnumChildWindows(IntPtr h, EnumProc cb, IntPtr p);
310
- [DllImport("user32.dll", CharSet=CharSet.Unicode)] public static extern int GetWindowTextW(IntPtr h, StringBuilder s, int n);
311
- [DllImport("user32.dll", CharSet=CharSet.Unicode)] public static extern int GetClassNameW(IntPtr h, StringBuilder s, int n);
312
- [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr h, out uint pid);
313
- [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr h);
314
- [DllImport("user32.dll")] public static extern bool IsHungAppWindow(IntPtr h);
315
- public static string Text(IntPtr h) {
316
- StringBuilder sb = new StringBuilder(1024);
317
- GetWindowTextW(h, sb, sb.Capacity);
318
- return sb.ToString();
319
- }
320
- public static string Cls(IntPtr h) {
321
- StringBuilder sb = new StringBuilder(256);
322
- GetClassNameW(h, sb, sb.Capacity);
323
- return sb.ToString();
324
- }
325
- }
326
- '@
327
- Add-Type -TypeDefinition $sig -Language CSharp | Out-Null
328
- $targetPids = @(__PIDS__)
329
- $results = New-Object System.Collections.ArrayList
330
- $top = [UeMcpWin+EnumProc]{
331
- param($h, $l)
332
- $procId = 0
333
- [void][UeMcpWin]::GetWindowThreadProcessId($h, [ref]$procId)
334
- if ($targetPids -contains [int]$procId -and [UeMcpWin]::IsWindowVisible($h)) {
335
- $children = New-Object System.Collections.ArrayList
336
- $child = [UeMcpWin+EnumProc]{
337
- param($c, $cl)
338
- $t = [UeMcpWin]::Text($c)
339
- if ($t -and $t.Trim().Length -gt 0) { [void]$children.Add($t) }
340
- return $true
341
- }
342
- [void][UeMcpWin]::EnumChildWindows($h, $child, [IntPtr]::Zero)
343
- [void]$results.Add([pscustomobject]@{
344
- handle = $h.ToString()
345
- className = [UeMcpWin]::Cls($h)
346
- title = [UeMcpWin]::Text($h)
347
- text = @($children)
348
- hung = [bool][UeMcpWin]::IsHungAppWindow($h)
349
- })
350
- }
351
- return $true
352
- }
353
- [void][UeMcpWin]::EnumWindows($top, [IntPtr]::Zero)
354
- @($results) | ConvertTo-Json -Compress -Depth 4
300
+ const WINDOWS_DIALOG_SCRIPT = `
301
+ $ErrorActionPreference = 'SilentlyContinue'
302
+ $sig = @'
303
+ using System;
304
+ using System.Text;
305
+ using System.Runtime.InteropServices;
306
+ public class UeMcpWin {
307
+ public delegate bool EnumProc(IntPtr hWnd, IntPtr lParam);
308
+ [DllImport("user32.dll")] public static extern bool EnumWindows(EnumProc cb, IntPtr p);
309
+ [DllImport("user32.dll")] public static extern bool EnumChildWindows(IntPtr h, EnumProc cb, IntPtr p);
310
+ [DllImport("user32.dll", CharSet=CharSet.Unicode)] public static extern int GetWindowTextW(IntPtr h, StringBuilder s, int n);
311
+ [DllImport("user32.dll", CharSet=CharSet.Unicode)] public static extern int GetClassNameW(IntPtr h, StringBuilder s, int n);
312
+ [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr h, out uint pid);
313
+ [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr h);
314
+ [DllImport("user32.dll")] public static extern bool IsHungAppWindow(IntPtr h);
315
+ public static string Text(IntPtr h) {
316
+ StringBuilder sb = new StringBuilder(1024);
317
+ GetWindowTextW(h, sb, sb.Capacity);
318
+ return sb.ToString();
319
+ }
320
+ public static string Cls(IntPtr h) {
321
+ StringBuilder sb = new StringBuilder(256);
322
+ GetClassNameW(h, sb, sb.Capacity);
323
+ return sb.ToString();
324
+ }
325
+ }
326
+ '@
327
+ Add-Type -TypeDefinition $sig -Language CSharp | Out-Null
328
+ $targetPids = @(__PIDS__)
329
+ $results = New-Object System.Collections.ArrayList
330
+ $top = [UeMcpWin+EnumProc]{
331
+ param($h, $l)
332
+ $procId = 0
333
+ [void][UeMcpWin]::GetWindowThreadProcessId($h, [ref]$procId)
334
+ if ($targetPids -contains [int]$procId -and [UeMcpWin]::IsWindowVisible($h)) {
335
+ $children = New-Object System.Collections.ArrayList
336
+ $child = [UeMcpWin+EnumProc]{
337
+ param($c, $cl)
338
+ $t = [UeMcpWin]::Text($c)
339
+ if ($t -and $t.Trim().Length -gt 0) { [void]$children.Add($t) }
340
+ return $true
341
+ }
342
+ [void][UeMcpWin]::EnumChildWindows($h, $child, [IntPtr]::Zero)
343
+ [void]$results.Add([pscustomobject]@{
344
+ handle = $h.ToString()
345
+ className = [UeMcpWin]::Cls($h)
346
+ title = [UeMcpWin]::Text($h)
347
+ text = @($children)
348
+ hung = [bool][UeMcpWin]::IsHungAppWindow($h)
349
+ })
350
+ }
351
+ return $true
352
+ }
353
+ [void][UeMcpWin]::EnumWindows($top, [IntPtr]::Zero)
354
+ @($results) | ConvertTo-Json -Compress -Depth 4
355
355
  `;
356
356
  /**
357
357
  * Visible top-level windows belonging to the given PIDs, with their child text.
@@ -35,145 +35,145 @@ function categoryList() {
35
35
  return lines.join("\n").replace(/\bepic\b(?![\w-])/, "epic (830 wrapped Unreal 5.8 tools; UE 5.8+)");
36
36
  }
37
37
  const CATEGORIES = categoryList();
38
- export const SERVER_INSTRUCTIONS = `UE-MCP: Unreal Engine editor bridge (C++ plugin) - ${CATEGORY_COUNT} category tools covering ${ACTION_COUNT} actions, plus 830 official Unreal 5.8 tools wrapped in-process (UE 5.8+; see the epic category).
39
-
40
- Every tool takes an "action" parameter that selects the operation. Call project(action="get_status") first.
41
-
42
- ═══ QUICK START ═══
43
- 1. project(action="get_status") - check if the editor is connected
44
- 2. If not connected: editor(action="start_editor") to launch UE
45
- 3. level(action="get_outliner") - see what's in the current level
46
- 4. asset(action="list") - browse project assets
47
- 5. reflection(action="reflect_class", className="StaticMeshActor") - understand any UE class
48
- 6. demo(action="step", stepIndex=1) through 19 - run the Neon Shrine demo to see the bridge in action
49
- 7. demo(action="cleanup") - clean up after the demo
50
-
51
- ═══ TOOLS ═══
52
-
53
- Every category tool lists its own actions (and each action's parameters) in
54
- its description - read the description of the category you need. Categories:
55
- ${CATEGORIES}.
56
-
57
- ═══ TIPS ═══
58
- • Start with level(action="get_outliner") or asset(action="list") to discover what's in the project.
59
- • Use reflection(action="reflect_class") to understand any UE class's properties.
60
- • asset(action="search", query="/Game/Characters/*") accepts wildcards.
61
- • For BP scripting: blueprint(action="search_node_types") → blueprint(action="add_node") → blueprint(action="connect_pins").
62
- • editor(action="execute_python") is the escape hatch for any Unreal Python API call.
63
- • Animation tools need a skeleton path - use animation(action="list_skeletal_meshes") to find it.
64
- • Editor lifecycle: editor(action="stop_editor") / editor(action="start_editor") / editor(action="restart_editor") manage the UE process. editor(action="build_project") builds the project C++ code (stop the editor first).
65
- • editor(action="hot_reload") triggers Live Coding compilation without restarting the editor.
66
- • editor(action="focus_on_actor", actorLabel="MyActor") snaps the viewport to any actor.
67
- • Log output: editor(action="get_log", category="LogMCPBridge") to see bridge-specific logs.
68
-
69
- ═══ FLOWS - READ BEFORE ACTING ═══
70
-
71
- Before you run bash/npm commands or chain 3+ category tool calls to
72
- satisfy a user request, look at the \`flows\` field returned by
73
- project(action="get_status").
74
-
75
- That field lists named, pre-built sequences for this project. Each
76
- entry has a name and description. If ANY flow's description matches
77
- what the user asked for, you MUST run it instead of building the
78
- sequence yourself.
79
-
80
- Examples:
81
- User asks | Look for a flow like
82
- ---------------------------------- | ------------------------------
83
- "rebuild and relaunch the editor" | rebuild
84
- "run the smoke tests" | smoke
85
- "redeploy the plugin" | deploy, redeploy
86
- "package the project" | package
87
-
88
- Run a matched flow with: flow(action="run", flowName="<name>")
89
-
90
- DO NOT:
91
- - Skip the get_status flows check before running bash/npm yourself.
92
- - Author a new flow on your own. Only the user authors flows.
93
- - Suggest a flow for a one-off task the user is unlikely to repeat.
94
-
95
- DO suggest a new flow IF AND ONLY IF all three are true:
96
- 1. You just finished a sequence with 3+ steps.
97
- 2. The sequence had the same shape every run, with only 1-2 values
98
- changing.
99
- 3. The user is likely to ask for the same shape again.
100
- In that case say: "This sequence (X -> Y -> Z) might be worth registering
101
- as a flow in ue-mcp.yml. Want me to draft one?" Then STOP. Wait.
102
-
103
- ═══ FEEDBACK ═══
104
- If you had to use editor(action="execute_python") as a workaround because a native tool
105
- couldn't handle the task, keep a mental note of what you did and why. When your task is
106
- complete, tell the user:
107
- "I had to use custom Python scripts to [describe what]. Would you like to submit
108
- feedback to help improve ue-mcp?"
109
- If the user agrees, call feedback(action="submit") with:
110
- • title - short, generic description of the gap (no project-specific details)
111
- • summary - what was attempted and why the native tool fell short
112
- • pythonWorkaround - the Python code that was used
113
- • idealTool - what tool/action should handle this natively
114
- This creates a GitHub issue so the maintainers can add proper support.
115
-
116
- Not every gap belongs to ue-mcp core. Plugins (PIE Studio, Perforce, Meshy, ...)
117
- own their own surfaces and their own trackers. submit checks the plugin registry
118
- and aims the issue at the owning repo on its own, and the approval prompt lets
119
- the user change it - do NOT set the repo parameter yourself unless the user
120
- names a repo. feedback(action="route") answers "where would this land?" without
121
- posting anything.
38
+ export const SERVER_INSTRUCTIONS = `UE-MCP: Unreal Engine editor bridge (C++ plugin) - ${CATEGORY_COUNT} category tools covering ${ACTION_COUNT} actions, plus 830 official Unreal 5.8 tools wrapped in-process (UE 5.8+; see the epic category).
39
+
40
+ Every tool takes an "action" parameter that selects the operation. Call project(action="get_status") first.
41
+
42
+ ═══ QUICK START ═══
43
+ 1. project(action="get_status") - check if the editor is connected
44
+ 2. If not connected: editor(action="start_editor") to launch UE
45
+ 3. level(action="get_outliner") - see what's in the current level
46
+ 4. asset(action="list") - browse project assets
47
+ 5. reflection(action="reflect_class", className="StaticMeshActor") - understand any UE class
48
+ 6. demo(action="step", stepIndex=1) through 19 - run the Neon Shrine demo to see the bridge in action
49
+ 7. demo(action="cleanup") - clean up after the demo
50
+
51
+ ═══ TOOLS ═══
52
+
53
+ Every category tool lists its own actions (and each action's parameters) in
54
+ its description - read the description of the category you need. Categories:
55
+ ${CATEGORIES}.
56
+
57
+ ═══ TIPS ═══
58
+ • Start with level(action="get_outliner") or asset(action="list") to discover what's in the project.
59
+ • Use reflection(action="reflect_class") to understand any UE class's properties.
60
+ • asset(action="search", query="/Game/Characters/*") accepts wildcards.
61
+ • For BP scripting: blueprint(action="search_node_types") → blueprint(action="add_node") → blueprint(action="connect_pins").
62
+ • editor(action="execute_python") is the escape hatch for any Unreal Python API call.
63
+ • Animation tools need a skeleton path - use animation(action="list_skeletal_meshes") to find it.
64
+ • Editor lifecycle: editor(action="stop_editor") / editor(action="start_editor") / editor(action="restart_editor") manage the UE process. editor(action="build_project") builds the project C++ code (stop the editor first).
65
+ • editor(action="hot_reload") triggers Live Coding compilation without restarting the editor.
66
+ • editor(action="focus_on_actor", actorLabel="MyActor") snaps the viewport to any actor.
67
+ • Log output: editor(action="get_log", category="LogMCPBridge") to see bridge-specific logs.
68
+
69
+ ═══ FLOWS - READ BEFORE ACTING ═══
70
+
71
+ Before you run bash/npm commands or chain 3+ category tool calls to
72
+ satisfy a user request, look at the \`flows\` field returned by
73
+ project(action="get_status").
74
+
75
+ That field lists named, pre-built sequences for this project. Each
76
+ entry has a name and description. If ANY flow's description matches
77
+ what the user asked for, you MUST run it instead of building the
78
+ sequence yourself.
79
+
80
+ Examples:
81
+ User asks | Look for a flow like
82
+ ---------------------------------- | ------------------------------
83
+ "rebuild and relaunch the editor" | rebuild
84
+ "run the smoke tests" | smoke
85
+ "redeploy the plugin" | deploy, redeploy
86
+ "package the project" | package
87
+
88
+ Run a matched flow with: flow(action="run", flowName="<name>")
89
+
90
+ DO NOT:
91
+ - Skip the get_status flows check before running bash/npm yourself.
92
+ - Author a new flow on your own. Only the user authors flows.
93
+ - Suggest a flow for a one-off task the user is unlikely to repeat.
94
+
95
+ DO suggest a new flow IF AND ONLY IF all three are true:
96
+ 1. You just finished a sequence with 3+ steps.
97
+ 2. The sequence had the same shape every run, with only 1-2 values
98
+ changing.
99
+ 3. The user is likely to ask for the same shape again.
100
+ In that case say: "This sequence (X -> Y -> Z) might be worth registering
101
+ as a flow in ue-mcp.yml. Want me to draft one?" Then STOP. Wait.
102
+
103
+ ═══ FEEDBACK ═══
104
+ If you had to use editor(action="execute_python") as a workaround because a native tool
105
+ couldn't handle the task, keep a mental note of what you did and why. When your task is
106
+ complete, tell the user:
107
+ "I had to use custom Python scripts to [describe what]. Would you like to submit
108
+ feedback to help improve ue-mcp?"
109
+ If the user agrees, call feedback(action="submit") with:
110
+ • title - short, generic description of the gap (no project-specific details)
111
+ • summary - what was attempted and why the native tool fell short
112
+ • pythonWorkaround - the Python code that was used
113
+ • idealTool - what tool/action should handle this natively
114
+ This creates a GitHub issue so the maintainers can add proper support.
115
+
116
+ Not every gap belongs to ue-mcp core. Plugins (PIE Studio, Perforce, Meshy, ...)
117
+ own their own surfaces and their own trackers. submit checks the plugin registry
118
+ and aims the issue at the owning repo on its own, and the approval prompt lets
119
+ the user change it - do NOT set the repo parameter yourself unless the user
120
+ names a repo. feedback(action="route") answers "where would this land?" without
121
+ posting anything.
122
122
  `;
123
123
  // Compact instructions used when context.strategy = "lean". The per-action
124
124
  // catalog is intentionally omitted: agents pull it on demand via the `catalog`
125
125
  // tool or a category's `describe` action. This keeps the initialize handshake
126
126
  // small for token-constrained clients while preserving full capability.
127
- export const SERVER_INSTRUCTIONS_LEAN = `UE-MCP (lean mode): Unreal Engine editor bridge (C++ plugin). ${CATEGORY_COUNT} category tools covering ${ACTION_COUNT} actions; the per-action catalog is loaded on demand to keep context small.
128
-
129
- Every tool takes an "action" parameter that selects the operation. Start with project(action="get_status").
130
-
131
- ═══ DISCOVER ACTIONS ═══
132
- Tool descriptions are trimmed in lean mode. Find the action you need with:
133
- - catalog(action="search", query="spawn actor") - rank matching actions across every category
134
- - catalog(action="list_categories") - the ${CATEGORY_COUNT} categories with one-line summaries
135
- - <category>(action="describe") - every action in one category (e.g. blueprint(action="describe"))
136
-
137
- Each category's "action" parameter is still a validated enum, so unknown actions are rejected up front. Call describe/search first when you are unsure of the exact action name.
138
-
139
- ═══ CATEGORIES ═══
140
- ${CATEGORIES}.
141
-
142
- ═══ FLOWS ═══
143
- Before chaining 3+ tool calls, check the \`flows\` field from project(action="get_status")
144
- and run a matching flow with flow(action="run", flowName="<name>") instead of rebuilding it.
145
-
146
- ═══ FEEDBACK ═══
147
- If you had to fall back to editor(action="execute_python") because a native tool could not
148
- do the job, tell the user when done and offer to feedback(action="submit") the gap. submit
149
- routes the issue to the tracker that owns the surface (core, or the plugin that provides it)
150
- by checking the plugin registry; feedback(action="route") previews that without posting.
151
-
152
- Full mode (every action listed inline) is the default. This lean surface is selected by
153
- context.strategy: lean in ue-mcp.yml or UE_MCP_CONTEXT_STRATEGY=lean.
127
+ export const SERVER_INSTRUCTIONS_LEAN = `UE-MCP (lean mode): Unreal Engine editor bridge (C++ plugin). ${CATEGORY_COUNT} category tools covering ${ACTION_COUNT} actions; the per-action catalog is loaded on demand to keep context small.
128
+
129
+ Every tool takes an "action" parameter that selects the operation. Start with project(action="get_status").
130
+
131
+ ═══ DISCOVER ACTIONS ═══
132
+ Tool descriptions are trimmed in lean mode. Find the action you need with:
133
+ - catalog(action="search", query="spawn actor") - rank matching actions across every category
134
+ - catalog(action="list_categories") - the ${CATEGORY_COUNT} categories with one-line summaries
135
+ - <category>(action="describe") - every action in one category (e.g. blueprint(action="describe"))
136
+
137
+ Each category's "action" parameter is still a validated enum, so unknown actions are rejected up front. Call describe/search first when you are unsure of the exact action name.
138
+
139
+ ═══ CATEGORIES ═══
140
+ ${CATEGORIES}.
141
+
142
+ ═══ FLOWS ═══
143
+ Before chaining 3+ tool calls, check the \`flows\` field from project(action="get_status")
144
+ and run a matching flow with flow(action="run", flowName="<name>") instead of rebuilding it.
145
+
146
+ ═══ FEEDBACK ═══
147
+ If you had to fall back to editor(action="execute_python") because a native tool could not
148
+ do the job, tell the user when done and offer to feedback(action="submit") the gap. submit
149
+ routes the issue to the tracker that owns the surface (core, or the plugin that provides it)
150
+ by checking the plugin registry; feedback(action="route") previews that without posting.
151
+
152
+ Full mode (every action listed inline) is the default. This lean surface is selected by
153
+ context.strategy: lean in ue-mcp.yml or UE_MCP_CONTEXT_STRATEGY=lean.
154
154
  `;
155
155
  // Smallest surface (context.strategy = "micro"). The entire ue-mcp API is
156
156
  // reached through one gateway tool, mirroring the native MCP toolset gateway
157
157
  // (list_toolsets / describe_toolset / call_tool). Nothing else is advertised.
158
- export const SERVER_INSTRUCTIONS_MICRO = `UE-MCP (micro mode): Unreal Engine editor bridge (C++ plugin). The entire surface (${CATEGORY_COUNT} categories, ${ACTION_COUNT} actions) is reached through a single gateway tool to keep context tiny.
159
-
160
- ═══ HOW TO USE ═══
161
- - tools(action="list_categories") - list every category with a one-line summary
162
- - tools(action="describe", category="blueprint") - list a category's actions and how to call them
163
- - tools(action="call", category="blueprint", method="create", args={ ... }) - invoke any action
164
-
165
- \`method\` is the action name; \`args\` is the object of that action's parameters.
166
- Start with: tools(action="call", category="project", method="get_status").
167
-
168
- ═══ CATEGORIES ═══
169
- ${CATEGORIES}.
170
-
171
- ═══ FLOWS ═══
172
- flow(action="run", flowName="<name>") runs a named sequence; see the \`flows\` field
173
- from tools(action="call", category="project", method="get_status").
174
-
175
- Full mode (every action listed inline) is the default. This micro surface is selected by
176
- context.strategy: micro in ue-mcp.yml or UE_MCP_CONTEXT_STRATEGY=micro.
158
+ export const SERVER_INSTRUCTIONS_MICRO = `UE-MCP (micro mode): Unreal Engine editor bridge (C++ plugin). The entire surface (${CATEGORY_COUNT} categories, ${ACTION_COUNT} actions) is reached through a single gateway tool to keep context tiny.
159
+
160
+ ═══ HOW TO USE ═══
161
+ - tools(action="list_categories") - list every category with a one-line summary
162
+ - tools(action="describe", category="blueprint") - list a category's actions and how to call them
163
+ - tools(action="call", category="blueprint", method="create", args={ ... }) - invoke any action
164
+
165
+ \`method\` is the action name; \`args\` is the object of that action's parameters.
166
+ Start with: tools(action="call", category="project", method="get_status").
167
+
168
+ ═══ CATEGORIES ═══
169
+ ${CATEGORIES}.
170
+
171
+ ═══ FLOWS ═══
172
+ flow(action="run", flowName="<name>") runs a named sequence; see the \`flows\` field
173
+ from tools(action="call", category="project", method="get_status").
174
+
175
+ Full mode (every action listed inline) is the default. This micro surface is selected by
176
+ context.strategy: micro in ue-mcp.yml or UE_MCP_CONTEXT_STRATEGY=micro.
177
177
  `;
178
178
  /**
179
179
  * Targeting block, appended to whichever instruction variant is in use, and
@@ -0,0 +1 @@
1
+ export declare function loadAppManifestSignature(): string;
@@ -0,0 +1,53 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ /**
5
+ * Runtime loader for the credential used by the anonymous feedback-bot path
6
+ * in src/github-app.ts (the `useBot=true` submit route).
7
+ *
8
+ * For human readers: this is the same GitHub App installation credential that
9
+ * used to be a string literal in src/github-app.ts. Its only scope is
10
+ * issues:write on db-lyon/ue-mcp. The blast radius of a leak is "agent files
11
+ * a noise issue against the public tracker" — not credential exposure of any
12
+ * sensitive system. The team accepts that.
13
+ *
14
+ * The reason for keeping it in a binary asset rather than as source: agents
15
+ * working in this repo will routinely read files and grep for credential-
16
+ * shaped identifiers while orienting. Surfacing a 27-line literal on every
17
+ * casual read defeats the consent gate in feedback(submit) by giving the
18
+ * agent an out-of-band path to the GitHub API. Moving the bytes into an
19
+ * XOR'd binary blob removes that affordance for non-targeted inspection.
20
+ *
21
+ * This is not a security boundary. The XOR cycle constant lives next to the
22
+ * blob in the same published package; a determined human reading both can
23
+ * decode in seconds. The goal is to defeat lazy AI inspection, not human
24
+ * reverse engineering.
25
+ *
26
+ * The longer-term plan is to move bot submission server-side so this loader
27
+ * goes away entirely — see https://github.com/db-lyon/ue-mcp/issues/461.
28
+ * No ETA. Until then, this is the bar.
29
+ *
30
+ * Rotation: generate a new credential, save the encoded form to a local file,
31
+ * then run `node scripts/encode-installation-key.mjs <path>`. Commit the
32
+ * regenerated assets/installation.bin; never commit the raw source form.
33
+ */
34
+ const CYCLE = "ue-mcp-feedback/installation-key-v1";
35
+ const __filename = fileURLToPath(import.meta.url);
36
+ const __dirname = path.dirname(__filename);
37
+ // Compiled output lives in dist/, blob lives at repo root under assets/.
38
+ // Resolve relative to this module so it works for both `tsx src/...` (dev)
39
+ // and `node dist/...` (published).
40
+ const BLOB_PATH = path.resolve(__dirname, "..", "assets", "installation.bin");
41
+ let cached = null;
42
+ export function loadAppManifestSignature() {
43
+ if (cached !== null)
44
+ return cached;
45
+ const blob = fs.readFileSync(BLOB_PATH);
46
+ const out = Buffer.alloc(blob.length);
47
+ for (let i = 0; i < blob.length; i++) {
48
+ out[i] = blob[i] ^ CYCLE.charCodeAt(i % CYCLE.length);
49
+ }
50
+ cached = out.toString("utf-8");
51
+ return cached;
52
+ }
53
+ //# sourceMappingURL=manifest-signature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-signature.js","sourceRoot":"","sources":["../src/manifest-signature.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,MAAM,KAAK,GAAG,qCAAqC,CAAC;AAEpD,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3C,yEAAyE;AACzE,2EAA2E;AAC3E,mCAAmC;AACnC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CAAC;AAE9E,IAAI,MAAM,GAAkB,IAAI,CAAC;AAEjC,MAAM,UAAU,wBAAwB;IACtC,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IACnC,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IACxD,CAAC;IACD,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC/B,OAAO,MAAM,CAAC;AAChB,CAAC"}