AI assistants
Column meanings

Column meanings

Column names are abbreviations. amt_c is an integer to a database and "order total in cents, excluding tax" to a person, and an assistant that only sees the first will write plausible SQL that returns a number a hundred times too large.

Thunderduck keeps a description, a role and a unit for each column, serves them alongside the schema, and lets you correct them.

What an annotation holds

FieldWhat it is
descriptionOne sentence on what the column means
roleidentifier, dimension, measure, timestamp, text or unknown
unitcents, usd, seconds, bytes — where a number is meaningless without one
is_piiWhether the column holds personal data
join_candidatesColumns this one probably joins to, as {table, column} pairs
provenanceWhere the meaning came from — see below
staleThe table's structure changed after the annotation was written

Provenance

The field an assistant should weigh most heavily.

ValueMeaning
inferredA model's guess from the column names and types. Useful, and wrong often enough to be worth checking.
confirmedA person read the guess and agreed with it.
editedA person wrote the description themselves.

Inference will overwrite its own guesses on any later refresh. It will never overwrite confirmed or edited — once you have been through a column, it is yours.

When a table's structure changes, annotations a person owns are marked stale rather than re-inferred. A column that gained a sibling has not necessarily changed meaning, and silently replacing your text on a schema tweak is the fastest way to make a semantic layer untrustworthy. The Tables page shows these as needs review, keeping the existing description.

What is sent for inference

Descriptions are inferred by Claude (opens in a new tab), and what it receives is bounded by an allowlist rather than by redaction:

  • the catalog type, the schema and table name
  • each column's name and type
  • unique index definitions, as a hint at what identifies a row

No data values are sent — not a sample row, not a minimum or maximum, not a distinct count. The row count is not sent either, and is deliberately excluded from the change fingerprint so that a table merely growing does not trigger re-inference.

Only tables whose structure changed since the last pass are sent, so a refresh on an unchanged catalog costs nothing. Object-storage catalogs (s3, gcs, r2, azure) are skipped: their "tables" are files, with no schema to reason about.

Bring your own Anthropic key

The metadata above runs through Thunderduck's own Anthropic account by default. Some companies consider their table and column names sensitive in their own right — not customer data, but still their own naming conventions, not something they want flowing through a shared account.

Set ANTHROPIC_API_KEY under Settings → Environment variables and every future inference for your account runs through your own Anthropic account and agreement instead. We verify the key works the moment you save it — a typo or a revoked key is rejected immediately, not discovered later as a quietly empty Tables page.

What's sent is identical either way (the same allowlist above — schema only, never data values); this only changes whose account processes it. Your inferences also never enter the cross-tenant template cache every other company's enrichment shares: nothing inferred through your key reaches another company, and nothing cached through another company's key is ever served to you. If your key stops working later, enrichment is skipped for your account until it's fixed — it never silently falls back to the shared key.

Reading them

Over MCP, from Claude or ChatGPT:

  • describe_table returns the annotations inline with the schema, under each column's semantics key.
  • get_table_semantics returns the meanings alone, for an assistant that already holds the schema.
  • The resource thunderduck://semantics/{catalog}/{schema}/{table} carries the same data for clients that browse resources instead of calling tools.

A column nobody has annotated simply has no semantics key.

In the console, Studio carries the same meanings where you write SQL: hover a table in the catalog tree for its columns, then hover a column for what it means, with its provenance. Tables shows a whole table's meanings at once, and is where they are corrected.

join_candidates names are unqualified guesses, not resolved references: inference sees one table at a time, so it can suggest that theme_id joins themes.id without ever having been shown a themes table. Treat them as a place to look, and confirm with list_tables.

Correcting them

Inferred meanings are served as they are — nothing waits on your approval, and a catalog you never visit is still described to an assistant.

Tables in the console shows every column's meaning with its provenance. Confirm records that the guess was right, Edit replaces the text and records that a person wrote it, and Confirm all signs off on a whole table at once. All three clear needs review.

Confirming is optional, and it is not busywork: it is the difference between an assistant treating a description as a hint and treating it as a fact. Editing matters more — a confirmed guess and a person's own sentence are different claims, and only the second can say something the schema does not.