Skip to main content
Glama
johnnyvincentvitale

touchdesigner-manual

touchdesigner-wiki-rag

Search the entire TouchDesigner documentation from your AI agent — with real citations.

This tool builds a local search index over the official TouchDesigner wiki (docs.derivative.ca, ~2,270 articles) and exposes it to AI agents over MCP (Claude Code, Claude Desktop, Cursor, and any other MCP client). Every answer carries a Page § Section citation with a clickable URL, so you can verify what the agent tells you instead of trusting a model's memory of TouchDesigner — which is exactly how you end up with hallucinated parameter names.

You build the index yourself, on your machine, from the live wiki. This repo ships no documentation content — the docs belong to Derivative and are fetched by each user directly from docs.derivative.ca. The build takes ~45 minutes of unattended fetching (politely rate-limited) plus a few minutes of indexing, and can run entirely free with no API keys.

Why not just let the agent browse the wiki?

Three reasons this beats live browsing: hybrid retrieval (exact-term BM25 + semantic vectors, fused and reranked) finds sections a keyword search misses; the index knows TouchDesigner's operator-family trap — Noise TOP, Noise CHOP, Noise SOP, and Noise POP are four different operators, and results warn when same-named siblings exist in other families; and answers are grounded in retrieved text with citations rather than model memory.

Related MCP server: OmniDocs MCP

Requirements

  • Python 3.12+ and uv

  • ~1.5 GB disk for the index

  • No API keys required for the default fully-local setup

  • Optional (better retrieval): an OpenAI key for stronger embeddings (about $1–2 one-time build cost) and HyDE query expansion; a Cohere key for stronger reranking (about $2 per 1,000 searches; free trial keys work)

Quickstart

git clone https://github.com/johnnyvincentvitale/touchdesigner-wiki-rag
cd touchdesigner-wiki-rag
uv sync
cp .env.example .env        # optional: add API keys; works fine empty

uv run python fetch.py      # ~45 min, fetches the wiki via its public API
uv run python ingest.py --rebuild   # chunks + embeds + indexes

# try it from the terminal
uv run python query.py "how do I add noise to a texture"

The embedding backend is chosen at build time — local bge-base (free, no account) by default, OpenAI text-embedding-3-large if OPENAI_API_KEY is set — and stamped into the index so queries always use the matching model. See .env.example for the details.

Already have the docs? Skip the fetch

TouchDesigner installs bundle an Offline Help mirror of the wiki. If you have it, import it instead of fetching (seconds instead of ~45 minutes):

# Windows (default install location):
uv run python fetch.py --offline-help "C:/Program Files/Derivative/TouchDesigner/Samples/Learn/OfflineHelp/https.docs.derivative.ca"

# then optionally pull only the pages edited since your mirror was generated:
uv run python fetch.py --update

uv run python ingest.py --rebuild

Notes: the mirror snapshots the wiki at your TouchDesigner release date — --update tops it up from the live API. Recent macOS builds ship the Offline Help folder empty (a known TouchDesigner issue), so Mac users should use the plain API fetch.

Configuration — what goes in .env

Nothing is required. With an empty (or absent) .env, everything runs on free local models. Keys only upgrade individual stages:

Variable

What it enables

Without it

OPENAI_API_KEY

text-embedding-3-large embeddings (stronger dense retrieval) + HyDE query expansion

local bge embeddings; HyDE disabled

COHERE_API_KEY

Cohere Rerank scoring

local ms-marco cross-encoder reranks

EMBED_BACKEND

force local or openai embeddings explicitly

inferred: openai if a key is set, else local

EMBED_MODEL

different OpenAI embedding model (e.g. -3-small, cheaper/weaker)

text-embedding-3-large

COHERE_RERANK_MODEL

different Cohere rerank model

rerank-v3.5

HYDE_MODEL

different HyDE model

gpt-4o-mini

OPENAI_BASE_URL

route OpenAI-client calls to any compatible server (see below)

api.openai.com

Two behaviors worth knowing: the embedding backend is stamped into the index at build time and queries auto-detect it, so you can't accidentally query with the wrong model — but switching backends means rebuilding (ingest.py --rebuild). And every API stage degrades visibly, never silently: no Cohere key falls back to the local reranker, an unreachable embedding API drops that search to keyword-only and says so in the response.

