Verse MCP
MCP server for Fortnite UEFN & Verse development - scan devices ,document the Fortnite API
Ask AI about Verse MCP
Powered by Claude ยท Grounded in docs
I know everything about Verse MCP. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
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.pyruns externally as a FastMCP stdio server.Content/Python/uefn_tools/tools/mcp_bridge.pyruns 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_toolsregistry 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:
- Copy
uefn_tools/andinit_unreal.pyinto the target project. - Start the bridge in the editor.
- 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.
Link mode (opt-in, local dev only)
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.urcignoreentry forContent/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:
- Copy
Content/Python/uefn_tools/into your project atContent/Python/uefn_tools/. - Copy
init_unreal.pyinto your project atContent/Python/init_unreal.py. - Install host dependencies:
python -m pip install -r requirements.txt
- Ensure the project root
.urcignoreincludes:
Content/Python/*
Not Recommended: install.py
install.py still contains older Toolbelt/UI-era behavior. Use deploy.py for the current MCP-first workflow.
Quick Start
- Clone the repo.
git clone https://github.com/quangdang46/uefn-verse-mcp.git
cd uefn-verse-mcp
- Install the host dependency into the same Python interpreter your MCP client will use.
python -m pip install -r requirements.txt
- Deploy the UEFN-side package.
python deploy.py --project "MyIsland"
- Start the listener inside UEFN.
import uefn_tools as ut
ut.register()
ut.run("mcp_start")
- Add the server to your MCP client config.
{
"mcpServers": {
"uefn": {
"command": "python",
"args": ["C:\\path\\to\\uefn-verse-mcp\\server\\main.py"]
}
}
}
- Verify the connection.
ping()
get_status()
run_tool("smoke_test")
- 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/tosys.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.
