Skip to main content
Glama

uefn-mcp

Python 3.10+ UEFN MCP

A two-process MCP bridge for Unreal Editor for Fortnite that lets Claude Code, Cursor, and other MCP clients drive a live UEFN editor safely.

MCP client (Claude Code / Cursor / custom host)
    │
    │ stdio
    ▼
server/main.py  ── FastMCP host process
    │
    │ HTTP 127.0.0.1:8765-8770
    ▼
Content/Python/uefn_tools/tools/mcp_bridge.py
    │
    │ queue + Slate tick callback
    ▼
UEFN editor main thread -> unreal.* -> 354 registered uefn_tools actions

Quick setup

git clone https://github.com/quangdang46/uefn-verse-mcp.git
cd uefn-verse-mcp
python -m pip install -r requirements.txt
python deploy.py --project "MyIsland"

There is no packaged one-line installer yet. The supported path today is source checkout plus local deploy into a UEFN project.

TL;DR

The Problem

UEFN's Python layer is powerful, but it is a bad place to host a modern MCP server directly:

  • unreal.* calls must run on the editor main thread.

  • Blocking Python code can freeze the editor.

  • The MCP SDK belongs in a normal host Python runtime, not inside the editor sandbox.

The Solution

uefn-mcp splits the system in two:

  • server/main.py runs externally as a FastMCP stdio server.

  • Content/Python/uefn_tools/tools/mcp_bridge.py runs inside UEFN as a local HTTP listener.

  • Commands are queued and drained on Slate ticks so editor API calls stay on the main thread.

Why Use uefn-mcp?

Capability

What it gives you

31 direct MCP tools

A curated surface for actors, assets, project info, viewport, logs, and status

354 run_tool entries

An escape hatch into the full uefn_tools registry without re-wrapping every action as first-class MCP

Main-thread-safe dispatch

unreal.* work is queued and executed on editor ticks instead of from the HTTP thread

Auto port discovery

The host scans ports 8765-8770 and reconnects automatically if the listener moved

Generic UEFN loader

init_unreal.py auto-loads any package under Content/Python/ that exposes register()

Deployment helper

deploy.py copies the package and keeps .urcignore aligned for editor-only Python files

The live inventory is tracked in FEATURES.md, and deeper engine constraints are documented in docs/UEFN_QUIRKS.md.

Origins and Credits

This repo sits on top of two upstream lines:

  • UEFN Toolbelt — broad in-editor uefn_tools registry and tooling: UEFN-TOOLBELT (Ocean Bennett).

  • External MCP + in-editor listener — architecture and code lineage from KirChuvakov’s MIT-licensed projects (commonly referenced as uefn-mcp-server or uefn-verse-server).

This fork keeps both ideas, but reshapes them into an MCP-first repo:

  • external FastMCP host process in server/

  • in-editor HTTP bridge in Content/Python/uefn_tools/tools/mcp_bridge.py

  • curated direct MCP surface plus a registry escape hatch

License

