Skip to content

Use cases

Seven things you can build that you couldn’t with a single-LLM, single-Blender setup. Each pattern shows who does what and which tools carry the load. Direct dispatch tools (blender_get_scene_info, blender_execute_code, blender_download_polyhaven_asset, etc.) handle the common cases; blender_send_message + job_dispatch is the escape hatch when a pattern needs script flexibility or custom subscription.

Different LLM clients with different specialties on the same Blender scene. A modeling-focused LLM builds geometry; a shading-focused LLM writes node graphs; a lighting LLM places HDRIs.

Who:

  • llm-modeler (LLM session, capabilities ["modeling", "geometry"])
  • llm-shader (LLM session, capabilities ["shading", "materials"])
  • llm-lighter (LLM session, capabilities ["lighting", "hdri"])
  • blender-main (Blender peer, capabilities ["python_execution", "modeling", "rendering"])

Flow:

  • Modeler calls blender_get_scene_info() to read the current scene, then blender_execute_code(code="import bpy; bpy.ops.mesh.primitive_cube_add(...)") to add geometry.
  • Shader calls blender_get_object_info(name="Cube") to discover existing materials, then dispatches its node-graph script via blender_execute_code.
  • Lighter calls blender_search_polyhaven_assets(asset_type="hdris", categories="outdoor") then blender_download_polyhaven_asset(...) — the addon handles the import.

Why MCP bus: previously this meant three separate Claude sessions each holding a full Blender context. With the bus they all see the same scene; with direct dispatch tools, each LLM just makes normal tool calls and gets JSON back.

Many Blender peers in one logical group, dispatched in parallel for a batch render.

Who:

  • llm-coordinator (LLM session)
  • blender-farm-01, blender-farm-02, …, blender-farm-N (Blender peers, all group_id="render-cluster", capabilities include "rendering" and "gpu")

Flow:

  • Coordinator calls blender_list_available_clients, filters to group_id=="render-cluster" peers with gpu capability.
  • For each peer, dispatches blender_execute_code(target_uuid="blender-farm-N", code="bpy.context.scene.frame_set(42); bpy.ops.render.render(write_still=True)"). The target_uuid kwarg overrides auto-pick.
  • Each tool call returns when that peer reports completion. Run them concurrently with asyncio.gather.

Payload (under the hood):

{"message_type":"command_dispatch","job_id":"j-...","command":"execute_code",
"params":{"code":"import bpy\nbpy.context.scene.frame_set(42)\nbpy.ops.render.render(write_still=True)"}}

A simulation bake takes minutes. The dispatching client wants progress updates, not a single completion event.

Who:

  • llm-supervisor (LLM session)
  • blender-sim (Blender peer)

Flow:

  • Supervisor dispatches via blender_send_message with a job_dispatch payload — the script needs to emit progress events back, which the flat dispatch tools don’t model.
  • The script calls a helper that wraps blender_send_message with priority notice and the same job_id.
  • Supervisor’s on_message handles both job_update (terminal) and progress notifications (intermediate).

Payload (dispatch):

{"message_type":"job_dispatch","job_id":"job-bake-001",
"script":"# bake with periodic executor.send_progress(...) calls"}

Payload (progress, addon-side):

{"kind":"progress","job_id":"job-bake-001","percent":47,"frame":120}

Why script-dispatch here: the dispatch-tool path returns one JSON result per call. Progress streaming needs the LLM to subscribe to _message_bus and decode events as they arrive — that’s the custom-client pattern in Style B.

Two Blender peers are supposed to stay in sync — a sculpting workstation and a preview render station. Changes on one propagate to the other.

Who:

  • blender-sculpt (Blender peer, group_id="studio-1")
  • blender-preview (Blender peer, group_id="studio-1")
  • An external llm-mirror driving change detection.

Flow:

  • Mirror periodically calls blender_get_scene_info(target_uuid="blender-sculpt") and blender_browse_data(target_uuid="blender-sculpt", collection="objects").
  • On detected delta, mirror calls blender_execute_code(target_uuid="blender-preview", code="bpy.data.objects['Suzanne'].location = (1.2, 0, 0)").

Why MCP bus: nothing in the bus is tied to single-Blender state. Two peers on the same user’s bus is the same primitive as one peer; the dispatch tools take target_uuid so addressing either is one kwarg.

An LLM proposes a change; a human (or another LLM) approves before it lands.

Who:

  • llm-proposer (LLM session)
  • llm-approver (LLM session — could be a UI sitting on a notebook)
  • blender-target (Blender peer)

Flow:

  • Proposer sends a proposal payload directly to approver via blender_send_message. Not a job_dispatch — just data.
  • Approver’s UI surfaces the proposal. On approval, approver calls blender_execute_code(target_uuid="blender-target", code="...") to apply the change.
  • Proposer doesn’t see the dispatch directly; the approver returns the result of blender_execute_code over its own LLM channel.

Payload (proposal):

{"kind":"proposal","proposal_id":"prop-001","script":"...","rationale":"..."}

Why MCP bus: an LLM-to-LLM message uses the same blender_send_message primitive as an LLM-to-Blender message. The approver doesn’t need a separate channel for “approval requests.”

Server maintenance notice, scene-reset announcement, “anyone available?” pings.

Who: any sender; every client on the user’s bus.

Flow:

  • Call blender_send_message with no target_uuid / group_id / client_type — defaults to broadcast. Sender is automatically excluded.

Payload:

{"kind":"announcement","text":"Server restarting in 5 min","at":1706203789}

Use sparingly. Broadcasts go to everyone — blender-* peers will ignore announcement payloads (no message_type == "job_dispatch" and no message_type == "command_dispatch"), but they still get the notification and queue it briefly before discarding.

Tier-3 dispatch tools (PolyHaven, Hyper3D, Sketchfab) are gated by addon preferences. Calling a gated-off tool returns status: "failed" with the addon-side error. Check first.

Who:

  • llm-asset-finder (LLM session)
  • blender-main (Blender peer)

Flow:

status = json.loads(await client.call_tool("blender_get_polyhaven_status", {}))
if status["result"]["enabled"]:
await client.call_tool("blender_download_polyhaven_asset", {
"asset_id": "kloofendal_43d_clear_puresky",
"asset_type": "hdris",
"resolution": "2k",
})
else:
# surface the hint, or fall back to a different asset source
print("PolyHaven disabled; try Sketchfab or Rodin instead")

The same pattern applies to blender_get_hyper3d_status and blender_get_sketchfab_status. Each status tool has a tight default timeout (TIMEOUT_FAST = 15s) so the probe doesn’t add real latency.

Why this pattern: the dispatch tools mirror the addon’s command registry one-to-one, including the gating. Status probes are the cheap way to keep the LLM from dispatching a tool that’s structurally disabled.

These compose. A render farm (pattern 2) with progress events (pattern 3) and an approval gate (pattern 5) is just three layers of routing on the same bus. Nothing in the design treats them as different kinds of traffic. The dispatch tools cover most calls; script-dispatch is reserved for the parts that genuinely need it.