mcpField
B
blender

blender

github.com/jablonkaiCreative & media451 installsUpdated today
Repository

About blender

blender is a skill for controlling a running Blender session through Blender Bridge and Python. It is intended for creators automating scene construction, animation, and rendering, with scene inspection and visual or geometry checks built into the workflow.

Key features

  1. 01Run Python inside Blender
  2. 02Inspect scene objects and settings
  3. 03Build re-runnable scene scripts
  4. 04Create viewport snapshots and renders
  5. 05Verify geometry with metrics

Use cases

  1. 01Script models, materials, and cameras
  2. 02Create keyframed or procedural animations
  3. 03Inspect geometry and iterate on a scene
  4. 04Render images and export scene files

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/jablonkai/skills --skill blender

After installing, type/blenderin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
animation-rigging.md
api-reference.md
geometry-nodes.md
gotchas.md
grease-pencil.md
io-formats.md
modeling.md
rendering.md
shading.md
simulation.md
vse.md
scripts/
blender-bridge.py
blender-send.sh
example-animated-logo.py
example-product-shot.py

Details

From the project README

Blender Control

Blender (/Applications/Blender.app) is scriptable in Python against bpy, bmesh and mathutils. Drive it through the Blender Bridge — a script running inside a running Blender GUI that executes whatever build script is POSTed to 127.0.0.1:8736. Because it runs in the live session on the main thread, bpy.data writes are legal, the viewport updates as you build, and viewport screenshots work. Everything documented here was executed and verified on Blender 5.2.2 LTS (bundled Python 3.13) — the current stable release; 5.2 is the LTS line, so this holds for every 5.2.x. Check version in the ping reply: on a newer release, trust the live session over these notes and probe before assuming.

Read references/gotchas.md before writing anything. Blender's API changed substantially in 5.x and most of what a model has memorised — action.fcurves, scene.node_tree, mod["Socket_2"], BLENDER_EEVEE_NEXT, use_nodes = True — is now wrong or deprecated.

The control loop

  1. User starts the bridge (one-time). This cannot be done remotely — if bash scripts/blender-send.sh --ping gets no answer, ask the user to do one of:

    • copy scripts/blender-bridge.py to ~/Library/Application Support/Blender/5.2/scripts/startup/blender_bridge.py (the folder is the running major.minor version) and restart Blender — it then starts automatically on every launch (recommended), or
    • open the Scripting workspace, paste the file into the text editor, press Run Script (⌥P) — lasts for that session, or
    • install it as an add-on via Preferences ▸ Add-ons ▸ Install.

    A successful ping returns {"ok": true, "bridge": "blender", "bridge_version": "1.2.0", "version": "5.2.2 LTS", ...}. An older bridge_version means the user's installed copy is stale — ask them to re-copy the file (older copies crash metrics() on cameras and empties).

  2. Look before you build: bash scripts/blender-send.sh --state returns objects and types, collections, materials, node groups, frame range, resolution, engine and whether the file has unsaved changes — cheaper than writing a script to ask.

  3. Write a build script to the scratchpad, starting from the cheatsheet below and the right reference file.

  4. Send it: OUT=/path/to/outdir bash scripts/blender-send.sh /path/build.py. The bridge runs it in the live session and returns the script's captured stdout; on error it returns the traceback and the sender exits non-zero. BLENDER_SEND_TIMEOUT=900 (seconds) for heavy renders and bakes. The script runs inside Blender, so your shell's environment does not reach it — parameterise a send with --arg quality=final --arg samples=128, read back as ARGS.get("quality", "preview") (values are strings).

  5. Feedback: returned stdout first; metrics(...) for structured geometry checks; snapshot(...) for a viewport PNG to Read; render(...) for the real thing.

  6. Iterate: inspect, fix, re-send. Scripts must be re-runnable — build inside stage("name") so a re-send replaces its own output instead of stacking duplicates.

Reference routing

TaskRead
Anything, before you startgotchas.md
Data-blocks, transforms, collections, depsgraph, scenesapi-reference.md
bmesh, modifiers, curves, text, UVs, booleansmodeling.md
Materials, shader nodes, textures, world/HDRI, lights, camerasshading.md
Procedural geometry, scattering, instancing, fields, zonesgeometry-nodes.md
Keyframes, F-curves, drivers, NLA, armatures, IK, shape keysanimation-rigging.md
Rigid body, cloth, soft body, particles, hair, fluid, bakessimulation.md
EEVEE/Cycles settings, passes, compositor, output, videorendering.md
2D / toon linework, Line Artgrease-pencil.md
Editing clips, titles, transitions, final cutvse.md
glTF, FBX, USD, Alembic, OBJ, STL, .blend append/linkio-formats.md

Injected namespace

Pre-imported: bpy, bmesh, mathutils, Vector, Matrix, Euler, Quaternion, math, os, json, plus OUT (from $OUT, also os.environ["OUT"]), ARGS (the --arg KEY=VALUE strings as a dict) and these helpers:

