feat: add TouchDesigner integration skill

New skill: creative/touchdesigner — control a running TouchDesigner
instance via REST API. Build real-time visual networks programmatically.

Architecture:
  Hermes Agent -> HTTP REST (curl) -> TD WebServer DAT -> TD Python env

Key features:
- Custom API handler (scripts/custom_api_handler.py) that creates a
  self-contained WebServer DAT + callback in TD. More reliable than the
  official mcp_webserver_base.tox which frequently fails module imports.
- Discovery-first workflow: never hardcode TD parameter names. Always
  probe the running instance first since names change across versions.
- Persistent setup: save the TD project once with the API handler baked
  in. TD auto-opens the last project on launch, so port 9981 is live
  with zero manual steps after first-time setup.
- Works via curl in execute_code (no MCP dependency required).
- Optional MCP server config for touchdesigner-mcp-server npm package.

Skill structure (2823 lines total):
  SKILL.md (209 lines) — setup, workflow, key rules, operator reference
  references/pitfalls.md (276 lines) — 24 hard-won lessons
  references/operators.md (239 lines) — all 6 operator families
  references/network-patterns.md (589 lines) — audio-reactive, generative,
    video processing, GLSL, instancing, live performance recipes
  references/mcp-tools.md (501 lines) — 13 MCP tool schemas
  references/python-api.md (443 lines) — TD Python scripting patterns
  references/troubleshooting.md (274 lines) — connection diagnostics
  scripts/custom_api_handler.py (140 lines) — REST API handler for TD
  scripts/setup.sh (152 lines) — prerequisite checker

Tested on TouchDesigner 099 Non-Commercial (macOS/darwin).
This commit is contained in:
kshitijk4poor 2026-04-15 10:33:15 +05:30 committed by Teknium
parent c49a58a6d0
commit 7a5371b20d
9 changed files with 3277 additions and 0 deletions

View file

