Skip to main content
Glama
BlkDem

Blender MCP Server

by BlkDem

Blender MCP Server

Blender MCP Server lets AI clients control a running Blender through the Model Context Protocol. Point Claude, Cursor, OpenCode or any other MCP client at it, and the model can inspect the scene, create and edit objects, run renders, and execute Blender Python.

Everything runs locally. There is no cloud component, no account, no database.

User
 │
 ▼
AI client  ── "make me a table"
 │
 │ MCP (stdio)
 ▼
┌────────────────────────────┐
│  Blender MCP Server        │
│                            │
│  Tools      blender.*      │
│  Resources  blender://     │
│  Validation AST policy     │
└─────────────┬──────────────┘
              │ WebSocket (JSON)
              ▼
┌────────────────────────────┐
│  Blender add-on            │
│                            │
│  main-thread dispatch      │
│  bpy / bmesh / operators   │
└─────────────┬──────────────┘
              ▼
           Blender

The two processes are separate on purpose. The MCP server never imports bpy; the add-on never imports the MCP SDK. They meet at one JSON protocol over a WebSocket, which is what makes each half testable on its own and what will let the transport change later without touching either side.


Table of contents


Related MCP server: dcc-mcp-blender

Requirements

Python

3.11 or newer

Blender

3.6 or newer (4.x recommended)

MCP SDK

mcp 2.x — the current major version

Blender add-on dependencies

none (standard library only)

The add-on deliberately has no pip dependencies. Blender's bundled Python ships neither websockets nor pydantic, so addon/blender_mcp/websocket.py is a small RFC 6455 client built on socket, and protocol.py is a plain-dict mirror of the server's models. Nothing needs installing into Blender itself.

Installation

git clone https://github.com/your-org/blender-mcp.git
cd blender-mcp

python3 -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate

pip install -e .

Optionally copy the example environment file and edit it:

cp .env.example .env

You can also install with the dev extras, which bring in pytest and the linters:

pip install -e ".[dev]"

Check that the server starts:

python -m server.main

It will print a startup line to stderr and then sit waiting on stdio, which is normal: with no AI client attached, silence is the correct behaviour. Watch the logs from another terminal while you start Blender.

Installing the Blender add-on

  1. Zip the add-on package including the top-level folder:

    cd addon
    zip -r blender_mcp.zip blender_mcp -x '*.pyc' '*__pycache__*'

    The archive must contain blender_mcp/__init__.py at its root.

  2. In Blender: Edit → Preferences → Add-ons → Install… (or Install from Disk in 4.2+) and select blender_mcp.zip.

  3. Enable the Development: Blender MCP check box.

  4. Open the 3D Viewport, press N, and switch to the Blender MCP tab:

    ┌──────────────────────────┐
    │ Blender MCP              │
    │                          │
    │ Status: Connected        │
    │ Server: 127.0.0.1:8765   │
    │                          │
    │ Host: [127.0.0.1      ]  │
    │ Port: [8765            ]  │
    │                          │
    │ [      Connect      ]    │
    └──────────────────────────┘
  5. Press Connect. The status turns green once the add-on has reached the MCP server; it retries automatically with a backoff if the server is not up yet, so you can start Blender first and the server second.

Blender must be the one holding the connection: the MCP server only ever dials nothing, it listens.

Running the server

The server speaks stdio, which is what MCP clients spawn:

python -m server.main

or, equivalently, through the installed console script:

blender-mcp-server

It opens two things at once:

  • the MCP stdio channel to the AI client, and

  • a WebSocket listener on BLENDER_HOST:BLENDER_PORT (default 127.0.0.1:8765) for the add-on.

Both start and stop with the process, and all logs go to stderr — stdout belongs to the protocol.

For debugging you can also serve MCP over HTTP instead:

MCP_TRANSPORT=streamable-http MCP_PORT=8000 python -m server.main

Checking the bridge without an AI client

Two scripts speak the bridge protocol directly:

python examples/create_cube.py     # one cube, one round trip
python examples/create_scene.py    # a table, several requests, in a transaction

They are clients of the bridge, not of Blender, so they run anywhere Python 3.11 is available. See Testing it for more ways to check a change.

Connecting an AI client

Add the server to your client's MCP configuration. The command is the interpreter from your virtual environment, so the client can find the dependencies.

Claude Desktop — claude_desktop_config.json (~/Library/Application Support/Claude/ on macOS, %APPDATA%\Claude\ on Windows):

{
  "mcpServers": {
    "blender": {
      "command": "/absolute/path/to/blender-mcp/.venv/bin/python",
      "args": ["-m", "server.main"],
      "env": {
        "BLENDER_HOST": "127.0.0.1",
        "BLENDER_PORT": "8765",
        "ALLOW_PYTHON_EXECUTION": "true"
      }
    }
  }
}

Cursor — .cursor/mcp.json in your project, or the global ~/.cursor/mcp.json:

{
  "mcpServers": {
    "blender": {
      "command": "/absolute/path/to/blender-mcp/.venv/bin/python",
      "args": ["-m", "server.main"],
      "env": {
        "ALLOW_PYTHON_EXECUTION": "true"
      }
    }
  }
}

OpenCode — opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "blender": {
      "type": "local",
      "command": ["/absolute/path/to/blender-mcp/.venv/bin/python", "-m", "server.main"],
      "enabled": true,
      "environment": {
        "ALLOW_PYTHON_EXECUTION": "true"
      }
    }
  }
}

Restart the client after editing. If a tool call fails with NOT_CONNECTED, the bridge is up but the add-on is not connected — check the Blender panel.

.env is read relative to the server process's working directory, and an MCP client chooses that for you. For a setup you can rely on, pass the settings in the env block of the client's MCP configuration, as above.

First test

Say to your AI client:

Create a red cube at the origin.

A good model does this in two calls:

blender.create_object  {"type": "cube", "name": "RedCube", "location": [0, 0, 0]}
blender.execute_python
import bpy

cube = bpy.data.objects["RedCube"]
material = bpy.data.materials.new("Red")
material.use_nodes = True
material.node_tree.nodes["Principled BSDF"].inputs["Base Color"].default_value = (0.8, 0.05, 0.05, 1.0)
material.diffuse_color = (0.8, 0.05, 0.05, 1.0)   # so the solid viewport matches
cube.data.materials.append(material)

result = {"object": cube.name, "material": material.name}

Setting only material.diffuse_color is the classic mistake: it colours the solid viewport but leaves the Principled BSDF at its default grey, so a render comes out white. Set both, as above.

and answers with the first response, not the whole bpy object dump:

{
  "success": true,
  "object": {
    "name": "RedCube",
    "type": "MESH",
    "location": [0.0, 0.0, 0.0],
    "scale": [1.0, 1.0, 1.0],
    "dimensions": [2.0, 2.0, 2.0]
  }
}

Then ask it to inspect the current scene and render, and watch the viewport.

Testing it

There are four levels, from cheapest to most convincing. Pick how far down you want to go.

1. The test suite, no Blender needed

pip install -e ".[dev]"
pytest

