mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
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:
parent
c49a58a6d0
commit
7a5371b20d
9 changed files with 3277 additions and 0 deletions
501
skills/creative/touchdesigner/references/mcp-tools.md
Normal file
501
skills/creative/touchdesigner/references/mcp-tools.md
Normal 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue