From 85352b68b5d6424e2c5a8947f14b56fb87301117 Mon Sep 17 00:00:00 2001 From: Devon Fulcher <24593113+DevonFulcher@users.noreply.github.com> Date: Wed, 13 May 2026 09:31:19 -0500 Subject: [PATCH 1/2] refactor(tools): default structured_output to True, require title Flip `GenericToolDefinition.structured_output` from `bool | None = False` to `bool = True` so newly-added tools don't have to opt in. Make `title` required in `generic_dbt_mcp_tool` and force keyword-only args so call sites stay explicit. Co-Authored-By: AI Assistant --- .../unreleased/Under the Hood-20260513-085632.yaml | 3 +++ src/dbt_mcp/dbt_cli/tools.py | 11 +++++++++++ src/dbt_mcp/dbt_codegen/tools.py | 6 +++--- src/dbt_mcp/lsp/tools.py | 2 +- src/dbt_mcp/tools/definitions.py | 11 +++++------ 5 files changed, 23 insertions(+), 10 deletions(-) create mode 100644 .changes/unreleased/Under the Hood-20260513-085632.yaml diff --git a/.changes/unreleased/Under the Hood-20260513-085632.yaml b/.changes/unreleased/Under the Hood-20260513-085632.yaml new file mode 100644 index 000000000..332e8d372 --- /dev/null +++ b/.changes/unreleased/Under the Hood-20260513-085632.yaml @@ -0,0 +1,3 @@ +kind: Under the Hood +body: Make ToolDefinition.structured_output default to True and require title in generic_dbt_mcp_tool. Decorator now keyword-only. +time: 2026-05-13T08:56:32.224755-05:00 diff --git a/src/dbt_mcp/dbt_cli/tools.py b/src/dbt_mcp/dbt_cli/tools.py index 595270710..70cf6a718 100644 --- a/src/dbt_mcp/dbt_cli/tools.py +++ b/src/dbt_mcp/dbt_cli/tools.py @@ -429,6 +429,7 @@ def get_node_details_dev( return [ ToolDefinition( fn=build, + title="dbt build", description=get_prompt("dbt_cli/build"), annotations=create_tool_annotations( title="dbt build", @@ -439,6 +440,7 @@ def get_node_details_dev( ), ToolDefinition( fn=compile, + title="dbt compile", description=get_prompt("dbt_cli/compile"), annotations=create_tool_annotations( title="dbt compile", @@ -449,6 +451,7 @@ def get_node_details_dev( ), ToolDefinition( fn=docs, + title="dbt docs", description=get_prompt("dbt_cli/docs"), annotations=create_tool_annotations( title="dbt docs", @@ -460,6 +463,7 @@ def get_node_details_dev( ToolDefinition( name="list", fn=ls, + title="dbt list", description=get_prompt("dbt_cli/list"), annotations=create_tool_annotations( title="dbt list", @@ -470,6 +474,7 @@ def get_node_details_dev( ), ToolDefinition( fn=parse, + title="dbt parse", description=get_prompt("dbt_cli/parse"), annotations=create_tool_annotations( title="dbt parse", @@ -480,6 +485,7 @@ def get_node_details_dev( ), ToolDefinition( fn=run, + title="dbt run", description=get_prompt("dbt_cli/run"), annotations=create_tool_annotations( title="dbt run", @@ -490,6 +496,7 @@ def get_node_details_dev( ), ToolDefinition( fn=test, + title="dbt test", description=get_prompt("dbt_cli/test"), annotations=create_tool_annotations( title="dbt test", @@ -500,6 +507,7 @@ def get_node_details_dev( ), ToolDefinition( fn=show, + title="dbt show", description=get_prompt("dbt_cli/show"), annotations=create_tool_annotations( title="dbt show", @@ -510,6 +518,7 @@ def get_node_details_dev( ), ToolDefinition( fn=clone, + title="dbt clone", description=get_prompt("dbt_cli/clone"), annotations=create_tool_annotations( title="dbt clone", @@ -521,6 +530,7 @@ def get_node_details_dev( ToolDefinition( name="get_lineage_dev", fn=get_lineage_dev, + title="Get Model Lineage (Dev)", description=get_prompt("dbt_cli/get_lineage_dev"), annotations=create_tool_annotations( title="Get Model Lineage (Dev)", @@ -532,6 +542,7 @@ def get_node_details_dev( ToolDefinition( name="get_node_details_dev", fn=get_node_details_dev, + title="Get Node Details (Dev)", description=get_prompt("dbt_cli/get_node_details_dev"), annotations=create_tool_annotations( title="Get Node Details (Dev)", diff --git a/src/dbt_mcp/dbt_codegen/tools.py b/src/dbt_mcp/dbt_codegen/tools.py index 08685d737..3ea74cc46 100644 --- a/src/dbt_mcp/dbt_codegen/tools.py +++ b/src/dbt_mcp/dbt_codegen/tools.py @@ -163,9 +163,9 @@ def generate_staging_model( return [ ToolDefinition( fn=generate_source, + title="Generate Source", description=get_prompt("dbt_codegen/generate_source"), annotations=create_tool_annotations( - title="dbt-codegen generate_source", read_only_hint=True, destructive_hint=False, idempotent_hint=True, @@ -173,9 +173,9 @@ def generate_staging_model( ), ToolDefinition( fn=generate_model_yaml, + title="Generate Model YAML", description=get_prompt("dbt_codegen/generate_model_yaml"), annotations=create_tool_annotations( - title="dbt-codegen generate_model_yaml", read_only_hint=True, destructive_hint=False, idempotent_hint=True, @@ -183,9 +183,9 @@ def generate_staging_model( ), ToolDefinition( fn=generate_staging_model, + title="Generate Staging Model", description=get_prompt("dbt_codegen/generate_staging_model"), annotations=create_tool_annotations( - title="dbt-codegen generate_staging_model", read_only_hint=True, destructive_hint=False, idempotent_hint=True, diff --git a/src/dbt_mcp/lsp/tools.py b/src/dbt_mcp/lsp/tools.py index d88faa771..d841d187c 100644 --- a/src/dbt_mcp/lsp/tools.py +++ b/src/dbt_mcp/lsp/tools.py @@ -70,9 +70,9 @@ async def wrapper(*args, **kwargs) -> Any: return [ ToolDefinition( fn=call_with_lsp_client(get_column_lineage), + title="Get Column Lineage", description=get_prompt("lsp/get_column_lineage"), annotations=create_tool_annotations( - title="get_column_lineage", read_only_hint=False, destructive_hint=False, idempotent_hint=True, diff --git a/src/dbt_mcp/tools/definitions.py b/src/dbt_mcp/tools/definitions.py index 4dd9392d4..16a2061cc 100644 --- a/src/dbt_mcp/tools/definitions.py +++ b/src/dbt_mcp/tools/definitions.py @@ -14,14 +14,12 @@ @dataclass class GenericToolDefinition[NameEnum: Enum]: fn: Callable[..., Any] + title: str description: str name_enum: type[NameEnum] name: str | None = None - title: str | None = None annotations: ToolAnnotations | None = None - # We haven't strictly defined our tool contracts yet. - # So we're setting this to False by default for now. - structured_output: bool | None = False + structured_output: bool = True meta: dict[str, Any] | None = None def get_name(self) -> NameEnum: @@ -62,15 +60,16 @@ class ToolDefinition(GenericToolDefinition[ToolName]): def generic_dbt_mcp_tool[NameEnum: Enum]( + *, description: str, + title: str, name_enum: type[NameEnum], name: str | None = None, - title: str | None = None, read_only_hint: bool = False, destructive_hint: bool = True, idempotent_hint: bool = False, open_world_hint: bool = True, - structured_output: bool | None = False, + structured_output: bool = True, meta: dict[str, Any] | None = None, ) -> Callable[[Callable], GenericToolDefinition[NameEnum]]: """Decorator to define a tool definition for dbt MCP""" From 0b4a96322d6ab6441c7b2e51e663f17ee658193d Mon Sep 17 00:00:00 2001 From: Devon Fulcher <24593113+DevonFulcher@users.noreply.github.com> Date: Wed, 13 May 2026 17:04:11 -0500 Subject: [PATCH 2/2] Comment name & title --- src/dbt_mcp/tools/definitions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dbt_mcp/tools/definitions.py b/src/dbt_mcp/tools/definitions.py index 16a2061cc..c5fca039c 100644 --- a/src/dbt_mcp/tools/definitions.py +++ b/src/dbt_mcp/tools/definitions.py @@ -14,10 +14,10 @@ @dataclass class GenericToolDefinition[NameEnum: Enum]: fn: Callable[..., Any] - title: str + title: str # Human-friendly title for the tool description: str name_enum: type[NameEnum] - name: str | None = None + name: str | None = None # Machine-friendly name for the tool annotations: ToolAnnotations | None = None structured_output: bool = True meta: dict[str, Any] | None = None