Skip to content

Commit e8b40d8

Browse files
committed
Create list of tool names
1 parent 3799015 commit e8b40d8

8 files changed

Lines changed: 94 additions & 11 deletions

File tree

src/dbt_mcp/config/config.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import yaml
66
from dotenv import load_dotenv
77

8+
from dbt_mcp.tools.tool_names import ToolName
9+
810

911
@dataclass
1012
class TrackingConfig:
@@ -56,7 +58,7 @@ class Config:
5658
dbt_cli_config: DbtCliConfig | None
5759
discovery_config: DiscoveryConfig | None
5860
semantic_layer_config: SemanticLayerConfig | None
59-
disable_tools: list[str]
61+
disable_tools: list[ToolName]
6062

6163

6264
def load_config() -> Config:
@@ -77,7 +79,10 @@ def load_config() -> Config:
7779
disable_remote = os.environ.get("DISABLE_REMOTE", "true") == "true"
7880
multicell_account_prefix = os.environ.get("MULTICELL_ACCOUNT_PREFIX", None)
7981
dbt_cli_timeout = int(os.environ.get("DBT_CLI_TIMEOUT", 10))
80-
disable_tools = os.environ.get("DISABLE_TOOLS", "").split(",")
82+
disable_tools = [
83+
ToolName(tool_name)
84+
for tool_name in os.environ.get("DISABLE_TOOLS", "").split(",")
85+
]
8186

8287
# set default warn error options if not provided
8388
if os.environ.get("DBT_WARN_ERROR_OPTIONS") is None:

src/dbt_mcp/discovery/tools.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import logging
2-
32
from collections.abc import Sequence
3+
44
from mcp.server.fastmcp import FastMCP
55

66
from dbt_mcp.config.config import DiscoveryConfig
77
from dbt_mcp.discovery.client import MetadataAPIClient, ModelsFetcher
88
from dbt_mcp.prompts.prompts import get_prompt
99
from dbt_mcp.tools.definitions import ToolDefinition
1010
from dbt_mcp.tools.register import register_tools
11+
from dbt_mcp.tools.tool_names import ToolName
1112

1213
logger = logging.getLogger(__name__)
1314