295 tests, ~12 s, no Blender required. These cover the protocol, the WebSocket transport (driving the add-on's own client against the real server), every tool, the policy screen, and a full MCP session over a real socket. See Development for what each file covers.

2. The bridge, without an AI client

Two scripts speak the bridge protocol directly, so you can prove the server and the add-on are talking to each other before involving a model:

python examples/create_cube.py     # one request, one object
python examples/create_scene.py    # a table, several requests, grouped in a transaction

Each prints what Blender answered and exits non-zero on failure.

3. A real scene, headless

Builds a still life in Blender, saves a .blend and renders it — no GUI, no AI client, just the add-on's operator layer doing real bpy work:

# with the Blender app
blender --background --python examples/test_scene.py -- --out /tmp/mcp-demo

# or with the bpy Python module, no Blender install needed
pip install bpy==4.2.0        # must match your Python; 4.2.0 is the 3.11 build
python -m bpy --background --python examples/test_scene.py -- --out /tmp/mcp-demo
Scene built in 0.04s: 13 objects
  Ball       MESH   [0.5, 0.5, 0.5]
  Cone       MESH   [0.24, 0.24, 0.36]
  Cup        MESH   [0.24, 0.24, 0.36]
  Floor      MESH   [16.0, 16.0, 0.0]
  LegBL      MESH   [0.16, 0.16, 1.4]
  ...
  transaction: {'success': True, 'transaction': 'committed', 'undo_steps': 4}
Saved /tmp/mcp-demo/test_scene.blend
Rendered /tmp/mcp-demo/test_scene.png in 5.07s -> {'engine': 'CYCLES', 'resolution': [640, 400], 'samples': 32}

the scene the script builds

Every primitive type, a material pass, a transaction, and a Cycles render. If this produces an image, blender.create_object, blender.update_object, blender.begin_transaction and blender.render all work.

4. The whole stack, in a real Blender window

The three levels above need no AI client. This one is the real thing: an MCP client drives a running Blender GUI and photographs the result.

Blender 5.2 with the add-on connected and a scene built through MCP

Two scripts, in two processes, exactly like a real setup:

# terminal 1 — the AI client, which also starts the MCP server
python examples/mcp_client_demo.py \
    --out /tmp/mcp-demo \
    --blender "blender --python examples/gui_blender_demo.py -- --out /tmp/mcp-demo"

# Windows
python examples\mcp_client_demo.py --out C:\temp\mcp-demo --blender ^
    '"C:\Program Files\Blender Foundation\Blender 5.2\blender.exe" --python ^
     C:\path\to\blender-mcp\examples\gui_blender_demo.py -- --out C:\temp\mcp-demo'

mcp_client_demo.py spawns server.main over stdio, waits for the add-on to connect, then builds a table through the tools and writes a flag file. gui_blender_demo.py runs inside Blender: it enables the add-on, connects it, opens the Blender MCP tab, and takes the screenshot once the flag appears.

Output lands in --out: screenshot.png, mcp_demo.blend, gui.log.

Two Blender rules that this exercise pins down, both of which cost a crash to learn:

  • Do scene work in a timer, not in a --python startup script. The UI is not realised yet during startup and Blender 5.x segfaults.

  • Region.active_panel_category is read-only until the region has been drawn with panels in it. Open the sidebar, redraw, then select the tab.

5. The acceptance scenario

Two scripts, and they check the same things at different levels:

# in-process, against a real bpy: the operator layer
blender --background --python examples/acceptance_check.py

# the whole chain: MCP client -> server -> WebSocket -> add-on -> bpy
python examples/mcp_acceptance.py --port 8774 \
    --blender-arg=blender --blender-arg=--background \
    --blender-arg=--python --blender-arg=examples/blender_attach.py

acceptance_check.py prints a PASS/FAIL line per step and exits non-zero on failure. mcp_acceptance.py does the same over MCP, and additionally asserts the tool surface, the resources, the error codes and the transaction bookkeeping. Together they are the definition of done for this project: every tool, every resource, the idempotency rule, five error paths, a render and an undo group.

6. The full stack against real Blender, in CI

bpy installed → two more test modules switch themselves on and test the real thing, headless:

pip install bpy==4.2.0
pytest                          # 437 tests: 338 + 99 against real bpy
BLENDER_MCP_SKIP_BPY=1 pytest   # 295, opt the real-Blender ones back out
  • tests/test_blender_integration.py — the operator layer against real bpy: every primitive at Blender's own default size, transform reporting, undo and transaction rollback, execute_python, and a real Cycles render written to disk.

  • tests/test_blender_bridge.py — the whole chain: a real MCP client calls tools, the real server forwards them over a real WebSocket, the add-on's own client receives them, and real bpy mutates a real scene. Includes the build-a-table example from below, and 10 concurrent requests to prove responses stay correlated.

The one thing this cannot cover headlessly is Blender's timer scheduler, which needs the GUI event loop. The tests call the dispatch function directly instead — same function, same thread, same queue.

The bpy module segfaults while tearing itself down at interpreter exit, after pytest has reported. tests/conftest.py exits hard with pytest's own status so a green run stays green.

Troubleshooting

Symptom

Cause

Fix

NOT_CONNECTED on every tool

The add-on is not connected

Blender → N → Blender MCP → Connect; check the port matches BLENDER_PORT

TIMEOUT

Blender is busy or the add-on is disconnected mid-request

Check Blender's console; a modal dialog blocks the main thread

Connection lost / Reconnecting in the panel

The server is not running, or on a different port

Start python -m server.main; the add-on retries with a backoff on its own

PERMISSION_DENIED from execute_python

The gate is off by default

ALLOW_PYTHON_EXECUTION=true

VALIDATION_ERROR from execute_python

The code hit the policy screen

The violations list names the line and the reason

The AI client sees no tools

The server failed to start

Run python -m server.main by hand and read stderr; stdout is the protocol

Render fails with an OpenGL/EGL error

EEVEE needs a GPU context

Use engine: "CYCLES" (and set scene.cycles.device = "CPU") when headless

INVALID_PARAMETER: Unknown render engine

The id is version-specific

blender.get_scene reports render.engines; the error also lists known_ids. EEVEE is BLENDER_EEVEE in 5.x and BLENDER_EEVEE_NEXT in 4.x

MCP tools

Scene

Tool

Required

Optional

What it does

blender.get_scene

—

object_limit

Compact summary of the active scene

blender.get_objects

—

type, collection, name_contains, limit, offset

Filtered, paged object list

blender.get_instances

—

—

Which Blender this server is attached to, and what happened to the others

Object

Tool

Required

Optional

What it does

blender.get_object

name

—

Full detail of one object, mesh statistics included

blender.create_object

type, name

location, rotation, scale, collection

Create a primitive

blender.update_object

name

location, rotation, scale, dimensions, visibility, new_name, material, material_color

Change only the fields given

blender.delete_object

name

—

Delete an object, by exact name

Advanced and render

Tool

Required

Optional

What it does

blender.render

—

engine, resolution_x, resolution_y, samples, output_path

Render the scene

blender.render_preview

—

max_edge, engine, samples, include_image

Small render, returned as an image

blender.wait_for_change

—

timeout, objects, include_image

Wait until something changes, or time out

blender.execute_python

code

—

Run Python inside Blender; a power-user escape hatch, not the normal path

Undo grouping

Tool

Required

Optional

What it does

blender.begin_transaction

—

label

Start a transaction

blender.checkpoint

label

—

Name a point a later rollback can stop at

blender.commit_transaction

—

—

Keep the changes

blender.rollback_transaction

—

to

Undo the transaction, or just the stage after a checkpoint

create_object accepts cube, sphere, cylinder, cone, plane and torus, in any case. The enum is in the tool's JSON schema, so a model cannot invent a primitive that does not exist, and the case is normalised for it.

get_scene returns the whole scene; get_objects is the one to reach for when there are hundreds of objects, because it filters and pages:

{
  "objects": [{"name": "LegFL", "type": "MESH", "...": "..."}],
  "count": 1, "total": 42, "offset": 0, "limit": 50, "truncated": true
}

total is the number of matches before paging and truncated says whether more remain, so a model can tell that it has seen everything without guessing.

get_scene sends an object list too, capped at object_limit (200 by default, 1000 at most) and saying so with objects_shown and objects_truncated. A scene with ten thousand objects is a few hundred KB of JSON; a model that wants the rest asks for it with get_objects instead of having it forced on every scene read.

Looking at what you built

blender.render_preview renders small and returns the image itself, so a vision-capable model can check its own work:

{"success": true, "output_path": "/tmp/blender/preview.png", "render_time": 0.42}

followed by a PNG content block. It is capped at 2048 px on the long edge and defaults to 512, because a render is expensive in time and an image is expensive in context. blender.render is still the tool for the picture the user will keep.

One path caveat, because it is a trap: the add-on and the MCP server have to share a filesystem for a path to mean anything. Normally they do. If you run Blender on Windows and the server in WSL — or the other way round — a path the server sends is a path the other side cannot resolve, and a render fails with "cannot save". render_preview and blender://render/latest avoid the problem by using Blender's own temp directory, because the add-on names the file and reports the absolute path back. The images work across that boundary; an explicit output_path does not.

blender.wait_for_change is the companion: after a change you are unsure about, wait instead of re-reading the whole scene. It returns as soon as Blender reports a change, and tells you which objects changed:

{"changed": true, "changes": 2, "waited": 0.3, "changed_objects": ["Cube"], "watched": "scene"}

It notices edits the user makes in the UI as well as the ones the tools make, because a user dragging an object is the most common reason to need a second look. It is a long poll rather than a push: this SDK can only publish notifications/resources/updated from inside a request, and a client that wants to be told something changed gets the same answer either way.

Examples

blender.get_scene — the whole scene, no mesh data:

{
  "scene": "Scene",
  "objects_count": 2,
  "frame": 1,
  "active_object": "TableTop",
  "active_camera": "Camera",
  "render_engine": "CYCLES",
  "objects": [
    {
      "name": "TableTop", "type": "MESH",
      "location": [0.0, 0.0, 0.75], "rotation": [0.0, 0.0, 0.0],
      "scale": [1.6, 0.8, 0.05], "dimensions": [3.2, 1.6, 0.1], "visible": true
    }
  ],
  "collections": ["Collection"],
  "cameras": ["Camera"],
  "lights": [{"name": "Key", "type": "AREA", "energy": 400.0}],
  "render": {"engines": ["CYCLES", "BLENDER_EEVEE"], "resolution": [1920, 1080]}
}

blender.get_objects — filtered and paged:

blender.get_objects {"type": "MESH", "name_contains": "leg", "limit": 2}

blender.create_object:

blender.create_object {"type": "CUBE", "name": "Table", "location": [0, 0, 1], "scale": [2, 1, 1]}

blender.update_object — rename and material in one call:

blender.update_object {"name": "Table", "new_name": "TableTop"}
blender.update_object {"name": "TableTop", "material": "Wood"}
blender.update_object {"name": "TableTop", "material": "Red", "material_color": [0.8, 0.05, 0.05]}

material is created when it does not exist, and the response says so, so a model never has to guess whether the colour landed:

{"success": true, "object": {"...": "..."}, "material": "Red", "material_created": true}

blender.delete_object:

blender.delete_object {"name": "TableTop"}

blender.render:

blender.render {"resolution_x": 1024, "resolution_y": 1024}

Responses

Success:

{"success": true, "object": {"name": "Table", "type": "MESH", "location": [0, 0, 1]}}

Failure — the call is marked failed and the body stays machine-readable:

{"success": false, "error": {"code": "OBJECT_NOT_FOUND", "message": "Object 'Chair' does not exist"}}

Every tool returns small JSON summaries. Vertices, polygons, face loops and raw bpy dumps are never sent, whatever the caller asks for; use blender.execute_python and return a result variable when you need more.

Two behaviours worth knowing:

  • blender.render overrides are temporary. Engine, resolution, samples and output path are restored to whatever the scene had once the image is written, so rendering twice gives the same result and a user's Cycles setup is not silently switched to EEVEE. The response reports what was actually used:

    {
      "success": true,
      "output_path": "/home/you/render.png",
      "render_time": 3.42,
      "used": {"engine": "CYCLES", "resolution": [1024, 1024], "samples": 64},
      "settings_restored": true
    }
  • blender.create_object does not use bpy.ops. Primitives are built with bmesh, so creation works the same in the UI and under blender --background, where operators that need a window context cannot run.

Idempotency

An agent may repeat a call. Two rules, both about names:

  • create_object(name="Table") when Table exists fails with OBJECT_ALREADY_EXISTS. It does not quietly create Table.001, because the model asked for an object by name and would then go on to refer to a name that does not exist. Delete and re-create is two deliberate calls.

  • update_object(new_name=...) onto a taken name fails the same way, for the same reason. Renaming an object to the name it already has is a no-op that succeeds, so a retried rename is harmless.

delete_object matches the exact name only. There is no fuzzy matching: a model that meant Table must not delete Table.001 by accident.

MCP resources

Resources are for state you want without spending a tool call.

URI

MIME

Contents

blender://scene

application/json

Scene name, object list with transforms, collections, cameras, lights, render settings

blender://objects

application/json

The same object list, ready to quote

blender://render/latest

image/png

The most recent render, as image bytes

blender://render/latest is empty until something has been rendered, and it reports "no rendered image" rather than inventing one. It also stops offering a render once the file it was made in has been closed: the picture belongs to that scene, and serving it under a caption it does not match is worse than serving nothing.

// blender://objects
{
  "scene": "Scene",
  "objects_total": 1,
  "objects": [
    {
      "name": "RedCube",
      "type": "MESH",
      "location": [0.0, 0.0, 0.0],
      "rotation": [0.0, 0.0, 0.0],
      "scale": [1.0, 1.0, 1.0],
      "dimensions": [2.0, 2.0, 2.0]
    }
  ],
  "collections": ["Collection"],
  "cameras": ["Camera"],
  "lights": []
}

blender.get_object is the same idea for a single object, plus materials, modifiers, visibility and parent.

Planned, and already shaped for by the resource layer: blender://materials, blender://collections, blender://cameras, blender://render/latest.

Example AI workflows

1. "Create a cube named Box at the origin."

blender.create_object {"type": "cube", "name": "Box", "location": [0, 0, 0]}

One call.

2. "Create three cylinders in a row."

blender.begin_transaction {}
blender.create_object {"type": "cylinder", "name": "Cyl1", "location": [-1, 0, 0]}
blender.create_object {"type": "cylinder", "name": "Cyl2", "location": [ 0, 0, 0]}
blender.create_object {"type": "cylinder", "name": "Cyl3", "location": [ 1, 0, 0]}
blender.commit_transaction {}

The transaction means one bad placement can be undone with a single blender.rollback_transaction instead of three deletes.

3. "Inspect the current scene and tell me what objects are present."

blender.get_scene {}

Then, if the model needs detail on one of them:

blender.get_object {"name": "TableTop"}

4. "Create a simple table with a wooden top and four legs."

blender.begin_transaction {}
  blender.create_object {"type":"cube","name":"TableTop","location":[0,0,0.75],"scale":[2,1,0.05]}
  blender.create_object {"type":"cube","name":"LegFL","location":[-0.9,-0.4,0.35],"scale":[0.1,0.1,0.7]}
  blender.create_object {"type":"cube","name":"LegFR","location":[ 0.9,-0.4,0.35],"scale":[0.1,0.1,0.7]}
  blender.create_object {"type":"cube","name":"LegBL","location":[-0.9, 0.4,0.35],"scale":[0.1,0.1,0.7]}
  blender.create_object {"type":"cube","name":"LegBR","location":[ 0.9, 0.4,0.35],"scale":[0.1,0.1,0.7]}
blender.commit_transaction {}
blender.execute_python
    import bpy
    wood = bpy.data.materials.new("Wood")
    wood.diffuse_color = (0.32, 0.19, 0.07, 1.0)
    for name in ("TableTop", "LegFL", "LegFR", "LegBL", "LegBR"):
        obj = bpy.data.objects.get(name)
        if obj is not None:
            obj.data.materials.append(wood)
    result = {"materialised": 5}
blender.render {"resolution_x": 800, "resolution_y": 600}

That chain — create, transform, materialise, render — is the shape a future autonomous agent will follow too.

Units and conventions

  • Lengths are Blender units (metres), at every tool and in every payload.

  • Rotations are degrees in XYZ Euler order, everywhere. bpy stores radians; the conversion happens inside the add-on so models never have to think about it. This is the single most common source of nonsense output from a model driving a 3D tool, so it is worth being explicit about.

  • Sizes are reported as dimensions, the world-space bounding box.

  • Names are Blender data-block names, unique per type.

Error handling

Failures are structured, with a stable error.code to branch on:

Code

Meaning

OBJECT_NOT_FOUND

No object with that name

OBJECT_ALREADY_EXISTS

Refused rather than silently renamed

INVALID_OBJECT_TYPE

Unknown primitive

INVALID_PARAMETER

Bad vector, empty name, unknown engine

BLENDER_NOT_CONNECTED

No add-on is attached to the bridge

BLENDER_OPERATION_FAILED

Blender itself raised

TIMEOUT

Blender did not answer in time

PYTHON_EXECUTION_DISABLED

ALLOW_PYTHON_EXECUTION=false

PYTHON_EXECUTION_ERROR

execute_python code raised

VALIDATION_ERROR

Code blocked by the Python policy screen

CONNECTION_LOST

The socket dropped mid-request

MALFORMED_MESSAGE

A frame could not be parsed

UNKNOWN_ACTION

The add-on does not implement that action

TRANSACTION_ACTIVE

A transaction is already open

TRANSACTION_NOT_ACTIVE

Commit or rollback without a begin

INTERNAL_ERROR

Anything unclassified

Tracebacks are logged on the server and in Blender's console; the model gets the exception type, message and a short traceback tail, which is what it needs to fix its own mistake. Full tracebacks are not shipped to the AI.

The bridge protocol

One JSON object per WebSocket text frame. Every request carries a unique id, and exactly one response is sent per request.

Request:

{"id": "3f2a…", "action": "create_object", "params": {"type": "cube", "name": "Box"}}

The dispatch field is action; the add-on also accepts method as a synonym, so a peer written against the other common spelling interoperates.

Success:

{"id": "3f2a…", "success": true, "result": {"object": {"name": "Box"}}}

Error:

{
  "id": "3f2a…",
  "success": false,
  "error": {"code": "OBJECT_NOT_FOUND", "message": "Object 'Box' does not exist", "details": {}}
}

Actions

Action

Kind

Params

get_scene

read

object_limit

get_objects

read

type, collection, name_contains, limit, offset

get_object

read

name

ping

read

— (the answer carries the pid, version and file: the add-on's identity)

change_count

read

—

changes

read

since

create_object

write

type, name, location, rotation, scale, collection

update_object

write

name + any of location, rotation, scale, dimensions, visibility, new_name, material, material_color

delete_object

write

name

render

write

engine, resolution_x, resolution_y, resolution_percentage, samples, output_path

render_preview

write

resolution_x, resolution_y, engine, samples

last_render

read

—

execute_python

write

code

begin_transaction

write

label

checkpoint

write

label

commit_transaction

write

—

rollback_transaction

write

to

"write" means it pushes an undo step. Adding an action means adding an entry here, a handler in addon/blender_mcp/operators.py, and — if the model should see it — a tool in server/mcp/tools/. The two protocol modules mirror each other field for field, and tests/test_addon_protocol.py fails if they drift.

There is one frame that is not a request: {"type": "disconnect", "reason": …}, which the server sends to refuse a Blender that is not the one already attached. It is how a refusal reaches the add-on instead of leaving it retrying a connection it will never be given.

Failure handling on the wire

Situation

What happens

Blender not connected

NOT_CONNECTED immediately, no socket involved

Blender never answers

TIMEOUT after BLENDER_REQUEST_TIMEOUT

Blender disconnects mid-request

every pending request fails with CONNECTION_LOST

Unparsable frame

logged and dropped; the connection survives

A second Blender connects

it takes over the bridge; the old one's pending requests fail fast

A handler raises

caught, logged with a traceback, returned as BLENDER_ERROR

Security

Read this before pointing anything other than your own machine at this server.

Network exposure

  • The WebSocket bridge binds to BLENDER_HOST, which defaults to 127.0.0.1. Keep it there. It has no authentication of any kind: anything that can reach the port can drive your Blender, including blender.execute_python if you have enabled it.

  • The MCP server speaks stdio, so it is only reachable by whoever can spawn the process. MCP_TRANSPORT=streamable-http exists for tooling and debugging; if you use it, bind it to loopback and put authentication in front of it. It has none of its own.

  • There is no auth, no multi-user support and no audit trail beyond the request log. That is deliberate for a local tool, not an oversight.

One Blender at a time

The bridge serves one Blender. A second one connecting is refused by default, with the reason sent to the add-on so its panel says why instead of retrying forever:

blender-4711 (pid 4711) is already connected. Close it, or start the server
with BLENDER_ALLOW_TAKEOVER=1 if you meant to replace it.

The alternative — letting the newcomer take the socket — is the failure mode this prevents: one user's tool calls quietly start landing in someone else's scene, and the symptom is impossible to trace back. Both instances are recorded either way, so blender.get_instances can explain a scene that changed unexpectedly:

{
  "active": {"id": "blender-4711", "status": "active", "pid": 4711, "blender_version": "5.2.2"},
  "takeover_allowed": false,
  "instances": [
    {"id": "blender-4711", "status": "active"},
    {"id": "blender-4822", "status": "refused", "reason": "..."}
  ]
}

A reconnect of the same process is not a takeover and is allowed: the pid identifies the instance, so a dropped socket is not mistaken for a second Blender.

If you must expose it

Don't, but if you do: keep it on a private network or behind an SSH tunnel, set ALLOW_PYTHON_EXECUTION=false, and treat the Blender process as compromised. blender.execute_python can run arbitrary code inside Blender, and Blender's Python can read your files.

Production notes

  • The request log is the only record of what a client did. It is one line per call, on stderr, and safe to ship to a collector.

  • Error messages sent to a client are structured and short. Tracebacks stay in the log, so a client cannot use an error to read the filesystem.

  • The add-on is standard-library only, has no network listener of its own, and only ever dials the bridge address you give it.

blender.execute_python

blender.execute_python is not a sandbox. It is intended for a trusted, local AI client. Blender's own Python API can already read and write files, open sockets and terminate the application; no amount of source screening changes that. Keep ALLOW_PYTHON_EXECUTION=false unless you trust the model and the prompt it is working from.

Two gates, both outside the tool so that no tool grows its own ad-hoc checks:

  1. Permission. ALLOW_PYTHON_EXECUTION=false (the default) refuses the call with PYTHON_EXECUTION_DISABLED before the socket is touched.

  2. AST screen. server/validation/python.py parses the code and rejects imports and calls that have no business in a 3D scripting session: os, subprocess, shutil.rmtree, socket, requests, urllib, pathlib writes, open, eval, exec, __import__, builtins, pickle, ctypes, importlib, dunder escapes such as __globals__ and __subclasses__, and the bpy.ops.wm calls that would replace or quit the file.

import bpy, import mathutils and the rest of the standard library stay available — they are what makes the tool useful at all. The screen is a filter against accidents, not a security boundary, and it is structured so a stricter policy can replace it later.

The code itself never runs in the MCP server. It is validated, forwarded, and executed by the add-on on Blender's main thread in a fresh namespace, where result is pre-defined and nothing leaks between calls.

When to use execute_python

It is a power-user escape hatch, not the intended path. In order of preference:

  1. blender.get_scene / get_objects / get_object to read the scene

  2. create_object / update_object / delete_object to change it

  3. blender.begin_transaction … commit_transaction to group the changes

  4. blender.render to look at the result

  5. blender.execute_python only for what the tools above do not cover

If a model is writing Blender Python for something a tool could do, that is a gap in the tools, not a reason to reach for step 5. The tools return compact, predictable JSON and push an undo step; a Python snippet can do neither for you.

Transactions, checkpoints and undo

Every mutating action pushes one Blender undo step, so a user can always step back by hand.

On top of that, four tools group a sequence:

blender.begin_transaction    → start recording
  …mutating calls…           → each object's state is captured before it changes
blender.checkpoint "shell"   → name a point in the middle
  …more calls…
blender.commit_transaction   → close it, keep the changes
blender.rollback_transaction → put everything back, or only what came after "shell"

Rollback restores recorded state — transforms, names, visibility, materials, collection membership — rather than replaying undo steps. That distinction matters: Blender's undo history is not readable from Python, so a count of steps is only right if nothing else touched the undo stack, and a user clicking around mid-transaction breaks that assumption. Restoring recorded state is exact for what the tools changed, whatever the user does in between. The undo steps stay on the stack for the human.

A checkpoint splits a transaction into stages, which is what a build with phases needs:

begin "layout"
  build the shell
checkpoint "shell"
  add the details
rollback to="shell"   → the details go, the shell stays, the transaction stays open

The limits, stated plainly: edits made through blender.execute_python are not recorded, so a transaction containing them restores only its tool calls — the rollback result says so in a note field rather than pretending otherwise. And an object deleted by a tool can be brought back, but only while its mesh data still exists; if Blender has purged it, the object comes back empty and the rollback result lists it under unrecoverable.

Configuration

All configuration is environment variables, read from the process environment or a .env file next to the server. See .env.example.

Variable

Default

Meaning

BLENDER_HOST

127.0.0.1

Address the add-on connects to

BLENDER_PORT

8765

Bridge port

MCP_TRANSPORT

stdio

stdio or streamable-http

MCP_HOST

127.0.0.1

Bind address for HTTP transport

MCP_PORT

8000

Bind port for HTTP transport

BLENDER_REQUEST_TIMEOUT

30.0

Seconds to wait for a response

BLENDER_CONNECT_TIMEOUT

5.0

Socket connect timeout used by the add-on

BLENDER_RENDER_TIMEOUT

600.0

Seconds to wait for a render

ALLOW_PYTHON_EXECUTION

false

Gate for blender.execute_python

BLENDER_ALLOW_TAKEOVER

false

Let a newly connected Blender replace the attached one

ENABLED_TOOLS

all

Comma-separated allowlist of tool names

LOG_LEVEL

INFO

DEBUG, INFO, WARNING, ERROR, CRITICAL

LOG_FORMAT

see .env.example

logging format string

Names are explicit about scope on purpose: BLENDER_REQUEST_TIMEOUT is the wait for a Blender response, BLENDER_RENDER_TIMEOUT is the longer one a render needs, and MCP_PORT only matters if you switch away from stdio.

ENABLED_TOOLS is for narrowing the surface, not for switching things on:

ENABLED_TOOLS=blender.get_scene,blender.get_objects,blender.get_object,blender.create_object

Read-only tools stay available whatever the list says, because a client that cannot read the scene cannot do anything sensible with it. An unknown name in the list is refused at startup rather than ignored, so a typo does not quietly remove a tool you meant to keep.

2026-09-27 12:30:21 INFO server.mcp.support: mcp tool=blender.create_object request=7 duration_ms=41.8 success=true
2026-09-27 12:30:24 INFO server.mcp.support: mcp tool=blender.render request=8 duration_ms=3012.4 success=true
2026-09-27 12:30:25 INFO server.mcp.support: mcp tool=blender.get_object request=9 duration_ms=2.1 success=false error=OBJECT_NOT_FOUND
2026-09-27 12:30:26 INFO server.blender.connection: Blender add-on connected from ('127.0.0.1', 51234)

One line per MCP call, in a fixed key=value shape, added by server/mcp/support.py at registration so no tool has to remember to do it:

Field

Meaning

tool

The MCP tool name

request

The MCP request id, so a line can be tied to a client trace

duration_ms

Wall-clock time of the tool call

success

true / false

error

The structured error code, when it failed

An expected failure is INFO with no traceback, because a model asking for an object that is not there is normal operation. A traceback appears only for something nobody anticipated, and it stays in the log rather than in the reply.

Project layout

blender-mcp/
├── README.md  LICENSE  pyproject.toml  .env.example  .gitignore
│
├── server/                      # runs as its own process; never imports bpy
│   ├── main.py                  # entry point: python -m server.main
│   ├── config.py                # Settings from the environment
│   ├── errors.py                # error codes and the structured envelope
│   ├── transactions.py          # begin / commit / rollback helpers
│   ├── mcp/
│   │   ├── server.py            # assembles tools, resources, lifespan
│   │   ├── support.py           # AppContext: how a tool reaches the bridge
│   │   ├── tools/               # scene, objects, render, python, transactions
│   │   └── resources/           # blender://scene, objects, render/latest
│   ├── blender/                 # the transport
│   │   ├── protocol.py          # pydantic request/response models
│   │   ├── client.py            # one connection, request/response correlation
│   │   └── connection.py        # the WebSocket server, one active add-on
│   └── validation/
│       └── python.py            # the AST policy screen
│
├── addon/blender_mcp/           # runs inside Blender; never imports the MCP SDK
│   ├── __init__.py              # bl_info, register(), unregister()
│   ├── protocol.py              # stdlib mirror of the server's protocol
│   ├── websocket.py             # minimal RFC 6455 client (no dependencies)
│   ├── connection.py            # socket thread, reconnect, main-thread dispatch
│   ├── operators.py             # every bpy operation
│   ├── executor.py              # executes model-written code
│   └── ui.py                    # N-panel and its operators
│
├── tests/                       # pytest; 437 tests (338 without bpy)
│   └── support/                 # protocol double, stubs, the bpy gate
│
├── examples/                    # bridge scripts, acceptance checks, GUI demo
├── docs/                        # what the two demo scripts produce
└── …

The layering is enforced by tests, not just convention: tests/test_architecture.py fails the build if bpy appears anywhere under server/, or if the MCP SDK or pydantic appear anywhere in the add-on.

Development

pip install -e ".[dev]"

pytest                       # the whole suite
pytest tests/test_protocol.py -v
pytest -k "not subprocess"   # skip the spawned-server smoke test

ruff check .
ruff format .
mypy server

The suite is layered to match the architecture:

File

Covers

test_protocol.py

Request/response/error serialisation, malformed frames, action classification

test_validation.py

The Python policy screen: what is allowed, what is blocked, why

test_tools.py

Each tool's request shape, and the error it returns

test_transport.py

The add-on's real WebSocket client against the real server: handshake, framing, pings, timeouts, disconnects, takeover

test_end_to_end.py

A full MCP session against a Blender double over a real socket, plus request logging through the real registry

test_addon_protocol.py

The add-on's stdlib protocol copy has not drifted from the server's

test_architecture.py

The layering rules, and that every module compiles

test_request_logging.py

One log line per call: id, duration, outcome, error code, and that the signature survives instrumentation

test_entrypoint.py

python -m server.main spawned as a subprocess and driven over stdio

test_blender_integration.py

Needs bpy. The operator layer against real Blender: primitive sizes, transforms, get_objects paging, rename, materials, mesh statistics, undo, execute_python, a real render

test_blender_bridge.py

Needs bpy. The whole chain, MCP client → server → add-on → real scene

Where a real Blender is not available, the tests use a protocol double (tests/support/fake_blender.py): it speaks the real protocol over a real WebSocket using the add-on's own client, and keeps a small in-memory object store. It verifies the transport and the MCP surface. It does not verify bpy, and it is not dressed up as if it did — mesh generation, modifiers and rendering are exercised by running the real add-on in Blender.

Roadmap

Planned, roughly in the order they become useful:

  • Richer mesh operations — primitives beyond the six, booleans, extrusion, subdivision, mesh statistics per selection rather than per object

  • Materials — create_material and the node properties models actually reach for (metallic, roughness, node graphs). Today update_object(material=...) assigns by name and creates a flat colour, which covers "make this red" and nothing more

  • Modifiers — add, configure, order, remove

  • Geometry nodes — build and wire node trees

  • Textures and UV — image textures, UV operations, unwrapping

  • Collections — create, nest, move objects between them

  • Camera and light controls — add cameras, set the active one, place and tune lights, depth of field. Today this is execute_python territory

  • Animation — keyframes, drivers, frame ranges, playback control

  • Rigging — armatures and pose

  • Scene diff — "what changed since I last looked?", so a model can verify its own work instead of re-reading everything

  • Render resources — blender://render/latest, and the image returned as MCP image content

  • Vision feedback loop — blender.inspect_image: hand a render to a vision model and get critique back. This is the piece that closes the loop plan → build → render → look → correct → render, and it is why the render tool's response shape leaves room for the image itself. The vision model belongs to the client, not here

  • Transaction and undo — named checkpoints, selective undo

  • Batch operations — apply a list of operations in one call, with one undo step and per-item results

  • Blender event notifications — push notifications when the scene changes underneath the model, rather than having it poll

  • Authentication — a token on the bridge, so it can leave loopback

  • Multi-Blender — a bridge that routes to several instances by name

Out of scope on purpose, and not planned: distributed deployment, Docker, Kubernetes, multi-user, a database, a web dashboard, vector stores and RAG. This is a local MCP backend, and adding those would not make it better at being one.

Not this project's job

This is an MCP server and nothing more. It has no opinion about models, and it must stay that way for a client to be able to swap one out:

  • no OpenAI, Anthropic or Gemini SDK, no provider-specific branches, no if model == ... anywhere

  • no GUI toolkit, no chat, no benchmark harness

  • no Tripo or any other asset-provider integration

A separate GUI client may use blender-mcp as an MCP backend, exactly the way Claude Desktop or Cursor does:

                  Blender AI Studio            (a separate project)
                         |
                     AI Agent
                    /        \
                  LLM        Tools
                 /             \
        GPT / Claude /      MCP Client
        Gemini / etc.           |
                                v
                          blender-mcp
                                |
                             Blender

The agent on top may also call other providers — Tripo, a vision model — but it does that itself. blender-mcp only speaks MCP to Blender, which is why it can sit underneath any of them.

License

MIT — see LICENSE.

Available Tools

15 tools
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.

ParametersJSON Schema
NameRequiredDescriptionDefault
labelNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses the recording semantics of the transaction, that rollback reverts all recorded calls, and the specific failure mode TRANSACTION_ACTIVE when one is already open. Missing details such as permission requirements or nesting behavior keep it from a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action, then the parameter note, then the error condition, each in its own compact block. Every sentence earns its place; no filler or repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be described. For a single-optional-parameter transaction opener, the definition covers semantics, parameter meaning, and the key failure mode, leaving nothing an agent needs before calling it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must compensate, and it does: the label parameter is explained as an optional name marking the starting point, with a concrete example ("layout") and its use case (later rollbacks by name). This adds real meaning beyond the bare string type in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("Open a transaction") and explicitly names the sibling that undoes it (blender.rollback_transaction), so the agent can separate it from the rollback path. It does not contrast with the other transaction siblings (commit_transaction, checkpoint), so the differentiation is partial rather than complete.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

"Every mutating call until commit or rollback is recorded" communicates the intended position in the workflow (open before mutating, close via commit/rollback), and the named rollback_transaction alternative gives context. There is no explicit when-not or comparison to checkpoint, so it falls short of full routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
labelYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the burden and does well: it explains the effect ('a later rollback can stop there instead of unwinding everything') and the consequence for partial work ('discards only the details'). It does not state what happens if no transaction is open or if the label already exists, so minor gaps remain.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose, followed by a compact worked example and a one-line parameter note. Every sentence earns its place, though the build-stage example is slightly more verbose than strictly necessary.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained, and the description covers purpose, workflow, and the one parameter's constraint. It omits edge-case behavior (no open transaction, duplicate label), which would round it out fully.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must compensate; it adds the key constraint that 'label' is required and 'unique within the transaction', which the bare string schema does not convey. However, it gives no format or syntax guidance, leaving the compensation partial.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('Record a named point inside the open transaction') and immediately clarifies its role relative to rollback. An agent can distinguish it from begin_transaction, commit_transaction, and rollback_transaction without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives an explicit usage pattern ('begin -> build the shell -> checkpoint "shell" -> add the details -> commit') that makes the when-to-use condition concrete. It does not name when-not-to-use or explicitly contrast with sibling tools, so it falls just short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does meaningful work: it discloses that undo steps remain on Blender's undo stack (so the commit is not an irreversible destruction of history) and names a specific failure mode. It does not cover permissions, rate/scope limits, or whether other sessions see the change.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences with no filler: the primary effect is front-loaded, followed by the reversibility note and the error condition. Every sentence adds information an agent needs.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need no explanation, and with zero parameters the only real gaps were behavior and failure modes, both of which are addressed. What remains unstated is how this interacts with the sibling transaction tools (e.g. an implicit open begin_transaction), a minor omission for a simple commit primitive.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is nothing for the description to disambiguate and the baseline of 4 applies. No param documentation is missing.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Close the open transaction and keep the changes'), and the phrase 'keep the changes' implicitly contrasts with rollback_transaction's discard semantics. It stops short of naming the sibling, so an agent must infer the pairing from the sibling list rather than the description.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'keep the changes' framing gives clear context for when to call this versus discarding work, and the stated TRANSACTION_NOT_ACTIVE precondition tells the agent an open transaction must exist first. There is no explicit routing to begin_transaction or rollback_transaction for the when-not case, which keeps it below a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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]}

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesUnique object name; an existing name is refused.
typeYesPrimitive to create: cube, sphere, cylinder, cone, plane or torus. Case does not matter. The schema lists the valid values, but the tool is case-tolerant because a model writing CUBE should not be turned away for it.
scaleNo
locationNo
rotationNo
collectionNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses that duplicate names fail with OBJECT_ALREADY_EXISTS rather than being silently renamed, that coordinates are in Blender units (meters), and that rotation is in degrees applied in XYZ Euler order. It stops short of covering mutation side effects on the scene, undo/transaction interaction, or permissions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One summary sentence followed by a parameter glossary and a compact JSON example; every line adds information and the most important scoping fact leads. No padding or repetition of the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 6-parameter creation tool with an output schema present (so return values need not be described) and no annotations to lean on, the description covers nearly everything an agent needs, including error behavior. The only real hole is the undocumented 'collection' parameter.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is only 33%, so the description must compensate, and it largely does: units and [x, y, z] vector format for location/scale, degrees and Euler order for rotation, enum values and case tolerance for type, and uniqueness semantics for name. The 'collection' parameter, however, receives no mention anywhere, leaving one of six parameters undocumented.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Create a mesh object') plus a scope constraint ('in the active scene'), which an agent can immediately distinguish from siblings like update_object, delete_object, and get_object. The primitive-type listing further pins down what is created.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description never says when to reach for this tool versus alternatives such as clone/instance creation or execute_python, nor does it state exclusions. Usage is only implied by the tool name and the create semantics.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden, and it does disclose a pre-flight existence check and the OBJECT_NOT_FOUND failure mode. However, it omits whether the deletion is reversible, whether it can be committed/rolled back within a transaction, and what permissions or scene state are required.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, operation stated first, failure mode second. No filler and nothing buried.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be described. But for a destructive mutation with zero annotation coverage embedded in a scene with explicit transaction siblings, the description should address reversibility or transaction interaction to be fully complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

