| name | multi-instance |
| description | Workflow for running multiple TD instances with Envoy, switching between them, and understanding the instance registry. |
Multi-Instance Workflow
Envoy supports multiple TouchDesigner instances running simultaneously in the same git repo. Each instance gets its own port, and the bridge can switch between them on demand.
Architecture
Claude Code <--> STDIO Bridge <--> Envoy (TD instance A, port 9870)
|
+---switch---> Envoy (TD instance B, port 9871)
- One bridge process per git root (spawned by Claude Code's MCP client)
- One active connection at a time — the bridge talks to whichever instance is marked
active
- Switching is instant —
switch_instance redirects the bridge's HTTP target in-memory
Instance Registry (.embody/envoy.json)
Each Envoy instance registers itself in .embody/envoy.json at the git root on startup:
{
"active": "Embody-5.257",
"td_executable": "/Applications/TouchDesigner.app",
"instances": {
"Embody-5.257": {
"toe_path": "dev/Embody-5.257.toe",
"port": 9870,
"td_pid": 12345
},
"MySecondProject": {
"toe_path": "MySecondProject.toe",
"port": 9871,
"td_pid": 67890
}
}
}
| Field | Purpose |
|---|
active | Instance the bridge currently targets |
td_executable | Path to TD app (used by launch_td) |
instances.<name>.toe_path | Relative path to the .toe file |
instances.<name>.port | Envoy HTTP port for this instance |
instances.<name>.td_pid | OS process ID of the TD process |
Instances are added on Envoy startup and removed on graceful shutdown (onDestroyTD).
Port Allocation
Each instance picks a port from a 10-port range starting at the configured Envoyport parameter (default: 9870):
- Try the base port first
- If occupied by this TD process's old server, force-close it and reclaim
- If occupied by another process, scan
base+1 through base+9
- First available port wins; if all 10 are taken, startup fails
This means up to 10 simultaneous instances per base port.
Workflow: Running Multiple Instances
1. Open the first .toe file
Launch TD normally or via launch_td. Envoy starts on its configured port and registers in .embody/envoy.json.
2. Open additional .toe files
Open them in separate TD instances (File > Open or double-click). Each Embody/Envoy auto-starts and claims the next available port.
3. List instances
Call switch_instance with no parameters to see all registered instances and their status:
switch_instance()
Returns each instance's name, port, PID, reachability (PID alive + port responding), and whether it's the active target.
4. Switch to a different instance
switch_instance(instance="MySecondProject")
The bridge immediately redirects to the target instance's port. All subsequent MCP calls go to that TD process.
5. Switch back
switch_instance(instance="Embody-5.257")
Reachability Checks
An instance is reachable only when:
- Its registered PID is alive (
kill -0 / process table check)
- Its registered port responds to a TCP connect
Both conditions must be true. A dead PID with an open port means another instance reused that port — the entry is stale.
get_td_status includes the full instance registry with reachability in its response.
Stale Instance Cleanup
Instances are deregistered on graceful TD shutdown. If TD crashes:
- The PID becomes dead, so the instance shows as unreachable
- The port may be freed, allowing a new instance to claim it
- Stale entries remain in
.embody/envoy.json but are filtered by reachability checks
- Re-launching TD with the same
.toe overwrites the stale entry
Closing Instances
Preferred: Close TD instances via Envoy by calling execute_python with project.quit(). This prompts the user to save unsaved changes, then triggers onDestroyTD for clean deregistration and port release. Works reliably across platforms.
To close a specific instance, switch_instance to it first, then send the quit command:
switch_instance(instance="MySecondProject")
execute_python(code="project.quit()")
Never use project.quit(force=True) unless the user has explicitly asked — it skips the save dialog and risks losing unsaved work.
Avoid: osascript -e 'quit app "TouchDesigner"' and similar OS-level approaches are unreliable — they may not target the correct instance and don't guarantee clean Envoy shutdown.
Common Scenarios
| Scenario | Action |
|---|
Test code in two .toe files side by side | Open both, switch_instance between them |
Test code in a secondary .toe | Open it, switch to it, perform work, switch back |
| Check if a second TD is still running | switch_instance() (list mode) or get_td_status |
| One instance crashed, want the other | switch_instance to the surviving instance |
| Close a specific instance | switch_instance to it, then execute_python with project.quit() |
Same-Project Instances
When you open the same .toe file in multiple TD instances, Envoy auto-suffixes the registry key to avoid collisions. The first instance registers as MyProject, the second as MyProject-2, etc. Stale entries (dead PIDs) are automatically reclaimed.
Limitations
- The bridge connects to one instance at a time — no parallel MCP calls to multiple instances
- Maximum 10 instances per base port range
.embody/envoy.json is per git root — instances in different repos have separate registries
launch_td always launches the .toe configured in .embody/envoy.json top-level toe_path — use TD directly to open additional files
- Opening the same
.toe file in multiple instances auto-suffixes keys (MyProject-2, -3, etc.)