内容
# substance-painter-mcp
通过模型上下文协议(MCP)将 Claude 连接到 Substance 3D Painter。
`substance-painter-mcp` 允许 Claude 创建项目、烘焙纹理图、应用智能材质、导出 PBR 纹理和捕获视口截图 —— 所有这些都通过与 Substance Painter 内置的远程脚本 API 进行通信。
---
## 快速入门(艺术家)
在 5 分钟内让 Claude 与 Substance Painter 通信。
### 先决条件
- **Substance 3D Painter** 10.x 或更高版本已安装
- **Node.js** 18+ 已安装([nodejs.org](https://nodejs.org))
- **pnpm** 已安装(`npm install -g pnpm`)
- **Claude Code** 或 **Claude Desktop**
### 1. 克隆和构建
```bash
git clone https://github.com/user/substance-painter-mcp.git
cd substance-painter-mcp
pnpm install
pnpm build
```
### 2. 配置 Claude
将以下内容添加到您的 Claude Code MCP 配置(`.claude/mcp.json` 或全局 `~/.claude.json`):
```json
{
"mcpServers": {
"substance-painter": {
"command": "node",
"args": ["/absolute/path/to/substance-painter-mcp/packages/mcp-server/dist/index.js"]
}
}
}
```
> **Windows 示例路径:** `"E:/coding/substance-painter-mcp/packages/mcp-server/dist/index.js"` (使用正斜杠)
对于 **Claude Desktop**,请将相同的块添加到您的 `claude_desktop_config.json` 中。
### 3. 启动 Substance Painter 并启用远程脚本
Substance Painter **必须** 使用 `--enable-remote-scripting` 标志启动。没有此标志,MCP 服务器无法与 SP 通信。
**Windows:**
```
"C:\Program Files\Adobe\Adobe Substance 3D Painter\Adobe Substance 3D Painter.exe" --enable-remote-scripting
```
**macOS:**
```
/Applications/Adobe\ Substance\ 3D\ Painter.app/Contents/MacOS/Adobe\ Substance\ 3D\ Painter --enable-remote-scripting
```
> **提示:** 创建一个带有标志的桌面快捷方式,以便您始终启用远程脚本启动 SP。
### 4. 验证连接
询问 Claude:
> "检查 Substance Painter 是否正在运行"
Claude 将调用 `sp_health_check` 并报告 SP 是否可达、是否打开了项目以及 SP 版本。
### 5. 首次使用
尝试这些提示:
> "从 C:/models/my-model.fbx 创建一个新的 SP 项目"
> "为当前项目烘焙所有纹理图"
> "将 PBR 纹理导出到 C:/exports/my-model"
> "截取 SP 视口的截图"
---
## 可用工具
| 工具 | 描述 | 关键参数 |
|------|-------------|----------------|
| **sp_health_check** | 检查 SP 是否正在运行且可访问 | -- |
| **sp_create_project** | 从 FBX/OBJ 网格创建项目 | `mesh_file_path`, `normal_map_format?`, `texture_resolution?` |
| **sp_close_project** | 关闭当前项目 | `save?` |
| **sp_bake_maps** | 开始烘焙纹理图(非阻塞) | `map_types?` |
| **sp_get_bake_status** | 查询烘焙进度(完成时自动截图) | `job_id` |
| **sp_apply_smart_material** | 按名称搜索和应用智能材质 | `material_name`, `texture_set?` |
| **sp_export_textures** | 将 PBR 纹理导出到磁盘 | `export_path`, `preset?`, `file_format?`, `bit_depth?` |
| **sp_get_viewport_screenshot** | 将 SP 视口捕获为 PNG 图像 | -- |
### 工具类别
**健康状况:** `sp_health_check` -- 始终首先运行以验证 SP 连接性。
**项目:** `sp_create_project`, `sp_close_project` -- 具有安全检查的项目生命周期(如果已经打开,则不会创建;如果未打开,则不会关闭)。
**烘焙:** `sp_bake_maps`, `sp_get_bake_status` -- 发射和轮询模式。`sp_bake_maps` 立即返回 `job_id`;Claude 轮询 `sp_get_bake_status` 直到完成。完成响应包括视口截图。
**材质:** `sp_apply_smart_material` -- 按名称搜索 SP 的架子并将第一个匹配项应用于活动纹理集。
**导出:** `sp_export_textures` -- 使用可配置的预设、格式(PNG/EXR/TGA)和位深度(8/16)导出 PBR 纹理。
**视口:** `sp_get_viewport_screenshot` -- 将 SP 窗口捕获为 PNG 图像(小于 1MB 用于 MCP 传输)。
---
## 开发者指南
### 架构
```
Claude (LLM)
|
v
MCP 服务器(Node.js / TypeScript)
| stdio 传输
v
SPClient.execute(pythonCode)
| HTTP POST(base64 编码的 Python)
v
Substance Painter 远程脚本(localhost:60041)
| 在 SP 环境中执行 Python
v
substance_painter Python API
| print(json.dumps({...}))
v
JSON 响应返回到 MCP 服务器
|
v
Claude 接收结构化结果
```
MCP 服务器是一个薄桥梁。 它 **不** 嵌入 SP 逻辑。 相反,它向 SP 的内置远程脚本 HTTP 服务器(localhost:60041)发送 Python 代码片段。 SP 在其自己的环境中执行 Python(具有对 `substance_painter` API 的完全访问权限)并返回 JSON 结果。
### 单体仓库结构
```
substance-painter-mcp/
packages/
mcp-server/ # Node.js MCP 服务器(npm 包)
src/
index.ts # 入口点,注册所有工具
sp-client.ts # 到 SP 远程脚本的 HTTP 桥接
js-snippets.ts # 每个工具的 JS/Python 代码模板
types.ts # 共享响应类型
tools/ # 工具处理程序(每个类别一个文件)
health.ts
project.ts
baking.ts
export.ts
materials.ts
viewport.ts
__tests__/ # 单元测试(vitest)
sp-client.test.ts
tools.test.ts
mocks/
sp-server.ts # 用于测试的模拟 SP HTTP 服务器
package.json
tsconfig.json
vitest.config.ts
sp-plugin/ # SP 的 Python 帮助程序
sp_mcp_bridge/
__init__.py
helpers.py # make_response(), make_error()
install.py # SP 插件目录安装程序
README.md
scripts/
integration-test.py # 针对正在运行的 SP 的完整往返测试
claude-mcp-config.example.json
package.json # 根工作区配置
pnpm-workspace.yaml
LICENSE # MIT
README.md # 此文件
```
### 如何添加新工具
工具使用混合 JS+Python 模式:用于预检查和验证的 JS IIFE 片段,用于需要 `substance_painter` API 的操作的 Python 片段。
1. **在 `packages/mcp-server/src/js-snippets.ts` 中添加代码片段:**
```typescript
// 预检查(JS IIFE — 通过 executeJS 运行)
myToolPreCheck(): string {
return `(function() {
try {
if (!alg.project.isOpen()) {
return { success: false, summary: "无打开的项目", error: "no_project_open" };
}
return { success: true, summary: "就绪" };
} catch(e) {
return { success: false, summary: "预检查失败", error: e.message };
}
})()`;
},
// 操作(Python — 通过 executePython 运行)
myToolPython(param: string): string {
const safePath = escapePath(param);
return `
import substance_painter.some_module
substance_painter.some_module.do_thing("${safePath}")
`.trim();
},
// 验证(JS IIFE — 通过 executeJS 运行)
verifyMyTool(): string {
return `(function() {
try {
return { success: true, summary: "工具完成", data: {} };
} catch(e) {
return { success: false, summary: "验证失败", error: e.message };
}
})()`;
},
```
2. **在 `packages/mcp-server/src/tools/my-tool.ts` 中创建工具处理程序:**
```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import type { SPClient } from "../sp-client.js";
import { makeToolResponse } from "../types.js";
import { jsSnippets } from "../js-snippets.js";
export function registerMyTools(server: McpServer, client: SPClient): void {
server.tool(
"sp_my_new_tool",
"此工具的作用描述",
{ param: z.string().describe("此参数控制的内容") },
async ({ param }) => {
// 1. 预检查(JS)
const preCheck = await client.executeJS(jsSnippets.myToolPreCheck());
if (!preCheck.success) return makeToolResponse(preCheck);
// 2. 操作(Python)
await client.executePython(jsSnippets.myToolPython(param));
// 3. 验证(JS)
const result = await client.executeJS(jsSnippets.verifyMyTool());
return makeToolResponse(result);
},
);
}
```
3. **在 `index.ts` 中注册:**
```typescript
import { registerMyTools } from "./tools/my-tool.js";
registerMyTools(server, client);
```
4. **在 `packages/mcp-server/src/__tests__/tools.test.ts` 中添加测试。**
5. **运行测试:** `pnpm test`
### SPClient 工作原理
`SPClient` 是 MCP 服务器和 Substance Painter 之间的 HTTP 桥接:
1. 接受原始 Python 代码作为字符串
2. Base64 编码(SP 需要此编码)
3. 使用 `{"python": "<base64>"}` 以 `http://localhost:60041/run.json` 发送 POST 请求
4. SP 在其环境中执行 Python 代码
5. Python 代码必须以 `print(json.dumps({...}))` 结束以返回结果
6. SPClient 解析 JSON 响应并返回
错误处理包括:
- `ECONNREFUSED` -- SP 未运行或未启用 `--enable-remote-scripting`
- HTTP 错误 -- 来自 SP 的意外状态代码
- 无效 JSON -- Python 代码未打印有效 JSON
- 超时 -- 操作时间超过 30 秒(烘焙使用轮询代替)
### Python 片段模式
每个工具都向 SP 发送 Python 代码片段。片段遵循此模式:
```python
import substance_painter.module
import json
try:
# 1. 安全检查(项目是否打开等)
# 2. 使用 substance_painter API 执行操作
# 3. 以 JSON 形式返回结果
print(json.dumps({
"success": True,
"summary": "人类可读的一句话",
"data": { "key": "结构化数据" }
}))
except Exception as e:
print(json.dumps({
"success": False,
"summary": "出了什么问题",
"error": str(e),
"hint": "可行的建议"
}))
```
所有响应都使用标准形状:`{success, summary, data?, error?, hint?}`。
### 贡献
1. 复刻仓库
2. 创建功能分支:`git checkout -b feat/my-new-tool`
3. 先编写测试(鼓励 TDD):`pnpm test`
4. 遵循上述工具添加模式
5. 确保所有测试通过:`pnpm test`
6. 提交拉取请求
---
## 故障排除
### "连接被拒绝" 或 ECONNREFUSED
**原因:** Substance Painter 未运行,或启动时未带 `--enable-remote-scripting` 标志。
**解决方法:** 使用标志启动 SP:
```
"Adobe Substance 3D Painter.exe" --enable-remote-scripting
```
然后运行 `sp_health_check` 以验证连接性。
### "项目已打开"
**原因:** 在 SP 中已有项目打开时调用了 `sp_create_project`。SP 每次只能打开一个项目。
**解决方法:** 首先关闭当前项目:
- 询问 Claude: "关闭当前 SP 项目"
- 或手动: 文件 > 在 SP 中关闭
### "烘焙时间过长" 或 "烘焙超时"
**原因:** 烘焙是 GPU 密集型操作,根据网格复杂性和贴图数量的不同,可能需要 30 秒到几分钟。
**工作原理:** `sp_bake_maps` 立即返回 `job_id`。Claude 自动轮询 `sp_get_bake_status` 以检查进度。当烘焙完成时,响应包括视口截图。
**解决方法:** 只需等待。如果烘焙似乎卡住,请检查 SP 的 UI 以查看烘焙是否正在进行。非常复杂的网格和许多纹理集将需要更长时间。
### "导出失败"
**原因:** 导出路径不存在、不可写或预设名称与已安装的预设不匹配。
**解决方法:**
- 确保导出目录存在
- 检查磁盘空间
- 使用标准预设名称,如 "PBR Metallic Roughness"
### 版本兼容性
| SP 版本 | 状态 | 注释 |
|------------|--------|-------|
| 10.0 (2024) | 支持 | 基线 API,PySide2 (Qt5) |
| 10.1+ (晚 2024) | 支持 | PySide6 (Qt6),Python 3.11 |
| 11.x (2025) | 支持 | 最新 API (0.3.4+),实验性自动围栏烘焙 |
MCP 服务器通过条件导入自动处理 PySide6/PySide2 差异。
---
## 许可证
MIT -- 参见 [LICENSE](LICENSE)。