One parameter, "name", at 0% schema description coverage. The description clarifies that the name must refer to an existing object in the active scene, which adds a real constraint beyond the bare schema, but does not specify lookup semantics (exact match, path, case sensitivity).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb ("Delete") plus resource ("an object from the active scene"), with scope made explicit. The verb alone cleanly separates it from create_object, update_object, and get_object among the siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The action implies its own usage, but nothing states when to reach for this over alternatives like update_object, nor whether the deletion participates in the transaction tools (begin_transaction/commit/rollback) that sit alongside it. Usage is inferred rather than guided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

blender.execute_pythonA

Execute Python inside the running Blender and return whatever the snippet left in a variable named result.

code: statements to run. import bpy, import mathutils and the standard library are available. Anything that touches the network, spawns processes, reads or writes files, or reaches for eval/exec/import is rejected with VALIDATION_ERROR before Blender is contacted. Assign a JSON-serialisable value to result to get structured data back; otherwise only a summary is returned.

  Otherwise, if the server was started with ALLOW_PYTHON_EXECUTION=false,
  the call is refused with PYTHON_EXECUTION_DISABLED without contacting Blender.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
codeYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden and does so richly: it names the two failure modes and their error codes (VALIDATION_ERROR, PYTHON_EXECUTION_DISABLED), the ALLOW_PYTHON_EXECUTION server flag, the exact rejected constructs, and the explicit 'policy screen, not a sandbox' caveat about Blender's own API power.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the purpose and the return convention, then restrictions, then error/failure conditions in a logical order. The example earns its place for a code-execution tool, though the repeated 'Otherwise' connective and the trailing policy paragraph add some length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite no annotations and a minimal schema, the description covers purpose, parameter conventions, failure modes, and return behavior (even though an output schema exists). An agent has everything needed to call it correctly or decide against it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% for the single `code` parameter, so the description must compensate, and it does: available imports, banned operations, the `result` assignment convention for structured output, and a worked example together give far more meaning than the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource — execute Python inside the running Blender — and clarifies the return convention (`result` variable). It is unmistakably distinct from the structured siblings like get_objects, create_object, or render.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It signals the intended audience ('a trusted, local AI client') and warns that enabling the tool is a deliberate choice, which implies caution, but it never says when to prefer this over the structured siblings or when it is inappropriate. Usage is implied rather than routed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden and does so well: it discloses ordering ('newest first'), the significance of statuses 'refused' and 'replaced,' and that the tool answers even when no Blender is connected. These are exactly the runtime traits an agent needs to interpret results correctly.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tightly written sentences with no filler. The core purpose is front-loaded, and each subsequent sentence adds distinct behavioral or usage context.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given that the tool has no parameters and an output schema exists, the description is complete enough for correct selection and invocation. It explains purpose, usage context, ordering, status semantics, and fallback behavior without needing to restate return fields.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4. The description correctly avoids implying any parameters, and the empty schema is fully covered.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'List the Blender instances that have connected to this server, newest first.' It also distinguishes this tool from all siblings by explaining that the active instance is what every other tool acts on, so an agent can tell this is the connection-history tool rather than an object/scene tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides clear context for when to call it: 'useful first call after a connection error,' and explains that the active instance is the target of every other tool. It does not explicitly state when not to use it or name an alternative, but no sibling appears to serve the same purpose.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden, and it does disclose one concrete failure mode (OBJECT_NOT_FOUND for a missing name), which is genuinely useful. It is silent on the read-only/safety profile, permissions, and whether the name is scoped to the active scene, so it is helpful but incomplete.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences: the payload contents are front-loaded and the error condition is appended without padding. Every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Returned fields are covered by the output schema (and restated), and the failure mode is documented, so an agent has what it needs to call and interpret a read. It could be slightly more complete by routing to get_objects for plural lookups.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but there is only one self-evident parameter ('name'). The description adds that a nonexistent name is an error case, clarifying that name must identify an existing object; it still omits format details such as exact-match or case sensitivity.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Return the full detail of a single object') and immediately contrasts with the plural sibling get_objects by scoping to 'a single object'. It even enumerates the returned detail, so an agent knows exactly what it gets without opening the output schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'single object' implies this is the singular counterpart to blender.get_objects, giving implied usage guidance. However, it never explicitly says when to prefer this over get_objects, get_scene, or get_instances, and lists no prerequisites or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
typeNo
limitNo
offsetNo
collectionNo
name_containsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are supplied, so the description carries the full burden, and it does disclose the read-only listing nature, the page window semantics, and what "total"/"truncated" mean for deciding whether to page. It omits permission/authorization expectations and does not state reversibility (moot for a read), which keeps it just under a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded purpose sentence, then a tight per-parameter block, then the return/paging note. Every line earns its place and nothing is repeated or padded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a filtered, paginated list tool with five optional parameters the description covers purpose, sibling routing, all parameters, and pagination behavior; the output schema already handles the return shape, so nothing an agent needs to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate fully, and it does: every one of the five parameters is explained with examples (""MESH", "CAMERA", "LIGHT""), semantics (case-insensitive substring, only objects linked to the collection), and constraints (limit 1-500, offset skip count). This adds real meaning beyond the bare property names and defaults.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("List objects") with the distinguishing scope ("with filters and a page window") and explicitly contrasts itself with the sibling blender.get_scene for large scenes. An agent can tell it apart from get_scene/get_object without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a clear usage condition: use this when the scene is too large for blender.get_scene to be useful, and the paging fields tell the agent when to request the next page. It does not mention other plausible alternatives (e.g. blender.get_object for a single object, blender.get_instances), so it falls short of naming explicit when-not cases.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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 object_limit objects are inlined (200 by default); when objects_truncated is true, use blender.get_objects to page through the rest. No mesh data is included.

