ue-mcp 1.1.17 → 1.1.18

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # UE-MCP
2
2
 
3
- **Unreal Engine Model Context Protocol Server** - gives AI assistants deep read/write access to the Unreal Editor through <!-- count:tools -->23<!-- /count --> category tools covering <!-- count:actions -->711+<!-- /count --> actions, plus a YAML flow engine for multi-step workflows.
3
+ **Unreal Engine Model Context Protocol Server** - gives AI assistants deep read/write access to the Unreal Editor through <!-- count:tools -->24<!-- /count --> category tools covering <!-- count:actions -->719+<!-- /count --> actions, plus a YAML flow engine for multi-step workflows.
4
4
 
5
5
  ```mermaid
6
6
  flowchart LR
@@ -7,12 +7,34 @@ import { findEngineInstall } from "./deployer.js";
7
7
  // The MCP server itself is cross-platform; only process control is gated.
8
8
  const IS_WINDOWS = process.platform === "win32";
9
9
  const WINDOWS_ONLY_MSG = "editor start/stop/restart is Windows-only. On macOS/Linux, start and stop the Unreal Editor manually; ue-mcp will reconnect when the bridge is reachable.";
10
- function findUEBuildTool() {
10
+ /** Read EngineAssociation from a .uproject, or null if unreadable. */
11
+ function readEngineAssociation(projectPath) {
12
+ try {
13
+ const parsed = JSON.parse(fs.readFileSync(projectPath, "utf-8"));
14
+ return typeof parsed?.EngineAssociation === "string" ? parsed.EngineAssociation : null;
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ }
20
+ function findUEBuildTool(engineAssociation) {
11
21
  const envPath = process.env.UE_BUILD_TOOL_PATH;
12
22
  if (envPath)
13
23
  return envPath;
14
- const versions = ["5.8", "5.7", "5.6", "5.5", "5.4", "5.3"];
15
24
  const scriptName = IS_WINDOWS ? "Build.bat" : "Build.sh";
25
+ // Prefer the engine the project's EngineAssociation actually points at, so a
26
+ // 5.7 project builds with 5.7's Build tool - not whatever version happens to
27
+ // sort first in the fallback search below. The editor launch already respects
28
+ // the association (findEditorExecutable); without this the CLI build could
29
+ // silently compile against a different engine than the editor runs, masking
30
+ // API incompatibilities until the editor's own rebuild fails.
31
+ const associatedRoot = findEngineInstall(engineAssociation ?? null);
32
+ if (associatedRoot) {
33
+ const associatedTool = path.join(associatedRoot, "Engine", "Build", "BatchFiles", scriptName);
34
+ if (fs.existsSync(associatedTool))
35
+ return associatedTool;
36
+ }
37
+ const versions = ["5.8", "5.7", "5.6", "5.5", "5.4", "5.3"];
16
38
  const searchRoots = IS_WINDOWS
17
39
  ? [
18
40
  "C:/Program Files/Epic Games",
@@ -56,7 +78,7 @@ function findEditorExecutable(project) {
56
78
  return associatedEditorExe;
57
79
  }
58
80
  }
59
- const buildTool = findUEBuildTool();
81
+ const buildTool = findUEBuildTool(project?.engineAssociation ?? null);
60
82
  if (!buildTool)
61
83
  return null;
62
84
  const engineRoot = path.resolve(buildTool, "..", "..", "..", "..");
@@ -231,7 +253,8 @@ function getPlatformString() {
231
253
  return "Linux";
232
254
  }
233
255
  export async function buildProject(projectPath, opts = {}) {
234
- const buildTool = findUEBuildTool();
256
+ const resolvedPath = path.resolve(projectPath);
257
+ const buildTool = findUEBuildTool(readEngineAssociation(resolvedPath));
235
258
  if (!buildTool) {
236
259
  return {
237
260
  success: false,
@@ -239,7 +262,6 @@ export async function buildProject(projectPath, opts = {}) {
239
262
  message: "Unreal Engine build tool not found. Set UE_BUILD_TOOL_PATH or install UE5.3+ to a default location.",
240
263
  };
241
264
  }
242
- const resolvedPath = path.resolve(projectPath);
243
265
  if (!fs.existsSync(resolvedPath)) {
244
266
  return { success: false, exitCode: null, message: `Project file not found: ${resolvedPath}` };
245
267
  }
@@ -1 +1 @@
1
- {"version":3,"file":"editor-control.js","sourceRoot":"","sources":["../src/editor-control.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"editor-control.js","sourceRoot":"","sources":["../src/editor-control.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- export declare const SERVER_INSTRUCTIONS = "UE-MCP: Unreal Engine editor bridge (C++ plugin) - 23 category tools covering 677+ actions, plus 830 official Unreal 5.8 tools wrapped in-process (UE 5.8+; see the epic category).\n\nEvery tool takes an \"action\" parameter that selects the operation. Call project(action=\"get_status\") first.\n\n\u2550\u2550\u2550 QUICK START \u2550\u2550\u2550\n1. project(action=\"get_status\") \u2014 check if the editor is connected\n2. If not connected: editor(action=\"start_editor\") to launch UE\n3. level(action=\"get_outliner\") \u2014 see what's in the current level\n4. asset(action=\"list\") \u2014 browse project assets\n5. reflection(action=\"reflect_class\", className=\"StaticMeshActor\") \u2014 understand any UE class\n6. demo(action=\"step\", stepIndex=1) through 19 \u2014 run the Neon Shrine demo to see the bridge in action\n7. demo(action=\"cleanup\") \u2014 clean up after the demo\n\n\u2550\u2550\u2550 TOOLS \u2550\u2550\u2550\n\nEvery category tool lists its own actions (and each action's parameters) in\nits description - read the description of the category you need. Categories:\nproject, asset, blueprint, level, material, animation, landscape, pcg, foliage,\nniagara, audio, widget, editor, reflection, gameplay, gas, networking, demo,\nfeedback, statetree, chooser, plugins, epic (830 wrapped Unreal 5.8 tools; UE 5.8+).\n\n\u2550\u2550\u2550 TIPS \u2550\u2550\u2550\n\u2022 Start with level(action=\"get_outliner\") or asset(action=\"list\") to discover what's in the project.\n\u2022 Use reflection(action=\"reflect_class\") to understand any UE class's properties.\n\u2022 asset(action=\"search\", query=\"/Game/Characters/*\") accepts wildcards.\n\u2022 For BP scripting: blueprint(action=\"search_node_types\") \u2192 blueprint(action=\"add_node\") \u2192 blueprint(action=\"connect_pins\").\n\u2022 editor(action=\"execute_python\") is the escape hatch for any Unreal Python API call.\n\u2022 Animation tools need a skeleton path \u2014 use animation(action=\"list_skeletal_meshes\") to find it.\n\u2022 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).\n\u2022 editor(action=\"hot_reload\") triggers Live Coding compilation without restarting the editor.\n\u2022 editor(action=\"focus_on_actor\", actorLabel=\"MyActor\") snaps the viewport to any actor.\n\u2022 Log output: editor(action=\"get_log\", category=\"LogMCPBridge\") to see bridge-specific logs.\n\n\u2550\u2550\u2550 FLOWS \u2014 READ BEFORE ACTING \u2550\u2550\u2550\n\nBefore you run bash/npm commands or chain 3+ category tool calls to\nsatisfy a user request, look at the `flows` field returned by\nproject(action=\"get_status\").\n\nThat field lists named, pre-built sequences for this project. Each\nentry has a name and description. If ANY flow's description matches\nwhat the user asked for, you MUST run it instead of building the\nsequence yourself.\n\nExamples:\n User asks | Look for a flow like\n ---------------------------------- | ------------------------------\n \"rebuild and relaunch the editor\" | rebuild\n \"run the smoke tests\" | smoke\n \"redeploy the plugin\" | deploy, redeploy\n \"package the project\" | package\n\nRun a matched flow with: flow(action=\"run\", flowName=\"<name>\")\n\nDO NOT:\n- Skip the get_status flows check before running bash/npm yourself.\n- Author a new flow on your own. Only the user authors flows.\n- Suggest a flow for a one-off task the user is unlikely to repeat.\n\nDO suggest a new flow IF AND ONLY IF all three are true:\n 1. You just finished a sequence with 3+ steps.\n 2. The sequence had the same shape every run, with only 1-2 values\n changing.\n 3. The user is likely to ask for the same shape again.\nIn that case say: \"This sequence (X -> Y -> Z) might be worth registering\nas a flow in ue-mcp.yml. Want me to draft one?\" Then STOP. Wait.\n\n\u2550\u2550\u2550 FEEDBACK \u2550\u2550\u2550\nIf you had to use editor(action=\"execute_python\") as a workaround because a native tool\ncouldn't handle the task, keep a mental note of what you did and why. When your task is\ncomplete, tell the user:\n \"I had to use custom Python scripts to [describe what]. Would you like to submit\n feedback to help improve ue-mcp?\"\nIf the user agrees, call feedback(action=\"submit\") with:\n \u2022 title \u2014 short, generic description of the gap (no project-specific details)\n \u2022 summary \u2014 what was attempted and why the native tool fell short\n \u2022 pythonWorkaround \u2014 the Python code that was used\n \u2022 idealTool \u2014 what tool/action should handle this natively\nThis creates a GitHub issue so the maintainers can add proper support.\n";
2
- export declare const SERVER_INSTRUCTIONS_LEAN = "UE-MCP (lean mode): Unreal Engine editor bridge (C++ plugin). 23 category tools; the per-action catalog is loaded on demand to keep context small.\n\nEvery tool takes an \"action\" parameter that selects the operation. Start with project(action=\"get_status\").\n\n\u2550\u2550\u2550 DISCOVER ACTIONS \u2550\u2550\u2550\nTool descriptions are trimmed in lean mode. Find the action you need with:\n- catalog(action=\"search\", query=\"spawn actor\") - rank matching actions across every category\n- catalog(action=\"list_categories\") - the 22 categories with one-line summaries\n- <category>(action=\"describe\") - every action in one category (e.g. blueprint(action=\"describe\"))\n\nEach 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.\n\n\u2550\u2550\u2550 CATEGORIES \u2550\u2550\u2550\nproject, asset, blueprint, level, material, animation, landscape, pcg, foliage,\nniagara, audio, widget, editor, reflection, gameplay, gas, networking, demo,\nfeedback, statetree, chooser, plugins, epic (830 wrapped Unreal 5.8 tools; UE 5.8+).\n\n\u2550\u2550\u2550 FLOWS \u2550\u2550\u2550\nBefore chaining 3+ tool calls, check the `flows` field from project(action=\"get_status\")\nand run a matching flow with flow(action=\"run\", flowName=\"<name>\") instead of rebuilding it.\n\n\u2550\u2550\u2550 FEEDBACK \u2550\u2550\u2550\nIf you had to fall back to editor(action=\"execute_python\") because a native tool could not\ndo the job, tell the user when done and offer to feedback(action=\"submit\") the gap.\n\nFull mode (every action listed inline) is the default. This lean surface is selected by\ncontext.strategy: lean in ue-mcp.yml or UE_MCP_CONTEXT_STRATEGY=lean.\n";
3
- export declare const SERVER_INSTRUCTIONS_MICRO = "UE-MCP (micro mode): Unreal Engine editor bridge (C++ plugin). The entire surface (22 categories, 600+ actions) is reached through a single gateway tool to keep context tiny.\n\n\u2550\u2550\u2550 HOW TO USE \u2550\u2550\u2550\n- tools(action=\"list_categories\") - list every category with a one-line summary\n- tools(action=\"describe\", category=\"blueprint\") - list a category's actions and how to call them\n- tools(action=\"call\", category=\"blueprint\", method=\"create\", args={ ... }) - invoke any action\n\n`method` is the action name; `args` is the object of that action's parameters.\nStart with: tools(action=\"call\", category=\"project\", method=\"get_status\").\n\n\u2550\u2550\u2550 CATEGORIES \u2550\u2550\u2550\nproject, asset, blueprint, level, material, animation, landscape, pcg, foliage,\nniagara, audio, widget, editor, reflection, gameplay, gas, networking, demo,\nfeedback, statetree, chooser, plugins, epic (830 wrapped Unreal 5.8 tools; UE 5.8+).\n\n\u2550\u2550\u2550 FLOWS \u2550\u2550\u2550\nflow(action=\"run\", flowName=\"<name>\") runs a named sequence; see the `flows` field\nfrom tools(action=\"call\", category=\"project\", method=\"get_status\").\n\nFull mode (every action listed inline) is the default. This micro surface is selected by\ncontext.strategy: micro in ue-mcp.yml or UE_MCP_CONTEXT_STRATEGY=micro.\n";
1
+ export declare const SERVER_INSTRUCTIONS = "UE-MCP: Unreal Engine editor bridge (C++ plugin) - 24 category tools covering 685+ actions, plus 830 official Unreal 5.8 tools wrapped in-process (UE 5.8+; see the epic category).\n\nEvery tool takes an \"action\" parameter that selects the operation. Call project(action=\"get_status\") first.\n\n\u2550\u2550\u2550 QUICK START \u2550\u2550\u2550\n1. project(action=\"get_status\") \u2014 check if the editor is connected\n2. If not connected: editor(action=\"start_editor\") to launch UE\n3. level(action=\"get_outliner\") \u2014 see what's in the current level\n4. asset(action=\"list\") \u2014 browse project assets\n5. reflection(action=\"reflect_class\", className=\"StaticMeshActor\") \u2014 understand any UE class\n6. demo(action=\"step\", stepIndex=1) through 19 \u2014 run the Neon Shrine demo to see the bridge in action\n7. demo(action=\"cleanup\") \u2014 clean up after the demo\n\n\u2550\u2550\u2550 TOOLS \u2550\u2550\u2550\n\nEvery category tool lists its own actions (and each action's parameters) in\nits description - read the description of the category you need. Categories:\nproject, asset, blueprint, level, material, animation, landscape, pcg, foliage,\nniagara, audio, widget, editor, reflection, gameplay, gas, networking, demo,\nfeedback, statetree, chooser, plugins, epic (830 wrapped Unreal 5.8 tools; UE 5.8+).\n\n\u2550\u2550\u2550 TIPS \u2550\u2550\u2550\n\u2022 Start with level(action=\"get_outliner\") or asset(action=\"list\") to discover what's in the project.\n\u2022 Use reflection(action=\"reflect_class\") to understand any UE class's properties.\n\u2022 asset(action=\"search\", query=\"/Game/Characters/*\") accepts wildcards.\n\u2022 For BP scripting: blueprint(action=\"search_node_types\") \u2192 blueprint(action=\"add_node\") \u2192 blueprint(action=\"connect_pins\").\n\u2022 editor(action=\"execute_python\") is the escape hatch for any Unreal Python API call.\n\u2022 Animation tools need a skeleton path \u2014 use animation(action=\"list_skeletal_meshes\") to find it.\n\u2022 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).\n\u2022 editor(action=\"hot_reload\") triggers Live Coding compilation without restarting the editor.\n\u2022 editor(action=\"focus_on_actor\", actorLabel=\"MyActor\") snaps the viewport to any actor.\n\u2022 Log output: editor(action=\"get_log\", category=\"LogMCPBridge\") to see bridge-specific logs.\n\n\u2550\u2550\u2550 FLOWS \u2014 READ BEFORE ACTING \u2550\u2550\u2550\n\nBefore you run bash/npm commands or chain 3+ category tool calls to\nsatisfy a user request, look at the `flows` field returned by\nproject(action=\"get_status\").\n\nThat field lists named, pre-built sequences for this project. Each\nentry has a name and description. If ANY flow's description matches\nwhat the user asked for, you MUST run it instead of building the\nsequence yourself.\n\nExamples:\n User asks | Look for a flow like\n ---------------------------------- | ------------------------------\n \"rebuild and relaunch the editor\" | rebuild\n \"run the smoke tests\" | smoke\n \"redeploy the plugin\" | deploy, redeploy\n \"package the project\" | package\n\nRun a matched flow with: flow(action=\"run\", flowName=\"<name>\")\n\nDO NOT:\n- Skip the get_status flows check before running bash/npm yourself.\n- Author a new flow on your own. Only the user authors flows.\n- Suggest a flow for a one-off task the user is unlikely to repeat.\n\nDO suggest a new flow IF AND ONLY IF all three are true:\n 1. You just finished a sequence with 3+ steps.\n 2. The sequence had the same shape every run, with only 1-2 values\n changing.\n 3. The user is likely to ask for the same shape again.\nIn that case say: \"This sequence (X -> Y -> Z) might be worth registering\nas a flow in ue-mcp.yml. Want me to draft one?\" Then STOP. Wait.\n\n\u2550\u2550\u2550 FEEDBACK \u2550\u2550\u2550\nIf you had to use editor(action=\"execute_python\") as a workaround because a native tool\ncouldn't handle the task, keep a mental note of what you did and why. When your task is\ncomplete, tell the user:\n \"I had to use custom Python scripts to [describe what]. Would you like to submit\n feedback to help improve ue-mcp?\"\nIf the user agrees, call feedback(action=\"submit\") with:\n \u2022 title \u2014 short, generic description of the gap (no project-specific details)\n \u2022 summary \u2014 what was attempted and why the native tool fell short\n \u2022 pythonWorkaround \u2014 the Python code that was used\n \u2022 idealTool \u2014 what tool/action should handle this natively\nThis creates a GitHub issue so the maintainers can add proper support.\n";
2
+ export declare const SERVER_INSTRUCTIONS_LEAN = "UE-MCP (lean mode): Unreal Engine editor bridge (C++ plugin). 24 category tools; the per-action catalog is loaded on demand to keep context small.\n\nEvery tool takes an \"action\" parameter that selects the operation. Start with project(action=\"get_status\").\n\n\u2550\u2550\u2550 DISCOVER ACTIONS \u2550\u2550\u2550\nTool descriptions are trimmed in lean mode. Find the action you need with:\n- catalog(action=\"search\", query=\"spawn actor\") - rank matching actions across every category\n- catalog(action=\"list_categories\") - the 23 categories with one-line summaries\n- <category>(action=\"describe\") - every action in one category (e.g. blueprint(action=\"describe\"))\n\nEach 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.\n\n\u2550\u2550\u2550 CATEGORIES \u2550\u2550\u2550\nproject, asset, blueprint, level, material, animation, landscape, pcg, foliage,\nniagara, audio, widget, editor, reflection, gameplay, gas, networking, demo,\nfeedback, statetree, chooser, plugins, epic (830 wrapped Unreal 5.8 tools; UE 5.8+).\n\n\u2550\u2550\u2550 FLOWS \u2550\u2550\u2550\nBefore chaining 3+ tool calls, check the `flows` field from project(action=\"get_status\")\nand run a matching flow with flow(action=\"run\", flowName=\"<name>\") instead of rebuilding it.\n\n\u2550\u2550\u2550 FEEDBACK \u2550\u2550\u2550\nIf you had to fall back to editor(action=\"execute_python\") because a native tool could not\ndo the job, tell the user when done and offer to feedback(action=\"submit\") the gap.\n\nFull mode (every action listed inline) is the default. This lean surface is selected by\ncontext.strategy: lean in ue-mcp.yml or UE_MCP_CONTEXT_STRATEGY=lean.\n";
3
+ export declare const SERVER_INSTRUCTIONS_MICRO = "UE-MCP (micro mode): Unreal Engine editor bridge (C++ plugin). The entire surface (23 categories, 600+ actions) is reached through a single gateway tool to keep context tiny.\n\n\u2550\u2550\u2550 HOW TO USE \u2550\u2550\u2550\n- tools(action=\"list_categories\") - list every category with a one-line summary\n- tools(action=\"describe\", category=\"blueprint\") - list a category's actions and how to call them\n- tools(action=\"call\", category=\"blueprint\", method=\"create\", args={ ... }) - invoke any action\n\n`method` is the action name; `args` is the object of that action's parameters.\nStart with: tools(action=\"call\", category=\"project\", method=\"get_status\").\n\n\u2550\u2550\u2550 CATEGORIES \u2550\u2550\u2550\nproject, asset, blueprint, level, material, animation, landscape, pcg, foliage,\nniagara, audio, widget, editor, reflection, gameplay, gas, networking, demo,\nfeedback, statetree, chooser, plugins, epic (830 wrapped Unreal 5.8 tools; UE 5.8+).\n\n\u2550\u2550\u2550 FLOWS \u2550\u2550\u2550\nflow(action=\"run\", flowName=\"<name>\") runs a named sequence; see the `flows` field\nfrom tools(action=\"call\", category=\"project\", method=\"get_status\").\n\nFull mode (every action listed inline) is the default. This micro surface is selected by\ncontext.strategy: micro in ue-mcp.yml or UE_MCP_CONTEXT_STRATEGY=micro.\n";
@@ -1,4 +1,4 @@
1
- export const SERVER_INSTRUCTIONS = `UE-MCP: Unreal Engine editor bridge (C++ plugin) - 23 category tools covering 677+ actions, plus 830 official Unreal 5.8 tools wrapped in-process (UE 5.8+; see the epic category).
1
+ export const SERVER_INSTRUCTIONS = `UE-MCP: Unreal Engine editor bridge (C++ plugin) - 24 category tools covering 685+ actions, plus 830 official Unreal 5.8 tools wrapped in-process (UE 5.8+; see the epic category).
2
2
 
3
3
  Every tool takes an "action" parameter that selects the operation. Call project(action="get_status") first.
4
4
 
@@ -82,14 +82,14 @@ This creates a GitHub issue so the maintainers can add proper support.
82
82
  // catalog is intentionally omitted: agents pull it on demand via the `catalog`
83
83
  // tool or a category's `describe` action. This keeps the initialize handshake
84
84
  // small for token-constrained clients while preserving full capability.
85
- export const SERVER_INSTRUCTIONS_LEAN = `UE-MCP (lean mode): Unreal Engine editor bridge (C++ plugin). 23 category tools; the per-action catalog is loaded on demand to keep context small.
85
+ export const SERVER_INSTRUCTIONS_LEAN = `UE-MCP (lean mode): Unreal Engine editor bridge (C++ plugin). 24 category tools; the per-action catalog is loaded on demand to keep context small.
86
86
 
87
87
  Every tool takes an "action" parameter that selects the operation. Start with project(action="get_status").
88
88
 
89
89
  ═══ DISCOVER ACTIONS ═══
90
90
  Tool descriptions are trimmed in lean mode. Find the action you need with:
91
91
  - catalog(action="search", query="spawn actor") - rank matching actions across every category
92
- - catalog(action="list_categories") - the 22 categories with one-line summaries
92
+ - catalog(action="list_categories") - the 23 categories with one-line summaries
93
93
  - <category>(action="describe") - every action in one category (e.g. blueprint(action="describe"))
94
94
 
95
95
  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.
@@ -113,7 +113,7 @@ context.strategy: lean in ue-mcp.yml or UE_MCP_CONTEXT_STRATEGY=lean.
113
113
  // Smallest surface (context.strategy = "micro"). The entire ue-mcp API is
114
114
  // reached through one gateway tool, mirroring the native MCP toolset gateway
115
115
  // (list_toolsets / describe_toolset / call_tool). Nothing else is advertised.
116
- export const SERVER_INSTRUCTIONS_MICRO = `UE-MCP (micro mode): Unreal Engine editor bridge (C++ plugin). The entire surface (22 categories, 600+ actions) is reached through a single gateway tool to keep context tiny.
116
+ export const SERVER_INSTRUCTIONS_MICRO = `UE-MCP (micro mode): Unreal Engine editor bridge (C++ plugin). The entire surface (23 categories, 600+ actions) is reached through a single gateway tool to keep context tiny.
117
117
 
118
118
  ═══ HOW TO USE ═══
119
119
  - tools(action="list_categories") - list every category with a one-line summary
@@ -7,7 +7,7 @@ import { type ToolDef } from "./types.js";
7
7
  * actions. That is great for discoverability but expensive on the MCP
8
8
  * initialize handshake for token-constrained clients.
9
9
  *
10
- * Lean mode keeps the exact same 23 typed category tools and their validated
10
+ * Lean mode keeps the exact same 24 typed category tools and their validated
11
11
  * `action` enums, but:
12
12
  * - trims each tool description to its one-line summary + a discovery pointer,
13
13
  * - trims the server instructions (see SERVER_INSTRUCTIONS_LEAN),
@@ -48,7 +48,7 @@ export declare function applyLeanContext(tools: ToolDef[]): ToolDef[];
48
48
  /**
49
49
  * Micro strategy: collapse the entire surface behind a single gateway tool,
50
50
  * mirroring the native MCP toolset gateway (list_toolsets / describe_toolset /
51
- * call_tool). The 22 category tools are NOT advertised; the agent enumerates
51
+ * call_tool). The 23 category tools are NOT advertised; the agent enumerates
52
52
  * with list_categories, learns a category with describe, and invokes anything
53
53
  * with call. This is the smallest possible seed.
54
54
  *
@@ -149,7 +149,7 @@ export function applyLeanContext(tools) {
149
149
  /**
150
150
  * Micro strategy: collapse the entire surface behind a single gateway tool,
151
151
  * mirroring the native MCP toolset gateway (list_toolsets / describe_toolset /
152
- * call_tool). The 22 category tools are NOT advertised; the agent enumerates
152
+ * call_tool). The 23 category tools are NOT advertised; the agent enumerates
153
153
  * with list_categories, learns a category with describe, and invokes anything
154
154
  * with call. This is the smallest possible seed.
155
155
  *
@@ -1,7 +1,7 @@
1
1
  {
2
- "tools": 23,
3
- "actions": 711,
4
- "bridgeActions": 677,
2
+ "tools": 24,
3
+ "actions": 719,
4
+ "bridgeActions": 685,
5
5
  "localActions": 34,
6
6
  "perTool": {
7
7
  "project": 32,
@@ -26,10 +26,11 @@
26
26
  "statetree": 36,
27
27
  "chooser": 7,
28
28
  "plugins": 2,
29
- "epic": 4
29
+ "epic": 4,
30
+ "fab": 8
30
31
  },
31
32
  "nativeToolsets": 52,
32
33
  "nativeToolActions": 830,
33
- "generatedAt": "2026-07-13T23:32:59.918Z",
34
- "version": "1.1.17"
34
+ "generatedAt": "2026-07-14T04:38:47.390Z",
35
+ "version": "1.1.18"
35
36
  }
@@ -0,0 +1,2 @@
1
+ import { type ToolDef } from "../types.js";
2
+ export declare const fabTool: ToolDef;
@@ -0,0 +1,29 @@
1
+ import { z } from "zod";
2
+ import { categoryTool, bp } from "../types.js";
3
+ // Fab asset importer. Fab is Epic's unified content marketplace; UE ships an
4
+ // editor plugin whose window is a web frontend (catalog browse, library,
5
+ // purchase, and signed-URL resolution all live on Epic's servers) sitting on a
6
+ // thin native download+import layer. This category drives the native pieces
7
+ // that don't need the web frontend: login lifecycle, syncing the user's owned
8
+ // library into the Content Browser, inspecting/clearing the download cache, and
9
+ // importing owned/local source files through the Fab import pipeline.
10
+ //
11
+ // Store catalog browsing and buying are intentionally out of scope: they need
12
+ // the authenticated Fab backend (no official consumer REST API exists) and stay
13
+ // in the web window. Log in there once, add items to your library, then use
14
+ // sync_library + import_file here.
15
+ export const fabTool = categoryTool("fab", "Import Fab (Epic marketplace) content: check plugin/login status, trigger login/logout, sync your owned library into the Content Browser, inspect/clear the download cache, and import owned or local source files into the project.", {
16
+ status: bp("Report Fab plugin state: whether the module is loaded, whether the native import/cache API is linked in this build, whether the Fab window has been opened this session, and the download cache location/size. Call this first.", "fab_status", () => ({})),
17
+ login: bp("Trigger the Fab login flow (EOS account portal). Asynchronous - returns once the flow is opened, not once authenticated. Complete any prompt, then call status.", "fab_login", () => ({})),
18
+ logout: bp("Clear the persistent Fab authentication for this device.", "fab_logout", () => ({})),
19
+ sync_library: bp("Load the user's owned Fab library (\"My Folder\") into the Content Browser via TEDS. Requires an active login; items appear asynchronously. Params: batchSize? (items per sync request).", "fab_sync_library", (p) => ({ batchSize: p.batchSize })),
20
+ list_cached: bp("List the entries currently in the local Fab download cache (already-downloaded owned assets). Params: none.", "fab_list_cached", () => ({})),
21
+ cache_info: bp("Report the Fab download cache location, total size, and entry count.", "fab_cache_info", () => ({})),
22
+ clear_cache: bp("Delete the local Fab download cache to reclaim disk. Does not affect assets already imported into the project.", "fab_clear_cache", () => ({})),
23
+ import_file: bp("Import a source file into the project through the Fab Interchange import pipeline. Use for owned assets that are downloaded/cached locally, or any local source file (fbx, textures). Single files import synchronously and report the created asset paths; pack/quixel workflows may run asynchronously. Params: source (absolute path to the source file on disk), destination (content path like /Game/Fab/Imported).", "fab_import_file", (p) => ({ source: p.source, destination: p.destination })),
24
+ }, undefined, {
25
+ batchSize: z.number().optional().describe("sync_library: number of library items to pull per sync request"),
26
+ source: z.string().optional().describe("import_file: absolute path to the source file on disk"),
27
+ destination: z.string().optional().describe("import_file: destination content path, e.g. /Game/Fab/Imported"),
28
+ });
29
+ //# sourceMappingURL=fab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fab.js","sourceRoot":"","sources":["../../src/tools/fab.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,EAAE,EAAgB,MAAM,aAAa,CAAC;AAE7D,6EAA6E;AAC7E,yEAAyE;AACzE,+EAA+E;AAC/E,4EAA4E;AAC5E,8EAA8E;AAC9E,gFAAgF;AAChF,sEAAsE;AACtE,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,4EAA4E;AAC5E,mCAAmC;AACnC,MAAM,CAAC,MAAM,OAAO,GAAY,YAAY,CAC1C,KAAK,EACL,sOAAsO,EACtO;IACE,MAAM,EAAQ,EAAE,CAAC,iOAAiO,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7Q,KAAK,EAAS,EAAE,CAAC,iKAAiK,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAC5M,MAAM,EAAQ,EAAE,CAAC,0DAA0D,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACtG,YAAY,EAAE,EAAE,CAAC,0LAA0L,EAAE,kBAAkB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IACrQ,WAAW,EAAG,EAAE,CAAC,6GAA6G,EAAE,iBAAiB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9J,UAAU,EAAI,EAAE,CAAC,sEAAsE,EAAE,gBAAgB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACtH,WAAW,EAAG,EAAE,CAAC,gHAAgH,EAAE,iBAAiB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACjK,WAAW,EAAG,EAAE,CAAC,0ZAA0Z,EAAE,iBAAiB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;CAC3f,EACD,SAAS,EACT;IACE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gEAAgE,CAAC;IAC3G,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uDAAuD,CAAC;IAC/F,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gEAAgE,CAAC;CAC9G,CACF,CAAC"}
package/dist/tools.js CHANGED
@@ -21,6 +21,7 @@ import { statetreeTool } from "./tools/statetree.js";
21
21
  import { chooserTool } from "./tools/chooser.js";
22
22
  import { pluginsTool } from "./tools/plugins.js";
23
23
  import { epicTool } from "./tools/epic.js";
24
+ import { fabTool } from "./tools/fab.js";
24
25
  export const ALL_TOOLS = [
25
26
  projectTool,
26
27
  assetTool,
@@ -45,6 +46,7 @@ export const ALL_TOOLS = [
45
46
  chooserTool,
46
47
  pluginsTool,
47
48
  epicTool,
49
+ fabTool,
48
50
  ];
49
51
  /** Flatten to (toolName, actionName, bridgeMethod) triples for every action
50
52
  * that dispatches to a C++ bridge method (i.e. has `bridge` set). Local-only
package/dist/tools.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,MAAM,CAAC,MAAM,SAAS,GAAc;IAClC,WAAW;IACX,SAAS;IACT,aAAa;IACb,SAAS;IACT,YAAY;IACZ,aAAa;IACb,aAAa;IACb,OAAO;IACP,WAAW;IACX,WAAW;IACX,SAAS;IACT,UAAU;IACV,UAAU;IACV,cAAc;IACd,YAAY;IACZ,OAAO;IACP,cAAc;IACd,QAAQ;IACR,YAAY;IACZ,aAAa;IACb,WAAW;IACX,WAAW;IACX,QAAQ;CACT,CAAC;AAEF;;4DAE4D;AAC5D,MAAM,UAAU,sBAAsB;IAKpC,MAAM,GAAG,GAA4D,EAAE,CAAC;IACxE,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;YACvD,IAAI,IAAI,CAAC,MAAM;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,MAAM,CAAC,MAAM,SAAS,GAAc;IAClC,WAAW;IACX,SAAS;IACT,aAAa;IACb,SAAS;IACT,YAAY;IACZ,aAAa;IACb,aAAa;IACb,OAAO;IACP,WAAW;IACX,WAAW;IACX,SAAS;IACT,UAAU;IACV,UAAU;IACV,cAAc;IACd,YAAY;IACZ,OAAO;IACP,cAAc;IACd,QAAQ;IACR,YAAY;IACZ,aAAa;IACb,WAAW;IACX,WAAW;IACX,QAAQ;IACR,OAAO;CACR,CAAC;AAEF;;4DAE4D;AAC5D,MAAM,UAAU,sBAAsB;IAKpC,MAAM,GAAG,GAA4D,EAAE,CAAC;IACxE,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;YACvD,IAAI,IAAI,CAAC,MAAM;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ue-mcp",
3
- "version": "1.1.17",
4
- "description": "Unreal Engine MCP server - 23 tools, 711+ actions for AI-driven editor control",
3
+ "version": "1.1.18",
4
+ "description": "Unreal Engine MCP server - 24 tools, 719+ actions for AI-driven editor control",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "exports": {
@@ -36,6 +36,7 @@
36
36
  #include "Handlers/StateTreeHandlers.h"
37
37
  #include "Handlers/ChooserHandlers.h"
38
38
  #include "Handlers/EpicHandlers.h"
39
+ #include "Handlers/FabHandlers.h"
39
40
 
40
41
  // Platform-specific socket includes
41
42
  #if PLATFORM_WINDOWS
@@ -94,6 +95,7 @@ FMCPBridgeServer::FMCPBridgeServer(int32 Port)
94
95
  FStateTreeHandlers::RegisterHandlers(HandlerRegistry);
95
96
  FChooserHandlers::RegisterHandlers(HandlerRegistry);
96
97
  FEpicHandlers::RegisterHandlers(HandlerRegistry);
98
+ FFabHandlers::RegisterHandlers(HandlerRegistry);
97
99
  }
98
100
 
99
101
  FMCPBridgeServer::~FMCPBridgeServer()
@@ -413,7 +413,13 @@ TSharedPtr<FJsonValue> FAnimationHandlers::CreateMirrorDataTable(const TSharedPt
413
413
  Table->MirrorFindReplaceExpressions.Add(FMirrorFindReplaceExpression(TEXT("_r"), TEXT("_l"), EMirrorFindReplaceMethod::Suffix));
414
414
  }
415
415
 
416
+ // UMirrorDataTable::UpdateFromFindReplaceExpressions(FFindReplaceOptions) is
417
+ // UE 5.8+. On 5.7 the expressions are stored on the table above but the
418
+ // mirror rows are not auto-synced from them here; the asset editor's Sync
419
+ // button (or manual row entry) fills them in.
420
+ #if ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8)
416
421
  Table->UpdateFromFindReplaceExpressions(UMirrorDataTable::FFindReplaceOptions::Sync());
422
+ #endif
417
423
  UEditorAssetLibrary::SaveLoadedAsset(Table);
418
424
 
419
425
  TSharedPtr<FJsonObject> Res = MCPSuccess();
@@ -1930,6 +1930,12 @@ TSharedPtr<FJsonValue> FAnimationHandlers::BatchRetargetAnimations(const TShared
1930
1930
  return MCPError(TEXT("Missing 'animPaths' (array of AnimSequence paths to retarget)"));
1931
1931
  }
1932
1932
 
1933
+ // The FIKRetargetBatchOperationInputs / UIKRetargetBatchOperation::RunBatchRetarget
1934
+ // batch API is UE 5.8+. The 5.7 batch-retarget API differs; rather than a
1935
+ // partial reimplementation, return a clear error below 5.8.
1936
+ #if !(ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8))
1937
+ return MCPError(TEXT("batch_retarget_animations requires UE 5.8+ (the FIKRetargetBatchOperationInputs / RunBatchRetarget API is unavailable in this engine version)."));
1938
+ #else
1933
1939
  FIKRetargetBatchOperationInputs Inputs;
1934
1940
  Inputs.SourceMesh = SourceMesh;
1935
1941
  Inputs.TargetMesh = TargetMesh;
@@ -1966,6 +1972,7 @@ TSharedPtr<FJsonValue> FAnimationHandlers::BatchRetargetAnimations(const TShared
1966
1972
  Result->SetNumberField(TEXT("createdCount"), OutPaths.Num());
1967
1973
  Result->SetArrayField(TEXT("createdAssets"), OutPaths);
1968
1974
  return MCPResult(Result);
1975
+ #endif
1969
1976
  }
1970
1977
 
1971
1978
  // ─── #657 inspect_anim_nodes ────────────────────────────────────────
@@ -2356,7 +2356,9 @@ TSharedPtr<FJsonValue> FAssetHandlers::SetStringTableEntry(const TSharedPtr<FJso
2356
2356
  const bool bExisted = StringTable->GetStringTable()->GetSourceString(EntryKey, PreviousSourceString);
2357
2357
 
2358
2358
  StringTable->Modify(true);
2359
- #if WITH_EDITORONLY_DATA
2359
+ // The 3-arg SetSourceString (with a trailing metadata/namespace arg) is UE 5.8+.
2360
+ // 5.7 (and non-editor) take the 2-arg form.
2361
+ #if WITH_EDITORONLY_DATA && (ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8))
2360
2362
  StringTable->GetMutableStringTable()->SetSourceString(EntryKey, SourceString, FString());
2361
2363
  #else
2362
2364
  StringTable->GetMutableStringTable()->SetSourceString(EntryKey, SourceString);
@@ -234,9 +234,6 @@ static UScriptStruct* ResolveChooserStruct(const FString& TypeName)
234
234
  static void ConfigureColumnInput(FChooserColumnBase* Col, const FString& InputStruct, const FString& BoundProperty, const FString& EnumPath)
235
235
  {
236
236
  #if WITH_EDITOR
237
- FInstancedStruct* InputPtr = Col->GetInputValuePtr();
238
- if (!InputPtr) return;
239
-
240
237
  if (!InputStruct.IsEmpty())
241
238
  {
242
239
  if (UScriptStruct* InSS = ResolveChooserStruct(InputStruct))
@@ -244,10 +241,24 @@ static void ConfigureColumnInput(FChooserColumnBase* Col, const FString& InputSt
244
241
  Col->SetInputType(InSS);
245
242
  }
246
243
  }
247
- if (!InputPtr->IsValid()) return;
248
244
 
249
- const UScriptStruct* PSS = InputPtr->GetScriptStruct();
250
- void* PData = InputPtr->GetMutableMemory();
245
+ // The input parameter's struct type + memory. FChooserColumnBase::GetInputValuePtr()
246
+ // (a FInstancedStruct accessor) is UE 5.8+; on 5.7 reach the same memory through
247
+ // GetInputValue() (the parameter pointer) + GetInputType() (its struct).
248
+ const UScriptStruct* PSS = nullptr;
249
+ void* PData = nullptr;
250
+ #if ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8)
251
+ FInstancedStruct* InputPtr = Col->GetInputValuePtr();
252
+ if (!InputPtr || !InputPtr->IsValid()) return;
253
+ PSS = InputPtr->GetScriptStruct();
254
+ PData = InputPtr->GetMutableMemory();
255
+ #else
256
+ FChooserParameterBase* Param = Col->GetInputValue();
257
+ PSS = Col->GetInputType();
258
+ if (!Param || !PSS) return;
259
+ PData = Param;
260
+ #endif
261
+
251
262
  FStructProperty* BindingProp = CastField<FStructProperty>(PSS->FindPropertyByName(TEXT("Binding")));
252
263
  if (!BindingProp) return;
253
264
  void* BindingData = BindingProp->ContainerPtrToValuePtr<void>(PData);
@@ -632,8 +643,15 @@ TSharedPtr<FJsonValue> FChooserHandlers::DeleteRow(const TSharedPtr<FJsonObject>
632
643
  Table->Modify();
633
644
 
634
645
  // Drop the per-row cell from every column, then the result + disabled flag.
646
+ // FChooserColumnBase::DeleteRows takes a TArrayView<int32> on UE 5.8+ and a
647
+ // TArray<uint32> on 5.7.
648
+ #if ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8)
635
649
  int32 RowToDelete = RowIndex;
636
650
  TArrayView<int32> RowView(&RowToDelete, 1);
651
+ #else
652
+ TArray<uint32> RowView;
653
+ RowView.Add(static_cast<uint32>(RowIndex));
654
+ #endif
637
655
  for (FInstancedStruct& ColStruct : Table->ColumnsStructs)
638
656
  {
639
657
  if (FChooserColumnBase* Col = ColStruct.GetMutablePtr<FChooserColumnBase>())
@@ -0,0 +1,244 @@
1
+ #include "FabHandlers.h"
2
+ #include "HandlerRegistry.h"
3
+ #include "HandlerUtils.h"
4
+
5
+ #include "Modules/ModuleManager.h"
6
+ #include "UObject/UObjectHash.h"
7
+ #include "Misc/PackageName.h"
8
+ #include "Engine/Engine.h"
9
+
10
+ #ifndef WITH_FAB_PLUGIN
11
+ #define WITH_FAB_PLUGIN 0
12
+ #endif
13
+
14
+ #if WITH_FAB_PLUGIN
15
+ #include "Importers/GenericAssetImporter.h"
16
+ #include "Utilities/FabAssetsCache.h"
17
+ #endif
18
+
19
+ // ─── Helpers ──────────────────────────────────────────────────────────────
20
+
21
+ static const TCHAR* FabModuleName = TEXT("Fab");
22
+
23
+ // The Fab plugin is an engine editor plugin, enabled by default on 5.8 but not
24
+ // guaranteed to be present or loaded (disabled, or an older engine). Every
25
+ // action funnels through this so callers get a clear message instead of a
26
+ // crash or an "Unknown method".
27
+ static bool IsFabModuleLoaded()
28
+ {
29
+ return FModuleManager::Get().IsModuleLoaded(FabModuleName);
30
+ }
31
+
32
+ // Run one of the Fab plugin's registered console commands (Fab.Login,
33
+ // Fab.Logout, Fab.ClearCache, Fab.TEDS.MyFolderIntegration, ...). These are
34
+ // FAutoConsoleCommands, so they route through GEngine->Exec without any
35
+ // compile-time dependency on the Fab module - the login/sync/clear paths work
36
+ // even when WITH_FAB_PLUGIN is off (as long as the module is loaded).
37
+ static bool RunFabConsoleCommand(const FString& Command)
38
+ {
39
+ if (!GEngine) return false;
40
+ return GEngine->Exec(nullptr, *Command);
41
+ }
42
+
43
+ // Best-effort login/window hint: whether a UFabBrowserApi (the JS<->native
44
+ // bridge object created with the Fab window) currently exists. Reached by
45
+ // reflection so we never link the plugin's Private headers. Not a definitive
46
+ // auth check - the real login state lives in EOS behind private symbols - but
47
+ // a useful "has the Fab window been opened this session" signal.
48
+ static bool HasFabBrowserApiInstance()
49
+ {
50
+ UClass* ApiClass = FindObject<UClass>(nullptr, TEXT("/Script/Fab.FabBrowserApi"));
51
+ if (!ApiClass) return false;
52
+ TArray<UObject*> Instances;
53
+ GetObjectsOfClass(ApiClass, Instances, /*bIncludeDerivedClasses=*/true, RF_ClassDefaultObject);
54
+ return Instances.Num() > 0;
55
+ }
56
+
57
+ // ─── Actions ──────────────────────────────────────────────────────────────
58
+
59
+ TSharedPtr<FJsonValue> FFabHandlers::Status(const TSharedPtr<FJsonObject>& Params)
60
+ {
61
+ auto Res = MCPSuccess();
62
+ const bool bLoaded = IsFabModuleLoaded();
63
+ Res->SetBoolField(TEXT("pluginLoaded"), bLoaded);
64
+ // Compile-time link state: whether cache/import actions are backed by the
65
+ // native Fab API in this build, vs. console-command-only.
66
+ Res->SetBoolField(TEXT("nativeApiLinked"), WITH_FAB_PLUGIN ? true : false);
67
+ Res->SetBoolField(TEXT("fabWindowOpened"), HasFabBrowserApiInstance());
68
+
69
+ if (!bLoaded)
70
+ {
71
+ Res->SetStringField(TEXT("note"), TEXT("Fab plugin module not loaded. It ships enabled by default on UE 5.8; enable it in the editor's Plugins panel if missing."));
72
+ return MCPResult(Res);
73
+ }
74
+
75
+ #if WITH_FAB_PLUGIN
76
+ Res->SetStringField(TEXT("cacheLocation"), FFabAssetsCache::GetCacheLocation());
77
+ Res->SetNumberField(TEXT("cacheSizeBytes"), (double)FFabAssetsCache::GetCacheSize());
78
+ Res->SetStringField(TEXT("cacheSize"), FFabAssetsCache::GetCacheSizeString().ToString());
79
+ #endif
80
+ return MCPResult(Res);
81
+ }
82
+
83
+ TSharedPtr<FJsonValue> FFabHandlers::Login(const TSharedPtr<FJsonObject>& Params)
84
+ {
85
+ if (!IsFabModuleLoaded()) return MCPError(TEXT("Fab plugin not loaded"));
86
+ // Fab.Login opens the EOS account-portal login flow. Asynchronous: this
87
+ // returns once the flow is triggered, not once the user has authenticated.
88
+ if (!RunFabConsoleCommand(TEXT("Fab.Login")))
89
+ return MCPError(TEXT("Failed to invoke Fab.Login console command"));
90
+ auto Res = MCPSuccess();
91
+ Res->SetStringField(TEXT("note"), TEXT("Login flow triggered. Complete authentication in the account-portal window if prompted; call fab(status) afterward."));
92
+ return MCPResult(Res);
93
+ }
94
+
95
+ TSharedPtr<FJsonValue> FFabHandlers::Logout(const TSharedPtr<FJsonObject>& Params)
96
+ {
97
+ if (!IsFabModuleLoaded()) return MCPError(TEXT("Fab plugin not loaded"));
98
+ if (!RunFabConsoleCommand(TEXT("Fab.Logout")))
99
+ return MCPError(TEXT("Failed to invoke Fab.Logout console command"));
100
+ auto Res = MCPSuccess();
101
+ Res->SetStringField(TEXT("note"), TEXT("Persistent Fab auth cleared."));
102
+ return MCPResult(Res);
103
+ }
104
+
105
+ TSharedPtr<FJsonValue> FFabHandlers::SyncLibrary(const TSharedPtr<FJsonObject>& Params)
106
+ {
107
+ if (!IsFabModuleLoaded()) return MCPError(TEXT("Fab plugin not loaded"));
108
+ // Loads the user's owned Fab library ("My Folder") into TEDS so it surfaces
109
+ // in the Content Browser. Optional batch size controls how many items are
110
+ // pulled per sync request.
111
+ const int32 BatchSize = OptionalInt(Params, TEXT("batchSize"), 0);
112
+ const FString Command = BatchSize > 0
113
+ ? FString::Printf(TEXT("Fab.TEDS.MyFolderIntegration %d"), BatchSize)
114
+ : FString(TEXT("Fab.TEDS.MyFolderIntegration"));
115
+ if (!RunFabConsoleCommand(Command))
116
+ return MCPError(TEXT("Failed to invoke Fab.TEDS.MyFolderIntegration console command"));
117
+ auto Res = MCPSuccess();
118
+ Res->SetStringField(TEXT("note"), TEXT("Library sync queued. Owned Fab items load into the Content Browser asynchronously; requires an active Fab login."));
119
+ return MCPResult(Res);
120
+ }
121
+
122
+ TSharedPtr<FJsonValue> FFabHandlers::ListCached(const TSharedPtr<FJsonObject>& Params)
123
+ {
124
+ if (!IsFabModuleLoaded()) return MCPError(TEXT("Fab plugin not loaded"));
125
+ #if WITH_FAB_PLUGIN
126
+ auto Res = MCPSuccess();
127
+ const TArray<FString> Cached = FFabAssetsCache::GetCachedAssets();
128
+ TArray<TSharedPtr<FJsonValue>> Arr;
129
+ for (const FString& Entry : Cached)
130
+ {
131
+ Arr.Add(MakeShared<FJsonValueString>(Entry));
132
+ }
133
+ Res->SetArrayField(TEXT("cachedAssets"), Arr);
134
+ Res->SetNumberField(TEXT("count"), Cached.Num());
135
+ Res->SetStringField(TEXT("cacheLocation"), FFabAssetsCache::GetCacheLocation());
136
+ return MCPResult(Res);
137
+ #else
138
+ return MCPError(TEXT("Fab native API not linked in this build (Fab plugin absent at build time). Cache listing unavailable."));
139
+ #endif
140
+ }
141
+
142
+ TSharedPtr<FJsonValue> FFabHandlers::CacheInfo(const TSharedPtr<FJsonObject>& Params)
143
+ {
144
+ if (!IsFabModuleLoaded()) return MCPError(TEXT("Fab plugin not loaded"));
145
+ #if WITH_FAB_PLUGIN
146
+ auto Res = MCPSuccess();
147
+ Res->SetStringField(TEXT("cacheLocation"), FFabAssetsCache::GetCacheLocation());
148
+ Res->SetNumberField(TEXT("cacheSizeBytes"), (double)FFabAssetsCache::GetCacheSize());
149
+ Res->SetStringField(TEXT("cacheSize"), FFabAssetsCache::GetCacheSizeString().ToString());
150
+ Res->SetNumberField(TEXT("count"), FFabAssetsCache::GetCachedAssets().Num());
151
+ return MCPResult(Res);
152
+ #else
153
+ return MCPError(TEXT("Fab native API not linked in this build (Fab plugin absent at build time). Cache info unavailable."));
154
+ #endif
155
+ }
156
+
157
+ TSharedPtr<FJsonValue> FFabHandlers::ClearCache(const TSharedPtr<FJsonObject>& Params)
158
+ {
159
+ if (!IsFabModuleLoaded()) return MCPError(TEXT("Fab plugin not loaded"));
160
+ // Routed through the console command so this works without WITH_FAB_PLUGIN.
161
+ if (!RunFabConsoleCommand(TEXT("Fab.ClearCache")))
162
+ return MCPError(TEXT("Failed to invoke Fab.ClearCache console command"));
163
+ auto Res = MCPSuccess();
164
+ Res->SetStringField(TEXT("note"), TEXT("Fab download cache cleared."));
165
+ return MCPResult(Res);
166
+ }
167
+
168
+ TSharedPtr<FJsonValue> FFabHandlers::ImportFile(const TSharedPtr<FJsonObject>& Params)
169
+ {
170
+ if (!IsFabModuleLoaded()) return MCPError(TEXT("Fab plugin not loaded"));
171
+ #if WITH_FAB_PLUGIN
172
+ FString Source;
173
+ if (auto Err = RequireStringAlt(Params, TEXT("source"), TEXT("sourceFile"), Source)) return Err;
174
+ FString Destination;
175
+ if (auto Err = RequireStringAlt(Params, TEXT("destination"), TEXT("destPath"), Destination)) return Err;
176
+
177
+ if (!FPaths::FileExists(Source))
178
+ return MCPError(FString::Printf(TEXT("Source file not found on disk: %s"), *Source));
179
+ if (!Destination.StartsWith(TEXT("/")))
180
+ return MCPError(FString::Printf(TEXT("Destination must be a content path like /Game/Fab/Imported (got '%s')"), *Destination));
181
+ if (!FPackageName::IsValidLongPackageName(Destination, /*bIncludeReadOnlyRoots=*/true))
182
+ return MCPError(FString::Printf(TEXT("Destination is not a valid content path: %s"), *Destination));
183
+
184
+ // FFabGenericImporter::ImportAsset runs the Fab Interchange pipeline and
185
+ // delivers the created objects via callback. Single source files (fbx,
186
+ // textures) complete on the game thread synchronously, so the callback has
187
+ // fired by the time ImportAsset returns; pack/quixel workflows can defer.
188
+ // We capture whatever the callback produced and report accordingly rather
189
+ // than blocking the game thread.
190
+ TSharedRef<bool> bCompleted = MakeShared<bool>(false);
191
+ TSharedRef<TArray<FString>> ImportedPaths = MakeShared<TArray<FString>>();
192
+
193
+ FFabGenericImporter::ImportAsset(
194
+ { Source },
195
+ Destination,
196
+ [bCompleted, ImportedPaths](const TArray<UObject*>& Objects)
197
+ {
198
+ *bCompleted = true;
199
+ for (const UObject* Obj : Objects)
200
+ {
201
+ if (Obj) ImportedPaths->Add(Obj->GetPathName());
202
+ }
203
+ });
204
+
205
+ auto Res = MCPSuccess();
206
+ Res->SetStringField(TEXT("source"), Source);
207
+ Res->SetStringField(TEXT("destination"), Destination);
208
+ if (*bCompleted)
209
+ {
210
+ TArray<TSharedPtr<FJsonValue>> Arr;
211
+ for (const FString& P : *ImportedPaths)
212
+ {
213
+ Arr.Add(MakeShared<FJsonValueString>(P));
214
+ }
215
+ Res->SetArrayField(TEXT("importedAssets"), Arr);
216
+ Res->SetNumberField(TEXT("count"), ImportedPaths->Num());
217
+ MCPSetCreated(Res);
218
+ }
219
+ else
220
+ {
221
+ // Async workflow (pack extraction, quixel gltf, plugin install): the
222
+ // import is running and will land in Destination shortly.
223
+ Res->SetBoolField(TEXT("async"), true);
224
+ Res->SetStringField(TEXT("note"), FString::Printf(TEXT("Import running asynchronously into %s; poll asset(list) on that path to confirm."), *Destination));
225
+ }
226
+ return MCPResult(Res);
227
+ #else
228
+ return MCPError(TEXT("Fab native API not linked in this build (Fab plugin absent at build time). Import unavailable."));
229
+ #endif
230
+ }
231
+
232
+ // ─── Registration ──────────────────────────────────────────────────────────
233
+
234
+ void FFabHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
235
+ {
236
+ Registry.RegisterHandler(TEXT("fab_status"), &Status);
237
+ Registry.RegisterHandler(TEXT("fab_login"), &Login);
238
+ Registry.RegisterHandler(TEXT("fab_logout"), &Logout);
239
+ Registry.RegisterHandler(TEXT("fab_sync_library"), &SyncLibrary);
240
+ Registry.RegisterHandler(TEXT("fab_list_cached"), &ListCached);
241
+ Registry.RegisterHandler(TEXT("fab_cache_info"), &CacheInfo);
242
+ Registry.RegisterHandler(TEXT("fab_clear_cache"), &ClearCache);
243
+ Registry.RegisterHandler(TEXT("fab_import_file"), &ImportFile);
244
+ }
@@ -0,0 +1,33 @@
1
+ #pragma once
2
+
3
+ #include "CoreMinimal.h"
4
+ #include "Dom/JsonValue.h"
5
+ #include "Dom/JsonObject.h"
6
+
7
+ // Fab asset importer. Fab is Epic's unified content marketplace; the engine
8
+ // ships an editor plugin (/Engine/Plugins/Fab) whose window is a web frontend
9
+ // (catalog browse, library, purchase, signed-URL resolution all live server
10
+ // side) sitting on top of a thin native download+import layer. This category
11
+ // drives the parts of that native layer that don't need the web frontend:
12
+ // login lifecycle, syncing the user's owned library into the Content Browser,
13
+ // inspecting/clearing the download cache, and importing owned/local source
14
+ // files into the project via the Fab import pipeline.
15
+ //
16
+ // Store catalog browsing and signed-URL resolution are deliberately out of
17
+ // scope for this v1: they require the authenticated Fab backend (no official
18
+ // consumer REST API exists), so they stay in the web window.
19
+ class FFabHandlers
20
+ {
21
+ public:
22
+ static void RegisterHandlers(class FMCPHandlerRegistry& Registry);
23
+
24
+ private:
25
+ static TSharedPtr<FJsonValue> Status(const TSharedPtr<FJsonObject>& Params);
26
+ static TSharedPtr<FJsonValue> Login(const TSharedPtr<FJsonObject>& Params);
27
+ static TSharedPtr<FJsonValue> Logout(const TSharedPtr<FJsonObject>& Params);
28
+ static TSharedPtr<FJsonValue> SyncLibrary(const TSharedPtr<FJsonObject>& Params);
29
+ static TSharedPtr<FJsonValue> ListCached(const TSharedPtr<FJsonObject>& Params);
30
+ static TSharedPtr<FJsonValue> CacheInfo(const TSharedPtr<FJsonObject>& Params);
31
+ static TSharedPtr<FJsonValue> ClearCache(const TSharedPtr<FJsonObject>& Params);
32
+ static TSharedPtr<FJsonValue> ImportFile(const TSharedPtr<FJsonObject>& Params);
33
+ };
@@ -569,11 +569,17 @@ static bool AddEditorNodeToArray(TArray<FStateTreeEditorNode>& Arr, const FStrin
569
569
  // the node's instance data type is a UClass (UObject-backed nodes such as
570
570
  // the Blueprint task/condition/evaluator wrappers), which the previous
571
571
  // struct-only path left null, so BP tasks could not be wired.
572
+ // The 3-arg FStateTreeEditorNode::InitializeAs(Outer, NodeStruct) - which also
573
+ // allocates the InstanceObject for UObject-backed (Blueprint-wrapped) nodes -
574
+ // is UE 5.8+. On 5.7 we fall back to the struct-only path; BP-wrapped nodes
575
+ // therefore do not get their InstanceObject auto-wired on 5.7 (#681).
576
+ #if ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8)
572
577
  if (Outer)
573
578
  {
574
579
  EditorNode.InitializeAs(Outer, NodeStruct);
575
580
  }
576
581
  else
582
+ #endif
577
583
  {
578
584
  EditorNode.Node.InitializeAs(NodeStruct);
579
585
  const FStateTreeNodeBase& Node = EditorNode.Node.Get<FStateTreeNodeBase>();
@@ -582,6 +588,9 @@ static bool AddEditorNodeToArray(TArray<FStateTreeEditorNode>& Arr, const FStrin
582
588
  EditorNode.Instance.InitializeAs(InstanceType);
583
589
  }
584
590
  }
591
+ #if !(ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8))
592
+ (void)Outer; // unused on 5.7 (the Outer-based path above is 5.8+ only)
593
+ #endif
585
594
 
586
595
  if (InstanceProperties.IsValid())
587
596
  {
@@ -1894,7 +1903,13 @@ TSharedPtr<FJsonValue> FStateTreeHandlers::AddBinding(const TSharedPtr<FJsonObje
1894
1903
  {
1895
1904
  for (FStateTreeEditorNode& EN : Arr)
1896
1905
  {
1906
+ // FStateTreeEditorNode::GetInstanceDataID() is UE 5.8+. On 5.7 the
1907
+ // node's editor ID keys the instance data, so match on that.
1908
+ #if ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8)
1897
1909
  if (EN.GetInstanceDataID() != TargetID) continue;
1910
+ #else
1911
+ if (EN.ID != TargetID) continue;
1912
+ #endif
1898
1913
  if (FStateTreeNodeBase* NodeBase = EN.Node.GetMutablePtr<FStateTreeNodeBase>())
1899
1914
  {
1900
1915
  NodeBase->OnBindingChanged(EN.ID, EN.GetInstance(), SourcePath, TargetPath, Lookup);
@@ -102,5 +102,23 @@ public class UE_MCP_Bridge : ModuleRules
102
102
  {
103
103
  PrivateDependencyModuleNames.Add("LiveCoding");
104
104
  }
105
+
106
+ // Fab is Epic's marketplace plugin. It ships enabled by default on UE 5.8
107
+ // but is absent on older engines and can be disabled, so we do not hard
108
+ // depend on it: detect the plugin on disk and only then link its native
109
+ // import/cache API, guarding those code paths with WITH_FAB_PLUGIN. When
110
+ // absent, the Fab handlers still register and fall back to console-command
111
+ // paths (login/sync/clear) or return a clean "not available" error.
112
+ bool bFabPluginPresent = System.IO.Directory.Exists(
113
+ System.IO.Path.Combine(EngineDirectory, "Plugins", "Fab"));
114
+ if (bFabPluginPresent && Target.bBuildEditor)
115
+ {
116
+ PrivateDependencyModuleNames.Add("Fab");
117
+ PublicDefinitions.Add("WITH_FAB_PLUGIN=1");
118
+ }
119
+ else
120
+ {
121
+ PublicDefinitions.Add("WITH_FAB_PLUGIN=0");
122
+ }
105
123
  }
106
124
  }