OpenParable
Skip to content
← Back to skills

blueprints

Development

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.

View on GitHub ↗
QUICK START

How to use this skill

Bring this guide into your coding agent with a prompt tailored to the tool you use.

  1. Open your project in Codex.
  2. Copy and paste the prompt below.
  3. Review files and risks before approving any action.
Prompt to paste
Inspect the Agent Skill at https://github.com/kevinpbuckley/VibeUE/blob/HEAD/Content/Skills/blueprints/SKILL.md and all supporting files. Report provenance, permissions, scripts, dependencies, and security risks. Propose an exact installation into .agents/skills/blueprints, but wait for my approval before writing files or running scripts.

Copying this prompt does not install or run the skill. Review third-party files before use.

🧠 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

āš ļø 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:

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

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

# 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}:

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

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.

āš ļø 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:

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:

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.

āš ļø 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_*):

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

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.