ParametersJSON Schema
NameRequiredDescriptionDefault
object_limitNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden, and it does disclose the key behavioral traits: results are truncated at object_limit (200 by default), an objects_truncated flag signals overflow, and no mesh data is returned. It stops short of stating read-only/safety or any permission requirements, which for a read-style tool is a minor omission.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the return summary, then usage routing, then the truncation caveat and the mesh-data exclusion. Four tight sentences, each carrying distinct information, with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be re-explained, yet the description still covers the practical invocation facts (default limit, truncation flag, paging escape hatch, absence of mesh data). Nothing an agent needs to call this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% and the single parameter is bare, but the description compensates by explaining that at most `object_limit` objects are inlined and that 200 is the default, plus the related objects_truncated output signal. That gives real meaning beyond the schema, though the parameter's exact interaction with paging is only implied.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Return a compact, LLM-sized summary of the active scene') and enumerates exactly what is returned: object names/types/transforms/visibility, collections, cameras, lights, active object, render engine, current frame. This is clearly distinguishable from sibling readers like blender.get_object (single object) and blender.get_objects (paged listing).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Use this first to see what exists' gives an explicit ordering/entry-point instruction, and it names the alternative ('use blender.get_objects to page through the rest') together with the triggering condition ('when objects_truncated is true'). When-to-use and the alternative are both spelled out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
engineNo
samplesNo
output_pathNo
resolution_xNo
resolution_yNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With zero annotations, the description carries the full burden and does reasonably well: it discloses the return values, warns that "Render time grows quickly with resolution and samples," and clarifies that omitted args preserve current scene settings (implying overrides may mutate scene state). It stops short of stating whether the call blocks or what permissions/preconditions apply.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded purpose sentence followed by tightly formatted per-parameter notes and a closing perf tip; every line earns its place. Slightly verbose but no padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 5-param, all-optional render tool with an output schema and no annotations, the definition covers parameters, defaults, side-effect-preserving omissions, and cost trade-offs. The main gap is routing guidance versus blender.render_preview and confirmation of blocking/async behavior, but the return values are covered by the output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate and it does: each of the 5 params gets real semantics — engine ids with an example and a pointer to blender.get_scene, resolution units in pixels, samples behavior (ignored by non-sampling engines), and output_path path conventions. This is meaningful context well beyond the schema's bare anyOf/null types.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ("Render the active scene with the current camera") and even names the return payload (output path and wall-clock render time). It is clear on its own, but it never mentions the sibling blender.render_preview, so an agent must infer which of the two rendering tools to pick.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The perf note ("prefer 512-1024 px while iterating") and the per-parameter "Omit to keep..." defaults imply a usage posture, and the engine note routes to blender.get_scene for valid ids. However there is no explicit when-to-use or when-not-to-use guidance and no comparison against blender.render_preview, which is the obvious alternative.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
engineNo
samplesNo
max_edgeNo
include_imageNo

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the burden and does well: it discloses the return format (image vs. file path via include_image), that size and samples are capped, and the context cost of larger max_edge. It does not state whether supplying engine/samples mutates scene state, which matters for a tool that can alter the active render configuration.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose, then a compact per-parameter block, then the sibling routing rule last. Every line carries information; nothing is redundant with the schema titles.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, yet the description explains what comes back (image or path) and the tradeoff governing it, so an agent can call it correctly. The only unaddressed gap is whether render settings persist in the scene after the call.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the description fully compensates: max_edge gets a range (64-2048), a default (512), and cost rationale; engine and samples are marked optional with 'omit to keep current/cheap default'; include_image's false behavior is spelled out.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Render a small preview and return the image') plus the agent-facing rationale, and implicitly contrasts with the sibling blender.render by calling it a 'preview'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Prefer this over blender.render while iterating' and names the alternative for the opposite case ('Use blender.render for a final image the user will keep'), so the selection condition is unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
toNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does so well: it discloses the restoration scope, the critical limitation that blender.execute_python edits are untracked (so a mixed transaction only rolls back its tool calls), and the specific error raised when no transaction is open. This is exactly the behavioral context an agent needs for a mutation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action, then parameter semantics, then restoration scope and caveats, closing with the error case. Every sentence carries distinct information with no repetition of the tool name or title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return values need not be explained; the description instead covers the operation's scope, the untracked execute_python edge case, and the failure mode. For a one-parameter transaction mutation tool, nothing an agent needs in order to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% for the single `to` parameter, so the description must compensate and does: it defines `to` as an optional checkpoint label, explains the omit-vs-name semantics, and pairs it with the rewind target behavior. Nothing about the parameter is left ambiguous.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a precise verb ('put back') and the exact resource scope (everything the transaction changed), then enumerates what gets restored: transforms, names, visibility, materials and collection membership. An agent can distinguish this from commit_transaction and begin_transaction without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Clearly specifies the two usage modes of the tool via the `to` parameter (omit to rewind to transaction start, name a checkpoint to rewind to it) and the failure condition TRANSACTION_NOT_ACTIVE. It does not explicitly state when to prefer rollback over commit_transaction or confirm the transaction closes afterwards, so guidance is clear but not exhaustive.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes
scaleNo
locationNo
materialNo
new_nameNo
rotationNo
dimensionsNo
visibilityNo
material_colorNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and delivers: explicit unit conventions (meters, degrees), the OBJECT_ALREADY_EXISTS failure for name collisions instead of silent 'Name.001' renaming, the side effect that a missing material is created, and the 'material_created' response flag. These are non-obvious behaviors an agent could not infer from the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the partial-update rule, then a scannable field-by-field list where every line contributes new information (units, error behavior, or side effects). No filler sentences.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 9-parameter mutation tool with no annotations and no schema descriptions, the definition covers semantics, units, error cases, and side effects thoroughly, and an output schema exists to cover return values (which the description still references via 'material_created'). Nothing essential to correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate, and it does: it defines units and axis order for location/scale/rotation/dimensions, notes dimensions replaces scale, and explains that material_color is only used when creating a material and sets both viewport colour and shader. Only the obvious 'name' identifier is left implicit.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb+resource ('Change an existing object') and immediately scopes it as a partial update ('Only the fields you pass are touched'). This clearly distinguishes it from sibling create_object/delete_object, which operate on different lifecycles.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives strong operational context — omitted fields retain their current value, and material is created on demand — which tells the agent how to call it safely. However, it never names an alternative (e.g. create_object for new objects, or the transaction siblings) or states when not to use this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

