| name | blender-python-execution |
| description | Execute and validate Blender Python through a Blender MCP provider using small, context-safe, idempotent transactions. Use when an agent must inspect or mutate a Blender scene, translate a Blender task into bpy code, recover from Blender code execution errors, or prove that an MCP-driven edit actually changed the intended scene. |
Blender Python execution
Use the provider's Blender tools as a transaction loop: inspect, execute one
bounded change, inspect structured state, then inspect pixels when the result is
visual. This skill owns execution discipline, not modeling or look-development
decisions.
Discover the adapter
The known adapter exposes these schemas:
get_scene_info()
execute_blender_code(code)
get_object_info(object_name)
get_viewport_screenshot(max_size=800)
Tool prefixes and provider aliases are discoverable and may differ. Match tools
by schema and behavior; do not guess an endpoint or port. Read
references/mcp-call-contract.md before the first mutation.
Run the transaction loop
- Call
get_scene_info() before executing code. Identify the current file,
scene, mode, active object, camera, engine, and objects in scope. Preserve
existing content unless the user explicitly asks to replace it.
- Run the read-only code in
scripts/scene_probe.py through
execute_blender_code(code) when the provider's scene summary is incomplete.
Use scripts/scene_audit.py before rendering, export, or handoff. To prove
camera coverage, prepend
BLENDER_AUDIT_REQUEST = {"targets": ["ROOT-subject"], "margin": 0.08, "include_descendants": True, "include_instances": True}
when executing through MCP, or pass --targets GEO-subject --margin 0.08
after Blender's -- delimiter; use --no-descendants or --no-instances
only when those bounds are intentionally excluded. A camera merely existing is not a framing
pass.
- Define one transaction with named inputs, stable target names, a narrow
mutation, and an expected postcondition. Prefer Blender's data API. Use an
operator only when its context is prepared explicitly.
- Send a small, self-contained code string to
execute_blender_code(code). Every call has a fresh Python namespace:
re-import modules and reacquire datablocks from bpy.data by name.
- End the code with one JSON object on standard output. Include
ok,
operation, target names, changed values or counts, and any warnings. Do not
print credentials, environment variables, or unrelated paths.
- Call
get_object_info(object_name) for every primary target and
get_scene_info() after scene-wide changes. Check the expected state rather
than treating a successful tool response as proof.
- For visible changes, call
get_viewport_screenshot(max_size=800) or inspect
the requested render. Verify framing, geometry, materials, and lighting. A
screenshot can be stale or produced by another engine; it does not by itself
prove that OVRTX rendered or accepted an edit.
Execution rules
- Never use
time.sleep() or a polling loop inside Blender code. Return control
to the agent and poll with provider/status tools outside Blender.
- Keep each call bounded. Split creation, materials, lighting, rendering, and
export into independently verifiable transactions.
- Use stable names and explicit ownership. Do not rely on selection order,
default names, or Python variables from a previous call.
- Make mutations idempotent: get-or-create owned datablocks, set requested
values absolutely, and avoid duplicate modifiers, links, handlers, or nodes.
- Call
bpy.context.view_layer.update() before reading evaluated transforms,
bounds, or dependency-graph results.
- Prefer direct data access. When an operator is necessary, set mode, selection,
and active object explicitly or use
bpy.context.temp_override(...) with a
verified area/region. Restore user-visible context when practical.
- Do not delete broadly, reset the scene or World, open another file, save over
the source, enable scripts, install add-ons, or render to an arbitrary path
unless the user requested that exact action.
- Treat external
.blend, USD, archives, scripts, drivers, and add-ons as
untrusted content. Opening or enabling them is outside this execution skill.
Mutation shape
Use the patterns in references/python-transaction-patterns.md. A minimal
transaction has this form:
import bpy, json
name = "GEO-requested-target"
obj = bpy.data.objects.get(name)
if obj is None:
raise RuntimeError(f"missing target: {name}")
obj.hide_render = False
bpy.context.view_layer.update()
print(json.dumps({
"ok": True,
"operation": "set_render_visibility",
"object": obj.name,
"hide_render": obj.hide_render,
}, sort_keys=True))
Failure handling
- On a traceback, report the exception and failing line, reduce the transaction,
reacquire state, and retry only the failed operation.
- On timeout, do not resend the same large script. Inspect the scene, split the
work, and remove blocking waits or expensive loops.
- If structured state changed but pixels did not, check camera, view layer,
visibility, dependency-graph evaluation, active engine, and stale viewport
state before repeating the mutation. Use the interactive redraw/frame pattern
in
references/python-transaction-patterns.md, return control, then capture a
new screenshot; use a render when background mode or freshness matters.
- For reproducible render-camera coverage, route to
blender-camera-framing.
Viewport view_selected changes only RegionView3D and cannot prove that a
render camera contains the subject.
- If the provider lacks code execution, do not use shell Blender as a hidden
substitute for mutating the user's live session. Stop and report the missing
capability. A background Blender script is allowed only when the user
explicitly authorizes an offline/caller-owned derivative or the routed
workflow already declares background execution for a large build, frame
loop, render, or round-trip test; keep it isolated from the live source.
Completion gate
Report success only when the requested postcondition passes structured
inspection and every visual claim has current visual evidence. State the active
engine and label screenshots, Blender renders, native renderer products, and
postprocessed images separately.