Content
<div align="center">
# Tool List
[](https://www.python.org/)
[](https://www.adobe.com/products/substance3d-painter.html)
[](https://github.com/jlowin/fastmcp)
[](https://pypi.org/project/requests/)
[](./CHANGELOG.md)
[](#-mcp-tools-94-个-)
[](#-调试与排错)
*Make AI your chief material artist.*
This project implements a **MCP (Model Context Protocol) Server** for Substance 3D Painter, enabling major LLMs (such as OpenCode, Claude Code, Cursor, etc.) to interact with Painter directly through standardized tools. The project aims to leverage the powerful reasoning and design capabilities of large models to automate visual creation workflows (such as material design, skin creation, wear and tear, etc.).
> 📌 **v0.6.0**: Complete tool audit - 80/92 tools verified on real SP 10.0.1, 7 mock self-covering bugs fixed, bake modified to asynchronous architecture. See [CHANGELOG](./CHANGELOG.md) for details.
</div>
---
> [!IMPORTANT]
> This project requires LLMs to have both **visual recognition (Vision)** and **tool calling (Tool Calling)** capabilities, and neither can be missing. The core closed loop of material creation workflow is: AI screenshot evaluation → parameter adjustment → screenshot verification again, so pure text models cannot be used.
>
> ✅ **Recommended models**: Claude Sonnet 4/4.5, Claude Opus 4.5+
> ⚠️ **Also try**: Gemini, Qwen, Kimi and other models that support Vision + Tool (compatibility varies by client, need to test yourself)
## 🎯 Core Capabilities and Workflow
Through this MCP Server, AI can achieve the following core closed-loop operations:
1. 🔍 **Understand current state**: Read Painter's layer stack structure, texture set, project information.
2. 🧠 **Make modification decisions**: Analyze and infer material parameters that need to be modified.
3. 🛠️ **Execute operations**: Call tools (such as new layer, add material/mask, adjust channel values, batch operations).
4. 👁️ **Get visual feedback**: Control Painter to capture 3D viewport screenshots.
5. 🔄 **Iterate and confirm**: Perform multiple rounds of self-assessment and modification based on screenshots until satisfied.
6. 📦 **Bake and export**: Bake mesh maps, export textures.
## ⚙️ Architecture Overview
This project consists of two main parts:
* 🔌 **`plugin/sp_bridge/` (SP embedded plugin)**: Runs as a Python plugin for Painter, starts an HTTP Server (port `27182`), receives and executes `substance_painter` API calls.
* 🌐 **`server/` (MCP Server)**: External service built based on `FastMCP`, exposes MCP Tools to AI clients.
```mermaid
graph LR
LLM["LLM / MCP Client<br>(stdio 或 SSE)"] <--> Server["server/sp_mcp.py<br>(MCP Tools)"]
Server <-->|HTTP POST 27182| Bridge["plugin/sp_bridge/bridge.py<br>(HTTP Server + QTimer)"]
Bridge <-->|主线程轮询| Handlers["plugin/sp_bridge/handlers.py"]
Handlers <--> SP["Painter 图层栈 / 导出 / 烘焙"]
```
## 🚀 Installation and Launch
### Environment Requirements
* **Substance 3D Painter** 10.0.1+
* **Python** 3.10+
* **Dependencies**: `fastmcp` (0.9+), `requests` (2.31+)
* **Operating System**: Windows (Computer Use tools depend on Windows API; other tools are cross-platform)
### 1. Clone Repository Locally
```bash
git clone https://github.com/ora233-fantaea/SP-MCP.git
cd SP-MCP
```
> Subsequent updates only require `git pull`. The following steps are based on the cloned repository root directory (denoted as `<SP-MCP>` below).
### 2. Deploy Painter Plugin
The SP embedded plugin is located in the `plugin/sp_bridge/` directory of the repository, and Painter needs to be able to load it. Choose one of two methods:
**Method A — Copy (simple)**: Copy the `plugin/sp_bridge/` folder to Painter's plugin directory:
```
%USERPROFILE%\Documents\Adobe\Adobe Substance 3D Painter\python\plugins\sp_bridge\
```
> ⚠️ With this method, you need to copy again every time you update with `git pull`.
**Method B — Symbolic Link (recommended, easy to update)**: Create a symbolic link pointing to the repository in Painter's plugin directory.
Open PowerShell as an administrator:
```powershell
$plugins = "$env:USERPROFILE\Documents\Adobe\Adobe Substance 3D Painter\python\plugins"
New-Item -ItemType SymbolicLink -Path "$plugins\sp_bridge" -Target "<SP-MCP>\plugin\sp_bridge"
```
> **Success indicator**: Launch Painter, Python Console displays:
> `[INFO] sp_bridge: SP Bridge started on port 27182`
### 3. Install Python Dependencies
Create a virtual environment in the repository root directory and install dependencies (MCP server runs in this venv):
```powershell
python -m venv .venv
.venv\Scripts\activate
pip install -e ".[dev]"
```
### 4. Configure LLM Client
Register `server/sp_mcp.py` as the MCP server in the repository. Fill `<Python path>` with the Python path in the venv above, and `<SP-MCP>` with the absolute path of the repository.
<details>
<summary><b>OpenCode</b></summary>
Add the following to `~/.config/opencode/opencode.jsonc`:
```json
{
"mcp": {
"substance-painter": {
"type": "local",
"command": ["<SP-MCP>\\.venv\\Scripts\\python.exe", "<SP-MCP>\\server\\sp_mcp.py"],
"enabled": true,
"timeout": 30000
}
}
}
```
</details>
<details>
<summary><b>Claude Code / Cursor / OpenClaw</b></summary>
```json
{
"mcpServers": {
"substance-painter": {
"command": "<SP-MCP>\\.venv\\Scripts\\python.exe",
"args": ["<SP-MCP>\\server\\sp_mcp.py"]
}
}
}
```
</details>
<details>
<summary><b>Hermes Agent</b></summary>
Add the following to `~/.hermes/config.yaml`:
```yaml
mcp_servers:
substance-painter:
command: "<SP-MCP>\\.venv\\Scripts\\python.exe"
args: ["<SP-MCP>\\server\\sp_mcp.py"]
```
</details>
### 5. Launch and Use
1. Launch Painter and open a project
2. Confirm Console displays `SP Bridge started on port 27182`
3. Launch LLM client (will automatically start MCP server)
4. 💬 Directly use in conversation: *"Help me check the current Painter layer structure"*
> 💡 Update: Just `git pull` (Method B) or copy the plugin again (Method A) for future updates, no need to reinstall dependencies.
## 🛠️ Tool List
> [!TIP]
> All layer modification operations (`add_fill_layer`, `set_layer_channel`, `apply_smart_material`, etc.) are automatically wrapped in `ScopedModification`, **each API call = 1 undo**.
> Use `sp_begin_batch` / `sp_end_batch` to merge multiple operations into 1 undo for convenience.
<details>
<summary><b>🔌 Connection and Project Management (4)</b></summary>
| Tool | Description |
|------|------|
| `sp_ping` | Check bridge connectivity and return SP version |
| `sp_get_project_info` | Read project name/path/status |
| `sp_save_project` | Save project |
| `sp_run_python` | 【Escape Hatch】Execute arbitrary Python in the main thread |
</details>
<details>
<summary><b>📚 Layer and Texture Set Reading (5)</b></summary>
| Tool | Description |
|------|------|
| `sp_get_layer_stack` | Return the current texture set's layer tree JSON (including Group recursion) |
| `sp_get_texture_sets` | Return all texture sets and layer structures, supporting filtering |
| `sp_find_layer_by_name` | Search for layers across texture sets by name and return a list of matches |
| `sp_get_layer_properties` | Return layer detailed properties |
| `sp_get_layer_channels` | Return all channels' opacity/blend/source |
</details>
<details>
<summary><b>✏️ Layer and Channel Writing (12)</b></summary>
| Tool | Description |
|------|------|
| `sp_add_fill_layer` | Create a new Fill Layer (including color/channel/blend mode) |
| `sp_add_group_layer` | Create a new empty group layer |
| `sp_add_paint_layer` | Create a new paint layer |
| `sp_set_layer_property` | Modify layer properties (opacity/visible/name/blend_mode) |
| `sp_set_layer_channel` | Set channel values (Roughness/Metallic/Height/BaseColor/Normal) |
| `sp_delete_layer` | Delete a layer |
| `sp_duplicate_layer` | Duplicate a layer |
| `sp_move_layer` | Move a layer to a target position (up/down) |
| `sp_group_layers` | Package layers into a new group |
| `sp_ungroup_layer` | Ungroup a layer, promoting child layers to the parent level |
| `sp_add_mask` | Add a mask to a layer (black/white background) |
| `sp_remove_mask` | Remove a layer's mask |
</details>
<details>
<summary><b>⏪ Undo / Redo and Batch Operations (4)</b></summary>
| Tool | Description |
|------|------|
| `sp_undo` | Undo the last step (via SP's native QUndoStack) |
| `sp_redo` | Redo (via SP's native QUndoStack) |
| `sp_begin_batch` | Start a batch operation (based on ScopedModification) |
| `sp_end_batch` | End the batch and merge into a single undo |
</details>
<details>
<summary><b>✨ Materials and Effects (5)</b></summary>
| Tool | Description |
|------|------|
| `sp_list_shelf_materials` | List available Smart Materials, supporting filtering |
| `sp_apply_smart_material` | Apply a Smart Material to a specified layer |
| `sp_list_materials` | List available regular materials |
| `sp_apply_material` | Apply a regular material |
| `sp_add_smart_mask` | Add a procedural mask to a layer |
</details>
<details>
<summary><b>📸 Visual Feedback and Rendering (10)</b></summary>
| Tool | Description |
|------|------|
| `sp_capture_viewport` | Capture the viewport (`"quick"` iteration / `"render"` capture the current screen, not automatically triggering Iray) |
| `sp_set_camera` | Set the camera position and view |
| `sp_frame_mesh` | Automatically adapt the view to the model |
| `sp_set_environment` | Switch HDRI environment lighting |
| `sp_set_iray_params` | Set Iray parameters |
| `sp_get_iray_params` | Read current Iray parameters |
| `sp_start_iray_render`| Asynchronously start Iray |
| `sp_check_iray_render`| Check rendering progress |
| `sp_list_export_presets` | List all available export presets |
| `sp_export_textures` | Export textures |
</details>
<details>
<summary><b>🛠️ Texture Set and JS API (5)</b></summary>
| Tool | Description |
|------|------|
| `sp_set_active_texture_set`| Switch the active texture set |
| `sp_set_texture_set_resolution`| Modify the texture set resolution |
| `sp_bake_mesh_maps` | **Asynchronously** bake mesh maps (AO/Curvature/Normal, etc.), return immediately; use `sp_get_bake_status` to poll, **do not timeout and retry** |
| `sp_add_texture_set_channel` | Add a channel to a texture set |
| `sp_remove_texture_set_channel`| Remove a texture set channel |
</details>
<details>
<summary><b>🖱️ Computer Use (12)</b></summary>
> Implemented using Windows API for mini Computer Use, allowing visual models to drive SP UI through screenshot → coordinate mapping → mouse click/keyboard input.
> `sp_window_focus()` will display a red "MCP Control Active" warning bar at the top of the SP window, and `sp_cu_unlock()` will clear it after the operation.
>
> ⚠️ **Security Warning**: `sp_mouse_*` / `sp_key_send` / `sp_shortcut` will **actually control your physical mouse and keyboard**.
> When calling these tools, do not use your computer for other tasks (the cursor will be occupied), and ensure the SP window is in the foreground (first call `sp_window_focus()`).
> These operations may accidentally trigger other windows or dialogs - only use them when you can immediately regain control.
| Tool | Description |
|------|------|
| `sp_window_info` | Return window position/size/status, used with screenshot for coordinate mapping |
| `sp_window_grab` | Capture the SP window or a specified area → base64 PNG |
| `sp_window_focus` | Focus the SP window + display a red warning bar |
| `sp_cu_unlock` | Release the lock + hide the warning bar |
| `sp_cu_banner_text` | Update the warning bar text |
| `sp_cu_warning` | Switch the warning bar to a yellow waiting state (processing timeout) |
| `sp_mouse_move` | Move the mouse (screen/window relative coordinates) |
| `sp_mouse_click` | Click (left/right/middle, single/double click) |
| `sp_mouse_scroll` | Scroll wheel (±120) |
| `sp_mouse_drag` | Drag from A to B |
| `sp_key_send` | Keyboard input (single key/combination key/typing) |
| `sp_shortcut` | Predefined shortcut encapsulation (save/undo/frame_all, etc., 26 types) |
</details>
<details>
<summary><b>✨ Effect Nodes (9)</b></summary>
| Tool | Description |
|------|------|
| `sp_add_filter_effect` | Add a Filter effect (optional filter resource) |
| `sp_add_generator_effect` | Add a Generator effect (optional generator resource) |
| `sp_add_levels_effect` | Add a Levels color adjustment effect |
| `sp_add_compare_mask_effect` | Add a Compare Mask channel comparison mask effect |
| `sp_add_color_selection_effect` | Add a Color Selection ID mask effect |
| `sp_add_anchor_point_effect` | Add an Anchor Point anchor effect |
| `sp_get_effect_parameters` | Read effect node parameters |
| `sp_get_selected_nodes` | Get the current selected node list |
| `sp_set_selected_nodes` | Set the selected nodes |
</details>
<details>
<summary><b>🔥 Baking API (7)</b></summary>
> Complete Python baking parameter control, replacing JS `alg.baking.bake()`.
> `bake_mesh_maps` / `bake_texture_set` are both asynchronous and return immediately - **do not retry due to delay/timeout**, otherwise, it will trigger baking repeatedly. Use `sp_get_bake_status` to poll and confirm completion.
| Tool | Description |
|------|------|
| `sp_get_baking_parameters` | Read the complete baking parameters of the texture set (common + each baker) |
| `sp_set_baking_parameters` | Set baking parameters (resolution/high model path, etc.) |
| `sp_bake_texture_set` | Asynchronously start texture set baking |
| `sp_get_baking_state` | Get the baking state (enabled/bakers/UV tiles/link) |
| `sp_set_baking_state` | Set the baking state (enable texture set/bakers/curvature method) |
| `sp_get_bake_status` | Poll the asynchronous status of `sp_bake_mesh_maps` (phase/progress/status) |
| `sp_cancel_bake` | Cancel the ongoing asynchronous baking (based on StopSource.request_stop) |
</details>
<details>
<summary><b>📁 Project Lifecycle (7)</b></summary>
| Tool | Description |
|------|------|
| `sp_create_project` | Create a new project (mesh/settings/workflow/normal format) |
| `sp_open_project` | Open an existing .spp project |
| `sp_close_project` | Close the current project (without saving) |
| `sp_reload_mesh` | Asynchronously reload the project mesh |
| `sp_get_project_metadata` | Read project persistent metadata |
| `sp_set_project_metadata` | Write project persistent metadata |
| `sp_list_project_metadata` | List all metadata keys under the context |
</details>
<details>
<summary><b>🔍 Resource Discovery (1)</b></summary>
| Tool | Description |
|------|------|
| `sp_list_resources_by_usage` | List resources by usage type (filter/generator/substance, etc.) |
</details>
<details>
<summary><b>🎨 Procedural Source Control (7)</b></summary>
| Tool | Description |
|------|------|
| `sp_get_source_info` | Get the source information of a fill layer/effect |
| `sp_get_substance_parameters` | Read procedural source parameter values |
| `sp_set_substance_parameters` | Modify procedural source parameter values |
| `sp_get_substance_presets` | List available presets for the procedural source |
| `sp_apply_substance_preset` | Apply a specified preset |
| `sp_get_source_outputs` | Get source output mapping information |
| `sp_set_source_output` | Switch the source active output |
</details>
<details>
<summary><b>📷 Camera and Display (6)</b></summary>
| Tool | Description |
|------|------|
| `sp_get_camera` | Read the main camera's complete state |
| `sp_get_tone_mapping` | Get the tone mapping function |
| `sp_set_tone_mapping` | Set the tone mapping function |
| `sp_get_color_lut` | Get the color LUT configuration |
| `sp_set_color_lut` | Set the color LUT configuration |
| `sp_get_scene_bounding_box` | Get the scene bounding box |
</details>
## ⚠️ Known Limitations
* **Viewport State**: `sp_capture_viewport` requires the project to be open and the 3D viewport to be visible.
* **Version Limitations**: Smart Material API requires SP 10.0+, and related tools on 9.x versions will return explicit errors.
* **ID Cache**: Layer IDs change after Painter restarts, so do not cache across sessions.
* **API Alternatives**:
* `schedule_on_ui_thread` does not exist in SP 10.x and has been replaced with a `QTimer` polling solution.
* `alg.ui.clickButton` has an internal bug in SP 10.0.1 (`findChild of undefined`), use Computer Use's mouse click to bypass this limitation.
* **Foreground Window**: Computer Use mouse/keyboard input is limited by the Windows foreground window, so be sure to call `sp_window_focus()` before operation.
## 🔧 Debugging and Troubleshooting
### Local Testing
Prioritize using the project's virtual environment Python for testing, avoiding misuse of WindowsApps' `python.exe` stub or global pytest:
```powershell
.venv\Scripts\python.exe -m pytest tests/ -m "not integration" -v
```
| Issue | Common Troubleshooting Solutions |
|------|------|
| **Bridge Connection Failure** | Check if Painter is launched and the plugin is loaded successfully in Python Console |
| **Timeout Exception** | Iray rendering will block the main thread, wait for completion and retry |
| **Plugin Not Loaded** | Check Python Console output error messages |
| **View Running Logs** | `%USERPROFILE%\sp_bridge.log` |
| **Code Hot Reload** | `import importlib, sp_bridge.handlers; importlib.reload(sp_bridge.handlers)` |
| **CU Warning Bar Not Disappearing** | Manually call `sp_cu_unlock()` or restart Painter |
> 📚 For details and advanced development guides, refer to [AGENTS.md](./AGENTS.md) and [PHASES.md](./PHASES.md) in the code repository. Version change records can be found in [CHANGELOG.md](./CHANGELOG.md).
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available