Skip to content

Commit e1bde2e

Browse files
committed
feat: Add query timeout and thread pool for SELECT queries
1 parent cd6ebf9 commit e1bde2e

File tree

1 file changed

+20
-3
lines changed

1 file changed

+20
-3
lines changed

mcp_clickhouse/mcp_server.py

+20-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import logging
22
from typing import Sequence
3+
import concurrent.futures
4+
import atexit
35

46
import clickhouse_connect
57
from clickhouse_connect.driver.binding import quote_identifier, format_query_value
@@ -16,6 +18,10 @@
1618
)
1719
logger = logging.getLogger(MCP_SERVER_NAME)
1820

21+
QUERY_EXECUTOR = concurrent.futures.ThreadPoolExecutor(max_workers=10)
22+
atexit.register(lambda: QUERY_EXECUTOR.shutdown(wait=True))
23+
SELECT_QUERY_TIMEOUT_SECS = 30
24+
1925
load_dotenv()
2026

2127
deps = [
@@ -105,9 +111,7 @@ def get_table_info(table):
105111
return tables
106112

107113

108-
@mcp.tool()
109-
def run_select_query(query: str):
110-
logger.info(f"Executing SELECT query: {query}")
114+
def execute_query(query: str):
111115
client = create_clickhouse_client()
112116
try:
113117
res = client.query(query, settings={"readonly": 1})
@@ -125,6 +129,19 @@ def run_select_query(query: str):
125129
return f"error running query: {err}"
126130

127131

132+
@mcp.tool()
133+
def run_select_query(query: str):
134+
logger.info(f"Executing SELECT query: {query}")
135+
future = QUERY_EXECUTOR.submit(execute_query, query)
136+
try:
137+
result = future.result(timeout=SELECT_QUERY_TIMEOUT_SECS)
138+
return result
139+
except concurrent.futures.TimeoutError:
140+
logger.warning(f"Query timed out after {SELECT_QUERY_TIMEOUT_SECS} seconds: {query}")
141+
future.cancel()
142+
return f"Queries taking longer than {SELECT_QUERY_TIMEOUT_SECS} seconds are currently not supported."
143+
144+
128145
def create_clickhouse_client():
129146
client_config = config.get_client_config()
130147
logger.info(

0 commit comments

Comments
 (0)