Going fully local (including HyDE)

The default keyless setup is already local except HyDE, which simply disables itself. To run HyDE on a local model, point the OpenAI client at any OpenAI-compatible server — Ollama or LM Studio:

# .env
OPENAI_API_KEY=anything-nonempty        # satisfies the client; never sent anywhere real
OPENAI_BASE_URL=http://localhost:11434/v1
HYDE_MODEL=llama3.2                     # any model you've pulled locally
EMBED_BACKEND=local                     # keeps embeddings on bge — required!

EMBED_BACKEND=local is load-bearing here: without it, setting OPENAI_API_KEY flips embeddings to the OpenAI backend, which would then be aimed at your local server expecting a model it doesn't serve. A small local HyDE model is rougher than gpt-4o-mini, but HyDE only fires on low-scoring searches and only keeps its result when it scores better — so the downside is bounded. (This recipe follows the OpenAI SDK's documented OPENAI_BASE_URL behavior but hasn't been exercised against a live Ollama by the author — issue reports welcome.)

"Local" models still download once from Hugging Face on first use (no account needed) and are cached after that; the wiki fetch itself needs the network once. After the index is built, the keyless setup searches fully offline.

Hook it up to your agent

Claude Code:

claude mcp add --scope user touchdesigner-manual -- uv run --directory /ABSOLUTE/PATH/TO/touchdesigner-wiki-rag python server.py

Any other MCP client — stdio server config:

{
  "mcpServers": {
    "touchdesigner-manual": {
      "command": "uv",
      "args": ["run", "--directory", "/ABSOLUTE/PATH/TO/touchdesigner-wiki-rag", "python", "server.py"]
    }
  }
}

What the agent gets

Tier

Tool

Returns

L0

browse_manual(query, family, limit)

section map — cheap orientation, discovers the wiki's own vocabulary

L1

search_manual(query, family, limit)

reranked passages with Page § Section + URL citations

L2

read_page(title, section)

full page as markdown

Plus resources: manual://index (corpus stats and freshness) and manual://operators/{family} (operator roster per family).

family accepts TOP, CHOP, SOP, DAT, MAT, COMP, or POP to scope a search to one operator family.

How retrieval works

  1. BM25 over SQLite FTS5, Porter-stemmed — exact operator and parameter names

  2. Dense vector search over Chroma — paraphrased and conceptual questions

  3. Reciprocal Rank Fusion combines both rankings

  4. Rerank — Cohere Rerank if a key is configured, local ms-marco cross-encoder otherwise (automatic fallback, works offline)

  5. Adaptive HyDE — if the top rerank score is low (a reliable "retrieval missed" signal), an LLM writes a hypothetical answer in the wiki's vocabulary and retrieval re-runs on that. Fires only when needed, only with an OpenAI key, and only keeps the result if it scores better.

Two guardrails ship in every response: a family warning when a result's operator has same-named siblings in other families, and a low-confidence warning when scores suggest retrieval missed — so an agent reports "not found" instead of inventing an answer, and never concludes a feature doesn't exist just because retrieval came back empty.

Keeping the index fresh

The wiki changes continuously. Responses warn when pages have been edited since your index was built (checked against the wiki's recentchanges feed, cached hourly, skipped silently when offline). To update:

uv run python fetch.py --update     # re-fetches only pages edited since last fetch
uv run python ingest.py --rebuild   # rebuilds the index

Known issues

  • Every operator page carries an identical "Common Operator Info Channels" boilerplate section; those chunks can pollute results on hard queries.

  • The --offline-help importer is tested against synthetic MediaWiki static pages, not yet against a real TouchDesigner install's mirror (the author's macOS build ships it empty). If the import misses pages on your install, please open an issue with a sample HTML file.

  • Four title-variant pages (e.g. Palette:webRTC Ext vs Palette:WebRTC Ext) collide in the page cache on case-insensitive filesystems; their partner pages are indexed, so coverage impact is negligible.

  • Rerank/HyDE score thresholds were calibrated on a small held-out query set; they're adjustable in retrieval.py (THRESHOLDS).

  • ingest.py is full-rebuild-only; with OpenAI embeddings that re-costs ~$1–2 per rebuild. The local backend rebuilds for free.

Content ownership

All documentation content belongs to Derivative. This repository contains no wiki content — only code that fetches it from the public API into a private, local index, the same way a browser or TouchDesigner's built-in help fetches it. Please keep the polite rate limits in fetch.py intact.

License

MIT — see LICENSE. Applies to the code in this repository only, not to TouchDesigner or its documentation.

Available Tools

3 tools
browse_manualA

L0 - list page sections to orient before retrieving full passages.

Cheapest way to discover the wiki's own vocabulary — the best recovery move
when a search misses. Prefer this over search_manual when you need the
shape of a topic rather than a specific answer.

Args:
    query: Optional keywords to filter section headings and page titles.
    family: Optional operator family filter — TOP, CHOP, SOP, DAT, MAT, COMP, POP.
    limit: Maximum sections to list.
ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
queryNo
familyNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It effectively communicates that this is a cheap, non-destructive orientation tool ('Cheapest way', 'list sections'). It discloses what it filters by and discloses its return granularity (sections/headings, not full passages). The depth is good given zero annotation coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact, front-loaded with the core purpose, and every sentence earns its place. The strategy framing (cheapest, recovery move) and sibling comparison are woven into two tight lines, followed by a minimal Args block. Zero waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple 3-parameter browsing tool with an output schema present, the description is complete. It explains what it does, why to use it, when to prefer it over the key sibling, and clarifies all parameter semantics. The presence of an output schema relieves it from describing return values. Nothing material is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate for all three parameters. It does: 'query' gets keyword-filter semantics (section headings and page titles), 'family' gets an operator-family filter with the enumerated options, and 'limit' gets maximum-section semantics. This fully compensates for the 0% coverage, though exact value formats aren't elaborated.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states a specific verb+resource ('list page sections') with a defined scope ('to orient before retrieving full passages'). It distinguishes itself from siblings by positioning it as a discovery/browsing tool versus search_manual. The 'L0' prefix and vocabulary-discovery framing make its purpose unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly compares to search_manual ('Prefer this over search_manual when you need the shape of a topic rather than a specific answer'), giving a clear when-to-use directive. It also states the recovery use case ('best recovery move when a search misses'). It doesn't discuss when NOT to use it relative to read_page, but the when-to-use guidance relative to the key sibling is strong.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

read_pageA

L2 - read the full markdown of one wiki page, or one of its sections.

Use after search_manual or browse_manual to get surrounding context.
Titles are exact wiki titles, e.g. "Noise TOP", "CHOP Execute DAT",
"noiseTOP Class".

Args:
    title: Exact wiki page title.
    section: Optional section heading to return just that section.
ParametersJSON Schema
NameRequiredDescriptionDefault
titleYes
sectionNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses the L2 classification (suggesting a tiered access model), the exact-title matching behavior, and the section-scoping capability. It doesn't detail error behavior for nonexistent titles, but for a simple read with an output schema and no safety-critical behavior, this is largely sufficient. A 'read' operation's mutability is implicit in the name.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Every sentence earns its place: a one-line purpose, context positioning, title-format clarification with examples, and a compact args block. Zero waste, well front-loaded. The markdown structure mirrors the args list cleanly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read tool with an output schema, the description is nearly complete. It adds positioning among siblings, exact-title requirements, and section behavior. Minor gap: no statement about behavior when section doesn't exist or when title is invalid, and the L2 reference is unexplained, but overall the tool is adequately specified for selection and invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate and does. It explains that title must be an exact wiki title with concrete format examples, and explains section is optional and returns just that section. Both parameters get semantic context beyond bare names/null defaults. It doesn't specify section heading exactness rules, but the core semantics are well covered.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool reads the full markdown of one wiki page or one of its sections, using a specific verb ('read') with a clear resource ('wiki page'). It gives concrete example titles ('Noise TOP', 'CHOP Execute DAT') and explicitly distinguishes from sibling tools by referencing search_manual and browse_manual as preceding steps.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit usage guidance: 'Use after search_manual or browse_manual to get surrounding context.' It also establishes the exact-title matching requirement and gives examples of valid titles, which prevents common agent errors like fuzzy matching. No exclusions needed since the tool is a straightforward read.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_manualA

L1 - find passages answering a question, with wiki-URL citations.

