#ifdef PROXYSQLGENAI #include "proxysql.h" /** * @file AI_Tool_Handler.cpp * @brief Implementation of AI Tool Handler for MCP protocol * * Implements AI-powered tools through MCP protocol, primarily * the ai_nl2sql_convert tool for natural language to SQL conversion. * * @see AI_Tool_Handler.h */ #include "AI_Tool_Handler.h" #include "LLM_Bridge.h" #include "Anomaly_Detector.h" #include "AI_Features_Manager.h" #include "proxysql_debug.h" #include "cpp.h" #include #include // JSON library #include "../deps/json/json.hpp" using json = nlohmann::json; #define PROXYJSON // ============================================================================ // Constructor/Destructor // ============================================================================ /** * @brief Constructor using existing AI components */ AI_Tool_Handler::AI_Tool_Handler(LLM_Bridge* llm, Anomaly_Detector* anomaly) : llm_bridge(llm), anomaly_detector(anomaly), owns_components(false) { proxy_debug(PROXY_DEBUG_GENAI, 3, "AI_Tool_Handler created (wrapping existing components)\n"); } /** * @brief Constructor - creates own components * Note: This implementation uses global instances */ AI_Tool_Handler::AI_Tool_Handler() : llm_bridge(NULL), anomaly_detector(NULL), owns_components(false) { // Use global instances from AI_Features_Manager if (GloAI) { llm_bridge = GloAI->get_llm_bridge(); anomaly_detector = GloAI->get_anomaly_detector(); } proxy_debug(PROXY_DEBUG_GENAI, 3, "AI_Tool_Handler created (using global instances)\n"); } /** * @brief Destructor */ AI_Tool_Handler::~AI_Tool_Handler() { close(); proxy_debug(PROXY_DEBUG_GENAI, 3, "AI_Tool_Handler destroyed\n"); } // ============================================================================ // Lifecycle // ============================================================================ /** * @brief Initialize the tool handler */ int AI_Tool_Handler::init() { if (!llm_bridge) { proxy_error("AI_Tool_Handler: LLM bridge not available\n"); return -1; } proxy_info("AI_Tool_Handler initialized\n"); return 0; } /** * @brief Close and cleanup */ void AI_Tool_Handler::close() { if (owns_components) { // Components would be cleaned up here // For now, we use global instances managed by AI_Features_Manager } } // ============================================================================ // Helper Functions // ============================================================================ /** * @brief Extract string parameter from JSON */ std::string AI_Tool_Handler::get_json_string(const json& j, const std::string& key, const std::string& default_val) { if (j.contains(key) && !j[key].is_null()) { if (j[key].is_string()) { return j[key].get(); } else { // Convert to string if not already return j[key].dump(); } } return default_val; } /** * @brief Extract int parameter from JSON */ int AI_Tool_Handler::get_json_int(const json& j, const std::string& key, int default_val) { if (j.contains(key) && !j[key].is_null()) { if (j[key].is_number()) { return j[key].get(); } else if (j[key].is_string()) { try { return std::stoi(j[key].get()); } catch (const std::exception& e) { proxy_error("AI_Tool_Handler: Failed to convert string to int for key '%s': %s\n", key.c_str(), e.what()); return default_val; } } } return default_val; } // ============================================================================ // Tool List // ============================================================================ /** * @brief Get list of available AI tools */ json AI_Tool_Handler::get_tool_list() { json tools = json::array(); // NL2SQL tool json nl2sql_params = json::object(); nl2sql_params["type"] = "object"; nl2sql_params["properties"] = json::object(); nl2sql_params["properties"]["natural_language"] = { {"type", "string"}, {"description", "Natural language query to convert to SQL"} }; nl2sql_params["properties"]["schema"] = { {"type", "string"}, {"description", "Database/schema name for context"} }; nl2sql_params["properties"]["context_tables"] = { {"type", "string"}, {"description", "Comma-separated list of relevant tables (optional)"} }; nl2sql_params["properties"]["max_latency_ms"] = { {"type", "integer"}, {"description", "Maximum acceptable latency in milliseconds (optional)"} }; nl2sql_params["properties"]["allow_cache"] = { {"type", "boolean"}, {"description", "Whether to check semantic cache (default: true)"} }; nl2sql_params["required"] = json::array({"natural_language"}); tools.push_back({ {"name", "ai_nl2sql_convert"}, {"description", "Convert natural language query to SQL using LLM"}, {"inputSchema", nl2sql_params} }); json result; result["tools"] = tools; return result; } /** * @brief Get description of a specific tool */ json AI_Tool_Handler::get_tool_description(const std::string& tool_name) { json tools_list = get_tool_list(); for (const auto& tool : tools_list["tools"]) { if (tool["name"] == tool_name) { return tool; } } return create_error_response("Tool not found: " + tool_name); } // ============================================================================ // Tool Execution // ============================================================================ /** * @brief Execute an AI tool */ json AI_Tool_Handler::execute_tool(const std::string& tool_name, const json& arguments) { proxy_debug(PROXY_DEBUG_GENAI, 3, "AI_Tool_Handler: execute_tool(%s)\n", tool_name.c_str()); try { // LLM processing tool (generic, replaces NL2SQL) if (tool_name == "ai_nl2sql_convert") { // NOTE: The ai_nl2sql_convert tool is deprecated. // NL2SQL functionality has been replaced with a generic LLM bridge. // Future NL2SQL will be implemented as a Web UI using external agents (Claude Code + MCP server). return create_error_response("The ai_nl2sql_convert tool is deprecated. " "Use the generic LLM: queries via MySQL protocol instead."); } // Unknown tool return create_error_response("Unknown tool: " + tool_name); } catch (const std::exception& e) { proxy_error("AI_Tool_Handler: Exception in execute_tool: %s\n", e.what()); return create_error_response(std::string("Exception: ") + e.what()); } catch (...) { proxy_error("AI_Tool_Handler: Unknown exception in execute_tool\n"); return create_error_response("Unknown exception"); } } #endif /* PROXYSQLGENAI */