🧠 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
| WRONG |
CORRECT |
get_component_info(path, name) |
get_component_info(type) - takes ONLY type! |
⚠️ Property Name Gotchas
| WRONG |
CORRECT |
info.inputs |
info.input_parameters |
var.name |
var.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++ name |
Python name |
bAlreadyOverridden |
already_overridden |
bIsEventStyle |
is_event_style |
bIsNativeEvent |
is_native_event |
bEnabled |
enabled |
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.
- 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.
| Struct |
Fields |
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) |
BlueprintVariableInfo |
variable_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 |
BlueprintComponentInfo |
component_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 |
BlueprintCompileResult |
success, 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:
| Method |
Notes |
get_available_components(search_filter="", max_results=50) → [ComponentTypeInfo] |
Discover addable component types by partial name match |
get_component_info(component_type) → ComponentDetailedInfo or None |
Takes 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) → bool |
Fast idempotency check before adding |
add_component(bp, type, name, parent_name="") → bool |
Compiles inline. See root behavior below |
remove_component(bp, name, remove_children=True) → bool |
Removes children too by default |
reparent_component(bp, name, new_parent) → bool |
|
set_root_component(bp, name) → bool |
New 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) → bool |
value 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 None |
Diff as text; same or different blueprints |
set_collision_settings(bp, comp, collision_enabled, object_type, collision_profile, channel_responses) → bool |
Collision 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 type |
Format 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:
| Method |
What 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.
| Method |
What 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
| Task |
Workflow |
Sample script (run via execute_python_code) |
| Create a Blueprint + variables |
workflows.md → Create a Blueprint |
scripts/create_blueprint.txt |
| Add components + set properties |
workflows.md → Add components |
scripts/add_component.txt |
| Add an event dispatcher (delegate) |
workflows.md → Event dispatcher |
scripts/event_dispatcher.txt |
| Implement a Blueprint interface |
workflows.md → Implement an interface |
scripts/add_interface.txt |
| Inspect a Blueprint (components, parent class) |
introspection.md |
— |
| Node-level graph editing |
load 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.
1---2name: blueprints3description: 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.4---56> 🧠 **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.78# Blueprint System Skill910> **For node-level graph editing** (adding nodes, connecting pins, wiring logic, timers, layout), load the `blueprint-graphs` skill.1112## Critical Rules1314### ⚠️ Engine `BlueprintTools` args are `{refPath}` objects, NOT strings1516Every UObject/UClass argument to the engine `BlueprintTools` toolset (`blueprint`, `asset_type`,17`graph`, `parent_class`, …) is a **reference object** `{"refPath": "<object path>"}`, and these tools18**return** the same shape. Passing a plain string fails with19`"<x> is not a valid object path for property '<arg>'"`. This rule is **only** for the engine20`BlueprintTools` `call_tool` path — VibeUE's own `unreal.BlueprintService.*` Python methods still take21plain string paths.2223The `refPath` must be the **full object path** — `/Game/Dir/Asset.Asset` (the asset name repeated24after the dot). A bare **package** path (`/Game/Dir/Asset`, no `.Asset`) is rejected. Two safe ways25to get one:2627```python28# (a) Reuse what create/load returns — it is already a {refPath} object:29bp_ref = call_tool("create", "editor_toolset.toolsets.blueprint.BlueprintTools",30 {"folder_path": "/Game/BP", "asset_name": "BP_X",31 "asset_type": {"refPath": "/Script/Engine.Actor"}})["returnValue"]3233# (b) Build one from a package path you already have:34def bp_ref_of(p): # "/Game/Dir/BP_X" -> {"refPath": "/Game/Dir/BP_X.BP_X"}35 name = p.rsplit("/", 1)[-1].split(".")[0]36 return {"refPath": p if "." in p.rsplit("/", 1)[-1] else f"{p}.{name}"}37```3839All the `arguments={"blueprint": path, ...}` / `{"blueprint": bp, ...}` examples in the sub-docs are40shorthand — wrap the path with `bp_ref_of(...)` (or reuse the returned ref) when you actually call.41On success these tools return `{"returnValue": null}`; an error comes back as a `Parameter error`.4243### ⚠️ Creating a Blueprint — engine `BlueprintTools.create`4445Blueprint basics now live on the engine's `BlueprintTools` toolset (VibeUE extends the native MCP46endpoint). Create a Blueprint with `call_tool`, not `unreal.BlueprintService.create_blueprint` (that47method was cut):4849```python50# CORRECT — folder_path + asset_name are strings; asset_type is a {refPath} class reference.51result = call_tool(52 tool_name="create",53 toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools",54 arguments={"folder_path": "/Game/Blueprints", "asset_name": "BP_MyActor",55 "asset_type": {"refPath": "/Script/Engine.Actor"}},56)57# -> {"returnValue": {"refPath": "/Game/Blueprints/BP_MyActor.BP_MyActor"}}58bp_ref = result["returnValue"] # reuse this {refPath} object for later BlueprintTools calls59```6061The folder and asset name are passed separately (`folder_path` + `asset_name`) — do not jam a full62asset path into one argument. `asset_type` is the parent **class** as a `{refPath}`:63`/Script/Engine.Actor`, `/Script/Engine.Pawn`, `/Script/Engine.Character`, or a Blueprint's generated64class `"/Game/.../BP_Base.BP_Base_C"`.6566### Blueprint Types in add_variable — engine `BlueprintTools.add_variable`6768Adding a variable is also an engine `BlueprintTools` call now (`unreal.BlueprintService.add_variable`69was cut). Use `call_tool` with `blueprint`, `name`, `type_name` (and `add_object_variable` /70`add_struct_variable` for object- and struct-typed variables):7172```python73# CORRECT — blueprint is a {refPath} object; name/type_name are strings74call_tool(75 tool_name="add_variable",76 toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools",77 arguments={"blueprint": {"refPath": "/Game/BP_MyActor.BP_MyActor"}, "name": "Health", "type_name": "float"},78)79```8081When the variable's type is a **Blueprint class**, use the Blueprint asset name directly in82`type_name`. Do **not** add `_C`, and do **not** construct a generated class path — just use the name83(`"BP_Enemy"`, or the full `"/Game/Blueprints/BP_Enemy"`). Appending `_C` (`"BP_Enemy_C"`) or guessing84a generated class path is **wrong**; the type system resolves Blueprint names automatically via asset85search.8687Engine structs need the `F` prefix: color variables are `"FLinearColor"` / `"FColor"` —88plain `"LinearColor"` / `"Color"` is not resolved. Unsure of a type string? Use the surviving89`unreal.BlueprintService.search_variable_types("Linear")` to look it up.9091**Supported primitive `type_name` values (exact):** `bool`, `int`, `float`, `byte`, `name`, `string`,92`text`, `Vector`, `Rotator`, `Transform`, `Vector2D`, `LinearColor`. Use `int` (**not** `int32`) and93`float` (**not** `double`) — `add_variable` rejects `int32`/`double` with94*"Unknown type 'int32'. Supported: …"*. For other engine structs (e.g. `FColor`, `FGameplayTag`) use95`add_struct_variable` with `struct_type`; for object/class refs use `add_object_variable` with96`object_class` as a `{refPath}`:9798```python99# Object-reference variable — object_class is a {refPath} to the class100call_tool("add_object_variable", "editor_toolset.toolsets.blueprint.BlueprintTools",101 {"blueprint": {"refPath": "/Game/BP_MyActor.BP_MyActor"}, "name": "DeathFX",102 "object_class": {"refPath": "/Script/Niagara.NiagaraSystem"}})103```104105### Struct/object/enum types from Python — `add_member_variable` (VibeUE delta)106107From inside `execute_python_code`, `unreal.BlueprintService.add_member_variable(bp_path, name,108type_string, default="", is_array=False, container_type="")` creates a member variable with full109type-string support — structs, objects, enums, and containers — in one batchable Python call110(no `call_tool` round-trip). It takes the same type strings as `add_function_local_variable`111(`"float"`, `"FVector"`, `"FStateTreeDelegateDispatcher"`, `"AActor"`):112113```python114unreal.BlueprintService.add_member_variable(bp_path, "FinishRotatingDispatcher", "FStateTreeDelegateDispatcher")115unreal.BlueprintService.add_member_variable(bp_path, "Waypoints", "FVector", "", True) # FVector array116unreal.BlueprintEditorLibrary.compile_blueprint(unreal.EditorAssetLibrary.load_asset(bp_path))117```118119Returns `False` (with the reason in the log) on a duplicate name or an unresolvable type string.120Compile after adding — the variable is registered but the class is only rebuilt on compile.121122### ⚠️ Adding a function graph — engine `BlueprintTools.add_function_graph`123124Creating a function graph moved to the engine toolset (`create_function` / `add_function` on125`BlueprintService` were cut). Use `call_tool` with `blueprint` + `graph_name`:126127```python128call_tool(129 tool_name="add_function_graph",130 toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools",131 arguments={"blueprint": {"refPath": "/Game/Blueprints/BP_MyActor.BP_MyActor"}, "graph_name": "DoThing"},132)133```134135Refining the graph afterward still uses the surviving VibeUE delta methods136(`add_function_parameter`, `add_function_local_variable`, `get_function_parameters`,137`get_function_info`, `override_function`, `open_function_graph`).138139### ⚠️ Method Name Gotchas140141| WRONG | CORRECT |142|-------|---------|143| `get_component_info(path, name)` | `get_component_info(type)` - takes ONLY type! |144145### ⚠️ Property Name Gotchas146147| WRONG | CORRECT |148|-------|---------|149| `info.inputs` | `info.input_parameters` |150| `var.name` | `var.variable_name` |151152### ⚠️ Python Naming: Boolean `b` Prefix Is Stripped153154UE Python **strips the lowercase `b` prefix** from boolean properties. Always use the name without it:155156| C++ name | Python name |157|---|---|158| `bAlreadyOverridden` | `already_overridden` |159| `bIsEventStyle` | `is_event_style` |160| `bIsNativeEvent` | `is_native_event` |161| `bEnabled` | `enabled` |162163Do **not** use `b_already_overridden`, `b_is_event_style`, etc. — these will raise `AttributeError`.164165**Exception:** the string-based CDO accessors `get_property` / `set_property` take the **native C++166property name**, so booleans KEEP the `b` prefix there (`"bReplicates"`, not `"replicates"`). See the167next section.168169### ⚠️ CDO Properties: `get_property` / `set_property` (replication, lifespan, etc.)170171Use these to read/write Class Default Object properties like replication or initial lifespan:172173```python174import unreal175bp = "/Game/Blueprints/TestActor"176177# READ — returns a single value: str, or None if the property wasn't found. NOT a (success, value) tuple!178value = unreal.BlueprintService.get_property(bp, "bReplicates") # "True" / "False" (a STRING)179life = unreal.BlueprintService.get_property(bp, "InitialLifeSpan") # "5.000000"180181# WRONG — raises ValueError: too many values to unpack182# success, value = unreal.BlueprintService.get_property(bp, "bReplicates")183184# WRITE — values are strings; returns bool185unreal.BlueprintService.set_property(bp, "bReplicates", "True")186187# Compile via the engine BlueprintTools toolset (compile_blueprint was cut from BlueprintService):188# call_tool(tool_name="compile_blueprint",189# toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools",190# arguments={"blueprint": {"refPath": "/Game/Blueprints/TestActor.TestActor"}})191unreal.EditorAssetLibrary.save_asset(bp)192```193194Rules:195- Returns **str or None** — UE Python collapses `bool Func(..., Out&)` into one return value. This196 applies to every `get_*_info` method too (`get_variable_info`, `get_function_info`,197 `get_component_info`, `get_node_details`): they return the info struct or `None`, never a tuple.198- Property names are the **native C++ names**: `bReplicates`, `InitialLifeSpan`, `bCanBeDamaged` —199 the `b` prefix is NOT stripped here (a stripped name returns `None`).200- Values read back as strings: compare `value == "True"`, not `value is True`.201- On UE 5.3+, `set_property` maps the deprecated GameplayEffect CDO fields202 `InheritableGameplayEffectTags` and `InheritableOwnedTagsContainer` to their Asset Tags and203 Target Tags GameplayEffect Components. Writing only the legacy fields is otherwise erased by204 `UGameplayEffect::PreSave`.205206### ⚠️ Info Struct Fields (don't guess — these are the complete lists)207208`get_blueprint_info(path)` → **BlueprintDetailedInfo**: `blueprint_name`, `blueprint_path`,209`parent_class`, `is_widget_blueprint`, `variables`, `functions`, `components`.210211| Struct | Fields |212|---|---|213| `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) |214| `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`) |215| `BlueprintVariableInfo` | `variable_name`, `variable_type`, `category`, `is_public`, `is_exposed`, `default_value` |216| `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**) |217| `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`** |218| `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`) |219| `BlueprintNodeTypeInfo` (from `discover_nodes`) | `display_name`, `category`, `spawner_key`, `node_class`, `tooltip`, `is_pure`, `is_latent`, `keywords` — the description text is `tooltip`, **not `description`** |220| `BlueprintComponentInfo` | `component_name`, `component_class`, `attach_parent`, `is_root_component`, `is_scene_component`, `is_inherited`, `children` |221| `BlueprintGraphInfo` (from `list_graphs`) | `graph_name`, `graph_kind`, `node_count` |222| `BlueprintNodeInfo` (from `get_nodes_in_graph`) | `node_id`, `node_type`, `node_title`, `pos_x`, `pos_y`, `pin_names`, `pins` |223| `BlueprintCompileResult` | `success`, `num_errors`, `num_warnings`, `errors`, `warnings` |224| `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`** |225| `ComponentTypeInfo` (from `get_available_components`) | `name`, `display_name`, `class_path`, `category`, `base_class`, `is_scene_component`, `is_primitive_component`, `is_abstract` |226| `ComponentPropertyInfo` (from `get_all_component_properties`) | `property_name`, `property_type`, `category`, `value`, `is_editable`, `is_inherited` |227228### Component API quick reference (all on `unreal.BlueprintService`)229230These signatures are complete — no need to `discover_python_class` them again:231232| Method | Notes |233|---|---|234| `get_available_components(search_filter="", max_results=50)` → `[ComponentTypeInfo]` | Discover addable component types by partial name match |235| `get_component_info(component_type)` → `ComponentDetailedInfo` or `None` | Takes ONLY the type name — no blueprint path |236| `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 |237| `component_exists(bp, name)` → `bool` | Fast idempotency check before adding |238| `add_component(bp, type, name, parent_name="")` → `bool` | Compiles inline. See root behavior below |239| `remove_component(bp, name, remove_children=True)` → `bool` | Removes children too by default |240| `reparent_component(bp, name, new_parent)` → `bool` | |241| `set_root_component(bp, name)` → `bool` | New 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 |242| `get_component_property(bp, comp, prop)` → `str` or `None` | |243| `set_component_property(bp, comp, prop, value)` → `bool` | `value` is a STRING — see formats below |244| `get_all_component_properties(bp, comp, include_inherited=True)` → `[ComponentPropertyInfo]` | `list_component_properties` is an alias |245| `compare_components(bp_a, comp_a, bp_b, comp_b)` → `str` or `None` | Diff as text; same or different blueprints |246| `set_collision_settings(bp, comp, collision_enabled, object_type, collision_profile, channel_responses)` → `bool` | Collision lives in `BodyInstance` — `set_component_property` cannot reach it |247248**Root component behavior**: components added with no `parent_name` go to root level. The first249scene component added this way replaces the auto-generated DefaultSceneRoot and becomes the root;250later parentless scene components become floating siblings, NOT children of the root. To build a251hierarchy, pass `parent_name` when adding, or use `reparent_component` / `set_root_component`.252253### ⚠️ Property Value String Formats (`set_component_property`)254255Values are strings in UE export-text syntax. Check `property_type` via256`get_all_component_properties` before guessing a struct format:257258| Property type | Format example |259|---|---|260| `float` / `int32` / `bool` | `"5000.0"`, `"25"`, `"true"` |261| `FVector` / `FRotator` | `"(X=0,Y=0,Z=50)"`, `"(Pitch=0,Yaw=90,Roll=0)"` |262| **`FColor`** (e.g. light `LightColor`) | `"(R=255,G=127,B=0,A=255)"` — **integer bytes 0–255** |263| **`FLinearColor`** | `"(R=1.0,G=0.5,B=0.0,A=1.0)"` — floats 0–1 |264| Enum (e.g. `IntensityUnits`) | `"Candelas"` |265266❌ Common mistake: `LightColor` is `FColor`, so passing LinearColor-style floats like267`"(R=1,G=0.5,B=0)"` silently produces a nearly-black light (R=1 of 255), not orange.268269### ⚠️ StateTree Dispatcher Variable Type270271To add a StateTree delegate dispatcher variable to a Blueprint task (e.g. `STT_*`):272273```python274# Use type "FStateTreeDelegateDispatcher" — NOT "EventDispatcher".275# The engine's BlueprintTools.add_variable REJECTS this type ("Unknown type" — basic types276# only, live-verified); use the VibeUE struct-capable door instead:277unreal.BlueprintService.add_member_variable(bp_path, "FinishRotatingDispatcher", "FStateTreeDelegateDispatcher")278unreal.BlueprintEditorLibrary.compile_blueprint(unreal.EditorAssetLibrary.load_asset(bp_path))279```280281`"EventDispatcher"` is a Blueprint-only concept and is not a valid type string. The correct type282for StateTree delegate transitions is the USTRUCT `FStateTreeDelegateDispatcher`.283After adding this variable, use `bind_transition_to_delegate` on the StateTree to link it to a284transition — it matches the task by **registered class name** (`STT_Rotate_C`), not asset path.285286### Event Dispatchers (regular Blueprint multicast delegates)287288Use the dedicated dispatcher API — **NOT** `add_variable` — to create the multicast delegates that appear in the Blueprint editor's *Event Dispatchers* section:289290| Method | What it does |291|---|---|292| `add_event_dispatcher(bp_path, name)` | Adds the dispatcher (member variable + signature graph). Skeleton is recompiled inline; the dispatcher is callable immediately. |293| `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. |294| `add_call_delegate_node(bp_path, graph, name, pos_x, pos_y)` | Spawns the "Call <Name>" broadcast node on a `UK2Node_CallDelegate`. Wire the broadcast into its `execute` pin. |295| `remove_event_dispatcher(bp_path, name)` | Removes the dispatcher and its signature graph. |296297```python298import unreal299300bp = "/Game/StateTree/BP_Cube"301302# 1. Create the dispatcher (no compile needed before placing nodes — skeleton is regenerated inline)303unreal.BlueprintService.add_event_dispatcher(bp, "FinishedLooking")304305# 2. (Optional) Add parameters to the signature306unreal.BlueprintService.add_event_dispatcher_parameter(bp, "FinishedLooking", "Direction", "FRotator")307308# 3. Spawn the Call node and wire something into its execute pin309call_id = unreal.BlueprintService.add_call_delegate_node(bp, "EventGraph", "FinishedLooking", 1400, -700)310unreal.BlueprintService.connect_nodes(bp, "EventGraph", timeline_id, "Finished", call_id, "execute")311312# 4. Compile (engine BlueprintTools toolset) + save313# call_tool(tool_name="compile_blueprint",314# toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools",315# arguments={"blueprint": bp})316unreal.EditorAssetLibrary.save_asset(bp)317```318319**Pins on the Call node:** `execute` (input exec), `then` (output exec), `self` (input target — defaulted to Self), plus one input pin per signature parameter.320321**Subscribing to it elsewhere:**322- **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`.323- **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.324325**Common mistakes:**326327- ❌ `add_variable(bp, "FinishedLooking", "EventDispatcher")` — `"EventDispatcher"` is not a real type string; use `add_event_dispatcher` instead.328- ❌ 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`.329- ❌ 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.330331### Blueprint Interfaces (implement / remove)332333Add or remove a Blueprint Interface on a Blueprint class — the equivalent of *Class Settings → Interfaces → Add* in the editor.334335| Method | What it does |336|---|---|337| `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. |338| `remove_interface(bp_path, interface)` | Removes the interface from the Blueprint. Marks structurally modified and recompiles inline. |339340```python341import unreal342343bp = "/Game/Blueprints/BP_Player"344345# Prefer the FULL interface asset path — most reliable resolution346unreal.BlueprintService.add_interface(bp, "/Game/Interfaces/BPI_Interactable")347348# Short name also works IF the interface asset is already loaded in the editor349unreal.BlueprintService.add_interface(bp, "BPI_Interactable")350351# Persist (both methods compile inline, but still save the asset)352unreal.EditorAssetLibrary.save_asset(bp)353354# Remove it later355unreal.BlueprintService.remove_interface(bp, "BPI_Interactable")356unreal.EditorAssetLibrary.save_asset(bp)357```358359**Notes & gotchas:**360361- **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.362- Both methods **recompile the Blueprint inline** (`CompileBlueprint`) but do **not** save it — call `EditorAssetLibrary.save_asset(bp)` afterward to persist.363- `add_interface` is idempotent — calling it for an interface that is already implemented returns `True` without duplicating it.364- After adding an interface, its functions appear as overridable events/functions; use `override_function(bp_path, function_name)` to implement them in the graph.365- 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.366- 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.367368---369370## Task Index371372| Task | Workflow | Sample script (run via `execute_python_code`) |373|------|----------|-----------------------------------------------|374| Create a Blueprint + variables | `workflows.md` → Create a Blueprint | `scripts/create_blueprint.txt` |375| Add components + set properties | `workflows.md` → Add components | `scripts/add_component.txt` |376| Add an event dispatcher (delegate) | `workflows.md` → Event dispatcher | `scripts/event_dispatcher.txt` |377| Implement a Blueprint interface | `workflows.md` → Implement an interface | `scripts/add_interface.txt` |378| Inspect a Blueprint (components, parent class) | `introspection.md` | — |379| Node-level graph editing | load the **`blueprint-graphs`** skill | — |380381## Sub-docs382383- **`workflows.md`** — step-by-step create/component/dispatcher/interface workflows with copy-paste Python.384- **`introspection.md`** — what Blueprint introspection works/doesn't in UE 5.7 (CDO reads, parent class via385 asset tags, listing BPs by class, getting level actors).386387## Verification388389After any edit: compile via the engine `BlueprintTools.compile_blueprint` toolset call390(`call_tool(tool_name="compile_blueprint", toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools", arguments={"blueprint": path})`)391and check the result's `success` / `num_errors`, then `unreal.EditorAssetLibrary.save_asset(path)`.392Don't claim success until compile reports zero errors.