Fmodel Unpack
pa001024/dna-builder
This skill documents how to use the fmodel-mcp toolkit (a CUE4Parse-based .NET CLI plus a thin Python MCP server) to inspect and export Unreal Engine game assets — pak files, textures, meshes…
Imports files into Unreal Engine, exports textures, reads the Content Browser selection and checks open assets via VibeUE, plus mesh reimport material fixes.
$ npx skills add kevinpbuckley/VibeUE --skill asset-management -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install kevinpbuckley/VibeUE asset-management --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/kevinpbuckley/VibeUE.git skills-src && mkdir -p .claude/skills && cp -r skills-src/Content/Skills/asset-management .claude/skills/asset-management && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "asset-management" agent skill from https://github.com/kevinpbuckley/VibeUE/tree/master/Content/Skills/asset-management into .claude/skills/asset-management/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "asset-management", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/kevinpbuckley/VibeUE/tree/master/Content/Skills/asset-managementType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add kevinpbuckley/VibeUE --skill asset-management -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install kevinpbuckley/VibeUE asset-management --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/kevinpbuckley/VibeUE.git skills-src && mkdir -p .agents/skills && cp -r skills-src/Content/Skills/asset-management .agents/skills/asset-management && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "asset-management" agent skill from https://github.com/kevinpbuckley/VibeUE/tree/master/Content/Skills/asset-management into .agents/skills/asset-management/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "asset-management", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add kevinpbuckley/VibeUE --skill asset-management -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install kevinpbuckley/VibeUE asset-management --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/kevinpbuckley/VibeUE.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/Content/Skills/asset-management .cursor/skills/asset-management && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "asset-management" agent skill from https://github.com/kevinpbuckley/VibeUE/tree/master/Content/Skills/asset-management into .cursor/skills/asset-management/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "asset-management", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/kevinpbuckley/VibeUE.git --path Content/Skills/asset-management--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add kevinpbuckley/VibeUE --skill asset-management -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install kevinpbuckley/VibeUE asset-management --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/kevinpbuckley/VibeUE.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/Content/Skills/asset-management .gemini/skills/asset-management && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "asset-management" agent skill from https://github.com/kevinpbuckley/VibeUE/tree/master/Content/Skills/asset-management into .gemini/skills/asset-management/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "asset-management", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install kevinpbuckley/VibeUE asset-managementInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add kevinpbuckley/VibeUE --skill asset-management -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/kevinpbuckley/VibeUE.git skills-src && mkdir -p .github/skills && cp -r skills-src/Content/Skills/asset-management .github/skills/asset-management && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "asset-management" agent skill from https://github.com/kevinpbuckley/VibeUE/tree/master/Content/Skills/asset-management into .github/skills/asset-management/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "asset-management", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add kevinpbuckley/VibeUE --skill asset-management -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install kevinpbuckley/VibeUE asset-management --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/kevinpbuckley/VibeUE.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/Content/Skills/asset-management .opencode/skills/asset-management && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "asset-management" agent skill from https://github.com/kevinpbuckley/VibeUE/tree/master/Content/Skills/asset-management into .opencode/skills/asset-management/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "asset-management", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
asset-managementImports files into Unreal Engine, exports textures, reads the Content Browser selection and checks open assets via VibeUE, plus mesh reimport material fixes.
After Unreal 5.8 consolidated general asset operations, the engine's native AssetTools toolset or EditorAssetLibrary handles searching, loading, saving, moving, renaming, duplicating and deleting assets, so this skill covers only the remaining AssetDiscoveryService calls: import_asset and import_texture for bringing in images, meshes or other importable files, export_texture, get_primary_content_browser_selection and is_asset_open. The old manage_asset MCP tool is gone.
A reference, references/mesh-reimport-materials.md, and the script scripts/mesh_material_slots.py handle static-mesh LOD reimport problems such as lost textures or gray default surfaces despite a correct component material, by inspecting and remapping material sections through the engine's Python API. The skill also notes that data assets and data tables have no dedicated service and are driven natively from Python, and it can call an external skills-manager tool for engine knowledge when one exists in the session.
Read from SKILL.md and the folder at commit dc051be. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Ships 2 files in scripts/ (Python), which the agent can run.
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Unreal Asset Import and Export loads about 6.5k tokens when it runs, and up to ~7k if it reads all its reference files. Until then it costs about 154 tokens; SKILL.md has 2,199 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); the scripts in this folder are not scanned.
The full file from kevinpbuckley/VibeUE at commit dc051be, republished under its MIT licence (© kevinpbuckley). 2,199 words, ~6,517 tokens.
.claude/skills/asset-management/SKILL.md (or your agent's skills folder). This skill also uses 3 other files; get the full folder from GitHub.🧠 Brains complement: IF an
unreal-engine-skills-managertool (external MCP) exists in this session, call it with{action: "load", skill: "asset-management"}for UE domain knowledge on this topic — correct APIs, architecture, best practices — and treat it as the rubric for any review / "best practices" question. If no such tool is available (e.g. running under Claude Code or Codex without that MCP), skip this line entirely and proceed with this skill alone — do NOT attempt the call.
For static-mesh LOD reimport, lost textures or gray/default surfaces despite a
correct component material, read mesh reimport material mappings.
The bundled scripts/mesh_material_slots.py exposes explicit section inspection
and remapping through the existing engine Python API; no native service rebuild.
🔀 Engine owns general asset ops now. In the Unreal 5.8 consolidation, searching, loading, saving, moving, renaming, duplicating, and deleting assets moved to Unreal's native
AssetToolstoolset (reach it viacall_tool; rundescribe_toolsetfor its actions) — or you can driveunreal.EditorAssetLibrary/unreal.AssetRegistryHelpersdirectly fromexecute_python_code. The oldmanage_assetMCP tool is GONE. VibeUE'sAssetDiscoveryServicewas trimmed to only the crash-safe delta the engine doesn't provide:
Kept on AssetDiscoveryServicePurpose import_asset(src, dest_folder, name)→(path, err)Import an image, mesh or any importable file (folder + name) import_texture(src, dest_asset_path)Same importer, full asset path export_texture(asset_path, file_path)Export a texture to disk get_primary_content_browser_selection()→AssetData or NonePrimary Content Browser selection is_asset_open(asset_path)→boolWhether an asset is open in an editor Everything else below (
search_assets,list_assets_in_path,move_asset,duplicate_asset,delete_asset,save_asset,get_open_assets,get_asset_referencers,find_asset_by_path,asset_exists, …) was removed fromAssetDiscoveryService— use the engineAssetToolstoolset orEditorAssetLibrary/ the Asset Registry.
There is no DataAssetService / DataTableService. Drive them natively:
import unreal
# --- Data Assets (UPrimaryDataAsset / UDataAsset subclasses) ---
# Create: engine DataAssetTools toolset via call_tool (describe_toolset for the action), OR a factory.
da = unreal.load_asset("/Game/Data/DA_Thing")
val = da.get_editor_property("MyField") # read
da.set_editor_property("MyField", 42) # write — works for any UPROPERTY
unreal.EditorAssetLibrary.save_loaded_asset(da)
# --- Data Tables ---
dt = unreal.load_asset("/Game/Data/DT_Items")
names = unreal.DataTableFunctionLibrary.get_data_table_row_names(dt) # row keys
# Read/write rows with the editor DataTable API:
# unreal.DataTableFunctionLibrary.evaluate_curve_table_row(...) / get_data_table_column_as_string(dt, "Col")
# Engine DataTableTools (call_tool) covers add/remove/get rows + import/export CSV/JSON.⚠️ Data Table gotchas (verified, #452): the row key ("Name") is reported as a column by some schema readers — it is the row id, not a data field. Writing a row with the wrong value type can be silently coerced — read the row back to confirm. There is no
clear_rows; remove rows individually (engineDataTableTools) or re-import an empty CSV. An emptyGameplayTagfield serializes as"None".
delete_asset on a REFERENCED asset opens a modal dialog that wedges an unattended editorEditorAssetLibrary.delete_asset asks "this asset is referenced, force delete?" through a modal
window when anything points at the asset (a data asset holding the montage you are replacing, a
Blueprint default, a level). Nobody answers it from MCP: the game thread stalls (308 s observed),
every later call hangs, and the process has to be force-killed with the files removed from disk.
Use the plugin's dialog-free delete instead:
# Refuses when referenced and tells you who:
result = unreal.AssetDiscoveryService.delete_asset_unattended("/Game/Anim/AM_Old", False)
# Delete anyway and null every reference (what the dialog's Force Delete does):
result = unreal.AssetDiscoveryService.delete_asset_unattended("/Game/Anim/AM_Old", True)
if not result.b_success:
print(result.error_message) # why it did not happen
print(result.referencers) # what pointed at the assetThe call returns an FUnattendedDeleteResult struct — result.b_success, result.referencers,
result.error_message — so the reason ALWAYS survives, even on a refusal. (It used to return a
bool with out-params, and Python maps a False return to None, which silently dropped the reason;
that is fixed.) result.referencers is filled on a refusal AND on a forced delete, so you always see
what pointed at the asset. Prefer creating the replacement under a NEW name and repointing references
over force-deleting.
Even delete_asset_unattended(path, True) refuses a natively rooted in-memory object — it never
prompts. Before deleting, the call does a conservative, non-mutating check: it looks at who holds the
asset and refuses ONLY when a native GC root holds it directly — a UGCObjectReferencer, which in an
agent session is a Python module-level global that created or loaded the asset earlier this session.
That is the one case the engine's own force delete cannot clear, so it would stall on the modal "is in
use" dialog (6+ minutes observed). When it refuses, the call returns b_success == False with those
roots in result.referencers and an result.error_message explaining it. The fix is what the error
says: release the Python globals holding it — del my_var, then unreal.SystemLibrary.collect_garbage()
— and retry. Every OTHER in-memory referencer is left to the engine's real force delete, which clears it
without prompting: on-disk asset references, the Blueprint-palette node spawners, and transient editor
helpers like an anim data controller all delete fine. Read-only package files are also refused before
the engine delete path, because Unreal can show a read-only-package prompt even when confirmation is
disabled. Clear the filesystem/source-control read-only state explicitly, then retry.
The check never mutates state (it does not null any references before deciding), so a refusal leaves the asset and everything around it exactly as they were — safe to retry after releasing the global.
delete_asset a Blueprint you loaded or compiled this sessionEditorAssetLibrary.delete_asset on a Blueprint (Anim Blueprints especially) that is still loaded —
which it is, if you created, compiled or read it earlier in the same script — fails inside
ForceDeleteObjects and leaves the package half-deleted and corrupt:
Ensure condition failed: false [ObjectTools.cpp:4045]
Failed to unload all packages during ForceDeleteObjects - these packages are likely corrupt.
Consider restarting the editor, noting which assets remain and then deleting them from the
file system manually: /Game/Path/ABP_ThingEvery later call against that path then times out, and the editor typically has to be killed. The
recovery is exactly what the message says — close the editor, delete the .uasset from disk,
relaunch (watch for a ZenServer stall on the way back up) — so it costs several minutes.
The rebuild-an-asset pattern, instead of delete-then-create:
The same caution applies to any asset currently open in an editor tab or referenced by a loaded level. Non-Blueprint assets you never loaded (textures, meshes written by a factory) delete fine.
AssetData Argumentget_primary_content_browser_selection is shaped like bool Func(FAssetData& Out) in C++ and is
exposed to Python as () -> AssetData or None. Passing an AssetData argument raises TypeError.
# WRONG - TypeError: takes no arguments (1 given)
asset = unreal.AssetData()
unreal.AssetDiscoveryService.get_primary_content_browser_selection(asset)
# CORRECT - call with no out-arg, check the return for None
asset = unreal.AssetDiscoveryService.get_primary_content_browser_selection()
if asset:
print(asset.asset_name)| Operation | Use |
|---|---|
| Search / find / list assets | engine AssetTools toolset via call_tool, or unreal.AssetRegistryHelpers.get_asset_registry() |
| Load / save / save-all | unreal.EditorAssetLibrary.load_asset / save_asset / save_directory, or AssetTools |
| Move / rename / duplicate / delete | unreal.EditorAssetLibrary.rename_asset (move), duplicate_asset, delete_asset (unreferenced only — see the modal warning above), or AssetTools; unreal.AssetDiscoveryService.delete_asset_unattended for anything that may be referenced |
| Existence check | unreal.EditorAssetLibrary.does_asset_exist(path) |
| Referencers / dependencies | unreal.AssetRegistryHelpers.get_asset_registry().get_referencers(...) |
| Open an asset / list ALL open editors | Epic EditorAppToolset via call_tool (see below) |
| Import a file from disk (image, mesh, …) | unreal.AssetDiscoveryService.import_asset / import_texture (see below) |
| Export a texture to disk | unreal.AssetDiscoveryService.export_texture |
| Primary Content Browser selection | unreal.AssetDiscoveryService.get_primary_content_browser_selection() |
| Is an asset open in an editor | unreal.AssetDiscoveryService.is_asset_open(path) |
EditorAppToolsetVibeUE covers the single/primary queries above; the list-all and open operations live on
Epic's native EditorToolset.EditorAppToolset (call via call_tool; returns {"returnValue": [...]}
of package-path strings). These are NOT Python-bound — they only work through call_tool:
| Need | Call |
|---|---|
| All assets open in editors | call_tool("GetOpenAssets", "EditorToolset.EditorAppToolset") |
| All Content Browser selections | call_tool("GetSelectedAssets", "EditorToolset.EditorAppToolset") |
| Open an asset in its editor | call_tool("OpenEditorForAsset", "EditorToolset.EditorAppToolset", {"assetPath": "/Game/.../BP_X"}) |
# "Show me all open Blueprints" = Epic GetOpenAssets + a type filter
opens = call_tool("GetOpenAssets", "EditorToolset.EditorAppToolset")["returnValue"]
bps = [p for p in opens if isinstance(unreal.load_asset(p), unreal.Blueprint)]
# "Open the selected asset" = VibeUE primary-selection + Epic OpenEditorForAsset
sel = unreal.AssetDiscoveryService.get_primary_content_browser_selection()
if sel:
call_tool("OpenEditorForAsset", "EditorToolset.EditorAppToolset",
{"assetPath": str(sel.package_name)})/Game listing can return 30,000+ assetsListing every asset under /Game returns tens of thousands of entries and floods the conversation.
Whether you go through the engine AssetTools toolset or EditorAssetLibrary.list_assets, always
narrow first:
/Game/Blueprints/HUD), and by class where supported.EditorAssetLibrary Save MethodsThere is no save_dirty_assets (AttributeError). The real options:
unreal.EditorAssetLibrary.save_asset(path), save_directory(dir), save_loaded_asset(obj),
save_loaded_assets([objs]). To save everything dirty, iterate or use the engine AssetTools
toolset's save action.
EditorAssetLibrary.list_assets Has No Class Filterunreal.EditorAssetLibrary.list_assets(directory_path, recursive=True, include_folder=False)
returns path strings only — there is no asset_class_names or any other class-filter kwarg
(TypeError: invalid keyword argument). To list assets of one type, either filter the returned
paths in Python (load each and check its class), or query the Asset Registry with a class filter
via unreal.AssetRegistryHelpers.get_asset_registry().get_assets(...).
AssetData Property Namesget_primary_content_browser_selection() returns an AssetData. Read it with:
| WRONG (old) | CORRECT |
|---|---|
asset.name | asset.asset_name |
asset.path | asset.package_path |
asset.asset_class | asset.asset_class_path (a TopLevelAssetPath; compare .asset_name) |
asset.object_path | f"{asset.package_name}.{asset.asset_name}" |
AssetData has no object_path attribute (AttributeError). Build it from package_name +
asset_name, or just use str(asset.package_name).
AssetDiscoveryService.import_assetTo bring a file from disk into the Content Browser, use AssetDiscoveryService.import_asset
(or import_texture, which takes a full asset path and runs the same importer). It takes any
format the editor imports, and saves what it imports:
Texture2D.AssetImportTask — no option dialogs.import unreal
# Disk → Content Browser. Returns (asset_path, error); path is "" on failure.
path, err = unreal.AssetDiscoveryService.import_asset(
"C:/Images/rocks.jpg", "/Game/UI/Textures", "T_Rocks")
print(path or err)
# A mesh takes the same call. Give a file that brings materials/textures a folder of its own.
path, err = unreal.AssetDiscoveryService.import_asset(
"C:/Art/crate.fbx", "/Game/Props/Crate", "SM_Crate")
print(path or err)What to expect:
RecursionGuard asserts and
the editor goes down. import_asset checks for exactly that and returns an error naming
RecursionGuard instead. execute_python_code and MCP tool calls do not run inside such a task,
so imports from them go ahead. Calling unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks(...)
yourself skips that check (and the save, and the main-asset pick) — prefer import_asset.Need a source image to import? Editor screenshots live under the project's
Saved/Screenshots (and Saved/VibeUE/Screenshots):
import os, unreal
shots = os.path.join(unreal.Paths.project_saved_dir(), "Screenshots")
print(os.listdir(shots) if os.path.isdir(shots) else "no screenshots yet")Use /Game/... paths, not file system paths, everywhere except the disk side of import/export.
# WRONG - file system path
unreal.EditorAssetLibrary.does_asset_exist("C:/Projects/Content/BP_Player.uasset")
# CORRECT - content browser path
unreal.EditorAssetLibrary.does_asset_exist("/Game/Blueprints/BP_Player")Duplicating creates a new asset identity. References stay pointed at the original, so deleting the original after a duplicate can break those references. Use a real move/rename (which fixes up references) instead:
import unreal
# EditorAssetLibrary.rename_asset performs a move + reference fixup
unreal.EditorAssetLibrary.rename_asset(
"/Game/StateTree/STT_Rotate",
"/Game/StateTree/Tasks/STT_Rotate")(The engine AssetTools toolset exposes an equivalent move/rename action via call_tool.)
Use the engine's WidgetBlueprintFactory — a plain BlueprintFactory with a UserWidget
parent creates a regular Blueprint, not a WidgetBlueprint (the removed
BlueprintService.create_blueprint used to pick the UMG factory automatically):
factory = unreal.WidgetBlueprintFactory()
factory.set_editor_property("ParentClass", unreal.UserWidget)
wbp = unreal.AssetToolsHelpers.get_asset_tools().create_asset("WBP_Menu", "/Game/UI", None, factory)For designing the widget afterwards (hierarchy, slots, bindings), load the umg-widgets skill.
Searching is an engine AssetTools job (via call_tool) — or query the Asset Registry directly:
import unreal
ar = unreal.AssetRegistryHelpers.get_asset_registry()
# All Blueprints under /Game (recursive). Use a class filter and a narrow path to avoid huge results.
f = unreal.ARFilter(
package_paths=["/Game/Blueprints"],
class_paths=[unreal.TopLevelAssetPath("/Script/Engine", "Blueprint")],
recursive_paths=True)
for a in ar.get_assets(f)[:25]:
print(f"{a.asset_name}: {a.package_path}")import unreal
if unreal.EditorAssetLibrary.does_asset_exist("/Game/BP_Player"):
print("Found")
else:
print("Not found - create it")import unreal
dup = unreal.EditorAssetLibrary.duplicate_asset("/Game/BP_Enemy", "/Game/BP_EnemyBoss")
if dup:
print("Duplicated")import unreal
ok = unreal.EditorAssetLibrary.rename_asset(
"/Game/StateTree/STT_Rotate",
"/Game/StateTree/Tasks/STT_Rotate")
print("Moved (references fixed up)" if ok else "Move failed")import unreal
# Save a specific asset
unreal.EditorAssetLibrary.save_asset("/Game/BP_Player")
# Save a whole directory of dirty assets
unreal.EditorAssetLibrary.save_directory("/Game/Blueprints")Asset Registry referencer queries return package-name strings, not AssetData.
import unreal
ar = unreal.AssetRegistryHelpers.get_asset_registry()
refs = ar.get_referencers(
"/Game/SM_Weapon",
unreal.AssetRegistryDependencyOptions(include_hard_package_references=True))
if not refs:
unreal.EditorAssetLibrary.delete_asset("/Game/SM_Weapon")
else:
for ref in refs: # ref is a package name, e.g. "/Game/Blueprints/BP_Player"
print(f"In use by: {ref}")⚠️ Deleting levels:
delete_asseton a recently-loaded.umapcan returnTrue(asset gone from the registry) yet leave the package file on disk, sodelete_directoryon its folder then fails. After deleting level assets, verify theContent/...folder on disk and remove leftover.umapfiles manually.
⚠️
delete_assetfalse success is not limited to levels (issue #557). Blueprints with live references have also returnedTruewhile their files stayed on disk — and later touching such half-deleted assets ("files gone, editor memory ghosts") has crashed the editor. After ANY scripted delete, verify with a filesystem check (os.path.existson the.uasset) ordoes_asset_exist, and never read properties (e.g. mesh bounds) off an asset you just deleted.
ℹ️
CaptureAssetImagecannot render Blueprints or Niagara systems ("Asset type does not support image capture"). To preview those, spawn them in the level on a clear spot, aim the viewport camera, and use thecapture_imageMCP tool; delete the preview actors after.
import unreal
# Import (disk → Content Browser). Returns (asset_path, error); asset_path is "" on failure.
# Pass a destination FOLDER + optional asset name.
path, err = unreal.AssetDiscoveryService.import_asset(
"C:/Textures/logo.png", "/Game/Textures", "T_Logo")
print(path or err)
# import_texture(src, dest_asset_path) takes a full asset path and uses the same importer:
unreal.AssetDiscoveryService.import_texture("C:/Textures/logo.png", "/Game/Textures/T_Logo")
# Export (project → file system)
unreal.AssetDiscoveryService.export_texture("/Game/Textures/T_Logo", "C:/Exports/logo.png")import unreal
# Is a specific asset open in an editor?
if unreal.AssetDiscoveryService.is_asset_open("/Game/BP_Player"):
print("BP_Player is open")
# Primary Content Browser selection — NO out-arg, returns AssetData or None
asset = unreal.AssetDiscoveryService.get_primary_content_browser_selection()
if asset:
print(f"Selected: {asset.asset_name} at {asset.package_path}")
# Open it via the AssetEditorSubsystem (engine), loading the object first:
obj = unreal.EditorAssetLibrary.load_asset(str(asset.package_name))
unreal.get_editor_subsystem(unreal.AssetEditorSubsystem).open_editor_for_assets([obj])
else:
print("Nothing selected")For the full multi-selection set, open-editor lists, and search, use the engine
AssetTools/AssetEditorSubsystemtoolsets viacall_tool.
asset.asset_class_path is a TopLevelAssetPath struct, not a string — compare its asset_name:
import unreal
asset = unreal.AssetDiscoveryService.get_primary_content_browser_selection()
if asset and str(asset.asset_class_path.asset_name) == "Blueprint":
print(f"{asset.asset_name} is a Blueprint")Assets not covered by a VibeUE service (e.g., LandscapeGrassType) require AssetToolsHelpers + a
factory. (create_asset imports nothing, so the import caveats above do not apply to it.)
import unreal
asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
factory = unreal.LandscapeGrassTypeFactory()
lgt = asset_tools.create_asset("LGT_MyGrass", "/Game/Landscape", unreal.LandscapeGrassType, factory)
# Set properties via set_editor_property, then save
unreal.EditorAssetLibrary.save_asset("/Game/Landscape/LGT_MyGrass")Tip: Use
discover_python_module("unreal", name_filter="Factory")to find available factories.
Blueprint, WidgetBlueprintTexture2D, Material, MaterialInstanceConstantStaticMesh, SkeletalMeshDataTable, PrimaryDataAssetLandscapeGrassType, LandscapeLayerInfoObjectClass names must match UE class names exactly (e.g., LandscapeGrassType, not GrassType). When a
typed Asset Registry query unexpectedly returns 0, retry without the class filter and inspect each
result's asset_class_path.asset_name to learn the real class name.
execute_python_code)scripts/find_and_save.txt — find an asset (Asset Registry / EditorAssetLibrary) and duplicate + save it.unreal.Rotator(...) positional order is (roll, pitch, yaw) — pass by keyword.unreal.AssetTools.create_asset(...) is a descriptor and throws; the working call is AssetToolsHelpers.get_asset_tools().create_asset(...).EditDefaultsOnly properties cannot be written on instances from Python; a server-only UPROPERTY() with no Blueprint flag reads as "protected"; and when a bool bIsDead and a UFUNCTION IsDead() both map to is_dead, the property wins.TInstancedStruct authoring: inst = unreal.InstancedStruct(); inst.import_text('/Script/<Module>.<Struct>(Field=...)'); a TSoftClassPtr UPROPERTY wants the loaded class, not a SoftClassPath.LevelEditorPlaySettings is not in the Python stub — reach it via load_class(None, "/Script/UnrealEd.LevelEditorPlaySettings") and exact CamelCase property names.line_trace_single(actor.get_world(), ...) (LevelEditorSubsystem.get_world() is a null context); HitResult.component/actor are read-protected, so read export_text().ObjectIterator(unreal.YourSubsystem) filtered on get_outer().get_path_name().save_dirty_packages(True, True) — save by explicit path; a dirty package you did not touch is the user's work.© kevinpbuckley, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 3 other files (scripts, references) in Content/Skills/asset-management of kevinpbuckley/VibeUE.
Open the folder on GitHubat commit dc051be
Unreal Asset Import and Export next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Unreal Asset Import and Export this skillkevinpbuckley/VibeUE | 730 | — | ~6.5k | Automated safety check: Pass | MIT | |
| Fmodel Unpackpa001024/dna-builder | 137 | — | ~2.6k | Automated safety check: Pass | MIT | |
| Scenario Unreal Expertscenario-labs/skills | 946 | — | ~6.4k | Automated safety check: Pass | MIT | |
| Unreal BridgeTornLux/UnrealBridge | 306 | — | ~8.4k | Automated safety check: Notes | MIT | |
| Unreal Blueprint Authoringflopperam/unreal-engine-mcp | 1.1k | — | ~1.6k | Automated safety check: Pass | None | |
| Unreal Material and VFX Workflowflopperam/unreal-engine-mcp | 1.1k | — | ~927 | Automated safety check: Pass | None |
pa001024/dna-builder
This skill documents how to use the fmodel-mcp toolkit (a CUE4Parse-based .NET CLI plus a thin Python MCP server) to inspect and export Unreal Engine game assets — pak files, textures, meshes…
scenario-labs/skills
A skill your agent uses when an agent drives Unreal Engine 5.8 on a Mac for any task: Editor Python (the unreal module), headless UnrealEditor-Cmd -run=pythonscript jobs, Epic's Unreal MCP server…
TornLux/UnrealBridge
Execute Python scripts inside a running Unreal Engine 5.3+ editor via TCP bridge.
flopperam/unreal-engine-mcp
Walks an agent through creating and editing Unreal Engine Blueprints with the Flopperam MCP's bp_* tools, from inspection to commit and verification.
flopperam/unreal-engine-mcp
Walks an Unreal Engine MCP agent through building materials, Niagara particle systems, Chaos destruction, and curve assets with inspect-then-edit steps.
LaurentiuGabriel/unreal-game-assets-creation-skill
Generate a game-ready 3D asset by running the local AI pipeline sequentially: Fooocus (SDXL text-to-image) - Hunyuan3D-2 (image-to-textured-GLB) - optional Blender FBX convert + Unreal import.
kevinpbuckley/VibeUE
Previews, validates and bakes bone-rotation edits on Unreal Engine AnimSequences, enforcing a coordinate space and a constraint-checked preview-validate-bake loop.
kevinpbuckley/VibeUE
Creates and edits Unreal Engine Animation Montages through AnimMontageService: sections, slots, segments, branching points, notifies and blend settings.
kevinpbuckley/VibeUE
Creates and edits AnimSequence keyframes and bone tracks in Unreal Engine through AnimSequenceService, following an inspect, preview, validate and bake workflow with correct bone-space handling.
kevinpbuckley/VibeUE
Changes Unreal Engine console variables, scalability levels and raw INI values through VibeUE's Python API, now that registered settings moved to the engine's own toolset.
kevinpbuckley/VibeUE
Creates and wires Unreal Engine Enhanced Input assets through VibeUE's InputService: Input Actions, Mapping Contexts, key bindings, triggers and modifiers.
kevinpbuckley/VibeUE
Creates, edits and inspects Unreal Engine UserDefinedEnums and UserDefinedStructs through Python, including adding values or members and reading internal versus display names.
Categories
Imports files into Unreal Engine, exports textures, reads the Content Browser selection and checks open assets via VibeUE, plus mesh reimport material fixes. 8 consolidated general asset operations, the engine's native AssetTools toolset or EditorAssetLibrary handles searching, loading, saving, moving, renaming, duplicating and deleting assets, so this skill covers only the remaining AssetDiscoveryService calls: import_asset and import_texture for bringing in images, meshes or other importable files, export_texture, get_primary_content_browser_selection and is_asset_open. The old manage_asset MCP tool is gone.
Unreal Asset Import and Export fits situations like: importing an image, mesh or other file from disk into the editor; exporting a texture asset to a file; finding out which asset is selected in the Content Browser; fixing gray or default surfaces after a static-mesh reimport.
Run `npx skills add kevinpbuckley/VibeUE --skill asset-management -a claude-code`. Or copy the skill folder (Content/Skills/asset-management in kevinpbuckley/VibeUE) into .claude/skills/asset-management in your project. Claude Code loads it when a task matches its description.
Run `npx skills add kevinpbuckley/VibeUE --skill asset-management -a codex`. Or copy the skill folder (Content/Skills/asset-management in kevinpbuckley/VibeUE) into .agents/skills/asset-management in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add kevinpbuckley/VibeUE --skill asset-management -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/asset-management, .gemini/skills/asset-management, .github/skills/asset-management and .opencode/skills/asset-management in your project.
Going by SKILL.md and its folder, Unreal Asset Import and Export needs Python for the scripts in its folder. Our summary lists: Unreal Engine with the VibeUE plugin; The editor's Python scripting API.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. The check reads SKILL.md only: the scripts in the folder are not scanned, so read them before running anything.
Unreal Asset Import and Export is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.5k tokens (SKILL.md is roughly 26k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 517 tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Unreal Asset Import and Export: Fmodel Unpack (pa001024/dna-builder, 137 stars), Scenario Unreal Expert (scenario-labs/skills, 946 stars), Unreal Bridge (TornLux/UnrealBridge, 306 stars) and Unreal Blueprint Authoring (flopperam/unreal-engine-mcp, 1.1k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
kevinpbuckley (a GitHub user) maintains it in kevinpbuckley/VibeUE, which has 730 GitHub stars. The repository holds 39 skills in this directory. The repository was last updated on October 7, 2026.
Source: kevinpbuckley/VibeUE on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.