Blueprints logo

Blueprints

Community
kevinpbuckley
blueprints

Create and modify Blueprint assets, variables, functions, components, event dispatchers, and interfaces. Use when the user asks to create a Blueprint/BP, add a variable or component to a Blueprint, make an event dispatcher/delegate, implement a Blueprint interface, or inspect a Blueprint's class/components. For node-level graph wiring, also load blueprint-graphs.

Overview

Publisherkevinpbuckley
RepositoryVibeUE
Skill nameblueprints
Stars
735
Forks
161
Bundled files
6
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.

  • 6 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 Blueprints 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/blueprints
  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/blueprints .claude/skills/blueprints
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Blueprints 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 Blueprints 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 Blueprints 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: "blueprint-fundamentals"} 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.

Blueprint System Skill

For node-level graph editing (adding nodes, connecting pins, wiring logic, timers, layout), load the blueprint-graphs skill.

Critical Rules

⚠️ Components added with add_component are NOT on the CDO

add_component creates a SCS node template, not an instance on the class default object. So the obvious way to configure it silently does nothing — get_components_by_class on the CDO returns an empty list and the loop body never runs:

python
# WRONG - prints [], the mesh is never assigned, and nothing errors
cdo = unreal.get_default_object(bp.generated_class())
for c in cdo.get_components_by_class(unreal.SkeletalMeshComponent):
    c.set_editor_property("skeletal_mesh_asset", mesh)

# CORRECT - go through the service, which edits the SCS template
BS.set_component_property(BP, "Windmill", "SkeletalMeshAsset", "/Game/X/SK_Windmill.SK_Windmill")
BS.set_component_property(BP, "Windmill", "AnimationMode", "AnimationBlueprint")
BS.set_component_property(BP, "Windmill", "AnimClass", "/Game/X/ABP_Windmill.ABP_Windmill_C")

Values are strings: an asset takes its object path (/Game/X/SK.SK), a class takes the _C path, an enum takes the entry name. Read back with get_component_property to confirm — it returns the resolved object, e.g. /Script/Engine.SkeletalMesh'/Game/X/SK_Windmill.SK_Windmill'. Components that come from the parent C++ class are on the CDO and can be set directly; only SCS-added ones need the service.

Level Blueprints: pass the MAP path

Every unreal.BlueprintService.* function that takes a blueprint_path also accepts a map/world path (e.g. /Game/Maps/MainMenu) and resolves it to that level's Level Blueprint — inspect and edit level scripts exactly like normal Blueprints. Explicit subobject paths (/Game/Maps/MyMap.MyMap:PersistentLevel.MyMap) work too. Compile still goes through unreal.BlueprintEditorLibrary.compile_blueprint(...) on the returned/loaded level script, and the MAP asset is what you save afterwards (unreal.EditorAssetLibrary.save_asset("/Game/Maps/MyMap")).

⚠️ Engine BlueprintTools args are {refPath} objects, NOT strings

Every UObject/UClass argument to the engine BlueprintTools toolset (blueprint, asset_type, graph, parent_class, …) is a reference object {"refPath": "<object path>"}, and these tools return the same shape. Passing a plain string fails with "<x> is not a valid object path for property '<arg>'". This rule is only for the engine BlueprintTools call_tool path — VibeUE's own unreal.BlueprintService.* Python methods still take plain string paths.

The refPath must be the full object path — /Game/Dir/Asset.Asset (the asset name repeated after the dot). A bare package path (/Game/Dir/Asset, no .Asset) is rejected. Two safe ways to get one:

python
# (a) Reuse what create/load returns — it is already a {refPath} object:
bp_ref = call_tool("create", "editor_toolset.toolsets.blueprint.BlueprintTools",
                   {"folder_path": "/Game/BP", "asset_name": "BP_X",
                    "asset_type": {"refPath": "/Script/Engine.Actor"}})["returnValue"]

