dcc-mcp-touchdesigner 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.
- dcc_mcp_touchdesigner-0.1.0/.github/scripts/start_mcp_server.py +42 -0
- dcc_mcp_touchdesigner-0.1.0/.github/workflows/ci.yml +59 -0
- dcc_mcp_touchdesigner-0.1.0/.github/workflows/release.yml +68 -0
- dcc_mcp_touchdesigner-0.1.0/.gitignore +32 -0
- dcc_mcp_touchdesigner-0.1.0/.release-please-manifest.json +3 -0
- dcc_mcp_touchdesigner-0.1.0/CHANGELOG.md +18 -0
- dcc_mcp_touchdesigner-0.1.0/PKG-INFO +129 -0
- dcc_mcp_touchdesigner-0.1.0/README.md +96 -0
- dcc_mcp_touchdesigner-0.1.0/install.md +123 -0
- dcc_mcp_touchdesigner-0.1.0/packaging/assemble_release.py +69 -0
- dcc_mcp_touchdesigner-0.1.0/pyproject.toml +89 -0
- dcc_mcp_touchdesigner-0.1.0/release-please-config.json +36 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/__init__.py +48 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/__version__.py +1 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/_core_compat.py +37 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/_env.py +107 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/api.py +26 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/capabilities.py +61 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/dispatcher/__init__.py +17 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/host.py +365 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/operations.py +240 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/server.py +561 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/skills/touchdesigner-scripting/SKILL.md +39 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/skills/touchdesigner-scripting/scripts/capture_top.py +20 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/skills/touchdesigner-scripting/scripts/connect_operators.py +30 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/skills/touchdesigner-scripting/scripts/create_operator.py +20 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/skills/touchdesigner-scripting/scripts/delete_operator.py +20 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/skills/touchdesigner-scripting/scripts/execute_python.py +65 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/skills/touchdesigner-scripting/scripts/get_op_parameters.py +29 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/skills/touchdesigner-scripting/scripts/get_project_info.py +20 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/skills/touchdesigner-scripting/scripts/list_operators.py +33 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/skills/touchdesigner-scripting/scripts/save_project.py +20 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/skills/touchdesigner-scripting/scripts/set_op_parameter.py +28 -0
- dcc_mcp_touchdesigner-0.1.0/src/dcc_mcp_touchdesigner/skills/touchdesigner-scripting/tools.yaml +329 -0
- dcc_mcp_touchdesigner-0.1.0/tests/test_host.py +73 -0
- dcc_mcp_touchdesigner-0.1.0/tests/test_operations.py +177 -0
- dcc_mcp_touchdesigner-0.1.0/tests/test_packaging.py +34 -0
- dcc_mcp_touchdesigner-0.1.0/tests/test_server.py +502 -0
- dcc_mcp_touchdesigner-0.1.0/tests/test_skill_contract.py +44 -0
- dcc_mcp_touchdesigner-0.1.0/uv.lock +1541 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""CI script — start the TouchDesigner MCP server for smoke testing.
|
|
2
|
+
|
|
3
|
+
This script CANNOT run in CI without a running TouchDesigner process.
|
|
4
|
+
It serves as a reference for manual smoke testing.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main() -> int:
|
|
14
|
+
"""Start dcc-mcp-touchdesigner and print the MCP URL as JSON."""
|
|
15
|
+
try:
|
|
16
|
+
import dcc_mcp_touchdesigner
|
|
17
|
+
except ImportError:
|
|
18
|
+
print(json.dumps({"error": "dcc_mcp_touchdesigner not installed"}))
|
|
19
|
+
return 1
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
server = dcc_mcp_touchdesigner.start_server(port=0)
|
|
23
|
+
server.discover_skills()
|
|
24
|
+
print(
|
|
25
|
+
json.dumps(
|
|
26
|
+
{
|
|
27
|
+
"status": "running",
|
|
28
|
+
"mcp_url": server.mcp_url,
|
|
29
|
+
"port": server.port,
|
|
30
|
+
"version": dcc_mcp_touchdesigner.__version__,
|
|
31
|
+
"skills_discovered": server.loaded_skill_count(),
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
return 0
|
|
36
|
+
except Exception as exc:
|
|
37
|
+
print(json.dumps({"error": str(exc)}))
|
|
38
|
+
return 1
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
if __name__ == "__main__":
|
|
42
|
+
sys.exit(main())
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
test:
|
|
17
|
+
name: Python ${{ matrix.python-version }}
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
timeout-minutes: 15
|
|
20
|
+
strategy:
|
|
21
|
+
fail-fast: false
|
|
22
|
+
matrix:
|
|
23
|
+
python-version: ["3.9", "3.12"]
|
|
24
|
+
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
27
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
28
|
+
with:
|
|
29
|
+
python-version: ${{ matrix.python-version }}
|
|
30
|
+
cache: pip
|
|
31
|
+
- name: Install
|
|
32
|
+
run: python -m pip install -e ".[dev]" build twine
|
|
33
|
+
- name: Install dcc-mcp-cli
|
|
34
|
+
env:
|
|
35
|
+
DCC_MCP_CLI_VERSION: 0.20.2
|
|
36
|
+
DCC_MCP_CLI_SHA256: 1168ebe901cfc0526d3d0f363d18ac4f8e21d2bcc874557e5813acbee8480f9c
|
|
37
|
+
run: |
|
|
38
|
+
curl --fail --location --retry 3 \
|
|
39
|
+
--output /tmp/dcc-mcp-cli \
|
|
40
|
+
"https://github.com/dcc-mcp/dcc-mcp-core/releases/download/v${DCC_MCP_CLI_VERSION}/dcc-mcp-cli-linux-x86_64"
|
|
41
|
+
echo "${DCC_MCP_CLI_SHA256} /tmp/dcc-mcp-cli" | sha256sum --check
|
|
42
|
+
chmod +x /tmp/dcc-mcp-cli
|
|
43
|
+
sudo install /tmp/dcc-mcp-cli /usr/local/bin/dcc-mcp-cli
|
|
44
|
+
- name: Lint
|
|
45
|
+
run: |
|
|
46
|
+
ruff check src tests packaging .github/scripts
|
|
47
|
+
ruff format --check src tests packaging .github/scripts
|
|
48
|
+
- name: Validate packaged Skill
|
|
49
|
+
run: dcc-mcp-cli lint --warnings-as-errors src/dcc_mcp_touchdesigner/skills/touchdesigner-scripting
|
|
50
|
+
- name: Test
|
|
51
|
+
run: pytest
|
|
52
|
+
- name: Build
|
|
53
|
+
if: matrix.python-version == '3.12'
|
|
54
|
+
run: |
|
|
55
|
+
python -m build
|
|
56
|
+
twine check dist/*
|
|
57
|
+
python -m venv wheel-smoke
|
|
58
|
+
wheel-smoke/bin/python -m pip install dist/*.whl
|
|
59
|
+
wheel-smoke/bin/python -c "import importlib.resources as r; root = r.files('dcc_mcp_touchdesigner'); assert root.joinpath('skills', 'touchdesigner-scripting', 'tools.yaml').is_file()"
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
tag_name:
|
|
9
|
+
description: Existing tag to rebuild and publish
|
|
10
|
+
required: false
|
|
11
|
+
default: ""
|
|
12
|
+
publish_to_pypi:
|
|
13
|
+
description: Publish a manually rebuilt tag to PyPI
|
|
14
|
+
type: boolean
|
|
15
|
+
required: false
|
|
16
|
+
default: false
|
|
17
|
+
|
|
18
|
+
concurrency:
|
|
19
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
20
|
+
cancel-in-progress: false
|
|
21
|
+
|
|
22
|
+
permissions:
|
|
23
|
+
contents: read
|
|
24
|
+
|
|
25
|
+
jobs:
|
|
26
|
+
release-please:
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
timeout-minutes: 10
|
|
29
|
+
if: github.ref == 'refs/heads/main'
|
|
30
|
+
permissions:
|
|
31
|
+
contents: write
|
|
32
|
+
pull-requests: write
|
|
33
|
+
outputs:
|
|
34
|
+
release_created: ${{ steps.release.outputs.release_created }}
|
|
35
|
+
tag_name: ${{ steps.release.outputs.tag_name || inputs.tag_name }}
|
|
36
|
+
steps:
|
|
37
|
+
- uses: googleapis/release-please-action@0dfd8538845b8e92600d271a895a5372865d4062 # v5
|
|
38
|
+
id: release
|
|
39
|
+
with:
|
|
40
|
+
token: ${{ github.token }}
|
|
41
|
+
config-file: release-please-config.json
|
|
42
|
+
manifest-file: .release-please-manifest.json
|
|
43
|
+
|
|
44
|
+
build-and-publish:
|
|
45
|
+
needs: release-please
|
|
46
|
+
if: needs.release-please.outputs.release_created == 'true' || (github.event_name == 'workflow_dispatch' && inputs.tag_name != '' && inputs.publish_to_pypi)
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
timeout-minutes: 15
|
|
49
|
+
environment:
|
|
50
|
+
name: pypi
|
|
51
|
+
url: https://pypi.org/p/dcc-mcp-touchdesigner
|
|
52
|
+
permissions:
|
|
53
|
+
contents: read
|
|
54
|
+
id-token: write
|
|
55
|
+
steps:
|
|
56
|
+
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
57
|
+
with:
|
|
58
|
+
ref: ${{ needs.release-please.outputs.tag_name || inputs.tag_name }}
|
|
59
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
60
|
+
with:
|
|
61
|
+
python-version: "3.12"
|
|
62
|
+
- run: python -m pip install --upgrade pip build twine
|
|
63
|
+
- run: python -m build
|
|
64
|
+
- run: python -m twine check dist/*
|
|
65
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
|
|
66
|
+
with:
|
|
67
|
+
verbose: true
|
|
68
|
+
print-hash: true
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.eggs/
|
|
8
|
+
*.egg
|
|
9
|
+
|
|
10
|
+
# Virtual envs
|
|
11
|
+
venv/
|
|
12
|
+
.venv/
|
|
13
|
+
env/
|
|
14
|
+
|
|
15
|
+
# IDE
|
|
16
|
+
.vscode/
|
|
17
|
+
.idea/
|
|
18
|
+
*.swp
|
|
19
|
+
*.swo
|
|
20
|
+
|
|
21
|
+
# Testing
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.coverage
|
|
24
|
+
htmlcov/
|
|
25
|
+
.tox/
|
|
26
|
+
|
|
27
|
+
# OS
|
|
28
|
+
.DS_Store
|
|
29
|
+
Thumbs.db
|
|
30
|
+
|
|
31
|
+
# Project
|
|
32
|
+
*.toe.hold
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (2026-08-12)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* initial TouchDesigner MCP adapter ([b1df6a7](https://github.com/dcc-mcp/dcc-mcp-touchdesigner/commit/b1df6a70605bc0066ff55a21602de67f662b3d58))
|
|
9
|
+
* ship production-ready TouchDesigner workflows ([#3](https://github.com/dcc-mcp/dcc-mcp-touchdesigner/issues/3)) ([973eef9](https://github.com/dcc-mcp/dcc-mcp-touchdesigner/commit/973eef9b1ee8c6c076b3c66fa4d03771f25cfc04))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* start TouchDesigner UI dispatcher lifecycle ([614f59f](https://github.com/dcc-mcp/dcc-mcp-touchdesigner/commit/614f59fced955f7088a6530755cf55263ced8a05))
|
|
15
|
+
|
|
16
|
+
## Changelog
|
|
17
|
+
|
|
18
|
+
All notable changes are generated from Conventional Commits by Release Please.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: dcc-mcp-touchdesigner
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: TouchDesigner integration for the DCC Model Context Protocol (MCP) ecosystem — embeds a Streamable HTTP MCP server directly inside TouchDesigner
|
|
5
|
+
Project-URL: Homepage, https://github.com/dcc-mcp/dcc-mcp-touchdesigner
|
|
6
|
+
Project-URL: Repository, https://github.com/dcc-mcp/dcc-mcp-touchdesigner
|
|
7
|
+
Project-URL: Issues, https://github.com/dcc-mcp/dcc-mcp-touchdesigner/issues
|
|
8
|
+
Author-email: Long Hao <hal.long@outlook.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: ai,dcc,llm,mcp,model-context-protocol,touchdesigner
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Requires-Dist: dcc-mcp-core<1.0.0,>=0.19.91
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest-mock>=3.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pyyaml>=5.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff>=0.8.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: twine<7.0,>=6.2; (python_version < '3.10') and extra == 'dev'
|
|
31
|
+
Requires-Dist: twine>=7.0; (python_version >= '3.10') and extra == 'dev'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# dcc-mcp-touchdesigner
|
|
35
|
+
|
|
36
|
+
<p align="center">
|
|
37
|
+
<img src="docs/assets/dcc-mcp-touchdesigner.svg" alt="DCC-MCP · TouchDesigner" width="600">
|
|
38
|
+
</p>
|
|
39
|
+
|
|
40
|
+
Typed, main-thread-safe TouchDesigner control for the [DCC Model Context Protocol](https://github.com/dcc-mcp/dcc-mcp-core).
|
|
41
|
+
|
|
42
|
+
The adapter embeds the DCC-MCP HTTP runtime in TouchDesigner, registers the instance with the local gateway, and executes TouchDesigner API work through the documented `td.run()` scheduler. Agents can inspect and edit operator networks, set parameters, save projects, and export TOP images without sending TouchDesigner objects to background threads.
|
|
43
|
+
|
|
44
|
+

|
|
45
|
+
|
|
46
|
+
_Illustrative workflow visualization generated with OpenAI ImageGen from the retained source in `docs/images/sources`. It is not a TouchDesigner screenshot or host-validation artifact. The header uses an approved operator-network reference motif rather than an official host mark._
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
TouchDesigner 2025 uses Python 3.11. Install this package into a matching external environment and expose that environment to TouchDesigner. The preferred TouchDesigner 2025 route is the built-in `TDPyEnvManager`; the Preferences dialog's external Python module path is also supported.
|
|
51
|
+
|
|
52
|
+
```powershell
|
|
53
|
+
py -3.11 -m venv .venv
|
|
54
|
+
.\.venv\Scripts\python -m pip install --upgrade pip
|
|
55
|
+
.\.venv\Scripts\python -m pip install dcc-mcp-touchdesigner
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
See [install.md](install.md) for the complete Windows and macOS setup, dependency isolation guidance, and startup configuration. TouchDesigner documents both [external Python packages](https://docs.derivative.ca/Python#Installing_Custom_Python_Packages) and the [TDPyEnvManager](https://docs.derivative.ca/Palette%3AtdPyEnvManager).
|
|
59
|
+
|
|
60
|
+
## Start in TouchDesigner
|
|
61
|
+
|
|
62
|
+
Run this from the Textport or an Execute DAT `onStart()` callback:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import dcc_mcp_touchdesigner
|
|
66
|
+
|
|
67
|
+
server = dcc_mcp_touchdesigner.start_server()
|
|
68
|
+
print(server.mcp_url)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`start_server()` is idempotent inside one TouchDesigner process. It loads the bundled skill by default, selects an OS-assigned loopback port, starts the main-thread pump, and advertises the instance to a running DCC-MCP gateway. Stop it explicitly during project teardown when needed:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
dcc_mcp_touchdesigner.stop_server()
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Typed tools
|
|
78
|
+
|
|
79
|
+
| Tool | Contract | Risk |
|
|
80
|
+
| --- | --- | --- |
|
|
81
|
+
| `get_project_info` | Read project name, folder, build, cook rate, and root count | Read-only |
|
|
82
|
+
| `list_operators` | List direct or recursive operators with an optional family/type filter | Read-only |
|
|
83
|
+
| `get_op_parameters` | Read selected or all parameters from one operator | Read-only |
|
|
84
|
+
| `create_operator` | Create a documented operator type under an explicit COMP | Mutation |
|
|
85
|
+
| `connect_operators` | Connect an explicit output connector to an explicit input | Mutation |
|
|
86
|
+
| `set_op_parameter` | Set one parameter and return its evaluated value | Idempotent mutation |
|
|
87
|
+
| `capture_top` | Save one TOP to an explicit PNG path with size and SHA-256 metadata | File write |
|
|
88
|
+
| `save_project` | Save the current project to an explicit `.toe` path | File write |
|
|
89
|
+
| `delete_operator` | Delete one explicitly addressed non-root operator | Destructive |
|
|
90
|
+
| `execute_python` | Advanced in-host Python escape hatch; disabled by policy when required | Destructive |
|
|
91
|
+
|
|
92
|
+
Every tool has a JSON input/output contract and is dispatched on TouchDesigner's main thread. File-writing tools require an explicit path and reject accidental overwrite unless `overwrite=true` is supplied. Root deletion is always rejected.
|
|
93
|
+
|
|
94
|
+
## Security controls
|
|
95
|
+
|
|
96
|
+
Disable arbitrary Python execution in controlled environments before starting TouchDesigner:
|
|
97
|
+
|
|
98
|
+
```powershell
|
|
99
|
+
$env:DCC_MCP_TOUCHDESIGNER_DISABLE_EXECUTE_PYTHON = "1"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The adapter binds to loopback by default. Do not expose the per-instance port directly to an untrusted network. Use the DCC-MCP gateway when multiple instances or attributable agent sessions are required.
|
|
103
|
+
|
|
104
|
+
## Runtime configuration
|
|
105
|
+
|
|
106
|
+
| Variable | Purpose |
|
|
107
|
+
| --- | --- |
|
|
108
|
+
| `DCC_MCP_TOUCHDESIGNER_SKILL_PATHS` | Additional skill directories |
|
|
109
|
+
| `DCC_MCP_TOUCHDESIGNER_METRICS` | Enable Prometheus `/metrics` |
|
|
110
|
+
| `DCC_MCP_TOUCHDESIGNER_ENABLE_GATEWAY_FAILOVER` | Enable gateway failover; on by default |
|
|
111
|
+
| `DCC_MCP_TOUCHDESIGNER_DISABLE_EXECUTE_PYTHON` | Disable the advanced Python tool |
|
|
112
|
+
|
|
113
|
+
## Development
|
|
114
|
+
|
|
115
|
+
```powershell
|
|
116
|
+
py -3.11 -m pip install -e ".[dev]"
|
|
117
|
+
pytest
|
|
118
|
+
ruff check .
|
|
119
|
+
ruff format --check .
|
|
120
|
+
dcc-mcp-cli lint src/dcc_mcp_touchdesigner/skills/touchdesigner-scripting --warnings-as-errors
|
|
121
|
+
python -m build
|
|
122
|
+
python -m twine check dist/*
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Unit and contract tests use a deterministic TouchDesigner API double. A release additionally requires a real TouchDesigner process, six readiness checks, discovery of all ten typed tools, and a create/connect/set/capture/save/delete chain through `dcc-mcp-cli`; simulation alone is not release evidence.
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
MIT
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# dcc-mcp-touchdesigner
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="docs/assets/dcc-mcp-touchdesigner.svg" alt="DCC-MCP · TouchDesigner" width="600">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
Typed, main-thread-safe TouchDesigner control for the [DCC Model Context Protocol](https://github.com/dcc-mcp/dcc-mcp-core).
|
|
8
|
+
|
|
9
|
+
The adapter embeds the DCC-MCP HTTP runtime in TouchDesigner, registers the instance with the local gateway, and executes TouchDesigner API work through the documented `td.run()` scheduler. Agents can inspect and edit operator networks, set parameters, save projects, and export TOP images without sending TouchDesigner objects to background threads.
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
_Illustrative workflow visualization generated with OpenAI ImageGen from the retained source in `docs/images/sources`. It is not a TouchDesigner screenshot or host-validation artifact. The header uses an approved operator-network reference motif rather than an official host mark._
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
TouchDesigner 2025 uses Python 3.11. Install this package into a matching external environment and expose that environment to TouchDesigner. The preferred TouchDesigner 2025 route is the built-in `TDPyEnvManager`; the Preferences dialog's external Python module path is also supported.
|
|
18
|
+
|
|
19
|
+
```powershell
|
|
20
|
+
py -3.11 -m venv .venv
|
|
21
|
+
.\.venv\Scripts\python -m pip install --upgrade pip
|
|
22
|
+
.\.venv\Scripts\python -m pip install dcc-mcp-touchdesigner
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
See [install.md](install.md) for the complete Windows and macOS setup, dependency isolation guidance, and startup configuration. TouchDesigner documents both [external Python packages](https://docs.derivative.ca/Python#Installing_Custom_Python_Packages) and the [TDPyEnvManager](https://docs.derivative.ca/Palette%3AtdPyEnvManager).
|
|
26
|
+
|
|
27
|
+
## Start in TouchDesigner
|
|
28
|
+
|
|
29
|
+
Run this from the Textport or an Execute DAT `onStart()` callback:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import dcc_mcp_touchdesigner
|
|
33
|
+
|
|
34
|
+
server = dcc_mcp_touchdesigner.start_server()
|
|
35
|
+
print(server.mcp_url)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`start_server()` is idempotent inside one TouchDesigner process. It loads the bundled skill by default, selects an OS-assigned loopback port, starts the main-thread pump, and advertises the instance to a running DCC-MCP gateway. Stop it explicitly during project teardown when needed:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
dcc_mcp_touchdesigner.stop_server()
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Typed tools
|
|
45
|
+
|
|
46
|
+
| Tool | Contract | Risk |
|
|
47
|
+
| --- | --- | --- |
|
|
48
|
+
| `get_project_info` | Read project name, folder, build, cook rate, and root count | Read-only |
|
|
49
|
+
| `list_operators` | List direct or recursive operators with an optional family/type filter | Read-only |
|
|
50
|
+
| `get_op_parameters` | Read selected or all parameters from one operator | Read-only |
|
|
51
|
+
| `create_operator` | Create a documented operator type under an explicit COMP | Mutation |
|
|
52
|
+
| `connect_operators` | Connect an explicit output connector to an explicit input | Mutation |
|
|
53
|
+
| `set_op_parameter` | Set one parameter and return its evaluated value | Idempotent mutation |
|
|
54
|
+
| `capture_top` | Save one TOP to an explicit PNG path with size and SHA-256 metadata | File write |
|
|
55
|
+
| `save_project` | Save the current project to an explicit `.toe` path | File write |
|
|
56
|
+
| `delete_operator` | Delete one explicitly addressed non-root operator | Destructive |
|
|
57
|
+
| `execute_python` | Advanced in-host Python escape hatch; disabled by policy when required | Destructive |
|
|
58
|
+
|
|
59
|
+
Every tool has a JSON input/output contract and is dispatched on TouchDesigner's main thread. File-writing tools require an explicit path and reject accidental overwrite unless `overwrite=true` is supplied. Root deletion is always rejected.
|
|
60
|
+
|
|
61
|
+
## Security controls
|
|
62
|
+
|
|
63
|
+
Disable arbitrary Python execution in controlled environments before starting TouchDesigner:
|
|
64
|
+
|
|
65
|
+
```powershell
|
|
66
|
+
$env:DCC_MCP_TOUCHDESIGNER_DISABLE_EXECUTE_PYTHON = "1"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The adapter binds to loopback by default. Do not expose the per-instance port directly to an untrusted network. Use the DCC-MCP gateway when multiple instances or attributable agent sessions are required.
|
|
70
|
+
|
|
71
|
+
## Runtime configuration
|
|
72
|
+
|
|
73
|
+
| Variable | Purpose |
|
|
74
|
+
| --- | --- |
|
|
75
|
+
| `DCC_MCP_TOUCHDESIGNER_SKILL_PATHS` | Additional skill directories |
|
|
76
|
+
| `DCC_MCP_TOUCHDESIGNER_METRICS` | Enable Prometheus `/metrics` |
|
|
77
|
+
| `DCC_MCP_TOUCHDESIGNER_ENABLE_GATEWAY_FAILOVER` | Enable gateway failover; on by default |
|
|
78
|
+
| `DCC_MCP_TOUCHDESIGNER_DISABLE_EXECUTE_PYTHON` | Disable the advanced Python tool |
|
|
79
|
+
|
|
80
|
+
## Development
|
|
81
|
+
|
|
82
|
+
```powershell
|
|
83
|
+
py -3.11 -m pip install -e ".[dev]"
|
|
84
|
+
pytest
|
|
85
|
+
ruff check .
|
|
86
|
+
ruff format --check .
|
|
87
|
+
dcc-mcp-cli lint src/dcc_mcp_touchdesigner/skills/touchdesigner-scripting --warnings-as-errors
|
|
88
|
+
python -m build
|
|
89
|
+
python -m twine check dist/*
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Unit and contract tests use a deterministic TouchDesigner API double. A release additionally requires a real TouchDesigner process, six readiness checks, discovery of all ten typed tools, and a create/connect/set/capture/save/delete chain through `dcc-mcp-cli`; simulation alone is not release evidence.
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
MIT
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Installing dcc-mcp-touchdesigner
|
|
2
|
+
|
|
3
|
+
## Requirements
|
|
4
|
+
|
|
5
|
+
- TouchDesigner 2025 official build
|
|
6
|
+
- A Python environment matching TouchDesigner's embedded Python version
|
|
7
|
+
- `dcc-mcp-touchdesigner` and its `dcc-mcp-core` dependency installed into that environment
|
|
8
|
+
|
|
9
|
+
TouchDesigner 2025 currently embeds Python 3.11. Confirm the exact interpreter from the Textport before creating an environment:
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
app.pythonExecutable
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
sys.version
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Do not mix an environment built for a different Python minor version. Avoid overriding TouchDesigner's bundled NumPy or OpenCV unless the project explicitly owns and tests those versions.
|
|
19
|
+
|
|
20
|
+
## Preferred: TDPyEnvManager
|
|
21
|
+
|
|
22
|
+
TouchDesigner 2025 includes `TDPyEnvManager` in the Palette. It creates a project-local Python environment, adds it to TouchDesigner's import path, and can restore requirements at startup.
|
|
23
|
+
|
|
24
|
+
1. Add `TDPyEnvManager` from the Palette to the project.
|
|
25
|
+
2. Select Python vEnv mode and create a `.venv` beside the `.toe` project.
|
|
26
|
+
3. Open that environment's CLI from the component.
|
|
27
|
+
4. Install the adapter:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
python -m pip install --upgrade pip
|
|
31
|
+
python -m pip install dcc-mcp-touchdesigner
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
5. Restart TouchDesigner so its Python search path is rebuilt.
|
|
35
|
+
|
|
36
|
+
For a reproducible project, add this line to the project's `requirements.txt` and let the component restore it:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
dcc-mcp-touchdesigner==0.1.0
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
See Derivative's [TDPyEnvManager documentation](https://docs.derivative.ca/Palette%3AtdPyEnvManager) for `pyproject.toml`, `autoSetup`, and environment lifecycle options.
|
|
43
|
+
|
|
44
|
+
## Alternative: external Python 3.11 environment
|
|
45
|
+
|
|
46
|
+
Create a normal Python 3.11 virtual environment outside TouchDesigner:
|
|
47
|
+
|
|
48
|
+
```powershell
|
|
49
|
+
py -3.11 -m venv .venv
|
|
50
|
+
.\.venv\Scripts\python -m pip install --upgrade pip
|
|
51
|
+
.\.venv\Scripts\python -m pip install dcc-mcp-touchdesigner
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
In TouchDesigner, open **Edit > Preferences > Python**:
|
|
55
|
+
|
|
56
|
+
1. Enable **Add Externally Installed Python Site-Packages to Search Path**.
|
|
57
|
+
2. Set **Python 64-bit Module Path** to the environment's `Lib\site-packages` directory on Windows, or `lib/python3.11/site-packages` on macOS.
|
|
58
|
+
3. Restart TouchDesigner.
|
|
59
|
+
|
|
60
|
+
Derivative documents this path in [Installing Custom Python Packages](https://docs.derivative.ca/Python#Installing_Custom_Python_Packages).
|
|
61
|
+
|
|
62
|
+
## Verify imports
|
|
63
|
+
|
|
64
|
+
Open the Textport and run:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import dcc_mcp_core
|
|
68
|
+
import dcc_mcp_touchdesigner
|
|
69
|
+
|
|
70
|
+
print(dcc_mcp_touchdesigner.__version__)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The `td` module is only available inside TouchDesigner. A normal terminal can validate package installation, but it cannot validate host dispatch or typed tool execution.
|
|
74
|
+
|
|
75
|
+
## Start and stop with the project
|
|
76
|
+
|
|
77
|
+
For manual testing:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import dcc_mcp_touchdesigner
|
|
81
|
+
|
|
82
|
+
server = dcc_mcp_touchdesigner.start_server()
|
|
83
|
+
print(server.mcp_url)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
For persistent startup, place the same call in an Execute DAT `onStart()` callback. Pair it with `stop_server()` in `onExit()`:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
def onStart():
|
|
90
|
+
import dcc_mcp_touchdesigner
|
|
91
|
+
|
|
92
|
+
dcc_mcp_touchdesigner.start_server()
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def onExit():
|
|
97
|
+
import dcc_mcp_touchdesigner
|
|
98
|
+
|
|
99
|
+
dcc_mcp_touchdesigner.stop_server()
|
|
100
|
+
return
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The server uses an OS-assigned loopback port and registers itself when a DCC-MCP gateway is available. Do not hard-code a port unless an isolated integration requires it.
|
|
104
|
+
|
|
105
|
+
## Verify readiness and tools
|
|
106
|
+
|
|
107
|
+
From a terminal with `dcc-mcp-cli` installed:
|
|
108
|
+
|
|
109
|
+
```powershell
|
|
110
|
+
dcc-mcp-cli list --output json
|
|
111
|
+
dcc-mcp-cli search --dcc-type touchdesigner --output json
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
For release acceptance, verify all six readiness checks and call a cheap read-only typed tool before any mutation. Route later calls to the exact `instance_id` when more than one TouchDesigner process is running.
|
|
115
|
+
|
|
116
|
+
## Troubleshooting
|
|
117
|
+
|
|
118
|
+
- `No module named dcc_mcp_touchdesigner`: confirm the configured site-packages path and restart TouchDesigner.
|
|
119
|
+
- Binary import or DLL load errors: rebuild the environment with the exact Python minor version reported by `sys.version`.
|
|
120
|
+
- `No module named td` in a terminal: expected; run host API code inside TouchDesigner.
|
|
121
|
+
- Instance absent from the CLI: compare the registry directory and gateway environment used by TouchDesigner and the terminal.
|
|
122
|
+
- HTTP server is available but host tools time out: confirm the project is cooking and the adapter's `td.run()` main-thread pump has not been stopped.
|
|
123
|
+
- `execute_python` is unavailable: check `DCC_MCP_TOUCHDESIGNER_DISABLE_EXECUTE_PYTHON`; typed tools remain available.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Assemble dcc-mcp-touchdesigner release payload.
|
|
2
|
+
|
|
3
|
+
Usage: python packaging/assemble_release.py
|
|
4
|
+
|
|
5
|
+
Produces a zip archive with the wheel plus a bootstrap script that can be
|
|
6
|
+
extracted into TouchDesigner's site-packages or a custom project folder.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from zipfile import ZIP_DEFLATED, ZipFile
|
|
16
|
+
|
|
17
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
18
|
+
DIST = ROOT / "dist"
|
|
19
|
+
BOOTSTRAP_TEMPLATE = '''"""TouchDesigner MCP bootstrap — run from a Text DAT on the host thread."""
|
|
20
|
+
import os
|
|
21
|
+
import sys
|
|
22
|
+
|
|
23
|
+
_WHEEL = os.path.join(os.path.dirname(__file__), "payload", {wheel_name!r})
|
|
24
|
+
if not os.path.isfile(_WHEEL):
|
|
25
|
+
raise RuntimeError(f"TouchDesigner adapter wheel not found: {{_WHEEL}}")
|
|
26
|
+
if _WHEEL not in sys.path:
|
|
27
|
+
sys.path.insert(0, _WHEEL)
|
|
28
|
+
|
|
29
|
+
import dcc_mcp_touchdesigner
|
|
30
|
+
|
|
31
|
+
server = dcc_mcp_touchdesigner.start_server()
|
|
32
|
+
print(f"[dcc-mcp-touchdesigner] MCP server listening at {{server.mcp_url}}")
|
|
33
|
+
'''
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def build_wheel() -> Path:
|
|
37
|
+
"""Build the Python wheel and return its path."""
|
|
38
|
+
subprocess.run(
|
|
39
|
+
[sys.executable, "-m", "pip", "wheel", "--no-deps", "-w", str(DIST), str(ROOT)],
|
|
40
|
+
check=True,
|
|
41
|
+
)
|
|
42
|
+
wheels = sorted(DIST.glob("dcc_mcp_touchdesigner-*.whl"))
|
|
43
|
+
if not wheels:
|
|
44
|
+
raise SystemExit("wheel build failed: no .whl found")
|
|
45
|
+
return wheels[-1]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def assemble_zip(out_path: Path, wheel: Path) -> None:
|
|
49
|
+
"""Create the release zip with bootstrap script + wheel."""
|
|
50
|
+
with ZipFile(out_path, "w", ZIP_DEFLATED) as zf:
|
|
51
|
+
zf.writestr("bootstrap.py", BOOTSTRAP_TEMPLATE.format(wheel_name=wheel.name))
|
|
52
|
+
zf.write(wheel, f"payload/{wheel.name}")
|
|
53
|
+
print(f"Release zip written to: {out_path}")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def main() -> None:
|
|
57
|
+
parser = argparse.ArgumentParser(description="Assemble dcc-mcp-touchdesigner release")
|
|
58
|
+
parser.add_argument("-o", "--output", default=None, help="Output zip path")
|
|
59
|
+
args = parser.parse_args()
|
|
60
|
+
|
|
61
|
+
DIST.mkdir(exist_ok=True)
|
|
62
|
+
wheel = build_wheel()
|
|
63
|
+
out = args.output or str(DIST / "dcc-mcp-touchdesigner-release.zip")
|
|
64
|
+
assemble_zip(Path(out), wheel)
|
|
65
|
+
print("Done.")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if __name__ == "__main__":
|
|
69
|
+
main()
|