Skip to main content
Glama
README.md
# uefn-mcp-pro

**A fast, complete MCP server for Unreal Editor for Fortnite (UEFN).** It lets AI coding agents — Claude Code, Cursor, Codex, anything that speaks [MCP](https://modelcontextprotocol.io) — work inside a live UEFN session: write and compile Verse, place and wire Creative devices, edit Scene Graph entities, move actors and assets in bulk, take screenshots, run playtest sessions and read every log the editor produces.

It uses **Epic's official Unreal MCP** (embedded in UEFN 42.00+) as the executor for everything Epic exposes: Verse files and build diagnostics, devices and event bindings, Scene Graph, play sessions, UMG/MVVM, Niagara, materials, physics assets — about 400 tools.

Alongside it runs our own lightweight **Python sidecar** inside the editor for everything the official server does not expose: arbitrary editor Python, bulk actor/asset operations, screenshots, batching.

On top of that, uefn-mcp-pro ships the single biggest speed fix for UEFN automation (see [Performance](#performance)), a curated flat tool surface designed for agents, disk-speed Verse reading, and a complete "where do I look when it breaks" layer over the editor logs.

---

## Contents

1. [What you get](#what-you-get)
2. [Requirements](#requirements)
3. [Installation](#installation)
4. [Every-day workflow](#every-day-workflow)
5. [Tool reference](#tool-reference)
6. [Logs and errors: where the agent looks](#logs-and-errors-where-the-agent-looks)
7. [Performance](#performance)
8. [Comparison with Epic's MCP and earlier community bridges](#comparison)
9. [Architecture](#architecture)
10. [Known limitations (UEFN 42.00 beta)](#known-limitations-uefn-4200-beta)
11. [Development and tests](#development-and-tests)
12. [Credits and license](#credits-and-license)

---

## What you get

| Area | What the agent can do |
|---|---|
| **Verse** | list/read/grep files at disk speed (30 ms), write and replace through the editor, compile with `BuildAll` and get diagnostics with file/line/column/error code, poll build status for free |
| **Creative devices** | browse the device catalog (built-in and your Verse devices), place devices with a transform, read/set device properties in bulk, list bindable events/functions and **wire event bindings** (the Functions/Events panel) without touching the UI |
| **Scene Graph entities** | find/create/delete entities, transforms, add/remove components, read/write component properties, browse entity and component classes |
| **Actors & level** | filtered/projected actor queries, bulk spawn / transform / delete in one undo step, actor and component properties, selection and focus, save level |
| **Assets** | Asset Registry search, asset info, import FBX/PNG/TGA/WAV, rename/duplicate/delete/save, Content Browser selection |
| **Playtest** | start/stop a play session, start/stop the game, push changes (Verse-only or all), session status, **read the Fortnite client log** (Verse `Print()` output, runtime errors) |
| **Viewport** | get/set camera, render a PNG through a SceneCapture (works with the editor unfocused) |
| **Editor Python** | run arbitrary Python on the game thread (sync or as a background job), batch many commands in one round-trip and one undo transaction |
| **Long tail** | keyword search over all ~400 official tools, compact schema view, direct call with automatic object-reference wrapping (UMG, MVVM, Niagara, materials, physics, gameplay tags, …) |
| **Logs & errors** | editor log with category/severity filters, "what went wrong in the last N minutes", Verse diagnostics, crash reports, revision-control log, sidecar log, play-session client log — and a built-in map of where each lives |

62 tools in total. The agent also receives a short instruction block telling it when to build, how to reference objects and where to look for errors, so it behaves sensibly without a custom prompt.

---

## Requirements

- **UEFN 42.00 or newer** (August 2026 release, the one that added Unreal MCP) on Windows.
- Beta features enabled for the project (the installer writes them into `.uefnproject`): **Python Editor Scripting** and **UEFN MCP Toolsets**. Your Epic account must have access to these betas.
- **Python 3.11+** on the host with the packages `mcp` and `httpx`.
- An MCP client: Claude Code, Cursor, Codex CLI, or any other.

---

## Installation

### 1. Get the code and dependencies

```bash
git clone https://github.com/novikit/uefn-mcp-pro.git C:/Projects/UEFN/uefn-mcp-pro
cd C:/Projects/UEFN/uefn-mcp-pro
pip install -e .
```

### 2. Run the installer for your project

```bash
python install.py "C:/Projects/UEFN/MyIsland"
```

What it does (all idempotent, backups are created next to each file it changes):

| Step | File | Change |
|---|---|---|
| editor side | `<Project>/Content/Python/` | copies `init_unreal.py` and the `uefn_pro/` package |
| beta flags | `<Project>/<Name>.uefnproject` | `pythonExperimental.bEnablePythonForProject = true`, `toolsets.bEnableToolsetsForProject = true` |
| editor prefs | `%LOCALAPPDATA%\UnrealEditorFortnite\Saved\Config\WindowsEditor\EditorPerProjectUserSettings.ini` | MCP server auto-start on port 8000; **`bThrottleCPUWhenNotForeground=False`** (the speed fix) |
| MCP client | `<Project>/.mcp.json` | adds `uefn-pro` (this gateway, stdio) and `unreal-mcp` (raw official server, `http://127.0.0.1:8000/mcp`) |

Run `python install.py <project> --check` at any time to see the state without changing anything. Do not edit `.uefnproject` while the project is open in UEFN — the editor reloads the whole project when that file changes.

### 3. Start UEFN, open the project, bootstrap once

Open the project. Then, in the Python console field at the bottom-left of the editor window (placeholder text *"Enter Python script or a filename"*), paste the path of the startup script and press Enter:

```
C:/Projects/UEFN/uefn-mcp-pro/editor/uefn_pro_startup.py
```

Within half a second the Output Log shows a line like:

```
[uefn-pro] {"throttle_disabled": true, "official_mcp": "started", "toolset": "registered", "sidecar": "listening:8765", "seconds": 0.3}
```

That single step turns off the background throttle, starts Epic's MCP server on port 8000 and starts the Python sidecar on port 8765. It has to be repeated after every editor restart (see [Known limitations](#known-limitations-uefn-4200-beta) for why it is not automatic yet). The gateway tool `editor_start_mcp` can type it for you; it briefly brings the editor window to the front.

### 4. Connect your agent

- **Claude Code**: open the project folder; approve the new servers from `.mcp.json` when asked (or run with `--mcp-config .mcp.json`). If a session was already open, restart it.
- **Cursor / Codex / others**: add a stdio server with command `python -m uefn_mcp_pro.server`, working directory = your project folder (or set `UEFN_PROJECT_DIR`), and `PYTHONPATH` pointing at this repository.

Ask the agent to call `status`. Expected: both `official_mcp.up` and `sidecar.up` are `true` and `verse_build.status` is `success`.

### Working in a team (UEFN revision control)

`.uefnproject`, `.mcp.json` and `Content/Python/` are tracked by UEFN's revision control (Lore) and shared with every
teammate, so treat them like any other shared file:

- **Sync before installing.** Run `install.py` only after "Sync latest"; otherwise both of you edit `.uefnproject` and get a conflict.
- **One person enables the beta flags**, checks in, everyone else syncs. `install.py --check` on the other machines will report the flags as already on.
- **`.mcp.json` is machine-independent by design** (`python -m uefn_mcp_pro.server`, no absolute paths), so it can be shared as-is. Each teammate only needs `pip install -e <repo>` on their machine.
- **If a conflict appears** in the Conflict Resolution panel for `.uefnproject` or `.mcp.json`: take the *teammate's* version, then re-run `python install.py <project>` — it re-adds the beta flags and the server entries without touching anything else. Take *your* version only if you know the teammate did not change those files.
- Per-machine things (editor prefs, the bootstrap line, ports 8000/8765) never sync; each editor talks only to its own local agent, so two people using uefn-mcp-pro on the same island do not interfere with each other.

### Configuration (environment variables, all optional)

| Variable | Default | Meaning |
|---|---|---|
| `UEFN_MCP_URL` | `http://127.0.0.1:8000/mcp` | official server endpoint |
| `UEFN_SIDECAR_PORT` | `8765` | first port to probe for the sidecar (8765–8770 are scanned) |
| `UEFN_PROJECT_DIR` | auto (cwd or parents containing `*.uefnproject`) | project root for disk-speed Verse tools |
| `UEFN_SAVED_DIR` | `%LOCALAPPDATA%\UnrealEditorFortnite\Saved` | where logs, crashes and screenshots are read from |
| `UEFN_PRO_STARTUP` | `editor/uefn_pro_startup.py` in this repo | script typed by `editor_start_mcp` |

---

## Every-day workflow

A typical loop the agent runs on its own:

1. `verse_grep` / `verse_read` (disk, instant) to understand the code, `verse_replace` or `verse_write` to change it — both trigger `verse_build` and return diagnostics.
2. `device_catalog` → `device_place` → `device_set_props` → `device_bindings(options=True)` → `device_bind` to add and wire a device.
3. `actors_find(fields=[...], limit=...)` to inspect the level cheaply; `actors_spawn` / `actors_transform` / `actors_delete` in bulk.
4. `screenshot` and read the PNG to check the result visually.
5. `session_start` → `game_start` → `session_client_log(pattern="Print|Error")` → `session_push(verse_only=True)` for an automated playtest.
6. `errors_recent` after anything unexpected; `crash_reports` if the editor died.

The gateway never reports a green build on a red log: `verse_build` cross-checks the official diagnostics with the editor log.

---

## Tool reference

### System

| Tool | Purpose |
|---|---|
| `status` | health of both paths (official MCP, sidecar), latencies, last Verse build state |
| `editor_start_mcp` | run the bootstrap (throttle off, official server, sidecar); falls back to typing the startup script into the editor console |
| `execute_python(code, timeout_seconds)` | run Python on the editor game thread; `unreal`, `actor_sub`, `asset_sub`, `level_sub` pre-bound; assign `result` |
| `execute_python_async(code)` / `get_job(job_id, wait_seconds)` | long-running Python as a background job |
| `batch(commands, transaction, stop_on_error)` | many sidecar commands in one round-trip and optionally one undo step |
| `focus_editor` | bring the UEFN window to the foreground (rarely needed now) |

### Logs and diagnostics

| Tool | Purpose |
|---|---|
| `logs_overview` | map of every log source with paths, freshness and the tool that reads it |
| `get_editor_log(last_n, filter_str, regex, log_name, category, severity)` | tail any log in the Logs folder with filters |
| `errors_recent(minutes, include_warnings, exclude_categories)` | errors/warnings of the last N minutes grouped by category with samples |
| `verse_build_status(wait_seconds)` | last Verse build parsed from the log: status + diagnostics (file, line, col, code, text) |
| `crash_reports(limit)` | recent crash folders: error message, fatal lines, log tail |
| `sidecar_log(last_n, level)` | messages of the in-editor bridge |
| `session_client_log(pattern, max_results, start_line)` | Fortnite client log of a running play session |

### Long tail (official server pass-through)

| Tool | Purpose |
|---|---|
| `ue_toolsets(refresh)` | registered official toolsets |
| `ue_search_tools(query, limit)` | keyword search over all official tools with brief params |
| `ue_describe(toolset, tool, full_schema)` | compact description of a toolset or one tool; short names accepted (`DeviceToolset`, `SceneTools`, `UMGToolSet`) |
| `ue_call(toolset, tool, arguments, timeout_seconds)` | call any official tool; plain paths are wrapped as `{"refPath": ...}` automatically |

### Verse

| Tool | Purpose |
|---|---|
| `verse_files(path, recursive, via_editor)` | list `.verse` files (disk by default) |
| `verse_read(path, start_line, end_line, via_editor)` | read a file or a line range (disk by default) |
| `verse_write(path, content, create_if_missing, build)` | replace a whole file through the editor, then build |
| `verse_replace(path, old_string, new_string, replace_all, build)` | exact-string edit through the editor, then build |
| `verse_grep(pattern, path_glob, max_results, via_editor)` | regex search → file/line/col/text (disk by default) |
| `verse_build(timeout_seconds)` | `BuildAll` + diagnostics + log cross-check |
| `verse_fs(op, path, dest, recursive)` | move / copy / delete / mkdir |

### Devices

| Tool | Purpose |
|---|---|
| `device_catalog(name_filter, fields)` | placeable devices: displayName, assetPath, category, isVerseDevice |
| `device_place(asset_path, location, rotation, scale, label)` / `device_place_many(items)` | place devices |
| `device_props(device_path, names)` | list or read properties |
| `device_set_props(device_path, properties)` | set many properties at once |
| `device_bindings(device_path, options)` | current event bindings, or the bindable events/functions |
| `device_bind(source_device, source_event, target_device, target_function, remove)` | add or remove an event binding |

### Scene Graph entities

`entity_find`, `entity_classes(name_filter, components)`, `entity_create`, `entity_delete`, `entity_transform`, `entity_components(entity, add_class, remove_class)`, `entity_prop(component, name, value)`.

### Actors

`actors_find(class_filter, class_contains, label_contains, folder, fields, limit, offset, count_only)`, `actors_selected`, `actors_spawn(items, transaction)`, `actors_delete(actor_paths)`, `actors_transform(items)`, `actor_props(actor_path, properties)`, `actor_set_props(actor_path, properties)`, `actor_components(actor_path)`, `component_props(actor_path, component_name, properties, set_values)`, `select_actors(actor_paths, add_to_selection, focus)`.

### Assets and project

`assets_search(class_name, directory, name_contains, recursive, limit, offset)`, `asset_info(asset_path)`, `asset_import(file_path, destination_path, asset_name, replace_existing, save)`, `asset_ops(op, asset_path, dest_path)` with `rename | duplicate | delete | exists | save | selected`, `project_info`, `save_level`.

### Play sessions

`session_start(location, rotation, timeout_seconds)`, `session_stop`, `session_status`, `session_push(verse_only)`, `game_start(stop)`, `session_client_log(pattern, max_results, start_line)`.

### Viewport

`camera(location, rotation)` (get when called without arguments), `screenshot(filename, res_x, res_y, camera_location, camera_rotation, fov)`.

---

## Logs and errors: where the agent looks

The gateway's instructions teach the agent this map, and `logs_overview` returns it with live paths:

| Symptom | Tool | Source |
|---|---|---|
| Verse does not compile | `verse_build_status` / `verse_build` | `VerseBuild:` lines in `UnrealEditorFortnite.log`, parsed to file/line/col/code |
| Something failed silently in the editor | `errors_recent` | Error/Warning lines of the last N minutes, grouped by category |
| Need the raw text | `get_editor_log(category=..., severity=...)` | any file in `%LOCALAPPDATA%\UnrealEditorFortnite\Saved\Logs\` |
| Python script errors | returned inline by `execute_python`; `get_editor_log(category="LogPython")` | editor log |
| In-game behaviour, `Print()` output, runtime Verse errors | `session_client_log` | Fortnite client log via Epic's SessionToolset |
| Editor crashed | `crash_reports` | `Saved\Crashes\UECC-*\` (CrashContext, minidump, log) |
| Revision control (Lore) trouble | `get_editor_log(log_name="Lore.log")` | Lore client log |
| The bridge itself | `status`, `sidecar_log`, `editor_start_mcp` | ports 8000 / 8765 |

All of these except `session_client_log` and `sidecar_log` read files from disk and cost no editor time.

---

## Performance

UEFN's default preference *Editor Performance → Throttle CPU when not in foreground* drops the whole editor main loop to **3 ticks per second** while the window is not focused. Every automation call — Slate callbacks, ticker callbacks and Epic's own MCP server alike — then waits for the next tick, about **333 ms**. That is why earlier bridges had to focus the editor window or batch everything.

uefn-mcp-pro turns the setting off (persistently in the editor prefs and at bootstrap). Measured on UEFN 42.00 with the editor **not** focused:

| Call | throttle on (default) | throttle off (uefn-mcp-pro) |
|---|---|---|
| sidecar `execute_python` | 337 ms | 14–31 ms |
| sidecar `get_viewport_camera` | 338 ms | 9–23 ms |
| official `SessionToolset.GetSessionStatus` | 332 ms | 11–28 ms |
| official `EntityToolset.ListEntityClasses` | 336 ms | 19 ms |
| official `DeviceToolset.ListDeviceAssets` | 334 ms | 51–74 ms |
| official `VerseToolset.ListFiles` / `Grep` | ~1300 ms | ~1000 ms (editor) / **34–44 ms** (uefn-mcp-pro disk path) |
| editor tick rate | 3 Hz | 60 Hz |

**Do not minimize the editor window.** A minimized UEFN ticks at 3 Hz no matter what the setting says (~333 ms per call again). Keep it restored behind other windows or on a second monitor; `status` warns when it is minimized and `focus_editor(restore_only=True)` un-minimizes it without stealing focus. The sidecar also re-applies the throttle setting every 15 s in case the editor reloads its preferences.

Other design choices that keep latency down: one persistent HTTP session to the official server, background pre-indexing of all tool schemas (so `ue_search_tools` is instant), server-side filtering/projection (`fields`, `limit`, `count_only`), bulk tools and `batch`.

---

## Comparison

### Epic's official Unreal MCP (built into UEFN 42.00+)

**Pros**
- Zero install: it is in the editor. Supported by Epic and will follow UEFN to UE6.
- Deep, native C++ tools that nothing else can reach from Python: Verse write/build with diagnostics, device event bindings, Scene Graph, play sessions and the client log, UMG/MVVM/VerseFields, Niagara, materials, physics assets — about 400 tools in 29 toolsets.
- Tools run on the game thread with proper async support; object references are typed.

**Cons**
- Agent-hostile surface: three meta-tools (`list_toolsets`, `describe_toolset`, `call_tool`); a single `describe_toolset` answer is ~44 KB of JSON, and every action needs the nested `call_tool` envelope.
- Same 3 Hz background throttle as everything else: ~333 ms per call while the editor is not focused.
- No arbitrary editor Python, no bulk actor/asset helpers, no screenshots, no log/crash tooling beyond a basic log reader.
- Custom Python toolsets are silently dropped by Epic's `ToolsetPolicy` in UEFN, so it cannot be extended by creators.
- Beta: not all UE 5.8 toolsets are enabled, coordinates and hitches are documented rough edges, the API may change.

### Earlier community bridges (Python listener inside the editor + external MCP process)

**Pros**
- Pure Python, no C++: a FastMCP process plus an in-editor HTTP listener; 41 practical tools (actors, components, assets, viewport, screenshots, `execute_python`, jobs, batch).
- Works on any UEFN build with the Python beta, independent of Epic's MCP.
- Good ergonomics for agents: flat tools, server-side filters, object paths, an explicit cost model in the prompt.

**Cons**
- Cannot touch what only Epic's C++ exposes: no Verse compile diagnostics beyond log scraping, no device bindings, no Scene Graph, no play sessions, no UMG.
- Executes on a Slate post-tick callback: fully exposed to the 3 Hz throttle, hence `focus_editor` and "batch everything".
- Listener copied by hand into each project's `Content/Python`; stopped running once the *UEFN MCP Toolsets* beta changed when Python is initialised.

### uefn-mcp-pro (this project)

**Pros**
- Everything above in one flat, documented surface of 62 tools, plus search/describe/call over the whole official registry.
- The throttle fix: 10–50 ms per call with the editor unfocused; disk-speed Verse reading.
- Complete log/error layer: Verse diagnostics, recent errors by category, crash reports, session client log, revision-control log, with the agent told where to look.
- Degrades gracefully: if the official server is unavailable the sidecar still provides the actor/asset/Python tools; if the sidecar is down the official tools still work.
- Installer, unit tests, a live end-to-end test, research notes on UEFN internals — all open, MIT.

**Cons**
- The in-editor bootstrap must be triggered once per editor start (one line in the Python console) until Epic runs project startup scripts after enabling the Toolsets beta.
- Depends on two betas and on Epic's beta API; when Epic renames a toolset the pass-through still works but curated wrappers may need a one-line update.
- Windows only (as is UEFN).

---

## Architecture

```
Claude Code / Cursor / Codex
   │ stdio (MCP)
   ▼
uefn-mcp-pro gateway  (host Python: uefn_mcp_pro/server.py)
   ├─ 62 curated tools, instructions for the agent, background schema index
   ├─ disk readers: Verse files, editor log, crash reports (0 editor ticks)
   ├──HTTP──▶ Epic's Unreal MCP inside the editor  127.0.0.1:8000/mcp
   │            VerseToolset · DeviceToolset · EntityToolset · SessionToolset · UMG · MVVM · Niagara · editor_toolset.*
   └──HTTP──▶ Python sidecar inside the editor      127.0.0.1:8765   (editor/uefn_pro/sidecar.py)
                execute_python · jobs · batch · bulk actor/asset ops (ops.py) · SceneCapture screenshots
```

- `editor/uefn_pro/bootstrap.py` — throttle off, `ModelContextProtocol.StartServer`, sidecar start; idempotent.
- `editor/uefn_pro/sidecar.py` — `ThreadingHTTPServer` on a daemon thread; editor commands are queued and executed from an `FTSTicker` callback on the game thread (never a Slate tick — double-unregistering those crashes the editor); thread-safe commands answer directly.
- `editor/uefn_pro/ops.py` — pure functions for every actor/asset/viewport command.
- `uefn_mcp_pro/unreal_client.py` — streamable-HTTP client for the official server: session handling, serialization lock, schema cache, tool search, `{"refPath"}` wrapping.
- Sidecar wire format: `POST {"command", "params"}` on 127.0.0.1:8765 (JSON in, `{"success", "result"|"error"}` out).

---

## Known limitations (UEFN 42.00 beta)

- **Bootstrap is manual.** With *UEFN MCP Toolsets* enabled, UEFN turns Python on during `OpenProject_PrePluginLoading`, before the project content is mounted, so `<Project>/Content/Python/init_unreal.py` never runs. `Engine.ini` startup scripts, `UE_PYTHONPATH` and the MCP auto-start preference were tested and are not honoured. One line in the editor's Python console (or the `editor_start_mcp` tool) does the job.
- **Custom Python toolsets are rejected** by Epic's `ToolsetPolicy`; our own tools therefore live in the sidecar, not in Epic's registry.
- The official server executes tools **serially** on the game thread; the gateway serializes calls accordingly. Long operations (`BuildAll`, `StartSession`) can take seconds to minutes; timeouts are per-tool parameters.
- Editing `.uefnproject` while the project is open reloads the project and kills the sidecar. The editor rewrites its ini files on exit; re-run `install.py` if MCP auto-start or the throttle setting disappears.
- A play session (`session_start`) launches the Fortnite client and takes a while; use `session_status` to poll.

---

## Development and tests

```bash
python -m pytest tests -q                       # offline unit tests (log parsers, client helpers)
python -m pyflakes uefn_mcp_pro editor tests    # lint
python tests/smoke_gateway.py                   # live end-to-end test against a running editor (spawns/deletes one actor)
python tests/smoke_gateway.py --quick           # read-only subset
python install.py <project> --check             # report install state
```

Research material: `PLAN.md` (full write-up of the investigation, measurements and decisions), `docs/research/official_toolsets.md` and `docs/research/toolset_schemas_full.json` (every official UEFN toolset with schemas).

---

## Credits and license

- Epic Games — Unreal MCP, the Toolset Registry and the UEFN toolsets that do the heavy lifting.
- Built by [NOVIKIT](https://novikit.gg) while making Fortnite islands with AI agents; a tutorial series on that workflow is in progress.

MIT License — see `LICENSE`.