HelperDoes
stage(name)get-or-recreate a named collection, make it active — makes re-sends idempotent (also frees the meshes, materials and node groups its old objects leave orphaned)
sync()view_layer.update(); required before reading matrix_world
evaluated(ob)the depsgraph-evaluated object (modifier / geometry-nodes result)
frame(n)frame_set(n) + depsgraph update
metrics(objs, path=)dict + JSON: counts, verts/tris, world bbox, materials, frame range
snapshot(path, view=, shading=, fit=)viewport PNG (fast visual check)
render(path, engine=, samples=, ...)real EEVEE/Cycles still; restores every setting it touches
frame_view(objs, view=)aim the viewport (ISO/FRONT/TOP/CAMERA/…)
world_bounds(objs)world-space (min, max)
fcurves(ob) / fcurve(ob, path, i) / channelbag(ob)slotted-action F-curve access
gn_input(mod, name, value=, attribute=)read/set a geometry-nodes modifier input by name
ui_override(area)context override for the few bpy.ops that need an editor

Cheatsheet

Units are metres and radians. Prefer the data API; bpy.ops is for mode changes, smart_project, rigidbody.*, nla.bake, and importers/exporters.

Build geometry (re-runnable)
coll = stage("hero_v1")                       # owns its own output; safe to re-send
me = bpy.data.meshes.new("Body")
bm = bmesh.new()
bmesh.ops.create_cube(bm, size=2.0)
bmesh.ops.bevel(bm, geom=list(bm.edges), offset=0.06, segments=3, affect="EDGES")
bm.to_mesh(me); bm.free()
ob = bpy.data.objects.new("Body", me)
coll.objects.link(ob)
ob.location = (0, 0, 1)
sync()                                        # before ANY matrix_world read
Material
m = bpy.data.materials.new("Shell")
b = m.node_tree.nodes["Principled BSDF"]
b.inputs["Base Color"].default_value = (0.75, 0.2, 0.15, 1.0)
b.inputs["Roughness"].default_value = 0.35
b.inputs["Metallic"].default_value = 0.0      # no "Specular"/"Emission" inputs since 4.0
ob.data.materials.append(m)
Camera aimed at the subject
cd = bpy.data.cameras.new("Cam"); cd.lens = 50
cam = bpy.data.objects.new("Cam", cd); coll.objects.link(cam)
cam.location = (6, -6, 4)
cam.rotation_euler = (Vector((0, 0, 1)) - cam.location).to_track_quat("-Z", "Y").to_euler()
bpy.context.scene.camera = cam
Feedback
m = metrics([ob], path=OUT + "/metrics.json")
print("METRICS", json.dumps(m))               # comes straight back through the sender
print(snapshot(OUT + "/check.png", view="ISO"))
print(render(OUT + "/hero.png", engine="BLENDER_EEVEE", samples=64,
             width=1280, height=720))
Keyframes
for f, z in ((1, 0.0), (24, 3.0), (48, 0.0)):
    ob.location.z = z
    ob.keyframe_insert("location", index=2, frame=f)
for kp in fcurves(ob)[0].keyframe_points:     # NOT action.fcurves — that is gone
    kp.interpolation = "BEZIER"
    kp.easing = "EASE_IN_OUT"
Export
bpy.context.view_layer.objects.active = ob; ob.select_set(True)
bpy.ops.export_scene.gltf(filepath=OUT + "/hero.glb", export_format="GLB",
                          use_selection=True)
bpy.ops.wm.obj_export(filepath=OUT + "/hero.obj", export_selected_objects=True)
bpy.ops.wm.save_as_mainfile(filepath=OUT + "/hero.blend", copy=True)

Verification

  • Returned stdout is the immediate signal — print(...) comes straight back.
  • metrics(...) is the structured check (object/vert/tri counts, world bbox, materials). Write it to $OUT/metrics.json and Read it to confirm geometry without eyeballing.
  • snapshot(...) is the fast visual check — a viewport OpenGL PNG, no full render. shading="RENDERED" previews materials and lights.
  • render(...) for the deliverable. It raises a diagnostic RuntimeError if Blender reported success but wrote nothing. It restores every setting it touched afterwards — so when the user also wants a .blend that re-renders the same shot (F12), set resolution, engine and samples on the scene yourself before saving.
  • Hand-off: a .glb/.blend in $OUT opens in whatever the user already has.

Security

Installing this skill means running a code-execution server on the user's machine. Say so before asking them to start the bridge.

  • The bridge binds 127.0.0.1:8736 (BLENDER_BRIDGE_PORT) and executes any Python POSTed to /run inside the live session — the user's privileges, the user's open file. Requests carry no authentication: every local process, and every other user on a shared machine, can drive Blender through it.
  • Web pages cannot. Requests carrying an Origin header or a cross-site Sec-Fetch-Site are rejected with 403, so a page in the user's browser can't reach the bridge. That check is the only gate — there is no token.
  • The install choice decides how long the port stays open. Scripting workspace ▸ Run Script lasts until Blender quits. The scripts/startup/ install is recommended above for convenience, but it makes every Blender launch listen, whether or not an agent is driving it — offer the trade-off rather than assuming it.
  • To stop the bridge, quit Blender. There is no remote shutdown; the port is released with the process.

Safety

  • The open file may hold unsaved work. Never call bpy.ops.wm.read_homefile() or bpy.data.batch_remove without asking. stage() is the non-destructive default; it isolates geometry but not scene-level settings.
  • Renders, bakes and sims block the main thread — the UI freezes until they finish. Iterate small, and warn the user before anything long.
  • Save into $OUT, not over the user's .blend; save_as_mainfile(..., copy=True) leaves the live session pointed at their own file.

Comments

Markdown supported