Skip to content

Add run_query_with_params tool for parameterized ClickHouse queries#146

Open
FnayouSeif wants to merge 4 commits intoClickHouse:mainfrom
FnayouSeif:seif/run_query_with_params
Open

Add run_query_with_params tool for parameterized ClickHouse queries#146
FnayouSeif wants to merge 4 commits intoClickHouse:mainfrom
FnayouSeif:seif/run_query_with_params

Conversation

@FnayouSeif
Copy link
Copy Markdown

Add run_query_with_params tool for parameterized ClickHouse queries

Motivation

The existing run_query tool requires callers to interpolate values directly into the SQL string, which is error-prone and bypasses ClickHouse's type validation. ClickHouse supports parameterized queries natively via {param_name:ClickHouseType} placeholders, and clickhouse-connect exposes this through the parameters argument on client.query(). This PR surfaces that capability as a dedicated MCP tool.

All of that description was AI written. My motivation was the pain of seeing agent writing a vector of 1024 dimension in a SQL and seeing that in the logs.

Changes

  • mcp_clickhouse/mcp_server.py: Extended execute_query() to accept an optional params: Dict[str, Any] argument, and added run_query_with_params() which passes typed parameters to the ClickHouse driver. The new tool is registered on the MCP server alongside run_query.
  • mcp_clickhouse/__init__.py: Exported run_query_with_params as part of the public API.
  • tests/test_mcp_server.py: Added integration tests covering the happy path, aggregation with params, query errors, syntax errors, type mismatches, and missing/incorrect parameter placeholders.
  • tests/test_tool.py: Added unit tests for the success and failure paths of run_query_with_params.

Usage

query = "SELECT * FROM my_db.users WHERE id = {id:UInt32} AND name = {name:String}"
params = {"id": 42, "name": "Alice"}
result = run_query_with_params(query, params)

Or via MCP tool call:

{
  "tool": "run_query_with_params",
  "params": {
    "query": "SELECT * FROM my_db.users WHERE id = {id:UInt32}",
    "params": { "id": 42 }
  }

}

Copilot AI review requested due to automatic review settings March 12, 2026 03:18
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new MCP tool, run_query_with_params, to execute parameterized ClickHouse queries via clickhouse-connect’s parameters support, reducing error-prone SQL interpolation (and large value logging).

Changes:

  • Extend execute_query() to accept optional parameters and pass them to client.query(..., parameters=...).
  • Add and register a new MCP tool function run_query_with_params() and export it from the package API.
  • Add unit/integration tests covering success and several failure modes for parameterized queries.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

File Description
mcp_clickhouse/mcp_server.py Adds parameter support to the query execution path and registers the new tool.
mcp_clickhouse/__init__.py Exposes run_query_with_params as part of the public API.
tests/test_mcp_server.py Adds MCP-level integration tests for run_query_with_params.
tests/test_tool.py Adds direct (non-MCP) unit tests for run_query_with_params.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread tests/test_tool.py
Comment on lines +108 to +112

self.assertIn("Query execution failed", str(context.exception))



Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are multiple trailing blank lines introduced around this test, which adds noise and makes diffs harder to read. Please remove the extra blank lines to keep formatting consistent with the rest of the file.

Suggested change
self.assertIn("Query execution failed", str(context.exception))
self.assertIn("Query execution failed", str(context.exception))

Copilot uses AI. Check for mistakes.
except ToolError:
raise
except Exception as e:
logger.error("Unexpected error in run_query: %s", str(e))
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In run_query_with_params, the exception log message says "Unexpected error in run_query" which is misleading and makes it harder to trace issues in logs. Update the log message to reference run_query_with_params (or otherwise identify the tool consistently) so operators can quickly identify the failing path.

Suggested change
logger.error("Unexpected error in run_query: %s", str(e))
logger.error("Unexpected error in run_query_with_params: %s", str(e))

Copilot uses AI. Check for mistakes.
Comment on lines +518 to +526
# Check if we received an error structure from execute_query
if isinstance(result, dict) and "error" in result:
logger.warning(f"Query failed: {result['error']}")
# MCP requires structured responses; string error messages can cause
# serialization issues leading to BrokenResourceError
return {
"status": "error",
"message": f"Query failed: {result['error']}",
}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run_query_with_params checks for an { "error": ... } return from execute_query, but execute_query raises ToolError on failure and never returns an error dict. This makes the if isinstance(result, dict) and "error" in result: branch dead code. Either remove this branch here, or change execute_query to return a structured {error: ...} object consistently (and update callers/tests accordingly).

Copilot uses AI. Check for mistakes.
Comment thread mcp_clickhouse/mcp_server.py Outdated
Comment thread tests/test_mcp_server.py
@pytest.mark.asyncio
async def test_run_select_query_with_join(mcp_server, setup_test_database):
"""Test running a SELECT query with JOIN."""
"""Test running a SELECT query with JOIN.""" ##TODO: make this test actually do a join instead of just counting rows in the second table
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring/comment here includes a TODO about the test not actually doing a join. Leaving knowingly incorrect/misleading test descriptions makes the suite harder to trust. Either implement an actual JOIN in this test or update the docstring to describe what’s really being validated (and move the TODO to an issue/tracker).

Suggested change
"""Test running a SELECT query with JOIN.""" ##TODO: make this test actually do a join instead of just counting rows in the second table
"""Test running a SELECT query that counts distinct event types in the second table."""

Copilot uses AI. Check for mistakes.
Comment thread tests/test_mcp_server.py
Comment on lines +362 to +367
"""Test running a SELECT query with a type mismathc error."""

test_db, test_table, _ = setup_test_database
async with Client(mcp_server) as client:
# Invalid SQL syntax
query = f"SELECT id, name, age FROM {test_db}.{test_table} WHERE id = {{id_param:Array(String)}} ORDER BY id"
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring contains a typo: "mismathc" → "mismatch". Also, the comment below says "Invalid SQL syntax" but the test is about parameter type mismatch; updating the wording will make failures easier to interpret.

Copilot uses AI. Check for mistakes.
Comment thread tests/test_mcp_server.py

@pytest.mark.asyncio
async def test_run_select_query_with_params_incorrect_placeholders(mcp_server, setup_test_database):
"""Test running a SELECT query with a type mismathc error."""
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This docstring says it’s testing a "type mismathc error" but the test is about missing/incorrect parameter names vs placeholders. Please correct the docstring to reflect the behavior under test (and fix the "mismathc" typo).

Suggested change
"""Test running a SELECT query with a type mismathc error."""
"""Test running a SELECT query with incorrect parameter placeholders or names."""

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants