@@ -128,21 +128,36 @@ def execute_query(query: str):
128128 return rows
129129 except Exception as err :
130130 logger .error (f"Error executing query: { err } " )
131- return f"error running query: { err } "
131+ # Return a structured dictionary rather than a string to ensure proper serialization
132+ # by the MCP protocol. String responses for errors can cause BrokenResourceError.
133+ return {"error" : str (err )}
132134
133135
134136@mcp .tool ()
135137def run_select_query (query : str ):
136138 """Run a SELECT query in a ClickHouse database"""
137139 logger .info (f"Executing SELECT query: { query } " )
138- future = QUERY_EXECUTOR .submit (execute_query , query )
139140 try :
140- result = future .result (timeout = SELECT_QUERY_TIMEOUT_SECS )
141- return result
142- except concurrent .futures .TimeoutError :
143- logger .warning (f"Query timed out after { SELECT_QUERY_TIMEOUT_SECS } seconds: { query } " )
144- future .cancel ()
145- return f"Queries taking longer than { SELECT_QUERY_TIMEOUT_SECS } seconds are currently not supported."
141+ future = QUERY_EXECUTOR .submit (execute_query , query )
142+ try :
143+ result = future .result (timeout = SELECT_QUERY_TIMEOUT_SECS )
144+ # Check if we received an error structure from execute_query
145+ if isinstance (result , dict ) and "error" in result :
146+ logger .warning (f"Query failed: { result ['error' ]} " )
147+ # MCP requires structured responses; string error messages can cause
148+ # serialization issues leading to BrokenResourceError
149+ return {"status" : "error" , "message" : f"Query failed: { result ['error' ]} " }
150+ return result
151+ except concurrent .futures .TimeoutError :
152+ logger .warning (f"Query timed out after { SELECT_QUERY_TIMEOUT_SECS } seconds: { query } " )
153+ future .cancel ()
154+ # Return a properly structured response for timeout errors
155+ return {"status" : "error" , "message" : f"Query timed out after { SELECT_QUERY_TIMEOUT_SECS } seconds" }
156+ except Exception as e :
157+ logger .error (f"Unexpected error in run_select_query: { str (e )} " )
158+ # Catch all other exceptions and return them in a structured format
159+ # to prevent MCP serialization failures
160+ return {"status" : "error" , "message" : f"Unexpected error: { str (e )} " }
146161
147162
148163def create_clickhouse_client ():
0 commit comments