feat: Add query_tool_calls table to log MCP tool invocations

Add query_tool_calls table to Discovery Schema to track all MCP tool
invocations via the /mcp/query/ endpoint. Logs:
- tool_name: Name of the tool that was called
- schema: Schema name (nullable, empty if not applicable)
- run_id: Run ID from discovery (nullable, 0 if not applicable)
- start_time: Start monotonic time in microseconds
- execution_time: Execution duration in microseconds
- error: Error message (null if success)

Modified files:
- Discovery_Schema.cpp: Added table creation and log_query_tool_call function
- Discovery_Schema.h: Added function declaration
- Query_Tool_Handler.cpp: Added logging after each tool execution
pull/5318/head
Rene Cannao 4 months ago
parent 8a395b9b47
commit 2250b762a3

@ -637,6 +637,25 @@ public:
int limit = 25
);
/**
* @brief Log MCP tool invocation via /mcp/query/ endpoint
* @param tool_name Name of the tool that was called
* @param schema Schema name (empty if not applicable)
* @param run_id Run ID (0 or -1 if not applicable)
* @param start_time Start monotonic time (microseconds)
* @param execution_time Execution duration (microseconds)
* @param error Error message (empty if success)
* @return 0 on success, -1 on error
*/
int log_query_tool_call(
const std::string& tool_name,
const std::string& schema,
int run_id,
unsigned long long start_time,
unsigned long long execution_time,
const std::string& error
);
/**
* @brief Get database handle for direct access
* @return SQLite3DB pointer

@ -481,6 +481,26 @@ int Discovery_Schema::create_llm_tables() {
db->execute("CREATE INDEX IF NOT EXISTS idx_llm_search_log_query ON llm_search_log(query);");
db->execute("CREATE INDEX IF NOT EXISTS idx_llm_search_log_time ON llm_search_log(searched_at);");
// Query endpoint tool invocation log - tracks all MCP tool calls via /mcp/query/
db->execute(
"CREATE TABLE IF NOT EXISTS query_tool_calls ("
" call_id INTEGER PRIMARY KEY AUTOINCREMENT , "
" tool_name TEXT NOT NULL , "
" schema TEXT , "
" run_id INTEGER , "
" start_time INTEGER NOT NULL , "
" execution_time INTEGER NOT NULL , "
" error TEXT , "
" called_at TEXT NOT NULL DEFAULT (datetime('now'))"
");"
);
proxy_debug(PROXY_DEBUG_GENERIC, 3, "Discovery_Schema: query_tool_calls table created/verified\n");
db->execute("CREATE INDEX IF NOT EXISTS idx_query_tool_calls_tool ON query_tool_calls(tool_name);");
db->execute("CREATE INDEX IF NOT EXISTS idx_query_tool_calls_schema ON query_tool_calls(schema);");
db->execute("CREATE INDEX IF NOT EXISTS idx_query_tool_calls_run ON query_tool_calls(run_id);");
db->execute("CREATE INDEX IF NOT EXISTS idx_query_tool_calls_time ON query_tool_calls(called_at);");
return 0;
}
@ -1872,3 +1892,50 @@ int Discovery_Schema::log_llm_search(
return 0;
}
int Discovery_Schema::log_query_tool_call(
const std::string& tool_name,
const std::string& schema,
int run_id,
unsigned long long start_time,
unsigned long long execution_time,
const std::string& error
) {
sqlite3_stmt* stmt = NULL;
const char* sql = "INSERT INTO query_tool_calls(tool_name, schema, run_id, start_time, execution_time, error) VALUES(?1, ?2, ?3, ?4, ?5, ?6);";
int rc = db->prepare_v2(sql, &stmt);
if (rc != SQLITE_OK || !stmt) {
proxy_error("Failed to prepare query_tool_calls insert: %d\n", rc);
return -1;
}
sqlite3_bind_text(stmt, 1, tool_name.c_str(), -1, SQLITE_TRANSIENT);
if (!schema.empty()) {
sqlite3_bind_text(stmt, 2, schema.c_str(), -1, SQLITE_TRANSIENT);
} else {
sqlite3_bind_null(stmt, 2);
}
if (run_id > 0) {
sqlite3_bind_int(stmt, 3, run_id);
} else {
sqlite3_bind_null(stmt, 3);
}
sqlite3_bind_int64(stmt, 4, start_time);
sqlite3_bind_int64(stmt, 5, execution_time);
if (!error.empty()) {
sqlite3_bind_text(stmt, 6, error.c_str(), -1, SQLITE_TRANSIENT);
} else {
sqlite3_bind_null(stmt, 6);
}
rc = sqlite3_step(stmt);
(*proxy_sqlite3_finalize)(stmt);
if (rc != SQLITE_DONE) {
proxy_error("Failed to insert query_tool_calls: %d\n", rc);
return -1;
}
return 0;
}

@ -1525,6 +1525,24 @@ json Query_Tool_Handler::execute_tool(const std::string& tool_name, const json&
unsigned long long duration = monotonic_time() - start_time;
track_tool_invocation(this, tool_name, schema, duration);
// Log tool invocation to catalog
int run_id = 0;
std::string run_id_str = json_string(arguments, "run_id");
if (!run_id_str.empty()) {
run_id = catalog->resolve_run_id(run_id_str);
}
// Extract error message if present
std::string error_msg;
if (result.contains("error") && result.contains("message")) {
const json& err = result["error"];
if (err.contains("message") && err["message"].is_string()) {
error_msg = err["message"].get<std::string>();
}
}
catalog->log_query_tool_call(tool_name, schema, run_id, start_time, duration, error_msg);
return result;
}

Loading…
Cancel
Save