Skip to main content
Glama
bpy-bridge-mcp

bpy-bridge-mcp

README.md
# bpy-bridge-mcp

[![CI](https://github.com/CodeByDeepak-dev/bpy-bridge-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/CodeByDeepak-dev/bpy-bridge-mcp/actions/workflows/ci.yml)
[![PyPI version](https://img.shields.io/pypi/v/bpy-bridge-mcp.svg?color=blue)](https://pypi.org/project/bpy-bridge-mcp/)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![Blender 3.6+](https://img.shields.io/badge/blender-3.6+-orange.svg)](https://www.blender.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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)).