Skip to main content
Glama

πŸ’¬ Embody

Create at the speed of thought.

Version TouchDesigner MCP Tools License GitHub Stars

Full Documentation  |  Manifesto  |  Changelog


Embody puts your ideas on screen as fast as you can describe them. Operators, connections, parameters, the works. Want to try a different direction? Spin up a new approach in seconds. Compare attempts side by side. Branch off the one that works. The tool keeps up with you, instead of the other way around.

Three Tools, One Idea

Envoy β€” forward velocity. An embedded MCP server lets Claude Code, Cursor, and Windsurf talk directly to your live TouchDesigner session. Create operators, wire them up, set parameters, write extensions, debug errors β€” by saying what you want. No copy-pasting code. No describing your network in chat. Idea β†’ operators in seconds.

Embody β€” lateral velocity. Tag any operator and Embody externalizes it to files on disk that mirror your network hierarchy. Try a new direction, branch off a good one, restore the state from yesterday β€” all in seconds. Your externalized files are the source of truth, so every project opens already in flow.

TDN β€” the substrate that makes both possible. TouchDesigner networks exported as human-readable YAML. The format is what lets your AI agent understand what's on the screen, what lets you diff one attempt against another, and what lets a network reconstruct itself from text on the next project open. TDN is what makes the rest of this possible.

Embody Manager UI

What

Why it matters

πŸ€–

Envoy MCP Server

49 tools let your AI assistant build, wire, parameterize, and debug live networks. The first time you watch it happen, you stop typing operator names by hand for good.

πŸ“„

TDN Network Format

Networks become text. Diff two versions, revisit any version, hand an LLM a complete picture of what's on screen β€” all from a single .tdn file.

πŸ“¦

Automatic Restoration

Externalized operators rebuild themselves from disk on every project open. The .toe is no longer the source of truth β€” your files are.

πŸ“€

Portable Tox Export

Pull any COMP out as a self-contained .tox with external references stripped. Ship a piece of your project anywhere.


Related MCP server: touch-mcp

Quick Start

1. Project Setup

Embody writes externalized files relative to your .toe location β€” no special folder structure required. Embody works in any project folder; if you happen to use git, every change is also a clean diff for free.

my-project/              ← project folder (optionally a git repo)
β”œβ”€β”€ my-project.toe       ← your TouchDesigner project
β”œβ”€β”€ base1/               ← externalized operators
β”‚   β”œβ”€β”€ base2.tox        ← COMP (TOX strategy)
β”‚   β”œβ”€β”€ base3.tdn        ← COMP (TDN strategy β€” diffable YAML)
β”‚   └── text1.py         ← DAT
└── ...

2. Install and Tag

  1. Download the Embody .tox from /release and drag it into your TouchDesigner project

  2. Tag operators β€” select any COMP or DAT and press lctrl twice to tag and externalize it

  3. Work normally β€” press ctrl + shift + u to update all externalizations, or ctrl + alt + u to update only the current COMP. On project open, Embody restores everything from disk automatically

Tip: If no operators are tagged, Embody will externalize all eligible COMPs and DATs, which may slow down complex projects. Tagging selectively is recommended.

3. Keyboard Shortcuts

Shortcut

Action

lctrl + lctrl

Tag or manage the operator under the cursor

ctrl + shift + u

Update all externalizations

ctrl + alt + u

Update only the current COMP

ctrl + shift + r

Refresh tracking state

ctrl + shift + o

Open the Manager UI

ctrl + shift + e

Export entire project to .tdn file

ctrl + alt + e

Export current COMP to .tdn file

For supported formats, folder configuration, duplicate handling, Manager UI, and more β€” see the Embody docs.


Envoy MCP Server

Embody includes Envoy, an embedded MCP server that gives AI coding assistants direct access to your live TouchDesigner session.

Setup

  1. Enable Envoy β€” toggle the Envoyenable parameter on the Embody COMP

  2. Server starts on localhost:9870 (configurable via Envoyport)

  3. Auto-configuration β€” Envoy creates a .mcp.json in your git repo root

  4. Connect β€” open a Claude Code session (or restart your IDE) in the repo root β€” it picks up .mcp.json automatically

If your project isn't in a git repo, add .mcp.json manually to your project root:

{
  "mcpServers": {
    "envoy": {
      "type": "http",
      "url": "http://localhost:9870/mcp"
    }
  }
}

Tools at a Glance

Tool

What It Does

create_op

Create any operator type in any network

set_parameter

Set values, expressions, or bind modes on any parameter

connect_ops

Wire operators together

execute_python

Run arbitrary Python in TD's main thread

export_network

Export networks to diffable .tdn YAML

create_extension

Scaffold a full extension (COMP + DAT + wiring)

get_op_errors

Inspect errors on any operator and its children

...and 37 more. See the full tools reference.

When Envoy starts, it generates a CLAUDE.md file in your project root with TD development patterns, the complete MCP tool reference, and project-specific guidance.


TDN Network Format

TDN (TouchDesigner Network) is the file format that makes the rest of Embody possible. It exports an entire operator network β€” operators, connections, parameters, layout, annotations, DAT content β€” as a single human-readable YAML file. Your AI agent can read it. You can read it. Any text tool can diff it. The network can rebuild itself from it on the next project open.

