Asset Management logo

Asset Management

Community
kevinpbuckley
asset-management

Import files from disk (images, meshes, any format the editor imports), export textures, query the Content Browser selection, and check if an asset is open (AssetDiscoveryService). Search, load, save, move, rename, duplicate, and delete assets are handled by Unreal's native AssetTools toolset or EditorAssetLibrary. Use when the user asks to import an image, mesh or other file from disk, export a texture, query the Content Browser selection, or check whether an asset is open in an editor. Also use for static-mesh LOD reimport, section material mapping, or gray/default surfaces after reimport.

Overview

Publisherkevinpbuckley
RepositoryVibeUE
Skill nameasset-management
Stars
735
Forks
161
Bundled files
3
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.

  • 3 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

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

Installation

Install the Asset Management 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/asset-management
  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/asset-management .claude/skills/asset-management
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Asset Management 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 Asset Management 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 Asset Management 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.

🧠 Brains complement: IF an unreal-engine-skills-manager tool (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.

Asset Discovery & Management Skill

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 AssetTools toolset (reach it via call_tool; run describe_toolset for its actions) — or you can drive unreal.EditorAssetLibrary / unreal.AssetRegistryHelpers directly from execute_python_code. The old manage_asset MCP tool is GONE. VibeUE's AssetDiscoveryService was 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 from AssetDiscoveryService — use the engine AssetTools toolset or EditorAssetLibrary / the Asset Registry.

Data Assets & Data Tables — no dedicated VibeUE service (issues #451, #452)

There is no DataAssetService / DataTableService. Drive them natively:

python
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 (engine DataTableTools) or re-import an empty CSV. An empty GameplayTag field serializes as "None".

Critical Rules

⚠️ delete_asset on a REFERENCED asset opens a modal dialog that wedges an unattended editor

EditorAssetLibrary.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:

python
# 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 asset

The 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.

⚠️ Never delete_asset a Blueprint you loaded or compiled this session

EditorAssetLibrary.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_Thing

Every 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:

  • Write to a new name and swap references, or
  • Delete the file on disk while the editor is closed, then create it fresh, or
  • Edit the existing asset in place (clear the graph, re-add nodes) rather than recreating it.

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.

⚠️ Out-Params Become Return Values in Python — Never Pass an AssetData Argument

get_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.

python
# 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)

🔀 Where each operation lives now

OperationUse
Search / find / list assetsengine AssetTools toolset via call_tool, or unreal.AssetRegistryHelpers.get_asset_registry()
Load / save / save-allunreal.EditorAssetLibrary.load_asset / save_asset / save_directory, or AssetTools
Move / rename / duplicate / deleteunreal.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 checkunreal.EditorAssetLibrary.does_asset_exist(path)
Referencers / dependenciesunreal.AssetRegistryHelpers.get_asset_registry().get_referencers(...)
Open an asset / list ALL open editorsEpic 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 diskunreal.AssetDiscoveryService.export_texture
Primary Content Browser selectionunreal.AssetDiscoveryService.get_primary_content_browser_selection()
Is an asset open in an editorunreal.AssetDiscoveryService.is_asset_open(path)

🔀 ALL open assets / ALL selections / open an asset — use Epic's EditorAppToolset

VibeUE 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:

NeedCall
All assets open in editorscall_tool("GetOpenAssets", "EditorToolset.EditorAppToolset")
All Content Browser selectionscall_tool("GetSelectedAssets", "EditorToolset.EditorAppToolset")
Open an asset in its editorcall_tool("OpenEditorForAsset", "EditorToolset.EditorAppToolset", {"assetPath": "/Game/.../BP_X"})
python
# "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)})

🚨 Never list broad paths — a /Game listing can return 30,000+ assets

Listing 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:

  • Filter by a specific subfolder (e.g. /Game/Blueprints/HUD), and by class where supported.
  • If you only need a count or sample, slice in Python before printing — never print the full array.

⚠️ EditorAssetLibrary Save Methods

There 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 Filter

unreal.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(...).

⚠️ UE 5.8 AssetData Property Names

get_primary_content_browser_selection() returns an AssetData. Read it with:

WRONG (old)CORRECT
asset.nameasset.asset_name
asset.pathasset.package_path
asset.asset_classasset.asset_class_path (a TopLevelAssetPath; compare .asset_name)
asset.object_pathf"{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).

⚠️ Importing Files From Disk — Use AssetDiscoveryService.import_asset

To 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:

  • Images (png, jpg, jpeg, bmp, tga, dds, exr, hdr, tiff, tif, psd, pcx) go through the texture factory's direct binary path and become a Texture2D.
  • Everything else (FBX, OBJ, glTF/GLB, audio, …) goes through an automated, synchronous AssetImportTask — no option dialogs.
python
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:

  • Same-name assets are replaced, without a prompt. That includes the side assets a multi-asset file brings along: an FBX's materials and textures (and a skeletal mesh's Skeleton and PhysicsAsset) overwrite any asset of the same name already in the destination folder. Their names are only known once the importer has run, so there is no pre-check — import such files into an empty or dedicated folder.
  • The returned path is the main asset. For a file that brings several assets it is the mesh, not a material or texture; when the importer did not use the name you passed, the path carries the importer's name. List the folder (Asset Registry) to find the side assets.
  • The one context where an import would crash, and is refused instead. A synchronous AssetTools import waits by pumping the game thread's task queue. If the caller is itself running inside a game-thread task (the queue is already being processed), TaskGraph's 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):

