BundledCreativeVersion 1.1.0

TouchDesigner MCP Skill: Control TD via Hermes Agent with twozero

Control TouchDesigner via twozero MCP.

Written by Neura Market from the official Hermes Agent documentation for Touchdesigner Mcp. Commands, paths, and version numbers are reproduced from the source unchanged.

Read the official documentation

Twozero MCP for TouchDesigner: Reference Guide

This guide documents the integration between TouchDesigner and an AI agent through the twozero MCP plugin. The plugin exposes 36 native tools that allow AI-driven creation, manipulation, and inspection of TouchDesigner networks. The architecture follows this communication path:

Hermes Agent -> MCP (Streamable HTTP) -> twozero.tox (port 40404) -> TD Python

All MCP communication occurs over localhost only on port 40404. There is no authentication — any local process can send commands. The skill never sends data outside localhost.

When to Use This Integration

  • Building or modifying TouchDesigner projects programmatically from an AI agent.
  • Automating repetitive TD tasks like node creation, parameter setting, and wiring.
  • Creating audio-reactive or generative GLSL shaders with precise control over signal chains.
  • Recording or exporting video from TD without manual interaction.
  • Debugging TD networks by inspecting errors, performance, and operator details.
  • Setting up a new TD environment for AI-assisted creative workflows.

Prerequisites

  • TouchDesigner installed (any edition, but Non-Commercial has a resolution cap).
  • Hermes agent environment set up with HERMES_HOME variable or default ~/.hermes.
  • twozero.tox plugin downloaded (automatically by the setup script or manually from 404zero.com).
  • MCP server configured in Hermes config (automatically by the setup script).
  • Manual steps completed: drag ~/Downloads/twozero.tox into TD, click Install, enable MCP toggle in twozero settings, restart Hermes session.
  • Port 40404 available on localhost.
  • For video recording: ffmpeg installed for frame extraction (optional).
  • For audio-reactive: audio file available.

Setup

Automated Setup

Run the automated setup script:

bash "${HERMES_HOME:-$HOME/.hermes}/skills/creative/touchdesigner-mcp/scripts/setup.sh"

The script checks if TD is running, downloads twozero.tox if not cached, adds the MCP server to Hermes config, tests the MCP connection on port 40404, and reports remaining manual steps.

Manual Steps

  1. Drag ~/Downloads/twozero.tox into the TD network editor and click Install.
  2. Enable MCP: click the twozero icon → Settings → mcp → "auto start MCP" → Yes.
  3. Restart Hermes session to pick up the new MCP server.

Verify Readiness

nc -z 127.0.0.1 40404 && echo "twozero MCP: READY"

The MCP server listens at GET http://localhost:40404/mcp. The tool prefix is twozero_td.

Capabilities Overview

The integration provides tools for:

  • Creating, deleting, and wiring operators (TOP, CHOP, SOP, DAT, MAT, COMP) with auto-positioning.
  • Setting operator parameters safely with validation to prevent crashes.
  • Reading and writing DAT text content, CHOP channel values, and TD console output.
  • Capturing screenshots of operators or the full screen.
  • Navigating the network editor to a specific operator.
  • Searching for operators by name/type across the project.
  • Searching code, expressions, and string parameters.
  • Getting performance profiling (FPS, slow ops).
  • Listing all running TD instances.
  • Getting in-depth documentation on TD topics.
  • Reading/writing per-COMP markdown documentation.
  • Reloading extensions after code edits.
  • Clearing the textport console.
  • Automating mouse and keyboard input to TD.
  • Getting screen coordinates of operators and click points.
  • Converting screenshot pixels to absolute screen coordinates.
  • Executing arbitrary Python in TD with full API access.
  • Getting parameter info for an operator type before setting params.
  • Getting hints/patterns for building specific topics.
  • Getting focus info (current network, selected operator).
  • Getting network structure at a path.
  • Finding errors/warnings recursively.
  • Inspecting one or multiple operators (connections, params, errors).
  • Admin utilities: quit project, test session, dev log, clear dev log.

Tool Reference

Discovery and Inspection Tools

ToolPurpose
td_get_par_infoGet parameter info for an operator type before setting params.
td_get_operator_infoInspect one operator (connections, params, errors).
td_get_operators_infoInspect multiple operators.
td_get_networkGet network structure at a path.
td_get_focusGet focus info (current network, selected operator).
td_get_errorsFind errors/warnings recursively.
td_get_perfGet performance profiling (FPS, slow ops).
td_get_hintsGet hints/patterns for building specific topics.
td_get_docsGet in-depth docs on a TD topic.
td_list_instancesList all running TD instances.
td_find_opFind operators by name/type across project.
td_searchSearch code, expressions, string params.

Creation and Manipulation Tools

