dbt-agent-layer 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. dbt_agent_layer-0.1.0/.claude/settings.local.json +23 -0
  2. dbt_agent_layer-0.1.0/PKG-INFO +267 -0
  3. dbt_agent_layer-0.1.0/README.md +224 -0
  4. dbt_agent_layer-0.1.0/dbt-agent-layer-spec.md +927 -0
  5. dbt_agent_layer-0.1.0/dbt_agent_layer/__init__.py +3 -0
  6. dbt_agent_layer-0.1.0/dbt_agent_layer/cli/__init__.py +5 -0
  7. dbt_agent_layer-0.1.0/dbt_agent_layer/cli/build.py +138 -0
  8. dbt_agent_layer-0.1.0/dbt_agent_layer/cli/init.py +74 -0
  9. dbt_agent_layer-0.1.0/dbt_agent_layer/cli/main.py +18 -0
  10. dbt_agent_layer-0.1.0/dbt_agent_layer/cli/serve.py +181 -0
  11. dbt_agent_layer-0.1.0/dbt_agent_layer/config/__init__.py +5 -0
  12. dbt_agent_layer-0.1.0/dbt_agent_layer/config/settings.py +183 -0
  13. dbt_agent_layer-0.1.0/dbt_agent_layer/executor/__init__.py +6 -0
  14. dbt_agent_layer-0.1.0/dbt_agent_layer/executor/base.py +19 -0
  15. dbt_agent_layer-0.1.0/dbt_agent_layer/executor/bigquery_exec.py +73 -0
  16. dbt_agent_layer-0.1.0/dbt_agent_layer/executor/duckdb_exec.py +60 -0
  17. dbt_agent_layer-0.1.0/dbt_agent_layer/executor/factory.py +69 -0
  18. dbt_agent_layer-0.1.0/dbt_agent_layer/executor/postgres_exec.py +70 -0
  19. dbt_agent_layer-0.1.0/dbt_agent_layer/executor/snowflake_exec.py +83 -0
  20. dbt_agent_layer-0.1.0/dbt_agent_layer/generator/__init__.py +17 -0
  21. dbt_agent_layer-0.1.0/dbt_agent_layer/generator/anomaly.py +145 -0
  22. dbt_agent_layer-0.1.0/dbt_agent_layer/generator/delta.py +146 -0
  23. dbt_agent_layer-0.1.0/dbt_agent_layer/generator/narrative.py +93 -0
  24. dbt_agent_layer-0.1.0/dbt_agent_layer/generator/tool_builder.py +479 -0
  25. dbt_agent_layer-0.1.0/dbt_agent_layer/mcp/__init__.py +5 -0
  26. dbt_agent_layer-0.1.0/dbt_agent_layer/mcp/schema.py +81 -0
  27. dbt_agent_layer-0.1.0/dbt_agent_layer/mcp/server.py +171 -0
  28. dbt_agent_layer-0.1.0/dbt_agent_layer/parser/__init__.py +7 -0
  29. dbt_agent_layer-0.1.0/dbt_agent_layer/parser/manifest.py +229 -0
  30. dbt_agent_layer-0.1.0/dbt_agent_layer/parser/models.py +56 -0
  31. dbt_agent_layer-0.1.0/dbt_agent_layer/parser/project.py +54 -0
  32. dbt_agent_layer-0.1.0/dbt_agent_layer/parser/semantic_layer.py +140 -0
  33. dbt_agent_layer-0.1.0/pyproject.toml +74 -0
  34. dbt_agent_layer-0.1.0/tests/__init__.py +0 -0
  35. dbt_agent_layer-0.1.0/tests/conftest.py +25 -0
  36. dbt_agent_layer-0.1.0/tests/fixtures/sample_dbt_project/dbt_project.yml +13 -0
  37. dbt_agent_layer-0.1.0/tests/fixtures/sample_dbt_project/models/marts/fct_revenue.sql +8 -0
  38. dbt_agent_layer-0.1.0/tests/fixtures/sample_dbt_project/models/marts/fct_users.sql +6 -0
  39. dbt_agent_layer-0.1.0/tests/fixtures/sample_dbt_project/models/schema.yml +71 -0
  40. dbt_agent_layer-0.1.0/tests/fixtures/sample_dbt_project/profiles.yml +7 -0
  41. dbt_agent_layer-0.1.0/tests/fixtures/sample_dbt_project/target/manifest.json +100 -0
  42. dbt_agent_layer-0.1.0/tests/test_cli.py +108 -0
  43. dbt_agent_layer-0.1.0/tests/test_delta.py +61 -0
  44. dbt_agent_layer-0.1.0/tests/test_executor_duckdb.py +59 -0
  45. dbt_agent_layer-0.1.0/tests/test_mcp_server.py +121 -0
  46. dbt_agent_layer-0.1.0/tests/test_narrative.py +112 -0
  47. dbt_agent_layer-0.1.0/tests/test_parser.py +88 -0
  48. dbt_agent_layer-0.1.0/tests/test_tool_builder.py +150 -0