# (b) Build one from a package path you already have:
def bp_ref_of(p):  # "/Game/Dir/BP_X" -> {"refPath": "/Game/Dir/BP_X.BP_X"}
    name = p.rsplit("/", 1)[-1].split(".")[0]
    return {"refPath": p if "." in p.rsplit("/", 1)[-1] else f"{p}.{name}"}

All the arguments={"blueprint": path, ...} / {"blueprint": bp, ...} examples in the sub-docs are shorthand — wrap the path with bp_ref_of(...) (or reuse the returned ref) when you actually call. On success these tools return {"returnValue": null}; an error comes back as a Parameter error.

⚠️ Creating a Blueprint — engine BlueprintTools.create

Blueprint basics now live on the engine's BlueprintTools toolset (VibeUE extends the native MCP endpoint). Create a Blueprint with call_tool, not unreal.BlueprintService.create_blueprint (that method was cut):

python
# CORRECT — folder_path + asset_name are strings; asset_type is a {refPath} class reference.
result = call_tool(
    tool_name="create",
    toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools",
    arguments={"folder_path": "/Game/Blueprints", "asset_name": "BP_MyActor",
               "asset_type": {"refPath": "/Script/Engine.Actor"}},
)
# -> {"returnValue": {"refPath": "/Game/Blueprints/BP_MyActor.BP_MyActor"}}
bp_ref = result["returnValue"]   # reuse this {refPath} object for later BlueprintTools calls

The folder and asset name are passed separately (folder_path + asset_name) — do not jam a full asset path into one argument. asset_type is the parent class as a {refPath}: /Script/Engine.Actor, /Script/Engine.Pawn, /Script/Engine.Character, or a Blueprint's generated class "/Game/.../BP_Base.BP_Base_C".

Blueprint Types in add_variable — engine BlueprintTools.add_variable

Adding a variable is also an engine BlueprintTools call now (unreal.BlueprintService.add_variable was cut). Use call_tool with blueprint, name, type_name (and add_object_variable / add_struct_variable for object- and struct-typed variables):

python
# CORRECT — blueprint is a {refPath} object; name/type_name are strings
call_tool(
    tool_name="add_variable",
    toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools",
    arguments={"blueprint": {"refPath": "/Game/BP_MyActor.BP_MyActor"}, "name": "Health", "type_name": "float"},
)

When the variable's type is a Blueprint class, use the Blueprint asset name directly in type_name. Do not add _C, and do not construct a generated class path — just use the name ("BP_Enemy", or the full "/Game/Blueprints/BP_Enemy"). Appending _C ("BP_Enemy_C") or guessing a generated class path is wrong; the type system resolves Blueprint names automatically via asset search.

Engine structs need the F prefix: color variables are "FLinearColor" / "FColor" — plain "LinearColor" / "Color" is not resolved. Unsure of a type string? Use the surviving unreal.BlueprintService.search_variable_types("Linear") to look it up.

Supported primitive type_name values (exact): bool, int, float, byte, name, string, text, Vector, Rotator, Transform, Vector2D, LinearColor. Use int (not int32) and float (not double) — add_variable rejects int32/double with "Unknown type 'int32'. Supported: …". For other engine structs (e.g. FColor, FGameplayTag) use add_struct_variable with struct_type; for object/class refs use add_object_variable with object_class as a {refPath}:

python
# Object-reference variable — object_class is a {refPath} to the class
call_tool("add_object_variable", "editor_toolset.toolsets.blueprint.BlueprintTools",
    {"blueprint": {"refPath": "/Game/BP_MyActor.BP_MyActor"}, "name": "DeathFX",
     "object_class": {"refPath": "/Script/Niagara.NiagaraSystem"}})

Struct/object/enum types from Python — add_member_variable (VibeUE delta)

From inside execute_python_code, unreal.BlueprintService.add_member_variable(bp_path, name, type_string, default="", is_array=False, container_type="") creates a member variable with full type-string support — structs, objects, enums, and containers — in one batchable Python call (no call_tool round-trip). It takes the same type strings as add_function_local_variable ("float", "FVector", "FStateTreeDelegateDispatcher", "AActor"):

python
unreal.BlueprintService.add_member_variable(bp_path, "FinishRotatingDispatcher", "FStateTreeDelegateDispatcher")
unreal.BlueprintService.add_member_variable(bp_path, "Waypoints", "FVector", "", True)  # FVector array
unreal.BlueprintEditorLibrary.compile_blueprint(unreal.EditorAssetLibrary.load_asset(bp_path))

Returns False (with the reason in the log) on a duplicate name or an unresolvable type string. Compile after adding — the variable is registered but the class is only rebuilt on compile.

Removing one variable — remove_member_variable (VibeUE delta)

unreal.BlueprintService.remove_member_variable(bp_path, name, force=False) removes exactly one member variable. The engine alternative, BlueprintEditorLibrary.remove_unused_variables, sweeps every unreferenced variable — use this when you want to delete just one. It counts Get/Set references across all graphs first: with references present and force=False it refuses (returns False, logs the referencing graphs and node counts); with force=True it removes the variable and its referencing nodes. A component name (from the Simple Construction Script) is refused with a pointer to the component API. Verifies by readback and returns True only when the variable is gone.

python
unreal.BlueprintService.remove_member_variable(bp_path, "UnusedScratch")        # refuses if referenced
unreal.BlueprintService.remove_member_variable(bp_path, "OldHealth", True)       # force: nodes go too

⚠️ Adding a function graph — engine BlueprintTools.add_function_graph

Creating a function graph moved to the engine toolset (create_function / add_function on BlueprintService were cut). Use call_tool with blueprint + graph_name:

python
call_tool(
    tool_name="add_function_graph",
    toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools",
    arguments={"blueprint": {"refPath": "/Game/Blueprints/BP_MyActor.BP_MyActor"}, "graph_name": "DoThing"},
)

Refining the graph afterward still uses the surviving VibeUE delta methods (add_function_parameter, add_function_local_variable, get_function_parameters, get_function_info, override_function, open_function_graph).

⚠️ Method Name Gotchas

WRONGCORRECT
get_component_info(path, name)get_component_info(type) - takes ONLY type!

⚠️ Property Name Gotchas

WRONGCORRECT
info.inputsinfo.input_parameters
var.namevar.variable_name

⚠️ Python Naming: Boolean b Prefix Is Stripped

UE Python strips the lowercase b prefix from boolean properties. Always use the name without it:

C++ namePython name
bAlreadyOverriddenalready_overridden
bIsEventStyleis_event_style
bIsNativeEventis_native_event
bEnabledenabled

Do not use b_already_overridden, b_is_event_style, etc. — these will raise AttributeError.

Exception: the string-based CDO accessors get_property / set_property take the native C++ property name, so booleans KEEP the b prefix there ("bReplicates", not "replicates"). See the next section.

⚠️ CDO Properties: get_property / set_property (replication, lifespan, etc.)

Use these to read/write Class Default Object properties like replication or initial lifespan:

python
import unreal
bp = "/Game/Blueprints/TestActor"

# READ — returns a single value: str, or None if the property wasn't found. NOT a (success, value) tuple!
value = unreal.BlueprintService.get_property(bp, "bReplicates")   # "True" / "False" (a STRING)
life  = unreal.BlueprintService.get_property(bp, "InitialLifeSpan")  # "5.000000"

# WRONG — raises ValueError: too many values to unpack
# success, value = unreal.BlueprintService.get_property(bp, "bReplicates")

# WRITE — values are strings; returns bool
unreal.BlueprintService.set_property(bp, "bReplicates", "True")

# Compile via the engine BlueprintTools toolset (compile_blueprint was cut from BlueprintService):
# call_tool(tool_name="compile_blueprint",
#           toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools",
#           arguments={"blueprint": {"refPath": "/Game/Blueprints/TestActor.TestActor"}})
unreal.EditorAssetLibrary.save_asset(bp)