ToolPurpose
td_create_operatorCreate operators with auto-positioning.
td_set_operator_parsSet operator parameters safely with validation.
td_execute_pythonExecute arbitrary Python in TD with full API access.
td_reinit_extensionReload extension after code edit.

Data Access Tools

ToolPurpose
td_read_datRead DAT content.
td_write_datWrite DAT content.
td_read_chopRead CHOP channel values.
td_read_textportRead TD console output.
td_clear_textportClear console.
td_agents_mdRead/write per-COMP markdown docs.

Capture and Navigation Tools

ToolPurpose
td_get_screenshotCapture one operator viewer to file.
td_get_screenshotsCapture multiple operator viewers to files.
td_get_screen_screenshotCapture actual screen via TD.
td_navigate_toNavigate network editor to an operator.
td_op_screen_rectGet screen coords of a node.
td_click_screen_pointClick a point in a screenshot.
td_screen_point_to_globalConvert screenshot pixel to absolute screen coords.

Input Automation Tools

ToolPurpose
td_input_executeSend mouse/keyboard to TD.
td_input_statusPoll input queue status.
td_input_clearStop input automation.

Admin Tools

ToolPurpose
td_project_quitQuit project.
td_test_sessionTest session.
td_dev_logDev log.
td_clear_dev_logClear dev log.

Parameters

Common Parameters

ParameterMeaningRequired
op_typeThe type of operator to create or get info for (e.g., noiseTOP, glslTOP, audiofileinCHOP).Yes for td_create_operator and td_get_par_info.
parentThe parent path where the operator will be created (e.g., /project1).Yes for td_create_operator.
nameThe name for the new operator.Yes for td_create_operator.
parametersA dictionary of parameter names and values to set on the operator.Optional for td_create_operator and td_set_operator_pars.
pathThe absolute path to an operator (e.g., /project1/bg).Yes for td_set_operator_pars, td_get_operator_info, td_get_errors, td_get_screenshot, etc.
recursiveWhether to search recursively for errors or operators.Optional for td_get_errors.
detailLevel of detail for operator info (e.g., "full").Optional for td_get_operator_info.
topicThe topic for which to get hints (e.g., "glsl", "audio reactive", "feedback").Yes for td_get_hints.

Audio-Reactive Parameters

ParameterMeaningRequired
timesliceWhether timeslice mode is ON for AudioSpectrum. Must stay ON for audio-reactive.Yes for AudioSpectrum in audio-reactive chains.
outlengthOutput length for AudioSpectrum. Set to 256 manually.Yes for AudioSpectrum in audio-reactive chains.
gainGain value for Math CHOP in audio-reactive chains. Use 10.Yes for Math CHOP in audio-reactive chains.
dataformatData format for CHOP to TOP. Use "r" for audio-reactive.Yes for CHOP to TOP in audio-reactive chains.
layoutLayout for CHOP to TOP. Use "rowscropped" for audio-reactive.Yes for CHOP to TOP in audio-reactive chains.

Recording Parameters

ParameterMeaningRequired
codecVideo codec for recording (e.g., "prores", "mjpa"). H.264/H.265/AV1 require Commercial license.Yes for MovieFileOut setup.
outputresolutionResolution setting for operators. Use "custom" for Non-Commercial TD and set width/height explicitly.Optional, but needed for Non-Commercial TD to avoid 1280x1280 cap.

Procedures

Build a TD Network (General Workflow)

Step 0 - Discover:

Call td_get_par_info with op_type for each type you plan to use.
Call td_get_hints with the topic you're building (e.g. "glsl", "audio reactive", "feedback").
Call td_get_focus to see where the user is and what's selected.
Call td_get_network to see what already exists.

Step 1 - Clean + Build: Split cleanup and creation into separate MCP calls to avoid "Invalid OP object" errors. Use td_create_operator for each node (handles viewport positioning). For bulk creation or wiring, use td_execute_python.

Example creating a simple TOP chain:

td_create_operator(type="noiseTOP", parent="/project1", name="bg", parameters={"resolutionw": 1280, "resolutionh": 720})
td_create_operator(type="levelTOP", parent="/project1", name="brightness")
td_create_operator(type="nullTOP", parent="/project1", name="out")

For bulk creation and wiring:

# td_execute_python script:
root = op('/project1')
nodes = []
for name, optype in [('bg', noiseTOP), ('fx', levelTOP), ('out', nullTOP)]:
    n = root.create(optype, name)
    nodes.append(n.path)
# Wire chain
for i in range(len(nodes)-1):
    op(nodes[i]).outputConnectors[0].connect(op(nodes[i+1]).inputConnectors[0])
result = {'created': nodes}

Step 2 - Set Parameters: Prefer td_set_operator_pars (validates params). For expressions or modes, use td_execute_python.

td_set_operator_pars(path="/project1/bg", parameters={"roughness": 0.6, "monochrome": true})

