LLM Easy Shell
TCP shell interface for driving the Unreal Editor from any external client (LLM agent loop, CLI, test harness). One-line commands in, JSON out — no UE editor knowledge required.
Quick value: 37 editor commands · 46 built-in Python abilities · TCP server on ports 15151–15200 · any plugin can host its own abilities · works alongside any LLM agent framework.
🎯 For a minimal-dependency, read-only variant, see LLM Easy Shell Lite (9 commands, 2 engine plugin dependencies, free).
Core Features
- Scene read/write — List, view, spawn, delete, move, rename actors and components
- Property reflection —
set/catover any UObject, struct, or asset property - Asset operations — Browse, create, copy, delete, save assets via
/Assets/...paths - Function dispatch —
callany reflectedUFUNCTIONon actor or component - PIE control —
play,stop,pause,gamestate,select,focus - Live Coding —
livecoding/lc/reload,--restarteditor with crash detection - Logs — Editor log, error log, compile log, Niagara validation messages
- UI introspection —
uitree/uifind/uiinfo/uipress/uitype/uishot - Ability framework — Plugins register C++ commands via
RegisterShellCommand()(auto-discovered) - PyAbility framework — Drop a
.pyinto any plugin'sContent/Python/<Package>/abilities/to register a command - TCP server — Plain text in / JSON out, ports 15151–15200
- Multi-platform — Win64, Mac, Linux (Live Coding subcommand: Windows only)
Quick Preview
# Agent loop — one line per action
> info
{"ok":true,"project":"MyGame","engine":"5.7.4","pie":"stopped","port":15151}
> spawn /Game/Blueprints/BP_Enemy at 100,200,50
{"ok":true,"actor":"/Actor/BP_Enemy_C_42"}
> set /Actor/BP_Enemy_C_42.Health=200
{"ok":true}
> call /Actor/BP_Enemy_C_42.SetActive true
{"ok":true,"result":"activated"}
> uishot
{"ok":true,"file":"Saved/Screenshots/2026-06-15.png"}Send a one-line command → Get a one-line JSON response. The agent loop never stalls.
Technical Information
Features: 37 editor commands (scene read/write, asset read/write, property/function discovery, PIE control, Live Coding, logs, UI introspection, screenshots), TCP server on ports 15151–15200, Ability framework for plugin-registered C++ commands (RegisterShellCommand() / AddPythonAbilityRoot() extension points), PyAbility framework for .py-defined commands hosted by any plugin, 46 built-in Python abilities, command trace logging, and a live Trace Tab window (v1.3.0).
Code Modules: LLMEasyShell (Editor)
Number of Blueprints: 0
Number of C++ Classes: ~115
Network Replicated: No
Supported Development Platforms: Windows: Yes, Mac: Yes, Linux: Yes
Supported Target Build Platforms: Windows, Mac, Linux
Documentation Link: llm-easy-shell skill & reference docs
Example Project: (the public repo ships the skill folder — reference docs and the bundled Python ability set)
Important/Additional Notes: Editor-only plugin. For a minimal-dependency, read-only variant, see LLM Easy Shell Lite. UE 5.5–5.8.
Build Environment
Required Host Plugins
The following engine plugins must be enabled in the host project before compilation. They are declared in LLMEasyShell.uplugin and the host .uproject must enable each one:
- PythonScriptPlugin — backs the PyAbility framework and Python-driven abilities (
python <name> ...) - GameplayAbilities — required by built-in abilities that introspect the Gameplay Ability System
- GameplayTagsEditor — editor-side gameplay tag inspection
- EnhancedInput — input mapping queries via
discover/cat - PoseSearch — Motion Matching database introspection
- Chooser — Chooser table introspection
- StructUtils — runtime struct property reflection
- Niagara — VFX introspection + validation message capture
- EditorScriptingUtilities — asset registry and editor scripting helpers
Editor-build dependency (host must enable in .uproject):
- WebBrowserWidget — required at link time for the CEF-state (
url/title/page_loaded) block ofui info/uishot. Ships withEnabledByDefault: false; without it the editor target fails to link. - ModelContextProtocol (UE 5.8 hosts only) — backs the
mcpshell command. Experimental,EnabledByDefault: false. On UE 5.5–5.7 the plugin builds without it andmcpreturns a not-available hint.
Verifying a Clean Build
cd "/c/Program Files/Epic Games/UE_5.7/Engine/Build/BatchFiles"
./RunUAT.bat BuildPlugin \
-plugin="D:/path/to/LLMEasyShell.uplugin" \
-package="D:/Temp/LLMEasyShell" \
-platform=Win64 \
-configuration=Development \
-rocketThe -rocket flag enables Epic's IWYU + ABI-strict build configuration, matching the Fab submission environment. Confirm zero errors and zero consequential warnings (deprecation warnings such as C4996 are treated as errors by marketplace scanners).
UE Version Compatibility
- Minimum: UE 5.5
- Tested: UE 5.5, 5.6, 5.7, 5.8 (each Fab Project Version ships an engine-matched package)
Command Reference (37 commands)
Scene
| Command | Description |
|---|---|
ls /Level/Actors | List Actors in current level |
cat /Actor/{name} | View Actor / component / asset details |
spawn /Game/.../BP_Actor | Spawn actor (class path) |
rm /Actor/{name} | Delete actor |
mv /Actor/{name} 100,200,50 | Move (Location only) |
cp /Actor/{src} /Actor/{dst} | Clone actor |
Property / Function
| Command | Description |
|---|---|
set /Actor/{name}.Property=Value | Set any reflected property |
call /Actor/{name}.Function arg1 | Call any UFUNCTION |
addcomp /Actor/{name} TypeName | Add component |
discover /Actor/{name} --funcs | List available functions |
discover /Actor/{name} --props | List properties |
Assets
| Command | Description |
|---|---|
ls /Assets/ | Browse asset registry |
cat /Assets/.../Asset.Asset | View asset properties |
new /Assets/.../Asset.Asset Class | Create new asset |
cp /Assets/A.A /Assets/B.B | Duplicate asset |
mv /Assets/A.A /Assets/B.B | Rename / move asset |
rm /Assets/.../Asset.Asset | Delete asset |
save /Assets/.../Asset.Asset | Save modified asset |
mkdir /Assets/Folder | Create asset folder |
Search / Info
| Command | Description |
|---|---|
find Actor {pattern} | Search Actors by name |
find Asset {pattern} | Search assets by name |
find /Game {pattern} | Search assets by path |
info | Project + editor + PIE state |
gamestate | PIE game state summary (reflection-based player / wave stats) |
help | List all commands |
Editor Control
| Command | Description |
|---|---|
play | Start PIE |
stop | Stop PIE |
pause | Pause / resume PIE |
select /Actor/{name} | Select Actor in viewport |
focus /Actor/{name} | Focus camera on Actor |
undo / redo | Editor undo / redo |
save | Save the level |
Live Coding / Restart
| Command | Description |
|---|---|
livecoding / lc / reload | Trigger Live Coding compile |
log --compile | Last Live Coding compile log |
log --restart | Restart editor with crash detection |
Logs
| Command | Description |
|---|---|
log --errors | Editor error log |
msglog --niagara | Niagara validation messages |
livelog | Tail current editor log |
UI Introspection
| Command | Description |
|---|---|
uitree | Dump current UMG widget tree |
uifind {pattern} | Find UMG widget by name |
uiinfo /Widget/{name} | Inspect widget properties |
uipress /Widget/{name} | Simulate click on a button (--root <class> picks the resolution root, v1.3.0) |
uitype /Widget/{name} "text" | Simulate text input |
uishot | Slate composite screenshot |
Python Abilities
| Command | Description |
|---|---|
python {name} {args...} | Invoke any PyAbility |
python reload | Rescan abilities/ and clear Python module cache |
Python Abilities (46 built-in)
LLMEasyShell ships with a reference set of Python abilities in Content/Python/LLMShellAbilities/abilities/. Each ability is a standalone .py module that registers one or more python <name> subcommands.
Drop your own .py into the abilities/ folder, restart the editor (or run python reload), and the ability becomes invocable as python <name>. Any other plugin can host its own ability package the same way — <Plugin>/Content/Python/<Package>/abilities/*.py is discovered automatically; plugin hosts take precedence over the project script directory (ordered by plugin name).
| Category | Abilities |
|---|---|
| Asset / blueprint | asset, blueprint, bpcomp, bpprop, spawn_mesh, mesh_check |
| Property / reflection | property, inspect_subobject_api, dump_rip |
| Gameplay / player | gameplay, get_gamestate, walk_to, set_view_target, unpause |
| Animation / pose | anim, poseseach |
| Tables / choosers | datatable, chooser |
| Levels / scene / editor | level, level_load, editor, editorctl, editor_camera |
| Materials / VFX | create_unlit_mat, vfx, vfxsim, bind_niagara_asset, open_niagara_editor, diag_niagara_actor |
| UI | umg |
| Navigation | navigation |
| Performance | perf |
| Test automation | run_automation, automation_check_test, automation_dump_api, automation_list_tests, automation_run_and_wait, automation_sanity_check |
| Diagnostics | debug_burst, diag_pie, diag_subsys, pcg_diag, voxel_diag, quixel_table, hd3dscene, list_hd3d_methods |
Authoring a New Python Ability
# <Plugin>/Content/Python/<Package>/abilities/my_ability.py
import unreal
from LLMShellAbilities.registry import Ability, Command
# (inside this plugin's own package, `from ..registry import ...` also works)
class MyAbility(Ability):
name = "my_ability"
version = "1.0.0"
def get_commands(self):
return [
Command("my_command", "Does something useful", []),
]
def execute(self, command, args, context):
if command == "my_command":
return {"success": True, "output": "..."}
return {"success": False, "error": "unknown"}
def register(reg):
reg.register_ability(MyAbility())Restart the editor or run python reload — the loader scans every enabled plugin for abilities/*.py and calls the module-level register(reg).
TCP Protocol
The TCP server speaks a minimal text protocol.
Request
Plain ASCII command terminated by \n:
info
ls /Level/Actors
set /Actor/MyActor.Location=100,200,50
python bpprop set BP_Hero.AttackRange=1200Response
Each command returns a single-line JSON object terminated by \n:
| Key | Type | Meaning |
|---|---|---|
ok | bool | Success / failure |
result | any | Command-specific payload |
error | string | Human-readable error message (only if ok=false) |
trace | string | Optional command trace id for debugging |
{"ok": true, "result": {"actor_count": 12, "level": "Lvl_Main"}}{"ok": false, "error": "Actor /Actor/MyActor not found"}Port Allocation
The listener binds to the first free port in 15151–15200. The chosen port is logged at editor startup and recorded in Saved/LLMEasyShell/port.txt.
How to Use
- Install the plugin to your project's
Plugins/folder. - Enable the 9 engine plugins listed under Required Host Plugins in your project's Plugins browser.
- Restart the editor. LLMEasyShell starts a TCP listener on the first free port in
15151–15200. - Connect to the listener from any TCP client to issue commands:
# Plain TCP — send "info\n" -> receive JSON # Or wire it up to an LLM agent that POSTs commands and parses JSON results - To author new C++ commands, derive from
ULLMEasyShellAbilityCommandObjectand register via the ability registry (auto-discovered at startup). - To author new Python commands, drop a
.pyintoContent/Python/LLMShellAbilities/abilities/(or your own plugin's ability package) and runpython reload.
Trace Tab
Every command the shell executes is traced to JSONL under Saved/LLMEasyShell/traces/. Since v1.3.0 you can also watch them live in the editor:
- LLM Shell Trace tab — a live list of executed commands with a detail pane (Input / Output / Error) for the selected row, a filter box, a failures-only toggle, auto-scroll, and Clear.
- Opening it — click the port status widget on the Play toolbar (the copy-port action moved to a Copy port button in the tab header). The tab also auto-opens once per editor session when the first traced command arrives; disable with
LLMEasyShell.TraceAutoOpen 0. Closing it manually keeps it closed for the rest of the session. - C++ data plane — the subsystem keeps a 500-entry in-memory ring buffer (
GetRecentTraces(), display-truncated) and broadcastsFOnShellCommandTracedafter each command. The JSONL archive remains the full-fidelity record.
Comparison: Easy Shell vs Lite
LLMEasyShell ships in two independent variants. They listen on disjoint TCP port ranges and do not conflict at runtime. Install either or both.
| LLMEasyShell (this plugin) | LLMEasyShell Lite | |
|---|---|---|
| Purpose | Full read + write + automation | Read-only exploration |
| Commands | 37 (read + write + control) | 9 (query only) |
| Python abilities | 46 built-in abilities | Framework + 1 demo |
| Screenshots | Yes (PIE + Win32 paths) | No |
| Live Coding | Yes | No |
| TCP port | 15151–15200 | 15201–15250 |
| Engine plugin deps | 9 + WebBrowserWidget (+ ModelContextProtocol on UE 5.8) | 2 |
| CLI binary | llm-shell.exe | llmshelllite.exe |
FAQ
Q: Can I use LLMEasyShell on Mac or Linux? Yes — the editor module compiles on Win64, Mac, and Linux. The Live Coding subcommand is Windows-only (UE constraint).
Q: How is this different from LLMEasyShell Lite? Lite is a 9-command, read-only subset on ports 15201–15250 with only 2 engine plugin dependencies. Use Lite for safe exploration; use the full plugin for write automation.
Q: Can I register my own commands? Yes. C++ commands: RegisterShellCommand() on the shell subsystem (or derive from ULLMEasyShellAbilityCommandObject). Python commands: drop a .py into Content/Python/LLMShellAbilities/abilities/ — or host your own ability package in your plugin.
Q: Is this safe to leave running in production? No — this is an editor automation tool. The TCP server exposes full editor write capability. Disable it in shipping builds; never expose it to untrusted networks.
LLMEasyShell v1.3.0 | YominUnreal