Runs BM25 and dense retrieval, fuses with Reciprocal Rank Fusion, rescores
with a cross-encoder. If the top score signals a probable miss, a second
HyDE pass fires automatically. Handles exact operator/parameter names and
paraphrased questions alike.

Args:
    query: What you need to know.
    family: Optional operator family scope — TOP, CHOP, SOP, DAT, MAT, COMP, POP.
        Use it when the question is about one family's operator; same-named
        operators exist across families.
    limit: Maximum passages to return.
ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
queryYes
familyNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden, and it delivers: it discloses the multi-stage retrieval process, the automatic HyDE second pass when a miss is suspected, the citation output ('wiki-URL citations'), and that it handles both exact names and paraphrases. This is rich behavioral context well beyond what a schema would convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with a header line, retrieval-technique summary, and clear per-parameter annotations. Every sentence earns its place, technical detail is front-loaded, and the format is scannable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is complex (hybrid retrieval, fusion, rescoring, conditional HyDE), and the description covers this well. There is an output schema, so return-value explanation isn't required. The only slight gap is whether the 'L1' tag and end-user intent guidance could be clearer, but for a single-free-text-query retrieval tool this is quite complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate for all three parameters. It does well: query ('What you need to know'), family (with concrete enum-like values and usage guidance), and limit ('Maximum passages to return'). Only minor gap is no detail on limit's default or range, but overall the description adds strong meaning absent from the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description is specific: 'find passages answering a question, with wiki-URL citations' plus a 'L1' label. It clearly identifies the resource (manual/search passages) and the action (retrieve answers). Though siblings include browse_manual and read_page, the search/retrieve semantics are distinguishable from browsing or reading.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly explains the retrieval pipeline (BM25, dense, RRF fusion, cross-encoder rescoring), notes the automated HyDE fallback on probable misses, and tells when to use the family parameter ('when the question is about one family's operator; same-named operators exist across families'). This gives a clear context and exclusions for parameter usage, though it doesn't explicitly name when NOT to use this vs siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updatesv0.1.0
    • First observedbrowse_manual
    • First observedread_page
    • First observedsearch_manual

TDQS

A4.4/5.0

Scored across 3 tools

Disambiguation4/5

The three tools are mostly distinct: browse_manual for orientation/discovery, search_manual for finding passages, and read_page for reading full content. The browse_manual and search_manual could potentially overlap when searching for a specific topic (both filter by family and query), but the descriptions clearly differentiate them by cost tier and use case, so an agent can usually tell them apart.

Naming Consistency4/5

The tool names follow a consistent verb_noun pattern (browse_manual, search_manual, read_page), all snake_case. Slight inconsistency in that browse_manual and search_manual both target 'manual' while read_page targets 'page', but the pattern is predictable and readable overall.

Tool Count4/5

Three tools is a reasonable count for a documentation/manual retrieval server. It's on the lean side but each tool serves a distinct retrieval stage (discover, search, read). Given the narrow scope of a documentation lookup server, three well-chosen tools feel appropriate, though it borders on thin.

Completeness3/5

The core browse-search-read workflow is covered and represents the essential operations for a wiki documentation server. However, there are minor gaps: no way to list all pages in a family without a query, no direct tool for navigating to a specific page by title from scratch (read_page requires knowing the exact title), and no distinction between getting a section vs. full page could be improved. These are workable gaps but notable.

Maintenance

ActivityStale
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    B
    quality
    C
    maintenance
    A MCP server that allows you to search and retrieve content on any wiki site using MediaWiki with LLMs 🤖. wikipedia.org, fandom.com, wiki.gg and more sites using Mediawiki are supported!
    2
    27
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    An intelligent MCP server that enables AI agents to crawl, index, and semantically search official framework documentation using local RAG. It prevents hallucinations by providing precise, up-to-date documentation excerpts directly into the AI's context window.
    1
    -
  • A
    license
    B
    quality
    B
    maintenance
    An MCP server for TouchDesigner that lets AI agents inspect, build, wire, optimize, and stabilize live TD networks with 106 tools, plus a technique memory system for reusable patterns.
    100
    14
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    A local-first MCP server for TouchDesigner that provides offline documentation retrieval and live control of TouchDesigner sessions, enabling AI-assisted network building and parameter management.
    MIT