---
title: "Grounding an Agent in Your dbt Project: What It Reads, and What Changes"
slug: "t6-agent-grounding"
type: article
locale: en-US
canonical: "https://www.coremodels.io/connector/dbt/t6-agent-grounding"
updatedAt: "2026-09-28T18:42:53+02:00"
---

# Grounding an Agent in Your dbt Project: What It Reads, and What Changes

An agent pointed at your warehouse can already write SQL. The question is what it knows about the
columns it writes against, and the honest answer is: the names, the types, and whatever free text
happens to be in a description. Everything else it infers.

This guide connects an agent to governed meaning instead, and shows the difference in the answers.
It assumes you have imported a dbt project — `dbt parse`, post the manifest — and nothing else.

## What the agent actually connects to

Two endpoints, split by what they let an agent do:

| Endpoint | For | Role |
|---|---|---|
| `https://go.coremodels.io/mcp` | Reading the governed model | Viewer |
| `https://go.coremodels.io/mcp-admin` | Writing to it | Admin |

**Use `/mcp` for grounding. It is the whole point.** An agent should be able to look meaning up and
be unable to redefine it — a wrong answer is recoverable, a silently rewritten definition is not.
Nothing in this guide needs the admin surface.

Authenticate with OAuth2, or with an integration API key as a bearer token:

```
Authorization: Bearer <your CoreModels API key>
```

Issue the key from the recipe's third step, or from project settings later. It is scoped to the
project, and a Viewer-scoped key cannot write regardless of which endpoint it is sent to.

## The four questions worth wiring

Grounding is not "give the agent everything". A model context window filled with your whole estate
is worse than a name, because it buries the relevant fact. These four cover the real cases.

**1. What is in this project at all?**
`get_project_summary` returns the shape — types, elements, taxonomies, counts. This is the cheap
orientation call that stops an agent inventing a table that does not exist.

**2. What is this thing?**
`search_nodes` finds a column, a model or a vocabulary by name or partial match, and returns the
governed node with its description and the mixin values attached to it. This is where the recipe's
`Agent Guidance` columns arrive: `meaningNote`, `commonMistake`, `doNotUseFor`. They are ordinary
mixin values, so they come back with the node — no special tool, no separate lookup.

**3. What may this column contain?**
The vocabulary behind the column. Because an `accepted_values` test became a governed taxonomy at
import, the permitted values arrive as a list with structure and descriptions rather than as a
string array — including, where you set it, that `return_pending` sits *under* `returned` rather
than beside it.

**4. What else carries this concept?**
`analyze_concept_impact` answers "what depends on this?" for a column, model or vocabulary: where it
is used inside the governed model, which estates carry it and by what path, and what it is bound to
externally. An agent asked to change something should call this before it proposes the change.

## A grounded exchange, end to end

The prompt an analyst actually types:

> *"How many orders were returned last month?"*

**Ungrounded**, the agent has one move: guess that `status = 'returned'` is the filter, because the
column is called `status` and `returned` is a word that appears in the question. It writes confident
SQL and undercounts by however many orders sat in `return_pending`.

**Grounded**, the sequence is:

```
search_nodes            → the governed column `status` on `fct_orders`
                          description, and the Agent Guidance values you filled in
(vocabulary on it)      → placed · shipped · completed · return_pending · returned
                          with return_pending nested under returned
```

Now the agent has the fact that changes the query: **two values mean returned.** If you filled in
`commonMistake` on that column — *"`return_pending` is a return; filtering on `returned` alone
undercounts"* — it reads that sentence directly.

The answer comes back citing the vocabulary rather than asserting from the column name. That is the
whole difference: not a smarter model, a model that was told.

## Which twenty columns to write

Do not describe the estate. Describe the columns that get answered wrong, and nothing else.

A practical way to find them: look at the questions your team asks the assistant repeatedly, and the
ones where somebody had to correct it. Those columns — usually status-like enums, date columns with
a business meaning (`closed_at` is not always when it closed), and identifiers that look
interchangeable and are not — are the twenty that matter. The recipe opens on a grid where you can
fill all three guidance columns inline.

The three columns and what belongs in each:

- **`meaningNote`** — what the column means in business terms. Not a restatement of the name.
- **`commonMistake`** — the wrong assumption you have seen made. This is the highest-value field and
  the one people skip.
- **`doNotUseFor`** — the query this column looks right for and is wrong for. `created_at` on a
  `dim_customer` that is a slowly-changing dimension is the classic.

## Verifying it worked

Take a question the assistant used to get wrong. Ask it again, with grounding connected.

The check is not that the answer changed — it is **whether the agent cites something**. A grounded
answer names the vocabulary or quotes the guidance; an ungrounded one asserts. If the answer is
right but uncited, the agent guessed correctly this time, which is not the same as knowing.

## What this does not do

- **It does not connect to your warehouse.** The agent reads governed meaning from CoreModels and
  executes SQL wherever it already did. No credentials change hands here.
- **It does not make the agent's SQL correct.** It removes one class of error — inferred meaning —
  and leaves every other class exactly where it was.
- **It does not update itself.** The guidance columns are worth as much as the attention you put in
  them, and they go stale like any documentation. The review cadence on the ownership columns is
  there for that reason.
- **Read-only means read-only.** An agent on `/mcp` cannot edit a definition, and should not be able
  to. Curating meaning is a human act with a role behind it.

## Where to go next

The columns you just described want owners — the ownership guide covers making "who decides this?"
answerable. The dbt quickstart that ships with the CoreModels integration docs covers the import
this guide assumed.

## Recipes

- [Give your agent the governed meaning of `status`](https://www.coremodels.io/recipe/dbt/agent-grounding): An LLM writing SQL against your warehouse has three bad options: guess from the column name, read the SQL, or ask a human. Give it a fourth.
