MCP server for Unreal Editor for Fortnite (UEFN) — lets Claude / Cursor drive UEFN via Python
Project description
uefn-mcp
MCP server for Unreal Editor for Fortnite — let Claude / Cursor drive UEFN through Python, the same way
rbxstudio-mcpdrives Roblox Studio.
Now at v1.6 — Verse digest tooling + a human-in-the-loop coordination
primitive. The LLM can now (a) ask "what devices exist in Fortnite?" and get a
parsed answer from Fortnite.digest.verse without dumping 50K lines into
context, and (b) block on wait_for_events(...) until the user does
something — press Ctrl+Shift+B to build, click an actor in the viewport —
instead of asking and re-asking. Transport is HTTP long-poll, not literal
WebSockets (UEFN's embedded Python is painful to pip-install into; the UX is
identical for the events that actually fire). 47 tools total (was 43). See
§v1.6 highlights below.
Honest receipt on
wait_for_events: today the events that fire arelog.error/log.warning(mostly echoes of the LLM's ownexecute_pythonerrors),verse.file_written(circular — the LLM emits it itself), and the two genuinely useful ones:verse.build(user pressed Ctrl+Shift+B) andselection.changed(user clicked an actor). There are no gameplay hooks wired in — noplayer.spawnedordevice.triggeredfrom PIE — and the MCP tool surface can't start a play session anyway. Treat this as "wait for the human at the keyboard to do X", not as autonomous reactivity.
v1.5 (previous) — audit fixup release. v1.4 shipped MVVM notes that contained
a handful of errors (phantom set_*_conversion_function methods, wrong type
on override_execution_mode, an undercount of MVVMBlueprintView properties,
two fabricated quotes). v1.5 rewrites MVVM_API_NOTES.md from the actual
Python 5.7 docs, fixes a real runtime bug where the conversion-function
parameter on add_view_binding silently no-op'd, and acknowledges that the
entire conversion-function surface is Read-Only in 5.7 Python (set it in
the editor UI). See §v1.5 audit fixup below.
Architecture
┌──────────────┐ stdio MCP ┌──────────────┐ HTTP ┌────────────────────┐
│ Claude/Cursor│ ◄────────────► │ uefn-mcp │ ◄──────► │ UEFN listener │
│ │ │ (this server)│ :8766 │ (embedded Python) │
└──────────────┘ └──────────────┘ └────────────────────┘
│
▼
unreal.* on game thread
- The MCP server runs outside UEFN (talks stdio to your IDE).
- The listener lives inside UEFN's embedded Python 3.11 interpreter and
serves an HTTP API on
127.0.0.1:8766. Every command is parked on a queue and executed on the game thread viaregister_slate_post_tick_callback— because allunreal.*calls have to happen there. - Mutating tools wrap their snippet in
unreal.ScopedEditorTransactionso each tool call is a single Ctrl+Z step.
Install
1. Install the UEFN plugin
Copy the two files in uefn_plugin/ into your UEFN project:
[YourUEFNProject]/Content/Python/init_unreal.py
[YourUEFNProject]/Content/Python/uefn_listener.py
If the Python/ folder doesn't exist, create it. UEFN's embedded interpreter
auto-runs init_unreal.py on project open.
Make sure UEFN's Python plugin is enabled (Edit ▸ Plugins ▸ search "Python"). For MVVM tools you also need the Model View Viewmodel plugin enabled (Edit ▸ Plugins ▸ search "MVVM").
Open your project. You should see in the Output Log:
[uefn-mcp] ✅ Listener started on http://127.0.0.1:8766
Sanity check from a terminal:
curl http://127.0.0.1:8766/health
# {"ok": true, "service": "uefn-mcp-listener", "queue_depth": 0, ...}
2. Install the MCP server
From inside this repo:
pip install -e .
# or, if you're a uv person:
uv pip install -e .
That gives you a uefn-mcp console script.
3. Wire it into your IDE
Claude Desktop — ~/Library/Application Support/Claude/claude_desktop_config.json (mac) or %APPDATA%\Claude\claude_desktop_config.json (windows):
{
"mcpServers": {
"uefn": {
"command": "uefn-mcp"
}
}
}
Cursor — Settings ▸ MCP ▸ Add new MCP Server:
{
"uefn": { "command": "uefn-mcp" }
}
Restart your IDE. You should see uefn show up as a connected MCP server.
Project layout (v1.5)
src/uefn_mcp/
├── server.py ~70 lines: entry point — imports tools/ and runs FastMCP
├── _bridge.py HTTP transport + transaction wrapper + shared mcp instance
├── _snippets.py reusable Python text snippets (ACTOR/MVVM/FRAME/VERSE/WIDGET)
└── tools/
├── system.py ping, execute_python, get_output_log
├── actors.py query, property access, mutation
├── navigation.py select_actors, focus_viewport_on
├── discovery.py class distribution, tags, device catalog
├── level.py level info / save / undo / redo
├── verse.py list / read / write / restore Verse files
├── assets.py list_assets
├── screenshots.py viewport / actor screenshots
├── widgets.py WBP create / duplicate / compile / inspect
└── mvvm.py view bindings, viewmodel contexts, conversions
MVVM_API_NOTES.md documents the verified Read-Only / Read-Write surface
map per UE Python 5.7 docs — read it before touching tools/mvvm.py or
_snippets.MVVM_HELPERS.
Tools (v1.6 — 47 total)
System
| Tool | What it does |
|---|---|
ping |
Health check on the listener |
execute_python |
Run arbitrary Python on the game thread |
get_output_log |
Read recent UEFN log messages |
Actor — query
| Tool | What it does |
|---|---|
get_all_actors |
List actors in the level (summary view, paginated) |
get_selected_actors |
Actors selected in the viewport |
get_actor_details |
One actor with summary/detailed/full verbosity |
find_actors |
Filter by name regex, class, or tag |
get_actor_components |
List components on an actor |
get_actor_property |
Read any editor property (supports nested paths) |
get_class_distribution |
Count actors per class |
get_all_tags |
List every tag in use across the level |
get_device_catalog |
List runtime UE class names (e.g. TriggerDevice_C) — for spawning into the level via Python. Distinct from v1.6's list_verse_devices, which returns Verse-language names for writing .verse code |
Actor — mutate (transaction-wrapped)
| Tool | What it does |
|---|---|
set_actor_property |
Write an editor property (with type coercion) |
spawn_actor |
Spawn an actor from an asset path |
delete_actor |
Delete an actor by label |
set_actor_transform |
Move/rotate/scale an actor |
select_actors |
Select a list of actors |
focus_viewport_on |
Frame the viewport on an actor (F-key equivalent) |
Level
| Tool | What it does |
|---|---|
get_level_info |
World name + actor count |
save_current_level |
Save the open level |
undo |
Undo N steps |
redo |
Redo N steps |
Verse
| Tool | What it does |
|---|---|
list_verse_modules |
List .verse files (scans VerseDevices, Verse, plugin Verse) |
read_verse_file |
Read a Verse file |
write_verse_file |
Write a Verse file (auto-snapshots prior content) |
list_verse_snapshots |
List recent snapshots of a Verse file |
restore_verse_file |
Restore from a snapshot (still hit Ctrl+Shift+B to build) |
Assets
| Tool | What it does |
|---|---|
list_assets |
List asset paths under a directory |
Screenshots
| Tool | What it does |
|---|---|
screenshot_viewport |
Take a viewport screenshot (returns inline MCP image — Claude can SEE it) |
screenshot_actor |
Frame an actor first, then screenshot it |
UMG / Widget Blueprint
| Tool | What it does |
|---|---|
create_widget_blueprint |
Create a new WBP at a path |
duplicate_widget_blueprint |
Duplicate a WBP |
compile_widget_blueprint |
Compile + save a WBP |
inspect_widget |
Read class, slots, viewmodels, bindings count |
get_widget_tree |
🆕 Read-only widget hierarchy (5.7 Python doesn't expose authoring; design in the UMG Designer) |
MVVM (v1.5 — re-verified against UE Python 5.7 docs)
| Tool | What it does |
|---|---|
inspect_viewmodel |
List a ViewModel's bindable fields and types |
list_view_bindings |
Existing MVVM bindings on a WBP |
list_available_conversions |
🆕 Conversion functions valid for a specific source→dest pair |
add_viewmodel_context |
Add a viewmodel context to a WBP |
add_view_binding |
Bind viewmodel field → widget property (with optional conversion + mode) |
remove_view_binding |
Remove a binding by index |
remove_viewmodel_context |
Remove a viewmodel context |
Verse digests (v1.6 — new)
The Verse *.digest.verse files are the canonical API surface for
Fortnite devices, UnrealEngine types, and core Verse types. These tools
parse them server-side so the LLM gets targeted answers instead of 50K-line
file dumps.
| Tool | What it does |
|---|---|
list_verse_digests |
🆕 Find every *.digest.verse file in the project (Fortnite, UnrealEngine, Verse, …) |
list_verse_devices |
🆕 Top-level classes from a digest, optional name-substring filter |
get_verse_device_api |
🆕 Full parsed API of one class — header, methods, events, fields |
search_verse_digest |
🆕 Regex grep across digests — find anything by line |
Typical workflow: "Create a system that uses a trigger and a prop_mover" →
list_verse_devices(filter_substring="trigger") → get_verse_device_api("trigger_device")
→ get_verse_device_api("prop_mover_device") → draft Verse → write_verse_file.
Reactive events — wait_for_events (v1.6 — new)
The listener gained an event buffer + HTTP long-poll endpoint. The LLM can
block on wait_for_events instead of asking the user "did the build
finish yet?" in a loop.
| Tool | What it does |
|---|---|
wait_for_events |
🆕 Block up to 60s until matching events arrive. timeout_s=0 peeks the buffer without blocking (max_count caps the slice). |
Single tool, two modes — peek (timeout 0) was previously a separate
get_recent_events; that was just wait_for_events(timeout_s=0) under the
hood, so it folded in.
Built-in event kinds: log.error, log.warning, verse.build (heuristic),
selection.changed, verse.file_written. Custom kinds can be emitted from
any snippet via the pre-injected push_event(kind, data) callable
(e.g. execute_python("push_event('build.done', {'ok': True})")).
The listener also exposes raw POST /events/push for external clients.
Realistic use cases (be honest about what fires):
# 1. You wrote a Verse file, ask the user to build, wait for the result
write_verse_file("MyDevice.verse", code)
# tell the user: "Press Ctrl+Shift+B"
out = wait_for_events(kinds=["verse.build"], timeout_s=60)
if out["events"]:
status = out["events"][0]["data"]["status"] # "ok" or "fail"
# 2. Coordinate with the user via viewport selection
# tell the user: "Click the wall you want me to align to"
out = wait_for_events(kinds=["selection.changed"], timeout_s=120)
What it's not good for: autonomous LLM-only reactivity. log.error and
log.warning mostly echo what execute_python already returns synchronously.
There are no game-event hooks (no player.spawned, no device.triggered),
and the MCP surface can't start a play session anyway. If gameplay hooks
ever land, the primitive is ready for them.
Why long-poll instead of WebSockets? UEFN's embedded Python is painful to
pip-install into, and stdlib has no WS support. Long-poll gives the same
"wake immediately when events arrive" UX with zero new deps. The bridge's
HTTP timeout for these calls is server_timeout + 10s (default 35s total).
execute_python is the escape hatch — anything not exposed as a tool can still
be done with raw Python. Pre-injected names: unreal, actor_sub, asset_sub,
level_sub, editor_sub, mvvm_sub, push_event, result. Assign to result
to return a value. (mvvm_sub is None if the MVVM plugin isn't enabled.)
Example prompt: "Spawn a cube at (100, 0, 50), name it Hello, screenshot it, then read it back."
A note on MVVM
UEFN's Python surface for MVVM (MVVMEditorSubsystem, MVVMBlueprintView,
MVVMBlueprintPropertyPath) varies a bit between UE 5.5 / 5.6 / 5.7. The
v1.5 implementation has been re-verified against the canonical UE Python 5.7
docs after a second-pass audit caught errors in the v1.4 notes (see
MVVM_API_NOTES.md §9 "Audit fixup" for the full diff).
Verified facts baked into the code:
- ✅
unreal.get_editor_subsystem(unreal.MVVMEditorSubsystem)is the right accessor (the runtimeMVVMSubsystemCDO has none of the editor verbs). - ✅
MVVMBlueprintView.bindingsandavailable_view_modelsare Read-Write arrays — accessed viaview.get_editor_property(...). (The full property set is 5: alsocompiled_binding_library_id,conditions,events.) - ✅
MVVMBindingModeenum:ONE_TIME_TO_DESTINATION=0,ONE_WAY_TO_DESTINATION=1,TWO_WAY=2,ONE_WAY_TO_SOURCE=4— note the gap at value 3. - ✅
MVVMEditorSubsystem.remove_view_modeltakesName, notGuid. - ✅
MVVMBlueprintViewBinding.override_execution_modeis anMVVMExecutionModeenum (notbool). - ⚠️ ALL
MVVMBlueprintPropertyPathproperties are Read-Only per docs, and there are no documentedset_widget_name/set_view_model_id/set_property_pathUFUNCTIONs. The path setters in_snippets.MVVM_HELPERSuse a multi-strategy fallback (typed UFUNCTION →set_editor_property→ directpathsarray writes viaMVVMBlueprintFieldPath) and stamp thesourceenum (WIDGET/VIEW_MODEL) for editor-UI sync. - 🚧 The conversion-function surface is fully Read-Only in 5.7 Python:
MVVMBlueprintViewBinding.conversion, both directions onMVVMBlueprintViewConversionPath, and all 6 fields onMVVMBlueprintViewConversionFunction. There are no documentedset_*_conversion_functionmethods onMVVMEditorSubsystem(v1.4 claimed there were — they're phantom). v1.5 attempts a best-effort in-place mutation whenadd_view_bindingis called withconversion_function, but it returns a clear note when the engine refuses, in which case set the conversion in the editor UI (Widget Designer → ViewModels panel → click the binding → choose conversion).
If a binding tool fails, the fast diagnostic is one execute_python call —
e.g. execute_python("result = sorted(dir(unreal.MVVMEditorSubsystem))") —
to see exactly which methods exist on MVVMEditorSubsystem /
MVVMBlueprintView / MVVMBlueprintPropertyPath /
MVVMBlueprintViewBinding in your specific install. (v1.4–v1.5 shipped a
dedicated probe_mvvm_api tool for this; v1.6 dropped it — the bundled
dir() walk is one line via execute_python, and MVVM_API_NOTES.md is
the documented baseline.)
The widget tree itself stays in your hands. There is no UEFN-specific "lock" — what's actually happening is lock by omission in UE 5.7's Python bindings:
WidgetTree(5.7 docs page): stub.Bases: Object, zero methods, zero properties. Noconstruct_widget, noroot_widget.BaseWidgetBlueprint/UserWidgetBlueprint/WidgetBlueprint: all stubs. Only blueprint metadata (compile_mode,blueprint_category, etc.). Nowidget_treeeditor property accessor anywhere in the chain.- C++
UWidgetTree::ConstructWidget<T>is a templated method, which Unreal's reflection system doesn't bind to Python (UnrealHeaderTool can't UFUNCTION- expose templates). WidgetLibrary/WidgetLayoutLibrary/UserWidgetFunctionLibrary/WidgetReferenceBlueprintFunctionLibrary/WidgetAnimationHandleFunctionLibrary: all runtime-mutation helpers. Zero authoring methods across all five.PanelWidget.add_child()/clear_children()/remove_child()etc. are exposed — but only for runtime mutation of already-instantiated widgets at play time. They don't help author the persisted WBP at editor time.
This matches Epic's design intent: author UMG in the Designer →
attach bindings/conditions/events via MVVMEditorSubsystem Python verbs →
consume at runtime from Verse. Editor-time UMG hierarchy authoring routes
through the Designer; runtime widget creation in cooked builds routes through
Verse, not Python. (Conversion functions are also Designer-set, see above.)
This MCP automates everything around the widget tree — viewmodels, bindings, compilation, save, screenshots, MVVM conversions where the engine permits.
Per Epic's official UEFN docs, MCP is a sanctioned use case:
"Remote execution of well made scripts or tools, such as a MCP integration with an Agentic AI" — Python Tools in UEFN, dev.epicgames.com (line 18 of the scrape)
v1.5 audit fixup
A second-pass audit against the firecrawl scrape of dev.epicgames.com/python-api
caught the following errors in v1.4. v1.5 fixes them all:
| Issue (v1.4) | Fix (v1.5) |
|---|---|
tools/mvvm.py called phantom sub.set_source_to_destination_conversion_function() |
Removed phantom calls; replaced with best-effort in-place mutation on binding.conversion |
MVVM_API_NOTES.md claimed MVVMEditorSubsystem has ~9 methods |
Documented all 21 (added add_condition, add_event, rename_view_model, reparent_view_model, etc.) |
MVVMBlueprintView listed as 2 properties |
Now lists all 5 (compiled_binding_library_id, conditions, events were missing) |
override_execution_mode typed as bool |
Corrected to MVVMExecutionMode enum |
Restrictions section quoted uefn-vs-ue-in-unreal-editor-for-fortnite.md |
Quote doesn't exist in source — section now only cites python-tools-in-uefn.md with line nums |
MVVMBindingMode listed without flagging the value-3 gap |
Now explicitly noted: values are [0, 1, 2, 4] |
No new tools added — the audit's "missing methods" list (add_condition,
add_event, rename_view_model, get_child_view_models, etc.) was
deliberately left out of the tool surface to avoid count inflation. Use
execute_python with the pre-injected mvvm_sub to call them directly.
v1.6 highlights
Two big features. Both shipped together because they're complementary — digest tooling tells the LLM what to build, reactive events tell it when something happened.
1. Verse digest tooling — 4 new tools (tools/verse_digest.py)
The problem v1.5 left open: the LLM's "what devices exist?" mental model. Without digest awareness, the LLM falls back to its training data, which is stale and incomplete for UEFN Verse. With it, you get:
list_verse_devices(filter_substring="trigger")
→ [{"name": "trigger_device", ...}, {"name": "billboard_trigger_device", ...}]
get_verse_device_api("trigger_device")
→ {
"header": "trigger_device := class<concrete>(creative_device_base):",
"members": [
{"kind": "event", "name": "Triggered", "signature": "..."},
{"kind": "method", "name": "Trigger", "signature": "..."},
{"kind": "method", "name": "Reset", "signature": "..."},
...
]
}
Parsing happens server-side in the listener — the bridge never receives
the 50K-line digest, only the targeted slice. search_verse_digest adds a
regex grep across all digests in case the structured tools miss something.
The fix to _verse_search_roots is small but important: digest files live
at Plugins/<X>/Content/<X>.digest.verse, NOT in Plugins/<X>/Content/Verse/.
Pre-v1.6 list_verse_modules missed them entirely.
2. Human-in-the-loop coordination — wait_for_events (1 new tool)
The roadmap entry said "WebSocket transport for reactive workflows". The ship reality: HTTP long-poll with the same UX. The transport tradeoff:
| Aspect | True WebSockets | HTTP long-poll (shipped) |
|---|---|---|
| Wake latency | < 10 ms | < 10 ms (cond var notify) |
| Listener deps | Needs websockets lib in UEFN Python |
Stdlib only |
| Code size | ~400 LoC (server + client + framing) | ~150 LoC |
| Persistent connection | Yes | No (one connection per wait call) |
| LLM-facing API | Identical | Identical |
UEFN's embedded Python is painful to pip-install into. Until that changes,
long-poll is the right call. The True WebSocket transport roadmap entry
is still there if/when a use case actually needs the persistent connection
(e.g. a separate web dashboard mirroring editor state in real time).
What this tool actually is (post-audit, fixed up from the v1.6 launch
narrative): a human-in-the-loop coordination primitive, not autonomous
reactivity. The events that fire today are mostly user-driven (verse.build
on Ctrl+Shift+B, selection.changed on click) or self-echoing
(log.error / log.warning from the LLM's own execute_python errors,
verse.file_written from its own writes). There are no gameplay-event
hooks; the MCP tool surface can't start a play session anyway. So the
realistic value is "block until the user does the thing", not "react to
the game".
get_recent_events was originally shipped as a separate peek tool; it was
literally wait_for_events(timeout_s=0) under the hood. Folded back in.
Set timeout_s=0 to peek; max_count caps the slice (default 100, server
buffer holds 500).
What you can do today:
# Wait for the user to press Ctrl+Shift+B after you wrote Verse code
out = wait_for_events(kinds=["verse.build"], timeout_s=60)
if out["events"]:
status = out["events"][0]["data"]["status"] # "ok" or "fail"
# Coordinate with the user via viewport selection
out = wait_for_events(kinds=["selection.changed"], timeout_s=120)
# Peek for recent errors without blocking
out = wait_for_events(kinds=["log.error"], timeout_s=0, max_count=20)
# Cursor across multiple polls
out = wait_for_events(since_id=out["last_id"], timeout_s=25)
Built-in event sources:
log.error/log.warning— anything passed tounreal.log_error/_warningverse.build— heuristic: matches"Verse compilation succeeded"/"...failed"patterns inLogVerse. Tweak_maybe_emit_verse_build_eventinuefn_listener.pyif your UEFN version uses different strings.selection.changed— actor selection in viewport (polled in the tick callback because UEFN doesn't expose a Python selection delegate)verse.file_written— emitted bywrite_verse_fileafter a successful write
Custom kinds: any tool can call push_event(kind, data) (pre-injected) to
emit its own events.
Limitations
- Verse builds are manual. UEFN doesn't expose a programmatic build trigger.
After
write_verse_file, you (or the user) hitCtrl+Shift+Bin UEFN. Mitigated in v1.6:wait_for_events(kinds=["verse.build"])blocks until the build finishes so the LLM can react without spin-polling. - Game thread only (for
unreal.*calls). Long-running snippets block the editor for up to 30s (the listener timeout). The new/events/waitendpoint runs in the HTTP worker thread, not the game thread — long-polling for events does not block the editor. verse.buildevent detection is heuristic. The listener pattern-matches "Verse compilation succeeded/failed" strings out ofLogVerse. If a future UEFN version changes the wording,_maybe_emit_verse_build_eventinuefn_listener.pyneeds a one-line tweak. (PollingLogVersetext is the only path; UEFN doesn't expose a build delegate to Python.)- Local only. Listener binds to
127.0.0.1. No auth — don't expose port 8766. - Widget-tree authoring is not exposed in 5.7 Python (lock-by-omission, not a UEFN-specific policy). Design the visual layout in the UMG Designer. Bindings/conditions/events around the tree are scriptable. See "A note on MVVM" above for the full receipts.
- MVVM plugin required for the MVVM tool family. The listener still starts
fine without it; just
mvvm_subwill beNoneand the MVVM tools will tell you to enable the plugin.
Documentation provenance
Two pools of source material drive the implementation, kept out of this repo (in a sibling chat workspace) but referenced when building each version:
- Verse API docs (
Fortnite.com,UnrealEngine.com,Verse.orgmodules, scraped fromdev.epicgames.com) — drive the Verse tool family (list_verse_modules,read_verse_file, etc.) and inform device-catalog / Verse-template work on the roadmap. - UE Python 5.7 API docs (
unreal.MVVMEditorSubsystem,unreal.MVVMBlueprintView,unreal.MVVMBlueprintPropertyPath,unreal.MVVMBlueprintViewBinding,unreal.MVVMBlueprintViewConversionPath,unreal.MVVMBlueprintViewConversionFunction,unreal.MVVMBlueprintFieldPath,unreal.MVVMBindingMode,unreal.MVVMExecutionMode,unreal.MVVMBlueprintFieldPathSource,unreal.MVVMBlueprintViewModelContextCreationType, plus the Python Tools in UEFN guide that explicitly endorses MCP) — drive the v1.5 MVVM verification. The full Read-Only / Read-Write surface map plus enum values is captured inMVVM_API_NOTES.md(re-verified line-by-line in v1.5 after a second-pass audit).
If a binding tool ever fails on a UEFN install whose MVVM plugin diverges
from those docs, the diagnostic is one execute_python call — e.g.
execute_python("result = sorted(dir(unreal.MVVMEditorSubsystem))") —
to dump the actual API surface in your install for diff'ing against the
notes. (v1.4–v1.5 wrapped this in a probe_mvvm_api tool; v1.6 dropped
the wrapper because it was a packaged dir() walk competing with
execute_python for the LLM's routing slot.)
Roadmap
See UEFN_MCP_BUILD_PLAN.md for the full vision. Short list:
-
ScopedEditorTransactionundo wrapping - Device catalog scan
- Screenshots (viewport + framed actor)
- MVVM tool family (v1.3 actually works)
- Modular refactor — split the 2k-line
server.pyinto focused modules (v1.4) - MVVM verification against canonical UE Python 5.7 docs (v1.4)
- MVVM audit fixup — corrected docs + fixed phantom-method bug in conversion path (v1.5)
- Visual widget authoring — investigated, closed: not feasible at design time in 5.7 Python.
BaseWidgetBlueprint,UserWidgetBlueprint,WidgetBlueprint,WidgetTree,UserWidgetExtensionall docs-stubbed by Epic (only metadata exposed, nowidget_treeaccessor, noconstruct_widget).PanelWidget.add_child()etc. are exposed but runtime-only — once instantiated. UMG hierarchy authoring routes through the Designer; runtime widget creation in cooked builds routes through Verse. See "A note on MVVM" §widget-tree below. - Verse digest tooling (v1.6) —
list_verse_digests/list_verse_devices/get_verse_device_api/search_verse_digest. The LLM can now answer "what devices exist?" / "what's the API of trigger_device?" without dumping 50K-line digests into context. - HITL coordination primitive (v1.6) —
wait_for_eventsblocks up to 60s for editor events (verse.buildon Ctrl+Shift+B,selection.changedon click, pluslog.error/log.warning/verse.file_writtenfor completeness). Transport is HTTP long-poll, not WebSockets — UEFN's embedded Python has no stdlib WS support; long-poll gives identical UX. Honest framing: the events that fire today are user-driven or self-echoing, not gameplay-reactive (no PIE hooks shipped). - Semantic actor/asset search — embed-based "find the boss spawner" lookup that beats regex-on-name (
find_actorstoday is literal-pattern only) - World-state JSON export (Toolbelt-style)
- Verse templates / scaffolding (next logical step after digest tooling — pre-canned device wirings)
- MCP resources & prompts (not just tools)
-
uvx uefn-mcpdistribution via PyPI - True WebSocket transport (rather than long-poll) — would require either a vendored
websocketslib in the listener or a hand-rolled stdlib WS implementation (~250 LoC for handshake + framing). Punted because long-poll already delivers the reactive UX; revisit if/when a use case actually needs persistent push (e.g. mirroring editor state to a web dashboard).
Credits
- Inspired by kirchuvakov/uefn-mcp-server and UEFN Toolbelt.
- Architecture mirrors my own rbxstudio-mcp.
- License: MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file uefn_mcp-0.1.6.tar.gz.
File metadata
- Download URL: uefn_mcp-0.1.6.tar.gz
- Upload date:
- Size: 67.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dfb0357ff5bdfc64eb6628cfba6698cf06de7b9012ae2725e4d88cfd1b5acd0b
|
|
| MD5 |
df2a86210da064721e4ada3b8341b7a4
|
|
| BLAKE2b-256 |
160f7a6a21c7e39d9d41fecaa38a9662506adf5c41c98a61cb6fd7c3ee7e0efb
|
Provenance
The following attestation bundles were made for uefn_mcp-0.1.6.tar.gz:
Publisher:
publish.yml on missatjuhvdk1/uefn-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
uefn_mcp-0.1.6.tar.gz -
Subject digest:
dfb0357ff5bdfc64eb6628cfba6698cf06de7b9012ae2725e4d88cfd1b5acd0b - Sigstore transparency entry: 1417559760
- Sigstore integration time:
-
Permalink:
missatjuhvdk1/uefn-mcp@c4cb40d0f316da72f65b8cc5e7e97838c5ae7a77 -
Branch / Tag:
refs/tags/v0.1.6 - Owner: https://github.com/missatjuhvdk1
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c4cb40d0f316da72f65b8cc5e7e97838c5ae7a77 -
Trigger Event:
push
-
Statement type:
File details
Details for the file uefn_mcp-0.1.6-py3-none-any.whl.
File metadata
- Download URL: uefn_mcp-0.1.6-py3-none-any.whl
- Upload date:
- Size: 55.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
771ea828e7746eab4a3d12ee67ab96935098d4807d0e848237ce89e1bdb7d099
|
|
| MD5 |
2f7a9bbc5b34890e746f232b8ed24f9e
|
|
| BLAKE2b-256 |
a12ead9fed95552e8e895dca7d607422ff42336179a7ef5d23df0cee1a3fdf12
|
Provenance
The following attestation bundles were made for uefn_mcp-0.1.6-py3-none-any.whl:
Publisher:
publish.yml on missatjuhvdk1/uefn-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
uefn_mcp-0.1.6-py3-none-any.whl -
Subject digest:
771ea828e7746eab4a3d12ee67ab96935098d4807d0e848237ce89e1bdb7d099 - Sigstore transparency entry: 1417559771
- Sigstore integration time:
-
Permalink:
missatjuhvdk1/uefn-mcp@c4cb40d0f316da72f65b8cc5e7e97838c5ae7a77 -
Branch / Tag:
refs/tags/v0.1.6 - Owner: https://github.com/missatjuhvdk1
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c4cb40d0f316da72f65b8cc5e7e97838c5ae7a77 -
Trigger Event:
push
-
Statement type: