iflow-mcp_cwahlfeldt_blender-mcp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/.gitignore +1 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/2985_process.log +4 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/PKG-INFO +117 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/README.md +104 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/blender/__init__.py +1 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/blender/executor.py +160 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/blender/uv_tools.py +323 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/examples/create_save_blend.py +67 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/examples/hello_cube.py +22 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/examples/modify_blend_file.py +33 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/examples/uv_mapping.py +315 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/language.json +1 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/package_name +1 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/push_info.json +5 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/pyproject.toml +39 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/requirements.txt +1 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/script_files/metadata.json +1 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/scripts/__init__.py +1 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/scripts/manager.py +200 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/server.py +87 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/utils/__init__.py +1 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/utils/helpers.py +206 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/utils/uv_integration.py +264 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/utils/uv_manager.py +64 -0
- iflow_mcp_cwahlfeldt_blender_mcp-0.1.0/uv.lock +405 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__pycache__
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
[2026-02-04 04:40:28] [SUCCESS] 获取项目: fork并克隆项目到本地成功,github_url=https://github.com/cwahlfeldt/blender-mcp, fork_github_url=https://github.com/iflow-mcp/cwahlfeldt-blender-mcp, name=cwahlfeldt-blender-mcp
|
|
2
|
+
[2026-02-04 04:40:53] [SUCCESS] 阅读代码: 项目类型为Python MCP服务端项目,使用FastMCP框架,支持stdio协议,非SKILL项目,非MCP代理服务,项目未过期
|
|
3
|
+
[2026-02-04 04:42:56] [SUCCESS] 本地测试: 构建和测试成功,修复了f-string语法错误,获取到4个工具(add_script, edit_script, execute_script, remove_script),无需环境变量配置
|
|
4
|
+
[2026-02-04 04:43:12] [SUCCESS] 推送iflow分支: 成功推送iflow分支到远程仓库 https://github.com/iflow-mcp/cwahlfeldt-blender-mcp
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: iflow-mcp_cwahlfeldt_blender-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for executing Blender scripts
|
|
5
|
+
Author: Blender MCP Team
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: mcp>=0.1.0
|
|
8
|
+
Provides-Extra: dev
|
|
9
|
+
Requires-Dist: black>=23.0.0; extra == 'dev'
|
|
10
|
+
Requires-Dist: isort>=5.12.0; extra == 'dev'
|
|
11
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# Blender MCP Server
|
|
15
|
+
|
|
16
|
+
A Model Context Protocol (MCP) server for managing and executing Blender scripts.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- Add, edit, execute, and remove Blender Python scripts
|
|
21
|
+
- Execute scripts in a headless Blender environment
|
|
22
|
+
- View execution results and errors
|
|
23
|
+
- Track script metadata (creation date, last modified, execution count)
|
|
24
|
+
|
|
25
|
+
## Requirements
|
|
26
|
+
|
|
27
|
+
- Python 3.7+
|
|
28
|
+
- Blender installed and accessible
|
|
29
|
+
- MCP library (`pip install mcp`)
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
1. Start the server:
|
|
34
|
+
```
|
|
35
|
+
python server.py
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
2. Connect to the server using an MCP client (like Claude Desktop)
|
|
39
|
+
|
|
40
|
+
3. Use the provided tools to manage scripts:
|
|
41
|
+
- `add_script(name, content)` - Add a new script
|
|
42
|
+
- `edit_script(name, content)` - Edit an existing script
|
|
43
|
+
- `execute_script(name, blend_file=None)` - Execute a script in Blender, optionally specifying a .blend file
|
|
44
|
+
- `remove_script(name)` - Remove a script
|
|
45
|
+
|
|
46
|
+
4. Access resources to get information:
|
|
47
|
+
- `scripts://list` - Get list of available scripts
|
|
48
|
+
- `script://{name}` - Get content of a specific script
|
|
49
|
+
- `result://{name}` - Get execution result of a script
|
|
50
|
+
|
|
51
|
+
## Examples
|
|
52
|
+
|
|
53
|
+
### Basic Example
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
# Add a simple script
|
|
57
|
+
add_script("hello_cube", '''
|
|
58
|
+
import bpy
|
|
59
|
+
|
|
60
|
+
# Clear existing objects
|
|
61
|
+
bpy.ops.object.select_all(action='SELECT')
|
|
62
|
+
bpy.ops.object.delete()
|
|
63
|
+
|
|
64
|
+
# Create a cube
|
|
65
|
+
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
|
|
66
|
+
print("Cube created!")
|
|
67
|
+
''')
|
|
68
|
+
|
|
69
|
+
# Execute the script
|
|
70
|
+
execute_script("hello_cube")
|
|
71
|
+
|
|
72
|
+
# Get the result
|
|
73
|
+
# Access using: result://hello_cube
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Working with Blend Files
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
# Add a script that works with a blend file
|
|
80
|
+
add_script("analyze_scene", '''
|
|
81
|
+
import bpy
|
|
82
|
+
|
|
83
|
+
# Print information about the current scene
|
|
84
|
+
print(f"Current Blender version: {bpy.app.version_string}")
|
|
85
|
+
print(f"Current file: {bpy.data.filepath}")
|
|
86
|
+
|
|
87
|
+
# List all objects in the scene
|
|
88
|
+
print("\\nObjects in the scene:")
|
|
89
|
+
for obj in bpy.data.objects:
|
|
90
|
+
print(f" - {obj.name} ({obj.type})")
|
|
91
|
+
''')
|
|
92
|
+
|
|
93
|
+
# Execute with a specific blend file
|
|
94
|
+
execute_script("analyze_scene", blend_file="/path/to/your/project.blend")
|
|
95
|
+
|
|
96
|
+
# Get the result
|
|
97
|
+
# Access using: result://analyze_scene
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## How It Works
|
|
101
|
+
|
|
102
|
+
1. When a script is added, it's stored in the `script_files/scripts` directory
|
|
103
|
+
2. When executed, the script is run in a headless Blender instance
|
|
104
|
+
- If a blend file is specified, Blender will open that file before running the script
|
|
105
|
+
- Otherwise, a default empty Blender scene is used
|
|
106
|
+
3. Output and errors are captured and stored in the `script_files/results` directory
|
|
107
|
+
4. Metadata about scripts is tracked in `script_files/metadata.json`
|
|
108
|
+
|
|
109
|
+
## Installation
|
|
110
|
+
|
|
111
|
+
1. Clone this repository
|
|
112
|
+
2. Install the MCP library: `pip install mcp`
|
|
113
|
+
3. Ensure Blender is installed and accessible from your PATH
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Blender MCP Server
|
|
2
|
+
|
|
3
|
+
A Model Context Protocol (MCP) server for managing and executing Blender scripts.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Add, edit, execute, and remove Blender Python scripts
|
|
8
|
+
- Execute scripts in a headless Blender environment
|
|
9
|
+
- View execution results and errors
|
|
10
|
+
- Track script metadata (creation date, last modified, execution count)
|
|
11
|
+
|
|
12
|
+
## Requirements
|
|
13
|
+
|
|
14
|
+
- Python 3.7+
|
|
15
|
+
- Blender installed and accessible
|
|
16
|
+
- MCP library (`pip install mcp`)
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
1. Start the server:
|
|
21
|
+
```
|
|
22
|
+
python server.py
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
2. Connect to the server using an MCP client (like Claude Desktop)
|
|
26
|
+
|
|
27
|
+
3. Use the provided tools to manage scripts:
|
|
28
|
+
- `add_script(name, content)` - Add a new script
|
|
29
|
+
- `edit_script(name, content)` - Edit an existing script
|
|
30
|
+
- `execute_script(name, blend_file=None)` - Execute a script in Blender, optionally specifying a .blend file
|
|
31
|
+
- `remove_script(name)` - Remove a script
|
|
32
|
+
|
|
33
|
+
4. Access resources to get information:
|
|
34
|
+
- `scripts://list` - Get list of available scripts
|
|
35
|
+
- `script://{name}` - Get content of a specific script
|
|
36
|
+
- `result://{name}` - Get execution result of a script
|
|
37
|
+
|
|
38
|
+
## Examples
|
|
39
|
+
|
|
40
|
+
### Basic Example
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
# Add a simple script
|
|
44
|
+
add_script("hello_cube", '''
|
|
45
|
+
import bpy
|
|
46
|
+
|
|
47
|
+
# Clear existing objects
|
|
48
|
+
bpy.ops.object.select_all(action='SELECT')
|
|
49
|
+
bpy.ops.object.delete()
|
|
50
|
+
|
|
51
|
+
# Create a cube
|
|
52
|
+
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
|
|
53
|
+
print("Cube created!")
|
|
54
|
+
''')
|
|
55
|
+
|
|
56
|
+
# Execute the script
|
|
57
|
+
execute_script("hello_cube")
|
|
58
|
+
|
|
59
|
+
# Get the result
|
|
60
|
+
# Access using: result://hello_cube
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Working with Blend Files
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
# Add a script that works with a blend file
|
|
67
|
+
add_script("analyze_scene", '''
|
|
68
|
+
import bpy
|
|
69
|
+
|
|
70
|
+
# Print information about the current scene
|
|
71
|
+
print(f"Current Blender version: {bpy.app.version_string}")
|
|
72
|
+
print(f"Current file: {bpy.data.filepath}")
|
|
73
|
+
|
|
74
|
+
# List all objects in the scene
|
|
75
|
+
print("\\nObjects in the scene:")
|
|
76
|
+
for obj in bpy.data.objects:
|
|
77
|
+
print(f" - {obj.name} ({obj.type})")
|
|
78
|
+
''')
|
|
79
|
+
|
|
80
|
+
# Execute with a specific blend file
|
|
81
|
+
execute_script("analyze_scene", blend_file="/path/to/your/project.blend")
|
|
82
|
+
|
|
83
|
+
# Get the result
|
|
84
|
+
# Access using: result://analyze_scene
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## How It Works
|
|
88
|
+
|
|
89
|
+
1. When a script is added, it's stored in the `script_files/scripts` directory
|
|
90
|
+
2. When executed, the script is run in a headless Blender instance
|
|
91
|
+
- If a blend file is specified, Blender will open that file before running the script
|
|
92
|
+
- Otherwise, a default empty Blender scene is used
|
|
93
|
+
3. Output and errors are captured and stored in the `script_files/results` directory
|
|
94
|
+
4. Metadata about scripts is tracked in `script_files/metadata.json`
|
|
95
|
+
|
|
96
|
+
## Installation
|
|
97
|
+
|
|
98
|
+
1. Clone this repository
|
|
99
|
+
2. Install the MCP library: `pip install mcp`
|
|
100
|
+
3. Ensure Blender is installed and accessible from your PATH
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# blender module initialization
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
class BlenderExecutor:
|
|
8
|
+
"""Class to handle script execution in Blender"""
|
|
9
|
+
|
|
10
|
+
def __init__(self, blender_path=None, timeout=60):
|
|
11
|
+
"""
|
|
12
|
+
Initialize the BlenderExecutor
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
blender_path (str, optional): Path to the Blender executable. If None, will try to find it.
|
|
16
|
+
timeout (int, optional): Timeout for script execution in seconds
|
|
17
|
+
"""
|
|
18
|
+
self.blender_path = blender_path or self._find_blender_executable()
|
|
19
|
+
self.timeout = timeout
|
|
20
|
+
self.temp_dir = Path(tempfile.gettempdir()) / "blender_mcp"
|
|
21
|
+
self._setup()
|
|
22
|
+
|
|
23
|
+
def _setup(self):
|
|
24
|
+
"""Setup the executor, create necessary directories"""
|
|
25
|
+
os.makedirs(self.temp_dir, exist_ok=True)
|
|
26
|
+
|
|
27
|
+
def _find_blender_executable(self):
|
|
28
|
+
"""Find Blender executable path"""
|
|
29
|
+
# Try common locations
|
|
30
|
+
common_paths = [
|
|
31
|
+
"blender", # If it's in PATH
|
|
32
|
+
"/usr/bin/blender",
|
|
33
|
+
"/usr/local/bin/blender",
|
|
34
|
+
"/Applications/Blender.app/Contents/MacOS/Blender",
|
|
35
|
+
"C:\\Program Files\\Blender Foundation\\Blender\\blender.exe",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
for path in common_paths:
|
|
39
|
+
try:
|
|
40
|
+
# Just check if we can run it with --version
|
|
41
|
+
subprocess.run([path, "--version"],
|
|
42
|
+
stdout=subprocess.PIPE,
|
|
43
|
+
stderr=subprocess.PIPE,
|
|
44
|
+
timeout=5)
|
|
45
|
+
return path
|
|
46
|
+
except (subprocess.SubprocessError, FileNotFoundError):
|
|
47
|
+
continue
|
|
48
|
+
|
|
49
|
+
# If we couldn't find it, default to "blender" and hope it's in PATH
|
|
50
|
+
return "blender"
|
|
51
|
+
|
|
52
|
+
def execute(self, script_name, script_content, blend_file=None):
|
|
53
|
+
"""
|
|
54
|
+
Execute a script in Blender
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
script_name (str): Name of the script
|
|
58
|
+
script_content (str): Content of the script to execute
|
|
59
|
+
blend_file (str, optional): Path to a .blend file to use
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
str: Output of the script execution
|
|
63
|
+
"""
|
|
64
|
+
# Create a temporary script file
|
|
65
|
+
script_file = self.temp_dir / f"{script_name}.py"
|
|
66
|
+
output_file = self.temp_dir / f"{script_name}.out"
|
|
67
|
+
|
|
68
|
+
# Prepare variables to avoid f-string backslash issues
|
|
69
|
+
output_file_str = str(output_file)
|
|
70
|
+
newlines = "\n\n"
|
|
71
|
+
|
|
72
|
+
# Write the script content with output capture
|
|
73
|
+
with open(script_file, "w") as f:
|
|
74
|
+
# Add code to capture stdout and stderr
|
|
75
|
+
indented_script = script_content.replace('\n', '\n ')
|
|
76
|
+
wrapped_script = f"""
|
|
77
|
+
import sys
|
|
78
|
+
import io
|
|
79
|
+
import traceback
|
|
80
|
+
|
|
81
|
+
# Redirect stdout and stderr
|
|
82
|
+
old_stdout = sys.stdout
|
|
83
|
+
old_stderr = sys.stderr
|
|
84
|
+
stdout_buffer = io.StringIO()
|
|
85
|
+
stderr_buffer = io.StringIO()
|
|
86
|
+
sys.stdout = stdout_buffer
|
|
87
|
+
sys.stderr = stderr_buffer
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
# Execute the script
|
|
91
|
+
{indented_script}
|
|
92
|
+
except Exception as e:
|
|
93
|
+
print(f"Error executing script: {{e}}")
|
|
94
|
+
traceback.print_exc()
|
|
95
|
+
finally:
|
|
96
|
+
# Restore stdout and stderr
|
|
97
|
+
sys.stdout = old_stdout
|
|
98
|
+
sys.stderr = old_stderr
|
|
99
|
+
|
|
100
|
+
# Write output to file
|
|
101
|
+
with open(r"{output_file_str}", "w") as output:
|
|
102
|
+
output.write(stdout_buffer.getvalue())
|
|
103
|
+
output.write("{newlines}")
|
|
104
|
+
output.write(stderr_buffer.getvalue())
|
|
105
|
+
"""
|
|
106
|
+
f.write(wrapped_script)
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
# Run Blender with the script
|
|
110
|
+
cmd = [
|
|
111
|
+
self.blender_path,
|
|
112
|
+
"--background", # Run without GUI
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
# Add blend file if provided
|
|
116
|
+
if blend_file:
|
|
117
|
+
if os.path.exists(blend_file):
|
|
118
|
+
cmd.extend(["--file", blend_file])
|
|
119
|
+
else:
|
|
120
|
+
return f"Error: Blend file not found: {blend_file}"
|
|
121
|
+
|
|
122
|
+
# Add Python script
|
|
123
|
+
cmd.extend(["--python", str(script_file)])
|
|
124
|
+
|
|
125
|
+
process = subprocess.run(
|
|
126
|
+
cmd,
|
|
127
|
+
stdout=subprocess.PIPE,
|
|
128
|
+
stderr=subprocess.PIPE,
|
|
129
|
+
timeout=self.timeout,
|
|
130
|
+
text=True
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Check if the output file exists
|
|
134
|
+
if output_file.exists():
|
|
135
|
+
with open(output_file, "r") as f:
|
|
136
|
+
script_output = f.read()
|
|
137
|
+
else:
|
|
138
|
+
script_output = "No output captured from script."
|
|
139
|
+
|
|
140
|
+
# Add Blender's stdout/stderr if there was an error
|
|
141
|
+
if process.returncode != 0:
|
|
142
|
+
script_output += "\n\nBlender process output:\n"
|
|
143
|
+
script_output += f"STDOUT: {process.stdout}\n"
|
|
144
|
+
script_output += f"STDERR: {process.stderr}"
|
|
145
|
+
|
|
146
|
+
return script_output
|
|
147
|
+
|
|
148
|
+
except subprocess.TimeoutExpired:
|
|
149
|
+
return f"Script execution timed out after {self.timeout} seconds"
|
|
150
|
+
except Exception as e:
|
|
151
|
+
return f"Error executing script: {str(e)}"
|
|
152
|
+
finally:
|
|
153
|
+
# Clean up temp files
|
|
154
|
+
try:
|
|
155
|
+
if script_file.exists():
|
|
156
|
+
script_file.unlink()
|
|
157
|
+
if output_file.exists():
|
|
158
|
+
output_file.unlink()
|
|
159
|
+
except Exception as e:
|
|
160
|
+
logging.warning(f"Failed to clean up temp files: {e}")
|