Blender MCP
An MCP server that enables AI agents to control Blender through natural language for 3D scene creation, animation, and rendering. It provides over 550 actions and direct Python execution via a bridge to a live local Blender session.
README
Inspired by blender-mcp by Siddharth Ahuja — the original proof-of-concept that demonstrated connecting AI agents to Blender over MCP. This project builds on that idea with a production-grade architecture: 69 tool groups, 550+ actions, multilingual intent routing, BVH assembly analysis, thread-safe execution, and a 499-test suite.
Blender MCP
Control Blender with AI — naturally, reliably, at production scale.
Give Claude, GPT, or any MCP-capable AI the ability to create, inspect, and animate 3D scenes in Blender using plain language and structured tool calls.
What is this?
MCP (Model Context Protocol) is an open standard that lets AI assistants call structured tools — the same way a programmer calls an API. This project implements an MCP server that bridges your AI assistant to a live Blender session running on your computer.
In practice: tell your AI "create a red metallic sphere above the cube" or "check if all drone parts are touching" and it will call the right Blender tools, get real geometry data back, and show you a screenshot — no Python required from your side.
Local-only. Blender must be installed and running on your machine. The
bpyAPI only exists inside Blender's own Python — there is no cloud version.
execute_blender_codeis the primary tool. Almost everything you can do with the other 68 tools can also be done directly withexecute_blender_code(which gives fullbpyPython access). The specialized tools exist for three reasons: they provide structured, validated inputs the AI can use reliably; they implement complex operations (like BVH assembly analysis or multi-view screenshots) that would be verbose to write from scratch each time; and they are useful for learning how specific Blender operations work.
Note on tool errors. Some tools may return errors in certain Blender states (e.g. sculpt tools require an active mesh in sculpt mode, render tools require a camera). The AI may also occasionally call a tool with slightly wrong parameters — this is normal. The parameter validator will coerce or reject bad values with a clear error message, and the AI will self-correct on the next attempt. If a tool consistently fails,
execute_blender_codecan usually accomplish the same thing directly.
Architecture
<details> <summary><strong>System diagram</strong></summary>
Claude / AI Agent
│ stdio (JSON-RPC 2.0 / MCP)
▼
stdio_bridge.py ← MCP bridge [standard Python, runs outside Blender]
│ · Validates JSON schemas before forwarding
│ · Caches tool list from Blender on first connect
│ TCP localhost:9879 (4-byte Big-Endian length-prefix + JSON body)
▼
Blender Addon ← blender_mcp/__init__.py [runs inside Blender]
├── dispatcher.py Command router + handler registry (HANDLER_REGISTRY)
├── handlers/ 52 handler modules (manage_*.py)
│ manage_scene_comprehension.py 11-action scene intelligence suite
│ manage_rendering.py Render, screenshot, view control
│ manage_scripting.py execute_blender_code (safe eval)
│ polyhaven / sketchfab / hunyuan / hyper3d (4 external integrations)
└── core/
protocol.py Wire protocol (4-byte header + JSON)
thread_safety.py All bpy calls routed to Blender's main thread
execution_engine.py Safe bpy.ops wrapper + ExecutionPolicy
parameter_validator.py Type coercion + JSON schema validation
intent_router.py Multi-language intent classification (EN/TR/FR)
semantic_memory.py Tag-based object resolution
security.py High Mode / Safe Mode toggle
job_manager.py Async subprocess + internal job queue
All bpy operations are marshalled to Blender's main thread via bpy.app.timers, preventing EXCEPTION_ACCESS_VIOLATION crashes that would occur if a background TCP thread touched the Blender API directly.
See docs/ARCHITECTURE.md for detailed diagrams.
</details>
Installation
<details open> <summary><strong>Prerequisites</strong></summary>
| Requirement | Version | Notes |
|---|---|---|
| Platform | Windows / macOS / Linux | All three are supported — the MCP protocol and wire format are platform-agnostic |
| Blender | 5.0 or later | Must be installed and running locally |
| Python | 3.10 or later | For the MCP bridge (outside Blender) |
| uv | latest | Recommended — fast, isolated environments |
| git | any | To clone the repository |
</details>
Step 1 — Clone the repository
git clone https://github.com/glonorce/Blender_mcp.git
cd Blender_mcp
Step 2 — Set up the Python environment
<details> <summary>Using <strong>uv</strong> (recommended)</summary>
# Create .venv and install all dependencies in one command
uv sync --all-extras
# Verify everything works (no Blender needed for tests)
uv run pytest tests/unit -q
# → 499 passed in ~1.4s
uv sync creates .venv/ in the project directory — your system Python stays clean.
All uv run <cmd> calls use this environment automatically, no manual activation needed.
</details>
<details> <summary>Using <strong>pip</strong> (alternative)</summary>
python -m venv .venv
# Windows:
.venv\Scripts\activate
# macOS / Linux:
source .venv/bin/activate
pip install -e ".[dev]"
</details>
Step 3 — Build the Blender addon ZIP
python create_release_zip.py
# → blender_mcp_v1.0.0.zip
Step 4 — Install the addon in Blender
Blender → Edit → Preferences → Add-ons → Install
→ select blender_mcp_v1.0.0.zip
→ enable "Blender MCP"
The addon starts a TCP server on port 9879 automatically.
Verify: press N in the 3D Viewport → MCP tab → status should show Listening on :9879.
MCP Client Configuration
Replace <path-to-blender-mcp> with the absolute path where you cloned this repo.
<details open> <summary><strong>Claude Desktop</strong> — <code>claude_desktop_config.json</code></summary>
Windows: %APPDATA%\Claude\claude_desktop_config.json
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"blender": {
"command": "python",
"args": ["-u", "<path-to-blender-mcp>/stdio_bridge.py"],
"env": {
"BLENDER_HOST": "localhost",
"BLENDER_PORT": "9879",
"MCP_TRANSPORT": "stdio",
"PYTHONPATH": "<path-to-blender-mcp>"
}
}
}
}
</details>
<details> <summary><strong>Cursor / Windsurf / VS Code</strong> — <code>.cursor/mcp.json</code> or <code>.vscode/mcp.json</code></summary>
{
"mcpServers": {
"blender": {
"command": "python",
"args": ["-u", "<path-to-blender-mcp>/stdio_bridge.py"],
"env": {
"BLENDER_HOST": "localhost",
"BLENDER_PORT": "9879",
"MCP_TRANSPORT": "stdio",
"PYTHONPATH": "<path-to-blender-mcp>"
}
}
}
}
</details>
<details> <summary><strong>OpenAI Codex CLI</strong> — <code>~/.codex/config.toml</code></summary>
[mcp_servers.blender]
command = "python"
args = ["-u", "<path-to-blender-mcp>/stdio_bridge.py"]
cwd = "<path-to-blender-mcp>"
enabled = true
[mcp_servers.blender.env]
BLENDER_HOST = "localhost"
BLENDER_PORT = "9879"
MCP_TRANSPORT = "stdio"
PYTHONPATH = "<path-to-blender-mcp>"
</details>
<details> <summary><strong>Generic shell / .env</strong></summary>
export BLENDER_HOST=localhost
export BLENDER_PORT=9879
export MCP_TRANSPORT=stdio
export PYTHONPATH=<path-to-blender-mcp>
python -u <path-to-blender-mcp>/stdio_bridge.py
</details>
-uflag — disables Python's stdout/stderr buffering. Required for stdio transport. Without it, MCP messages may be held in Python's internal buffer and never reach the client.
<details> <summary>Environment variables reference</summary>
| Variable | Default | Description |
|---|---|---|
BLENDER_HOST |
localhost |
Hostname where Blender's addon is listening |
BLENDER_PORT |
9879 |
TCP port of the Blender addon |
MCP_TRANSPORT |
stdio |
Transport mode (stdio for all current clients) |
PYTHONPATH |
— | Must include the project root so blender_mcp is importable |
</details>
Verify the Connection
Tool: get_server_status
→ {
"status": "active",
"blender_version": [5, 0, 0],
"blender_language": "en_US",
"handler_count": 69,
"next_step": "Call list_all_tools to see all available tools…"
}
Recommended First Workflow
1. manage_agent_context GET_PRIMER → full quick-start guide for the AI
2. get_scene_graph GET_OBJECTS_FLAT → what objects exist + world positions
3. get_viewport_screenshot_base64 → see the current viewport
4. execute_blender_code → create or modify objects
5. get_viewport_screenshot_base64 → confirm the result visually
ESSENTIAL Tools (priority 1–9)
<details open> <summary>These 9 tools handle the vast majority of tasks. The AI discovers them first.</summary>
| Pri | Tool | Purpose |
|---|---|---|
| 1 | execute_blender_code |
Full bpy Python API — the primary tool for everything |
| 2 | get_scene_graph |
11-action scene intelligence suite |
| 3 | get_viewport_screenshot_base64 |
Visual verification — see what Blender sees |
| 4 | get_object_info |
Deep object inspector — modifiers, constraints, animation |
| 5 | get_local_transforms |
Parent-relative coordinates |
| 6 | manage_agent_context |
Workflow guides (GET_PRIMER, GET_TACTICS) |
| 7 | list_all_tools |
Tool discovery with intent filtering (77% token savings) |
| 8 | get_server_status |
Health check — version, language, handler count |
| 9 | new_scene |
Create an empty scene |
</details>
⚡ execute_blender_code
The primary and most powerful tool. Executes arbitrary Python with full bpy access inside Blender.
Note: Almost all of the other 68 tools are essentially wrappers around things you can also do with
execute_blender_code. The specialized tools add structured validation, complex analysis algorithms, and clear action names — which makes them more reliable and readable for AI agents. But for one-off operations or learning whatbpycan do,execute_blender_codeis often the fastest path.
One hardcoded guard: bpy.ops.render.render() is always blocked — it freezes Blender's main thread for the entire render duration, making the MCP unresponsive. Use manage_rendering action=RENDER_FRAME instead (async subprocess).
# Create a metallic sphere
import bpy, bmesh
bpy.ops.mesh.primitive_uv_sphere_add(radius=0.5, location=(0, 0, 1))
obj = bpy.context.active_object
mat = bpy.data.materials.new("Metal")
mat.use_nodes = True
bsdf = mat.node_tree.nodes["Principled BSDF"]
bsdf.inputs["Metallic"].default_value = 1.0
bsdf.inputs["Roughness"].default_value = 0.1
obj.data.materials.append(mat)
print(f"Created: {obj.name}")
🔍 get_scene_graph
11-action scene intelligence suite. The AI uses this to understand what's in the scene before modifying it.
<details> <summary>All 11 actions</summary>
| Action | Description |
|---|---|
GET_OBJECTS_FLAT |
All objects: world transforms, hierarchy, matrix_world, geometry_center_world |
GET_SCENE_MATRIX |
Deep spatial analysis: AABB center, nearest neighbors, distances |
ANALYZE_ASSEMBLY |
Integrity score 0–100: BVH surface gaps, interpenetration, hierarchy-aware pairs |
CHECK_INTERSECTION |
AABB intersection test between two objects |
GET_SPATIAL_REPORT |
Spatial summary for one object: bounds, nearby objects |
CAST_RAY |
Ray cast from origin=[x,y,z] in direction=[dx,dy,dz] |
VERIFY_ASSEMBLY |
Rule-based checks: must_touch, parent_must_be |
DETECT_GEOMETRY_ERRORS |
Per-object: non_manifold_edges, boundary_edges, zero_area_faces |
GEOMETRY_COMPLEXITY |
Triangle/vertex/ngon counts + complexity tier |
CHECK_PRODUCTION_READINESS |
Manifold, UV, materials, naming, origin alignment; score 0–100 |
GET_HIERARCHY_TREE |
BFS parent→children tree up to max_depth |
</details>
📸 get_viewport_screenshot_base64
Captures the viewport as a base64 PNG the AI can directly see and reason about.
<details> <summary>Key parameters</summary>
| Parameter | Example | Description |
|---|---|---|
view_direction |
"MULTI" |
FRONT, RIGHT, TOP, ISOMETRIC, or MULTI (all four) |
target_objects |
["Arm_L", "Body"] |
Frame specific objects instead of full scene |
gap_focus_m |
0.002 |
Auto-zoom to show a gap of this distance |
display_mode |
"MATERIAL" |
SOLID, MATERIAL, or WIREFRAME |
action |
"SMART_SCREENSHOT" |
Automatically picks the best view |
</details>
Tool Tier System
The AI context window would overflow if all 69 tools were listed in full detail every time. The tier system solves this:
| Tier | Priority | Count | Format in list_all_tools |
|---|---|---|---|
| ESSENTIAL | 1–9 | 9 | Full detail — title, description, all parameters |
| CORE | 10–49 | ~35 | Compact table row |
| STANDARD | 50–149 | ~20 | Compact table row |
| OPTIONAL | 150+ | 4 | Listed last (external integrations) |
# Filter by intent — reduces 69 tools to ~15 relevant ones (77% fewer tokens)
list_all_tools(intent="rig a character")
list_all_tools(intent="physics simulation")
list_all_tools(intent="export to Unity")
Supports English, Turkish, and French intent keywords.
Key Features
<details> <summary><strong>Assembly Intelligence</strong></summary>
get_scene_graph ANALYZE_ASSEMBLY gives a full integrity report:
- BVH surface-gap measurement — actual vertex-to-face distance, not just bounding-box
- INTERPENETRATION detection — BVH face overlap, not just AABB
- Hierarchy-aware pairs — always tests parent↔child; skips distant objects to avoid O(N²)
- Production readiness score 0–100 — manifold geometry, UV maps, materials, naming, origin alignment
</details>
<details> <summary><strong>Thread Safety</strong></summary>
All bpy calls from the TCP socket thread are automatically marshalled to Blender's main thread via bpy.app.timers. This prevents EXCEPTION_ACCESS_VIOLATION crashes that occur when background threads touch the Blender API.
</details>
<details> <summary><strong>Multilingual Intent Routing</strong></summary>
list_all_tools(intent="...") accepts keywords in English, Turkish, and French. The intent router maps your description to tool categories and returns only the relevant subset — dramatically reducing context window usage.
</details>
<details> <summary><strong>Visual Verification</strong></summary>
get_viewport_screenshot_base64 captures the viewport and returns it as base64 the AI can see:
- Multi-view in one call:
FRONT,RIGHT,TOP,ISOMETRIC gap_focus_m— zoom precisely to the scale of an assembly gaptarget_objects— frame specific parts, not the whole scene
</details>
Testing
Tests run without Blender — bpy is mocked with unittest.mock.MagicMock.
uv run pytest tests/unit -q # 499 unit tests, ~1.4s
uv run pytest tests -v --tb=short # Full suite
uv run pytest tests -v --cov=blender_mcp # With coverage report
uv run python scripts/quality/run_checks.py --fast # 8 quality checks
uv run python scripts/quality/run_checks.py # 12 quality checks
<details> <summary>Coverage map — 17 test files</summary>
| Module | Test File | Tests |
|---|---|---|
| 8 ESSENTIAL handlers | test_essential_tools.py |
183 |
core/parameter_validator.py |
test_parameter_validator.py |
60 |
core/intent_router.py |
test_intent_router.py |
35 |
core/execution_engine.py |
test_execution_engine.py |
31 |
dispatcher.py |
test_dispatcher_deep.py + test_dispatch_routing.py |
51 |
core/semantic_memory.py |
test_semantic_memory.py |
25 |
core/job_manager.py |
test_job_manager.py |
25 |
core/response_builder.py |
test_response_builder.py |
21 |
core/protocol.py |
test_protocol.py |
17 |
core/error_protocol.py |
test_error_protocol.py |
13 |
| Project structure | test_smoke.py |
10 |
| MCP bridge validation | test_engine.py |
9 |
handlers/manage_history.py |
test_manage_history.py |
9 |
core/security.py |
test_security.py |
6 |
| Geometry center computation | test_scene_graph_geo_center.py |
6 |
| Total | 17 test files | 499 |
</details>
See tests/TESTS.md for full documentation.
Developer Guide
<details> <summary><strong>Environment setup</strong></summary>
uv sync --all-extras # Install all dependencies
</details>
<details> <summary><strong>Quality gates</strong></summary>
make check # 12 checks: lint + format + type-check + tests
make check-fast # 8 checks: lint + custom checks
make test-fast # Unit tests only, stop on first failure
</details>
<details> <summary><strong>Tool inspection</strong></summary>
make inspect-summary # Compact table of all 69 tools
make inspect-essential # ESSENTIAL tier full detail
uv run python scripts/inspect_tools.py --tool get_scene_graph
uv run python scripts/inspect_tools.py --cat animation
</details>
<details> <summary><strong>Adding a new handler</strong></summary>
Create a file in blender_mcp/handlers/ with @register_handler. Auto-discovered — no list to update.
from ..dispatcher import register_handler
@register_handler(
"manage_my_feature",
actions=["CREATE", "DELETE", "LIST"],
schema={
"type": "object",
"properties": {
"action": {"type": "string", "enum": ["CREATE", "DELETE", "LIST"]},
"name": {"type": "string"},
},
"required": ["action"],
},
category="scene",
priority=60, # 1-9=ESSENTIAL, 10-49=CORE, 50-149=STANDARD, 150+=OPTIONAL
description="STANDARD — Create, delete, and list my features.",
)
def manage_my_feature(action, **params):
if action == "CREATE":
return {"created": params.get("name")}
elif action == "LIST":
return {"items": []}
</details>
<details> <summary><strong>Scripts reference</strong></summary>
See scripts/SCRIPTS.md for comprehensive documentation of every script.
python -m blender_mcp --help # Quick CLI overview
python -m blender_mcp --list-tools # List all tools
python -m blender_mcp --status # Project health
</details>
Project Structure
<details> <summary>Click to expand</summary>
Blender_mcp/
├── blender_mcp/ Blender addon (installs via ZIP)
│ ├── __init__.py Addon entry + BlenderMCPServer + N-panel UI
│ ├── __main__.py python -m blender_mcp CLI
│ ├── dispatcher.py Handler registry + dispatch_command
│ ├── __version__.py Single source of version truth
│ ├── handlers/ 52 handler modules
│ │ ├── manage_scene_comprehension.py get_scene_graph (11 actions)
│ │ ├── manage_rendering.py RENDER_FRAME, screenshot
│ │ ├── manage_scripting.py execute_blender_code
│ │ ├── manage_agent_context.py GET_PRIMER, GET_TACTICS
│ │ ├── manage_history.py Session checkpoints
│ │ ├── get_local_transforms.py Parent-relative coordinates
│ │ ├── polyhaven_handler.py Polyhaven CC0 assets
│ │ ├── sketchfab_handler.py Sketchfab 3D models
│ │ ├── hunyuan_handler.py Hunyuan3D AI generation
│ │ ├── hyper3d_handler.py Hyper3D Rodin AI generation
│ │ └── ... 40+ more handlers
│ └── core/
│ ├── protocol.py Wire protocol (4-byte header + JSON)
│ ├── thread_safety.py Main-thread routing via bpy.app.timers
│ ├── execution_engine.py Safe bpy.ops + ExecutionPolicy
│ ├── parameter_validator.py Type coercion + schema validation
│ ├── intent_router.py Multi-language intent classification
│ ├── semantic_memory.py Tag-based object resolution
│ ├── security.py High Mode / Safe Mode
│ ├── job_manager.py Async subprocess queue
│ ├── response_builder.py Structured response format
│ └── error_protocol.py 280+ ErrorCode enum values
├── stdio_bridge.py MCP bridge (runs outside Blender)
├── scripts/
│ ├── SCRIPTS.md Full scripts documentation
│ ├── inspect_tools.py Tool catalog inspector
│ ├── count_tools.py Handler count audit
│ ├── sync_version.py Version synchronization
│ ├── test_blender_imports.py Runtime import validation (needs Blender)
│ ├── remove_unused_ignores.py mypy cleanup helper
│ └── quality/
│ ├── run_checks.py Quality gate (8–12 checks)
│ ├── check_version.py Version consistency
│ ├── check_forbidden.py Fatal Blender 5.x pattern scanner
│ ├── check_handler_completeness.py Registry audit
│ ├── check_handler_imports.py Import style validator
│ ├── check_schemas.py JSON schema validator
│ ├── check_tool_groups.py Tool group integrity
│ └── lint_imports.py Import architecture rules
├── tests/
│ ├── unit/ 499 unit tests (17 files, no Blender needed)
│ ├── integration/ 24 mock + live integration tests
│ └── TESTS.md Test suite documentation
├── docs/
│ └── ARCHITECTURE.md System design, wire protocol, thread model
├── create_release_zip.py Builds the addon ZIP for Blender
├── Makefile Developer shortcuts
├── pyproject.toml Build + lint + type-check config
└── LICENSE MIT License
</details>
Contribution Guidelines
- Use
@register_handlerdecorator on all handlers — no exceptions @ensure_main_threadon any handler that callsbpy- No
bpy.types.SimpleNamespace— not available in Blender 5.x - No
eval/execoutside theexecute_blender_codesecurity layer - Use
MCPLogger/get_logger()instead ofprint() - Run
make checkbefore committing — all 12 checks must pass - Add unit tests for new logic in
tests/unit/
Log Files
| Component | Windows | macOS / Linux |
|---|---|---|
| Addon (Blender side) | %TEMP%\blender_server_debug.log |
/tmp/blender_server_debug.log |
| Bridge (MCP side) | %TEMP%\mcp_bridge_debug.log |
/tmp/mcp_bridge_debug.log |
License
MIT License — © 2026 GÖKSEL ÖZKAN
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.