This is the substrate. Every other capability β€” AI-driven building, version control, automatic restoration β€” builds on top of it.

  • Entire project: ctrl + shift + e

  • Current COMP: ctrl + alt + e

  • Via Envoy: export_network / import_network MCP tools

See the full TDN specification for format details, import process, and round-trip guarantees.


Embody provides a multi-destination logging system:

  • File logging (default): dev/logs/<project_name>_YYMMDD.log, auto-rotates at 10 MB

  • FIFO DAT: Recent entries visible in the TD network editor

  • Textport: Enable the Print parameter to echo logs

  • Ring buffer: Last 200 entries via the Envoy get_logs MCP tool

op.Embody.Log('Something happened', 'INFO')
op.Embody.Warn('Check this out')
op.Embody.Error('Something broke')

Embody includes 73 test suites (1,707 tests) covering core externalization, MCP tools, TDN format, the Envoy server/bridge, and palette catalogs. Tests run inside TouchDesigner using a custom test runner with sandbox isolation.

op.unit_tests.RunTests()                              # All tests (non-blocking)
op.unit_tests.RunTests(suite_name='test_path_utils')   # Single suite
op.unit_tests.RunTestsSync()                           # All in one frame (blocks TD)

Via Envoy MCP: use the run_tests tool. See the full testing docs for coverage details and how to write new tests.

  • Timeline Paused: Embody requires the timeline to be running. An error appears if paused.

  • Clone/Replicant Operators: Cannot be externalized. Embody warns if you try to tag them.

  • Engine COMPs: Engine, time, and annotate COMPs are not supported for externalization.

For more, see Troubleshooting.


Version History

See the full changelog for detailed version history.