Setting an expression on a parameter:

op('/project1/time_driver').par.colorr.expr = "absTime.seconds % 1000.0"

Step 3 - Wire: Use td_execute_python (no native wire tool exists).

op('/project1/bg').outputConnectors[0].connect(op('/project1/fx').inputConnectors[0])

Step 4 - Verify:

td_get_errors(path="/project1", recursive=true)
td_get_perf()
td_get_operator_info(path="/project1/out", detail="full")

Step 5 - Display/Capture: Use td_get_screenshot or open a window via script.

td_get_screenshot(path="/project1/out")

Opening a display window:

win = op('/project1').create(windowCOMP, 'display')
win.par.winop = op('/project1/out').path
win.par.winw = 1280; win.par.winh = 720
win.par.winopen.pulse()

Record/Export Video

Create a moviefileoutTOP via td_execute_python:

# via td_execute_python:
root = op('/project1')
rec = root.create(moviefileoutTOP, 'recorder')
op('/project1/out').outputConnectors[0].connect(rec.inputConnectors[0])
rec.par.type = 'movie'
rec.par.file = '/tmp/output.mov'
rec.par.videocodec = 'prores'  # Apple ProRes — NOT license-restricted on macOS
rec.par.record = True   # start
# rec.par.record = False  # stop (call separately later)

To stop, set record parameter to False in a separate call.

Extract frames with ffmpeg:

ffmpeg -i /tmp/output.mov -vframes 120 /tmp/frames/frame_%06d.png

Build Audio-Reactive GLSL (Proven Recipe)

The signal chain:

AudioFileIn CHOP (playmode=sequential)
  → AudioSpectrum CHOP (FFT=512, outputmenu=setmanually, outlength=256, timeslice=ON)
  → Math CHOP (gain=10)
  → CHOP to TOP (dataformat=r, layout=rowscropped)
  → GLSL TOP input 1 (spectrum texture, 256x2)

Constant TOP (rgba32float, time) → GLSL TOP input 0
GLSL TOP → Null TOP → MovieFileOut

In GLSL, sample spectrum at y=0.25 for first channel, use multiple samples per band averaged for stability:

// Input 0 = time (1x1 rgba32float), Input 1 = spectrum (256x2)
float iTime = texture(sTD2DInputs[0], vec2(0.5)).r;

// Sample multiple points per band and average for stability:
// NOTE: y=0.25 for first channel (stereo texture is 256x2, first row center is 0.25)
float bass = (texture(sTD2DInputs[1], vec2(0.02, 0.25)).r +
              texture(sTD2DInputs[1], vec2(0.05, 0.25)).r) / 2.0;
float mid  = (texture(sTD2DInputs[1], vec2(0.2, 0.25)).r +
              texture(sTD2DInputs[1], vec2(0.35, 0.25)).r) / 2.0;
float hi   = (texture(sTD2DInputs[1], vec2(0.6, 0.25)).r +
              texture(sTD2DInputs[1], vec2(0.8, 0.25)).r) / 2.0;

Do NOT use Lag CHOP or Filter CHOP for spectrum smoothing (causes expansion to 2400+ samples and near-zero values). Smooth in GLSL via temporal lerp if needed: mix(prevValue, newValue, 0.3).

GLSL Time Setup

There is no uTDCurrentTime in GLSL TOP. Use the Values page with a parameter named uTime and set expression via script:

# Call td_get_par_info(op_type="glslTOP") first to confirm param names
td_set_operator_pars(path="/project1/shader", parameters={"value0name": "uTime"})
# Then set expression via script:
# op('/project1/shader').par.value0.expr = "absTime.seconds"
# In GLSL: uniform float uTime;

Constraints and Caveats

General Rules

  • NEVER guess parameter names. Always call td_get_par_info for the op type first. Training data is wrong for TD 2025.32.
  • If tdAttributeError fires, STOP and call td_get_operator_info on the failing node before continuing.
  • NEVER hardcode absolute paths in script callbacks. Use me.parent() / scriptOp.parent().
  • Prefer native MCP tools over td_execute_python. Use td_create_operator, td_set_operator_pars, td_get_errors etc. Only fall back to td_execute_python for complex multi-step logic.
  • Call td_get_hints before building. It returns patterns specific to the op type.

Resolution and Codec Constraints

  • Non-Commercial TD caps resolution at 1280x1280. Use outputresolution = 'custom' and set width/height explicitly.
  • H.264/H.265/AV1 codecs require a Commercial license. Use prores (preferred on macOS) or mjpa as fallback.

Node Management

  • Split cleanup and creation into SEPARATE MCP calls. Destroying and recreating same-named nodes in one td_execute_python script causes "Invalid OP object" errors.
  • Cleaning nodes: Always list(root.children) before iterating and check child.valid.
  • TOP.save() is useless for animation — captures same GPU texture every time. Always use MovieFileOut.

