Embody
π¬ Embody
Create at the speed of thought.
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.

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 |
π¦ | Automatic Restoration | Externalized operators rebuild themselves from disk on every project open. The |
π€ | Portable Tox Export | Pull any COMP out as a self-contained |
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
Download the Embody
.toxfrom/releaseand drag it into your TouchDesigner projectTag operators β select any COMP or DAT and press
lctrltwice to tag and externalize itWork normally β press
ctrl + shift + uto update all externalizations, orctrl + alt + uto 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 |
| Tag or manage the operator under the cursor |
| Update all externalizations |
| Update only the current COMP |
| Refresh tracking state |
| Open the Manager UI |
| Export entire project to |
| Export current COMP to |
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
Enable Envoy β toggle the
Envoyenableparameter on the Embody COMPServer starts on
localhost:9870(configurable viaEnvoyport)Auto-configuration β Envoy creates a
.mcp.jsonin your git repo rootConnect β open a Claude Code session (or restart your IDE) in the repo root β it picks up
.mcp.jsonautomatically
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 any operator type in any network |
| Set values, expressions, or bind modes on any parameter |
| Wire operators together |
| Run arbitrary Python in TD's main thread |
| Export networks to diffable |
| Scaffold a full extension (COMP + DAT + wiring) |
| 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 + eCurrent COMP:
ctrl + alt + eVia Envoy:
export_network/import_networkMCP 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 MBFIFO DAT: Recent entries visible in the TD network editor
Textport: Enable the
Printparameter to echo logsRing buffer: Last 200 entries via the Envoy
get_logsMCP 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 --
_writeTemplaterecords a SHA-256 of each generated file in.embody/generated-hashes.jsonand 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.tdnfiles (9 redundantannotateCOMPoperator 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, foldableoperators:/annotations:in the raw-TDN YAML viewer, a sidebar specimen layout, and a fixed 3-10s page-load freeze. New tests B08-B12 intest_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
.tdnexports -- 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 lineoperators:entry and in the compactannotations:array;_exportChildrennow skipsannotateCOMPchildren (the importer already rebuilds them fromannotations:, Phase 7a).build: nullis 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). Newtest_tdn_annotation_export(7) + 2test_dialog_suppressiontests; the shippedEmbody-v6.0.47.toxpassed 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+Oopens the Manager,getExternalizedOps(COMP)); andllms-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/contributeform (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 anopshortcut-hijack defense), and closes adjacent holes (Script OPs bypassed,tox_refstripped, cooking suspended during untrusted import). Paste UX: the new COMP auto-selects and the view pans to centre it (via writablepane.x/y), and the auto-paste prompt fires only while the TD window is the OS-frontmost app (NSWorkspace / GetForegroundWindow). Newtest_collection_pure(14) + standalonetest_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 --porcelainworker scan (no frame drop,--no-optional-locks) maps changes to op paths and stores them at runtime; achangedfilter keyword shows rows with pending changes on either axis; a shipped refresh-after-commit rule clears the badges after a commit. TheUncommittedcolorparam (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_tdnMCP 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.tdngit textconv driver (keeps committed.tdndiffs 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 arraydat_contentwith the v2.0 string form.get_externalizations/get_externalization_statusnow hintrecommended_tool: diff_tdn. A 4-lens adversarial panel caught two real regressions pre-merge (a dropped_get_externalizationsenrichment; 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 negativenow - storeddelta (always "< 2s ago"), so_reviveDeadServerreturned 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 usestime.monotonic()on an instance attribute (neverabsTime.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), andStart()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 theVerify()queue site, the deferred_promptEnvoy, and_messageBox, with a_suppress_dialogssave-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-lintmaxDepthfix (the v6.0.34 lint usedfindChildren(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._externalizeDATsinferred the tag from a baredat_type_to_tagmap (['text']='Pytag'), so shaders (typetext, languageglsl) 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(rawcomp.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 aLAYOUT WARNINGon the response when the call leaves ops at (0,0), overlapping, or with docked DATs >500u from their host (_lintLayout/_lintNewOps), withnetwork-layout.md+ template DRY'd around the enforcement. Specimen publish hook (specimen_publish.py) β a projectonProjectPostSavehook exports each manifest Specimen self-contained (DAT scripts embedded) tospecimens/<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
.defaultand never.val), which broke every parametric specimen on import; fixed with a defaultβvalue fallback in_setCustomParValuesplus 6 regression tests. The save-time watchdog log storm is gone β aproject.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_reviveDeadServercollapses 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 buttonhelptables) 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_logspiggyback is now WARNING/ERROR-only (was up to 20 routine-INFO entries on every call),run_testsreturns counts + failures only (drops ~1,400 PASS objects),export_networkto a file returns a compact summary instead of echoing the whole.tdn, andcapture_topreturns the file path by default (passinline=truefor 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-Ccopies the selected COMP (v6.0.11 shipped paste but never wired copy β its "Copy button" never existed),Cmd-Shift-Vnow also accepts a raw.tdnfile's text (sandboxed/inert, since bare text has no provenance βImportNetworkFromFileis the trusted/direct path for local files), the clipboard envelope is pretty-printed, and a pasted COMP is named from itsnetwork_pathbasename instead ofpasted_tdn. POP point-sequence import fixed βop.seq['pt']subscript returnsNonefor POP sequences, so importing aptsequence silently dropped its points; resolution now iterates. Plustest_tdn_file_io/test_tdn_helperswere 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
.tdnformat graduates to TDN v2.0: YAML. A network now serializes as a single self-contained YAML document instead of JSON, so a.tdnreads top-to-bottom like the network it describes. Multi-linedat_content(GLSL, Python, anytextDATscript) 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.tdnstill 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 nowapplication/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 aproject.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 priorStart()-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 aStart()-only watchdog. Verified β a live-listener kill self-heals in ~6s, and three consecutiveproject.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_tdnenvelope; Ctrl+Shift+V pastes it as a new COMP), with community TDN (embody.toolssource) scanned and defaulted inert β Execute DATs disarmed, expressions neutralized, IO bypassed, storage stripped, content preserved β via aCollectionExtextension and self-contained scanner / safe-import DATs (clipboard logic now lives inside the Embody COMP, portable in the.tox). New always-loadedperformance.mdrule (metric-gating protocol + wiki-cited crash-cause table + safe-default caps) andvisual-aestheticsskill (objective composition / value / color / contrast / motion / finishing + a mandatorycapture_toppreview-and-judge loop); preview-and-judge reinforced across create-/debug-operator, mcp-tools-reference, and CLAUDE.md;td-connectivitydocuments the watchdog;network-layouthardened against theexecute_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 underplatform/).5.0.429: A friendlier "Duplicate Path Detected" dialog. New
Template Master Nameparameter (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 taggedclonewith no prompt β the durable fix for the common template-plus-runtime-copies pattern (ascene_<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β aTdnexcludetagparameter (defaulttdn_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 (runtimeop.copy()content like Moonshine'sproj_<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, neverpar.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;DirtyCountreads the fingerprint result instead of the always-Trueoper.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 uvicornSystemExit);restart_tdvalidates real non-zombieTouchDesignerPIDs viaps; the status moved into the window header, prefixed "Envoy". Three issue #21 crash fixes hardened across the whole table-read surface:captureParameterscan't crash on broken expressions (authored read, no eval),_cellValguards every externalizations-table read (and warns on genuine row-level corruption), andonProjectPreSaveis 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.tsvno 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 inWindowHeaderExt; 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
CustomforAI Project Root(follow-up to Ten0's feedback on issue #19) β paired with a newAI Project Root (Custom)Folder parameter that's greyed out unless the menu is set toCustom. The custom path can be absolute or relative to the.toedirectory (e.g.../). For monorepos where multiple.toefiles share a parent directory, each.toesets the same relative path and they all converge on oneAGENTS.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:_messageBoxno 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 β multipleVerify()calls during a test's Disable/Enable cycle can no longer stack N prompts)._findSettingsFilegot a walk-up fallback to handle the Custom-mode chicken-and-egg (saved custom path lives inconfig.jsonwhich can't be read until settings are restored β so walk up from the.toelooking for any.embody/config.json).5.0.413: Two independent bodies of work bundled into one build. (1) Issue #20: parent
.tdnfiles no longer embed the contents of TOX-externalized child COMPs β emits atox_refpointer instead, mirroring the existingtdn_refpattern; round-trip restoration handled by a new Phase 8.5 (_restoreTOXShells) that setsexternaltoxand calls_reloadToxafter import. Plus_validateTOXRefsfor cross-validation parity, the_stripNestedTOXChildrenbackward-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 β_findAvailablePortwas paying a 1.5stime.sleepon 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 Rootparameter (gitrootdefault /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.toedirectory, with full migration of persistent state and marker-aware cleanup of the old root that preserves user-authored files. Issue #19 fix β thePath.home()length comparison in_findProjectRoot/_findGitRoot/_checkOrInitGitRepobailed 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.jsonconfig, and broke the MCP connection. Also: registry I/O (RefreshRegistry,_removeFromRegistry, port-conflict detection) routes through_findProjectRoot()for consistency underprojectfoldermode;_atomicMovehelper (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.orphanso 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.pyfrom previous Embody versions. 6 new tests;test_tdn_file_io66 β 92.5.0.407: Critical Windows-only crash hotfix introduced by v5.0.402's registry GC.
_isPidAlive(pid)was built onos.kill(pid, 0)β on Windows, CPython implements that asOpenProcess(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 iteratedinstancesand the row contained the running TD's own PID (which it does any time the project has been saved with Envoy enabled), the loop calledTerminateProcess(self_handle, 0)and TD exited with code 0 β silent, no traceback, repro fingerprint matched perfectly. Replaced with safeOpenProcess(SYNCHRONIZE)pattern via ctypes (already in use byenvoy_bridge.is_process_alive); SYNCHRONIZE access doesn't include termination rights. Also rewired the duplicateos.killinside_findAvailablePortthat would have killed any foreign live TD whose registry entry shared the port. Plus:CatalogManagerpalette scan now snapshots and restorestime.play/time.rate/cookRate/realTimearound each chunk so a misbehaving palette component (e.g. the refactoredPalette:logger v2.7.0on TD 2025.32820) can't leave the timeline paused;_verifyMcpImportableshort-circuits whenmcp.serveris already loaded instead of tearing down 82 submodules every toggle; bridgefind_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_ctrlreads/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 usedself.ownerComp(an EnvoyExt-only attribute) instead ofself.my. Every Update tick during a save threwAttributeError, 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.
_writeEnvoyConfignow 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_nameand triggersRefreshRegistry()on basename mismatch β defensive backstop forexecute.py's postSave hook in case it didn't reload. Bridgehandle_launch_tdadds a PID-aware slow-path scan after the fast-path key lookup: walks-forward each alive instance's registeredtoe_pathand 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 acrosstest_envoy_registryandtest_envoy_bridge.5.0.401:
envoy.jsonregistry walks forward across TD's save-time .toe version bump (Foo-5.398.toeβFoo-5.399.toe). Two-layer fix:EnvoyExt._instanceKeyand_writeEnvoyConfigdetect a PID's existing row under a different basename and rename + prune in one write; newRefreshRegistry()is called fromonProjectPostSaveso the registry walks forward even when Envoy doesn't restart. Bridge-side defensive layer:find_latest_versioned_toe()strips<digits>.toeand finds the highest-versioned sibling on disk;resolve_toe_path()now reads frominstances[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 acrosstest_envoy_registry(7) andtest_envoy_bridge(13), one updated for the v5.0.399 instance-specific guard.5.0.399: New
edit_dat_contentMCP tool for token-efficient surgical text edits β mirrors Claude Code's Edit tool (old_string/new_string, unique match by default, opt-inreplace_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_tdswapped the blanket "any TD running" guard for an instance-specific check, macOSopen -nflag 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 fortest_set_dat_content_clearthat 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 withEnsureCatalogs(), which setsStatus='Scanning defaults (X/N)'to show progress. The old gateif Status != 'Enabled': returnreturned early on that transient value, so_pending_envoy_promptwas 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_wipeguardrail onset_dat_contentMCP tool blocks accidental DAT wipes from malformed agent calls (refusestext="",rows=[], orclear=Truewith 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.jsonnow byte-stable across saves via sorted iteration of_PERSISTED_PARAMS+sort_keys=True(issue #18); test debt cleanup of 28 orphan.txtfiles, 3 stale envoy_bridge stubs replaced with 6 real tests, ancestor_rename tearDown leak fixed, palette tdn_reconstruction tests aligned with current production contract5.0.393: Harden Envoy bootstrap so silent failures surface a useful textport message instead of
No module named 'mcp.server'β_setupEnvironmentnow returnsbool, four previously-silent failure paths log explicit errors,Start()aborts before_runServerif deps aren't ready, and a finalimport mcp.servergate catches partial installs (issue #17)5.0.392: Critical Windows-only fix β
subprocess.runfrom inside TD raised[WinError 50] The request is not supportedbecause TD's GUI process stdin handle isn't duplicatable, causing Embody's venv-verify to falsely flag healthy venvs as corrupt andshutil.rmtreethem on every TD restart. Fixed by passingstdin=subprocess.DEVNULLon the 5 affectedsubprocess.runsites in the bootstrap and verify-venv paths5.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 failures5.0.386: Batch-confirm prompt for duplicate path detection β when multiple groups remain unresolved, one dialog offers
Auto-resolve all/Review individually/Dismissinstead of a separate modal per group5.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 column5.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
Moveno 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_tdnMCP tool (~20-90Γ fewer tokens thanget_opwalks), combined DAT+storage Content Safety dialog with "Never Ask" footgun removed, palette detection fix for nativebuttonCOMPoperators, migration nudge for upgrading users, docs + landing page rewrite5.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 resolution5.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,
/localpath prohibition, TD connectivity recovery5.0.258: Multi-instance Envoy support,
switch_instancetool, auto-suffix collision avoidance5.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_tdmeta-tool, local MCP handshake, operator overlap warnings5.0.233: Project-level performance monitoring,
/validatecommand, test runner dialog fix5.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 workflow5.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
This server cannot be installed
Maintenance
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