diff --git a/include/cpp.h b/include/cpp.h index 1b7ca25b8..8bd1b55a3 100644 --- a/include/cpp.h +++ b/include/cpp.h @@ -31,26 +31,22 @@ #endif /* PROXYSQLCLICKHOUSE */ #ifdef PROXYSQLGENAI -#include "MCP_Tool_Handler.h" #include "AI_Features_Manager.h" #include "AI_Tool_Handler.h" #include "AI_Vector_Storage.h" -#include "Admin_Tool_Handler.h" // Anomaly_Detector.h moved to plugins/genai/ in Step 3 of the GenAI // plugin carve-out; no core file should include it any more. -#include "Cache_Tool_Handler.h" -#include "Config_Tool_Handler.h" +// +// Step 4.C moved a further set of MCP-related headers into the genai +// plugin: Admin/Cache/Config/Observe/Stats/MCP_Tool/MySQL/Query +// tool handlers, MCP_Endpoint, MCP_Thread, ProxySQL_MCP_Server. +// Their includes were removed from this aggregate header. #include "Discovery_Schema.h" #include "GenAI_Thread.h" #include "LLM_Bridge.h" -#include "MCP_Endpoint.h" -#include "MCP_Thread.h" #include "MySQL_Catalog.h" -#include "MySQL_FTS.h" -#include "MySQL_Tool_Handler.h" -#include "Stats_Tool_Handler.h" -#include "ProxySQL_MCP_Server.hpp" -#include "Query_Tool_Handler.h" +// MySQL_FTS.h moved to plugins/genai/ in Step 4.C (its bidirectional +// coupling with MySQL_Tool_Handler forced the early move). #include "RAG_Tool_Handler.h" #include "PgSQL_Static_Harvester.h" #include "Static_Harvester.h" diff --git a/lib/Admin_Bootstrap.cpp b/lib/Admin_Bootstrap.cpp index 223571037..817e7bba2 100644 --- a/lib/Admin_Bootstrap.cpp +++ b/lib/Admin_Bootstrap.cpp @@ -192,7 +192,7 @@ extern MySQL_Monitor *GloMyMon; extern PgSQL_Threads_Handler* GloPTH; #ifdef PROXYSQLGENAI -extern MCP_Threads_Handler* GloMCPH; +// MCP_Threads_Handler ownership moved to the genai plugin in Step 4.C. extern GenAI_Threads_Handler* GloGATH; extern AI_Features_Manager *GloAI; #endif /* PROXYSQLGENAI */ diff --git a/lib/Admin_FlushVariables.cpp b/lib/Admin_FlushVariables.cpp index 52e18e8ca..bcca178d7 100644 --- a/lib/Admin_FlushVariables.cpp +++ b/lib/Admin_FlushVariables.cpp @@ -25,8 +25,9 @@ using json = nlohmann::json; #include "proxysql.h" #include "proxysql_config.h" #include "proxysql_restapi.h" -#include "MCP_Thread.h" -#include "ProxySQL_MCP_Server.hpp" +// MCP_Thread.h / ProxySQL_MCP_Server.hpp moved to the genai plugin in +// Step 4.C. flush_mcp_variables___*() are stubbed below until 4.F +// re-routes them through the plugin command registry. #include "proxysql_utils.h" #include "prometheus_helpers.h" #include "cpp.h" @@ -143,7 +144,7 @@ extern MySQL_Monitor *GloMyMon; extern PgSQL_Threads_Handler* GloPTH; #ifdef PROXYSQLGENAI -extern MCP_Threads_Handler* GloMCPH; +// MCP_Threads_Handler ownership moved to the genai plugin in Step 4.C. extern GenAI_Threads_Handler* GloGATH; extern AI_Features_Manager *GloAI; #endif /* PROXYSQLGENAI */ @@ -1409,45 +1410,34 @@ void ProxySQL_Admin::flush_admin_variables___runtime_to_database(SQLite3DB *db, #ifdef PROXYSQLGENAI // MCP (Model Context Protocol) VARIABLES +// +// Step 4.C of the GenAI plugin carve-out moved MCP_Threads_Handler ownership +// to the genai plugin. Both flush_mcp_variables___* functions are stubbed +// to no-ops here; Step 4.F will reintroduce the data flow via the plugin +// command registry (the plugin will register handlers for "SET mcp-* …" +// and "LOAD MCP VARIABLES …" and core admin SQL will dispatch to those). +// +// Effect during the 4.C → 4.F window: +// - mcp-* admin variables persist in global_variables on disk but are +// NOT pushed into the running MCP listener at startup. +// - "LOAD MCP VARIABLES TO RUNTIME" / "FROM DISK" are no-ops. +// - The MCP listener uses its compiled-in defaults. +// Acceptable temporary state — documented in the carve-out plan. void ProxySQL_Admin::flush_mcp_variables___database_to_runtime(SQLite3DB* db, bool replace, const std::string& checksum, const time_t epoch, bool lock) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing MCP variables. Replace:%d\n", replace); - if (GloMCPH == NULL) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "MCP handler not initialized, skipping MCP variables\n"); - return; - } - char* error = NULL; - int cols = 0; - int affected_rows = 0; - SQLite3_result* resultset = NULL; - char* q = (char*)"SELECT variable_name, variable_value FROM global_variables WHERE variable_name LIKE 'mcp-%'"; - db->execute_statement(q, &error, &cols, &affected_rows, &resultset); - if (error) { - proxy_error("Error on %s : %s\n", q, error); - return; - } - if (resultset) { - if (lock) wrlock(); - for (std::vector::iterator it = resultset->rows.begin(); it != resultset->rows.end(); ++it) { - SQLite3_row* r = *it; - char* name = r->fields[0]; - char* val = r->fields[1]; - // Skip the 'mcp-' prefix - char* var_name = name + 4; - GloMCPH->set_variable(var_name, val); - } - - // Update runtime_global_variables table to reflect current runtime state - flush_mcp_variables___runtime_to_database(admindb, false, false, false, true, false); - - // Manage MCP server state - load_mcp_server(); - - if (lock) wrunlock(); - delete resultset; - } + (void)db; (void)replace; (void)checksum; (void)epoch; (void)lock; + proxy_debug(PROXY_DEBUG_ADMIN, 4, "flush_mcp_variables___database_to_runtime: stubbed (Step 4.C); awaiting 4.F\n"); } void ProxySQL_Admin::flush_mcp_variables___runtime_to_database(SQLite3DB* db, bool replace, bool del, bool onlyifempty, bool runtime, bool use_lock) { + (void)db; (void)replace; (void)del; (void)onlyifempty; (void)runtime; (void)use_lock; + proxy_debug(PROXY_DEBUG_ADMIN, 4, "flush_mcp_variables___runtime_to_database: stubbed (Step 4.C); awaiting 4.F\n"); +} + +#if 0 // Original implementation preserved below for 4.F reference; the + // body references GloMCPH (now plugin-owned) so it cannot compile + // in core. Kept inside `#if 0` so a search for the original + // logic finds it next to the stub. +void ProxySQL_Admin::flush_mcp_variables___runtime_to_database_ORIGINAL(SQLite3DB* db, bool replace, bool del, bool onlyifempty, bool runtime, bool use_lock) { proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing MCP variables. Replace:%d, Delete:%d, Only_If_Empty:%d, Runtime:%d\n", replace, del, onlyifempty, runtime); if (GloMCPH == NULL) { proxy_debug(PROXY_DEBUG_ADMIN, 4, "MCP handler not initialized, skipping MCP variables\n"); @@ -1544,4 +1534,5 @@ void ProxySQL_Admin::flush_mcp_variables___runtime_to_database(SQLite3DB* db, bo } free(varnames); } +#endif // 0 — original flush_mcp_variables___runtime_to_database body for 4.F reference #endif /* PROXYSQLGENAI */ diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp index 87847b4d1..46ef0c4fd 100644 --- a/lib/Admin_Handler.cpp +++ b/lib/Admin_Handler.cpp @@ -45,7 +45,7 @@ using json = nlohmann::json; #endif /* PROXYSQL40 */ #include "MySQL_Logger.hpp" #include "PgSQL_Logger.hpp" -#include "MCP_Thread.h" +// MCP_Thread.h moved to plugins/genai/include/ in Step 4.C. #include "GenAI_Thread.h" #include "SQLite3_Server.h" #include "Web_Interface.hpp" @@ -158,7 +158,7 @@ extern MySQL_Monitor *GloMyMon; extern PgSQL_Threads_Handler* GloPTH; #ifdef PROXYSQLGENAI -extern MCP_Threads_Handler* GloMCPH; +// extern MCP_Threads_Handler* GloMCPH; — removed in Step 4.C. extern GenAI_Threads_Handler* GloGATH; extern AI_Features_Manager *GloAI; #endif /* PROXYSQLGENAI */ @@ -1164,8 +1164,9 @@ bool is_valid_global_variable(const char *var_name) { return true; #endif /* PROXYSQLCLICKHOUSE */ #ifdef PROXYSQLGENAI - } else if (strlen(var_name) > 4 && !strncmp(var_name, "mcp-", 4) && GloMCPH && GloMCPH->has_variable(var_name + 4)) { - return true; + // FIXME(4.F): mcp-* variables routed via plugin command registry. + // Until 4.F lands, "SET mcp-port=..." admin SQL returns "unknown + // variable" — temporary state documented in the carve-out plan. } else if (strlen(var_name) > 6 && !strncmp(var_name, "genai-", 6) && GloGATH && GloGATH->has_variable(var_name + 6)) { return true; #endif /* PROXYSQLGENAI */ @@ -2668,9 +2669,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } return false; } - if (GloMCPH) { - GloMCPH->load_target_auth_map(resultset); - } else if (resultset) { + // FIXME(4.F): GloMCPH->load_target_auth_map(resultset) + // removed in Step 4.C. 4.F will route this via the plugin + // command registry. Until then, "LOAD MCP PROFILES …" still + // updates the SQLite tables but the running plugin doesn't + // re-pick-up the new auth map — operator must restart for + // changes to take effect. Documented in the carve-out plan. + if (resultset) { delete resultset; } return true; diff --git a/lib/Makefile b/lib/Makefile index e13f2563b..d11e9d7cd 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -134,12 +134,14 @@ _OBJ_CXX += MySQLFFTO.oo PgSQLFFTO.oo endif # GenAI object files (conditionally included) +# Step 4.C of the GenAI plugin carve-out moved the MCP subsystem +# (MCP_Thread, MCP_Endpoint, ProxySQL_MCP_Server, the 8 non-AI/RAG +# tool handlers) into plugins/genai/. AI/RAG/discovery surface stays +# in core for Steps 5/6. ifeq ($(PROXYSQLGENAI),1) _OBJ_CXX += GenAI_Thread.oo \ - MCP_Thread.oo ProxySQL_MCP_Server.oo MCP_Endpoint.oo MCP_Tool_Handler.oo \ - MySQL_Catalog.oo MySQL_Tool_Handler.oo MySQL_FTS.oo \ - Config_Tool_Handler.oo Query_Tool_Handler.oo \ - Admin_Tool_Handler.oo Cache_Tool_Handler.oo Stats_Tool_Handler.oo \ + MCP_Tool_Handler.oo \ + MySQL_Catalog.oo \ AI_Features_Manager.oo LLM_Bridge.oo LLM_Clients.oo AI_Vector_Storage.oo AI_Tool_Handler.oo \ RAG_Tool_Handler.oo \ Discovery_Schema.oo Static_Harvester.oo PgSQL_Static_Harvester.oo diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index c66701db2..5d97fc9b8 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -22,7 +22,7 @@ using json = nlohmann::json; #include "mysql.h" #include "proxysql_admin.h" #include "Discovery_Schema.h" -#include "Query_Tool_Handler.h" +// Query_Tool_Handler.h moved to plugins/genai/include/ in Step 4.C. #include "re2/re2.h" #include "re2/regexp.h" #include "proxysql.h" @@ -45,8 +45,8 @@ using json = nlohmann::json; #include "ProxySQL_Statistics.hpp" #include "MySQL_Logger.hpp" #include "PgSQL_Logger.hpp" -#include "MCP_Thread.h" -#include "ProxySQL_MCP_Server.hpp" +// MCP_Thread.h / ProxySQL_MCP_Server.hpp moved to plugins/genai/include/ +// in Step 4.C. The MCP listener lifecycle is owned by the genai plugin. #include "SQLite3_Server.h" #include "Web_Interface.hpp" @@ -332,7 +332,7 @@ extern MySQL_Monitor *GloMyMon; extern PgSQL_Threads_Handler* GloPTH; #ifdef PROXYSQLGENAI -extern MCP_Threads_Handler* GloMCPH; +// extern MCP_Threads_Handler* GloMCPH; — removed in Step 4.C. extern GenAI_Threads_Handler* GloGATH; extern AI_Features_Manager *GloAI; #endif /* PROXYSQLGENAI */ @@ -3157,40 +3157,14 @@ void ProxySQL_Admin::init_pgsql_variables() { #ifdef PROXYSQLGENAI void ProxySQL_Admin::init_mcp_variables() { - if (GloMCPH) { - flush_mcp_variables___runtime_to_database(configdb, false, false, false, false, false); - flush_mcp_variables___runtime_to_database(admindb, false, true, false, false, false); - flush_mcp_variables___database_to_runtime(admindb, true, "", 0); - - // Load MCP target/auth profiles into runtime tables and then in-memory map. - admindb->execute("DELETE FROM runtime_mcp_auth_profiles"); - admindb->execute("INSERT OR REPLACE INTO runtime_mcp_auth_profiles SELECT * FROM main.mcp_auth_profiles"); - admindb->execute("DELETE FROM runtime_mcp_target_profiles"); - admindb->execute("INSERT OR REPLACE INTO runtime_mcp_target_profiles SELECT * FROM main.mcp_target_profiles"); - - char* error = NULL; - int cols = 0; - int affected_rows = 0; - SQLite3_result* resultset = NULL; - const char* q = - "SELECT t.target_id, t.protocol, t.hostgroup_id, t.auth_profile_id," - " t.max_rows, t.timeout_ms, t.allow_explain, t.allow_discovery, t.description," - " a.db_username, a.db_password, a.default_schema" - " FROM runtime_mcp_target_profiles t" - " JOIN runtime_mcp_auth_profiles a ON a.auth_profile_id=t.auth_profile_id" - " WHERE t.active=1" - " ORDER BY t.target_id"; - admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); - if (error) { - proxy_error("Failed to load MCP target auth map: %s\n", error); - free(error); - if (resultset) { - delete resultset; - } - } else { - GloMCPH->load_target_auth_map(resultset); - } - } + // FIXME(4.F/4.G): MCP variables / target-auth-map ownership moved to + // the genai plugin in Step 4.C. This function becomes a no-op until + // 4.F wires the variable flow through the plugin command registry + // and 4.G moves the runtime_mcp_* table maintenance into + // register_table-owned admin SQL. In the interim, the running MCP + // listener uses its compiled-in defaults and does not see admin + // SQL changes. Documented in the carve-out plan. + proxy_debug(PROXY_DEBUG_ADMIN, 4, "init_mcp_variables: stubbed (Step 4.C); awaiting 4.F/4.G\n"); } void ProxySQL_Admin::init_genai_variables() { @@ -3652,9 +3626,18 @@ void ProxySQL_Admin::load_restapi_server() { #ifdef PROXYSQLGENAI void ProxySQL_Admin::load_mcp_server() { + // FIXME(4.F): MCP listener lifecycle moved to the genai plugin in + // Step 4.C; the plugin owns the MCP server's start/stop/reconfigure + // flow now. This admin-side hook becomes a no-op. 4.F will wire + // "LOAD MCP VARIABLES TO RUNTIME" through the plugin command + // registry; until then, runtime reconfiguration of mcp-port etc. + // requires a process restart. if (!all_modules_started) { return; } - if (GloMCPH == NULL) { return; } + proxy_debug(PROXY_DEBUG_ADMIN, 4, "load_mcp_server: stubbed (Step 4.C); awaiting 4.F\n"); +} +#if 0 // Original load_mcp_server body preserved for 4.F reference. +void ProxySQL_Admin::load_mcp_server_ORIGINAL() { // Helper lambda to check if MCP port is available const auto check_mcp_port = [&](int port, bool& port_free) -> void { int e_port_check = check_port_availability(port, &port_free); @@ -3774,6 +3757,7 @@ void ProxySQL_Admin::load_mcp_server() { } } } +#endif // 0 — original load_mcp_server body for 4.F reference #endif /* PROXYSQLGENAI */ void ProxySQL_Admin::load_http_server() { @@ -8591,6 +8575,16 @@ char* ProxySQL_Admin::load_pgsql_firewall_to_runtime() { // #ifdef PROXYSQLGENAI char* ProxySQL_Admin::load_mcp_query_rules_to_runtime() { + // FIXME(4.F): MCP query rules now live with the genai plugin's + // Query_Tool_Handler. This admin entry-point becomes a no-op + // (returning a sentinel error) until 4.F wires + // "LOAD MCP QUERY RULES TO RUNTIME" through the plugin command + // registry. + return (char*)"MCP query rules: command moved to genai plugin (Step 4.C); awaiting 4.F wiring"; +} + +#if 0 // Original body preserved for 4.F reference. +char* ProxySQL_Admin::load_mcp_query_rules_to_runtime_ORIGINAL() { unsigned long long curtime1 = monotonic_time(); char* error = NULL; int cols = 0; @@ -8641,6 +8635,7 @@ char* ProxySQL_Admin::load_mcp_query_rules_to_runtime() { return NULL; } +#endif // 0 — original load_mcp_query_rules_to_runtime body for 4.F reference // Save MCP query rules from runtime to database // @@ -8658,6 +8653,17 @@ char* ProxySQL_Admin::load_mcp_query_rules_to_runtime() { // - Manual runtime-to-memory save operation // void ProxySQL_Admin::save_mcp_query_rules_from_runtime(bool _runtime) { + (void)_runtime; + // FIXME(4.F): the runtime MCP query rules cache moved with + // Query_Tool_Handler into the genai plugin in Step 4.C. Until 4.F + // reroutes "SAVE MCP QUERY RULES …" through the plugin command + // registry, this admin call is a no-op (the on-disk and runtime + // admin tables are NOT refreshed from the running plugin's cache). + proxy_debug(PROXY_DEBUG_ADMIN, 4, "save_mcp_query_rules_from_runtime: stubbed (Step 4.C); awaiting 4.F\n"); +} + +#if 0 // Original body preserved for 4.F reference. +void ProxySQL_Admin::save_mcp_query_rules_from_runtime_ORIGINAL(bool _runtime) { if (!GloMCPH) return; Query_Tool_Handler* qth = GloMCPH->query_tool_handler; if (!qth) return; @@ -8738,6 +8744,7 @@ void ProxySQL_Admin::save_mcp_query_rules_from_runtime(bool _runtime) { delete resultset; } } +#endif // 0 — original save_mcp_query_rules_from_runtime body for 4.F reference #endif /* PROXYSQLGENAI */ char* ProxySQL_Admin::load_mysql_query_rules_to_runtime(SQLite3_result* SQLite3_query_rules_resultset, SQLite3_result* SQLite3_query_rules_fast_routing_resultset, const std::string& checksum, const time_t epoch) { diff --git a/lib/ProxySQL_Admin_Stats.cpp b/lib/ProxySQL_Admin_Stats.cpp index ee763ae19..359991fd7 100644 --- a/lib/ProxySQL_Admin_Stats.cpp +++ b/lib/ProxySQL_Admin_Stats.cpp @@ -21,8 +21,12 @@ #include "MySQL_Logger.hpp" #include "PgSQL_Logger.hpp" #ifdef PROXYSQLGENAI -#include "MCP_Thread.h" -#include "Query_Tool_Handler.h" +// MCP_Thread.h / Query_Tool_Handler.h moved to plugins/genai/include/ +// in Step 4.C. RAG_Tool_Handler stays in core for Step 6 — its header +// is included here only because the (now stubbed) MCP-stats functions +// referenced GloMCPH->rag_tool_handler. Once those stats functions +// re-route through the plugin command registry (4.F), this include +// can go too. #include "RAG_Tool_Handler.h" #endif /* PROXYSQLGENAI */ #include @@ -1679,6 +1683,19 @@ void ProxySQL_Admin::stats___proxysql_message_metrics(bool reset) { #ifdef PROXYSQLGENAI void ProxySQL_Admin::stats___mcp_query_tools_counters(bool reset) { + (void)reset; + // FIXME(4.F/4.G): MCP tool-usage stats now live with the genai + // plugin's Query_Tool_Handler / RAG_Tool_Handler. Until 4.F wires + // the stats collection through the plugin command registry (or 4.G + // migrates the stats_mcp_* tables to the plugin), this admin call + // is a no-op. The tables are still created by Admin_Bootstrap so + // SELECTs against them work, but they're empty until the plugin + // publishes data through its own pathway. + proxy_debug(PROXY_DEBUG_ADMIN, 4, "stats___mcp_query_tools_counters: stubbed (Step 4.C); awaiting 4.F\n"); +} + +#if 0 // Original body preserved for 4.F reference. +void ProxySQL_Admin::stats___mcp_query_tools_counters_ORIGINAL(bool reset) { if (!GloMCPH) return; statsdb->execute("BEGIN"); @@ -1759,6 +1776,7 @@ void ProxySQL_Admin::stats___mcp_query_tools_counters(bool reset) { statsdb->execute("COMMIT"); } +#endif // 0 — original stats___mcp_query_tools_counters body for 4.F reference #endif /* PROXYSQLGENAI */ int ProxySQL_Admin::stats___save_mysql_query_digest_to_sqlite( @@ -2720,6 +2738,14 @@ int ProxySQL_Admin::stats___save_pgsql_query_digest_to_sqlite( // - max_time: Maximum execution time in microseconds #ifdef PROXYSQLGENAI void ProxySQL_Admin::stats___mcp_query_digest(bool reset) { + (void)reset; + // FIXME(4.F): MCP query-digest stats moved with Query_Tool_Handler + // to the genai plugin in Step 4.C. No-op until 4.F. + proxy_debug(PROXY_DEBUG_ADMIN, 4, "stats___mcp_query_digest: stubbed (Step 4.C); awaiting 4.F\n"); +} + +#if 0 // Original body preserved for 4.F reference. +void ProxySQL_Admin::stats___mcp_query_digest_ORIGINAL(bool reset) { if (!GloMCPH) return; Query_Tool_Handler* qth = GloMCPH->query_tool_handler; if (!qth) return; @@ -2836,6 +2862,7 @@ void ProxySQL_Admin::stats___mcp_query_digest(bool reset) { statsdb->execute("COMMIT"); delete resultset; } +#endif // 0 — original stats___mcp_query_digest body for 4.F reference #endif /* PROXYSQLGENAI */ // Collect MCP query rules statistics @@ -2854,6 +2881,13 @@ void ProxySQL_Admin::stats___mcp_query_digest(bool reset) { // #ifdef PROXYSQLGENAI void ProxySQL_Admin::stats___mcp_query_rules() { + // FIXME(4.F): MCP query-rules stats moved with Query_Tool_Handler + // to the genai plugin in Step 4.C. No-op until 4.F. + proxy_debug(PROXY_DEBUG_ADMIN, 4, "stats___mcp_query_rules: stubbed (Step 4.C); awaiting 4.F\n"); +} + +#if 0 // Original body preserved for 4.F reference. +void ProxySQL_Admin::stats___mcp_query_rules_ORIGINAL() { if (!GloMCPH) return; Query_Tool_Handler* qth = GloMCPH->query_tool_handler; if (!qth) return; @@ -2894,6 +2928,7 @@ void ProxySQL_Admin::stats___mcp_query_rules() { statsdb->execute("COMMIT"); delete resultset; } +#endif // 0 — original stats___mcp_query_rules body for 4.F reference #endif /* PROXYSQLGENAI */ // Helper: convert ASN1_TIME to ISO 8601 string (YYYY-MM-DDTHH:MM:SSZ) diff --git a/plugins/genai/Makefile b/plugins/genai/Makefile index 52dc74d2f..962573c14 100644 --- a/plugins/genai/Makefile +++ b/plugins/genai/Makefile @@ -71,15 +71,28 @@ SRCS := $(PLUGIN_DIR)/src/plugin_main.cpp \ $(PLUGIN_DIR)/src/plugin_hooks.cpp \ $(PLUGIN_DIR)/src/Anomaly_Detector.cpp \ $(PLUGIN_DIR)/src/backend_client.cpp \ - $(PLUGIN_DIR)/src/local_proxy_endpoint.cpp + $(PLUGIN_DIR)/src/local_proxy_endpoint.cpp \ + $(PLUGIN_DIR)/src/MCP_Endpoint.cpp \ + $(PLUGIN_DIR)/src/MCP_Thread.cpp \ + $(PLUGIN_DIR)/src/MySQL_FTS.cpp \ + $(PLUGIN_DIR)/src/ProxySQL_MCP_Server.cpp \ + $(PLUGIN_DIR)/src/tool_handlers/Admin_Tool_Handler.cpp \ + $(PLUGIN_DIR)/src/tool_handlers/Cache_Tool_Handler.cpp \ + $(PLUGIN_DIR)/src/tool_handlers/Config_Tool_Handler.cpp \ + $(PLUGIN_DIR)/src/tool_handlers/MySQL_Tool_Handler.cpp \ + $(PLUGIN_DIR)/src/tool_handlers/Observe_Tool_Handler.cpp \ + $(PLUGIN_DIR)/src/tool_handlers/Query_Tool_Handler.cpp \ + $(PLUGIN_DIR)/src/tool_handlers/Stats_Tool_Handler.cpp HEADERS := $(wildcard $(PLUGIN_DIR)/include/*.h) \ $(PROXYSQL_PATH)/include/ProxySQL_Plugin.h OBJS := $(patsubst $(PLUGIN_DIR)/src/%.cpp,$(ODIR)/%.o,$(SRCS)) $(ODIR): mkdir -p $(ODIR) + mkdir -p $(ODIR)/tool_handlers $(ODIR)/%.o: $(PLUGIN_DIR)/src/%.cpp $(HEADERS) | $(ODIR) + @mkdir -p $(dir $@) $(CXX) -c -o $@ $< $(CXXFLAGS) $(IDIRS) $(PLUGIN_SO): $(OBJS) diff --git a/include/Admin_Tool_Handler.h b/plugins/genai/include/Admin_Tool_Handler.h similarity index 100% rename from include/Admin_Tool_Handler.h rename to plugins/genai/include/Admin_Tool_Handler.h diff --git a/include/Cache_Tool_Handler.h b/plugins/genai/include/Cache_Tool_Handler.h similarity index 100% rename from include/Cache_Tool_Handler.h rename to plugins/genai/include/Cache_Tool_Handler.h diff --git a/include/Config_Tool_Handler.h b/plugins/genai/include/Config_Tool_Handler.h similarity index 100% rename from include/Config_Tool_Handler.h rename to plugins/genai/include/Config_Tool_Handler.h diff --git a/include/MCP_Endpoint.h b/plugins/genai/include/MCP_Endpoint.h similarity index 100% rename from include/MCP_Endpoint.h rename to plugins/genai/include/MCP_Endpoint.h diff --git a/include/MCP_Thread.h b/plugins/genai/include/MCP_Thread.h similarity index 100% rename from include/MCP_Thread.h rename to plugins/genai/include/MCP_Thread.h diff --git a/include/MySQL_FTS.h b/plugins/genai/include/MySQL_FTS.h similarity index 100% rename from include/MySQL_FTS.h rename to plugins/genai/include/MySQL_FTS.h diff --git a/include/MySQL_Tool_Handler.h b/plugins/genai/include/MySQL_Tool_Handler.h similarity index 100% rename from include/MySQL_Tool_Handler.h rename to plugins/genai/include/MySQL_Tool_Handler.h diff --git a/include/Observe_Tool_Handler.h b/plugins/genai/include/Observe_Tool_Handler.h similarity index 100% rename from include/Observe_Tool_Handler.h rename to plugins/genai/include/Observe_Tool_Handler.h diff --git a/include/ProxySQL_MCP_Server.hpp b/plugins/genai/include/ProxySQL_MCP_Server.hpp similarity index 100% rename from include/ProxySQL_MCP_Server.hpp rename to plugins/genai/include/ProxySQL_MCP_Server.hpp diff --git a/include/Query_Tool_Handler.h b/plugins/genai/include/Query_Tool_Handler.h similarity index 100% rename from include/Query_Tool_Handler.h rename to plugins/genai/include/Query_Tool_Handler.h diff --git a/include/Stats_Tool_Handler.h b/plugins/genai/include/Stats_Tool_Handler.h similarity index 100% rename from include/Stats_Tool_Handler.h rename to plugins/genai/include/Stats_Tool_Handler.h diff --git a/plugins/genai/include/genai_plugin.h b/plugins/genai/include/genai_plugin.h index 618e416b7..469258916 100644 --- a/plugins/genai/include/genai_plugin.h +++ b/plugins/genai/include/genai_plugin.h @@ -24,6 +24,7 @@ namespace prometheus { class Counter; } class Anomaly_Detector; +class MCP_Threads_Handler; /** * @brief Process-wide state shared across the plugin's translation units. @@ -54,6 +55,13 @@ struct GenAIPluginContext { /// Prometheus counter for anomalies that were *blocked* (DENY /// returned to the client). Same lifetime rules as above. prometheus::Counter* metric_blocked_queries { nullptr }; + + /// MCP listener handler. Replaces the former core global + /// `GloMCPH` as of Step 4.C. Constructed in `genai_init()`, + /// started by `genai_start()`, torn down by `genai_stop()`. + /// See plugins/genai/src/MCP_Thread.cpp for the listener + /// implementation. + MCP_Threads_Handler* mcp { nullptr }; }; /** diff --git a/lib/MCP_Endpoint.cpp b/plugins/genai/src/MCP_Endpoint.cpp similarity index 100% rename from lib/MCP_Endpoint.cpp rename to plugins/genai/src/MCP_Endpoint.cpp diff --git a/lib/MCP_Thread.cpp b/plugins/genai/src/MCP_Thread.cpp similarity index 100% rename from lib/MCP_Thread.cpp rename to plugins/genai/src/MCP_Thread.cpp diff --git a/lib/MySQL_FTS.cpp b/plugins/genai/src/MySQL_FTS.cpp similarity index 100% rename from lib/MySQL_FTS.cpp rename to plugins/genai/src/MySQL_FTS.cpp diff --git a/lib/ProxySQL_MCP_Server.cpp b/plugins/genai/src/ProxySQL_MCP_Server.cpp similarity index 75% rename from lib/ProxySQL_MCP_Server.cpp rename to plugins/genai/src/ProxySQL_MCP_Server.cpp index 29a01b338..269da119a 100644 --- a/lib/ProxySQL_MCP_Server.cpp +++ b/plugins/genai/src/ProxySQL_MCP_Server.cpp @@ -14,6 +14,16 @@ using json = nlohmann::json; #include "Admin_Tool_Handler.h" #include "Cache_Tool_Handler.h" #include "Stats_Tool_Handler.h" +// AI_Tool_Handler / RAG_Tool_Handler stay in core for Steps 5 / 6. +// We still include their headers because MCP_Threads_Handler has +// `AI_Tool_Handler*` and `RAG_Tool_Handler*` member fields that need +// the full class definitions to be visible in this TU. Their +// *constructions* below are wrapped in `#if 0` so the plugin .so +// doesn't take an unresolved reference to their constructors at +// dlopen time (host proxysql doesn't currently re-export those +// constructors from libproxysql.a since nothing in core calls them +// after the carve-out move). Steps 5 / 6 move the classes into +// the plugin and the wrapped blocks get re-enabled. #include "AI_Tool_Handler.h" #include "RAG_Tool_Handler.h" #include "AI_Features_Manager.h" @@ -137,26 +147,11 @@ ProxySQL_MCP_Server::ProxySQL_MCP_Server(int p, MCP_Threads_Handler* h) handler->stats_tool_handler = NULL; } - // 6. AI Tool Handler (for LLM and other AI features). In Step 3 - // of the GenAI plugin carve-out, the Anomaly_Detector moved to - // plugins/genai/ and AI_Features_Manager no longer holds one; - // AI_Tool_Handler's anomaly_detector field was removed (it was - // stored but never read), so the constructor now takes only the - // LLM_Bridge. - extern AI_Features_Manager *GloAI; - if (GloAI) { - handler->ai_tool_handler = new AI_Tool_Handler(GloAI->get_llm_bridge()); - if (handler->ai_tool_handler->init() == 0) { - proxy_info("AI Tool Handler initialized\n"); - } else { - proxy_error("Failed to initialize AI Tool Handler\n"); - delete handler->ai_tool_handler; - handler->ai_tool_handler = NULL; - } - } else { - proxy_warning("AI_Features_Manager not available, AI Tool Handler not initialized\n"); - handler->ai_tool_handler = NULL; - } + // 6. AI Tool Handler — disabled during the 4.C → 5 window. + // AI_Tool_Handler still lives in lib/, and the plugin .so cannot + // take an unresolved reference to its constructor (see the include + // block at the top of this file for the dlopen rationale). + handler->ai_tool_handler = nullptr; // Register MCP endpoints // Each endpoint gets its own dedicated tool handler @@ -178,34 +173,13 @@ ProxySQL_MCP_Server::ProxySQL_MCP_Server(int p, MCP_Threads_Handler* h) register_endpoint("/mcp/admin", handler->admin_tool_handler, "admin"); register_endpoint("/mcp/cache", handler->cache_tool_handler, "cache"); - // 6. AI endpoint (for LLM and other AI features) - if (handler->ai_tool_handler) { - std::unique_ptr ai_resource = - std::unique_ptr(new MCP_JSONRPC_Resource(handler, handler->ai_tool_handler, "ai")); - ws->register_resource("/mcp/ai", ai_resource.get(), true); - _endpoints.push_back({"/mcp/ai", std::move(ai_resource)}); - } + // AI endpoint registration omitted in 4.C-merged (ai_tool_handler + // always null until Step 5 moves AI_Tool_Handler into the plugin). - // 7. RAG endpoint (for Retrieval-Augmented Generation) - if (GloAI) { - // Use same catalog path as query_tool_handler for logging - std::string catalog_path = std::string(GloVars.datadir) + "/mcp_catalog.db"; - handler->rag_tool_handler = new RAG_Tool_Handler(GloAI, catalog_path); - if (handler->rag_tool_handler->init() == 0) { - std::unique_ptr rag_resource = - std::unique_ptr(new MCP_JSONRPC_Resource(handler, handler->rag_tool_handler, "rag")); - ws->register_resource("/mcp/rag", rag_resource.get(), true); - _endpoints.push_back({"/mcp/rag", std::move(rag_resource)}); - proxy_info("RAG Tool Handler initialized\n"); - } else { - proxy_error("Failed to initialize RAG Tool Handler\n"); - delete handler->rag_tool_handler; - handler->rag_tool_handler = NULL; - } - } else { - proxy_warning("AI_Features_Manager not available, RAG Tool Handler not initialized\n"); - handler->rag_tool_handler = NULL; - } + // 7. RAG endpoint — disabled during the 4.C → 6 window for the + // same reason as AI above. Step 6 moves RAG_Tool_Handler into the + // plugin and the /mcp/rag endpoint comes back online. + handler->rag_tool_handler = nullptr; std::string endpoints_list; for (size_t i = 0; i < _endpoints.size(); i++) { @@ -266,19 +240,9 @@ ProxySQL_MCP_Server::~ProxySQL_MCP_Server() { handler->stats_tool_handler = NULL; } - // Clean up AI Tool Handler (uses shared components, don't delete them) - if (handler->ai_tool_handler) { - proxy_info("Cleaning up AI Tool Handler...\n"); - delete handler->ai_tool_handler; - handler->ai_tool_handler = NULL; - } - - // Clean up RAG Tool Handler - if (handler->rag_tool_handler) { - proxy_info("Cleaning up RAG Tool Handler...\n"); - delete handler->rag_tool_handler; - handler->rag_tool_handler = NULL; - } + // AI / RAG cleanup omitted in 4.C-merged: ai_tool_handler and + // rag_tool_handler are always null because their constructions + // are wrapped in `#if 0` above. Re-enabled in Steps 5 / 6. } } diff --git a/plugins/genai/src/plugin_main.cpp b/plugins/genai/src/plugin_main.cpp index d501b8d90..96036d6c3 100644 --- a/plugins/genai/src/plugin_main.cpp +++ b/plugins/genai/src/plugin_main.cpp @@ -33,6 +33,7 @@ #include "genai_plugin.h" #include "Anomaly_Detector.h" +#include "MCP_Thread.h" #include "prometheus/counter.h" #include "prometheus/family.h" @@ -40,6 +41,13 @@ #include +// Plugin-local definition of the MCP_Threads_Handler global, replacing +// the core-side `GloMCPH` deleted in Step 4.C. This stays inside the +// .so — the plugin's tool handlers (Query_Tool_Handler etc.) reference +// the symbol locally; core code never sees it. Lifetime is managed by +// `genai_init` / `genai_stop` below. +MCP_Threads_Handler *GloMCPH = nullptr; + namespace { /** @@ -96,6 +104,7 @@ bool genai_init(ProxySQL_PluginServices* services) { ctx.services = services; ctx.started = false; ctx.anomaly_detector = nullptr; + ctx.mcp = nullptr; (void)register_prometheus_counters(ctx); // Counter registration failure is non-fatal: it just means metrics @@ -115,6 +124,17 @@ bool genai_init(ProxySQL_PluginServices* services) { ctx.anomaly_detector = nullptr; return false; } + + // Step 4.C: take over MCP_Threads_Handler ownership from former + // core global GloMCPH. Construct here; `init()` is called below. + // `start()` (the listener launch) happens in genai_start(). + // + // admindb access is not yet available during init() per the chassis + // ABI (services->get_admindb() returns nullptr until start()), so + // any plugin-side state that needs it must defer to genai_start. + ctx.mcp = new MCP_Threads_Handler(); + GloMCPH = ctx.mcp; // legacy alias used by Query_Tool_Handler etc. + ctx.mcp->init(); return true; } @@ -131,6 +151,13 @@ bool genai_init(ProxySQL_PluginServices* services) { bool genai_start() { GenAIPluginContext& ctx = genai_context(); ctx.started = true; + // FIXME(4.F): the MCP listener (ProxySQL_MCP_Server) does not + // auto-start here. Pre-4.C, the listener came up via + // ProxySQL_Admin::load_mcp_server() driven off the mcp-enabled + // admin variable. That admin surface is stubbed in core during + // the 4.C → 4.F window; consequently MCP runs with default + // (mcp_enabled=false) and stays idle. 4.F restores the admin + // SQL → plugin path and the listener auto-starts again. return true; } @@ -147,6 +174,14 @@ bool genai_start() { bool genai_stop() { GenAIPluginContext& ctx = genai_context(); ctx.started = false; + if (ctx.mcp != nullptr) { + // MCP listener teardown. ~MCP_Threads_Handler stops the + // embedded ProxySQL_MCP_Server (if running) and joins worker + // threads. Mirrors the pre-4.C `delete GloMCPH` in main.cpp. + delete ctx.mcp; + ctx.mcp = nullptr; + GloMCPH = nullptr; + } if (ctx.anomaly_detector != nullptr) { ctx.anomaly_detector->close(); delete ctx.anomaly_detector; diff --git a/lib/Admin_Tool_Handler.cpp b/plugins/genai/src/tool_handlers/Admin_Tool_Handler.cpp similarity index 100% rename from lib/Admin_Tool_Handler.cpp rename to plugins/genai/src/tool_handlers/Admin_Tool_Handler.cpp diff --git a/lib/Cache_Tool_Handler.cpp b/plugins/genai/src/tool_handlers/Cache_Tool_Handler.cpp similarity index 100% rename from lib/Cache_Tool_Handler.cpp rename to plugins/genai/src/tool_handlers/Cache_Tool_Handler.cpp diff --git a/lib/Config_Tool_Handler.cpp b/plugins/genai/src/tool_handlers/Config_Tool_Handler.cpp similarity index 100% rename from lib/Config_Tool_Handler.cpp rename to plugins/genai/src/tool_handlers/Config_Tool_Handler.cpp diff --git a/lib/MySQL_Tool_Handler.cpp b/plugins/genai/src/tool_handlers/MySQL_Tool_Handler.cpp similarity index 100% rename from lib/MySQL_Tool_Handler.cpp rename to plugins/genai/src/tool_handlers/MySQL_Tool_Handler.cpp diff --git a/lib/Observe_Tool_Handler.cpp b/plugins/genai/src/tool_handlers/Observe_Tool_Handler.cpp similarity index 100% rename from lib/Observe_Tool_Handler.cpp rename to plugins/genai/src/tool_handlers/Observe_Tool_Handler.cpp diff --git a/lib/Query_Tool_Handler.cpp b/plugins/genai/src/tool_handlers/Query_Tool_Handler.cpp similarity index 100% rename from lib/Query_Tool_Handler.cpp rename to plugins/genai/src/tool_handlers/Query_Tool_Handler.cpp diff --git a/lib/Stats_Tool_Handler.cpp b/plugins/genai/src/tool_handlers/Stats_Tool_Handler.cpp similarity index 100% rename from lib/Stats_Tool_Handler.cpp rename to plugins/genai/src/tool_handlers/Stats_Tool_Handler.cpp diff --git a/src/Makefile b/src/Makefile index e7e637975..0ea6e324e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -195,7 +195,7 @@ $(ODIR)/%.o: %.cpp $(EXECUTABLE): $(ODIR) $(OBJ) $(LIBPROXYSQLAR) ifeq ($(PROXYSQLCLICKHOUSE),1) - $(CXX) -o $@ $(OBJ) $(CLANGFIX) $(LIBPROXYSQLAR) $(CLICKHOUSE_CPP_LDIR)/libclickhouse-cpp-lib.a $(LZ4_LDIR)/liblz4.a $(MYCXXFLAGS) $(CXXFLAGS) $(LDIRS) $(LIBS) $(MYLIBS) + $(CXX) -o $@ $(OBJ) $(CLANGFIX) -Wl,--whole-archive $(PROXYSQL_LDIR)/libproxysql.a -Wl,--no-whole-archive $(filter-out $(PROXYSQL_LDIR)/libproxysql.a,$(LIBPROXYSQLAR)) $(CLICKHOUSE_CPP_LDIR)/libclickhouse-cpp-lib.a $(LZ4_LDIR)/liblz4.a $(MYCXXFLAGS) $(CXXFLAGS) $(LDIRS) $(LIBS) $(MYLIBS) else $(CXX) -o $@ $(OBJ) $(CLANGFIX) $(LIBPROXYSQLAR) $(MYCXXFLAGS) $(CXXFLAGS) $(LDIRS) $(LIBS) $(MYLIBS) endif diff --git a/src/main.cpp b/src/main.cpp index d8189b8a4..39559e5e7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,7 +30,8 @@ using json = nlohmann::json; #include "PgSQL_Logger.hpp" #ifdef PROXYSQLGENAI -#include "MCP_Thread.h" +// MCP_Thread.h has moved to plugins/genai/include/ as of Step 4.C. +// Core no longer references MCP_Threads_Handler — the plugin owns it. #include "GenAI_Thread.h" #include "AI_Features_Manager.h" #endif /* PROXYSQLGENAI */ @@ -499,7 +500,8 @@ MySQL_Threads_Handler *GloMTH = NULL; PgSQL_Threads_Handler* GloPTH = NULL; #ifdef PROXYSQLGENAI -MCP_Threads_Handler* GloMCPH = NULL; +// GloMCPH removed in Step 4.C — the genai plugin owns the +// MCP_Threads_Handler now. GloGATH / GloAI follow in Step 5. GenAI_Threads_Handler* GloGATH = NULL; AI_Features_Manager *GloAI = NULL; #endif /* PROXYSQLGENAI */ @@ -932,7 +934,9 @@ void ProxySQL_Main_init_main_modules() { GloPgAuth=NULL; GloPTH=NULL; #ifdef PROXYSQLGENAI - GloMCPH=new MCP_Threads_Handler(); + // MCP_Threads_Handler is now constructed by the genai plugin's + // init() callback (Step 4.C). GenAI_Threads_Handler / AI_Features + // move similarly in Step 5. GloGATH=new GenAI_Threads_Handler(); GloAI=NULL; #endif /* PROXYSQLGENAI */ @@ -978,10 +982,9 @@ void ProxySQL_Main_init_GenAI_module() { proxy_info("AI Features module initialized\n"); } -void ProxySQL_Main_init_MCP_module() { - GloMCPH->init(); - proxy_info("MCP module initialized\n"); -} +// ProxySQL_Main_init_MCP_module() removed in Step 4.C. +// The MCP listener is now started by the genai plugin's start() +// callback (see plugins/genai/src/plugin_main.cpp). #endif /* PROXYSQLGENAI */ void ProxySQL_Main_init_Admin_module(const bootstrap_info_t& bootstrap_info) { @@ -1316,14 +1319,7 @@ void ProxySQL_Main_shutdown_all_modules() { #endif } #ifdef PROXYSQLGENAI - if (GloMCPH) { - cpu_timer t; - delete GloMCPH; - GloMCPH = NULL; -#ifdef DEBUG - std::cerr << "GloMCPH shutdown in "; -#endif - } + // MCP shutdown is performed by the genai plugin's stop() callback (Step 4.C). if (GloGATH) { cpu_timer t; delete GloGATH; @@ -1568,7 +1564,7 @@ void ProxySQL_Main_init_phase2___not_started(const bootstrap_info_t& boostrap_in #ifdef PROXYSQLGENAI ProxySQL_Main_init_GenAI_module(); - ProxySQL_Main_init_MCP_module(); + // MCP module init moved to the genai plugin (Step 4.C). #endif /* PROXYSQLGENAI */ #ifdef PROXYSQL40 @@ -1800,9 +1796,10 @@ bool ProxySQL_Main_init_phase3___start_all() { // GenAI if (GloGATH) GloAdmin->init_genai_variables(); - if (GloMCPH) { - GloAdmin->init_mcp_variables(); - } + // init_mcp_variables() moved to the genai plugin (Step 4.C). + // FIXME(4.F): the mcp-* admin SQL surface (SET mcp-port=..., + // LOAD MCP PROFILES ...) is non-functional between Step 4.C and + // Step 4.F. Tracked in the carve-out plan. #endif /* PROXYSQLGENAI */ // HTTP Server should be initialized after other modules. See #4510 diff --git a/test/tap/test_helpers/test_globals.cpp b/test/tap/test_helpers/test_globals.cpp index 29e22d517..e223867bf 100644 --- a/test/tap/test_helpers/test_globals.cpp +++ b/test/tap/test_helpers/test_globals.cpp @@ -49,7 +49,9 @@ using json = nlohmann::json; #include "PgSQL_Logger.hpp" #ifdef PROXYSQLGENAI -#include "MCP_Thread.h" +// MCP_Thread.h moved to plugins/genai/include/ in Step 4.C; the test +// harness no longer needs to declare MCP_Threads_Handler at all (the +// stub global below was removed too). #include "GenAI_Thread.h" #include "AI_Features_Manager.h" #endif /* PROXYSQLGENAI */ @@ -94,7 +96,8 @@ MySQL_Threads_Handler *GloMTH = nullptr; PgSQL_Threads_Handler *GloPTH = nullptr; #ifdef PROXYSQLGENAI -MCP_Threads_Handler *GloMCPH = nullptr; +// MCP_Threads_Handler *GloMCPH stub removed in Step 4.C — core no +// longer references the symbol, so the test harness shouldn't either. GenAI_Threads_Handler *GloGATH = nullptr; AI_Features_Manager *GloAI = nullptr; #endif /* PROXYSQLGENAI */ diff --git a/test/tap/tests/unit/Makefile b/test/tap/tests/unit/Makefile index a1053f047..098b6eb1e 100644 --- a/test/tap/tests/unit/Makefile +++ b/test/tap/tests/unit/Makefile @@ -154,7 +154,7 @@ else LIBPROXYSQLAR_FULL := $(LIBPROXYSQLAR) MYLIBS := -Wl,--export-dynamic -Wl,-Bdynamic -lgnutls -lcurl -lssl -lcrypto -luuid \ - -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre \ + -Wl,-Bstatic -lconfig -Wl,--whole-archive -lproxysql -Wl,--no-whole-archive -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre \ -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lev \ -lprometheus-cpp-pull -lprometheus-cpp-core \ -Wl,-Bstatic -lpq -lpgcommon -lpgport \