We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ShirshovDIM/retopoflow_blender_mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
import pytest
import subprocess
import sys
import os
import time
import socket
import json
from pathlib import Path
import threading
# Path to the helper script
HELPER_SCRIPT = Path(__file__).parent / "start_blender_server.py"
@pytest.fixture(scope="session")
def blender_executable():
"""
Finds the Blender executable.
"""
# Hardcoded for this environment based on user input/discovery
# In a real project, this might search PATH or use env vars
candidates = [
r"C:\Program Files (x86)\Steam\steamapps\common\Blender\blender.exe",
r"C:\Program Files\Blender Foundation\Blender 4.0\blender.exe",
"blender"
]
for path in candidates:
if os.path.exists(path) or (path == "blender" and shutil.which("blender")):
return path
pytest.skip("Blender executable not found")
@pytest.fixture(scope="session")
def blender_server(blender_executable):
"""
Starts Blender in background mode with the addon enabled.
Yields the (host, port) tuple.
"""
host = "localhost"
port = 9876
# Command to run Blender
cmd = [
blender_executable,
"--background",
"--factory-startup",
"--python", str(HELPER_SCRIPT)
]
print(f"Starting Blender: {' '.join(cmd)}")
# Start the process
process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
bufsize=1,
universal_newlines=True,
encoding='utf-8',
errors='replace'
)
# Wait for the server to be ready
server_ready = False
start_time = time.time()
timeout = 30 # seconds
def monitor_output(proc):
while True:
line = proc.stdout.readline()
if not line:
break
print(f"[Blender] {line.strip()}")
if "BLENDER_MCP_SERVER_READY" in line:
nonlocal server_ready
server_ready = True
# Start monitoring thread
t = threading.Thread(target=monitor_output, args=(process,), daemon=True)
t.start()
# Wait loop
while not server_ready:
if time.time() - start_time > timeout:
process.terminate()
raise TimeoutError("Blender server failed to start within timeout")
if process.poll() is not None:
stderr = process.stderr.read()
raise RuntimeError(f"Blender process exited unexpectedly. Stderr: {stderr}")
time.sleep(0.1)
print("Blender server is ready!")
yield host, port
# Teardown
print("Stopping Blender server...")
process.terminate()
try:
process.wait(timeout=5)
except subprocess.TimeoutExpired:
process.kill()
@pytest.fixture
def real_blender_connection(blender_server):
"""
Provides a socket connection to the real Blender instance.
"""
host, port = blender_server
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(10.0)
try:
sock.connect((host, port))
except ConnectionRefusedError:
pytest.fail(f"Could not connect to Blender server at {host}:{port}")
yield sock
sock.close()
def send_command(sock, command_type, params=None):
"""Helper to send a command and receive response"""
if params is None:
params = {}
cmd = {
"type": command_type,
"params": params
}
sock.sendall(json.dumps(cmd).encode('utf-8'))
# Simple response reading (might need buffering for large responses)
# In a real scenario, we'd use a proper framing protocol or read until valid JSON
# For now, a large buffer is usually enough for simple tests
print(f"Waiting for response to {command_type}...")
data = sock.recv(32768)
print(f"Received {len(data)} bytes")
return json.loads(data.decode('utf-8'))