Your data, AI-ready in under five minutes.
Thunderduck reads your schemas, infers what every column means, and serves the result over a native MCP server. Claude, ChatGPT, any MCP client or your own agents connect through OAuth — each person's own identity, read-only by default, and every query priced before it runs.
Three steps, and none of them is a data model.
Hand over one credential
Add a connection in the console — Postgres, MySQL, BigQuery, a bucket on S3, GCS or Azure, or an Iceberg catalog. A read-only role is enough, and is what we recommend. Credentials go straight to a secrets manager; nothing that touches your data ever holds them in plain text.
Catalogs discover themselves
Connecting is the whole setup step. Thunderduck walks the source and records every schema, table, column, type and row count — no modelling, no YAML, no pipeline. Add a source later and the same thing happens again.
Connect your agent, once
Add one MCP endpoint to Claude, ChatGPT or your own client and sign in with OAuth. Each person connects as themselves — their own queries, their own audit trail — and every tool is read-only unless you grant the write scope. No shared API key pasted into a chat window.
{
"mcpServers": {
"thunderduck": {
"url": "https://mcp.thunderduck.ai/mcp"
}
}
}Endpoint and auth format are being finalized — talk to us for early access.
An agent that knows what your columns mean.
A schema tells an agent that a column is a BIGINT called `cust_ref`. That is not enough to answer a question about customers. So every column gets read once, described in a sentence, and tagged — and you stay in charge of the result.
Keys and references — what joins to what.
Something to group or filter by.
Something to sum or average, with its unit where there is one.
The time axis, so "last quarter" resolves to a column.
Free text: searchable, not summable.
Personal data, flagged separately from the role so it can be treated separately.
Inferred, then confirmed — and a refresh can't undo your edit.
- Every annotation carries its provenance: inferred, confirmed, or edited by a person.
- Correct a description and it becomes yours. The next refresh will not overwrite it — the upsert refuses to touch anything a human has signed off.
- A column whose type or name changes is marked stale rather than silently re-guessed, so you can see what moved.
- Nothing is inferred twice: tables sharing a shape reuse one inference, so a refresh that changes nothing costs nothing.
Judge, score, and classify rows — in plain English, in SQL.
Some questions aren't a GROUP BY. “Which of these tickets is an angry customer?” doesn't have a column for it. jev() turns a sentence into a WHERE clause, jev_prob() turns it into a ranking, and jev_choice() turns it into a label you name — all as ordinary SQL functions, powered by TypeSafe's Jev model.
A boolean, so it composes with the rest of SQL
jev() answers a yes/no question about a row in plain English and hands back true or false — nothing to parse, nothing to cast. Drop it straight into a WHERE clause, an AND alongside your other filters, a JOIN condition, or a CASE, exactly where a column would go if the thing you were asking had one. It's the right tool whenever the filter you want is a judgment call rather than a value already sitting in a column — "is this customer frustrated," "does this transcript mention a competitor," "is this address in the EU" — questions a LIKE or a lookup table can't answer, but a sentence can.
The probability behind the judgment
Every jev() call has a confidence score underneath it, and jev_prob() is how you get at it directly — a number between 0 and 1 instead of a flattened true/false. That's what you want the moment a judgment isn't binary in practice: ranking a queue by how urgent each ticket sounds, sorting leads by purchase intent, or picking your own cutoff instead of trusting a fixed 0.5 line someone else drew. Add it as an ordinary SELECT column — next to the rest of the row, so you can sort, bucket, or chart it like any other number.
One of n, per row
jev_choice() is for when the question has more than two answers: give it a sentence and a short list of labels you define, and it returns the one that fits each row best. Unstructured text — a transcript, a support message, a free-text survey response — becomes a categorical column you can GROUP BY, join on, or chart, without standing up a training pipeline or hand-labeling a dataset first. Routing tickets to a team, tagging the topic of a conversation, bucketing open-ended feedback into themes — anywhere you'd reach for a case statement if only the input were already structured.
Bring your own TypeSafe API key. Off until you set one — nothing is sent anywhere by default. Calling these functions sends the matched row's contents to TypeSafe's API to produce the judgment, so point them at what you're comfortable sharing.
Let agents explore. Cap what they can spend.
- Pre-flight cost checks: every agent query gets a cost and runtime estimate before execution; queries above budget are blocked or rerouted, not billed.
- Budgets per agent, per user, per workspace.
- Replica-only enforcement and per-source concurrency caps — agents can never touch production OLTP paths.
- Full audit log: every agent query, its estimate, its actual cost.
Curiosity is free. Full scans aren't. We check first.
Warehouse MCP servers expose one silo, meter after execution, and require your operational data to be ETL'd in first. Thunderduck exposes all your sources in one catalog, checks cost before execution, and needs no data movement.
| Warehouse MCP server | Thunderduck | |
|---|---|---|
| Sources covered | One silo | All your sources, one catalog |
| Cost check | Metered after execution | Checked before execution |
| Data movement | ETL your data in first | None — data stays put |
| Setup time | A warehouse project | About 5 minutes |
| Runs on-prem | Cloud-only | SaaS, your cloud, or air-gapped |