blender.wait_for_changeA

Wait until the scene changes, or until timeout seconds pass.

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": [...]}. changed false means nothing happened before the timeout, which is an answer rather than a failure.

ParametersJSON Schema
NameRequiredDescriptionDefault
objectsNo
timeoutNo
include_imageNo

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses early-return-on-change semantics, the 0-60s timeout window, and clarifies that changed=false is a valid answer rather than a failure. It does not state whether the call blocks/side effects or permissions, but for a passive wait this is minor.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core behavior, then parameters, then return shape — a logical order with no filler sentences. Slightly long for three parameters, with some prose that could be tightened, but nothing is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, and the description supplies the exact return keys (changed, changes, waited, changed_objects) plus their interpretation. Combined with usage guidance and parameter semantics, an agent has everything needed to call and interpret this tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate fully and does: it gives timeout's range and default, explains objects as an optional narrowing filter with explicit omit behavior, and explains include_image's purpose. Every one of the 3 parameters gains meaning beyond the bare schema types.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('wait until the scene changes, or until timeout seconds pass') and immediately differentiates itself from the read-everything siblings ('instead of reading the whole scene again'). An agent can distinguish it from blender.get_scene or blender.render without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly names the situation to use it ('after a change you are unsure about') and the alternative it replaces ('instead of reading the whole scene again'). It also specifies the include_image condition to avoid a second render call, which is concrete routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 15 tool updatesv0.1.0
    • First observedblender.begin_transaction
    • First observedblender.checkpoint
    • First observedblender.commit_transaction
    • First observedblender.create_object
    • First observedblender.delete_object
    • First observedblender.execute_python
    • First observedblender.get_instances
    • First observedblender.get_object
    • First observedblender.get_objects
    • First observedblender.get_scene
    • First observedblender.render
    • First observedblender.render_preview
    • First observedblender.rollback_transaction
    • First observedblender.update_object
    • First observedblender.wait_for_change

TDQS

A4.3/5.0

Scored across 15 tools

Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count5/5

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.

Completeness4/5

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers