Blender session
A Blender session (pipeline/gate/blender.js, class BlenderSession) is the
only way the pipeline touches Blender: an MCP for Blender server spoken to
over stdio JSON-RPC — never hand-rolled sockets, never a hand-managed Blender
subprocess. The server bridges into Blender's own addon, which needs a GUI
Blender session; glina setup provisions Blender, the server and the addon.
Shape
- Spawn:
uvx mcp-for-blenderby default.blender.mcp.command+blender.mcp.argsname the program instead;blender.mcp.uvx: falsestarts a baremcp-for-blender;blender.mcp.uvxBinnames the uvx executable. - Tool surface used: exactly
get_scene_infoandexecute_blender_code. Everything else — import, export, decimate, rig — is Blender Python (bpy) sent throughexecute_blender_code. - Environment: the MCP child gets PATH and HOME only, so no credential can leak through environment inheritance.
Lifecycle
BlenderSession.start(options)spawns the server and performs the MCP handshake. A failed start throws:blender MCP server failed to start (<command> <args>): <reason>. Run 'glina setup' to provision Blender, mcp-for-blender and its addon.execute(code)wraps the code intry/exceptbefore sending, so an exception in the block cannot kill the addon's server thread; it comes back asGAC-EXEC-ERROR: <traceback>in the output. The wrapped block ends by printing a fresh completion marker, and an answer without it is refused:Blender execute_blender_code did not complete: <answer>.importModel(path)resets the scene through the data API only (objects, actions, meshes, armatures, materials) and imports the GLB.bpy.ops.wm.read_factory_settingsis never used — it would wipe the addon's scene properties and kill the MCP server thread. Old actions are removed too, so a preview never picks a clip from an earlier import. An import Blender does not confirm is refused:Blender did not confirm import of <path>: <answer>.exportGlb(path)exports the whole scene and stats the file:Blender did not confirm export to <path>: <answer>, orexport produced no file at <path>: <reason>for an absent or empty file.close()ends the MCP process. A session closed from afinallywhose close fails says so on stderr —[blender] MCP session close failed, Blender may still be running: <reason>— without replacing the job's own outcome.
Health
health() answers { healthy, error } from what actually happened, because
the MCP server starts fine even when the Blender side of the bridge is dead:
- the server answers
tools/list, execute_blender_codeis among the tools (the bridge does not expose execute_blender_code),- a
print("health-probe")round-trips through Blender and its output comes back (execute_blender_code did not reach Blender: <answer>).
glina doctor runs this probe as part of its report and exits 1 when the
report is not healthy; glina setup --check reports the installed
prerequisites without changing them.
Invariants
- One session per job; sessions are always closed in a
finally, even on failure. - A sculpt job that sees a failed block asks the bridge for its tools and ends when the bridge refuses — see the sculpt job bridge-down rule.