Server data from the Official MCP Registry
Predicts the DDL a dbt change will execute, before you run it. Reads files, not the warehouse.
About
Predicts the DDL a dbt change will execute, before you run it. Reads files, not the warehouse.
Security Report
Valid MCP server (1 strong, 2 medium validity signals). No known CVEs in dependencies. Package registry verified. Imported from the Official MCP Registry.
7 files analyzed · 1 issue found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
Permissions Required
This plugin requests these system permissions. Most are normal for its category.
How to Install
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-presentjay-dbt-plan": {
"args": [
"dbt-plan"
],
"command": "uvx"
}
}
}Documentation
View on GitHubFrom the project's GitHub README.
dbt-plan
Static analysis tool that warns about risky DDL changes before dbt run.
Like terraform plan for dbt, and used the same way: you run it before the thing
that changes your warehouse, not only in CI afterwards.
Analyzes compiled SQL files from warehouses including Snowflake, BigQuery, Redshift, Postgres and DuckDB through one code path. SQL dialect support varies; see analysis limits.
What It Looks Like
$ dbt-plan check
dbt-plan -- 4 model(s) changed
dialect: snowflake (default; adapter: unknown)
baseline: unknown revision, unknown snapshot time
DESTRUCTIVE int_order_enriched (incremental, sync_all_columns)
ADD COLUMN billing_method
ADD COLUMN shipping_city
DROP COLUMN billing_info
DROP COLUMN shipping_info
Downstream: dim_customers, fct_daily_sales (2 model(s))
>> BROKEN_REF fct_daily_sales: reads dropped column(s): shipping_info
SAFE dim_customers (table)
CREATE OR REPLACE TABLE
SAFE dim_publishers (table)
CREATE OR REPLACE TABLE
SAFE fct_daily_sales (incremental, append_new_columns)
ADD COLUMN total_sales
dbt-plan: 4 checked, 3 safe, 0 warning, 1 destructive, 1 cascade risk(s)
What It Does
dbt-plan analyzes compiled SQL diffs to catch dangerous schema changes at PR time:
- Column changes: detects ADD/DROP COLUMN from SQL diff
- Risk assessment: judges safety based on materialization x on_schema_change rules
- Cascade analysis: finds downstream models broken by a dropped column — the ones that
name it, resolved against the project's own schema rather than matched as text; the ones
that select
*and lose it without their own file changing; and the tests whose fixtures pin it down. Names the exposures whose owners need telling - Contracts: reports a change an enforced contract will reject, in either direction
- Config changes: detects materialization or on_schema_change policy changes
- Type changes: compares explicit
CASTtypes between revisions SELECT *resolution: reads the columns from the CTEs of the same statement, and follows aref()into the referenced model's compiled SQL
The analysis reads files, compares them, and warns; it does not connect to a warehouse or simulate dbt run. The optional dbt-plan run workflow invokes your compile command, which can require credentials and execute macros.
Quick Start
pip install dbt-plan
dbt-plan run # compile baseline → compile current → check
dbt-plan run does the whole thing in one command, and needs whatever credentials your
dbt compile normally needs.
The loop it is built for
Once you have a baseline, the inner loop is a single sub-second command. Edit a model or a
macro, recompile, and see what dbt run would do — before running it:
dbt-plan snapshot # once, on the revision you are changing from
# ... edit models, edit macros ...
dbt compile && dbt-plan check
dbt-plan check reads local compiled artifacts. Its latency is separate from
dbt compile, which can depend on your adapter, macros, and warehouse.
See the reproducible CLI benchmark for measured workloads,
raw samples, and commands to measure your machine.
Working with a coding agent
An agent editing models cannot eyeball a diff and hesitate. Give it the check and the reasons behind it:
dbt-plan agent-setup # writes dbt-plan guidance into your AGENTS.md
dbt-plan check --format json
The guidance leads with what an agent most often gets wrong: adding a model to
ignore_models, or downgrading on_schema_change from sync_all_columns to ignore,
silences a real finding without making the change safe.
Or give it the check as an MCP tool:
pip install 'dbt-plan[mcp]'
dbt-plan-mcp # stdio MCP server exposing `plan` and `snapshot`
plan returns the verdict, the per-model operations, and — separately — a refusals
list naming everything dbt-plan declined to judge. That separation is the point: a person
reading "safe" may still glance at the diff, an agent reading it proceeds, so a
non-empty refusals must never be collapsed into the verdict.
Both tools accept target_dir when dbt writes artifacts outside the default target/,
for example plan(project_dir=".", target_dir="build").
The server is a separate package from the analysis core. The core is offline and
synchronous by design and tests/test_invariants.py fails the build on an asyncio or
network import anywhere inside it; an MCP server is both, so keeping them apart is what
keeps that guarantee provable.
Registry entry — the line below is how the MCP registry verifies that whoever publishes the entry also owns this PyPI package, so it has to stay in the README that ships:
mcp-name: io.github.PresentJay/dbt-plan
More commands
dbt-plan init # Generate .dbt-plan.yml config + update .gitignore
dbt-plan stats # Analyze project readiness
dbt-plan ci-setup # Generate GitHub Actions workflow
dbt-plan check --format github # GitHub markdown output
dbt-plan check --format json # JSON for CI pipelines
dbt-plan run --against main # compare with where this branch left main
dbt-plan check --select fct_orders # one model
dbt-plan check --select fct_orders+ # it and everything downstream
--select accepts model names and optional upstream/downstream + operators;
comma-separated terms form a union. In the next minor release, unsupported or
unknown selections fail with exit 3. Use explicit version names such as
fct_orders_v2. See the selection contract and examples.
Scope
dbt-plan is a static analysis warning tool, not a runtime simulator.
| In scope | Out of scope |
|---|---|
| Column ADD/DROP detection from compiled SQL | dbt run simulation |
| materialization × on_schema_change risk rules | Warehouse connection |
| Cascade: broken refs, build failures, inherited column loss | seed / source change detection |
| Config change detection (materialization, osc) | pre_hook / post_hook DDL analysis |
| Unit test fixtures and exposure owners downstream | seed / source fixtures dbt-plan cannot read |
| Enforced-contract violations: names, and types by family | Contract types compared more finely than family |
Explicit CAST type changes | Type changes on uncast columns |
SELECT * resolved through CTEs and ref() | SELECT * over a source or a raw table |
| CI exit codes + structured output | full_refresh mode judgment |
Design principle: false warnings are OK, false safe is never OK.
When to use it
dbt-plan answers a narrower question than the warehouse-connected tools (Recce, SQLMesh, data-diff) and costs nothing to run, so it works as the cheap gate in front of them — and on the Fusion engine, which compiles without a warehouse connection, that includes fork pull requests where they cannot run at all. See use cases for the comparison, real timings, and what it gets wrong.
Deliberately Not Planned
Ideas that look useful but contradict what this tool is:
| Idea | Why not |
|---|---|
| INFORMATION_SCHEMA query | Requires a warehouse connection. dbt-plan reads files and nothing else, which is what lets it run wherever its input exists — including a fork's pull request, once the project compiles on Fusion. |
Type changes on columns with no explicit CAST | The type is whatever the warehouse assigned, so seeing a change would mean asking it. Columns that are cast explicitly on both sides are compared — see below. |
DDL Prediction Rules
| Materialization | on_schema_change | Predicted DDL | Safety |
|---|---|---|---|
| table | any | CREATE OR REPLACE TABLE | SAFE |
| view | any | CREATE OR REPLACE VIEW | SAFE |
| ephemeral | any | (no physical object) | SAFE |
| snapshot | any | REVIEW REQUIRED | WARNING |
| incremental | ignore | no schema DDL; existing targets may fail on column changes | WARNING on changed/unknown columns |
| incremental | fail | build failure | WARNING |
| incremental | append_new_columns | ADD COLUMN only | SAFE |
| incremental | sync_all_columns | ADD + DROP COLUMN | DESTRUCTIVE if columns removed |
| any | (model removed) | MODEL REMOVED | DESTRUCTIVE |
| any | (unknown osc) | UNKNOWN on_schema_change | WARNING |
| materialized_view / custom | (not set by you) | UNKNOWN materialization | WARNING |
| materialized_view / custom | (you set one) | follows the incremental rules | per osc |
| any | (contract: {enforced: true}) | CONTRACT VIOLATION | WARNING |
Under a contract, a column's declared data_type is compared with its explicit CAST,
by family -- text against number against date/time against boolean. varchar and text
are the same family and not a finding; varchar and integer are a build failure.
Comparing more finely means a per-adapter type table, and a wrong answer about a type is
worse than no answer.
"Not set by you" means the author wrote no on_schema_change, in the model or in
dbt_project.yml. dbt resolves one for every model regardless, so the resolved value
asserts nothing; an explicit setting is a claim about how that materialization behaves
and is honoured. dbt-plan reads unrendered_config to tell them apart.
An enforced contract inverts the rules above: dbt requires every column to be declared,
so a column added to the SQL fails the build just as a removed one does. Names only —
dbt compares its declared data_type against the warehouse, which dbt-plan does not read.
CI Integration (GitHub Actions)
name: dbt-plan
on:
pull_request:
jobs:
plan:
runs-on: ubuntu-latest
permissions:
contents: read
env:
# Whatever your profiles.yml reads. `dbt compile` connects; dbt-plan does not.
UV_PROJECT_ENVIRONMENT: ${{ runner.temp }}/project-venv
SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
SNOWFLAKE_USER: ${{ secrets.SNOWFLAKE_USER }}
SNOWFLAKE_PRIVATE_KEY: ${{ secrets.SNOWFLAKE_PRIVATE_KEY }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # the base revision has to be in the clone
persist-credentials: false
- uses: actions/setup-python@v5
with: { python-version: '3.12' }
- run: pip install uv
- run: uv sync --locked
- uses: PresentJay/dbt-plan@v1
with:
compile-command: uv run --no-sync dbt compile
This example assumes a checked-in uv.lock with your dbt adapter included. The
virtual environment lives outside the checkout so changing revisions cannot replace
it. With pip/requirements.txt, install your adapter before the Action and use the
default dbt compile command. Run dbt deps first if your project uses dbt packages.
Keep the pull_request trigger. Never switch it to pull_request_target — dbt compile
runs Jinja and macros written in the pull request, so that would hand your warehouse
credentials to code from any fork.
| Input | Default | |
|---|---|---|
compile-command | dbt compile | Runs twice, once per revision. |
base-ref | the PR base | The revision to compare against. |
project-dir | . | dbt project directory. |
target-dir | target | dbt artifact directory relative to project-dir; set this when dbt writes to a custom path such as build. |
dialect | empty | Preserve CLI configuration and manifest adapter detection, then fall back to snowflake. Set only to override the project. |
version | latest | Pin a dbt-plan release. |
fail-on | destructive | Or warning, or never. |
summary | true | Write the report to the job step summary. |
Outputs verdict (safe / destructive / warning), exit-code, and report
(path to the JSON report), so a later step can comment on the PR or open a ticket.
For a workflow you own outright rather than a wrapped action, dbt-plan ci-setup
generates one with credential wiring and least-privilege notes inline. The next-minor
generator supports pyproject.toml/uv.lock and requirements.txt, installs dbt and
dbt-plan in one environment, and separates summary rendering from its gate. The default
FAIL_ON: destructive allows warnings but always blocks execution errors. Regenerate
existing workflows to adopt it; see docs/ci-integration.md.
Exit codes
The next minor release separates execution failures from review findings (a breaking change from 0.15.x). This contract is implemented on main and is not yet released.
| Code | Meaning |
|---|---|
0 | No blocking findings under the configured policy. |
1 | Destructive findings. |
2 | Review required: SQL uncertainty or potential build/test failures. |
3 | Execution failed: invalid input, missing artifacts, compile/recovery failure, or an internal error. No completed verdict. |
warning_exit_code still controls review findings; its default remains 2.
Setting it to 0 does not suppress execution errors. Code 3 is reserved and cannot
be used for warnings. The Action fails on execution errors even with fail-on: never.
See the migration guide before upgrading a CI script or pinned Action.
How It Works
flowchart TD
A[dbt-plan snapshot] --> B[Save compiled SQL + manifest.json]
C[dbt-plan check] --> D[diff_compiled_dirs]
D --> E[base compiled SQL]
D --> F[current compiled SQL]
E --> G[extract_columns]
F --> H[extract_columns]
G --> I[base columns]
H --> J[current columns]
I --> K[column diff]
J --> K
K --> L[predict_ddl + manifest config]
L --> M{Safety?}
M -->|SAFE| N[exit 0]
M -->|WARNING| O[exit 2]
M -->|DESTRUCTIVE| P[exit 1 — block merge]
L --> Q[find_downstream]
Q --> R[format_text / format_github]
Contributing
See CONTRIBUTING.md for development setup, TDD workflow, and coding rules.
First contribution? Choose a small, ready task: SQL fixtures, documentation or a sample-script fix, with exact expected results and no warehouse credentials required.
CI feedback for contributors: PRs to this repository get one automatically
updated status comment with the tested commit, approval waits, and links to failed
jobs. Comment /ci to refresh it, or /ci retry to retry a transient failure.
External contributions still need a maintainer's execution approval; retrying does
not bypass it. See commands and limits.
This helper is for contributions to dbt-plan itself, separate from the GitHub Action
you install in your own dbt project.
Architecture
src/dbt_plan/
├── columns.py # SQLGlot column extraction (multi-dialect)
├── config.py # .dbt-plan.yml + env var configuration
├── predictor.py # DDL risk assessment rules + cascade analysis
├── manifest.py # manifest.json parsing + downstream BFS
├── diff.py # compiled SQL directory comparison
├── formatter.py # text / GitHub markdown / JSON output
└── cli.py # CLI: snapshot, check, init, stats, run, ci-setup
How to Contribute
Where to start: the open issues, particularly those labelled good first issue. Each starter issue has an agreed scope, expected results and validation commands. Check its assignee and recent comments, then confirm ownership before starting.
Design decisions: See docs/design-notes.md.
Supported
- dbt-core 1.7+, and the dbt Fusion engine (verified against
2.0.0-preview.218) - Snowflake, BigQuery, Redshift, Postgres, DuckDB and other mapped adapters: dialect inferred from the manifest. Unmapped adapters fall back to Snowflake; see analysis limits.
- Python 3.10+
- CTE, UNION ALL, QUALIFY, window functions, VARIANT access
License
Apache-2.0
For adapter dialect fallback, dbt Mesh consumers, and stars over sources or seeds,
see analysis limits. For CLI compile_command argv
semantics, the Action's shell command input, and dbt deps setup, see
compile commands.
Reviews
No reviews yet
Be the first to review this server!
More Data & Analytics MCP Servers
Toleno
Freeby Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
mcp-creator-python
Freeby mcp-marketplace · Developer Tools
Create, build, and publish Python MCP servers to PyPI — conversationally.
MCP Marketplace
Freeby mcp-marketplace · Developer Tools
Search and install MCP servers from inside your AI client.
MarkItDown
Freeby Microsoft · Content & Media
Convert files (PDF, Word, Excel, images, audio) to Markdown for LLM consumption
FinAgent
Freeby mcp-marketplace · Finance
Free stock data and market news for any MCP-compatible AI assistant.
Google Workspace MCP
Freeby Taylorwilsdon · Productivity
Control Gmail, Calendar, Docs, Sheets, Drive, and more from your AI