@@ -0,0 +1,23 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(pip install:*)",
5
+ "Bash(pip3 install:*)",
6
+ "Bash(python3 -m pip --version)",
7
+ "Read(//usr/local/bin/**)",
8
+ "Read(//opt/homebrew/bin/**)",
9
+ "Read(//Users/abdeljalilsadouk/.pyenv/**)",
10
+ "Read(//Users/abdeljalilsadouk/Library/Python/**)",
11
+ "Bash(command -v uv)",
12
+ "Bash(command -v pipx)",
13
+ "Bash(uv venv:*)",
14
+ "Bash(source .venv/bin/activate)",
15
+ "Bash(uv pip:*)",
16
+ "Bash(python -m pytest tests/ -x -q)",
17
+ "Bash(python -m pytest tests/ -q)",
18
+ "Bash(dbt-agent --help)",
19
+ "Bash(python -m pip show build twine)",
20
+ "Bash(python -m build)"
21
+ ]
22
+ }
23
+ }
@@ -0,0 +1,267 @@
1
+ Metadata-Version: 2.4
2
+ Name: dbt-agent-layer
3
+ Version: 0.1.0
4
+ Summary: Make your dbt metrics queryable by AI agents via MCP
5
+ Project-URL: Homepage, https://github.com/dbt-agent-layer/dbt-agent-layer
6
+ Project-URL: Documentation, https://github.com/dbt-agent-layer/dbt-agent-layer#readme
7
+ Project-URL: Repository, https://github.com/dbt-agent-layer/dbt-agent-layer
8
+ License: Apache-2.0
9
+ Keywords: agents,analytics,dbt,llm,mcp,metrics
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Topic :: Software Development :: Libraries
14
+ Requires-Python: >=3.10
15
+ Requires-Dist: click>=8.1
16
+ Requires-Dist: httpx>=0.27
17
+ Requires-Dist: mcp[cli]>=1.0
18
+ Requires-Dist: pydantic>=2.0
19
+ Requires-Dist: python-dateutil>=2.9
20
+ Requires-Dist: pyyaml>=6.0
21
+ Requires-Dist: rich>=13.0
22
+ Provides-Extra: all
23
+ Requires-Dist: asyncpg>=0.29; extra == 'all'
24
+ Requires-Dist: duckdb>=0.10; extra == 'all'
25
+ Requires-Dist: google-cloud-bigquery>=3.0; extra == 'all'
26
+ Requires-Dist: snowflake-connector-python>=3.0; extra == 'all'
27
+ Provides-Extra: bigquery
28
+ Requires-Dist: google-cloud-bigquery>=3.0; extra == 'bigquery'
29
+ Provides-Extra: dev
30
+ Requires-Dist: duckdb>=0.10; extra == 'dev'
31
+ Requires-Dist: mypy>=1.10; extra == 'dev'
32
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
33
+ Requires-Dist: pytest-mock>=3.0; extra == 'dev'
34
+ Requires-Dist: pytest>=8.0; extra == 'dev'
35
+ Requires-Dist: ruff>=0.4; extra == 'dev'
36
+ Provides-Extra: duckdb
37
+ Requires-Dist: duckdb>=0.10; extra == 'duckdb'
38
+ Provides-Extra: postgres
39
+ Requires-Dist: asyncpg>=0.29; extra == 'postgres'
40
+ Provides-Extra: snowflake
41
+ Requires-Dist: snowflake-connector-python>=3.0; extra == 'snowflake'
42
+ Description-Content-Type: text/markdown
43
+
44
+ # dbt-agent-layer
45
+
46
+ > Make your dbt metrics queryable by AI agents via MCP.
47
+
48
+ ```bash
49
+ pip install dbt-agent-layer
50
+ cd my_dbt_project
51
+ dbt-agent serve
52
+ # → MCP server running, connect Claude Desktop, ask questions grounded in real data
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Quick start
58
+
59
+ ```bash
60
+ # 1. Install
61
+ pip install "dbt-agent-layer[duckdb]" # or [postgres], [bigquery], [snowflake]
62
+
63
+ # 2. Initialise (inside your dbt project)
64
+ cd my_dbt_project
65
+ dbt-agent init
66
+
67
+ # 3. Build tool registry
68
+ dbt-agent build
69
+
70
+ # 4. Start the MCP server
71
+ dbt-agent serve
72
+ ```
73
+
74
+ ---
75
+
76
+ ## How it works
77
+
78
+ ```
79
+ dbt project on disk
80
+
81
+ [Parser] reads manifest.json + schema.yml + metrics.yml
82
+
83
+ [Generator] produces async Python tool functions per metric
84
+
85
+ [MCP Server] registers tools, starts server
86
+
87
+ MCP client (Claude Desktop, any agent) calls tool
88
+
89
+ [Executor] runs SQL against your warehouse
90
+
91
+ [Delta + Narrative] enriches raw number with context
92
+
93
+ MetricResult returned to agent (value + delta + narrative + anomaly flag)
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Supported warehouses
99
+
100
+ | Adapter | Install extra | dbt adapter |
101
+ |------------|------------------------|------------------|
102
+ | DuckDB | `[duckdb]` | `dbt-duckdb` |
103
+ | PostgreSQL | `[postgres]` | `dbt-postgres` |
104
+ | BigQuery | `[bigquery]` | `dbt-bigquery` |
105
+ | Snowflake | `[snowflake]` | `dbt-snowflake` |
106
+
107
+ ### dbt version compatibility
108
+
109
+ | dbt version | Metric format | Support |
110
+ |-------------|------------------|---------|
111
+ | < 1.0 | None | Warns, skips gracefully |
112
+ | 1.0 – 1.5 | Legacy `metrics:` | Full support |
113
+ | 1.6+ | Semantic layer | Full support |
114
+ | dbt Cloud | Artifact API | Roadmap (v0.2) |
115
+
116
+ ---
117
+
118
+ ## Claude Desktop setup
119
+
120
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
121
+
122
+ ```json
123
+ {
124
+ "mcpServers": {
125
+ "dbt-metrics": {
126
+ "command": "dbt-agent",
127
+ "args": ["serve", "--project-dir", "/path/to/your/dbt/project"],
128
+ "env": {}
129
+ }
130
+ }
131
+ }
132
+ ```
133
+
134
+ Restart Claude Desktop. You can now ask:
135
+ - *"What's our MRR this month vs last month?"*
136
+ - *"Show me revenue broken down by channel for Q1 2024."*
137
+ - *"Is churn rate anomalous this month?"*
138
+
139
+ ---
140
+
141
+ ## Configuration reference (`dbt-agent.yml`)
142
+
143
+ ```yaml
144
+ version: 1
145
+
146
+ project:
147
+ dir: "." # path to dbt project
148
+ profiles_dir: "~/.dbt" # path to profiles.yml
149
+ target: "dev" # dbt target to use
150
+
151
+ adapter:
152
+ type: postgres # postgres | bigquery | snowflake | duckdb
153
+ # Connection details pulled from profiles.yml automatically.
154
+ # Override individual fields here if needed.
155
+
156
+ server:
157
+ host: "0.0.0.0"
158
+ port: 8000
159
+ transport: "stdio" # stdio (Claude Desktop) | http (web clients)
160
+
161
+ metrics:
162
+ include: [] # empty = all metrics
163
+ exclude: [] # metric names to hide from agents
164
+ default_period: "current_month"
165
+ compare_to: "prior_period" # prior_period | prior_year
166
+
167
+ narratives:
168
+ enabled: true
169
+ style: "concise" # concise | detailed
170
+ ```
171
+
172
+ ### Environment variable overrides
173
+
174
+ | Variable | Description |
175
+ |---------------------------|--------------------------------|
176
+ | `DBT_AGENT_ADAPTER_TYPE` | Override adapter type |
177
+ | `DBT_AGENT_PORT` | Override server port |
178
+ | `DBT_AGENT_TRANSPORT` | Override transport (stdio/http)|
179
+ | `DBT_AGENT_TARGET` | Override dbt target |
180
+ | `DBT_AGENT_PROFILES_DIR` | Override profiles directory |
181
+
182
+ ---
183
+
184
+ ## CLI reference
185
+
186
+ ### `dbt-agent init`
187
+ Detect your dbt project, auto-detect the warehouse adapter, and create `dbt-agent.yml`.
188
+
189
+ ```
190
+ dbt-agent init [--project-dir PATH] [--adapter postgres|bigquery|snowflake|duckdb]
191
+ ```
192
+
193
+ ### `dbt-agent build`
194
+ Parse `manifest.json`, extract all metric definitions, write `dbt_agent_tools/manifest_cache.json`.
195
+
196
+ ```
197
+ dbt-agent build [--project-dir PATH] [--skip-compile] [--dry-run] [--verbose]
198
+ ```
199
+
200
+ ### `dbt-agent serve`
201
+ Start the MCP server with all metrics registered as callable tools.
202
+
203
+ ```
204
+ dbt-agent serve [--project-dir PATH] [--port INT] [--transport stdio|http] [--skip-build] [--reload]
205
+ ```
206
+
207
+ ---
208
+
209
+ ## Built-in MCP tools (always available)
210
+
211
+ | Tool | Description |
212
+ |------------------|----------------------------------------------------------|
213
+ | `list_metrics` | List all available metrics with dimensions |
214
+ | `describe_metric`| Full metadata for a named metric |
215
+ | `query_metric` | Generic query when metric name is dynamic |
216
+ | `get_<name>` | Auto-generated per-metric tool (e.g. `get_monthly_revenue`) |
217
+
218
+ Each auto-generated tool returns a `MetricResult` containing:
219
+ - `value` + `formatted_value` (e.g. `"$142,500"`)
220
+ - `delta` — period-over-period comparison
221
+ - `narrative` — factual one-sentence summary
222
+ - `anomaly` — statistical anomaly flag (>2σ from baseline)
223
+ - `breakdown` — dimension breakdown rows (if `breakdown_by` used)
224
+ - `sql_executed` — exact SQL run (for debugging)
225
+
226
+ ---
227
+
228
+ ## Adding a new adapter
229
+
230
+ 1. Create `dbt_agent_layer/executor/mydb_exec.py` implementing `BaseExecutor`:
231
+ ```python
232
+ from .base import BaseExecutor
233
+
234
+ class MyDBExecutor(BaseExecutor):
235
+ async def execute(self, sql: str) -> list[dict]: ...
236
+ async def test_connection(self) -> bool: ...
237
+ def get_adapter_type(self) -> str: return "mydb"
238
+ ```
239
+ 2. Add a case to `executor/factory.py`:`get_executor()`.
240
+ 3. Add optional dependency to `pyproject.toml`.
241
+ 4. Open a PR — contributions welcome!
242
+
243
+ ---
244
+
245
+ ## Development
246
+
247
+ ```bash
248
+ git clone https://github.com/dbt-agent-layer/dbt-agent-layer
249
+ cd dbt-agent-layer
250
+ pip install -e ".[dev,duckdb]"
251
+ pytest
252
+ ruff check .
253
+ mypy dbt_agent_layer/
254
+ ```
255
+
256
+ ---
257
+
258
+ ## License
259
+
260
+ Apache 2.0 — see [LICENSE](LICENSE).
261
+
262
+ ---
263
+
264
+ ## Roadmap
265
+
266
+ - v0.2: Result caching, dbt Cloud artifact API, web UI
267
+ - Cloud tier: Team collaboration, audit logs, SSO
@@ -0,0 +1,224 @@
1
+ # dbt-agent-layer
2
+
3
+ > Make your dbt metrics queryable by AI agents via MCP.
4
+
5
+ ```bash
6
+ pip install dbt-agent-layer
7
+ cd my_dbt_project
8
+ dbt-agent serve
9
+ # → MCP server running, connect Claude Desktop, ask questions grounded in real data
10
+ ```
11
+
12
+ ---
13
+
14
+ ## Quick start
15
+
16
+ ```bash
17
+ # 1. Install
18
+ pip install "dbt-agent-layer[duckdb]" # or [postgres], [bigquery], [snowflake]
19
+
20
+ # 2. Initialise (inside your dbt project)
21
+ cd my_dbt_project
22
+ dbt-agent init
23
+
24
+ # 3. Build tool registry
25
+ dbt-agent build
26
+
27
+ # 4. Start the MCP server
28
+ dbt-agent serve
29
+ ```
30
+
31
+ ---
32
+
33
+ ## How it works
34
+
35
+ ```
36
+ dbt project on disk
37
+
38
+ [Parser] reads manifest.json + schema.yml + metrics.yml
39
+
40
+ [Generator] produces async Python tool functions per metric
41
+
42
+ [MCP Server] registers tools, starts server
43
+
44
+ MCP client (Claude Desktop, any agent) calls tool
45
+
46
+ [Executor] runs SQL against your warehouse
47
+
48
+ [Delta + Narrative] enriches raw number with context
49
+
50
+ MetricResult returned to agent (value + delta + narrative + anomaly flag)
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Supported warehouses
56
+
57
+ | Adapter | Install extra | dbt adapter |
58
+ |------------|------------------------|------------------|
59
+ | DuckDB | `[duckdb]` | `dbt-duckdb` |
60
+ | PostgreSQL | `[postgres]` | `dbt-postgres` |
61
+ | BigQuery | `[bigquery]` | `dbt-bigquery` |
62
+ | Snowflake | `[snowflake]` | `dbt-snowflake` |
63
+
64
+ ### dbt version compatibility
65
+
66
+ | dbt version | Metric format | Support |
67
+ |-------------|------------------|---------|
68
+ | < 1.0 | None | Warns, skips gracefully |
69
+ | 1.0 – 1.5 | Legacy `metrics:` | Full support |
70
+ | 1.6+ | Semantic layer | Full support |
71
+ | dbt Cloud | Artifact API | Roadmap (v0.2) |
72
+
73
+ ---
74
+
75
+ ## Claude Desktop setup
76
+
77
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
78
+
79
+ ```json
80
+ {
81
+ "mcpServers": {
82
+ "dbt-metrics": {
83
+ "command": "dbt-agent",
84
+ "args": ["serve", "--project-dir", "/path/to/your/dbt/project"],
85
+ "env": {}
86
+ }
87
+ }
88
+ }
89
+ ```
90
+
91
+ Restart Claude Desktop. You can now ask:
92
+ - *"What's our MRR this month vs last month?"*
93
+ - *"Show me revenue broken down by channel for Q1 2024."*
94
+ - *"Is churn rate anomalous this month?"*
95
+
96
+ ---
97
+
98
+ ## Configuration reference (`dbt-agent.yml`)
99
+
100
+ ```yaml
101
+ version: 1
102
+
103
+ project:
104
+ dir: "." # path to dbt project
105
+ profiles_dir: "~/.dbt" # path to profiles.yml
106
+ target: "dev" # dbt target to use
107
+
108
+ adapter:
109
+ type: postgres # postgres | bigquery | snowflake | duckdb
110
+ # Connection details pulled from profiles.yml automatically.
111
+ # Override individual fields here if needed.
112
+
113
+ server:
114
+ host: "0.0.0.0"
115
+ port: 8000
116
+ transport: "stdio" # stdio (Claude Desktop) | http (web clients)
117
+
118
+ metrics:
119
+ include: [] # empty = all metrics
120
+ exclude: [] # metric names to hide from agents
121
+ default_period: "current_month"
122
+ compare_to: "prior_period" # prior_period | prior_year
123
+
124
+ narratives:
125
+ enabled: true
126
+ style: "concise" # concise | detailed
127
+ ```
128
+
129
+ ### Environment variable overrides
130
+
131
+ | Variable | Description |
132
+ |---------------------------|--------------------------------|
133
+ | `DBT_AGENT_ADAPTER_TYPE` | Override adapter type |
134
+ | `DBT_AGENT_PORT` | Override server port |
135
+ | `DBT_AGENT_TRANSPORT` | Override transport (stdio/http)|
136
+ | `DBT_AGENT_TARGET` | Override dbt target |
137
+ | `DBT_AGENT_PROFILES_DIR` | Override profiles directory |
138
+
139
+ ---
140
+
141
+ ## CLI reference
142
+
143
+ ### `dbt-agent init`
144
+ Detect your dbt project, auto-detect the warehouse adapter, and create `dbt-agent.yml`.
145
+
146
+ ```
147
+ dbt-agent init [--project-dir PATH] [--adapter postgres|bigquery|snowflake|duckdb]
148
+ ```
149
+
150
+ ### `dbt-agent build`
151
+ Parse `manifest.json`, extract all metric definitions, write `dbt_agent_tools/manifest_cache.json`.
152
+
153
+ ```
154
+ dbt-agent build [--project-dir PATH] [--skip-compile] [--dry-run] [--verbose]
155
+ ```
156
+
157
+ ### `dbt-agent serve`
158
+ Start the MCP server with all metrics registered as callable tools.
159
+
160
+ ```
161
+ dbt-agent serve [--project-dir PATH] [--port INT] [--transport stdio|http] [--skip-build] [--reload]
162
+ ```
163
+
164
+ ---
165
+
166
+ ## Built-in MCP tools (always available)
167
+
168
+ | Tool | Description |
169
+ |------------------|----------------------------------------------------------|
170
+ | `list_metrics` | List all available metrics with dimensions |
171
+ | `describe_metric`| Full metadata for a named metric |
172
+ | `query_metric` | Generic query when metric name is dynamic |
173
+ | `get_<name>` | Auto-generated per-metric tool (e.g. `get_monthly_revenue`) |
174
+
175
+ Each auto-generated tool returns a `MetricResult` containing:
176
+ - `value` + `formatted_value` (e.g. `"$142,500"`)
177
+ - `delta` — period-over-period comparison
178
+ - `narrative` — factual one-sentence summary
179
+ - `anomaly` — statistical anomaly flag (>2σ from baseline)
180
+ - `breakdown` — dimension breakdown rows (if `breakdown_by` used)
181
+ - `sql_executed` — exact SQL run (for debugging)
182
+
183
+ ---
184
+
185
+ ## Adding a new adapter
186
+
187
+ 1. Create `dbt_agent_layer/executor/mydb_exec.py` implementing `BaseExecutor`:
188
+ ```python
189
+ from .base import BaseExecutor
190
+
191
+ class MyDBExecutor(BaseExecutor):
192
+ async def execute(self, sql: str) -> list[dict]: ...
193
+ async def test_connection(self) -> bool: ...
194
+ def get_adapter_type(self) -> str: return "mydb"
195
+ ```
196
+ 2. Add a case to `executor/factory.py`:`get_executor()`.
197
+ 3. Add optional dependency to `pyproject.toml`.
198
+ 4. Open a PR — contributions welcome!
199
+
200
+ ---
201
+
202
+ ## Development
203
+
204
+ ```bash
205
+ git clone https://github.com/dbt-agent-layer/dbt-agent-layer
206
+ cd dbt-agent-layer
207
+ pip install -e ".[dev,duckdb]"
208
+ pytest
209
+ ruff check .
210
+ mypy dbt_agent_layer/
211
+ ```
212
+
213
+ ---
214
+
215
+ ## License
216
+
217
+ Apache 2.0 — see [LICENSE](LICENSE).
218
+
219
+ ---
220
+
221
+ ## Roadmap
222
+
223
+ - v0.2: Result caching, dbt Cloud artifact API, web UI
224
+ - Cloud tier: Team collaboration, audit logs, SSO