substance-painter-mcp

substance-painter-mcp

Enables conversational control of Adobe Substance 3D Painter for look-dev iteration, including baking, smart materials, and generator parameter tuning.

Category
访问服务器

README

substance-painter-mcp

An MCP server for driving Adobe Substance 3D Painter conversationally — bake mesh maps, stack smart materials, and tune generator parameters by talking to the app instead of guessing weights and re-baking.

Built for look-dev iteration, where weathering is authored by taste against a reference rather than by fixed numbers.

Requirements

  • Substance 3D Painter 12.x (developed against 12.1.1 / Python API 0.3.5), running
  • Python 3.10+ for the MCP server

Windows, macOS and Linux. No launch flags and no --enable-remote-scripting — Painter can be started normally. If you have no Python installed, Painter bundles a complete CPython 3.13 at <install>/resources/pythonsdk/python.exe that works fine.

Install

1. Install the bridge plugin into Painter

python scripts/install_plugin.py

This copies painter_plugin/substance_painter_mcp_bridge into Painter's user plugin folder, <Documents>/Adobe/Adobe Substance 3D Painter/python/plugins/. On Windows the Documents location is read from the shell-folder registry, so OneDrive redirection and moved Documents folders are handled; pass --plugins-dir to override.

Use --link to symlink instead of copy while developing (needs Developer Mode or an elevated shell on Windows), and --uninstall to remove it.

2. Enable it in Painter

Python → Reload Plugins Folder, then tick substance_painter_mcp_bridge under Python → Plugins.

This step is required. Painter treats everything in python/plugins/ as an optional component: modules are discovered but not started until enabled. Only python/startup/ loads automatically. Painter remembers the choice, so this is one-time — but a fresh install that skips it will simply never open the port.

Prefer no UI step? python scripts/install_plugin.py --startup installs into startup/ instead, which always loads, at the cost of losing the on/off toggle.

Either way the log window should show:

[substance-painter-mcp] listening on 127.0.0.1:60043

3. Install the MCP server

python -m venv .venv
.venv/Scripts/python -m pip install -e .     # .venv/bin/python on macOS/Linux

4. Register it with your MCP client

{
  "mcpServers": {
    "substance-painter": {
      "command": "C:/Users/you/code/substance-painter-mcp/.venv/Scripts/python.exe",
      "args": ["-m", "substance_painter_mcp"]
    }
  }
}

Then call painter_status — it distinguishes "Painter isn't running" from "that operation was invalid", so it's the right first thing to try when something fails.

Configuration

Variable Default Meaning
SP_MCP_BRIDGE_HOST 127.0.0.1 Bind/connect address
SP_MCP_BRIDGE_PORT 60043 Bridge port
SP_MCP_BRIDGE_TIMEOUT 600 Client socket timeout, seconds

The bridge binds to loopback only. It executes arbitrary Python inside Painter by design (painter_exec), so do not expose the port beyond localhost.

Tools

Tool Purpose
painter_status Reachability and version check
painter_project_info / painter_create_project / painter_open_project / painter_save_project Project lifecycle
painter_search_resources Find smart materials, generators, masks, export presets
painter_get_baking_parameters / painter_set_baking_parameters Inspect and set bake settings, including high-poly path and cage distance
painter_bake / painter_baking_status Bake mesh maps (async, with optional wait)
painter_layer_tree Read the layer stack; every node has a uid
painter_insert_smart_material Instantiate a shelf smart material
painter_insert_layer / painter_insert_generator / painter_add_mask Build stacks
painter_set_fill_source / painter_import_resource Assign a bitmap or flat colour to a fill layer's channel
painter_set_layer / painter_delete_layer Name, visibility, opacity, blending mode; delete
painter_screenshot Capture the viewport — see the result instead of inferring it
painter_get_parameters / painter_set_parameters / painter_apply_preset Tune generators — the look-dev loop
painter_list_export_presets / painter_export_textures Export maps
painter_list_ops / painter_call / painter_exec Escape hatches

The tuning loop

painter_get_parameters is the tool that makes conversational iteration work. It returns each parameter's key, human label, widget type, current value and enum options, so the model discovers that a rust generator exposes Dirt Level rather than guessing at names:

painter_layer_tree()                        → find the generator's uid
painter_get_parameters(uid=42)              → see what knobs exist
painter_set_parameters(uid=42, '{"Dirt Level": 0.7}')
painter_screenshot()                        → look at what that did

painter_screenshot closes the loop. Without it the model is editing blind and has to trust that a parameter did what it claims; with it, each change can actually be checked.

A smart material is a group of layers, so tuning one means walking into the group and adjusting the generator effects inside it. painter_layer_tree returns the full hierarchy with uids in a single call for that reason.

Wear is driven by the baked mesh maps via generators — mg_metal_edge_wear for edges and bolts, mg_dirt for crevices, mg_position multiplied into a mask to bias wear vertically. Mask effects blend with each other, which is how "heavier down low, lighter up top" gets expressed.

How it works

Claude  ──stdio──>  MCP server (this repo, any Python)
                          │
                          │  newline-delimited JSON over TCP, loopback only
                          ▼
                    Bridge plugin (runs inside Painter, Qt server on the main thread)
                          │
                          ▼
                    substance_painter Python API

The bridge is a Painter plugin rather than the --enable-remote-scripting HTTP endpoint, for two reasons:

  1. No launch flag. Painter can be started normally, including from Creative Cloud.
  2. Thread affinity. The substance_painter API is only safe to call from the main/UI thread. A Qt QTcpServer created inside the plugin lives on Painter's own event loop, so every request handler already runs on the right thread — no cross-thread marshalling and no temp-file result passing.

Wire protocol

Newline-delimited JSON, one object per line:

→ {"id": 1, "op": "layers.tree", "params": {"deep": true}}
← {"id": 1, "ok": true, "result": {...}}
← {"id": 1, "ok": false, "error": {"kind": "no_project", "message": "..."}}

json.dumps escapes newlines inside strings, so a serialised message never contains a raw newline of its own and the framing is safe.

Development

.venv/Scripts/python -m pytest      # protocol tests, no Painter needed
.venv/Scripts/python -m ruff check .

tests/ runs the bridge client against a fake NDJSON server, so framing, error mapping and reconnect-after-plugin-reload are covered without the application installed. Anything touching the substance_painter API needs Painter running.

Against a running Painter:

python scripts/smoke_test.py                      # exercise the bridge end to end
python scripts/reload_plugin.py                   # hot-reload after editing the plugin
python scripts/smoke_test.py --mesh mesh.fbx --force   # also create a project

reload_plugin.py avoids a Painter restart on every edit. Install the plugin with --link so the files are already in place and only the reload is needed.

Notes and limitations

  • Baking is asynchronous. API 0.3.5 has no synchronous bake(), only bake_async. The bridge tracks progress through Painter's BakingProcessProgress / BakingProcessEnded events and painter_bake polls to completion by default.
  • Cage distance matters on meshes with overlapping shells. Cage distances are relative to the bounding box by default, and a generous cage samples interior surfaces and produces black patches. Read the real key names with painter_get_baking_parameters before setting them — they come from Painter, not from this server.
  • One bake at a time. The bridge rejects a second concurrent bake.

See docs/api-findings.md for what was verified against the installed SDK, including several silent-failure modes worth knowing about.

License

MIT — see LICENSE.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选