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
pull/5386/head
Rene Cannao 2 months ago
parent 8fbd570c79
commit 7e54883ba5

@ -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);
}

@ -857,6 +857,10 @@ std::string Query_Tool_Handler::execute_query(const std::string& query, const st
PGconn* pgconn = static_cast<PGconn*>(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*>(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;

Loading…
Cancel
Save