Metadata-Version: 2.5
Name: dcc-mcp-substance3d-painter
Version: 0.5.1
Summary: Substance 3D Painter adapter for the DCC Model Context Protocol ecosystem
Project-URL: Homepage, https://github.com/dcc-mcp/dcc-mcp-substance3d-painter
Project-URL: Repository, https://github.com/dcc-mcp/dcc-mcp-substance3d-painter
Project-URL: Issues, https://github.com/dcc-mcp/dcc-mcp-substance3d-painter/issues
Project-URL: Changelog, https://github.com/dcc-mcp/dcc-mcp-substance3d-painter/releases
Author-email: Long Hao <hal.long@outlook.com>
License: MIT
License-File: LICENSE
Keywords: ai,dcc,mcp,model-context-protocol,substance,substance-3d-painter
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling
Requires-Python: >=3.9
Requires-Dist: dcc-mcp-core<1.0.0,>=0.20.15
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: jsonschema>=4.23; extra == 'dev'
Requires-Dist: packaging>=24.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: pyyaml>=6.0; extra == 'dev'
Requires-Dist: ruff>=0.8.0; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Description-Content-Type: text/markdown

# dcc-mcp-substance3d-painter

<p align="center">
  <img src="docs/assets/dcc-mcp-substance3d-painter.svg" alt="DCC-MCP · SUBSTANCE3D-PAINTER" width="600">
</p>

## Agent workflow

AI agents should use the shared gateway through `dcc-mcp-cli`; IDE users may
continue to use the MCP endpoint. Prefer typed skills and tools over raw scripts.

### Install or update the CLI

`dcc-mcp-cli` is the preferred control path for every shell-capable agent. If
it is missing, ask the user before installing the latest official release:

```bash
# Linux/macOS
curl -fsSL https://raw.githubusercontent.com/dcc-mcp/dcc-mcp-core/main/scripts/install-cli.sh | sh

# Windows PowerShell
powershell -ExecutionPolicy Bypass -c "irm https://raw.githubusercontent.com/dcc-mcp/dcc-mcp-core/main/scripts/install-cli.ps1 | iex"
```

Keep an official build current through the release manifest:

```bash
dcc-mcp-cli update check
dcc-mcp-cli update apply
```

`update apply` downloads and stages the latest CLI for the next launch. It
does not update a running `dcc-mcp-server`; update that server in its own
environment.

```bash
dcc-mcp-cli dcc-types
dcc-mcp-cli list
dcc-mcp-cli search --query "<task>" --dcc-type substance3d_painter
dcc-mcp-cli describe <tool-slug>
dcc-mcp-cli call <tool-slug> --json '{"key":"value"}'
```

`dcc-types` reports release-catalog support; `list` reports live sessions. If a
tool belongs to an inactive progressive skill, call `dcc-mcp-cli load-skill <skill-name> --dcc-type substance3d_painter` before retrying. For post-task improvement,
attach a stable session id with `--meta-json`, query `dcc-mcp-cli stats --range 24h --session-id <task-id>`, then pass the bounded evidence to the
`review_skill_improvement` prompt from `dcc-mcp-skills-creator`.


Substance 3D Painter adapter for the DCC Model Context Protocol (MCP).

It embeds a Streamable HTTP MCP server in Painter and routes host API calls
through Painter's Qt main thread. Painter uses an OS-assigned port by default
and advertises the endpoint through DCC-MCP discovery.

## Install and load

Use the agent-first lifecycle for preflight, a non-mutating plan, receipted
installation, live verification, upgrades, and uninstall. See
[install.md](install.md) for exact Windows, macOS, and Linux commands and the
manual environment path.

The lifecycle uses the released Core 0.20.15 Install SOP contract. Readiness is
fail-closed unless the registry, independently observed process identity, and
in-host diagnostics all bind the same Painter executable, start identity,
process-owned loopback listener, adapter payload, and receipt. CI uses hermetic
identity/probe fixtures; it does not claim to validate a real Adobe Painter
launch.

Set `DCC_MCP_SUBSTANCE3D_PAINTER_PORT` before launching Painter only when a
fixed port is required; `0` keeps automatic allocation. Standard
`DCC_MCP_GATEWAY_PORT` and `DCC_MCP_REGISTRY_DIR` settings are also honoured.

## Bundled skills

`painter-project` provides typed tools for a complete material-authoring pass,
plus `execute_materialized_script` for an unchanged Python FileRef returned by
Core `materialize_script`:

- inspect the project and texture sets;
- create PBR fill layers;
- search Painter resources and apply smart materials;
- list export presets, save the `.spp`, and export textures.

The tools use Painter resource and preset URLs supplied by Painter itself. They
do not expose raw JavaScript, inline source, caller-selected paths, execution
modes, or UI automation. A materialized FileRef is an integrity envelope, not a
trust decision or a Python security sandbox: only materialize source that is
trusted to run with the current Painter process privileges. Reflective code or
background work intended to outlive the fixed request lifecycle is outside the
supported contract. The materialized-script executor validates Core's
scoped sidecar, file identity, digest, size, encoding, and expiry before the
fixed `main()` entry point reaches Painter's main thread. It repeats the full
FileRef check immediately before dispatch, clones and invokes the validated
function with its prefix globals through a host callable captured before source
entry, snapshots its strict JSON result when valid, and executes suffix source
exactly once after every source-entered `main()` attempt in a quarantined
side-effect phase. Direct `json` imports and executor-module imports resolve to
request-private JSON state. The executor import is a minimal facade with no
executor callables or canonical module state, so a retained facade cannot regain
host globals after return. Prefix or suffix rebinding cannot change the captured
entrypoint behavior or result, and suffix failures do not clobber the main
outcome (including a stable rejection for invalid results).
The suffix is side-effect-only: a `main()` that requires a helper, import, or
mutable initialization introduced only by the suffix is rejected with the
stable `script_suffix_dependency` error, so valid scripts must define those
dependencies before `main()`.
Cancellation remains bound to the exact host token and job captured before
source entry; the executor restores the captured host ContextVar state,
cancellation module bindings, validator alias, and canonical JSON serializer
after both source phases. These guarantees cover the fixed executor contract;
they must not be interpreted as isolation from arbitrary untrusted Python in
the same process.
Results accept only strict portable JSON: string-keyed plain objects, plain
arrays, JSON scalars, and finite numbers, with maximum depth 64, 10,000 nodes,
and 256 KiB of compact UTF-8 JSON. The byte limit covers the complete public
response, including host-owned context and postcondition fields. Validation and
normalization capabilities remain outside script-writable state. Source-entered
failures never carry retry or rematerialization guidance.

## Development

```bash
python -m pip install -e ".[dev]"
python -m pytest
ruff check src tests tools
uv lock --check
python -m build
```

Releases use release-please. The `release.yml` workflow builds one verified
wheel/sdist bundle, publishes those exact files through the `pypi` environment
using Trusted Publishing, and attaches the same checksummed files to the GitHub
Release.
