bpy-bridge-mcp
README.md
# bpy-bridge-mcp
[](https://github.com/CodeByDeepak-dev/bpy-bridge-mcp/actions/workflows/ci.yml)
[](https://pypi.org/project/bpy-bridge-mcp/)
[](https://www.python.org/downloads/)
[](https://www.blender.org/)
[](https://opensource.org/licenses/MIT)
> **Thread-safe TCP socket and Model Context Protocol (MCP) middleware bridging Blender's internal Python (`bpy`) runtime with external AI coding agents (Cursor, Codex, Claude) and headless CI/CD pipelines.**
---
## Table of Contents
- [Overview & Motivation](#overview--motivation)
- [System Architecture](#system-architecture)
- [Thread-Safety & Blender Concurrency](#thread-safety--blender-concurrency)
- [Static AST Security Sandbox](#static-ast-security-sandbox)
- [Quick Start](#quick-start)
- [1. Installation](#1-installation)
- [2. Enabling in Blender](#2-enabling-in-blender)
- [3. Headless Blender Startup](#3-headless-blender-startup)
- [CLI Usage](#cli-usage)
- [Model Context Protocol (MCP) Integration](#model-context-protocol-mcp-integration)
- [TCP Socket Protocol Specification](#tcp-socket-protocol-specification)
- [Development & Testing](#development--testing)
- [Contributing](#contributing)
- [License](#license)
---
## Overview & Motivation
Blender is the industry-standard open-source 3D creation suite, powered by its extensive Python API (`bpy`). However, integrating Blender with external automated tools and Large Language Model (LLM) agents introduces two critical architectural hurdles:
1. **Thread Insecurity & C-Core Segmentation Faults:** Blender's core architecture (the Dependency Graph, DNA/RNA data structures, undo stack, and OpenGL/Vulkan contexts) is fundamentally single-threaded. Calling `bpy.ops` or mutating geometry from a secondary OS thread causes immediate race conditions, memory corruption, and fatal segmentation faults.
2. **Security Vulnerabilities with Autonomous Agents:** LLM coding agents generated dynamically can hallucinate or execute malicious system commands (`os.system`, `subprocess.Popen`, file overwrites, or network sockets). Executing untrusted code inside an artist's workstation or render farm poses significant security risks.
**`bpy-bridge-mcp`** solves both challenges by introducing a lightweight, daemonized TCP bridge guarded by an **AST Static Security Sandbox** and synchronized directly into Blender's main event loop using **`bpy.app.timers`** and a thread-safe FIFO queue.
---
## System Architecture
```text
+-----------------------------------------------------------------------------------+
| EXTERNAL AI / CLIENT ECOSYSTEM |
| |
| +--------------------+ +---------------------+ +----------------------+ |
| | Cursor IDE | | Claude Desktop | | CI / CLI Script | |
| | (MCP Client) | | (MCP Client) | | (bpy-bridge CLI) | |
| +---------+----------+ +----------+----------+ +----------+-----------+ |
+------------|---------------------------|---------------------------|--------------+
| | |
+---------------------------+---------------------------+
| JSON Code Payload: {"code": "..."}
v (TCP: 127.0.0.1:9876)
+-----------------------------------------------------------------------------------+
| BLENDER HOST PROCESS |
| |
| [Background Daemon Thread: BpyBridgeServerThread] |
| +-----------------------------------------------------------------------------+ |
| | 1. TCP Socket Listener (SO_REUSEADDR on 127.0.0.1:9876) | |
| | 2. Receive & Buffer Client Payload | |
| | 3. AST Static Security Sandbox (bpy_bridge.security.validate_script_safety) | |
| | - Inspect Abstract Syntax Tree | |
| | - Reject: os, subprocess, sys, shutil, socket, ctypes, importlib, pty | |
| | - Reject: eval(), exec(), open(), __import__(), compile() | |
| +-------------------------------------+---------------------------------------+ |
| | |
| (Safe Task) v |
| +-----------------------------------------------------------------------------+ |
| | Thread-Safe FIFO Task Queue (queue.Queue[(code, result_queue)]) | |
| +-------------------------------------+---------------------------------------+ |
| | |
| v Polls every 0.1s |
| [Blender Main UI & Event Loop Thread] |
| +-----------------------------------------------------------------------------+ |
| | 4. Timer Hook: bpy.app.timers (process_bpy_queue) | |
| | 5. Pull Task from Queue within Blender's Native Main Context | |
| | 6. Execute Python Script safely: | |
| | - Inject {"bpy": bpy} execution scope | |
| | - Redirect and capture stdout & stderr | |
| | - Catch & serialize Python runtime exceptions and tracebacks | |
| +-------------------------------------+---------------------------------------+ |
| | |
| v Put Execution Response |
| +-----------------------------------------------------------------------------+ |
| | Result Queue (queue.Queue[dict]) | |
| +-------------------------------------+---------------------------------------+ |
| | |
| v Timeout wait (up to 10.0s) |
| [Background Daemon Thread] |
| +-----------------------------------------------------------------------------+ |
| | 7. Encode JSON Response: {"status": "success", "output": "...", ...} | |
| | 8. Send TCP Response & Close Client Connection | |
| +-----------------------------------------------------------------------------+ |
+-----------------------------------------------------------------------------------+
```
---
## Thread-Safety & Blender Concurrency
### The Problem
When you run a standard socket server in Python, incoming connections are processed on a background thread. If that thread attempts to execute:
```python
import bpy
bpy.ops.mesh.primitive_cube_add()
```
Blender will either crash instantly with `SIGSEGV` or corrupt its internal scene database. This is because:
1. **Dependency Graph (`depsgraph`):** Not thread-safe for writes or topological node updates.
2. **Context Window (`bpy.context`):** Thread-local to Blender's main event dispatch loop.
3. **Undo Stack:** Requires serialized push/pop operations managed exclusively by the main thread.
### The Solution: `bpy.app.timers` Pipeline
`bpy-bridge-mcp` decouples socket I/O from execution:
- Socket receiving, payload parsing, and AST validation occur concurrently in a lightweight daemon thread (`BpyBridgeServerThread`), leaving Blender's UI completely responsive with zero frame drops.
- Validated scripts are pushed to a thread-safe `queue.Queue`.
- A timer registered via `bpy.app.timers.register(process_bpy_queue, first_interval=0.1)` fires on Blender's main loop every 100 milliseconds, executes queued payloads within the native main thread context, and posts results to a client-specific response queue.
---
## Static AST Security Sandbox
Before any incoming code string is queued or evaluated, it is passed through `bpy_bridge.security.validate_script_safety()`.
The sandbox parses the code into a Python Abstract Syntax Tree (`ast.parse`) and performs static verification:
| Category | Blocked Entities | Reason |
|:---|:---|:---|
| **System & Shell Modules** | `os`, `subprocess`, `sys`, `shutil`, `pty` | Prevents shell execution, file deletion, and arbitrary process spawning. |
| **Low-Level & Memory** | `ctypes`, `importlib` | Prevents arbitrary memory manipulation and dynamic module bypasses. |
| **Network Sockets** | `socket` | Prevents outbound reverse shells and socket binding conflicts. |
| **Dangerous Built-ins** | `eval`, `exec`, `open`, `__import__`, `compile` | Blocks dynamic string evaluation, disk writes, and runtime import stealth. |
### Blocked Patterns Example
All of the following attacks are intercepted before execution:
```python
# Direct imports
import os
import subprocess as sp
# Aliased and submodule imports
from os import system as sys_exec
from subprocess import Popen
# Built-in calls
eval("1 + 1")
open("/home/user/.ssh/id_rsa", "r")
builtins.open("malicious.py", "w")
__builtins__.eval("...")
```
If a violation is detected, execution is aborted, and a structured security error is returned immediately:
```json
{
"status": "error",
"error": "Security Violation: Import of forbidden module 'subprocess' is prohibited"
}
```
---
## Quick Start
### 1. Installation
Install `bpy-bridge-mcp` via `pip`:
```bash
pip install bpy-bridge-mcp
```
Or install from source in editable mode:
```bash
git clone https://github.com/CodeByDeepak-dev/bpy-bridge-mcp.git
cd bpy-bridge-mcp
pip install -e .[dev]
```
### 2. Enabling in Blender
#### Method A: Direct Addon Installation
1. Download or zip the `bpy_bridge` directory.
2. In Blender, navigate to **Edit** → **Preferences** → **Add-ons** → **Install...**
3. Select the file and enable **BPY Bridge MCP**.
#### Method B: Blender Python Console
Open the Interactive Python Console inside Blender and run:
```python
import bpy_bridge
bpy_bridge.register()
```
Output:
```text
[bpy-bridge] Server listening on 127.0.0.1:9876
[bpy-bridge] Registered bpy.app.timers callback (interval: 0.1s).
```
### 3. Headless Blender Startup
For CI/CD pipelines, render farms, or Docker containers:
```bash
blender -b --python-expr "import bpy_bridge; bpy_bridge.register()"
```
---
## CLI Usage
The package bundles the `bpy-bridge` command-line executable:
### 1. Inline Python Code
```bash
bpy-bridge "import bpy; bpy.ops.mesh.primitive_cube_add(size=2.0); print('Cube successfully added!')"
```
Output:
```text
Cube successfully added!
```
### 2. Executing Script Files
Create a script (e.g. `generate_terrain.py`):
```python
import bpy
import math
bpy.ops.mesh.primitive_grid_add(x_subdivisions=64, y_subdivisions=64, size=10.0)
grid = bpy.context.active_object
grid.name = "ProceduralTerrain"
print(f"Created terrain with {len(grid.data.vertices)} vertices.")
```
Execute via CLI:
```bash
bpy-bridge --file generate_terrain.py
```
### 3. Structured JSON Output
Pass `--json` to receive raw structured execution output:
```bash
bpy-bridge "print('Testing JSON output')" --json
```
Output:
```json
{
"status": "success",
"output": "Testing JSON output",
"error": null
}
```
### 4. Custom Host and Port
```bash
bpy-bridge --file script.py --host 127.0.0.1 --port 9876
```
---
## Model Context Protocol (MCP) Integration
`bpy-bridge-mcp` acts as a zero-latency backend for MCP servers, allowing agents like **Claude Desktop**, **Cursor**, and **Codex** to manipulate Blender scenes in real time.
### Configuration for Claude Desktop / Cursor
Add the following tool definition to your MCP server configuration:
```json
{
"mcpServers": {
"blender": {
"command": "bpy-bridge",
"args": []
}
}
}
```
### MCP Tool Schema Example
```json
{
"name": "execute_blender_code",
"description": "Execute sandboxed Python code inside a live Blender session via bpy-bridge-mcp.",
"parameters": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "Python code using bpy to construct 3D scenes, shaders, or animations."
}
},
"required": ["code"]
}
}
```
---
## TCP Socket Protocol Specification
The bridge server communicates via raw TCP sockets on `127.0.0.1:9876`.
### Request Format
Clients send a JSON-encoded payload over TCP:
```json
{
"code": "import bpy\nbpy.ops.mesh.primitive_uv_sphere_add(radius=1.5)\nprint('Sphere generated')"
}
```
### Response Format
The server responds with a JSON-encoded object:
#### Success Response:
```json
{
"status": "success",
"output": "Sphere generated\n",
"error": null
}
```
#### Security Violation Response:
```json
{
"status": "error",
"error": "Security Violation: Import of forbidden module 'subprocess' is prohibited"
}
```
#### Runtime Exception Response:
```json
{
"status": "error",
"output": "",
"error": "AttributeError: 'NoneType' object has no attribute 'location'",
"traceback": "Traceback (most recent call last):\n File ... line 4, in <module>\nAttributeError: ..."
}
```
---
## Development & Testing
### Running Tests
The repository contains comprehensive unit tests verifying the AST security sandbox, CLI argument parser, and bridge lifecycle:
```bash
pytest -v
```
### Test Coverage Highlights
- Safe `bpy`, `bmesh`, and `mathutils` operations
- Comprehensive AST interception of `os`, `subprocess`, `sys`, `shutil`, `socket`, `ctypes`, `importlib`, `pty`
- Forbidden builtin interception (`open`, `eval`, `exec`, `__import__`, `compile`)
- Malformed syntax handling
- CLI parser validation and offline socket error reporting
---
## Contributing
Contributions are welcome! Please follow these guidelines:
1. Fork the repository and create a feature branch (`git checkout -b feature/amazing-feature`).
2. Ensure all tests pass (`pytest -v`).
3. Commit your changes (`git commit -m "Add amazing feature"`).
4. Push to your branch (`git push origin feature/amazing-feature`).
5. Open a Pull Request.
---
## License
Distributed under the **MIT License**. See [`LICENSE`](LICENSE) for details.
Developed with care by **Deepak Behera** ([@CodeByDeepak-dev](https://github.com/CodeByDeepak-dev)).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo recent community issues