touchdesigner-mcp — TouchDesigner MCP Server
Control TouchDesigner from AI assistants — Claude Code, Claude Desktop, GitHub Copilot, Cursor, or any Model Context Protocol client.
MCP client (Claude, Copilot, …) ←— stdio/MCP —→ touchdesigner-mcp ←— HTTP :9980 —→ TouchDesigner (Web Server DAT)
19 tools: create/connect/inspect nodes, get/set parameters, build whole networks in one call, export networks as JSON, run Python inside TD, save the project, and more.
Quick start
You need two things running: the bridge inside TouchDesigner and the MCP server config in your AI client. Python 3.10+ and uv are the only prerequisites (or plain pip if you prefer).
1. TouchDesigner side — install the bridge
Option A — paste the callbacks script:
- Print the bridge script and copy it:
uvx touchdesigner-mcp bridge
(or copytouchdesigner_mcp/bridge_script.pyfrom this repo / the latest release) - In TouchDesigner: right-click in the network editor →
Add Operator→DAT→Web Server - In the Web Server DAT parameters: set Port to
9980, toggle Active ON - Click the arrow icon on the Web Server DAT to open its callbacks DAT, and replace its entire contents with the copied script
Option B — drag & drop: download touchdesigner-mcp-bridge.tox from the latest release and drag it into your network.
Your .toe file can be saved anywhere — the bridge is fully self-contained.
2. Client side — add the MCP server
Claude Code:
claude mcp add touchdesigner -- uvx touchdesigner-mcp
Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"touchdesigner": {
"command": "uvx",
"args": ["touchdesigner-mcp"]
}
}
}
VS Code / GitHub Copilot (.vscode/mcp.json in your workspace):
{
"servers": {
"touchdesigner": {
"type": "stdio",
"command": "uvx",
"args": ["touchdesigner-mcp"]
}
}
}
Cursor (.cursor/mcp.json):
{
"mcpServers": {
"touchdesigner": {
"command": "uvx",
"args": ["touchdesigner-mcp"]
}
}
}
That's it. Open your assistant and try: "Create a noise TOP called myNoise in /project1".
Configuration
By default touchdesigner-mcp talks to http://127.0.0.1:9980. Override via environment variables or CLI flags:
| Setting | Default | Description |
|---|---|---|
TD_URL |
— | Full endpoint URL (overrides host/port) |
TD_HOST |
127.0.0.1 |
TouchDesigner host |
TD_PORT |
9980 |
Web Server DAT port |
--url, --host, --port |
— | CLI equivalents (take precedence over env vars) |
Example — TD on a non-default port, via the client config:
{
"command": "uvx",
"args": ["touchdesigner-mcp", "--port", "9981"]
}
Multiple TouchDesigner instances: add one MCP server entry per instance, each with a different name and port (each TD project needs its own Web Server DAT on a distinct port).
Usage examples
Prompts that work well in agent mode:
- "Create a noise TOP called myNoise in /project1"
- "What nodes are in /project1?"
- "Set the seed parameter of /project1/myNoise to 42"
- "Connect myNoise to null1"
- "Find all TOP nodes in the project"
- "Show me the info and parameters of /project1/myNoise"
- "Create a network with a noiseTOP, levelTOP, and nullTOP connected in series"
- "Export the network in /project1 as JSON"
- "Are there any errors in the project?"
- "Copy noise1 and call it noise_backup"
- "Create a constant TOP called bg, then set its color to red (colorr=1, colorg=0, colorb=0)"
- "Save the project"
Available MCP tools
| Tool | Parameters | Description |
|---|---|---|
create_node |
node_type, node_name, parent_path |
Create an operator node |
delete_node |
node_path |
Delete a node |
rename_node |
node_path, new_name |
Rename a node |
copy_node |
node_path, destination_path, new_name |
Duplicate a node |
get_node_info |
node_path |
Full node details (type, connections, position) |
list_nodes |
parent_path |
List children of a container |
get_parameter |
node_path, param_name |
Read a parameter value |
set_parameter |
node_path, param_name, value |
Set a parameter on a node |
list_parameters |
node_path, filter_pattern |
List all params with values/types/ranges |
connect_nodes |
source_path, target_path, input_index, output_index |
Wire output→input |
disconnect_nodes |
target_path, input_index |
Remove a connection |
create_network |
nodes, connections, parent_path |
Batch create nodes + connections in one call |
export_network |
parent_path, recursive |
Serialize a subnetwork to JSON |
search_nodes |
pattern, parent_path, family, recursive |
Find nodes by name/type pattern |
set_node_position |
node_path, x, y |
Position a node in the network |
save_project |
file_path (optional) |
Save the .toe file |
get_project_info |
(none) | Project metadata (name, folder, save version, cook rate, real-time flag, bridge version) |
get_errors |
parent_path, recursive, include_warnings |
List nodes with errors/warnings |
execute_script |
script, parent_path |
Run arbitrary Python inside TD |
Common TouchDesigner node types
| Human Name | TD Type | Family |
|---|---|---|
| Noise | noiseTOP |
TOP |
| Constant | constantTOP |
TOP |
| Circle | circletopTOP |
TOP |
| Rectangle | rectangletopTOP |
TOP |
| Text | textTOP |
TOP |
| Composite | compositeTOP |
TOP |
| Null | nullTOP |
TOP |
| Wave | waveCHOP |
CHOP |
| Constant | constantCHOP |
CHOP |
| LFO | lfoCHOP |
CHOP |
| Noise | noiseCHOP |
CHOP |
| Text | textDAT |
DAT |
| Table | tableDAT |
DAT |
| Circle | circleSOP |
SOP |
| Box | boxSOP |
SOP |
| Geometry | geometryCOMP |
COMP |
| Container | containerCOMP |
COMP |
A few non-obvious TD behaviors when building networks programmatically:
- The Geometry component's create-type is
geometryCOMP(notgeoCOMP), and it spawns with a default torus SOP inside. - A CHOP to TOP takes its source CHOP via its
chopparameter, not an input wire — connecting to input 0 fails. - A Feedback TOP shows an error until an input is wired to initialize its buffer.
Security note
The bridge executes commands sent to the Web Server DAT without authentication — including arbitrary Python via execute_script. It binds to your machine's local network interface. Keep the port firewalled from untrusted networks, and don't expose it to the internet.
Troubleshooting
"Cannot connect to TouchDesigner at …"
- Is TouchDesigner running?
- Is the Web Server DAT Active (toggle it on)?
- Does the DAT's port match your configured
TD_PORT(default9980)? - Try opening
http://localhost:9980in your browser — you should get a response.
"Failed to start server" on the Web Server DAT (Windows)
If the DAT refuses to start even though nothing else uses the port, Windows may have reserved the port range (Hyper-V/WSL/Docker do this, and the ranges move around after reboots). Check with:
netsh interface ipv4 show excludedportrange protocol=tcp
If 9980 falls inside one of the listed ranges, pick a port outside all of them, set it on the Web Server DAT, and point the MCP server at it (e.g. "args": ["touchdesigner-mcp", "--port", "9090"] or TD_PORT=9090).
"Unknown action" error
- Make sure you pasted the entire bridge script into the callbacks DAT.
- Your bridge may be older than your touchdesigner-mcp version — re-paste the output of
uvx touchdesigner-mcp bridge.
"TouchDesigner bridge is vX.Y.Z but this server is …" warning
- The bridge installed in your TD project doesn't match your touchdesigner-mcp version. Re-paste the output of
uvx touchdesigner-mcp bridgeinto the callbacks DAT (or update the.toxfrom the matching release). Everything keeps working in the meantime — but tools added since the bridge was installed will fail with "Unknown action".
"Node not found"
- Check the path with
list_nodesfirst. - Paths are case-sensitive and start with
/.
"Parameter not found"
- The error message lists all available parameters — check the exact name.
- Use TD's parameter dialog to find the programmatic name (hover over a parameter label).
Development
git clone https://github.com/quentinR2/TouchDesignerMcp
cd TouchDesignerMcp
uv venv && uv pip install -e .[dev] # or: python -m venv .venv && pip install -e .[dev]
pytest # smoke tests, no TouchDesigner needed
Project layout:
touchdesigner_mcp/ # MCP server package (runs outside TD)
│ ├── __main__.py # CLI entry point (touchdesigner-mcp / python -m touchdesigner_mcp)
│ ├── config.py # Endpoint resolution (env vars / flags)
│ ├── client.py # send_to_td() async HTTP helper
│ ├── bridge_script.py # GENERATED single-file TD bridge — do not edit
│ └── tools/ # One module per tool group, registered via @mcp.tool()
├── td_bridge/ # TD-side source of truth (modular, dev only, not shipped)
│ ├── router.py # Dispatch table + handle_request()
│ └── handlers/ # One module per tool group
├── scripts/build_bridge.py # Merges td_bridge/ → touchdesigner_mcp/bridge_script.py
└── tests/ # MCP-layer smoke tests
Editing the bridge: change files under td_bridge/, then regenerate:
python scripts/build_bridge.py
CI fails if touchdesigner_mcp/bridge_script.py is stale. To test bridge changes in TD, re-paste the regenerated script into the callbacks DAT.
Releasing: bump version in pyproject.toml, tag vX.Y.Z, push the tag. GitHub Actions builds, publishes to PyPI (Trusted Publishing), and creates a GitHub release with the bridge script attached. Build the .tox in TouchDesigner and upload it to the release manually when the bridge changed.
License
Release files for touchdesigner-mcp 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| touchdesigner_mcp-0.2.0.tar.gz | 21.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| touchdesigner_mcp-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 41.5 kB
Release files / touchdesigner_mcp-0.2.0.tar.gz
| Download URL | touchdesigner_mcp-0.2.0.tar.gz |
|---|---|
| Size | 21.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0edb5403079d81cdae1ec83e35b555b1781005fe80930b1cfff2fee3ba9f8942
|
|
BLAKE2b-256 checksum How to use checksums |
4d81baef8cc0c25bb628df6d5acc95df905010f30a280f3d84e286e9d3adef05
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.
Transparency logRelease files / touchdesigner_mcp-0.2.0-py3-none-any.whl
| Download URL | touchdesigner_mcp-0.2.0-py3-none-any.whl |
|---|---|
| Size | 19.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
53541b8d522ddab87a4a365a0f9a65dade209d3f940c12f76af73935561fceea
|
|
BLAKE2b-256 checksum How to use checksums |
fa1cf81e0aaaf36010224e4bb7d360068337b55b7c4fad904366de2cb50f6cee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.
Transparency log