Vibeue logo

Vibeue

Community
kevinpbuckley
vibeue

Unreal Engine 5 development using the VibeUE Python API. Use when working in Unreal Engine — blueprints, state trees, materials, actors, landscapes, animation, niagara, widgets, sound, foliage, gameplay tags, enhanced input, skeletons, PCG (procedural content generation), and more. VibeUE is an extension of Unreal's native MCP endpoint.

Overview

Publisherkevinpbuckley
RepositoryVibeUE
Skill namevibeue
Stars
735
Forks
161
Bundled files
Instructions only
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by kevinpbuckley on GitHub. Read the source before you install it.

Installation

Install the Vibeue AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/kevinpbuckley/VibeUE/tree/master/Content/Skills/vibeue
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/kevinpbuckley/VibeUE.git /tmp/VibeUE
mkdir -p .claude/skills
cp -r /tmp/VibeUE/Content/Skills/vibeue .claude/skills/vibeue
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Vibeue in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Vibeue on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Vibeue is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

VibeUE is an extension on Unreal Engine's native MCP endpoint (http://localhost:8000/mcp). There is no separate VibeUE server, no API key, and no in-editor chat — VibeUE simply registers extra Python services (unreal.<Service>) and skill packs on top of the engine's own toolsets.

Wait for VibeUE readiness after launch

BuildAndLaunchGame.ps1 / .sh print Editor-PID=<pid> — treat that as the process identity. Check once, then watch the filesystem for <ProjectDir>/Saved/VibeUE/Signals/editor-<pid>-true.json before using MCP. Wait at most 180 seconds, do not poll MCP while waiting, and fail if that Editor process exits or the timeout expires. Ignore signal files for other or dead PIDs. The signal only means RegisterToolsets() reached its end; Python, World, and level readiness remain separate checks.

The file is JSON, written atomically, so it is complete the moment it appears:

json
{"signal":"toolsets-registered","pid":21044,"createdUtc":"2026-08-03T17:04:11.921Z",
 "sessionStartUtc":"2026-08-03T17:03:22.108Z","pluginVersion":"3.0",
 "currentMap":"/Game/Maps/Level1_FullBody","mcpPort":8000,"mcpListening":true}

Process IDs get recycled. The launch scripts clear a matching stale signal right after starting the Editor, but if you launch it some other way, verify sessionStartUtc is later than the moment you started the process before trusting the signal.

currentMap is the loaded map's package name and the signal is re-published on every map open (issue #554) — gate world-edit scripts on it. A relaunch opens the project default map unless you pass -Map /Game/Maps/YourMap (--map on the .sh) to the launch script; editing "the current world" after a relaunch without checking has silently modified the wrong level before.

Health heartbeat — dead or wedged editor detection

