Compare commits

..

No commits in common. "a6ca7509d1bc47dfab546cc46bc966cbe79d54a0" and "de65ad64eeca6437ef030cfdaa04dc5ac049a61c" have entirely different histories.

3 changed files with 274 additions and 306 deletions

View File

@ -4,8 +4,6 @@
BlenderMCP connects Blender to Claude AI through the Model Context Protocol (MCP), allowing Claude to directly interact with and control Blender. This integration enables prompt assisted 3D modeling, scene creation, and manipulation. BlenderMCP connects Blender to Claude AI through the Model Context Protocol (MCP), allowing Claude to directly interact with and control Blender. This integration enables prompt assisted 3D modeling, scene creation, and manipulation.
**We have no official website. Any website you see online is unofficial and has no affiliation with this project. Use them at your own risk.**
[Full tutorial](https://www.youtube.com/watch?v=lCyQ717DuzQ) [Full tutorial](https://www.youtube.com/watch?v=lCyQ717DuzQ)
### Join the Community ### Join the Community
@ -32,8 +30,6 @@ Give feedback, get inspired, and build on top of the MCP: [Discord](https://disc
[CodeRabbit](https://www.coderabbit.ai/) [CodeRabbit](https://www.coderabbit.ai/)
[Satish Goda](https://github.com/satishgoda)
**All supporters:** **All supporters:**
[Support this project](https://github.com/sponsors/ahujasid) [Support this project](https://github.com/sponsors/ahujasid)
@ -91,18 +87,6 @@ Otherwise installation instructions are on their website: [Install uv](https://d
**⚠️ Do not proceed before installing UV** **⚠️ Do not proceed before installing UV**
### Environment Variables
The following environment variables can be used to configure the Blender connection:
- `BLENDER_HOST`: Host address for Blender socket server (default: "localhost")
- `BLENDER_PORT`: Port number for Blender socket server (default: 9876)
Example:
```bash
export BLENDER_HOST='host.docker.internal'
export BLENDER_PORT=9876
```
### Claude for Desktop Integration ### Claude for Desktop Integration
@ -167,12 +151,6 @@ For Windows users, go to Settings > MCP > Add Server, add a new server with the
**⚠️ Only run one instance of the MCP server (either on Cursor or Claude Desktop), not both** **⚠️ Only run one instance of the MCP server (either on Cursor or Claude Desktop), not both**
### Visual Studio Code Integration
_Prerequisites_: Make sure you have [Visual Studio Code](https://code.visualstudio.com/) installed before proceeding.
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_blender--mcp_server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=ffffff)](vscode:mcp/install?%7B%22name%22%3A%22blender-mcp%22%2C%22type%22%3A%22stdio%22%2C%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22blender-mcp%22%5D%7D)
### Installing the Blender Addon ### Installing the Blender Addon
1. Download the `addon.py` file from this repo 1. Download the `addon.py` file from this repo

View File

@ -28,10 +28,6 @@ bl_info = {
RODIN_FREE_TRIAL_KEY = "k9TcfFoEhNd9cCPP2guHAHHHkctZHIRhZDywZ1euGUXwihbYLpOjQhofby80NJez" RODIN_FREE_TRIAL_KEY = "k9TcfFoEhNd9cCPP2guHAHHHkctZHIRhZDywZ1euGUXwihbYLpOjQhofby80NJez"
# Add User-Agent as required by Poly Haven API
REQ_HEADERS = requests.utils.default_headers()
REQ_HEADERS.update({"User-Agent": "blender-mcp"})
class BlenderMCPServer: class BlenderMCPServer:
def __init__(self, host='localhost', port=9876): def __init__(self, host='localhost', port=9876):
self.host = host self.host = host
@ -427,7 +423,7 @@ class BlenderMCPServer:
if asset_type not in ["hdris", "textures", "models", "all"]: if asset_type not in ["hdris", "textures", "models", "all"]:
return {"error": f"Invalid asset type: {asset_type}. Must be one of: hdris, textures, models, all"} return {"error": f"Invalid asset type: {asset_type}. Must be one of: hdris, textures, models, all"}
response = requests.get(f"https://api.polyhaven.com/categories/{asset_type}", headers=REQ_HEADERS) response = requests.get(f"https://api.polyhaven.com/categories/{asset_type}")
if response.status_code == 200: if response.status_code == 200:
return {"categories": response.json()} return {"categories": response.json()}
else: else:
@ -449,7 +445,7 @@ class BlenderMCPServer:
if categories: if categories:
params["categories"] = categories params["categories"] = categories
response = requests.get(url, params=params, headers=REQ_HEADERS) response = requests.get(url, params=params)
if response.status_code == 200: if response.status_code == 200:
# Limit the response size to avoid overwhelming Blender # Limit the response size to avoid overwhelming Blender
assets = response.json() assets = response.json()
@ -469,7 +465,7 @@ class BlenderMCPServer:
def download_polyhaven_asset(self, asset_id, asset_type, resolution="1k", file_format=None): def download_polyhaven_asset(self, asset_id, asset_type, resolution="1k", file_format=None):
try: try:
# First get the files information # First get the files information
files_response = requests.get(f"https://api.polyhaven.com/files/{asset_id}", headers=REQ_HEADERS) files_response = requests.get(f"https://api.polyhaven.com/files/{asset_id}")
if files_response.status_code != 200: if files_response.status_code != 200:
return {"error": f"Failed to get asset files: {files_response.status_code}"} return {"error": f"Failed to get asset files: {files_response.status_code}"}
@ -489,7 +485,7 @@ class BlenderMCPServer:
# since Blender can't properly load HDR data directly from memory # since Blender can't properly load HDR data directly from memory
with tempfile.NamedTemporaryFile(suffix=f".{file_format}", delete=False) as tmp_file: with tempfile.NamedTemporaryFile(suffix=f".{file_format}", delete=False) as tmp_file:
# Download the file # Download the file
response = requests.get(file_url, headers=REQ_HEADERS) response = requests.get(file_url)
if response.status_code != 200: if response.status_code != 200:
return {"error": f"Failed to download HDRI: {response.status_code}"} return {"error": f"Failed to download HDRI: {response.status_code}"}
@ -585,7 +581,7 @@ class BlenderMCPServer:
# Use NamedTemporaryFile like we do for HDRIs # Use NamedTemporaryFile like we do for HDRIs
with tempfile.NamedTemporaryFile(suffix=f".{file_format}", delete=False) as tmp_file: with tempfile.NamedTemporaryFile(suffix=f".{file_format}", delete=False) as tmp_file:
# Download the file # Download the file
response = requests.get(file_url, headers=REQ_HEADERS) response = requests.get(file_url)
if response.status_code == 200: if response.status_code == 200:
tmp_file.write(response.content) tmp_file.write(response.content)
tmp_path = tmp_file.name tmp_path = tmp_file.name
@ -722,7 +718,7 @@ class BlenderMCPServer:
main_file_name = file_url.split("/")[-1] main_file_name = file_url.split("/")[-1]
main_file_path = os.path.join(temp_dir, main_file_name) main_file_path = os.path.join(temp_dir, main_file_name)
response = requests.get(file_url, headers=REQ_HEADERS) response = requests.get(file_url)
if response.status_code != 200: if response.status_code != 200:
return {"error": f"Failed to download model: {response.status_code}"} return {"error": f"Failed to download model: {response.status_code}"}
@ -740,7 +736,7 @@ class BlenderMCPServer:
os.makedirs(os.path.dirname(include_file_path), exist_ok=True) os.makedirs(os.path.dirname(include_file_path), exist_ok=True)
# Download the included file # Download the included file
include_response = requests.get(include_url, headers=REQ_HEADERS) include_response = requests.get(include_url)
if include_response.status_code == 200: if include_response.status_code == 200:
with open(include_file_path, "wb") as f: with open(include_file_path, "wb") as f:
f.write(include_response.content) f.write(include_response.content)

View File

@ -18,10 +18,6 @@ logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger("BlenderMCPServer") logger = logging.getLogger("BlenderMCPServer")
# Default configuration
DEFAULT_HOST = "localhost"
DEFAULT_PORT = 9876
@dataclass @dataclass
class BlenderConnection: class BlenderConnection:
host: str host: str
@ -229,9 +225,7 @@ def get_blender_connection():
# Create a new connection if needed # Create a new connection if needed
if _blender_connection is None: if _blender_connection is None:
host = os.getenv("BLENDER_HOST", DEFAULT_HOST) _blender_connection = BlenderConnection(host="localhost", port=9876)
port = int(os.getenv("BLENDER_PORT", DEFAULT_PORT))
_blender_connection = BlenderConnection(host=host, port=port)
if not _blender_connection.connect(): if not _blender_connection.connect():
logger.error("Failed to connect to Blender") logger.error("Failed to connect to Blender")
_blender_connection = None _blender_connection = None