Feedback TOP

  • Use top parameter reference, not direct input wire. "Not enough sources" resolves after first cook. "Cook dependency loop" warning is expected.

Vertex/Point Access

  • In TD 2025.32, use point.P[0], point.P[1], point.P[2] — NOT .x, .y, .z.

Extensions

  • ext0object format is "op('./datName').module.ClassName(me)" in CONSTANT mode.
  • After editing extension code with td_write_dat, call td_reinit_extension.

Recording

  • Before recording: Verify FPS > 0 via td_get_perf. If FPS=0 the recording will be empty.
  • Before recording: Verify shader output is not black via td_get_screenshot. Black output = shader error or missing input.
  • If recording with audio: cue audio to start first, then delay recording by 3 frames.
  • Set output path before starting record — setting both in the same script can race.

Audio-Reactive Specifics

  • TimeSlice must stay ON for AudioSpectrum. OFF = processes entire audio file → 24000+ samples → CHOP to TOP overflow.
  • Set Output Length manually to 256 via outputmenu='setmanually' and outlength=256. Default outputs 22050 samples.
  • DO NOT use Lag CHOP for spectrum smoothing. Lag CHOP operates in timeslice mode and expands 256 samples to 2400+, averaging all values to near-zero (~1e-06).
  • DO NOT use Filter CHOP for spectrum smoothing — same timeslice expansion problem.
  • Smoothing belongs in the GLSL shader if needed, via temporal lerp with a feedback texture: mix(prevValue, newValue, 0.3).
  • CHOP to TOP dataformat = 'r', layout = 'rowscropped'. Spectrum output is 256x2 (stereo). Sample at y=0.25 for first channel.
  • Math gain = 10 (not 5). Raw spectrum values are ~0.19 in bass range. Gain of 10 gives usable ~5.0 for the shader.
  • No Resample CHOP needed. Control output size via AudioSpectrum's outlength param directly.

Security

  • MCP runs on localhost only (port 40404). No authentication — any local process can send commands.
  • td_execute_python has unrestricted access to the TD Python environment and filesystem as the TD process user.
  • The skill never sends data outside localhost. All MCP communication is local.

Failure Modes

FailureCauseResolution
tdAttributeErrorParameter name is wrong or operator doesn't exist.STOP and call td_get_operator_info on the failing node.
Invalid OP object errorDestroying and recreating same-named nodes in one td_execute_python script.Split cleanup and creation into separate MCP calls.
Empty recordingFPS=0.Verify FPS > 0 via td_get_perf before recording.
Black outputShader error or missing input.Verify via td_get_screenshot.
Audio-reactive shader receives no usable dataUsing Lag CHOP or Filter CHOP for spectrum smoothing.Smooth in GLSL shader instead.
CHOP to TOP overflowTimeSlice OFF on AudioSpectrum.Keep TimeSlice ON.
AudioSpectrum outputs 22050 samplesNot setting outputmenu='setmanually' and outlength=256.Set both parameters explicitly.
Shader freezesUsing Constant TOP in non-rgba32float format.Use rgba32float format.
Recording race conditionSetting output path and starting record in the same script.Set path first, start record in separate call.
Connection failureMCP not running on port 40404.Verify with nc -z 127.0.0.1 40404.
Extension not reloadedAfter editing extension code with td_write_dat, forgot to call td_reinit_extension.Always call td_reinit_extension after edits.

Reference Documents

The following reference documents are available in the Hermes skills directory:

  • references/mcp-tools.md - Complete tool reference
  • references/network-patterns.md - Network building patterns
  • references/pitfalls.md - Common pitfalls
  • references/operators.md - Operator reference
  • references/python-api.md - Python API reference
  • references/troubleshooting.md - Troubleshooting guide
  • references/glsl.md - GLSL shader reference
  • references/postfx.md - Post-processing effects
  • references/layout-compositor.md - Layout and compositing
  • references/operator-tips.md - Operator tips
  • references/geometry-comp.md - Geometry COMP reference
  • references/audio-reactive.md - Audio-reactive techniques
  • references/animation.md - Animation patterns
  • references/midi-osc.md - MIDI and OSC
  • references/particles.md - Particle systems
  • references/projection-mapping.md - Projection mapping
  • references/external-data.md - External data integration
  • references/panel-ui.md - Panel UI components
  • references/replicator.md - Replicator COMP
  • references/dat-scripting.md - DAT scripting
  • references/3d-scene.md - 3D scene setup
  • scripts/setup.sh - Setup script

Writing GLSL to File

Write to /tmp/file.glsl, then use td_write_dat or td_execute_python to load into a GLSL TOP.

Skills the docs pair this with

More Creative skills