Signals/editor-<pid>-health.json is rewritten every ~5s by a background thread (issue #555):

json
{"signal":"health","pid":21044,"updatedUtc":"2026-08-03T17:09:00.000Z",
 "sessionStartUtc":"2026-08-03T17:03:22.108Z","gameThreadStallSeconds":0.03,
 "mcpPort":8000,"mcpListening":true}

Epic's MCP endpoint runs on the game thread with no request timeout, so a dead or wedged editor hangs MCP calls for the client's full timeout — and even JSON-RPC ping hangs with it. Read the health file instead: file missing or updatedUtc older than ~15s → the process is gone (relaunch); gameThreadStallSeconds above ~10 → alive but wedged (modal dialog / crash handler; MCP will hang — relaunch); fresh and small → the editor is healthy, debug something else.

Both the readiness and health JSON also carry mcpPort and mcpListening (issue B6). Check mcpListening before assuming a live link. The very first readiness signal is written the instant RegisterToolsets() ends, which is ~10 ms before Epic's MCP module finishes binding its HTTP listener, so mcpListening is normally false for a fraction of a second at startup and then flips to true. VibeUE republishes the readiness signal the moment the listener reports running, so a brief false-then-true is expected and healthy — poll the file (or the health heartbeat) rather than trusting the first read. If mcpListening stays false for more than the startup grace window (~15 s), this editor's MCP module has no running HTTP server and every MCP call to it will fail to connect; VibeUE logs one LogVibeUEMcp: Error line at that point saying whether the port is FREE (this editor's server failed to start / never started) or held by ANOTHER process (it lost the port fight). The usual cause of the latter is the port-8000 fight: a headless UnrealEditor-Cmd and the GUI editor started together, one lost the bind, and the loser looks healthy while owning no MCP. Restart the loser (never run a headless editor while the GUI editor is starting; also grep for Epic's LogHttpListener ... unable to bind to 127.0.0.1:<port>). Note that a bind-probe issued from inside the process cannot tell whether this editor or another holds the port, so mcpListening=true means only that this process's module started a server, not that it won the port — and VibeUE never forces mcpListening true from a probe result, it only ever reflects the module's own claim.

Persisted Python results -- a timed-out call is not a failed call

The MCP client aborts an execute_python_code call after its own timeout (~30s for most clients, 300s for some), but the script keeps running in the editor. Read the persisted result before re-running -- re-running double-executes a mutation. Every run's outcome is written to Signals/python-<pid>-last.json (always the latest) and appended to Signals/python-<pid>-runs.jsonl (last ~200 runs / ~2 MB):

A run that COMPLETES but overruns the server-side timeout is no longer reported as an error: the reply comes back success:true with timed_out:true, its run_id, and signal_file_path (the python-<pid>-last.json path), so a client whose own budget is longer -- or a retry -- gets the real output instead of a bare PYTHON_EXECUTION_TIMEOUT. Only a call the client actually abandoned (it gave up while the script was still running) returns nothing to you; that is the case last_python_result() below is for. Error replies now also append run_id=<N> and the signal path to the message for the same recovery.

json
{"runId":7,"pid":21044,"success":true,"label":"#7 execute_python_code","output":"...",
 "error":"","result":"","execution_time_ms":48210.5,"startedUtc":"...","finishedUtc":"..."}

The record deliberately does not store the submitted Python source: snippets commonly contain credentials passed to SDKs, and timeout recovery must not create a plaintext source-code secret log.

Recovery after a timeout -- the aborted call kept running in THIS same editor process, so the next call just reads the file:

python
import vibeue
print(vibeue.last_python_result())      # dict of the most recent run, or None
print(vibeue.python_run(7))             # a specific run by its runId, from the JSONL history

last_python_result() sees the lost run because the read happens before this recovery call's own result is persisted. A SECOND read reflects the recovery read itself -- grab the runId from the first read and use python_run(run_id) for a stable handle. Successful MCP replies also carry run_id so you can correlate a reply that did return with its record.

Skill packs (this file and its siblings) are loaded through the engine's AgentSkillToolset. Each skill carries exact API patterns and gotchas; load the relevant skill before writing any code in a domain, or you will guess wrong property names and spiral into discovery loops.

Discover and load skills

Skills are discovered and read through the engine AgentSkillToolset, invoked with call_tool:

# List every available skill (full path → description)
call_tool(toolset_name="ToolsetRegistry.AgentSkillToolset", tool_name="ListSkills", arguments={})

# Read one or more skills — GetSkills takes the FULL paths that ListSkills returns
call_tool(toolset_name="ToolsetRegistry.AgentSkillToolset", tool_name="GetSkills",
          arguments={"skillPaths": ["/VibeUE/Python/init_unreal_PY.VibeUE_pcg",
                                    "/VibeUE/Python/init_unreal_PY.VibeUE_materials"]})

Naming rule (verified live): skill pack <slug> registers as VibeUE_<slug> (hyphens → underscores) and sub-doc <slug>/<file>.md as VibeUE_<slug>__<file>, all under the path prefix /VibeUE/Python/init_unreal_PY.. Short names such as "pcg" or "state-trees/api-reference" return an empty result with no error — always pass full paths, taking them from ListSkills when unsure. Run describe_toolset on ToolsetRegistry.AgentSkillToolset if the call signature differs in your build. The old vibeue-skills-manager tool no longer exists.

Route by functional area. Find the area whose scope matches the task, then GetSkills the listed skill(s) — full paths per the naming rule above. The NOT for column is the disambiguator — when two areas seem to fit, the one that excludes your task is telling you where to go instead.

Functional areaUse for — NOT for…Load skill(s)
Scene & actorsplace / move / arrange / organize / tag actors in a level — NOT gameplay logic (→ Blueprints), NOT world-scale terrain/foliage (→ Environment), NOT attaching a Niagara/particle component (→ VFX)level-actors
Blueprints & gameplay logicauthor Blueprint classes & graphs, Enhanced Input, gameplay tags, Gameplay Ability System (abilities/attributes/effects/cues) — NOT AI behavior (→ AI), NOT AnimBP graphs (→ Animation), NOT C++/source (coding-agent handoff)blueprints, blueprint-graphs, enhanced-input, gameplay-tags, gas
AIauthor StateTree or Behavior Tree logic, Blackboards, states, tasks, transitions, event payloads, and delegate bindings — NOT character body animation (→ Animation), NOT generic actor placement (→ Scene)state-trees, behavior-trees
Animation & riggingAnimBP state machines, AnimSequence keyframes, montages & AnimNotify wiring, bone/skeleton editing & retarget — NOT cinematic timelines (Epic Sequencer), NOT AI movement (→ AI), NOT authoring/adding sound assets — even a character's footstep sounds (→ Audio)animation-blueprint, animsequence, animation-editing, animation-montage, skeleton
Character customization (Mutable)Customizable Object graphs: modular character parts, mesh sections, skin/clothing parameters, switches, hat/outfit groups and child objects, compiling a CO — NOT plain skeletal mesh or skeleton edits (→ Animation), NOT ordinary material graphs (→ Materials)customizable-object
Materials & shadingmaterials, instances, graph nodes, Custom HLSL — NOT Niagara particle materials (→ VFX), NOT landscape auto-materials (→ Environment)materials
VFX (Niagara)particle systems, emitters, scratch-pad HLSL, attaching/placing a Niagara component on an actor — NOT surface materials (→ Materials)niagara-systems, niagara-emitters
UI (UMG)widget blueprints, layout, fonts/brushes, MVVM — NOT the gameplay behind the UI (→ Blueprints)umg-widgets
Environment (world-scale)landscape sculpt/paint, landscape materials, foliage, PCG, map blockout, real-world terrain — NOT single-actor placement (→ Scene), NOT sound/audio (→ Audio)landscape, landscape-materials, landscape-auto-material, foliage, pcg, map-blockout, terrain-data
AudioMetaSound and SoundCue authoring — creating the sound asset itself: ambient, a character's footstep/foley, UI sounds — NOT triggering sounds from gameplay logic (→ Blueprints), NOT wiring an existing sound to anim-notify foot-plant frames (→ Animation)metasounds, sound-cues
Assets, data & projectimport/export assets, Fab catalog acquisition, UV mapping, enums/structs, engine & project settings, and bounded bulk asset maintenance/migrations — NOT actors placed in a level (→ Scene)asset-management, fab, bulk-maintenance, uv-mapping, enum-struct, engine-settings, project-settings
Diagnostics, testing & runstart/stop/query PIE, profile (CPU-vs-GPU / Insights), uncap frame rate — NOT fixing the logic a bug points to (→ its authoring area)pie-testing, profiling, frame-rate
Camera & viewportviewport camera, view mode, FOV, exposure, layout — NOT material look (→ Materials), NOT placing/editing light actors (→ Scene)viewport
Cinematics · PhysicsSequencer cinematics and Physics assets (ragdoll/skeletal) are Epic-native — VibeUE adds no skill here — NOT enabling simulate-physics on a level actor (→ Scene)(none — use list_toolsets: animation_toolset.* / PhysicsToolsets)

A loaded skill gives you:

  • workflows, gotchas, and property formats for the domain
  • vibeue_classes / unreal_classes — class names to feed into discover_python_class for live method signatures
  • sub-doc references (<skill>/<section>) you can fetch via GetSkills for deeper detail

Always call discover_python_class on the classes in vibeue_classes before writing code — never guess method names from the skill content alone. Batch the discovery into ONE call instead of one call per class:

# ONE call covers all classes and all topics:
discover_python_class(
    class_name="unreal.MaterialService, unreal.WidgetService, unreal.MaterialNodeService",
    method_filter="create|delete|compile|property|color")

# WRONG — three separate calls for three classes wastes round-trips and repeats boilerplate

class_name accepts a comma-separated list (response gains a classes array, one entry per class); method_filter ORs keywords with |.

How work gets done — execute_python_code is the workhorse

VibeUE services are plain Python on the editor's unreal module. Run everything through execute_python_code:

python
import unreal
widgets = unreal.WidgetService.list_widget_blueprints()
unreal.StateTreeService.create_state_tree("/Game/AI/MyBehavior")

You get the full unreal.* API plus every unreal.<Service> VibeUE adds. Reserve call_tool for engine toolsets and skills (e.g. AgentSkillToolset, EditorToolset.EditorAppToolset, LogsToolset, GameplayTagsToolset, AssetTools).

code is Python source, never a script path. A path such as C:/scripts/build.py passed as code is compiled as Python and fails (SyntaxError / NameError); it is not run as a file. (Code that merely mentions a .py file in a comment or string runs normally.) To run a script file, pass code that runs it:

python
import unreal, runpy
runpy.run_path(r"C:/scripts/build.py")        # the script gets its own globals
# or, sharing the console globals every execute_python_code call sees:
exec(open(r"C:/scripts/build.py").read())

auto_save (default true). Before running your script, execute_python_code saves every dirty content AND world package headlessly (issue #433: this avoids the modal save dialog that would hang the call). Every reply reports what actually happened: auto_save (true only when the sweep really ran), auto_save_note (empty when it ran, otherwise opted_out, previous_run_crashed, editor_unavailable, pie_active, or save_failed) and saved_packages (the package names it wrote). auto_save is the OUTCOME, not an echo of your argument -- so auto_save: true, saved_packages: [] means "swept, nothing was dirty", never "skipped". Pass auto_save=false to run the script WITHOUT that sweep -- use it when you do not want in-flight editor edits flushed to disk, or to keep a mutation you are about to make from being interleaved with an unrelated dirty package. The sweep is skipped anyway after a crashed run, when GEditor is missing, or in PIE.

Never leave a map loaded - it crashes the editor on the next level change

Opening a map as an asset keeps it resident:

python
w = unreal.load_asset("/Game/Maps/Foo")          # loads and KEEPS Foo
unreal.EditorAssetLibrary.load_asset("/Game/Maps/Foo")
unreal.find_object(None, "/Game/Maps/Foo.Foo")

The engine checks on every level load that no other map package is still alive. That check is a fatal, not a warning - the editor dies with World Memory Leaks (EditorServer.cpp) and Old level package /Game/Maps/Foo not cleaned up by garbage collection. The crash lands on whoever calls load_level next, which may be minutes later and a different tool entirely, and the message names neither the script nor the map that caused it.

execute_python_code reports this in every reply so the warning arrives with its cause:

json
"resident_maps": ["/Game/Maps/Foo.Foo"]

Non-empty resident_maps means the next level load will crash the editor.

There is no reliable in-process cure. A map package loads with RF_Standalone, and neither unreal.SystemLibrary.collect_garbage() nor EditorLoadingAndSavingUtils.unload_packages() releases it - both were tried against a live editor and the world stayed resident. Once a map is stranded, restart the editor (BuildAndLaunchGame.ps1) before changing levels.

So treat this as prevention, not repair:

  • To read a map's metadata, use the asset registry - it loads nothing:
    python
    ar = unreal.AssetRegistryHelpers.get_asset_registry()
    maps = ar.get_assets_by_path("/Game/Maps", recursive=True)
  • To change level, use unreal.get_editor_subsystem(unreal.LevelEditorSubsystem).load_level(path), which swaps the open world properly. Never load_asset a map to "look at it".

The open level is never reported - only stragglers.

Tools — what each is for

ToolUse it for
execute_python_codeRun unreal.* Python in the editor — the workhorse for every VibeUE service
call_toolInvoke engine toolsets and skills (skills, PIE control, logs, assets, gameplay tags)
describe_toolset / list_toolsetsDiscover engine toolsets and their actions/args
discover_python_class / discover_python_function / discover_python_moduleGet live signatures before writing code
list_python_subsystemsEnumerate editor subsystems for unreal.get_editor_subsystem(...)
terrain_dataReal-world heightmaps + water splines (see terrain-data skill)
deep_researchWeb research / page fetch / geocoding. If Jina Reader refuses your network (HTTP 401), set a free Jina key in the JINA_API_KEY environment variable and restart the editor

deep_research and terrain_data run off the game thread, so the editor keeps working while they wait. Four run at a time and more wait their turn; past 16 running or waiting, a call is refused with BUSY (retry when one finishes). Cancelling one from the client (notifications/cancelled) matches it by JSON-RPC request id only: with several MCP clients connected, a cancel can also end another client's call that carries the same id.

Engine toolsets replace the old VibeUE tools

Several capabilities that used to be VibeUE-specific MCP tools are now the engine's native toolsets, called via call_tool (run describe_toolset for action names/params):

NeedEngine toolset (via call_tool)
Start / stop / query PIEEditorToolset.EditorAppToolset → StartPIE / StopPIE / IsPIERunning
Capture a viewport screenshotEditorToolset.EditorAppToolset → CaptureViewport
List / read / filter / tail UE logsLogsToolset
Search / open / save / move / import assetsAssetTools
Single-tag gameplay-tag CRUDGameplayTagsToolset (see gameplay-tags skill)
Inspect a live Ability System (attributes/tags/effects/abilities), attribute-set discovery, gameplay cuesGASToolsets.* (see gas skill)

Performance/Insights tracing is the one net-new VibeUE service — unreal.PerformanceService.* (see the profiling skill) — because Unreal 5.8 ships no performance toolset.

Additional gotchas

  • A Python traceback still proves the MCP link is up — only "Unable to connect" means it is down. A dropped server or a newly added tool needs a Claude Code restart. A stuck call is usually a modal dialog and the client timeout does not stop the script, so persisted results (vibeue.last_python_result()) let you read the outcome on the next call rather than re-running.
  • Don't run a headless UnrealEditor-Cmd while the GUI editor is starting — they fight over the MCP port and the loser runs with no MCP; the readiness signal's mcpListening flag tells you when the port is claimed. -run=pythonscript fully substitutes for pure-asset work when no editor runs, but not for World Partition world surgery.
  • Engine toolset quirks: LogsToolset.GetLogEntries requires a pattern; StartPIE via execute_tool needs the full options object; results can be double-encoded (vibeue.exec_tool decodes them). A genuinely async tool like CaptureAssetImage never completes inside one call — fire it with vibeue.exec_tool_async and read it with vibeue.collect_tool_result on the next call.
  • A leaked register_slate_post_tick_callback can survive its own unregister; an editor restart is the only reliable purge.

Frequently asked questions

What does the Vibeue AI skill do?

Unreal Engine 5 development using the VibeUE Python API. Use when working in Unreal Engine — blueprints, state trees, materials, actors, landscapes, animation, niagara, widgets, sound, foliage, gameplay tags, enhanced input, skeletons, PCG (procedural content generation), and more. VibeUE is an extension of Unreal's native MCP endpoint.

Why use Vibeue on TypingMind?

Because you install it once and use it with any model. Vibeue is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Vibeue in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/kevinpbuckley/VibeUE/tree/master/Content/Skills/vibeue. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Vibeue?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Vibeue?

As many as you like. As long as a model supports skills, you can use Vibeue with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Vibeue AI skill free?

Yes. It is published on GitHub by kevinpbuckley under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