{"slug": "how-to-build-an-ai-context-layer-for-your-data-warehouse", "title": "How to Build an AI Context Layer for Your Data Warehouse", "summary": "Bruin Data released a free, open-source method to build an AI context layer for data warehouses, using the Bruin CLI and an AI coding agent to generate YAML files that describe tables, columns, and data quality checks. The approach, detailed in a guide, addresses the common failure of text-to-SQL agents on real schemas by providing executable, reviewable metadata in git, with commands like `bruin import database` and `bruin ai enhance`. The context layer answers four key questions an agent needs before writing SQL, and requires only read access to the warehouse and an installed AI CLI such as Claude Code, Codex, OpenCode, or Cursor.", "body_md": "**Quick answer:** to give an AI agent real context about your warehouse, install the [Bruin CLI](https://github.com/bruin-data/bruin), map your tables into local files with `bruin import database`\n\n, then run `bruin ai enhance`\n\nto fill those files with descriptions, data quality checks, and tags. Both commands are free and open source, they run on your machine, and the result is a folder of plain YAML you can review in a pull request. You do not need to be a Bruin user, and you do not need to move your pipelines.\n\nEvery text-to-SQL demo works on the demo schema. Then you point the same agent at a real warehouse with 400 tables, five naming conventions, and a `status`\n\ncolumn that stores integers, and the answers quietly go wrong.\n\nThe model is not the problem. The problem is that the agent can see your schema and nothing else. It knows a column is called `gmv`\n\nand typed `DECIMAL`\n\n. It does not know that `gmv`\n\nexcludes refunds, that `status = 3`\n\nmeans refunded, that `orders.total_amount`\n\ndoes not reconcile with the line items, or that `country_code`\n\nis ISO alpha-2 rather than a free-text country name.\n\nThat missing information is the context layer. This guide builds one in two commands.\n\nA directory of YAML files, one per table, that looks like this:\n\nThree things make this useful rather than decorative:\n\n**It is text in git.** Reviewable in a PR, greppable, diffable, and readable by any agent that can open a file. No catalog UI, no API, no export step.**The checks are executable.** `not_null`\n\n, `unique`\n\n, and `accepted_values`\n\nare not comments. Bruin can run them against the table, so the documentation gets tested instead of rotting.**It is yours.** Wrong description? Edit the line. There is no metadata service that owns the truth and no vendor that has to agree with you.\n\nAn agent writing SQL needs to answer four questions before it writes a line:\n\n- Which table has this?\n- What does this column actually mean?\n- What values are legal, and what do they encode?\n- How do these tables join?\n\nA raw `INFORMATION_SCHEMA`\n\ndump answers question one, badly, and none of the others. So the agent guesses. Guessing produces SQL that runs, returns a number, and is wrong - which is the worst failure mode available, because nothing errors and someone puts the number in a deck.\n\nThe context layer answers all four in a form the agent reads before querying. This is the same reason coding agents work well on code: the metadata and the artifact live in the same repository, in text.\n\nYou need three things.\n\n**1. The Bruin CLI.** Open source, Apache 2.0, single binary:\n\nVerify it:\n\n**2. An AI coding CLI.** `bruin ai enhance`\n\ndrives an agent you already have installed. Any one of these works:\n\n| Provider | Install | Flag |\n|---|\n| Claude Code | `curl -fsSL https://claude.ai/install.sh | bash` | `--claude` |\n| Codex | See the [Codex docs](https://developers.openai.com/codex/cli) | `--codex` |\n| OpenCode | See [opencode.ai](https://opencode.ai) | `--opencode` |\n| Cursor | `cursor-agent` CLI | `--cursor` |\n\nBruin auto-detects what is installed, so the flag is only needed when you have more than one and want to pick.\n\n**3. Read access to your warehouse.** `SELECT`\n\non the schema you want to map, plus permission to read its metadata. That is it - no write access, no admin role.\n\nYou are not building a pipeline, so skip the platform-specific templates. An empty project is all the context layer needs:\n\nThat gives you a `bruin/`\n\nfolder containing `ai-analyst/`\n\n- a `pipeline.yml`\n\nwith everything commented out, and an `assets/`\n\ndirectory waiting to be filled.\n\nTwo paths matter for the rest of this guide, and mixing them up is the most common mistake:\n\n`.bruin.yml`\n\nlives at the project root and holds credentials. It does not exist yet; it is created the first time you add a connection or validate the project, and it is gitignored.`ai-analyst/`\n\nis the pipeline folder. It contains `pipeline.yml`\n\n, and it is the path you pass to `import`\n\nand `enhance`\n\n.\n\nAdd a connection with the interactive wizard:\n\nIt asks for the connection type, a name, and the credentials that type needs, then writes `.bruin.yml`\n\nfor you. Or create the file yourself - it is small:\n\nConfirm it works before going further:\n\nIf you do not know which schema you want, ask the warehouse:\n\nThis is the first half of the context layer: turn every table into a file.\n\nThe result is one file per table, organised by schema:\n\nEach file is a skeleton with the table name, the asset type derived from your connection, and every column with its real database type:\n\nAccurate, and still not useful to an agent. That is expected - this step is the inventory, not the knowledge.\n\n| Flag | What it does |\n|---|\n`--connection` , `-c` | Connection name from `.bruin.yml` . Omit it to pick from an interactive list. |\n`--schema` , `-s` | Import a single schema. Start here. |\n`--schemas` | Repeat per schema (`--schemas raw --schemas analytics` ). BigQuery only. |\n`--no-columns` , `-n` | Skip column metadata. Faster, and much less useful. |\n`--ingestr` | Generate runnable [ingestr](https://github.com/bruin-data/ingestr) assets that replicate the source instead of metadata-only placeholders. |\n`--destination` | Destination platform for `--ingestr` assets, for example `duckdb` . |\n`--environment` , `--env` | Target a specific environment from `.bruin.yml` . |\n\nSupported sources: Snowflake, BigQuery, PostgreSQL, Redshift, Athena, Databricks, DuckDB, ClickHouse, Azure Synapse, MS SQL Server, and MongoDB.\n\n**Start with one schema.** Import your most-queried schema, run the next step, read the output, and decide whether you like it before pointing this at 400 tables.\n\nNow the second half - filling those skeletons with meaning:\n\nSet `--model`\n\non the first run. The CLI's built-in default for Claude Code is `claude-sonnet-4-20250514`\n\n, which has reached end of life, so the bare command currently fails on every asset with `There's an issue with the selected model`\n\n.\n\nYou can point it at a whole pipeline folder, a single schema folder, or one file. It processes 5 assets in parallel by default.\n\nEach asset goes through the same stages, and the command narrates them:\n\n(The step numbering in that output is inconsistent, which is cosmetic.) What actually happens per asset:\n\n**Fill columns** - re-reads the schema and adds any column that is missing from the file.**AI enhancement** - queries the warehouse for column statistics (row counts, null counts, distinct counts, min/max ranges), then hands the schema plus those statistics to your AI CLI to write descriptions, checks, and tags.**Format** - normalises the YAML.**Validate** - parses the result. If the AI produced something invalid, the file is reverted rather than left broken.\n\nThen a summary:\n\nExpect a few minutes for 15-20 tables, and 10 minutes or more for 50+.\n\n| Flag | What it does |\n|---|\n`--model` | Pick the model, for example `--model claude-sonnet-5` . Set it on the first run: the built-in default for Claude Code is `claude-sonnet-4-20250514` , which has reached end of life. |\n`--claude` / `--codex` / `--opencode` / `--cursor` | Force a provider when several CLIs are installed. |\n`--concurrency` | Assets enhanced in parallel. Default 5. Lower it if you hit rate limits. |\n`--system-prompt` | Append your own instructions to the default enhancement prompt. |\n`--environment` , `--env` | Target a specific environment. |\n`--output` , `-o` | `plain` or `json` . |\n`--debug` | Print the full agent conversation. Use this when output looks wrong. |\n\n`--system-prompt`\n\nis the underrated one. It is where your house rules go:\n\nHere is real output from the run above, on the `orders`\n\ntable, trimmed for length. Nothing in the input file said anything beyond column names and types:\n\nThree things happened there that a schema dump cannot do.\n\nIt **inferred the join graph** - `customer_id`\n\ngot a `foreign_key`\n\nblock pointing at `ecommerce.customers`\n\n, from naming and cardinality alone.\n\nIt **found a real data problem**. `total_amount`\n\ndoes not equal the sum of its line items in this dataset. The AI checked, noticed, and wrote the caveat into the description with instructions on which table to trust for which question. An agent reading this will not silently produce two different revenue numbers depending on which table it picked.\n\nIt **encoded the enum**. `status`\n\ngot an `accepted_values`\n\ncheck listing the four states it observed, so both the agent and the pipeline now know what is legal.\n\nBruin pre-fetches statistics so check selection is grounded in the data rather than in vibes:\n\n| Check | When it gets applied |\n|---|\n`not_null` | Columns with zero nulls observed, IDs, required fields |\n`unique` | Distinct count equals row count |\n`positive` / `non_negative` | Amounts, prices, quantities, counts |\n`accepted_values` | Low-cardinality enum-like columns: status, type, category |\n`pattern` | Formatted strings such as emails |\n`min` / `max` | Numeric columns with a clear observed range |\n\nThe generated context is a strong first draft written by something that has never spoken to your finance team. Read it.\n\n**The **`unique`\n\ntrap. A column can be unique in today's snapshot and not unique by design. `ticker`\n\nin a quarterly financials table is the classic case: unique in a single-quarter extract, one row per quarter forever after. Bruin only adds `unique`\n\nwhen the statistics support it, which is exactly why a coincidence can slip through. Delete any `unique`\n\ncheck that is true by accident.\n\n**Business meaning it cannot know.** The AI can tell that `status`\n\nhas four values. It cannot tell you that `refunded`\n\nis set by a nightly job with a 24-hour lag. Add that yourself.\n\n**Silent renames.** If two teams both maintain a `revenue`\n\ncolumn with different definitions, the AI will describe each in isolation. Reconciling them is a human decision. This is where a [glossary](https://getbruin.com/docs/bruin/getting-started/glossary.html) earns its keep: define the entity once and have assets `extends`\n\nit.\n\nEditing is cheap because these are files. Fix the line, commit, move on.\n\nThen prove the documentation is true:\n\nIf you have write access to run checks, `bruin run`\n\nexecutes them against the real tables. A `not_null`\n\ncheck that fails is documentation caught lying, which is the entire point of making the checks executable.\n\nRe-run the same command after a schema change:\n\nIt is additive, not destructive. Existing descriptions and checks are left alone - a second pass on our demo pipeline reported `No changes made.`\n\nfor the table it had nothing to add to, and for the others it only filled in fields it had skipped the first time, such as `meta`\n\n, `domains`\n\n, and `primary_key`\n\n. Nothing was duplicated and nothing human-written was overwritten. Your edits survive.\n\nThat property is what makes this CI-friendly. A weekly job that runs `import database`\n\nfollowed by `ai enhance`\n\nand opens a PR with the diff turns documentation drift into a reviewable change instead of a slow decay. Because the output is YAML, the diff is readable: a new column shows up as a new column, not as a re-rendered catalog page.\n\nThe context layer is useful the moment it exists - any agent with filesystem access can read `assets/`\n\n. To let it query as well, Bruin ships an MCP server:\n\nRegister it with your agent. For Claude Code:\n\nOr in a `mcp.json`\n\nfor Cursor and friends:\n\nNow the agent reads the context layer from the repository and runs queries through Bruin's connections, so credentials stay in `.bruin.yml`\n\nand never reach the model. One more useful command while you are here:\n\nThe skill name is required - a bare `bruin ai skills`\n\nopens an interactive picker and fails outright when the terminal is not interactive. `all`\n\ninstalls the full set into `.agents/skills`\n\nand writes an `AGENTS.md`\n\nat the project root, teaching your coding agent how Bruin projects are structured.\n\nIf you would rather follow this as a guided tutorial with per-warehouse setup for BigQuery, Snowflake, Redshift, Databricks, ClickHouse, Postgres, and SQL Server, work through [Build an AI Context Layer](/learn/ai-context-layer) in Bruin Academy.\n\nThe two steps here are steps one and two of building your own AI data analyst. The full path - connections, context, agent setup, and the harder context problems - is written up in the [AI data analyst course](/learn/ai-data-analyst), and the reasoning behind open-sourcing it is in [Building an AI Data Analyst Sucks](/blog/build-your-own-ai-data-analyst/).\n\n`error: There's an issue with the selected model`\n\nYou dropped `--model`\n\n, so the CLI fell back to `claude-sonnet-4-20250514`\n\n, which has reached end of life. Add `--model claude-sonnet-5`\n\nback, and on Codex or OpenCode use that provider's model name instead. Check that you are on a recent CLI too: `bruin --version`\n\n.\n\n`No AI CLI detected`\n\n`bruin ai enhance`\n\nneeds one of Claude Code, Codex, OpenCode, or Cursor's `cursor-agent`\n\non your `PATH`\n\n. Install one, confirm with `which claude`\n\n, then re-run.\n\n`unknown command \"enhance\"`\n\nThe command is `bruin ai enhance`\n\n. The `ai`\n\nsubcommand is easy to drop.\n\n**Import fails with permission denied**\n\nThe connection needs `SELECT`\n\non the target schema and access to its metadata. For BigQuery that means two roles, not one: `BigQuery Data Viewer`\n\non the dataset plus `BigQuery Job User`\n\non the project, because both `bruin query`\n\nand `ai enhance`\n\nsubmit query jobs. Data Viewer alone reads metadata but cannot run a query.\n\n`--schemas`\n\ndoes nothing\n\nIt only works on BigQuery, and it repeats rather than taking a comma-separated list: `--schemas raw --schemas analytics`\n\n. On other warehouses, run `--schema`\n\nonce per schema into the same pipeline folder.\n\n**It hangs on a large schema**\n\nEnhance is doing real work per asset. Lower `--concurrency`\n\nif you are hitting rate limits, split the work by schema folder, and remember you can re-run safely.\n\n**Descriptions are confidently wrong**\n\nThree fixes, in order of leverage: pass your rules with `--system-prompt`\n\n, edit the file, or write an `AGENTS.md`\n\ntelling the agent how to interpret the ambiguous parts.\n\nTwo commands, a warehouse you can read, and roughly ten minutes of waiting produce a context layer that lives in git, gets reviewed like code, and carries executable checks. Whatever you point at it next - Claude Code, Cursor, a Slack bot, your own agent - starts from what your data means rather than from what its column names look like.\n\nThe tooling is [open source](https://github.com/bruin-data/bruin) and runs locally, so the cost of finding out whether your agent gets better is one schema and one afternoon.\n\nAn AI context layer is a machine-readable description of your tables: what each table represents, what every column means, which values are valid, and how tables relate. An AI agent reads it before writing SQL, so it stops guessing at column names and business meaning. With Bruin the context layer is a folder of plain YAML asset files in your git repository, which means you can review it in a pull request and edit it with any text editor.\n\nInstall the free, open-source Bruin CLI, import your schema with `bruin import database --connection <name> --schema <schema> <pipeline-path>`\n\n, then run `bruin ai enhance <pipeline-path>`\n\n. The enhance command pulls column statistics from your warehouse and uses your local AI CLI - Claude Code, Codex, OpenCode, or Cursor - to write descriptions, data quality checks, and tags directly into each asset file.\n\nYes. The Bruin CLI is open source under Apache 2.0, and both `bruin import database`\n\nand `bruin ai enhance`\n\nrun locally with no Bruin account, signup, or credit card. You need one AI coding CLI installed and you pay your AI provider for the tokens it uses.\n\nIt sends schema plus aggregate column statistics such as row counts, null counts, distinct counts, and min/max ranges, along with the small samples needed to infer things like accepted values. It does not replicate your tables. Everything goes through the AI CLI already installed on your machine, so the traffic path is the one your coding agent already uses.\n\nSnowflake, BigQuery, PostgreSQL, Redshift, Athena, Databricks, DuckDB, ClickHouse, Azure Synapse, MS SQL Server, and MongoDB. The generated asset type follows the connection, for example `sf.source`\n\n, `bq.source`\n\n, or `pg.source`\n\n.\n\nNo. Import and enhance only read warehouse metadata and write YAML into a local folder. Keep dbt, Airflow, Fivetran, or hand-written SQL exactly where they are and use the context layer purely as documentation for your AI agent. If you later want the checks to run on a schedule, that is what `bruin run`\n\nand Bruin Cloud are for.\n\nA catalog stores metadata in a hosted service and exposes it through a UI and an API, which means an agent needs an integration to read it and a human needs a login to fix it. This context layer is text files next to your code: agents read them directly, humans edit them in a PR, and the quality checks are executable rather than descriptive.", "url": "https://wpnews.pro/news/how-to-build-an-ai-context-layer-for-your-data-warehouse", "canonical_source": "https://getbruin.com/blog/build-ai-context-layer-data-warehouse/", "published_at": "2026-08-21 00:00:00+00:00", "updated_at": "2026-08-26 14:15:17.231401+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "artificial-intelligence"], "entities": ["Bruin Data", "Bruin CLI", "Claude Code", "Codex", "OpenCode", "Cursor"], "alternates": {"html": "https://wpnews.pro/news/how-to-build-an-ai-context-layer-for-your-data-warehouse", "markdown": "https://wpnews.pro/news/how-to-build-an-ai-context-layer-for-your-data-warehouse.md", "text": "https://wpnews.pro/news/how-to-build-an-ai-context-layer-for-your-data-warehouse.txt", "jsonld": "https://wpnews.pro/news/how-to-build-an-ai-context-layer-for-your-data-warehouse.jsonld"}}