python
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")

⚠️ Asset Paths Must Be Content Browser Paths

Use /Game/... paths, not file system paths, everywhere except the disk side of import/export.

python
# 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")

⚠️ Never Emulate Move/Rename with Duplicate + Delete

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:

python
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.)

Creating Widget Blueprints

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):

python
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.


Workflows

Search Pattern

Searching is an engine AssetTools job (via call_tool) — or query the Asset Registry directly:

python
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}")

Check Exists Pattern

python
import unreal

if unreal.EditorAssetLibrary.does_asset_exist("/Game/BP_Player"):
    print("Found")
else:
    print("Not found - create it")

Duplicate Pattern

python
import unreal

dup = unreal.EditorAssetLibrary.duplicate_asset("/Game/BP_Enemy", "/Game/BP_EnemyBoss")
if dup:
    print("Duplicated")

Move / Rename Pattern

python
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")

Save Pattern

python
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")

Check References Before Delete

Asset Registry referencer queries return package-name strings, not AssetData.

python
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_asset on a recently-loaded .umap can return True (asset gone from the registry) yet leave the package file on disk, so delete_directory on its folder then fails. After deleting level assets, verify the Content/... folder on disk and remove leftover .umap files manually.

⚠️ delete_asset false success is not limited to levels (issue #557). Blueprints with live references have also returned True while 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.exists on the .uasset) or does_asset_exist, and never read properties (e.g. mesh bounds) off an asset you just deleted.

ℹ️ CaptureAssetImage cannot 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 the capture_image MCP tool; delete the preview actors after.

Import / Export Textures (VibeUE)

python
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")

Editor State & Content Browser (VibeUE)

python
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 / AssetEditorSubsystem toolsets via call_tool.

Inspect the Selection's Class

asset.asset_class_path is a TopLevelAssetPath struct, not a string — compare its asset_name:

python
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")

Create Non-Standard Asset Types (Factory Pattern)

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.)

python
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.

Common Asset Class Names for Filtering

  • Blueprint, WidgetBlueprint
  • Texture2D, Material, MaterialInstanceConstant
  • StaticMesh, SkeletalMesh
  • DataTable, PrimaryDataAsset
  • LandscapeGrassType, LandscapeLayerInfoObject

Class 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.

Sample scripts (run via execute_python_code)

  • scripts/find_and_save.txt — find an asset (Asset Registry / EditorAssetLibrary) and duplicate + save it.

Additional gotchas

  • 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.
  • Editor-world traces need a real world: line_trace_single(actor.get_world(), ...) (LevelEditorSubsystem.get_world() is a null context); HitResult.component/actor are read-protected, so read export_text().
  • World subsystems have no Python accessor — reach one via ObjectIterator(unreal.YourSubsystem) filtered on get_outer().get_path_name().
  • Never save_dirty_packages(True, True) — save by explicit path; a dirty package you did not touch is the user's work.

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Asset Management AI skill do?

Import files from disk (images, meshes, any format the editor imports), export textures, query the Content Browser selection, and check if an asset is open (AssetDiscoveryService). Search, load, save, move, rename, duplicate, and delete assets are handled by Unreal's native AssetTools toolset or EditorAssetLibrary. Use when the user asks to import an image, mesh or other file from disk, export a texture, query the Content Browser selection, or check whether an asset is open in an editor. Also use for static-mesh LOD reimport, section material mapping, or gray/default surfaces after reimport.

Why use Asset Management on TypingMind?

Because you install it once and use it with any model. Asset Management 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 Asset Management in TypingMind?

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

Which AI models can use Asset Management?

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 Asset Management?

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

Is the Asset Management 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 👇