Blender MCP Server
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| MCP_HOST | No | Bind address for HTTP transport | 127.0.0.1 |
| MCP_PORT | No | Bind port for HTTP transport | 8000 |
| LOG_LEVEL | No | One of DEBUG, INFO, WARNING, ERROR, CRITICAL | INFO |
| LOG_FORMAT | No | logging format string, as shown in .env.example | |
| BLENDER_HOST | No | Address the add-on connects to | 127.0.0.1 |
| BLENDER_PORT | No | Bridge port | 8765 |
| MCP_TRANSPORT | No | Either stdio or streamable-http | stdio |
| ALLOW_PYTHON_EXECUTION | No | Gate for blender.execute_python | false |
| BLENDER_RENDER_TIMEOUT | No | Seconds to wait for a render | 600.0 |
| BLENDER_CONNECT_TIMEOUT | No | Socket connect timeout used by the add-on | 5.0 |
| BLENDER_REQUEST_TIMEOUT | No | Seconds to wait for a response | 30.0 |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| blender.get_sceneA | Return a compact, LLM-sized summary of the active scene: object names, types, transforms and visibility, plus collections, cameras, lights, the active object, the render engine and the current frame. Use this first to see what exists. At most |
| blender.get_objectsA | List objects with filters and a page window, for scenes too large for blender.get_scene to be useful. type: object type to keep, e.g. "MESH", "CAMERA", "LIGHT". collection: only objects linked to this collection. name_contains: case-insensitive substring of the name. limit: page size, 1-500, default 50. offset: how many matches to skip, default 0. Returns the page, plus "total" (matches before paging) and "truncated" (true when more objects remain) so you know whether to ask for the next page. |
| blender.get_objectA | Return the full detail of a single object: transform, dimensions, visibility, collection, material names, modifiers, and vertex/edge/polygon counts for meshes. Fails with OBJECT_NOT_FOUND when the name does not exist. |
| blender.create_objectA | Create a mesh object in the active scene. type: one of cube, sphere, cylinder, cone, plane, torus. Case does not matter. name: must be unique; an existing name fails with OBJECT_ALREADY_EXISTS rather than being renamed to "Name.001". location / scale: Blender units (meters), as [x, y, z]. rotation: DEGREES as [x, y, z], applied in XYZ Euler order. Example: {"type": "cube", "name": "TableTop", "location": [0, 0, 1], "scale": [2, 1, 0.1]} |
| blender.update_objectA | Change an existing object. Only the fields you pass are touched; omitted fields keep their current value. location / scale: Blender units (meters) as [x, y, z]. rotation: DEGREES as [x, y, z]. dimensions: final bounding-box size in meters as [x, y, z] (replaces scale). visibility: hide or show the object in the viewport and renders. new_name: rename the object. Fails with OBJECT_ALREADY_EXISTS if that name is taken, rather than silently becoming "Name.001". material: material name to assign, replacing the object's material slots. Created if it does not exist yet; the response says whether it did, through "material_created". material_color: [r, g, b] or [r, g, b, a] in 0..1, used only when the material has to be created. Sets both the viewport colour and the shader, so the solid view and the render agree. |
| blender.delete_objectA | Delete an object from the active scene, checking first that it exists. Fails with OBJECT_NOT_FOUND when the name is unknown. |
| blender.renderA | Render the active scene with the current camera and return the output path and the wall-clock render time. engine: Blender engine id, e.g. "CYCLES" or "BLENDER_EEVEE" (4.x called EEVEE "BLENDER_EEVEE_NEXT"). blender.get_scene lists the ids this Blender accepts under render.engines. Omit to keep the scene's current engine. resolution_x / resolution_y: pixels. Omit to keep the scene's render size. samples: Cycles sample count; ignored by engines without sampling. output_path: absolute or Blender-relative path. Omit to use the scene's configured output path. Render time grows quickly with resolution and samples; prefer 512-1024 px while iterating. |
| blender.render_previewA | Render a small preview and return the image, so you can look at what you built. max_edge: longest edge in pixels, 64-2048, default 512. Bigger costs a lot of context, and a vision model rarely needs more to spot a mistake. engine: optional engine id; omit to keep the scene's current one. samples: optional sample count; omit for a cheap default. include_image: set false to get only the file path and no image. Prefer this over blender.render while iterating: it is capped in size and samples and it comes back as an image. Use blender.render for a final image the user will keep. |
| blender.execute_pythonA | Execute Python inside the running Blender and return whatever the snippet left
in a variable named code: statements to run. Example: import bpy bpy.ops.mesh.primitive_uv_sphere_add(location=(0, 0, 1)) obj = bpy.context.object obj.name = "Ball" result = {"created": obj.name, "verts": len(obj.data.vertices)} This tool is meant for a trusted, local AI client. It is a policy screen, not a sandbox: Blender's own API is powerful enough to do damage, and enabling this tool should be a deliberate choice. |
| blender.begin_transactionA | Open a transaction. Every mutating call until commit or rollback is recorded, and blender.rollback_transaction puts all of them back. label: optional name for the starting point, e.g. "layout". Later rollbacks can return to it by name. Fails with TRANSACTION_ACTIVE if one is already open. |
| blender.checkpointA | Record a named point inside the open transaction, so a later rollback can stop there instead of unwinding everything. Use it between stages of a build: begin -> build the shell -> checkpoint "shell" -> add the details -> commit. A rollback to "shell" then discards only the details. label: required, and unique within the transaction. |
| blender.commit_transactionA | Close the open transaction and keep the changes. The undo steps stay on Blender's undo stack, so the user can still step back by hand. Fails with TRANSACTION_NOT_ACTIVE if no transaction is open. |
| blender.rollback_transactionA | Put back everything the transaction changed. to: optional checkpoint label. Omit it to rewind to the start of the transaction; name one to rewind only to that checkpoint. Restores what the tools changed: transforms, names, visibility, materials and collection membership. Edits made through blender.execute_python are not tracked, so a transaction containing them rolls back only its tool calls. Fails with TRANSACTION_NOT_ACTIVE if no transaction is open. |
| blender.wait_for_changeA | Wait until the scene changes, or until Use it after a change you are unsure about, instead of reading the whole scene again: it returns as soon as Blender reports a change, and tells you what changed. timeout: seconds to wait, 0-60, default 10. objects: optional object names to narrow the answer to. Omit to watch everything. include_image: set true to also return the last render, if there is one, so a wait-and-look step is a single call. Returns {"changed": bool, "changes": int, "waited": seconds, "changed_objects": [...]}.
|
| blender.get_instancesA | List the Blender instances that have connected to this server, newest first. The active instance is the one every other tool acts on. The others are kept as history so a scene that changed unexpectedly can be explained: an entry with status "refused" or "replaced" means a second Blender tried to take the socket. Answers even when no Blender is connected, which makes it a useful first call after a connection error. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| Blender scene state | Compact JSON summary of the active Blender scene: every object with its transform, plus collection, camera and light names. Mesh data is not included. |
| Blender object inventory | JSON list of every object in the active Blender scene with type, location, rotation (degrees), scale and dimensions. Materials and modifiers are omitted; use blender.get_object for a single object in full. |
| Latest render | The most recent image rendered by this Blender, as image bytes. Empty until blender.render or blender.render_preview has been called. |
TDQS
Scored across 15 tools
Each tool has a clearly distinct purpose, and the read-tool triad (get_scene for a compact overview, get_objects for paging, get_object for full single-object detail) is explicitly delineated in the descriptions. The render vs render_preview distinction is also spelled out ('prefer this over blender.render while iterating'). No meaningful overlap remains.
All tools use snake_case with a consistent verb_noun or clear domain-noun pattern (get_objects, create_object, update_object, delete_object, begin_transaction, commit_transaction, rollback_transaction). Minor verb-only names like render and checkpoint fit naturally and introduce no inconsistency.
At 15 tools the set is well-scoped: a coherent object-CRUD core, a self-consistent transaction group (begin/checkpoint/commit/rollback), rendering, an inspection tool, and a controlled Python escape hatch. Every tool earns its place without bloat.
Object CRUD, transactions, rendering, and inspection are fully covered for the domain. Minor gaps remain: create_object only makes mesh primitives (no dedicated camera/light creation), and there are no standalone collection or material lifecycle tools, though update_object and execute_python provide workarounds.