From 7e54883ba503efad8944b26f8b559ac91295dc1c Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Tue, 17 Feb 2026 00:36:49 +0000 Subject: [PATCH] mcp query debugging: surface rule-id context for blocks and add backend failure SQL details Observed confusion: - MCP failures like 'Error 100' / 'Error 101' were visible at endpoint level but gave no direct clue whether they came from query-rule blocks or backend execution failures. - Connection/firewall-style failures also lacked enough target/query context in lower-level logs. Changes in this commit: 1) Log MCP query-rule block/OK matches with rule id and context - In Discovery_Schema::evaluate_mcp_query_rules(): - when error_msg rule action is applied, log: rule_id, tool, target_id, schema, error_msg, query preview. - when OK_msg rule action is applied, log: rule_id, tool, target_id, schema, ok_msg. - This makes it explicit which runtime_mcp_query_rules row produced the returned error. 2) Add detailed backend execution error logs in Query_Tool_Handler - For MySQL and PostgreSQL execution paths (with and without schema switching), failures now log: - target_id - schema (when applicable) - backend error text - SQL query text - Added context for PG search_path failures too. - This disambiguates rule-block errors from actual backend connectivity/authorization/firewall failures. Behavioral notes: - API responses are unchanged (to avoid breaking tests expecting exact messages). - Logging is now sufficiently descriptive to trace each failure to either: - a specific MCP rule id/action, or - a concrete backend execution error. Validation: - Recompiled successfully: - lib/obj/Discovery_Schema.oo - lib/obj/Query_Tool_Handler.oo --- lib/Discovery_Schema.cpp | 21 +++++++++++++++++++++ lib/Query_Tool_Handler.cpp | 30 ++++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/lib/Discovery_Schema.cpp b/lib/Discovery_Schema.cpp index 9debf8248..a6a425071 100644 --- a/lib/Discovery_Schema.cpp +++ b/lib/Discovery_Schema.cpp @@ -2660,6 +2660,19 @@ MCP_Query_Processor_Output* Discovery_Schema::evaluate_mcp_query_rules( // 3. Error message (block action) if (rule->error_msg) { + std::string q_preview = current_query; + if (q_preview.length() > 256) { + q_preview = q_preview.substr(0, 256) + "..."; + } + proxy_info( + "MCP query rule %d BLOCK matched: tool=%s target_id=%s schema=%s error_msg='%s' query='%s'\n", + rule->rule_id, + tool_name.c_str(), + target_id.c_str(), + schemaname.c_str(), + rule->error_msg, + q_preview.c_str() + ); if (qpo->error_msg) { free(qpo->error_msg); } @@ -2668,6 +2681,14 @@ MCP_Query_Processor_Output* Discovery_Schema::evaluate_mcp_query_rules( // 4. OK message (allow with response) if (rule->ok_msg) { + proxy_info( + "MCP query rule %d OK_MSG matched: tool=%s target_id=%s schema=%s ok_msg='%s'\n", + rule->rule_id, + tool_name.c_str(), + target_id.c_str(), + schemaname.c_str(), + rule->ok_msg + ); if (qpo->OK_msg) { free(qpo->OK_msg); } diff --git a/lib/Query_Tool_Handler.cpp b/lib/Query_Tool_Handler.cpp index 45cfa62f8..29ef0b3e9 100644 --- a/lib/Query_Tool_Handler.cpp +++ b/lib/Query_Tool_Handler.cpp @@ -857,6 +857,10 @@ std::string Query_Tool_Handler::execute_query(const std::string& query, const st PGconn* pgconn = static_cast(pgconn_v); PGresult* res = PQexec(pgconn, query.c_str()); if (res == NULL) { + proxy_error( + "Query_Tool_Handler: PQexec returned null result for target='%s' query='%s'\n", + target->target_id.c_str(), query.c_str() + ); return_connection(pgconn_v); json j; j["success"] = false; @@ -867,6 +871,10 @@ std::string Query_Tool_Handler::execute_query(const std::string& query, const st ExecStatusType st = PQresultStatus(res); if (st != PGRES_TUPLES_OK && st != PGRES_COMMAND_OK) { std::string err = PQresultErrorMessage(res); + proxy_error( + "Query_Tool_Handler: pgsql query failed for target='%s': %s | query='%s'\n", + target->target_id.c_str(), err.c_str(), query.c_str() + ); PQclear(res); return_connection(pgconn_v); json j; @@ -920,7 +928,10 @@ std::string Query_Tool_Handler::execute_query(const std::string& query, const st MYSQL* mysql_ptr = static_cast(mysql); if (mysql_query(mysql_ptr, query.c_str())) { - proxy_error("Query_Tool_Handler: Query failed: %s\n", mysql_error(mysql_ptr)); + proxy_error( + "Query_Tool_Handler: mysql query failed for target='%s': %s | query='%s'\n", + target->target_id.c_str(), mysql_error(mysql_ptr), query.c_str() + ); return_connection(mysql); json j; j["success"] = false; @@ -1003,6 +1014,10 @@ std::string Query_Tool_Handler::execute_query_with_schema( PGresult* set_res = PQexec(pgconn, set_search_path.c_str()); if (set_res == NULL || PQresultStatus(set_res) != PGRES_COMMAND_OK) { std::string err = set_res ? PQresultErrorMessage(set_res) : "set search_path failed"; + proxy_error( + "Query_Tool_Handler: failed SET search_path for target='%s' schema='%s': %s\n", + target->target_id.c_str(), validated_schema.c_str(), err.c_str() + ); if (set_res) { PQclear(set_res); } @@ -1018,6 +1033,10 @@ std::string Query_Tool_Handler::execute_query_with_schema( PGresult* res = PQexec(pgconn, query.c_str()); if (res == NULL) { + proxy_error( + "Query_Tool_Handler: PQexec returned null result for target='%s' query='%s'\n", + target->target_id.c_str(), query.c_str() + ); return_connection(pgconn_v); json j; j["success"] = false; @@ -1028,6 +1047,10 @@ std::string Query_Tool_Handler::execute_query_with_schema( ExecStatusType st = PQresultStatus(res); if (st != PGRES_TUPLES_OK && st != PGRES_COMMAND_OK) { std::string err = PQresultErrorMessage(res); + proxy_error( + "Query_Tool_Handler: pgsql query with schema failed for target='%s': %s | schema='%s' query='%s'\n", + target->target_id.c_str(), err.c_str(), schema.c_str(), query.c_str() + ); PQclear(res); return_connection(pgconn_v); json j; @@ -1100,7 +1123,10 @@ std::string Query_Tool_Handler::execute_query_with_schema( // Execute the actual query if (mysql_query(mysql_ptr, query.c_str())) { - proxy_error("Query_Tool_Handler: Query failed: %s\n", mysql_error(mysql_ptr)); + proxy_error( + "Query_Tool_Handler: mysql query with schema failed for target='%s': %s | schema='%s' query='%s'\n", + target->target_id.c_str(), mysql_error(mysql_ptr), schema.c_str(), query.c_str() + ); return_connection(mysql); json j; j["success"] = false;