Licensing is documented in LICENSE. In short:

  • Combined distribution: This repository is offered under GNU AGPL v3.0 with UEFN Toolbelt’s additional visible-attribution term (copyleft; network use triggers source-sharing obligations — see the license file and the AGPL-3.0 text).

  • Upstream MIT components: Portions derived from KirChuvakov’s work were originally under the MIT License; their copyright and permission notice must stay intact in copies of this project (also reproduced in LICENSE).

  • Forks and derivatives: If you ship something built on this codebase or its architecture, include the visible Toolbelt credit required in LICENSE (for example in README, docs, store page, or an in-editor credits panel):

    Built on UEFN Toolbelt by Ocean Bennett
    (https://github.com/undergroundrap/UEFN-TOOLBELT)

    …or an equivalent that names Ocean Bennett and links to that repository.

This is not legal advice; read LICENSE for full terms.

Quick Example

The fastest end-to-end workflow is:

git clone https://github.com/quangdang46/uefn-verse-mcp.git
cd uefn-verse-mcp
python -m pip install -r requirements.txt
python deploy.py --project "MyIsland"
# In the UEFN Python console
import uefn_tools as ut; ut.register(); ut.run("mcp_start")
// In your MCP client config (.mcp.json)
{
  "mcpServers": {
    "uefn": {
      "command": "python",
      "args": ["C:\\path\\to\\uefn-verse-mcp\\server\\main.py"]
    }
  }
}

Once the client reconnects, the typical first calls look like this:

ping()
get_project_info()
get_all_actors()
spawn_actor(class_path="/Game/Blueprints/BP_Cube.BP_Cube_C", location=[0, 0, 200])
set_viewport_camera(location=[-800, 0, 400], rotation=[-15, 0, 0])
list_tools(category="Materials")
describe_tool("material_gradient_painter")
run_tool("arena_generate", kwargs={"size": "large", "teams": True})
run_tool("smoke_test")

Examples above use conceptual MCP call syntax. The tool names and parameters match the real FastMCP surface exported by server/main.py.

Design Philosophy

1. Keep the MCP SDK outside UEFN

The host process uses ordinary Python and mcp>=1.2.0. UEFN only needs the local listener and the tool registry.

2. Never call unreal.* from the wrong thread

The HTTP server runs on a daemon thread, but every editor action is queued and drained on the Slate post-tick callback. That is the safety boundary that keeps automation from fighting the editor event loop.

3. Curate the common path, keep the full escape hatch

The direct MCP surface covers the most common actor, asset, level, and viewport workflows. run_tool, list_tools, and describe_tool expose the deeper registry when you need specialized behavior.

4. Make deploys boring

The intended setup is:

  1. Copy uefn_tools/ and init_unreal.py into the target project.

  2. Start the bridge in the editor.

  3. Point the MCP client at server/main.py.

No editor-side package installation and no IDE-specific glue should be required beyond that.

Comparison

Approach

MCP-native

Full registry access

Main-thread safety model

Best for

Ad hoc UEFN Python scripts

No

No

You own every threading mistake

One-off local editor tasks

Minimal custom HTTP bridge

Partial

Usually narrow

Depends on your implementation

Small, fixed editor automations

uefn-mcp

Yes

Yes, via run_tool

Queue + Slate tick dispatch

AI-agent-driven UEFN editing and experimentation

Prerequisites

  • UEFN on Windows, with the Python Editor Script Plugin enabled

  • Host Python 3.10 or newer for server/main.py, deploy.py, and the MCP dependency

  • A local UEFN project you can copy Content/Python/ files into

Installation

Option 1: deploy.py (recommended)

deploy.py is the clean MCP-first path.

python -m pip install -r requirements.txt
python deploy.py --project "MyIsland"

You can also deploy by full path:

python deploy.py --path "D:\\Projects\\MyIsland"

If you omit both flags, deploy.py will prompt you interactively.

For fast local iteration you can link uefn_tools/ instead of copying it. Edits in the repo checkout are then immediately visible to UEFN without redeploying.

python deploy.py --project "MyIsland" --link-kind junction
python deploy.py --path "D:\\Projects\\MyIsland" --link-kind symlink

--link-kind

Behavior

Privileges

junction

Windows directory junction (recommended on Windows)

No special privileges required

symlink

OS-native symbolic link

May need Developer Mode or admin on Windows

init_unreal.py is always copied as a real file regardless of link mode.

Note: Link mode is a local development convenience. Do not use it for shared or team projects — use the default copy mode instead.

What it installs:

  • Content/Python/uefn_tools/

  • Content/Python/init_unreal.py

  • .urcignore entry for Content/Python/*

Option 2: deploy.bat (Windows interactive helper)

If you prefer a Windows batch workflow:

deploy.bat

This is still present as a helper, but the repo's documented MCP-first path is deploy.py.

Option 3: Manual install

If you need full control:

  1. Copy Content/Python/uefn_tools/ into your project at Content/Python/uefn_tools/.

  2. Copy init_unreal.py into your project at Content/Python/init_unreal.py.

  3. Install host dependencies:

python -m pip install -r requirements.txt
  1. Ensure the project root .urcignore includes:

Content/Python/*

install.py still contains older Toolbelt/UI-era behavior. Use deploy.py for the current MCP-first workflow.

Quick Start

  1. Clone the repo.

git clone https://github.com/quangdang46/uefn-verse-mcp.git
cd uefn-verse-mcp
  1. Install the host dependency into the same Python interpreter your MCP client will use.

python -m pip install -r requirements.txt
  1. Deploy the UEFN-side package.

python deploy.py --project "MyIsland"
  1. Start the listener inside UEFN.

import uefn_tools as ut
ut.register()
ut.run("mcp_start")
  1. Add the server to your MCP client config.

{
  "mcpServers": {
    "uefn": {
      "command": "python",
      "args": ["C:\\path\\to\\uefn-verse-mcp\\server\\main.py"]
    }
  }
}
  1. Verify the connection.

ping()
get_status()
run_tool("smoke_test")
  1. Start editing the level from your MCP client.

get_all_actors()
search_assets(class_name="StaticMesh", directory="/Game", recursive=True)
spawn_actor(class_path="/Game/Blueprints/BP_Cube.BP_Cube_C", location=[0, 0, 100])

Command Reference

System

Tool

What it does

Example

ping

Confirms the host can reach the in-editor bridge

ping()

get_status

Returns listener status and bridge metadata

get_status()

execute_python

Executes arbitrary Python inside the UEFN editor runtime

execute_python(code="result = {'status': 'ok'}")

get_log

Returns recent bridge log lines

get_log(lines=100)

shutdown

Stops the in-editor listener

shutdown()

Actors

Tool

What it does

Example

get_all_actors

Lists actors in the current level

get_all_actors()

get_selected_actors

Reads the current editor selection

get_selected_actors()

spawn_actor

Spawns an actor from a class or object path

spawn_actor(class_path="/Game/Blueprints/BP_Cube.BP_Cube_C", location=[0, 0, 100])

delete_actors

Deletes actors by label

delete_actors(actor_labels=["Cube_1", "Cube_2"])

set_actor_transform

Updates actor location, rotation, and/or scale

set_actor_transform(actor_label="Cube_1", location=[200, 0, 100])

get_actor_properties

Reads properties for a named actor

get_actor_properties(actor_label="Cube_1")

set_actor_properties

Writes multiple actor properties

set_actor_properties(actor_label="Cube_1", properties={"mobility": "Static"})

select_actors

Selects actors in the editor

select_actors(actor_labels=["Cube_1"])

focus_selected

Moves the viewport to the active selection

focus_selected()

Assets

Tool

What it does

Example

list_assets

Lists assets in a directory

list_assets(directory="/Game", recursive=True)

get_asset_info

Reads details for a specific asset path

get_asset_info(asset_path="/Game/MyFolder/MyAsset")

get_selected_assets

Returns selected Content Browser assets

get_selected_assets()

rename_asset

Renames or moves an asset

rename_asset(old_path="/Game/Old", new_path="/Game/New")

delete_asset

Deletes an asset

delete_asset(asset_path="/Game/Trash/TempAsset")

duplicate_asset

Copies an asset to a new path

duplicate_asset(source_path="/Game/A", dest_path="/Game/B")

does_asset_exist

Checks if an asset exists

does_asset_exist(asset_path="/Game/Props/SM_Crate")

save_asset

Saves a dirty asset

save_asset(asset_path="/Game/Props/SM_Crate")

search_assets

Searches the Asset Registry with directory and class filters

search_assets(class_name="Material", directory="/Game", recursive=True)

Project / Level

Tool

What it does

Example

get_project_info

Returns project metadata

get_project_info()

save_current_level

Saves the current level

save_current_level()

get_level_info

Returns summary info about the active level

get_level_info()

Viewport

Tool

What it does

Example

get_viewport_camera

Reads the editor camera position and rotation

get_viewport_camera()

set_viewport_camera

Moves the editor camera

set_viewport_camera(location=[-800, 0, 400], rotation=[-15, 0, 0])

Escape Hatch

Tool

What it does

Example

run_tool

Executes any registered uefn_tools action by name

run_tool("arena_generate", kwargs={"size": "large"})

list_tools

Lists all registered tools, optionally by category

list_tools(category="Materials")

describe_tool

Returns description and parameters for one tool

describe_tool("material_gradient_painter")

Working With the Full Registry

The direct MCP surface is intentionally small. The deeper workflows are meant to be discovered dynamically:

list_tools()
list_tools(category="Verse Helpers")
describe_tool("device_call_method")
run_tool("device_call_method", kwargs={"class_filter": "TimerObjective", "method": "blueprint_pause"})

The full generated inventory lives in FEATURES.md.

Configuration

.mcp.json

Use the same Python interpreter here that you used for python -m pip install -r requirements.txt.

{
  "mcpServers": {
    "uefn": {
      "command": "python",
      "args": ["C:\\dev\\uefn-verse-mcp\\server\\main.py"]
    }
  }
}

Environment Variables

Variable

Default

Purpose

UEFN_MCP_REQUEST_TIMEOUT

30

Default timeout in seconds for bridge requests

UEFN_MCP_LONG_TOOL_TIMEOUT

900

Timeout for long-running run_tool calls that opt into the extended budget

Port Behavior

  • The in-editor listener binds to the first available port in 8765-8770.

  • The host discovers the active port by probing http://127.0.0.1:<port>/.

  • If the cached port stops responding, the host rescans automatically.

UEFN Loader Contract

init_unreal.py is intentionally generic:

  • it adds Content/Python/ to sys.path

  • it discovers packages with __init__.py

  • it calls register() when available

That means uefn_tools is only one possible package. The loader is reusable for other editor-side Python packages in the same project.

Architecture

┌──────────────────────────────────────────────────────────────────────┐
│ MCP client                                                          │
│ Claude Code / Cursor / custom FastMCP host                          │
└──────────────────────────────────────────────────────────────────────┘
                                │
                                │ stdio
                                ▼
┌──────────────────────────────────────────────────────────────────────┐
│ server/main.py                                                      │
│ - FastMCP server                                                    │
│ - 31 direct tools                                                   │
│ - run_tool / list_tools / describe_tool                             │
└──────────────────────────────────────────────────────────────────────┘
                                │
                                │ HTTP JSON POST
                                ▼
┌──────────────────────────────────────────────────────────────────────┐
│ Content/Python/uefn_tools/tools/mcp_bridge.py                       │
│ - Local HTTP listener inside the UEFN process                       │
│ - Queue-based handoff from HTTP thread to editor main thread        │
│ - Bridge commands: system, actors, assets, viewport, escape hatch   │
└──────────────────────────────────────────────────────────────────────┘
                                │
                                │ Slate post-tick callback
                                ▼
┌──────────────────────────────────────────────────────────────────────┐
│ UEFN editor main thread                                             │
│ - Executes unreal.* calls safely                                    │
│ - Reads and writes level / assets / viewport                        │
│ - Invokes 354 registered uefn_tools actions                         │
└──────────────────────────────────────────────────────────────────────┘

Important files:

File

Responsibility

server/main.py

External FastMCP entrypoint

server/bridge.py

Host-side HTTP client for the in-editor listener

server/port_discovery.py

Port probing across 8765-8770

Content/Python/uefn_tools/tools/mcp_bridge.py

In-editor HTTP server and queue dispatcher

Content/Python/uefn_tools/__init__.py

Package root, registry setup, plugin loading

deploy.py

Project deployment and .urcignore maintenance

init_unreal.py

Generic UEFN Python package loader

For a shorter architecture note, see ARCHITECTURE.md.

Troubleshooting

Symptom

Likely cause

Fix

No module named 'mcp'

Your MCP client is running a different Python than the one you installed into

Install requirements.txt into the exact interpreter used by the MCP command

UEFN listener not found on ports 8765-8770

The in-editor bridge is not running

In the UEFN console, run import uefn_tools as ut; ut.register(); ut.run("mcp_start")

Commands fail right after deploy

The package was copied into the project, but the editor has not loaded it yet

Re-run ut.register() manually, or restart UEFN if you changed init_unreal.py

The editor freezes during custom automation

Blocking code is running on the main thread

Avoid time.sleep() and long synchronous loops in editor-side code; keep work queued and tick-driven

Asset or project paths resolve strangely in init_unreal.py

UEFN path helpers do not always point where you expect during startup

Use the file-system path of init_unreal.py itself rather than unreal.Paths.project_content_dir()

run_tool works locally but returns unusable data to the client

A tool is returning None, a primitive, or a live Unreal object instead of a JSON-friendly result

Make the tool return a structured dict, preferably with a "status" field

Limitations

  • This repo does not make UEFN headless. You still need a live local editor session.

  • The direct MCP surface is curated; deeper workflows rely on run_tool.

  • The 354-tool registry is broad, but not every entry has been verified in every environment yet. See the status column in FEATURES.md.

  • UEFN Python has engine-specific quirks and unsafe APIs. Some operations that are fine in full Unreal Engine are limited or crash-prone in UEFN.

  • The documented install path is source-based. There is no PyPI, Homebrew, winget, or standalone installer release flow documented here today.

FAQ

Do I need to install anything inside UEFN's embedded Python?

No for the MCP server itself. The mcp dependency belongs in the external host Python that runs server/main.py.

Why not run the MCP SDK directly inside the editor?

Because the editor runtime is the wrong place for a long-lived stdio server and blocking automation. The two-process split is the safety boundary.

What should I call first after connecting?

ping(), then get_status(), then either get_project_info() or run_tool("smoke_test").

How do I access the specialized tools?

Use list_tools() to discover categories, describe_tool() to inspect parameters, and run_tool() to execute the chosen action.

Can I use this without Claude Code?

Yes. Any MCP client that can launch a stdio server can point at server/main.py.

Do I have to restart UEFN after every change?

No. Most package changes can be reloaded with ut.register(). Changes to startup loader behavior in init_unreal.py are the ones most likely to require a restart.

Where should I look if UEFN behavior seems weird?

Start with docs/UEFN_QUIRKS.md. That file captures the non-obvious engine behavior this repo is already compensating for.

F
license - not found
-
quality - not tested
-
maintenance - not tested

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/quangdang46/uefn-verse-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server