Skip to content

Commit c556033

Browse files
feat(discovery): add get_node_details; deprecate the 8 get_*_details tools (#825)
# Why The dbt Discovery toolset currently has 8 near-identical detail tools — `get_model_details`, `get_source_details`, and 6 others — that are thin wrappers around a single shared dispatcher keyed by resource type. Having 8 separate tools means 8 separate descriptions in the MCP tool list, increasing per-turn context overhead. A single `get_node_details(resource_type, ...)` tool carries equivalent functionality in one description slot. # What - Adds a new `get_node_details(resource_type, name, unique_id)` tool that dispatches to the existing `ResourceDetailsFetcher.fetch_details` based on `resource_type`. Supports all 8 resource types: model, source, exposure, test, seed, snapshot, macro, semantic_model. (Named `get_node_details` rather than `get_details` — `node` is the canonical term dbt uses for a model, snapshot, etc., and `get_details` alone was too generic.) - Marks the 8 existing `get_*_details` tools as deprecated using the `deprecated_description()` and `deprecation_meta()` helpers introduced in the parent PR. All 8 remain fully registered and callable, but their descriptions are now replaced with a short, blunt DEPRECATED line pointing to `get_node_details` (rather than a banner prepended to the full original description — a shorter description makes a model less likely to pick the tool, speeding the usage soak), and their `Tool._meta` carries `{"deprecated": true, "replacement": "get_node_details"}`. - The generated README now marks all 8 tools as deprecated automatically, via #834's meta-driven `generate_docs.py` (merged to main and picked up by rebasing this branch onto #824's latest state) — no manual mapping needed on this end. The underlying `ResourceDetailsFetcher` and its GraphQL queries are unchanged — `get_node_details` uses the same data path. # Notes Stacked on #824 (enrich `get_lineage` + deprecate `get_model_parents`/`get_model_children`). Merge #824 first, then retarget this PR to `main`. Drafted by claude-sonnet-4-6 and claude-opus-4-8 under the direction of @theyostalservice
1 parent 5eef9e9 commit c556033

11 files changed

Lines changed: 356 additions & 40 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
kind: Enhancement or New Feature
2+
body: Add get_node_details tool for fetching details of any dbt resource type; deprecate the 8 type-specific get_*_details tools in favor of get_node_details
3+
time: 2026-06-29T11:30:20.511624-07:00

README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,23 @@ To learn more about the dbt Discovery API, click [here](https://docs.getdbt.com/
4545
- `get_all_macros`: Retrieves macros; option to filter by package or return package names only.
4646
- `get_all_models`: Retrieves name and description of all models.
4747
- `get_all_sources`: Gets all sources with freshness status; option to filter by source name.
48-
- `get_exposure_details`: Gets exposure details including owner, parents, and freshness status.
48+
- `get_exposure_details`: *(deprecated — use `get_node_details` instead)*
4949
- `get_exposures`: Gets all exposures (downstream dashboards, apps, or analyses).
5050
- `get_lineage`: Gets full lineage graph (ancestors and descendants) with type and depth filtering.
51-
- `get_macro_details`: Gets details for a specific macro.
51+
- `get_macro_details`: *(deprecated — use `get_node_details` instead)*
5252
- `get_mart_models`: Retrieves all mart models.
5353
- `get_model_children`: *(deprecated — use `get_lineage` instead)*
54-
- `get_model_details`: Gets model details including compiled SQL, columns, and schema.
54+
- `get_model_details`: *(deprecated — use `get_node_details` instead)*
5555
- `get_model_health`: Gets health signals: run status, test results, and upstream source freshness.
5656
- `get_model_parents`: *(deprecated — use `get_lineage` instead)*
5757
- `get_model_performance`: Gets execution history for a model; option to include test results.
58+
- `get_node_details`: Gets full details for any dbt resource type (model, source, exposure, test, seed, snapshot, macro, semantic_model).
5859
- `get_related_models`: Finds similar models using semantic search.
59-
- `get_seed_details`: Gets details for a specific seed.
60-
- `get_semantic_model_details`: Gets details for a specific semantic model.
61-
- `get_snapshot_details`: Gets details for a specific snapshot.
62-
- `get_source_details`: Gets source details including columns and freshness.
63-
- `get_test_details`: Gets details for a specific test.
60+
- `get_seed_details`: *(deprecated — use `get_node_details` instead)*
61+
- `get_semantic_model_details`: *(deprecated — use `get_node_details` instead)*
62+
- `get_snapshot_details`: *(deprecated — use `get_node_details` instead)*
63+
- `get_source_details`: *(deprecated — use `get_node_details` instead)*
64+
- `get_test_details`: *(deprecated — use `get_node_details` instead)*
6465
- `search`: [Alpha] Searches for resources across the dbt project (not generally available).
6566

6667
### dbt CLI

src/dbt_mcp/discovery/param_descriptions.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44

55
DISCOVERY_PROJECT_ID_DESCRIPTION = MULTI_PROJECT_PROJECT_ID_DESCRIPTION
66

7+
RESOURCE_TYPE_DESCRIPTION = (
8+
"The type of dbt resource to fetch details for. "
9+
"One of: model, source, exposure, test, seed, snapshot, macro, semantic_model. "
10+
"Tip: the resource_type is encoded in the unique_id prefix "
11+
"(e.g. `model.my_project.orders` → model)."
12+
)
13+
714
SOURCE_NAMES_FILTER = (
815
"Filter by top-level source names from the project `sources:` YAML "
916
"(e.g. `raw_data`, `external_api`)"

src/dbt_mcp/discovery/tools.py

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
MACRO_RETURN_PACKAGE_NAMES_ONLY,
2626
MODEL_PERF_INCLUDE_TESTS,
2727
MODEL_PERF_NUM_RUNS,
28+
RESOURCE_TYPE_DESCRIPTION,
2829
SOURCE_NAMES_FILTER,
2930
SOURCE_UNIQUE_IDS_FILTER,
3031
)
@@ -145,11 +146,36 @@ async def get_all_models(
145146

146147

147148
@dbt_mcp_tool(
148-
description=get_prompt("discovery/get_model_details"),
149+
description=get_prompt("discovery/get_node_details"),
150+
title="Get Details",
151+
read_only_hint=True,
152+
destructive_hint=False,
153+
idempotent_hint=True,
154+
)
155+
async def get_node_details(
156+
context: DiscoveryToolContext,
157+
resource_type: Annotated[
158+
AppliedResourceType, Field(description=RESOURCE_TYPE_DESCRIPTION)
159+
],
160+
name: str | None = NAME_FIELD,
161+
unique_id: str | None = UNIQUE_ID_FIELD,
162+
) -> list[dict]:
163+
config = await context.config_provider.get_config()
164+
return await context.resource_details_fetcher.fetch_details(
165+
resource_type=resource_type,
166+
unique_id=unique_id,
167+
name=name,
168+
config=config,
169+
)
170+
171+
172+
@dbt_mcp_tool(
173+
description=deprecated_description(replacement="get_node_details"),
149174
title="Get Model Details",
150175
read_only_hint=True,
151176
destructive_hint=False,
152177
idempotent_hint=True,
178+
meta=deprecation_meta(replacement="get_node_details"),
153179
)
154180
async def get_model_details(
155181
context: DiscoveryToolContext,
@@ -297,11 +323,12 @@ async def get_exposures(
297323

298324

299325
@dbt_mcp_tool(
300-
description=get_prompt("discovery/get_exposure_details"),
326+
description=deprecated_description(replacement="get_node_details"),
301327
title="Get Exposure Details",
302328
read_only_hint=True,
303329
destructive_hint=False,
304330
idempotent_hint=True,
331+
meta=deprecation_meta(replacement="get_node_details"),
305332
)
306333
async def get_exposure_details(
307334
context: DiscoveryToolContext,
@@ -340,11 +367,12 @@ async def get_all_sources(
340367

341368

342369
@dbt_mcp_tool(
343-
description=get_prompt("discovery/get_source_details"),
370+
description=deprecated_description(replacement="get_node_details"),
344371
title="Get Source Details",
345372
read_only_hint=True,
346373
destructive_hint=False,
347374
idempotent_hint=True,
375+
meta=deprecation_meta(replacement="get_node_details"),
348376
)
349377
async def get_source_details(
350378
context: DiscoveryToolContext,
@@ -389,11 +417,12 @@ async def get_all_macros(
389417

390418

391419
@dbt_mcp_tool(
392-
description=get_prompt("discovery/get_macro_details"),
420+
description=deprecated_description(replacement="get_node_details"),
393421
title="Get Macro Details",
394422
read_only_hint=True,
395423
destructive_hint=False,
396424
idempotent_hint=True,
425+
meta=deprecation_meta(replacement="get_node_details"),
397426
)
398427
async def get_macro_details(
399428
context: DiscoveryToolContext,
@@ -410,11 +439,12 @@ async def get_macro_details(
410439

411440

412441
@dbt_mcp_tool(
413-
description=get_prompt("discovery/get_seed_details"),
442+
description=deprecated_description(replacement="get_node_details"),
414443
title="Get Seed Details",
415444
read_only_hint=True,
416445
destructive_hint=False,
417446
idempotent_hint=True,
447+
meta=deprecation_meta(replacement="get_node_details"),
418448
)
419449
async def get_seed_details(
420450
context: DiscoveryToolContext,
@@ -431,11 +461,12 @@ async def get_seed_details(
431461

432462

433463
@dbt_mcp_tool(
434-
description=get_prompt("discovery/get_semantic_model_details"),
464+
description=deprecated_description(replacement="get_node_details"),
435465
title="Get Semantic Model Details",
436466
read_only_hint=True,
437467
destructive_hint=False,
438468
idempotent_hint=True,
469+
meta=deprecation_meta(replacement="get_node_details"),
439470
)
440471
async def get_semantic_model_details(
441472
context: DiscoveryToolContext,
@@ -452,11 +483,12 @@ async def get_semantic_model_details(
452483

453484

454485
@dbt_mcp_tool(
455-
description=get_prompt("discovery/get_snapshot_details"),
486+
description=deprecated_description(replacement="get_node_details"),
456487
title="Get Snapshot Details",
457488
read_only_hint=True,
458489
destructive_hint=False,
459490
idempotent_hint=True,
491+
meta=deprecation_meta(replacement="get_node_details"),
460492
)
461493
async def get_snapshot_details(
462494
context: DiscoveryToolContext,
@@ -473,11 +505,12 @@ async def get_snapshot_details(
473505

474506

475507
@dbt_mcp_tool(
476-
description=get_prompt("discovery/get_test_details"),
508+
description=deprecated_description(replacement="get_node_details"),
477509
title="Get Test Details",
478510
read_only_hint=True,
479511
destructive_hint=False,
480512
idempotent_hint=True,
513+
meta=deprecation_meta(replacement="get_node_details"),
481514
)
482515
async def get_test_details(
483516
context: DiscoveryToolContext,
@@ -496,6 +529,7 @@ async def get_test_details(
496529
DISCOVERY_TOOLS = [
497530
get_mart_models,
498531
get_all_models,
532+
get_node_details,
499533
get_model_details,
500534
get_model_parents,
501535
get_model_children,

src/dbt_mcp/discovery/tools_multiproject.py

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
MACRO_RETURN_PACKAGE_NAMES_ONLY,
3131
MODEL_PERF_INCLUDE_TESTS,
3232
MODEL_PERF_NUM_RUNS,
33+
RESOURCE_TYPE_DESCRIPTION,
3334
SOURCE_NAMES_FILTER,
3435
SOURCE_UNIQUE_IDS_FILTER,
3536
)
@@ -156,11 +157,37 @@ async def get_all_models(
156157

157158

158159
@dbt_mcp_tool(
159-
description=get_prompt("discovery/get_model_details"),
160+
description=get_prompt("discovery/get_node_details"),
161+
title="Get Details",
162+
read_only_hint=True,
163+
destructive_hint=False,
164+
idempotent_hint=True,
165+
)
166+
async def get_node_details(
167+
context: MultiProjectDiscoveryToolContext,
168+
project_id: Annotated[int, Field(description=DISCOVERY_PROJECT_ID_DESCRIPTION)],
169+
resource_type: Annotated[
170+
AppliedResourceType, Field(description=RESOURCE_TYPE_DESCRIPTION)
171+
],
172+
name: str | None = NAME_FIELD,
173+
unique_id: str | None = UNIQUE_ID_FIELD,
174+
) -> list[dict]:
175+
config = await context.config_provider.get_config(project_id=project_id)
176+
return await context.resource_details_fetcher.fetch_details(
177+
resource_type=resource_type,
178+
unique_id=unique_id,
179+
name=name,
180+
config=config,
181+
)
182+
183+
184+
@dbt_mcp_tool(
185+
description=deprecated_description(replacement="get_node_details"),
160186
title="Get Model Details",
161187
read_only_hint=True,
162188
destructive_hint=False,
163189
idempotent_hint=True,
190+
meta=deprecation_meta(replacement="get_node_details"),
164191
)
165192
async def get_model_details(
166193
context: MultiProjectDiscoveryToolContext,
@@ -316,11 +343,12 @@ async def get_exposures(
316343

317344

318345
@dbt_mcp_tool(
319-
description=get_prompt("discovery/get_exposure_details"),
346+
description=deprecated_description(replacement="get_node_details"),
320347
title="Get Exposure Details",
321348
read_only_hint=True,
322349
destructive_hint=False,
323350
idempotent_hint=True,
351+
meta=deprecation_meta(replacement="get_node_details"),
324352
)
325353
async def get_exposure_details(
326354
context: MultiProjectDiscoveryToolContext,
@@ -361,11 +389,12 @@ async def get_all_sources(
361389

362390

363391
@dbt_mcp_tool(
364-
description=get_prompt("discovery/get_source_details"),
392+
description=deprecated_description(replacement="get_node_details"),
365393
title="Get Source Details",
366394
read_only_hint=True,
367395
destructive_hint=False,
368396
idempotent_hint=True,
397+
meta=deprecation_meta(replacement="get_node_details"),
369398
)
370399
async def get_source_details(
371400
context: MultiProjectDiscoveryToolContext,
@@ -412,11 +441,12 @@ async def get_all_macros(
412441

413442

414443
@dbt_mcp_tool(
415-
description=get_prompt("discovery/get_macro_details"),
444+
description=deprecated_description(replacement="get_node_details"),
416445
title="Get Macro Details",
417446
read_only_hint=True,
418447
destructive_hint=False,
419448
idempotent_hint=True,
449+
meta=deprecation_meta(replacement="get_node_details"),
420450
)
421451
async def get_macro_details(
422452
context: MultiProjectDiscoveryToolContext,
@@ -434,11 +464,12 @@ async def get_macro_details(
434464

435465

436466
@dbt_mcp_tool(
437-
description=get_prompt("discovery/get_seed_details"),
467+
description=deprecated_description(replacement="get_node_details"),
438468
title="Get Seed Details",
439469
read_only_hint=True,
440470
destructive_hint=False,
441471
idempotent_hint=True,
472+
meta=deprecation_meta(replacement="get_node_details"),
442473
)
443474
async def get_seed_details(
444475
context: MultiProjectDiscoveryToolContext,
@@ -456,11 +487,12 @@ async def get_seed_details(
456487

457488

458489
@dbt_mcp_tool(
459-
description=get_prompt("discovery/get_semantic_model_details"),
490+
description=deprecated_description(replacement="get_node_details"),
460491
title="Get Semantic Model Details",
461492
read_only_hint=True,
462493
destructive_hint=False,
463494
idempotent_hint=True,
495+
meta=deprecation_meta(replacement="get_node_details"),
464496
)
465497
async def get_semantic_model_details(
466498
context: MultiProjectDiscoveryToolContext,
@@ -478,11 +510,12 @@ async def get_semantic_model_details(
478510

479511

480512
@dbt_mcp_tool(
481-
description=get_prompt("discovery/get_snapshot_details"),
513+
description=deprecated_description(replacement="get_node_details"),
482514
title="Get Snapshot Details",
483515
read_only_hint=True,
484516
destructive_hint=False,
485517
idempotent_hint=True,
518+
meta=deprecation_meta(replacement="get_node_details"),
486519
)
487520
async def get_snapshot_details(
488521
context: MultiProjectDiscoveryToolContext,
@@ -500,11 +533,12 @@ async def get_snapshot_details(
500533

501534

502535
@dbt_mcp_tool(
503-
description=get_prompt("discovery/get_test_details"),
536+
description=deprecated_description(replacement="get_node_details"),
504537
title="Get Test Details",
505538
read_only_hint=True,
506539
destructive_hint=False,
507540
idempotent_hint=True,
541+
meta=deprecation_meta(replacement="get_node_details"),
508542
)
509543
async def get_test_details(
510544
context: MultiProjectDiscoveryToolContext,
@@ -524,6 +558,7 @@ async def get_test_details(
524558
MULTIPROJECT_DISCOVERY_TOOLS = [
525559
get_mart_models,
526560
get_all_models,
561+
get_node_details,
527562
get_model_details,
528563
get_model_parents,
529564
get_model_children,
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Retrieves full details for a dbt resource by type and name or unique ID.
2+
3+
Use this tool when you need the full schema, definition, or metadata for a specific dbt resource. The `resource_type` determines what fields are returned.
4+
5+
**Parameters:**
6+
- `resource_type`: The type of dbt resource. One of: `model`, `source`, `exposure`, `test`, `seed`, `snapshot`, `macro`, `semantic_model`. The resource type is also encoded in the `unique_id` prefix (e.g. `model.my_project.orders``model`).
7+
- `name`: The resource's short name (e.g. `orders`). Provide `name` or `unique_id`, not both.
8+
- `unique_id`: The resource's fully qualified unique ID (e.g. `model.my_project.orders`). Preferred when available — avoids ambiguity when multiple resources share a name.
9+
10+
**Usage examples:**
11+
```python
12+
# Get details for a model by unique_id
13+
get_node_details(resource_type="model", unique_id="model.analytics.orders")
14+
15+
# Get details for a source by name
16+
get_node_details(resource_type="source", name="raw_users")
17+
18+
# Get details for a semantic model
19+
get_node_details(resource_type="semantic_model", unique_id="semantic_model.analytics.orders")
20+
```

src/dbt_mcp/tools/readme_mappings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
# Discovery tools
2828
ToolName.GET_MART_MODELS: "Retrieves all mart models.",
2929
ToolName.GET_ALL_MODELS: "Retrieves name and description of all models.",
30+
ToolName.GET_NODE_DETAILS: "Gets full details for any dbt resource type (model, source, exposure, test, seed, snapshot, macro, semantic_model).",
3031
ToolName.GET_MODEL_DETAILS: "Gets model details including compiled SQL, columns, and schema.",
3132
ToolName.GET_MODEL_PARENTS: "Gets upstream dependencies of a model.",
3233
ToolName.GET_MODEL_CHILDREN: "Gets downstream dependents of a model.",

src/dbt_mcp/tools/tool_names.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class ToolName(Enum):
2929
# Discovery tools
3030
GET_MART_MODELS = "get_mart_models"
3131
GET_ALL_MODELS = "get_all_models"
32+
GET_NODE_DETAILS = "get_node_details"
3233
GET_MODEL_DETAILS = "get_model_details"
3334
GET_MODEL_PARENTS = "get_model_parents"
3435
GET_MODEL_CHILDREN = "get_model_children"

src/dbt_mcp/tools/toolsets.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class Toolset(Enum):
6060
Toolset.DISCOVERY: {
6161
ToolName.GET_MART_MODELS,
6262
ToolName.GET_ALL_MODELS,
63+
ToolName.GET_NODE_DETAILS,
6364
ToolName.GET_MODEL_DETAILS,
6465
ToolName.GET_MODEL_PARENTS,
6566
ToolName.GET_MODEL_CHILDREN,

0 commit comments

Comments
 (0)