Skip to main content
Glama
nodormu

unreal-mcp-additional-tools

by nodormu
README.md
# unreal-mcp-additional-tools

An MCP server that **complements Epic's official Unreal Engine MCP server** — **81 tools** across **14 subsystems** covering the areas Epic's server doesn't reach: build/cook/package, cinematics, Niagara, automation testing, source control, profiling, World Partition, and project-wide content maintenance.

Run it **alongside** Epic's official server. This repo has been deliberately deduplicated against it, so the two form a clean union with no functional overlap — see [Relationship to Epic's Official Server](#relationship-to-epics-official-server).

> Forked from [sam-david/unreal-mcp](https://github.com/sam-david/unreal-mcp) (MIT). The upstream project is a general-purpose Unreal MCP server; this fork deduplicates its toolset against Epic's official server so both can be connected at once.

## Relationship to Epic's Official Server

This project began as a general-purpose Unreal MCP server. After Epic shipped an official one, its toolset was deduplicated against Epic's so the two can be connected at the same time without redundant or conflicting tools.

### What was removed

Whole categories were dropped **only** where Epic has a genuine 1-call (or trivial 2-call) equivalent:

- **`blueprint` module (12 tools)** — deleted outright. Epic's `BlueprintTools` is a strict superset.
- **`plugin` module (3 tools)** — deleted outright. Epic's `PluginToolset` is a strict superset.
- **`actor`, `asset`, and `material`** — trimmed heavily for the same reason, leaving only the tools with no Epic counterpart.

### What was deliberately kept

Some remaining tools have names that sound similar to Epic's, but the behavior differs. These were kept on purpose:

| Tool | Why it isn't a duplicate |
|------|--------------------------|
| `apply_material` | Epic's mesh tools set the **asset's** default slot material. This sets a live **actor-instance** override. |
| `generate_collision` | Epic only does convex-hull generation. Box/sphere/capsule/auto modes have no Epic equivalent. |
| `set_skeletal_mesh_lod` | Epic's `SkeletalMeshTools` is read-only on LOD count. |
| `reimport_skeletal_mesh` | Epic has no in-place reimport. |
| `import_asset` | Epic's import tools are per-asset-type only — no generic or audio import. |
| `export_asset` | Epic has no asset export. |
| `validate_assets` | Epic has no data validation tool at all. |

### What was untouched

Entire domains were left fully intact because Epic has no equivalent:

- Build, cook, and packaging
- Cinematics / Sequencer
- Niagara VFX
- Automation testing and Gauntlet
- Source control (real checkin/checkout/diff, not just read-only flags)
- Profiling and Unreal Insights traces
- World Partition
- Undo/redo
- Remote Control Presets
- Project-wide maintenance — `fix_redirectors`, `resave_packages`, `content_audit`, `consolidate_assets`

## Quick Start

### Prerequisites

- Node.js >= 20.12.2 (matches [`engines`](package.json) in `package.json`)
- Unreal Engine 5.x with editor open
- **Python Editor Script Plugin** enabled (built-in) with **Enable Remote Execution** checked in its settings

That's it. All 81 tools reach the editor through Python Remote Execution, the Remote Control API, or a spawned UAT/UBT/commandlet subprocess — **no custom C++ plugin is required.** If you happen to already run a bridge plugin, that's fine too and nothing conflicts — see [Transport Layers](#transport-layers).

### Install

```bash
git clone https://github.com/nodormu/unreal-mcp-additional-tools.git
cd unreal-mcp-additional-tools
npm install
npm run build
```

### Add to Claude Code

**Per-project** (from your UE project directory):
```bash
claude mcp add --transport stdio unreal-extra -- node /path/to/unreal-mcp-additional-tools/dist/bin.js
```

**Global** (available in all projects):
```bash
claude mcp add --scope user --transport stdio unreal-extra -- node /path/to/unreal-mcp-additional-tools/dist/bin.js
```

Then drop a `.unrealmcp.json` in each UE project:
```json
{
  "projectPath": "."
}
```

### Add to MCP Server

Here is an example configuration you can add to your MCP Client as an available MCP server to connect to:

```json
{
  "mcpServers": {
    "unreal-extra": {
      "command": "node",
      "args": ["/path/to/unreal-mcp-additional-tools/dist/bin.js"],
      "env": {
        "UNREAL_MCP_PROJECT_PATH": "/path/to/YourProject.uproject"
      }
    }
  }
}
```

## Tool Modules

81 tools across 14 modules. Module names below are the values accepted by `enabledModules` / `UNREAL_MCP_MODULES`.

| Module | Tools | Description |
|--------|-------|-------------|
| **console** | 3 | Execute Python, run console commands, check transport connection status |
| **actor** | 2 | Duplicate actors, set actor tags |
| **asset** | 10 | Import/export, data validation, fix redirectors, resave packages, content audit, consolidate duplicates, orphan-asset finder, circular-dependency detector, dependency tree |
| **build** | 9 | Build targets, cook, package, BuildCookRun, build plugins, BuildGraph, generate project files, clean, parse build status |
| **material** | 1 | Apply a material to a live actor's mesh component |
| **sequencer** | 8 | Create sequences, inspect structure, bind actors, add tracks, playback range, framerate, FBX export, Movie Render Queue |
| **animation** | 6 | Animation blueprints, montages, sequence info, skeletal mesh LODs, reimport, animation modifiers |
| **niagara** | 8 | Spawn systems at a location or attached, set float/vector/color/bool parameters, reset, reinit |
| **testing** | 7 | List and run automation tests (by name, category, or all), map check, Gauntlet, fetch results |
| **source-control** | 6 | Status, checkout, checkin, revert, mark for add, diff |
| **profiling** | 5 | Start/stop Unreal Insights traces, stat commands, start/stop CSV profiling |
| **world-partition** | 4 | List data layers, set data layer state, query loaded cells, configure streaming sources |
| **editor-utils** | 7 | Run editor utility widgets/blueprints, generate collision and lightmap UVs, undo, redo, undo history |
| **remote-control-presets** | 5 | List and inspect presets, get/set exposed properties, call exposed functions |

## Resources

Beyond the 81 tools, the server also exposes two read-only MCP resources:

| URI | Contents |
|-----|----------|
| `unreal://project` | Current `projectPath`, `enginePath`, `platform`, `configuration`, and `enabledModules` |
| `unreal://status` | Live transport status (`remoteControl`, `pythonExec`, `pluginBridge`, `editorRunning`) plus negotiated `pluginCapabilities` if the optional bridge plugin is connected |

## Architecture

```
MCP Client (Claude Code, Claude Desktop, etc.)
  ↕ stdio (MCP protocol)
unreal-mcp-additional-tools server
  ↕ transport layers
Unreal Engine
```

### Transport Layers

| Transport | Protocol | Port | What It Needs | Used by |
|-----------|----------|------|---------------|---------|
| **Python Remote Execution** | UDP multicast + inverted TCP | 6776 | Python Editor Script Plugin (built-in) | Most tools |
| **Subprocess Runner** | Spawns UAT/UBT processes | N/A | Engine path only | Build, cook, package, Gauntlet |
| **Remote Control API** | HTTP REST | 30010 | Remote Control API plugin (built-in) | Remote Control Presets |
| **Plugin Bridge** | TCP, length-prefixed JSON | 55557 | Optional C++ plugin | *Currently unused* |

The server probes all transports on startup and tools degrade gracefully. Build tools run through the subprocess runner and don't need the editor open at all.

The Plugin Bridge client and its `executeWithPluginFallback()` plugin-first/Python-fallback path remain in the codebase, but no tool currently routes through it — its only consumer was the `blueprint` module, which was removed during the dedupe against Epic's server. No C++ plugin ships with this repo.

**Already running a bridge plugin?** One exists as a separate project: [unreal-mcp-bridge-plugin](https://github.com/nodormu/unreal-mcp-bridge-plugin_v0.1_ue5.8.1-linux_20260817) (v0.1, UE 5.8.1, Linux). Nothing here requires it, but if it's listening this server will find it during the startup probe, negotiate capabilities, and report `"pluginBridge": true` in `unreal://status` and `get_connection_status`.

That `true` is worth reading precisely: it means the transport connected, **not** that anything is using it. Because no tool routes through the bridge today, installing the plugin doesn't change any tool's behavior — every call still goes over Python Remote Execution, Remote Control, or a subprocess exactly as it would without the plugin. The status flag and the negotiated `pluginCapabilities` are the only visible difference. Use `--plugin-port` if yours listens somewhere other than `55557`.

The capabilities it advertises are Blueprint/K2 authoring commands (`create_blueprint`, `add_node`, `connect_nodes`, and similar) — which is precisely what the removed `blueprint` module consumed. So the plugin side of that pairing still works; it's this server's tools that are gone, deliberately, because Epic's official `BlueprintTools` covers the same ground. Run Epic's server alongside this one for Blueprint work.

## Configuration

Three-layer priority: CLI args > environment variables > config file > defaults.

### Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `UNREAL_MCP_PROJECT_PATH` | — | Path to .uproject file or project directory |
| `UNREAL_MCP_ENGINE_PATH` | auto-detect | UE engine install path |
| `UNREAL_MCP_RC_PORT` | 30010 | Remote Control API port |
| `UNREAL_MCP_PYTHON_PORT` | 6776 | Python Remote Execution port |
| `UNREAL_MCP_MULTICAST_BIND` | `0.0.0.0` | Bind address for the UDP multicast discovery socket. Advanced/rarely needed — see the security note below before narrowing this. |
| `UNREAL_MCP_PLATFORM` | Win64 | Target platform |
| `UNREAL_MCP_CONFIGURATION` | Development | Build configuration |
| `UNREAL_MCP_MODULES` | all | Comma-separated list of modules to enable |

> **Linux/macOS:** `UNREAL_MCP_ENGINE_PATH` auto-detection only probes common **Windows** install locations (`C:\Program Files\Epic Games\UE_<version>`, etc.) after reading `EngineAssociation` from your `.uproject` — it never finds a Linux or macOS install on its own. Set `UNREAL_MCP_ENGINE_PATH` explicitly on those platforms. `UNREAL_MCP_PLATFORM` also defaults to `Win64`; Linux users should set it to `Linux` (macOS: `Mac`) so `build_target`/`build_cook_run`/`package_project` target the right platform. The subprocess runner itself already knows how to locate `RunUAT.sh`, `UnrealBuildTool`, and the Linux/Mac `UnrealEditor` binary once `enginePath` points at a real install — only the auto-detect *guess* is Windows-only.

### CLI Arguments

```bash
node dist/bin.js --project-path /path/to/project --engine-path /path/to/UE_5.8 --rc-port 30010
```

Every config value has a matching flag:

| Flag | Env var equivalent | Description |
|------|---------------------|--------------|
| `--project-path <path>` | `UNREAL_MCP_PROJECT_PATH` | Path to `.uproject` or project directory |
| `--engine-path <path>` | `UNREAL_MCP_ENGINE_PATH` | UE engine install path |
| `--rc-port <port>` | `UNREAL_MCP_RC_PORT` | Remote Control API port |
| `--python-port <port>` | `UNREAL_MCP_PYTHON_PORT` | Python Remote Execution command port |
| `--multicast-bind <address>` | `UNREAL_MCP_MULTICAST_BIND` | Bind address for the UDP multicast discovery socket (default `0.0.0.0`) — advanced, see security note below |
| `--plugin-port <port>` | *(none)* | Port for the optional Plugin Bridge transport (default `55557`) — CLI-only, no env var reads this |
| `--platform <platform>` | `UNREAL_MCP_PLATFORM` | Target platform for build/cook/package |
| `--configuration <config>` | `UNREAL_MCP_CONFIGURATION` | Build configuration |
| `--modules <csv>` | `UNREAL_MCP_MODULES` | Comma-separated list of modules to enable |

### Config File

Place `.unrealmcp.json` in your project directory or home directory:

```json
{
  "projectPath": ".",
  "platform": "Linux",
  "configuration": "Development",
  "enabledModules": ["console", "asset", "build", "sequencer", "niagara"]
}
```
(`platform` defaults to `Win64` if omitted — set it to match your actual OS, e.g. `Linux` or `Mac`, per the note above.)

### Windows notes

**Engine path auto-detection only covers default launcher installs.** It reads
`EngineAssociation` from your `.uproject` and probes exactly three locations:
`C:\Program Files\Epic Games\UE_<association>`, the `(x86)` variant, and
`D:\Program Files\Epic Games\UE_<association>`. That works when the association
is a version string like `5.8`. It does **not** cover:

- **Source builds**, whose `EngineAssociation` is a GUID registered under
  `HKEY_CURRENT_USER\Software\Epic Games\Unreal Engine\Builds`. No registry
  lookup is performed, so the GUID is pasted straight into the paths above and
  matches nothing.
- Launcher installs on any other drive or directory.

In either case pass `--engine-path` or set `UNREAL_MCP_ENGINE_PATH`. Without a
resolved engine path, the 12 subprocess-backed tools fail with a
`Cannot find RunUAT` / `Cannot find UnrealBuildTool` / `Cannot find UnrealEditor`
error depending on which binary they need — that's the 8 spawning tools in
**build**, the three commandlet-backed **asset** tools (`fix_redirectors`,
`resave_packages`, `content_audit`), and `run_gauntlet`. Everything that talks
to the running editor over Python or Remote Control is unaffected, as is
`get_build_status`, which only parses a log you pass it.

**Build arguments containing `%` or `"` are rejected.** Windows cannot execute
`RunUAT.bat` directly — batch files only run through a command interpreter — so
the subprocess runner invokes `cmd.exe /d /s /c` and quotes each argument
itself. `cmd.exe` expands `%VAR%` even inside double quotes with no in-quote
escape available, and an embedded `"` would terminate the quoting early, so an
argument containing either is refused up front rather than silently mangled:

```
Rejected unsafe subprocess token (cmd.exe cannot safely quote '%' or '"')
```

This applies to your engine and project paths too. Paths containing **spaces**
are fine — they are quoted correctly and round-trip intact. If a path contains a
literal `%`, point `--engine-path` / `--project-path` somewhere without one.

## Unreal Editor Setup

### Required (for most tools)

1. Edit > Plugins > enable **Python Editor Script Plugin**
2. Restart the editor
3. Edit > Project Settings > Plugins > **Python** > scroll to **Remote Execution** section:
   - Check **Enable Remote Execution**
   - **UE 5.3+:** If discovery fails with the default, try changing **Multicast Bind Address** to `0.0.0.0` — Epic changed the default in 5.3 and it can break discovery on some setups (see the security note below before doing this on an untrusted network)
   - Verify Multicast Group Endpoint is `239.0.0.1:6766`
4. Restart the editor again

> **Security note — TCP command channel has no peer authentication.** This server's
> own multicast bind address (the client side, not the editor setting above) defaults
> to `0.0.0.0`, matching what `unreal-remote-execution` requires on Windows (where
> `setMulticastInterface()` needs `0.0.0.0` as the bind address — see the comment in
> `src/transports/python-exec.ts`). One consequence: the TCP command channel this
> opens (port 6776, via the `unreal-remote-execution` package) listens on every
> interface, and it accepts whichever process connects to it first — it does not
> verify the connecting peer is actually the UE Editor. This is a property of Epic's
> Python Remote Execution protocol itself (no shared secret to authenticate with),
> not something fixable in this server alone. In practice, this means any other
> process on a reachable interface that connects to port 6776 before the real editor
> does will be accepted as if it were the editor, and this server's connection-status
> cache has no way to detect that after the fact — recovery requires noticing the
> mismatch and terminating the stray process yourself. Don't run this on a
> shared/multi-tenant machine, or a network you don't trust, without being aware of
> this. Widening the
> *editor's own* Multicast Bind Address setting (the setup step above) carries the
> same no-peer-authentication exposure, just on the editor's socket instead of this
> server's — the same caution applies either way.
>
> `UNREAL_MCP_MULTICAST_BIND` / `--multicast-bind` let you narrow *this server's
> own* discovery socket's bind address (e.g. to `127.0.0.1`) — separately from the
> UE editor's own Multicast Bind Address setting above. These are two independent
> per-side settings, and a loopback-only round trip needs **both** to agree on
> `127.0.0.1` — narrowing only this server's side isn't enough on its own.
>
> **Why `0.0.0.0` stays the default here.** UDP multicast group membership is
> interface-scoped: whichever interface a socket joins the group on is the only
> interface it can receive replies on. On a genuinely single-NIC host, narrowing
> both sides to loopback should work fine, since multicast delivers locally over
> `lo` without ever leaving the machine. But on any host with more than one active
> network path (a real NIC alongside a VPN, virtual bridge, WSL, etc.), the
> outbound discovery ping goes out a real NIC (auto-selected — see
> `resolveMulticastInterface()`), and — this is the part that isn't obvious — the
> *editor's* reply is subject to the exact same constraint on its own side. Epic's
> compiled-in engine default for the editor's Multicast Bind Address is actually
> `127.0.0.1`, but real multi-NIC setups routinely have to override it to `0.0.0.0`
> just to get discovery working at all (that's exactly what the 5.3+ note above is
> describing). Once the editor's own setting is `0.0.0.0`, its reply leaves over
> its own real interface too — so narrowing only this server's bind address to
> loopback can't help, because the reply it's waiting for never travels over `lo`
> in the first place. Confirmed on a real multi-NIC Linux host: narrowing this
> server alone to `127.0.0.1` finds zero nodes, even when its outbound interface is
> also forced to loopback to match. `0.0.0.0` is kept as the default because it's
> the one value that works regardless of what the editor's own setting happens to
> be, rather than requiring every host to first verify both sides agree on
> loopback.
>
> **Running Epic's official Unreal MCP server alongside this one doesn't factor
> into this at all.** Epic's server runs its own in-process HTTP listener
> (typically port 8000) and never touches the Python Remote Execution multicast
> group or ports — the only two parties in the bind-address question above are
> this server's own client and the UE editor's Python Remote Execution socket.
>
> If you're on a genuinely single-NIC host and want to try loopback-only anyway:
> narrow both this setting and the editor's own Multicast Bind Address to
> `127.0.0.1`, restart the editor, and confirm discovery still finds a node before
> relying on it.
>
> **If you're about to open a PR changing this default to `127.0.0.1`, please
> test it on a real multi-NIC host first.** On this maintainer's own multi-NIC
> Linux dev machine, narrowing only this server's side to `127.0.0.1` fails
> cleanly (discovery finds zero nodes, every time). But actually running the
> both-sides test described above — setting the editor's own Multicast Bind
> Address to `127.0.0.1` and restarting the editor to match — has, every time
> it's been attempted, crashed the editor itself (not just failed to find a
> node). Neither result makes `127.0.0.1` look like a safer or more correct
> default here. PRs proposing it are welcome if they include evidence it
> behaves differently on your setup, but "it should just work on loopback"
> alone isn't going to be enough — it hasn't held up empirically on this box.

**Still getting "No Unreal Editor nodes found"?**
- **VPN/Tailscale users:** Tailscale's virtual network adapter can hijack multicast. Try temporarily disabling Tailscale, or disable/remove its virtual network interface (Windows: Network Connections; Linux: `ip link show` to find `tailscale0` and `sudo ip link set tailscale0 down`; macOS: System Settings > Network).
- **Firewall:** Allow UDP port 6766 and TCP port 6776.
  - Windows: allow the ports in Windows Firewall, or temporarily disable it to test.
  - Linux: `sudo ufw allow 6766/udp && sudo ufw allow 6776/tcp` (ufw) or the equivalent `firewall-cmd --add-port` rules (firewalld).
  - macOS: System Settings > Network > Firewall, or `sudo pfctl` rules if you run a custom `pf` config.
- **Multiple adapters:** WSL, Hyper-V, VPN adapters, and (on Linux) Docker/virtual bridge interfaces can all cause multicast to bind to the wrong interface. Disabling unused adapters helps.

### Optional (for Remote Control Preset tools)

1. Edit > Plugins > enable **Remote Control API**
2. Restart the editor
3. Edit > Project Settings > Plugins > **Remote Control** > **Server**:
   - Check **Restrict Server Access** — this sounds restrictive but actually *enables* the sub-options below (unchecked = features hidden/off)
   - Check **Enable Remote Python Execution**
   - Check **Allow Console Command Remote Execution**
   - Allowed Origins: leave blank or add `127.0.0.1`
   - These take effect immediately, no restart needed

## Other Community Projects

For context, other Unreal MCP implementations in the wild:

| | [flopperam](https://github.com/flopperam/unreal-engine-mcp) | [chongdashu](https://github.com/chongdashu/unreal-mcp) | [kvick-games](https://github.com/kvick-games/UnrealMCP) | [ChiR24](https://github.com/ChiR24/Unreal_mcp) |
|---|---|---|---|---|
| Tools | ~30 | ~20 | ~5 | 36 |
| Requires C++ plugin | Yes | Yes | Yes | Yes |
| Build/package tools | No | No | No | Partial |

All of them require compiling and installing a custom C++ plugin into your UE project. This one doesn't.

## Development

```bash
npm run dev        # Watch-mode dev server
npm run build      # Compile TypeScript
npm run lint       # Biome linter
npm run fmt        # Biome formatter
npm test           # Runs vitest in watch mode (add `-- run` for a single non-interactive pass, e.g. in CI)
```

## License

MIT — see [LICENSE](LICENSE). Original work copyright Sam David; modifications in this fork copyright nodormu.

TDQS

C2.6/5.0

Scored across 78 tools

Disambiguation2/5

Many tools overlap in purpose: execute_console_command and run_stat_command both run console/stat commands, and run_automation_test, run_all_automation_tests, run_automation_tests_by_category have unclear boundaries. Build-related tools also overlap significantly (build_cook_run, cook_content, package_project).

Naming Consistency2/5

Naming is inconsistent: source control tools use a 'sc_' prefix while similar workflows use full verb_noun patterns; run_stat_command vs execute_console_command; get_test_results vs list_automation_tests; and tool verbs mix 'get', 'list', 'run', 'execute', 'set' without a clear convention.

Tool Count2/5

78 tools is far beyond a well-scoped server and spans multiple unrelated domains (source control, build, automation, Niagara, sequences, remote control). The high count makes the tool surface difficult to navigate and likely contains redundant operations.

Completeness3/5

The broad toolkit covers many Unreal Editor workflows, including build, test, source control, Niagara, sequences, and remote control. However, each domain has notable gaps—e.g., no generic actor quer or asset creation/deletion—and some areas feel opportunistic rather than systematic.

Maintenance

ActivitySlowing
ResponsivenessNo issues