@ -0,0 +1,501 @@
# TouchDesigner MCP Tools Reference
Complete parameter schemas and usage examples for all 13 MCP tools from the 8beeeaaat/touchdesigner-mcp server.
## Hermes Configuration
Add a `touchdesigner` entry under the `mcp_servers` section of your Hermes config. Example YAML block:
```yaml
# Under mcp_servers: in config.yaml
mcp_servers:
touchdesigner:
command: npx
args: ["-y", "touchdesigner-mcp-server@latest"]
env:
TD_API_URL: "http://127.0.0.1:9981"
timeout: 120
connect_timeout: 60
```
For a locally built server, point `command` to `node` and `args` to the built server index.js path. Set `TD_API_URL` to the TouchDesigner WebServer DAT address (default port 9981).
For the documentation/knowledge server (no running TD needed), add a `td_docs` entry using `touchdesigner-mcp-server` as the npx package.
Tools are registered as `mcp_touchdesigner_<tool_name>` in Hermes.
**If MCP tools are not available as direct function calls** (common when the MCP server connects but Hermes doesn't expose them as callable tools), use the custom API handler directly via `curl` in `execute_code` or `terminal`:
```python
import json, shlex
from hermes_tools import terminal
def td_exec(script):
"""Execute Python in TouchDesigner via the REST API."""
escaped = json.dumps({"script": script})
cmd = f"curl -s --max-time 15 -X POST -H 'Content-Type: application/json' -d {shlex.quote(escaped)} 'http://127.0.0.1:9981/api/td/server/exec'"
r = terminal(cmd, timeout=20)
return json.loads(r['output'])
# Example: list all nodes
result = td_exec('result = [c.name for c in op("/project1").children]')
print(result) # {"result": ["node1", "node2", ...], "stdout": "", "stderr": ""}
```
This `td_exec` helper works with both the official .tox handler and the custom API handler from `scripts/custom_api_handler.py`.
Tools are registered as `mcp_touchdesigner_<tool_name>` in Hermes.
## Common Formatting Parameters
Most tools accept these optional formatting parameters:
| Parameter | Type | Values | Description |
|-----------|------|--------|-------------|
| `detailLevel` | string | `"minimal"`, `"summary"`, `"detailed"` | Response verbosity |
| `responseFormat` | string | `"json"`, `"yaml"`, `"markdown"` | Output format |
| `limit` | integer | 1-500 | Max items (on list-type tools only) |
These are client-side formatting — they control how the MCP server formats the response text, not what data TD returns.
---
## Tool 1: describe_td_tools
**Purpose:** Meta-tool — lists all available TouchDesigner MCP tools with descriptions and parameters.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `filter` | string | No | Keyword to filter tools by name, description, or parameter |
| `detailLevel` | string | No | Response verbosity |
| `responseFormat` | string | No | Output format |
**Example:** Find tools related to node creation
```
describe_td_tools(filter="create")
```
**Note:** This tool runs entirely in the MCP server — it does NOT contact TouchDesigner. Use it to discover what's available.
---
## Tool 2: get_td_info
**Purpose:** Get TouchDesigner server information (version, OS, build).
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `detailLevel` | string | No | Response verbosity |
| `responseFormat` | string | No | Output format |
**Example:** Check TD is running and get version
```
get_td_info()
```
**Returns:** TD version, build number, OS name/version, MCP API version.
**Use this first** to verify the connection is working before building networks.
---
## Tool 3: execute_python_script
**Purpose:** Execute arbitrary Python code inside TouchDesigner's Python environment.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `script` | string | **Yes** | Python code to execute |
| `detailLevel` | string | No | Response verbosity |
| `responseFormat` | string | No | Output format |
**Available globals in the script:**
- `op` — find operators by path
- `ops` — find multiple operators by pattern
- `me` — the WebServer DAT running the script
- `parent` — me.parent()
- `project` — root project component
- `td` — the full td module
- `result` — set this to explicitly return a value
**Execution behavior:**
- Single-line scripts: tries `eval()` first (returns value), falls back to `exec()`
- Multi-line scripts: uses `exec()` always
- stdout/stderr are captured and returned separately
- If `result` is not set, tries to evaluate the last expression as the return value
**Examples:**
```python
# Simple query
execute_python_script(script="op('/project1/noise1').par.seed.val")
# Returns: {"result": 42, "stdout": "", "stderr": ""}
# Multi-line script
execute_python_script(script="""
nodes = op('/project1').findChildren(type=TOP)
result = [{'name': n.name, 'type': n.OPType} for n in nodes]
""")
# Connect two operators
execute_python_script(script="op('/project1/noise1').outputConnectors[0].connect(op('/project1/level1'))")
# Create and configure in one script
execute_python_script(script="""
parent = op('/project1')
n = parent.create(noiseTop, 'my_noise')
n.par.seed.val = 42
n.par.monochrome.val = True
n.par.resolutionw.val = 1920
n.par.resolutionh.val = 1080
result = {'path': n.path, 'type': n.OPType}
""")
# Batch wire a chain
execute_python_script(script="""
chain = ['noise1', 'level1', 'blur1', 'composite1', 'null_out']
for i in range(len(chain) - 1):
src = op(f'/project1/{chain[i]}')
dst = op(f'/project1/{chain[i+1]}')
if src and dst:
src.outputConnectors[0].connect(dst)
result = 'Wired chain: ' + ' -> '.join(chain)
""")
```
**When to use:** Wiring connections, complex logic, batch operations, querying state that other tools don't cover. This is the most powerful and flexible tool.
---
## Tool 4: create_td_node
**Purpose:** Create a new operator in TouchDesigner.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `parentPath` | string | **Yes** | Path to parent (e.g., `/project1`) |
| `nodeType` | string | **Yes** | Operator type (e.g., `noiseTop`, `mathChop`) |
| `nodeName` | string | No | Custom name (auto-generated if omitted) |
| `detailLevel` | string | No | Response verbosity |
| `responseFormat` | string | No | Output format |
**Examples:**
```
create_td_node(parentPath="/project1", nodeType="noiseTop", nodeName="bg_noise")
create_td_node(parentPath="/project1", nodeType="compositeTop") # auto-named
create_td_node(parentPath="/project1/audio_chain", nodeType="audiospectrumChop", nodeName="spectrum")
```
**Returns:** Node summary with id, name, path, opType, and all default parameter values.
**Node type naming convention:** camelCase family suffix — `noiseTop`, `mathChop`, `gridSop`, `tableDat`, `phongMat`, `geometryComp`. See `references/operators.md` for the full list.
---
## Tool 5: delete_td_node
**Purpose:** Delete an existing operator.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `nodePath` | string | **Yes** | Absolute path to node (e.g., `/project1/noise1`) |
| `detailLevel` | string | No | Response verbosity |
| `responseFormat` | string | No | Output format |
**Example:**
```
delete_td_node(nodePath="/project1/noise1")
```
**Returns:** Confirmation with the deleted node's summary (captured before deletion).
---
## Tool 6: get_td_nodes
**Purpose:** List operators under a path with optional filtering.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `parentPath` | string | **Yes** | Parent path (e.g., `/project1`) |
| `pattern` | string | No | Glob pattern for name filtering (default: `*`) |
| `includeProperties` | boolean | No | Include full parameter values (default: false) |
| `detailLevel` | string | No | Response verbosity |
| `responseFormat` | string | No | Output format |
| `limit` | integer | No | Max items (1-500) |
**Examples:**
```
# List all direct children of /project1
get_td_nodes(parentPath="/project1")
# Find all noise operators
get_td_nodes(parentPath="/project1", pattern="noise*")
# Get full parameter details
get_td_nodes(parentPath="/project1", pattern="*", includeProperties=true, limit=20)
```
**Returns:** List of node summaries. With `includeProperties=false` (default): id, name, path, opType only. With `includeProperties=true`: full parameter values included.
---
## Tool 7: get_td_node_parameters
**Purpose:** Get detailed parameters of a specific node.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `nodePath` | string | **Yes** | Node path (e.g., `/project1/noise1`) |
| `detailLevel` | string | No | Response verbosity |
| `responseFormat` | string | No | Output format |
| `limit` | integer | No | Max parameters (1-500) |
**Example:**
```
get_td_node_parameters(nodePath="/project1/noise1")
```
**Returns:** All parameter name-value pairs for the node. Use this to discover available parameters before calling update_td_node_parameters.
---
## Tool 8: get_td_node_errors
**Purpose:** Check for errors on a node and all its descendants (recursive).
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `nodePath` | string | **Yes** | Absolute path to inspect (e.g., `/project1`) |
| `detailLevel` | string | No | Response verbosity |
| `responseFormat` | string | No | Output format |
| `limit` | integer | No | Max error items (1-500) |
**Examples:**
```
# Check entire project for errors
get_td_node_errors(nodePath="/project1")
# Check a specific chain
get_td_node_errors(nodePath="/project1/audio_chain")
```
**Returns:** Error count, hasErrors boolean, and list of errors each with nodePath, nodeName, opType, and error message.
**Always call this after building a network** to catch wiring mistakes, missing references, and configuration errors.
---
## Tool 9: update_td_node_parameters
**Purpose:** Update parameters on an existing node.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `nodePath` | string | **Yes** | Path to node (e.g., `/project1/noise1`) |
| `properties` | object | **Yes** | Key-value pairs to update (e.g., `{"seed": 42, "monochrome": true}`) |
| `detailLevel` | string | No | Response verbosity |
| `responseFormat` | string | No | Output format |
**Examples:**
```
# Set noise parameters
update_td_node_parameters(
nodePath="/project1/noise1",
properties={"seed": 42, "monochrome": false, "period": 4.0, "harmonics": 3,
"resolutionw": 1920, "resolutionh": 1080}
)
# Set a file path
update_td_node_parameters(
nodePath="/project1/moviefilein1",
properties={"file": "/Users/me/Videos/clip.mp4", "play": true}
)
# Set compositing mode
update_td_node_parameters(
nodePath="/project1/composite1",
properties={"operand": 0} # 0=Over, 1=Under, 3=Add, 18=Multiply, 27=Screen
)
```
**Returns:** List of successfully updated properties and any that failed (with reasons). Raises error if zero properties were updated.
**Parameter value types:** Floats, ints, booleans, and strings are all accepted. For menu parameters, use either the string label or the integer index.
---
## Tool 10: exec_node_method
**Purpose:** Call a Python method directly on a specific node.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `nodePath` | string | **Yes** | Path to node |
| `method` | string | **Yes** | Method name to call |
| `args` | array | No | Positional arguments (strings, numbers, booleans) |
| `kwargs` | object | No | Keyword arguments |
| `detailLevel` | string | No | Response verbosity |
| `responseFormat` | string | No | Output format |
**Examples:**
```
# Get all children of a component
exec_node_method(nodePath="/project1", method="findChildren")
# Find specific children
exec_node_method(nodePath="/project1", method="findChildren",
kwargs={"name": "noise*", "depth": 1})
# Get node errors
exec_node_method(nodePath="/project1/noise1", method="errors")
# Get node warnings
exec_node_method(nodePath="/project1/noise1", method="warnings")
# Save a component as .tox
exec_node_method(nodePath="/project1/myContainer", method="save",
args=["/path/to/component.tox"])
```
**Returns:** Processed return value of the method call. TD operators are serialized to their path strings, iterables to lists, etc.
---
## Tool 11: get_td_classes
**Purpose:** List available TouchDesigner Python classes and modules.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `detailLevel` | string | No | Response verbosity |
| `responseFormat` | string | No | Output format |
| `limit` | integer | No | Max items (default: 50) |
**Example:**
```
get_td_classes(limit=100)
```
**Returns:** List of class/module names and their docstrings from the td module. Useful for discovering what's available in TD's Python environment.
---
## Tool 12: get_td_class_details
**Purpose:** Get methods and properties of a specific TD Python class.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `className` | string | **Yes** | Class name (e.g., `noiseTop`, `OP`, `COMP`) |
| `detailLevel` | string | No | Response verbosity |
| `responseFormat` | string | No | Output format |
| `limit` | integer | No | Max methods/properties (default: 30) |
**Examples:**
```
# Inspect the noiseTop class
get_td_class_details(className="noiseTop")
# Inspect the base OP class (all operators inherit from this)
get_td_class_details(className="OP", limit=50)
# Inspect COMP (component) class
get_td_class_details(className="COMP")
```
**Returns:** Class name, type, description, methods (name + description + type), and properties (name + description + type).
---
## Tool 13: get_td_module_help
**Purpose:** Retrieve Python help() text for any TD module, class, or function.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `moduleName` | string | **Yes** | Module/class name (e.g., `noiseCHOP`, `tdu`, `td.OP`) |
| `detailLevel` | string | No | Response verbosity |
| `responseFormat` | string | No | Output format |
**Examples:**
```
# Get help for the noise CHOP class
get_td_module_help(moduleName="noiseCHOP")
# Get help for the tdu utilities module
get_td_module_help(moduleName="tdu")
# Dotted name resolution works
get_td_module_help(moduleName="td.OP")
```
**Returns:** Full Python help() text output, cleaned of backspace characters.
---
## Workflow: Building a Complete Network
Typical sequence of tool calls to build a project:
1. `get_td_info` — verify connection
2. `get_td_nodes(parentPath="/project1")` — see what already exists
3. `create_td_node` (multiple) — create all operators
4. `update_td_node_parameters` (multiple) — configure each operator
5. `execute_python_script` — wire all connections in one batch script
6. `get_td_node_errors(nodePath="/project1")` — check for problems
7. `get_td_node_parameters` — verify specific nodes if needed
8. Iterate: adjust parameters, add operators, fix errors
## TD Documentation MCP Server Tools
The bottobot/touchdesigner-mcp-server provides 21 reference/knowledge tools (no running TD needed):
| Tool | Purpose |
|------|---------|
| `get_operator` | Get full documentation for a specific operator |
| `search_operators` | Search operators by keyword |
| `list_operators` | List all operators (filterable by family) |
| `compare_operators` | Compare two operators side by side |
| `get_operator_examples` | Get usage examples for an operator |
| `suggest_workflow` | Get workflow suggestions for a task |
| `get_tutorial` | Get a full TD tutorial |
| `list_tutorials` | List available tutorials |
| `search_tutorials` | Search tutorial content |
| `get_python_api` | Get Python API class documentation |
| `search_python_api` | Search Python API |
| `list_python_classes` | List all documented Python classes |
| `get_version_info` | Get TD version release notes |
| `list_versions` | List all documented TD versions |
| `get_experimental_techniques` | Get advanced technique guides (GLSL, ML, generative, etc.) |
| `search_experimental` | Search experimental techniques |
| `get_glsl_pattern` | Get GLSL code patterns (SDF, color, math utilities) |
| `get_operator_connections` | Get common operator wiring patterns |
| `get_network_template` | Get complete network templates with Python generation scripts |
| `get_experimental_build` | Get experimental build info |
| `list_experimental_builds` | List experimental builds |
This server contains 630 operator docs, 14 tutorials, 69 Python API classes, and 7 experimental technique categories with working code.