@@ -85,7 +86,7 @@ def get_model_children(
8586
def register_discovery_tools(
8687
dbt_mcp: FastMCP,
8788
config: DiscoveryConfig,
88-
exclude_tools: Sequence[str] = [],
89+
exclude_tools: Sequence[ToolName] = [],
8990
) -> None:
9091
register_tools(
9192
dbt_mcp,

src/dbt_mcp/mcp/server.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ async def create_dbt_mcp():
9999

100100
if config.dbt_cli_config:
101101
logger.info("Registering dbt cli tools")
102-
register_dbt_cli_tools(dbt_mcp, config.dbt_cli_config, config.disable_tools)
102+
# TODO: allow for disabling CLI tools
103+
register_dbt_cli_tools(dbt_mcp, config.dbt_cli_config)
103104

104105
if config.remote_config:
105106
logger.info("Registering remote tools")

src/dbt_mcp/remote/tools.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from pydantic_core import PydanticUndefined
2727

2828
from dbt_mcp.config.config import RemoteConfig
29+
from dbt_mcp.tools.tool_names import ToolName
2930

3031
logger = logging.getLogger(__name__)
3132

@@ -72,7 +73,7 @@ def _get_remote_tools(base_url: str, headers: dict[str, str]) -> list[RemoteTool
7273
async def register_remote_tools(
7374
dbt_mcp: FastMCP,
7475
config: RemoteConfig,
75-
exclude_tools: Sequence[str] = [],
76+
exclude_tools: Sequence[ToolName] = [],
7677
) -> None:
7778
is_local = config.host and config.host.startswith("localhost")
7879
path = "/mcp" if is_local else "/api/ai/mcp"
@@ -92,7 +93,7 @@ async def register_remote_tools(
9293
f"Loaded remote tools: {', '.join([tool.name for tool in remote_tools])}",
9394
)
9495
for tool in remote_tools:
95-
if tool.name in exclude_tools:
96+
if tool.name.lower() in [tool.value.lower() for tool in exclude_tools]:
9697
continue
9798

9899
# Create a new function using a factory to avoid closure issues

src/dbt_mcp/semantic_layer/tools.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
from collections.abc import Sequence
21
import logging
2+
from collections.abc import Sequence
33

44
from dbtsl.api.shared.query_params import GroupByParam
55
from dbtsl.client.sync import SyncSemanticLayerClient
@@ -20,6 +20,7 @@
2020
)
2121
from dbt_mcp.tools.definitions import ToolDefinition
2222
from dbt_mcp.tools.register import register_tools
23+
from dbt_mcp.tools.tool_names import ToolName
2324

2425
logger = logging.getLogger(__name__)
2526

@@ -95,7 +96,7 @@ def query_metrics(
9596
def register_sl_tools(
9697
dbt_mcp: FastMCP,
9798
config: SemanticLayerConfig,
98-
exclude_tools: Sequence[str] = [],
99+
exclude_tools: Sequence[ToolName] = [],
99100
) -> None:
100101
register_tools(
101102
dbt_mcp,

src/dbt_mcp/tools/register.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
from collections.abc import Sequence
2+
23
from mcp.server.fastmcp import FastMCP
34

45
from dbt_mcp.tools.definitions import ToolDefinition
6+
from dbt_mcp.tools.tool_names import ToolName
57

68

79
def register_tools(
810
dbt_mcp: FastMCP,
911
tool_definitions: list[ToolDefinition],
10-
exclude_tools: Sequence[str] = [],
12+
exclude_tools: Sequence[ToolName] = [],
1113
) -> None:
1214
for tool_definition in tool_definitions:
13-
if tool_definition.get_name() in exclude_tools:
15+
if tool_definition.get_name().lower() in [
16+
tool.value.lower() for tool in exclude_tools
17+
]:
1418
continue
1519
dbt_mcp.tool(
1620
name=tool_definition.get_name(),

src/dbt_mcp/tools/tool_names.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from enum import Enum
2+
3+
4+
class ToolName(Enum):
5+
"""Tool names available in the FastMCP server.
6+
7+
This enum provides type safety and autocompletion for tool names.
8+
The validate_server_tools() function should be used to ensure
9+
this enum stays in sync with the actual server tools.
10+
"""
11+
12+
# dbt CLI tools
13+
BUILD = "build"
14+
COMPILE = "compile"
15+
DOCS = "docs"
16+
LIST = "list"
17+
PARSE = "parse"
18+
RUN = "run"
19+
TEST = "test"
20+
SHOW = "show"
21+
22+
# Semantic Layer tools
23+
LIST_METRICS = "list_metrics"
24+
GET_DIMENSIONS = "get_dimensions"
25+
GET_ENTITIES = "get_entities"
26+
QUERY_METRICS = "query_metrics"
27+
28+
# Discovery tools
29+
GET_MART_MODELS = "get_mart_models"
30+
GET_ALL_MODELS = "get_all_models"
31+
GET_MODEL_DETAILS = "get_model_details"
32+
GET_MODEL_PARENTS = "get_model_parents"
33+
GET_MODEL_CHILDREN = "get_model_children"
34+
35+
# Remote tools
36+
TEXT_TO_SQL = "text_to_sql"
37+
EXECUTE_SQL = "execute_sql"
38+
39+
@classmethod
40+
def get_all_tool_names(cls) -> set[str]:
41+
"""Returns a set of all tool names as strings."""
42+
return {member.value for member in cls}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import pytest
2+
3+
from dbt_mcp.mcp.server import create_dbt_mcp
4+
from dbt_mcp.tools.tool_names import ToolName
5+
6+
7+
@pytest.mark.asyncio
8+
async def test_tool_names_match_server_tools():
9+
"""Test that the ToolName enum matches the tools registered in the server."""
10+
# Create the dbt_mcp server instance
11+
dbt_mcp = await create_dbt_mcp()
12+
13+
# Get all tools from the server
14+
server_tools = await dbt_mcp.list_tools()
15+
server_tool_names = {tool.name for tool in server_tools}
16+
enum_names = ToolName.get_all_tool_names()
17+
18+
# This should not raise any errors if the enum is in sync
19+
if server_tool_names != enum_names:
20+
raise ValueError(
21+
f"Tool name mismatch:\n"
22+
f"In server but not in enum: {server_tool_names - enum_names}\n"
23+
f"In enum but not in server: {enum_names - server_tool_names}"
24+
)
25+
26+
# Double check that all enum values are strings
27+
for tool in ToolName:
28+
assert isinstance(tool.value, str), f"Tool {tool.name} value should be a string"

0 commit comments

Comments
 (0)