Skip to content

Commit 741575c

Browse files
committed
feat: align query comment format with dbt query tag schema
1 parent 1ed6663 commit 741575c

File tree

4 files changed

+90
-41
lines changed

4 files changed

+90
-41
lines changed

mcp_server_snowflake/query_manager/tools.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,10 @@ def run_query_tool(
9292
9393
Common context keys:
9494
- model: The AI model name (e.g., "claude-sonnet-4", "gpt-4")
95+
- agent_name: Name of the agent or application making queries (e.g., "Claude Code")
96+
- user_email: Email of the user running the agent
97+
- user_name: Name of the user running the agent
9598
- session_id: A unique session identifier for grouping related queries
96-
- agent_name: Name of the agent or application making queries
97-
- user_context: Additional user-provided context
9899
99100
You can also set custom key-value pairs that will be available as template variables.
100101
Context persists for the lifetime of the MCP server connection.""",
@@ -104,21 +105,35 @@ def set_query_context_tool(
104105
str,
105106
Field(
106107
default=None,
107-
description="AI model name (e.g., 'claude-sonnet-4')",
108+
description="AI model name (e.g., 'claude-sonnet-4-5-20250929')",
108109
),
109110
] = None,
110-
session_id: Annotated[
111+
agent_name: Annotated[
111112
str,
112113
Field(
113114
default=None,
114-
description="Unique session identifier for grouping queries",
115+
description="Name of the agent or application (e.g., 'Claude Code')",
115116
),
116117
] = None,
117-
agent_name: Annotated[
118+
user_email: Annotated[
119+
str,
120+
Field(
121+
default=None,
122+
description="Email of the user running the agent",
123+
),
124+
] = None,
125+
user_name: Annotated[
126+
str,
127+
Field(
128+
default=None,
129+
description="Name of the user running the agent",
130+
),
131+
] = None,
132+
session_id: Annotated[
118133
str,
119134
Field(
120135
default=None,
121-
description="Name of the agent or application",
136+
description="Unique session identifier for grouping queries",
122137
),
123138
] = None,
124139
custom_context: Annotated[
@@ -133,10 +148,14 @@ def set_query_context_tool(
133148
context = {}
134149
if model is not None:
135150
context["model"] = model
136-
if session_id is not None:
137-
context["session_id"] = session_id
138151
if agent_name is not None:
139152
context["agent_name"] = agent_name
153+
if user_email is not None:
154+
context["user_email"] = user_email
155+
if user_name is not None:
156+
context["user_name"] = user_name
157+
if session_id is not None:
158+
context["session_id"] = session_id
140159
if custom_context is not None:
141160
context.update(custom_context)
142161

mcp_server_snowflake/server.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,22 @@
5454
tag_minor_version = 3
5555
query_tag = {"origin": "sf_sit", "name": "mcp_server"}
5656

57-
# Default query comment template - provides observability metadata for queries
57+
# Default query comment template - matches dbt query tag format for observability
5858
DEFAULT_QUERY_COMMENT_TEMPLATE = {
59-
"source": "mcp-server-snowflake",
60-
"request_id": "{request_id}",
61-
"timestamp": "{timestamp}",
62-
"tool": "{tool_name}",
63-
"statement_type": "{statement_type}",
64-
"model": "{model}",
59+
"agent": "{agent_name}",
60+
"context": {
61+
"request_id": "{request_id}",
62+
"timestamp": "{timestamp}",
63+
},
64+
"model_version": "{model}",
65+
"query": {
66+
"tool": "{tool_name}",
67+
"statement_type": "{statement_type}",
68+
},
69+
"user": {
70+
"email": "{user_email}",
71+
"name": "{user_name}",
72+
},
6573
}
6674

6775
logger = get_logger(server_name)
@@ -525,8 +533,15 @@ def build_query_comment(
525533
),
526534
# Session ID: from runtime context or default
527535
"session_id": self.query_context.get("session_id", "unknown"),
528-
# Agent name: from runtime context or default
529-
"agent_name": self.query_context.get("agent_name", "unknown"),
536+
# Agent name: from runtime context or default to server name
537+
"agent_name": self.query_context.get("agent_name", server_name),
538+
# User info: from runtime context
539+
"user_email": self.query_context.get(
540+
"user_email", os.environ.get("SNOWFLAKE_MCP_USER_EMAIL", "unknown")
541+
),
542+
"user_name": self.query_context.get(
543+
"user_name", os.environ.get("SNOWFLAKE_MCP_USER_NAME", "unknown")
544+
),
530545
"server_name": server_name,
531546
"server_version": f"{tag_major_version}.{tag_minor_version}",
532547
}

mcp_server_snowflake/tests/test_query_comment.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -177,16 +177,20 @@ def test_substitutes_template_variables(self, tmp_path):
177177
assert result is not None
178178
comment = json.loads(result)
179179

180-
# Check substituted values
181-
assert comment["tool"] == "run_snowflake_query"
182-
assert comment["statement_type"] == "Select"
183-
assert comment["model"] == "claude-sonnet-4"
184-
assert comment["source"] == "mcp-server-snowflake"
180+
# Check substituted values (new nested format matching dbt query tag)
181+
assert comment["query"]["tool"] == "run_snowflake_query"
182+
assert comment["query"]["statement_type"] == "Select"
183+
assert comment["model_version"] == "claude-sonnet-4"
184+
assert comment["agent"] == "mcp-server-snowflake" # Default agent name
185185

186186
# Check that UUID and timestamp were generated (not literal template strings)
187-
assert "{request_id}" not in comment["request_id"]
188-
assert "{timestamp}" not in comment["timestamp"]
189-
assert len(comment["request_id"]) == 36 # UUID format
187+
assert "{request_id}" not in comment["context"]["request_id"]
188+
assert "{timestamp}" not in comment["context"]["timestamp"]
189+
assert len(comment["context"]["request_id"]) == 36 # UUID format
190+
191+
# Check user fields default to unknown
192+
assert comment["user"]["email"] == "unknown"
193+
assert comment["user"]["name"] == "unknown"
190194

191195
def test_nested_template_substitution(self, tmp_path):
192196
"""Test that nested template variables are substituted."""
@@ -257,7 +261,7 @@ def test_model_defaults_to_unknown(self, tmp_path):
257261
)
258262

259263
comment = json.loads(result)
260-
assert comment["model"] == "unknown"
264+
assert comment["model_version"] == "unknown"
261265

262266

263267
class TestSetQueryContext:
@@ -325,7 +329,7 @@ def test_runtime_context_overrides_env_var(self, tmp_path):
325329

326330
comment = json.loads(result)
327331
# Runtime context should override env var
328-
assert comment["model"] == "runtime-model"
332+
assert comment["model_version"] == "runtime-model"
329333

330334
def test_get_query_context_returns_copy(self, tmp_path):
331335
"""Test that get_query_context returns a copy of the context."""

services/configuration.yaml

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,32 +54,43 @@ sql_statement_permissions: # List SQL statements to explicitly allow (True) or d
5454
- Use: True
5555

5656
# Query comment configuration - adds metadata to queries for observability
57+
# Format matches dbt query tag schema for consistency across tools
5758
# Context can be set via:
58-
# 1. Environment variable: SNOWFLAKE_MCP_MODEL
59+
# 1. Environment variables: SNOWFLAKE_MCP_MODEL, SNOWFLAKE_MCP_USER_EMAIL, SNOWFLAKE_MCP_USER_NAME
5960
# 2. Runtime: Call the set_query_context tool at session start
6061
query_comment:
6162
enabled: True # Enabled by default for observability
62-
# template: # Optional custom template (uses default if not specified)
63-
# source: "my-mcp-agent"
64-
# request_id: "{request_id}"
65-
# timestamp: "{timestamp}"
66-
# tool: "{tool_name}"
67-
# statement_type: "{statement_type}"
68-
# model: "{model}"
69-
# session_id: "{session_id}"
70-
# agent_name: "{agent_name}"
63+
# template: # Optional custom template (default matches dbt query tag format)
64+
# agent: "{agent_name}"
65+
# context:
66+
# request_id: "{request_id}"
67+
# timestamp: "{timestamp}"
68+
# model_version: "{model}"
69+
# query:
70+
# tool: "{tool_name}"
71+
# statement_type: "{statement_type}"
72+
# user:
73+
# email: "{user_email}"
74+
# name: "{user_name}"
7175
#
7276
# Supported template variables:
7377
# {request_id} - Unique UUID for this request
7478
# {timestamp} - ISO 8601 timestamp (UTC)
7579
# {tool_name} - Name of the MCP tool executing the query
7680
# {statement_type} - SQL statement type (Select, Insert, etc.)
77-
# {model} - AI model name (from set_query_context or SNOWFLAKE_MCP_MODEL env var)
81+
# {model} - AI model version (from set_query_context or SNOWFLAKE_MCP_MODEL env var)
82+
# {agent_name} - Agent name (from set_query_context, defaults to "mcp-server-snowflake")
83+
# {user_email} - User email (from set_query_context or SNOWFLAKE_MCP_USER_EMAIL env var)
84+
# {user_name} - User name (from set_query_context or SNOWFLAKE_MCP_USER_NAME env var)
7885
# {session_id} - Session ID (from set_query_context)
79-
# {agent_name} - Agent name (from set_query_context)
8086
# {server_name} - MCP server name
8187
# {server_version} - Server version
8288
# {custom_key} - Any custom key set via set_query_context tool
8389
#
8490
# To set context at runtime, call the set_query_context tool:
85-
# set_query_context(model="claude-sonnet-4", session_id="sess-123", agent_name="my-agent")
91+
# set_query_context(
92+
# model="claude-sonnet-4-5-20250929",
93+
# agent_name="Claude Code",
94+
# user_email="user@example.com",
95+
# user_name="username"
96+
# )

0 commit comments

Comments
 (0)