Recent releases:

  • 6.0.49: A generated-file-safety + web-polish build. Re-running Envoy's config generation now PRESERVES your edits to generated rules/skills instead of clobbering them -- _writeTemplate records a SHA-256 of each generated file in .embody/generated-hashes.json and skips any whose on-disk content drifted (delete the file to opt back in); files stay byte-identical to their templates. The v6.0.47 annotation dedup reached Embody's own self-externalized .tdn files (9 redundant annotateCOMP operator copies + dead templates dropped, 840 lines, every annotation byte-identical -- confirmed a safe dedup by a 20-agent adversarial review). embody.tools got a deep polish pass: correct TD family colors and dotted op-reference edges in the network viewer, always-prevented node overlap + wires that arc over same-row nodes, a "by user" Collection filter, foldable operators: / annotations: in the raw-TDN YAML viewer, a sidebar specimen layout, and a fixed 3-10s page-load freeze. New tests B08-B12 in test_claude_config. Test suite 73 suites / 1,707 tests, all green.

  • 6.0.47: A TDN-format cleanup. Annotation COMPs are no longer double-captured in .tdn exports -- a stock TD annotate is a palette clone with an extension and ~40 custom parameters, so the exporter was writing it both as a 100-205 line operators: entry and in the compact annotations: array; _exportChildren now skips annotateCOMP children (the importer already rebuilds them from annotations:, Phase 7a). build: null is omitted from the header for untracked/portable networks, and all 12 gallery specimens were cleaned (-2,887 lines, pure deletion, validated by live re-import and clean reconstruction on project open). Save UX: the at-risk content check no longer logs a misleading [test] warning on every Ctrl+S -- the test gate and the save gate are now separate (a real test still warns; a save stays quiet). New test_tdn_annotation_export (7) + 2 test_dialog_suppression tests; the shipped Embody-v6.0.47.tox passed a fresh-install smoke test with both TDN fixes confirmed running live. Test suite 73 suites / 1,702 tests, all green.

  • 6.0.46: A docs-accuracy + web-polish build. A multi-agent audit reconciled the entire docs site, the AI machine-files (llms/for-ai), and this README to the live source: the stale "inert by default" community-paste wording is now the real clean/flagged/blocked verdict model; counts corrected (72 suites / 1,693 tests; 49 MCP tools); API/shortcut examples fixed in the docs and the shipped AGENTS template (Ctrl+Shift+O opens the Manager, getExternalizedOps(COMP)); and llms-full.txt's embedded TDN spec regenerated to v2.0 (ASCII-folded, application/yaml). embody.tools gained an app-native report dialog, a simplified specimen network-preview header, a centred /contribute form (renamed from /submit), and a themed 404. Plus minor custom-parameter organization.

  • 6.0.44: Community specimens paste in LIVE and working. The safe-import was zeroing EVERY parameter expression (a published specimen's GLSL uniform bindings, resolution, and animation drivers all collapsed to 0 -- a dead frame), so it now preserves provably-pure value expressions (par reads, absTime, math.*, Par.eval(), arithmetic) via a new AST allowlist and disarms only genuinely side-effecting surfaces; a clean specimen pastes with no warning (live-if-scanned-clean). Fixes the scanner false positives on .eval()/.store()/tdu/GLSL DATs, trusts TD palette extensions (with an opshortcut-hijack defense), and closes adjacent holes (Script OPs bypassed, tox_ref stripped, cooking suspended during untrusted import). Paste UX: the new COMP auto-selects and the view pans to centre it (via writable pane.x/y), and the auto-paste prompt fires only while the TD window is the OS-frontmost app (NSWorkspace / GetForegroundWindow). New test_collection_pure (14) + standalone test_safe_import_pure (25) + a 70-case validator corpus. Test suite 72 suites / 1,693 tests.

  • 6.0.42: Clipboard auto-paste -- bring a TDN into your network with no keyboard shortcut. The conflicting Cmd-Shift-V paste binding is removed (TD's native operator-clipboard paste fires on the same keystroke and can't be suppressed, pasting leftover nodes). Instead Embody watches the OS clipboard (ui.clipboard) and, when a TDN network appears (web "embody it" button or Cmd-Shift-C), prompts to Embody it into the current network as a new COMP -- debounced, gated on a new Clipboard Auto-Paste toggle, skipped in Perform Mode, and suppressed during saves/tests. test_clipboard_watch (5); no regression. Test suite 71 suites / 1,678 tests.

  • 6.0.41: The git-uncommitted status axis -- a second manager status axis (after diff_tdn in 6.40), completing the v5.0.437 feature set on the v6 line. Externalized files saved to disk but not yet committed to git now show a distinct orange Strategy badge, kept separate from the red "unsaved" axis. An async git status --porcelain worker scan (no frame drop, --no-optional-locks) maps changes to op paths and stores them at runtime; a changed filter keyword shows rows with pending changes on either axis; a shipped refresh-after-commit rule clears the badges after a commit. The Uncommittedcolor param (already in v6) is now wired. test_git_status (20 tests); full data path verified live. Test suite 70 suites / 1,673 tests, all green.

  • 6.0.40: The diff_tdn release. Re-integrates the diff_tdn MCP tool (what is UNSAVED in a TDN network -- the live network vs the on-disk .tdn, the view git can't give -- one COMP or the whole project) and its companion .tdn git textconv driver (keeps committed .tdn diffs clean by stripping the volatile export header) from v5.0.437 into v6's YAML v2.0 world. The textconv is now YAML-aware and the Embody venv carries PyYAML so git's diff driver no longer silently falls back to a raw, noisy diff; the diff engine reconciles a legacy v1.5 array dat_content with the v2.0 string form. get_externalizations / get_externalization_status now hint recommended_tool: diff_tdn. A 4-lens adversarial panel caught two real regressions pre-merge (a dropped _get_externalizations enrichment; four broken setup-environment tests), both fixed and verified. Test suite 69 suites / 1,653 tests, all green.

  • 6.0.39: The save-resilience release. The Envoy liveness watchdog now actually self-heals a save-time wedge β€” its revive cooldown measured a ~2s anti-spam window in absTime.frame (frames since app launch, resets to 0 each launch) but stored that value in COMP storage, which persists into the .toe/.tdn; a high frame baked from a prior session made every revive compute a negative now - stored delta (always "< 2s ago"), so _reviveDeadServer returned before scheduling the restart, every time, permanently β€” the watchdog detected the wedge forever but couldn't fix it (MCP stayed down after a save until a manual toggle). The cooldown now uses time.monotonic() on an instance attribute (never absTime.frame, never storage) and __init__ scrubs the obsolete _last_revive_frame; verified on a real save β€” the Envoy drop self-heals in ~1s. The watchdog also now trusts the socket, not _init_complete/_starting (both reset by a save, which used to idle the tick on a dead server), and Start() probes the socket before trusting a stale "Running" status. The "Enable Envoy?" onboarding modal no longer fires during a save or test β€” a single _suppressDialogs() predicate (test run active OR save in progress) gates the Verify() queue site, the deferred _promptEnvoy, and _messageBox, with a _suppress_dialogs save-window flag scrubbed on next open; the file-cleanup and deprecated-externaltox prompts are gated the same way. Plus 169 new v6 tests across 9 suites (clipboard copy/paste 42, collection scanner 22 + safe-import 18, v6 hardening 20, specimen publish 19, the watchdog 21, GLSL externalize 11, layout lint 10, dialog suppression 6) and a layout-lint maxDepth fix (the v6.0.34 lint used findChildren(depth=12) β€” exactly depth 12, matched nothing). Test suite 67 suites / 1,616 tests, all green.

  • 6.0.34: Everything since v6.0.26 in one release. GLSL shader DATs now externalize as .glsl, not .py β€” EmbodyExt._externalizeDATs inferred the tag from a bare dat_type_to_tag map (['text']='Pytag'), so shaders (type text, language glsl) were written as Python; it now resolves the tag from DAT content via _inferDATTagValue, and the 8 newer Specimens' 42 shaders were re-externalized to .glsl. Layout lint at the Envoy tool layer β€” execute_python (raw comp.create()/.copy(), no auto-position) was the recurring source of ops piled at (0,0); Envoy now snapshots the op tree before your code runs and emits a LAYOUT WARNING on the response when the call leaves ops at (0,0), overlapping, or with docked DATs >500u from their host (_lintLayout/_lintNewOps), with network-layout.md + template DRY'd around the enforcement. Specimen publish hook (specimen_publish.py) β€” a project onProjectPostSave hook exports each manifest Specimen self-contained (DAT scripts embedded) to specimens/<tdn_path> for the embody.tools "embody it" copy-paste, skipping unchanged files. Waveform-stack feedback fix β€” broke a Feedback-TOP cook-dependency loop by seeding from outside the loop (res_fb) and grabbing the frame-delayed state from its Target TOP. Plus six landscape transmission specimens (essence-streams, vertical-fibers, crosspoint, waveform-stack, packet-fabric, hyper_ntsc; reaction-diffusion landscaped). Test suite 58 suites / 1,439 tests, no regressions.

  • 6.0.26: A correctness + efficiency release. TDN custom-parameter values now round-trip β€” exporting a COMP with custom parameters and re-importing reset every value to 0/min (the exporter omits a value equal to its default, but the importer set .default and never .val), which broke every parametric specimen on import; fixed with a defaultβ†’value fallback in _setCustomParValues plus 6 regression tests. The save-time watchdog log storm is gone β€” a project.save() reinitializes EnvoyExt in a rapid same-frame burst, and ~4s later all the armed liveness-watchdog ticks came due in one frame and each revived + logged ("MCP socket on port None unreachable β€” reviving server" Γ—18-21 per save); a monotonic generation token collapses the leftover tick loops on the next reinit, and a frame-cooldown in _reviveDeadServer collapses the same-frame burst to one log + one revive (verified on a real save: 21 β†’ 1), with the genuine self-heal preserved. The pre-save "TDN Content at Risk" dialog no longer fires on annotated specimens β€” the content-safety scan was walking into palette-clone COMPs (an annotateCOMP's button help tables) and flagging their regenerable internals as user content at risk, so every TDN-tagged COMP containing an annotation popped the dialog on each save; both scans now skip anything inside a palette clone (verified live, 11 at-risk β†’ 0). MCP token-efficiency pass β€” tool results stay in context, so four changes cut response size: the per-response _logs piggyback is now WARNING/ERROR-only (was up to 20 routine-INFO entries on every call), run_tests returns counts + failures only (drops ~1,400 PASS objects), export_network to a file returns a compact summary instead of echoing the whole .tdn, and capture_top returns the file path by default (pass inline=true for an embedded preview). Murmuration joins the Specimen Collection (4th) β€” a dense GPU particle swarm flocking like a starling murmuration at dusk: true per-neighbor Reynolds (a Neighbor POP index list iterated in a GLSL POP for cohesion/alignment/inverse-square separation), a moving attractor, curl-noise, soft containment + drag, rendered as additive point sprites with a dusk speed-ramp + bloom. The TDN clipboard Copy/Paste loop is now complete β€” Cmd-Shift-C copies the selected COMP (v6.0.11 shipped paste but never wired copy β€” its "Copy button" never existed), Cmd-Shift-V now also accepts a raw .tdn file's text (sandboxed/inert, since bare text has no provenance β€” ImportNetworkFromFile is the trusted/direct path for local files), the clipboard envelope is pretty-printed, and a pasted COMP is named from its network_path basename instead of pasted_tdn. POP point-sequence import fixed β€” op.seq['pt'] subscript returns None for POP sequences, so importing a pt sequence silently dropped its points; resolution now iterates. Plus test_tdn_file_io/test_tdn_helpers were updated for TDN v2.0 YAML (33 tests red since the v2.0 migration, now green). Test suite 57 suites / 1,439 tests, all green.

  • 6.0.16: The on-disk .tdn format graduates to TDN v2.0: YAML. A network now serializes as a single self-contained YAML document instead of JSON, so a .tdn reads top-to-bottom like the network it describes. Multi-line dat_content (GLSL, Python, any textDAT script) is stored as a plain string rendered as a YAML literal block scalar (|) β€” source reads like code with no escaped newlines and diffs line-by-line (this reverts the v1.5 array-of-lines workaround). Round-trips are byte-exact (trailing newlines preserved via |/|-/|+ chomping) and deterministic (no key reorder, no anchors), so re-saving an unchanged network produces no diff. Legacy JSON .tdn still import β€” importers parse json-first (BOM/whitespace stripped), so back-compat is independent of any YAML C library and tab-indented legacy files load losslessly; migration is lazy (rewritten on next save). Auto-created default docked compute DATs are no longer serialized (TD recreates them on import), and with the YAML representation files are roughly 17% smaller. MIME type is now application/yaml. Docs (spec, examples, schema guide, supported formats) rewritten for v2.0.

  • 6.0.11: Envoy's MCP connection now self-heals across saves and reinits β€” the end of "connected:false while TouchDesigner is still running". A liveness watchdog tied to the EnvoyExt instance lifetime (armed from __init__, one loop per instance) probes the socket every ~4s and revives Envoy whenever it's enabled-but-down β€” a dead socket, or a project.save() / extension reinit that took the server down β€” force-freeing port 9870 if it's still held and rebinding in ~1s, with no restart and no manual toggle. The prior Start()-armed watchdog missed the save case: a mid-save reinit suppresses the old server thread's exit callback (no auto-restart) and can skip or race the new instance's auto-start, orphaning a Start()-only watchdog. Verified β€” a live-listener kill self-heals in ~6s, and three consecutive project.save() cycles each fired the watchdog (running=False), force-freed the held port, and rebound in ~1s. Plus: TDN clipboard Copy/Paste (the Copy tdn button writes a portable _embody_tdn envelope; Ctrl+Shift+V pastes it as a new COMP), with community TDN (embody.tools source) scanned and defaulted inert β€” Execute DATs disarmed, expressions neutralized, IO bypassed, storage stripped, content preserved β€” via a CollectionExt extension and self-contained scanner / safe-import DATs (clipboard logic now lives inside the Embody COMP, portable in the .tox). New always-loaded performance.md rule (metric-gating protocol + wiki-cited crash-cause table + safe-default caps) and visual-aesthetics skill (objective composition / value / color / contrast / motion / finishing + a mandatory capture_top preview-and-judge loop); preview-and-judge reinforced across create-/debug-operator, mcp-tools-reference, and CLAUDE.md; td-connectivity documents the watchdog; network-layout hardened against the execute_python / .create() (0, 0) bypass β€” all deployed to user projects via the template map. First v6 Embody/Envoy changelog entry (earlier v6.0.x builds were the embody.tools platform under platform/).

  • 5.0.429: A friendlier "Duplicate Path Detected" dialog. New Template Master Name parameter (Templatemaster, default __template__): when a group of operators sharing one external path has exactly one whose path contains that name as a whole segment (e.g. a __template__ parent COMP), it's auto-selected as master and the rest tagged clone with no prompt β€” the durable fix for the common template-plus-runtime-copies pattern (a scene_<id> chain each carrying the template's externalized DATs). Opt-in by convention (projects not using the name are unaffected; set your own like _master, or clear to always choose manually); matches a whole segment not a substring, and only on an unambiguous single match. The manual prompt no longer shows N identical same-named buttons β€” each is now labeled by the differing path segment, numbered to the dialog body (1: __template__, 2: scene_1exalohf, …) β€” and groups larger than 5 operators get a Keep first as master / Dismiss strategy prompt instead of an unreadable button row. Test suite 57 suites / 1,413 tests (+12), all green.

  • 5.0.428: Everything since v5.0.414 in one release. Headline is tdn_exclude β€” a Tdnexcludetag parameter (default tdn_exclude) that makes a COMP invisible to the TDN system (never exported, stripped, or reconstructed), the durable opt-out for app-managed children under cascade-autotag (runtime op.copy() content like Moonshine's proj_<id> chains). Exclusion is honored at a TDN boundary's direct children; a COMP tagged but nested under a non-excluded intermediate is serialized as normal content and preserved (with a warning) rather than silently lost. TDN dirty detection rebuilt: the fingerprint captures each operator's non-default authored parameter values (expr/bindExpr/val, never par.eval()), so a parameter edit flags a TDN COMP dirty while a dependency-driven change to a live expression's evaluated value (animation, audio, a moving CHOP) does not β€” no more perpetual re-export churn on animated COMPs. The sweep is consolidated to one fingerprint per Refresh (was two) for a real frame-time win on large networks; DirtyCount reads the fingerprint result instead of the always-True oper.dirty; a reverted edit clears the dirty flag. Envoy resilience: startup status waits for a real bind handshake before reading "Running" (no optimistic lie, including uvicorn SystemExit); restart_td validates real non-zombie TouchDesigner PIDs via ps; the status moved into the window header, prefixed "Envoy". Three issue #21 crash fixes hardened across the whole table-read surface: captureParameters can't crash on broken expressions (authored read, no eval), _cellVal guards every externalizations-table read (and warns on genuine row-level corruption), and onProjectPreSave is fail-safe end-to-end so a hook exception can't truncate the .toe. Plus: the "TDN Content at Risk" dialog gains a persistent Always Skip; externalizations.tsv no longer churns phantom timestamp rows per save; a calmer first-launch palette scan; the manager expand/collapse glyph on top-level rows; a duplicate-BOM fix in WindowHeaderExt; an AI-first Quickstart page; MCP tool count reconciled to 48. A final 7-angle regression review of the branch caught and fixed the live-expression churn, the nested-exclude data loss, the always-dirty count, the stuck dirty flag, the duplicate BOM, and the double sweep β€” each with a regression test. Verified by a fresh-install smoke test of the release .tox. Test suite 57 suites / 1,401 tests, all green.

  • 5.0.414: Third value Custom for AI Project Root (follow-up to Ten0's feedback on issue #19) β€” paired with a new AI Project Root (Custom) Folder parameter that's greyed out unless the menu is set to Custom. The custom path can be absolute or relative to the .toe directory (e.g. ../). For monorepos where multiple .toe files share a parent directory, each .toe sets the same relative path and they all converge on one AGENTS.md / .claude/ / .mcp.json / .embody/envoy.json β€” which lets the multi-instance MCP feature work naturally across sibling projects. Flipping the menu or changing the path migrates Embody state and AI config to the new location, same atomic move + marker-aware cleanup as the gitroot↔projectfolder flip. Plus two defense fixes from earlier in the cycle: _messageBox no longer falls back to a real modal dialog when seeded test responses are exhausted (would freeze TD with stacked dialogs after a test run); Verify() won't re-queue the Envoy opt-in prompt while one is already pending (idempotent flag β€” multiple Verify() calls during a test's Disable/Enable cycle can no longer stack N prompts). _findSettingsFile got a walk-up fallback to handle the Custom-mode chicken-and-egg (saved custom path lives in config.json which can't be read until settings are restored β€” so walk up from the .toe looking for any .embody/config.json).

  • 5.0.413: Two independent bodies of work bundled into one build. (1) Issue #20: parent .tdn files no longer embed the contents of TOX-externalized child COMPs β€” emits a tox_ref pointer instead, mirroring the existing tdn_ref pattern; round-trip restoration handled by a new Phase 8.5 (_restoreTOXShells) that sets externaltox and calls _reloadTox after import. Plus _validateTOXRefs for cross-validation parity, the _stripNestedTOXChildren backward-compat path for pre-v1.4 files, and TDN format version bumped to 1.4 with the schema updated accordingly. Envoy-toggle frame-drop fix surfaced during diagnosis β€” _findAvailablePort was paying a 1.5s time.sleep on the main thread (108 frames at 60fps) whenever the preferred port was held by any listener, including foreign zombie TD processes the wait could never free. Now skips the wait when force-close has nothing of ours to close (caps at 500ms when it does), and branches directly to range-scan when the holder is a foreign live instance in our registry. Measured: Start time 1797ms β†’ 346ms. (2) AI Project Root parameter (gitroot default / projectfolder) for monorepo setups where the TouchDesigner project lives in a subdirectory of a larger repo β€” flips Embody's AI/MCP config target between the git root and the .toe directory, with full migration of persistent state and marker-aware cleanup of the old root that preserves user-authored files. Issue #19 fix β€” the Path.home() length comparison in _findProjectRoot/_findGitRoot/_checkOrInitGitRepo bailed before searching when the project lived on a non-home drive (Windows D:\ with home on C:), so subsequent runs failed to find .git, duplicated .mcp.json config, and broke the MCP connection. Also: registry I/O (RefreshRegistry, _removeFromRegistry, port-conflict detection) routes through _findProjectRoot() for consistency under projectfolder mode; _atomicMove helper (copy-to-tmp + os.replace + unlink) makes cross-filesystem catalog moves safe against partial writes; settings-restore checks both Aiprojectroot candidate roots; orphan-handling renames any leftover critical files after a failed migration to .orphan so fallback lookups don't pick up stale data; legacy file sweep removes old .envoy.json, .embody.json, .envoy-tools-cache.json, and .claude/envoy-bridge.py from previous Embody versions. 6 new tests; test_tdn_file_io 66 β†’ 92.

  • 5.0.407: Critical Windows-only crash hotfix introduced by v5.0.402's registry GC. _isPidAlive(pid) was built on os.kill(pid, 0) β€” on Windows, CPython implements that as OpenProcess(PROCESS_ALL_ACCESS) + TerminateProcess(handle, sig) for all sig values, so the "liveness check" literally told the OS to kill the process being checked. Any time _writeEnvoyConfig's GC loop iterated instances and the row contained the running TD's own PID (which it does any time the project has been saved with Envoy enabled), the loop called TerminateProcess(self_handle, 0) and TD exited with code 0 β€” silent, no traceback, repro fingerprint matched perfectly. Replaced with safe OpenProcess(SYNCHRONIZE) pattern via ctypes (already in use by envoy_bridge.is_process_alive); SYNCHRONIZE access doesn't include termination rights. Also rewired the duplicate os.kill inside _findAvailablePort that would have killed any foreign live TD whose registry entry shared the port. Plus: CatalogManager palette scan now snapshots and restores time.play/time.rate/cookRate/realTime around each chunk so a misbehaving palette component (e.g. the refactored Palette:logger v2.7.0 on TD 2025.32820) can't leave the timeline paused; _verifyMcpImportable short-circuits when mcp.server is already loaded instead of tearing down 82 submodules every toggle; bridge find_all_td_pids() filters CEF/Web Render helper subprocesses that were generating ~214k phantom "TD process detected" log lines per session; _osLabel() disambiguates Windows 11 from Windows 10 (both report NT 10.0 β€” discriminator is build β‰₯22000); execute_src_ctrl reads/writes README as UTF-8 so emoji don't crash the bumper on Windows non-UTF-8 code pages. 19 new tests across 3 new files. Test file count 50 β†’ 53.

  • 5.0.403: Hotfix for v5.0.402 β€” EmbodyExt.Update() rename-detection used self.ownerComp (an EnvoyExt-only attribute) instead of self.my. Every Update tick during a save threw AttributeError, which got caught and logged at WARNING but meant the rename-detect path never actually fired. Layer 2 walk-forward in the bridge masked the symptom (lookups still resolved correctly), but the registry would have stayed perpetually keyed to the previous version. One-character fix.

  • 5.0.402: Three follow-on registry fixes after v5.0.401 verification surfaced edge cases. _writeEnvoyConfig now garbage-collects dead-PID rows on every write β€” registries that previously accumulated dead entries across sessions (hard kills, force-quits, crashes) collapse to live-only on the next save (verified: 28 rows β†’ 1 in one cycle). EmbodyExt.Update() caches _last_toe_name and triggers RefreshRegistry() on basename mismatch β€” defensive backstop for execute.py's postSave hook in case it didn't reload. Bridge handle_launch_td adds a PID-aware slow-path scan after the fast-path key lookup: walks-forward each alive instance's registered toe_path and refuses if any resolves to the same target β€” fixes the stale-key + walk-forward composition that could otherwise spawn a duplicate TD. 6 new tests across test_envoy_registry and test_envoy_bridge.

  • 5.0.401: envoy.json registry walks forward across TD's save-time .toe version bump (Foo-5.398.toe β†’ Foo-5.399.toe). Two-layer fix: EnvoyExt._instanceKey and _writeEnvoyConfig detect a PID's existing row under a different basename and rename + prune in one write; new RefreshRegistry() is called from onProjectPostSave so the registry walks forward even when Envoy doesn't restart. Bridge-side defensive layer: find_latest_versioned_toe() strips <digits>.toe and finds the highest-versioned sibling on disk; resolve_toe_path() now reads from instances[active] (was legacy-flat-only) and routes through the walk-forward helper. Plus a hotfix for the postSave's 'EnvoyExt' object has no attribute 'port' crash β€” RefreshRegistry() now reads the running port from envoy.json by PID instead of a nonexistent instance attribute. 20 new tests across test_envoy_registry (7) and test_envoy_bridge (13), one updated for the v5.0.399 instance-specific guard.

  • 5.0.399: New edit_dat_content MCP tool for token-efficient surgical text edits β€” mirrors Claude Code's Edit tool (old_string/new_string, unique match by default, opt-in replace_all), so a 2-line edit in a 500-line DAT no longer pays for the whole DAT's content on the wire. Bridge multi-launch fix: Envoy can now launch a TD instance alongside an unrelated TD project β€” handle_launch_td swapped the blanket "any TD running" guard for an instance-specific check, macOS open -n flag forces a new process instead of reusing an existing window, and PID identification diffs against a pre-launch snapshot instead of returning the first TD pid found. Plus 11 new tests for the new tool and a one-line fix for test_set_dat_content_clear that had been failing since v5.0.397's wipe guardrail.

  • 5.0.398: Hotfix for a latent race condition that broke the first-install dialog flow on fresh-project drops without a cached catalog. Update() raced with EnsureCatalogs(), which sets Status='Scanning defaults (X/N)' to show progress. The old gate if Status != 'Enabled': return returned early on that transient value, so _pending_envoy_prompt was never consumed and the Envoy opt-in dialog never appeared. Both gates (Update, ReconcileMetadata) now short-circuit only when Status is explicitly 'Disabled'. Plus 2 regression tests that fail without the fix.

  • 5.0.397: confirm_wipe guardrail on set_dat_content MCP tool blocks accidental DAT wipes from malformed agent calls (refuses text="", rows=[], or clear=True with no replacement unless explicitly confirmed); TDN at-risk dialog skips TD-managed read-only DAT types (Info, WebRTC, Folder, Monitors, device-discovery, etc.) so the warning only fires for content the user actually authored; .embody/config.json now byte-stable across saves via sorted iteration of _PERSISTED_PARAMS + sort_keys=True (issue #18); test debt cleanup of 28 orphan .txt files, 3 stale envoy_bridge stubs replaced with 6 real tests, ancestor_rename tearDown leak fixed, palette tdn_reconstruction tests aligned with current production contract

  • 5.0.393: Harden Envoy bootstrap so silent failures surface a useful textport message instead of No module named 'mcp.server' β€” _setupEnvironment now returns bool, four previously-silent failure paths log explicit errors, Start() aborts before _runServer if deps aren't ready, and a final import mcp.server gate catches partial installs (issue #17)

  • 5.0.392: Critical Windows-only fix β€” subprocess.run from inside TD raised [WinError 50] The request is not supported because TD's GUI process stdin handle isn't duplicatable, causing Embody's venv-verify to falsely flag healthy venvs as corrupt and shutil.rmtree them on every TD restart. Fixed by passing stdin=subprocess.DEVNULL on the 5 affected subprocess.run sites in the bootstrap and verify-venv paths

  • 5.0.391: Per-project TouchDesigner build pinning (committed .embody/project.json + Envoy bridge auto-discovers the matching install on fresh clones), thread-conflict fix in the MCP update checker, and a 21-assertion cleanup of bridge tests that had been silently broken since the bridge v2 refactor β€” bridge tests now 148/151 passing, zero failures

  • 5.0.386: Batch-confirm prompt for duplicate path detection β€” when multiple groups remain unresolved, one dialog offers Auto-resolve all / Review individually / Dismiss instead of a separate modal per group

  • 5.0.383: Clone detection fix for self-referencing masters (reusable UI components using iop.* expressions), and a cleaner list UI moving the tree expand/collapse indicator into a dedicated column

  • 5.0.381: Global Perform Mode toggle disables Embody/Envoy/TDN compute during TD performance (Issue #13), auto-resolve duplicate DATs inside active clones without prompting (Issue #15), ancestor-rename disk handling now uses the externalizations folder prefix so Move no longer fails with "source folder not found" (Issue #16), new render-coordinate-system rules for TD's bottom-left origin convention (Issue #14)

  • 5.0.376: Palette scan skips invasive palettes (TDVR framerate popup, AutoUI widget dialog) on fresh-build startup, rebaked palette catalog covers TD 2025.32460, and false "locked content" warnings suppressed inside clones/replicants (Issue #12)

  • 5.0.372: TDN three-mode master switch (Off/Export-on-Save/Roundtrip), read_tdn MCP tool (~20-90Γ— fewer tokens than get_op walks), combined DAT+storage Content Safety dialog with "Never Ask" footgun removed, palette detection fix for native buttonCOMP operators, migration nudge for upgrading users, docs + landing page rewrite

  • 5.0.362: Palette handling control (Ask/Black Box/Full Export) during TDN export, CatalogManager fires on fresh project drops, palette catalog portability + log-level fixes

  • 5.0.356: Palette catalog detection, animationCOMP keyframe preservation, external wire preservation across TDN strip/rebuild

  • 5.0.354: Consolidate runtime files into .embody/ folder, fix bridge path resolution

  • 5.0.352: Fix Envoy failing to start after Embody upgrade (restart counter, port race, reclaim timeout)

  • 5.0.351: Creation-defaults catalog, stdin-based bridge lifecycle, Envoy resilience hardening

  • 5.0.336: Batch MCP operations, Envoy auto-restart on crash and save, 46 MCP tools

  • 5.0.330: Envoy bridge v2 β€” proactive reconciliation, multi-session safety, zero forced restarts

  • 5.0.320: TDN v1.3 β€” parameter sequence round-trip + companion DAT handling (GLSL/Timer/Script/Ramp companions)

  • 5.0.310: Fix first-time Envoy setup stuck on "Disabled" (issues #8, #9), git config generation on fresh install

  • 5.0.305: Replicant duplicate detection fix (issue #4), TDN export improvements, ExternalizeProject dialog

  • 5.0.302: Fix duplicate path clone detection (issue #4), config file location (issue #5), Envoy startup flow

  • 5.0.278: Fix folder change crash (issue #3), regression tests

  • 5.0.277: Manager UI improvements, Ctrl+Shift+R shortcut, consistent "Update" terminology

  • 5.0.275: TDN export keyboard shortcut pars, keyboard shortcuts documentation

  • 5.0.274: Settings persistence across upgrades, extension initialization timing docs

  • 5.0.263: DAT content safety, palette clone fidelity, recursive TDN fingerprinting, venv validation

  • 5.0.259: Mandatory operator layout rules, /local path prohibition, TD connectivity recovery

  • 5.0.258: Multi-instance Envoy support, switch_instance tool, auto-suffix collision avoidance

  • 5.0.252: Windows process-kill fix, reconstruction verification fix

  • 5.0.251: Nested TDN child-skip on import, depth-sorted reconstruction ordering

  • 5.0.243: Headless smoke testing, file cleanup preferences, specialized COMP support, portable .tox hardening

  • 5.0.237: TDN v1.1 format, import error surfacing, save-cycle pane restoration, Envoy troubleshooting docs

  • 5.0.235: restart_td meta-tool, local MCP handshake, operator overlap warnings

  • 5.0.233: Project-level performance monitoring, /validate command, test runner dialog fix

  • 5.0.228: macOS timezone fix, toolbar hover highlight

  • 5.0.227: TDN crash safety β€” atomic writes, backup rotation, content-equal skip, About page filtering

  • 5.0.222: Rename tag_for_externalization β†’ externalize_op, clarify single-step workflow

  • 5.0.221: TDN annotation properties, GitHub release rule, templates cleanup

  • 5.0.220: Network layout rewrite, commit-push checklist rule, expanded MCP tool allowlist, tooltip fix

  • 5.0.217: TDN target COMP parameter preservation, user-prompted file cleanup, dock safety, git init hardening

  • 5.0.210: DAT restoration on startup, continuity check hardening, manager list row limiting

  • 5.0.208: Settings auto-deploy, bridge template, Envoy startup resilience

  • 5.0.206: Metadata reconciliation, network layout tool, TDN companion dedup

  • 5.0.204: Custom window header, path portability, TDN cleanup

  • 5.0.201: Robust first-install init, table schema expansion, release build hardening

  • 5.0.190: Automatic restoration β€” TOX and TDN strategy COMPs fully restored from disk on project open

  • 5.0: Envoy MCP server (46 tools), TDN format, test framework (38 suites), macOS support


Contributors

Originally derived from External Tox Saver by Tim Franklin. Refactored entirely by Dylan Roscover, with inspiration and guidance from Elburz Sorkhabi, Matthew Ragan and Wieland Hilker.

License

MIT License

A
license - permissive license
-
quality - not tested
A
maintenance

Maintenance

–Maintainers
11hResponse time
1dRelease cycle
55Releases (12mo)
Commit activity
Issues opened vs closed

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dylanroscover/Embody'

If you have feedback or need assistance with the MCP directory API, please join our Discord server