Rules:

  • Returns str or None — UE Python collapses bool Func(..., Out&) into one return value. This applies to every get_*_info method too (get_variable_info, get_function_info, get_component_info, get_node_details): they return the info struct or None, never a tuple.
  • Property names are the native C++ names: bReplicates, InitialLifeSpan, bCanBeDamaged — the b prefix is NOT stripped here (a stripped name returns None).
  • Values read back as strings: compare value == "True", not value is True.
  • On UE 5.3+, set_property maps the deprecated GameplayEffect CDO fields InheritableGameplayEffectTags and InheritableOwnedTagsContainer to their Asset Tags and Target Tags GameplayEffect Components. Writing only the legacy fields is otherwise erased by UGameplayEffect::PreSave.

⚠️ Info Struct Fields (don't guess — these are the complete lists)

get_blueprint_info(path) → BlueprintDetailedInfo: blueprint_name, blueprint_path, parent_class, is_widget_blueprint, variables, functions, components.

StructFields
BlueprintFunctionInfo (from list_functions)function_name, return_type, parameters, is_override, is_pure — there is no function_type and no input_parameters (that's the Detailed struct)
BlueprintFunctionDetailedInfo (from get_function_info)function_name, graph_guid, input_parameters, output_parameters, local_variables, is_override, is_pure, node_count — there is no return_type; return values are entries in output_parameters (each param: parameter_name, parameter_type, is_output, is_reference, default_value)
BlueprintVariableInfovariable_name, variable_type, category, is_public, is_exposed, default_value
BlueprintVariableDetailedInfo (from get_variable_info)variable_name, variable_type, type_path, category, default_value, tooltip, is_array, is_set, is_map, is_instance_editable, is_blueprint_read_only, is_expose_on_spawn, is_expose_to_cinematics, is_private, replication_condition — booleans use the is_ prefix (instance_editable alone raises AttributeError)
BlueprintLocalVariableInfo (in BlueprintFunctionDetailedInfo.local_variables)variable_name, friendly_name, variable_type, display_type, default_value, category, guid, is_const, is_reference, is_array, is_set, is_map — local variables are NOT parameters: there is no parameter_name
BlueprintPinInfo (from get_node_pins / node.pins)pin_name, pin_type, is_input, is_connected, default_value — direction is the single is_input bool; there is no is_output (use not pin.is_input)
BlueprintNodeTypeInfo (from discover_nodes)display_name, category, spawner_key, node_class, tooltip, is_pure, is_latent, keywords — the description text is tooltip, not description
BlueprintComponentInfocomponent_name, component_class, attach_parent, is_root_component, is_scene_component, is_inherited, children
BlueprintGraphInfo (from list_graphs)graph_name, graph_kind, node_count
BlueprintNodeInfo (from get_nodes_in_graph)node_id, node_type, node_title, pos_x, pos_y, pin_names, pins
BlueprintCompileResultsuccess, num_errors, num_warnings, errors, warnings
ComponentDetailedInfo (from get_component_info)name, display_name, class_path, category, parent_class, is_scene_component, is_primitive_component, property_count, function_count — there is no description
ComponentTypeInfo (from get_available_components)name, display_name, class_path, category, base_class, is_scene_component, is_primitive_component, is_abstract
ComponentPropertyInfo (from get_all_component_properties)property_name, property_type, category, value, is_editable, is_inherited

Component API quick reference (all on unreal.BlueprintService)

These signatures are complete — no need to discover_python_class them again:

MethodNotes
get_available_components(search_filter="", max_results=50) → [ComponentTypeInfo]Discover addable component types by partial name match
get_component_info(component_type) → ComponentDetailedInfo or NoneTakes ONLY the type name — no blueprint path
list_components(bp) / get_component_hierarchy(bp) → [BlueprintComponentInfo]Both return the same flat list — use attach_parent / children / is_root_component fields to render the tree
component_exists(bp, name) → boolFast idempotency check before adding
add_component(bp, type, name, parent_name="") → boolCompiles inline. See root behavior below
remove_component(bp, name, remove_children=True) → boolRemoves children too by default
reparent_component(bp, name, new_parent) → bool
set_root_component(bp, name) → boolNew root must be a SceneComponent. Old auto-generated DefaultSceneRoot is removed; an old user-created root and any other root-level scene components become children of the new root
get_component_property(bp, comp, prop) → str or None
set_component_property(bp, comp, prop, value) → boolvalue is a STRING — see formats below
get_all_component_properties(bp, comp, include_inherited=True) → [ComponentPropertyInfo]list_component_properties is an alias
compare_components(bp_a, comp_a, bp_b, comp_b) → str or NoneDiff as text; same or different blueprints
set_collision_settings(bp, comp, collision_enabled, object_type, collision_profile, channel_responses) → boolCollision lives in BodyInstance — set_component_property cannot reach it

Root component behavior: components added with no parent_name go to root level. The first scene component added this way replaces the auto-generated DefaultSceneRoot and becomes the root; later parentless scene components become floating siblings, NOT children of the root. To build a hierarchy, pass parent_name when adding, or use reparent_component / set_root_component.

⚠️ Property Value String Formats (set_component_property)

Values are strings in UE export-text syntax. Check property_type via get_all_component_properties before guessing a struct format:

Property typeFormat example
float / int32 / bool"5000.0", "25", "true"
FVector / FRotator"(X=0,Y=0,Z=50)", "(Pitch=0,Yaw=90,Roll=0)"
FColor (e.g. light LightColor)"(R=255,G=127,B=0,A=255)" — integer bytes 0–255
FLinearColor"(R=1.0,G=0.5,B=0.0,A=1.0)" — floats 0–1
Enum (e.g. IntensityUnits)"Candelas"

❌ Common mistake: LightColor is FColor, so passing LinearColor-style floats like "(R=1,G=0.5,B=0)" silently produces a nearly-black light (R=1 of 255), not orange.

⚠️ StateTree Dispatcher Variable Type

To add a StateTree delegate dispatcher variable to a Blueprint task (e.g. STT_*):

python
# Use type "FStateTreeDelegateDispatcher" — NOT "EventDispatcher".
# The engine's BlueprintTools.add_variable REJECTS this type ("Unknown type" — basic types
# only, live-verified); use the VibeUE struct-capable door instead:
unreal.BlueprintService.add_member_variable(bp_path, "FinishRotatingDispatcher", "FStateTreeDelegateDispatcher")
unreal.BlueprintEditorLibrary.compile_blueprint(unreal.EditorAssetLibrary.load_asset(bp_path))

"EventDispatcher" is a Blueprint-only concept and is not a valid type string. The correct type for StateTree delegate transitions is the USTRUCT FStateTreeDelegateDispatcher. After adding this variable, use bind_transition_to_delegate on the StateTree to link it to a transition — it matches the task by registered class name (STT_Rotate_C), not asset path.

Event Dispatchers (regular Blueprint multicast delegates)

Use the dedicated dispatcher API — NOT add_variable — to create the multicast delegates that appear in the Blueprint editor's Event Dispatchers section:

MethodWhat it does
add_event_dispatcher(bp_path, name)Adds the dispatcher (member variable + signature graph). Skeleton is recompiled inline; the dispatcher is callable immediately.
add_event_dispatcher_parameter(bp_path, name, param_name, param_type, is_array=False, container_type="")Adds a parameter to the dispatcher's signature. Subscribers receive these as inputs.
add_call_delegate_node(bp_path, graph, name, pos_x, pos_y)Spawns the "Call " broadcast node on a UK2Node_CallDelegate. Wire the broadcast into its execute pin.
remove_event_dispatcher(bp_path, name)Removes the dispatcher and its signature graph.
python
import unreal

bp = "/Game/StateTree/BP_Cube"

# 1. Create the dispatcher (no compile needed before placing nodes — skeleton is regenerated inline)
unreal.BlueprintService.add_event_dispatcher(bp, "FinishedLooking")

# 2. (Optional) Add parameters to the signature
unreal.BlueprintService.add_event_dispatcher_parameter(bp, "FinishedLooking", "Direction", "FRotator")

# 3. Spawn the Call node and wire something into its execute pin
call_id = unreal.BlueprintService.add_call_delegate_node(bp, "EventGraph", "FinishedLooking", 1400, -700)
unreal.BlueprintService.connect_nodes(bp, "EventGraph", timeline_id, "Finished", call_id, "execute")

# 4. Compile (engine BlueprintTools toolset) + save
# call_tool(tool_name="compile_blueprint",
#           toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools",
#           arguments={"blueprint": bp})
unreal.EditorAssetLibrary.save_asset(bp)

Pins on the Call node: execute (input exec), then (output exec), self (input target — defaulted to Self), plus one input pin per signature parameter.

Subscribing to it elsewhere:

  • Preferred — add_delegate_bind_on_variable(other_bp, graph, variable_name, "FinishedLooking", x, y) when the dispatcher lives on the class of an existing variable (e.g. Cube : BP_Cube_C). Mirrors add_function_call_on_variable: derives the owner class from the variable's type, creates the bind node + Get, and auto-wires Target. Pair with add_custom_event_node and wire CustomEvent.OutputDelegate → Bind.Delegate.
  • Lower-level — add_delegate_bind_node(other_bp, graph, "<owner class>", "FinishedLooking", x, y) when you have no variable to derive from. <owner class> accepts "Self", native class names (with/without U/A prefix), Blueprint asset paths (/Game/.../BP_Cube), or short BP names with/without _C (BP_Cube, BP_Cube_C). You wire the Target pin yourself.

Common mistakes:

  • ❌ add_variable(bp, "FinishedLooking", "EventDispatcher") — "EventDispatcher" is not a real type string; use add_event_dispatcher instead.
  • ❌ Treating the dispatcher like a regular variable (add_get_variable_node on it produces a delegate-getter, not a broadcast). The broadcast node is UK2Node_CallDelegate and is created only by add_call_delegate_node.
  • ❌ Skipping the compile before saving the asset. The skeleton compiles inline on dispatcher creation, but the final asset still needs the engine BlueprintTools.compile_blueprint toolset call before save_asset to ensure the generated class is up-to-date.

Blueprint Interfaces (implement / remove)

Add or remove a Blueprint Interface on a Blueprint class — the equivalent of Class Settings → Interfaces → Add in the editor.

MethodWhat it does
add_interface(bp_path, interface)Implements the interface on the Blueprint. No-op (returns True) if it is already implemented. Marks the Blueprint structurally modified and recompiles it inline.
remove_interface(bp_path, interface)Removes the interface from the Blueprint. Marks structurally modified and recompiles inline.
python
import unreal

bp = "/Game/Blueprints/BP_Player"

# Prefer the FULL interface asset path — most reliable resolution
unreal.BlueprintService.add_interface(bp, "/Game/Interfaces/BPI_Interactable")

# Short name also works IF the interface asset is already loaded in the editor
unreal.BlueprintService.add_interface(bp, "BPI_Interactable")

# Persist (both methods compile inline, but still save the asset)
unreal.EditorAssetLibrary.save_asset(bp)

# Remove it later
unreal.BlueprintService.remove_interface(bp, "BPI_Interactable")
unreal.EditorAssetLibrary.save_asset(bp)

Notes & gotchas:

  • Prefer the full asset path (e.g. /Game/Interfaces/BPI_Interactable). Short-name resolution only finds interface Blueprints that are already loaded in the editor; a full path always loads and resolves.
  • Both methods recompile the Blueprint inline (CompileBlueprint) but do not save it — call EditorAssetLibrary.save_asset(bp) afterward to persist.
  • add_interface is idempotent — calling it for an interface that is already implemented returns True without duplicating it.
  • After adding an interface, its functions appear as overridable events/functions; use override_function(bp_path, function_name) to implement them in the graph.
  • Both return bool (True on success). A False from add_interface means either the interface path could not be resolved, or it resolved to an asset that is not a Blueprint Interface (e.g. a normal Blueprint or one merely parented to UInterface) — check the log and pass a real Blueprint Interface asset path.
  • The interface must be a true Blueprint Interface (BPTYPE_Interface), created with BlueprintInterfaceFactory. add_interface validates this and returns False for non-interface classes rather than letting the compile fail.

Authoring the interface itself. A Blueprint Interface is nothing but its function graphs, so an empty one contributes nothing when implemented. Use create_function_graph() to define it — it is the counterpart to override_function(), which can only override a function that already exists:

python
BS = unreal.BlueprintService
BS.create_function_graph(bpi_path, "Interact")                                  # void -> an event on implementers
BS.create_function_graph(bpi_path, "GetDisplayName")                            # returns the graph name, "" on failure
BS.add_function_parameter(bpi_path, "GetDisplayName", "Name", "string", True)   # is_output=True -> a return value

BS.add_interface(bp_path, bpi_path)
BS.override_function(bp_path, "Interact")   # void interface fn -> event node in the EventGraph

create_function_graph() also works on a normal Blueprint (pass is_pure=True for a pure function). It returns "" and logs the reason for: an empty or non-identifier name, a name already used by any graph on the Blueprint, or a name that already exists on the parent hierarchy — that last case is override_function()'s job, and a shadowing graph would be a duplicate-function compile error.

A return-valued interface function is materialised as a graph on the implementer and shows up in list_graphs() as Interface (<InterfaceClass>).


Task Index

TaskWorkflowSample script (run via execute_python_code)
Create a Blueprint + variablesworkflows.md → Create a Blueprintscripts/create_blueprint.txt
Add components + set propertiesworkflows.md → Add componentsscripts/add_component.txt
Add an event dispatcher (delegate)workflows.md → Event dispatcherscripts/event_dispatcher.txt
Implement a Blueprint interfaceworkflows.md → Implement an interfacescripts/add_interface.txt
Inspect a Blueprint (components, parent class)introspection.md—
Node-level graph editingload the blueprint-graphs skill—

Sub-docs

  • workflows.md — step-by-step create/component/dispatcher/interface workflows with copy-paste Python.
  • introspection.md — what Blueprint introspection works/doesn't in UE 5.7 (CDO reads, parent class via asset tags, listing BPs by class, getting level actors).

Verification

After any edit: compile via the engine BlueprintTools.compile_blueprint toolset call (call_tool(tool_name="compile_blueprint", toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools", arguments={"blueprint": path})) and check the result's success / num_errors, then unreal.EditorAssetLibrary.save_asset(path). Don't claim success until compile reports zero errors.

Additional gotchas

  • SubobjectDataSubsystem is an ENGINE subsystem (get_engine_subsystem); get_object_for_blueprint lives on SubobjectDataBlueprintFunctionLibrary; SCS templates are named <Name>_GEN_VARIABLE; a SubobjectDataHandle has no is_valid() — test k2_find_subobject_data_from_handle(h) is not None.
  • set_collision_enabled(NO_COLLISION) on a spawned actor's component does not survive save/reload; set_collision_profile_name("NoCollision") serialises, and can_ever_affect_navigation=False persists.
  • Engine collision profiles predate custom trace channels, so a channel with no explicit entry falls back to the ini default response (BLOCK); a scripted volume or primitive must set each custom channel's response explicitly.

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 Blueprints AI skill do?

Create and modify Blueprint assets, variables, functions, components, event dispatchers, and interfaces. Use when the user asks to create a Blueprint/BP, add a variable or component to a Blueprint, make an event dispatcher/delegate, implement a Blueprint interface, or inspect a Blueprint's class/components. For node-level graph wiring, also load blueprint-graphs.

Why use Blueprints on TypingMind?

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

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/kevinpbuckley/VibeUE/tree/master/Content/Skills/blueprints. 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 Blueprints?

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 Blueprints?

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

Is the Blueprints 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 👇