Files
unreal-engine-mcp-system-pl…/Source/UEBlueprintMCPEditor/UEBlueprintMCPEditor.Build.cs
Bonchellon 341e694ab5 Add in-editor Slate setup wizard (Tools > MCP Setup Wizard)
Native UE editor tab (SMCPWizard) so setup happens inside Unreal, not just the
Node CLI. Three sections:
- Remote Control: live status of the :30010 bridge + a button to start it
  (WebControl.StartServer + EnableServerOnStartup).
- API keys: paste/save the agent-gateway Anthropic token (auth.env) and
  ElevenLabs key (integrations.json) — both git-ignored.
- MCP tools: lists the catalogue from tools.json.

Wiring:
- UEBlueprintMCPEditor module registers a nomad tab + a Tools-menu entry.
- Build.cs gains the Projects module (IPluginManager).
- server.js exports TOOLS and only launches the stdio server when run directly
  (isMain), so src/dumpTools.mjs can generate tools.json. `npm run dump-tools`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 01:35:53 +03:00

131 lines
4.6 KiB
C#

using System;
using System.IO;
using System.Collections.Generic;
using UnrealBuildTool;
public class UEBlueprintMCPEditor : ModuleRules
{
public UEBlueprintMCPEditor(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[]
{
"Core",
"CoreUObject",
"Engine",
"UMG",
});
PrivateDependencyModuleNames.AddRange(new string[]
{
"UnrealEd",
"UMGEditor",
"BlueprintGraph",
"KismetCompiler",
"Kismet",
"GraphEditor",
"InputCore",
"Slate",
"SlateCore",
"EditorSubsystem",
"ToolMenus",
"AssetTools",
"Projects",
"Json",
"JsonUtilities",
"AssetRegistry",
"HTTP",
"Niagara",
"NiagaraCore",
"NiagaraEditor",
"ImageWrapper",
"RenderCore",
"RHI",
"MovieScene",
"MovieSceneTracks",
});
// ---- Optional Voxel integration -----------------------------------
// voxel_graph_op needs Voxel's editor modules. Voxel is a heavy third-party
// plugin most projects don't have, so it is wired in ONLY when present. The
// C++ side guards everything behind WITH_MCP_VOXEL and compiles to stubs at 0.
// env UE_MCP_WITH_VOXEL = "1" -> force on (errors if the plugin is missing)
// env UE_MCP_WITH_VOXEL = "0" -> force off
// unset -> auto-detect
string voxelEnv = Environment.GetEnvironmentVariable("UE_MCP_WITH_VOXEL");
string voxelPrivate = (voxelEnv == "0") ? null : FindVoxelGraphEditorPrivate(Target);
bool voxelEnabled = voxelPrivate != null && voxelEnv != "0";
if (voxelEnabled)
{
// Reach a few VoxelGraphEditor private headers (node classes). We only use
// inline/virtual members, so no private link symbols cross the boundary.
PrivateIncludePaths.Add(voxelPrivate);
PrivateDependencyModuleNames.AddRange(new string[]
{
"VoxelCore",
"VoxelCoreEditor",
"VoxelGraph",
"VoxelGraphEditor",
});
PublicDefinitions.Add("WITH_MCP_VOXEL=1");
}
else
{
if (voxelEnv == "1")
{
throw new BuildException(
"UE_MCP_WITH_VOXEL=1 but the Voxel plugin (Source/VoxelGraphEditor/Private) " +
"could not be located. Install the Voxel plugin into this project or the engine, " +
"or unset UE_MCP_WITH_VOXEL.");
}
PublicDefinitions.Add("WITH_MCP_VOXEL=0");
}
}
// Locate <Voxel>/Source/VoxelGraphEditor/Private by scanning the project's and
// engine's plugin folders for Voxel.uplugin. Returns null if Voxel isn't installed.
private string FindVoxelGraphEditorPrivate(ReadOnlyTargetRules Target)
{
var roots = new List<string>();
if (Target.ProjectFile != null)
{
roots.Add(Path.Combine(Target.ProjectFile.Directory.FullName, "Plugins"));
}
// The Plugins folder this plugin itself lives in (covers <Project>/Plugins/Voxel).
roots.Add(Path.GetFullPath(Path.Combine(ModuleDirectory, "..", "..", "..")));
try { roots.Add(Path.Combine(EngineDirectory, "Plugins")); } catch { }
foreach (string root in roots)
{
string hit = FindVoxelUnder(root, 0);
if (hit != null) return hit;
}
return null;
}
// Bounded, fault-tolerant recursive search (GetFiles+AllDirectories aborts wholesale
// on a single inaccessible subdir, so we walk manually and swallow per-dir errors).
private string FindVoxelUnder(string dir, int depth)
{
if (depth > 5 || string.IsNullOrEmpty(dir) || !Directory.Exists(dir)) return null;
try
{
string upl = Path.Combine(dir, "Voxel.uplugin");
if (File.Exists(upl))
{
string priv = Path.Combine(dir, "Source", "VoxelGraphEditor", "Private");
if (Directory.Exists(priv)) return priv;
}
foreach (string sub in Directory.GetDirectories(dir))
{
string hit = FindVoxelUnder(sub, depth + 1);
if (hit != null) return hit;
}
}
catch { }
return null;
}
}