Merge pull request #5323 from wazir-ahmed/v4.0_rag_ingest_2

RAG MCP Fixes
v4.0_rag_sys_prompt
René Cannaò 3 months ago committed by GitHub
commit 8ef8503d13
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -50,6 +50,8 @@ public:
char* mcp_query_endpoint_auth; ///< Authentication for /mcp/query endpoint
char* mcp_admin_endpoint_auth; ///< Authentication for /mcp/admin endpoint
char* mcp_cache_endpoint_auth; ///< Authentication for /mcp/cache endpoint
char* mcp_rag_endpoint_auth; ///< Authentication for /mcp/rag endpoint
int mcp_timeout_ms; ///< Request timeout in milliseconds (default: 30000)
// MySQL Tool Handler configuration
char* mcp_mysql_hosts; ///< Comma-separated list of MySQL hosts

@ -566,6 +566,11 @@ class ProxySQL_Admin {
* @details Modules ready when 'all_modules_started=true'. See 'all_modules_started'.
*/
void load_restapi_server();
/**
* @brief Loads the MCP server config to runtime if all modules are ready, no-op otherwise.
* @details Modules ready when 'all_modules_started=true'. See 'all_modules_started'.
*/
void load_mcp_server();
bool get_read_only() { return variables.admin_read_only; }
bool set_read_only(bool ro) { variables.admin_read_only=ro; return variables.admin_read_only; }
bool has_variable(const char *name);

@ -356,4 +356,17 @@ static inline void set_thread_name(const char(&name)[LEN], const bool en = true)
*/
std::string get_client_addr(struct sockaddr* client_addr);
/**
* @brief Check if a port is available for binding
*
* Creates a temporary socket and attempts to bind to the specified port
* to verify availability. The socket is closed immediately after the test.
* Sets SO_REUSEADDR to allow rebinding to recently used ports.
*
* @param port_num Port number to check
* @param port_free Output parameter - set to true if port is available, false if in use
* @return int Error code (0 = success, -1 = setsockopt failed, -2 = invalid parameters)
*/
int check_port_availability(int port_num, bool* port_free);
#endif

@ -69,6 +69,11 @@ int AI_Features_Manager::init_vector_db() {
return -1;
}
// Enable SQLite extensions for vector_db
// Once enabled, SQLite loads extensions such as vec0 and rembed automatically.
// Refer - Admin_Bootstrap.cpp:590
(*proxy_sqlite3_enable_load_extension)(vector_db->get_db(), 1);
// Create tables for LLM cache
const char* create_llm_cache =
"CREATE TABLE IF NOT EXISTS llm_cache ("
@ -82,7 +87,7 @@ int AI_Features_Manager::init_vector_db() {
"created_at INTEGER DEFAULT (strftime('%s' , 'now'))"
");";
if (vector_db->execute(create_llm_cache) != 0) {
if (!vector_db->execute(create_llm_cache)) {
proxy_error("AI: Failed to create llm_cache table\n");
return -1;
}
@ -99,7 +104,7 @@ int AI_Features_Manager::init_vector_db() {
"created_at INTEGER DEFAULT (strftime('%s' , 'now'))"
");";
if (vector_db->execute(create_anomaly_patterns) != 0) {
if (!vector_db->execute(create_anomaly_patterns)) {
proxy_error("AI: Failed to create anomaly_patterns table\n");
return -1;
}
@ -116,7 +121,7 @@ int AI_Features_Manager::init_vector_db() {
"timestamp INTEGER DEFAULT (strftime('%s' , 'now'))"
");";
if (vector_db->execute(create_query_history) != 0) {
if (!vector_db->execute(create_query_history)) {
proxy_error("AI: Failed to create query_history table\n");
return -1;
}
@ -127,10 +132,10 @@ int AI_Features_Manager::init_vector_db() {
// 1. LLM cache virtual table
const char* create_llm_vec =
"CREATE VIRTUAL TABLE IF NOT EXISTS llm_cache_vec USING vec0("
"embedding float(1536)"
"embedding float[1536]"
");";
if (vector_db->execute(create_llm_vec) != 0) {
if (!vector_db->execute(create_llm_vec)) {
proxy_error("AI: Failed to create llm_cache_vec virtual table\n");
// Virtual table creation failure is not critical - log and continue
proxy_debug(PROXY_DEBUG_GENAI, 3, "Continuing without llm_cache_vec");
@ -139,10 +144,10 @@ int AI_Features_Manager::init_vector_db() {
// 2. Anomaly patterns virtual table
const char* create_anomaly_vec =
"CREATE VIRTUAL TABLE IF NOT EXISTS anomaly_patterns_vec USING vec0("
"embedding float(1536)"
"embedding float[1536]"
");";
if (vector_db->execute(create_anomaly_vec) != 0) {
if (!vector_db->execute(create_anomaly_vec)) {
proxy_error("AI: Failed to create anomaly_patterns_vec virtual table\n");
proxy_debug(PROXY_DEBUG_GENAI, 3, "Continuing without anomaly_patterns_vec");
}
@ -150,10 +155,10 @@ int AI_Features_Manager::init_vector_db() {
// 3. Query history virtual table
const char* create_history_vec =
"CREATE VIRTUAL TABLE IF NOT EXISTS query_history_vec USING vec0("
"embedding float(1536)"
"embedding float[1536]"
");";
if (vector_db->execute(create_history_vec) != 0) {
if (!vector_db->execute(create_history_vec)) {
proxy_error("AI: Failed to create query_history_vec virtual table\n");
proxy_debug(PROXY_DEBUG_GENAI, 3, "Continuing without query_history_vec");
}
@ -181,7 +186,7 @@ int AI_Features_Manager::init_vector_db() {
"updated_at INTEGER NOT NULL DEFAULT (unixepoch())"
");";
if (vector_db->execute(create_rag_sources) != 0) {
if (!vector_db->execute(create_rag_sources)) {
proxy_error("AI: Failed to create rag_sources table\n");
return -1;
}
@ -190,7 +195,7 @@ int AI_Features_Manager::init_vector_db() {
const char* create_rag_sources_enabled_idx =
"CREATE INDEX IF NOT EXISTS idx_rag_sources_enabled ON rag_sources(enabled);";
if (vector_db->execute(create_rag_sources_enabled_idx) != 0) {
if (!vector_db->execute(create_rag_sources_enabled_idx)) {
proxy_error("AI: Failed to create idx_rag_sources_enabled index\n");
return -1;
}
@ -198,7 +203,7 @@ int AI_Features_Manager::init_vector_db() {
const char* create_rag_sources_backend_idx =
"CREATE INDEX IF NOT EXISTS idx_rag_sources_backend ON rag_sources(backend_type, backend_host, backend_port, backend_db, table_name);";
if (vector_db->execute(create_rag_sources_backend_idx) != 0) {
if (!vector_db->execute(create_rag_sources_backend_idx)) {
proxy_error("AI: Failed to create idx_rag_sources_backend index\n");
return -1;
}
@ -217,7 +222,7 @@ int AI_Features_Manager::init_vector_db() {
"deleted INTEGER NOT NULL DEFAULT 0"
");";
if (vector_db->execute(create_rag_documents) != 0) {
if (!vector_db->execute(create_rag_documents)) {
proxy_error("AI: Failed to create rag_documents table\n");
return -1;
}
@ -226,7 +231,7 @@ int AI_Features_Manager::init_vector_db() {
const char* create_rag_documents_source_updated_idx =
"CREATE INDEX IF NOT EXISTS idx_rag_documents_source_updated ON rag_documents(source_id, updated_at);";
if (vector_db->execute(create_rag_documents_source_updated_idx) != 0) {
if (!vector_db->execute(create_rag_documents_source_updated_idx)) {
proxy_error("AI: Failed to create idx_rag_documents_source_updated index\n");
return -1;
}
@ -234,7 +239,7 @@ int AI_Features_Manager::init_vector_db() {
const char* create_rag_documents_source_deleted_idx =
"CREATE INDEX IF NOT EXISTS idx_rag_documents_source_deleted ON rag_documents(source_id, deleted);";
if (vector_db->execute(create_rag_documents_source_deleted_idx) != 0) {
if (!vector_db->execute(create_rag_documents_source_deleted_idx)) {
proxy_error("AI: Failed to create idx_rag_documents_source_deleted index\n");
return -1;
}
@ -253,7 +258,7 @@ int AI_Features_Manager::init_vector_db() {
"deleted INTEGER NOT NULL DEFAULT 0"
");";
if (vector_db->execute(create_rag_chunks) != 0) {
if (!vector_db->execute(create_rag_chunks)) {
proxy_error("AI: Failed to create rag_chunks table\n");
return -1;
}
@ -262,7 +267,7 @@ int AI_Features_Manager::init_vector_db() {
const char* create_rag_chunks_doc_idx =
"CREATE UNIQUE INDEX IF NOT EXISTS uq_rag_chunks_doc_idx ON rag_chunks(doc_id, chunk_index);";
if (vector_db->execute(create_rag_chunks_doc_idx) != 0) {
if (!vector_db->execute(create_rag_chunks_doc_idx)) {
proxy_error("AI: Failed to create uq_rag_chunks_doc_idx index\n");
return -1;
}
@ -270,7 +275,7 @@ int AI_Features_Manager::init_vector_db() {
const char* create_rag_chunks_source_doc_idx =
"CREATE INDEX IF NOT EXISTS idx_rag_chunks_source_doc ON rag_chunks(source_id, doc_id);";
if (vector_db->execute(create_rag_chunks_source_doc_idx) != 0) {
if (!vector_db->execute(create_rag_chunks_source_doc_idx)) {
proxy_error("AI: Failed to create idx_rag_chunks_source_doc index\n");
return -1;
}
@ -278,7 +283,7 @@ int AI_Features_Manager::init_vector_db() {
const char* create_rag_chunks_deleted_idx =
"CREATE INDEX IF NOT EXISTS idx_rag_chunks_deleted ON rag_chunks(deleted);";
if (vector_db->execute(create_rag_chunks_deleted_idx) != 0) {
if (!vector_db->execute(create_rag_chunks_deleted_idx)) {
proxy_error("AI: Failed to create idx_rag_chunks_deleted index\n");
return -1;
}
@ -292,7 +297,7 @@ int AI_Features_Manager::init_vector_db() {
"tokenize = 'unicode61'"
");";
if (vector_db->execute(create_rag_fts_chunks) != 0) {
if (!vector_db->execute(create_rag_fts_chunks)) {
proxy_error("AI: Failed to create rag_fts_chunks virtual table\n");
proxy_debug(PROXY_DEBUG_GENAI, 3, "Continuing without rag_fts_chunks");
}
@ -306,7 +311,7 @@ int AI_Features_Manager::init_vector_db() {
std::string create_rag_vec_chunks_sql =
"CREATE VIRTUAL TABLE IF NOT EXISTS rag_vec_chunks USING vec0("
"embedding float(" + std::to_string(vector_dimension) + "), "
"embedding float[" + std::to_string(vector_dimension) + "], "
"chunk_id TEXT, "
"doc_id TEXT, "
"source_id INTEGER, "
@ -315,7 +320,7 @@ int AI_Features_Manager::init_vector_db() {
const char* create_rag_vec_chunks = create_rag_vec_chunks_sql.c_str();
if (vector_db->execute(create_rag_vec_chunks) != 0) {
if (!vector_db->execute(create_rag_vec_chunks)) {
proxy_error("AI: Failed to create rag_vec_chunks virtual table\n");
proxy_debug(PROXY_DEBUG_GENAI, 3, "Continuing without rag_vec_chunks");
}
@ -338,7 +343,7 @@ int AI_Features_Manager::init_vector_db() {
"JOIN rag_documents d ON d.doc_id = c.doc_id "
"WHERE c.deleted = 0 AND d.deleted = 0;";
if (vector_db->execute(create_rag_chunk_view) != 0) {
if (!vector_db->execute(create_rag_chunk_view)) {
proxy_error("AI: Failed to create rag_chunk_view view\n");
proxy_debug(PROXY_DEBUG_GENAI, 3, "Continuing without rag_chunk_view");
}
@ -353,7 +358,7 @@ int AI_Features_Manager::init_vector_db() {
"last_error TEXT"
");";
if (vector_db->execute(create_rag_sync_state) != 0) {
if (!vector_db->execute(create_rag_sync_state)) {
proxy_error("AI: Failed to create rag_sync_state table\n");
return -1;
}

@ -92,8 +92,8 @@ using json = nlohmann::json;
*
* @see https://github.com/asg017/sqlite-vec for sqlite-vec documentation
*/
extern "C" int (*proxy_sqlite3_vec_init)(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi);
extern "C" int (*proxy_sqlite3_rembed_init)(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi);
extern int (*proxy_sqlite3_vec_init)(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi);
extern int (*proxy_sqlite3_rembed_init)(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi);
#include "microhttpd.h"
#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || defined(__mips__)) && defined(__linux)

@ -1092,6 +1092,10 @@ void ProxySQL_Admin::flush_genai_variables___database_to_runtime(SQLite3DB* db,
}
}
if (GloAI && GloGATH->variables.genai_enabled) {
GloAI->init();
}
if (lock) wrunlock();
}
if (resultset) delete resultset;
@ -1378,120 +1382,8 @@ void ProxySQL_Admin::flush_mcp_variables___database_to_runtime(SQLite3DB* db, bo
pthread_mutex_unlock(&GloVars.checksum_mutex);
}
// Handle server start/stop based on mcp_enabled
bool enabled = GloMCPH->variables.mcp_enabled;
proxy_info("MCP: mcp_enabled=%d after loading variables\n", enabled);
if (enabled) {
// Start the server if not already running
if (GloMCPH->mcp_server == NULL) {
// Only check SSL certificates if SSL mode is enabled
if (GloMCPH->variables.mcp_use_ssl) {
if (!GloVars.global.ssl_key_pem_mem || !GloVars.global.ssl_cert_pem_mem) {
proxy_error("MCP: Cannot start server in SSL mode - SSL certificates not loaded. "
"Please configure ssl_key_fp and ssl_cert_fp, or set mcp_use_ssl=false.\n");
} else {
int port = GloMCPH->variables.mcp_port;
const char* mode = GloMCPH->variables.mcp_use_ssl ? "HTTPS" : "HTTP";
proxy_info("MCP: Starting %s server on port %d\n", mode, port);
GloMCPH->mcp_server = new ProxySQL_MCP_Server(port, GloMCPH);
if (GloMCPH->mcp_server) {
GloMCPH->mcp_server->start();
proxy_info("MCP: Server started successfully\n");
} else {
proxy_error("MCP: Failed to create server instance\n");
}
}
} else {
// HTTP mode - start without SSL certificates
int port = GloMCPH->variables.mcp_port;
proxy_info("MCP: Starting HTTP server on port %d (unencrypted)\n", port);
GloMCPH->mcp_server = new ProxySQL_MCP_Server(port, GloMCPH);
if (GloMCPH->mcp_server) {
GloMCPH->mcp_server->start();
proxy_info("MCP: Server started successfully\n");
} else {
proxy_error("MCP: Failed to create server instance\n");
}
}
} else {
proxy_info("MCP: Server already running, checking if configuration changed...\n");
// Check if restart is needed due to configuration changes
bool needs_restart = false;
std::string restart_reason;
// Check if port changed
int current_port = GloMCPH->variables.mcp_port;
int server_port = GloMCPH->mcp_server->get_port();
if (current_port != server_port) {
needs_restart = true;
restart_reason += "port (" + std::to_string(server_port) + " -> " + std::to_string(current_port) + ") ";
}
// Check if SSL mode changed
bool current_use_ssl = GloMCPH->variables.mcp_use_ssl;
bool server_use_ssl = GloMCPH->mcp_server->is_using_ssl();
if (current_use_ssl != server_use_ssl) {
needs_restart = true;
restart_reason += "SSL mode (" + std::string(server_use_ssl ? "HTTPS" : "HTTP") + " -> " + std::string(current_use_ssl ? "HTTPS" : "HTTP") + ") ";
}
if (needs_restart) {
proxy_info("MCP: Configuration changed (%s), restarting server...\n", restart_reason.c_str());
// Stop server with old configuration
const char* old_mode = server_use_ssl ? "HTTPS" : "HTTP";
proxy_info("MCP: Stopping %s server on port %d\n", old_mode, server_port);
delete GloMCPH->mcp_server;
GloMCPH->mcp_server = NULL;
// Start server with new configuration
int new_port = GloMCPH->variables.mcp_port;
bool new_use_ssl = GloMCPH->variables.mcp_use_ssl;
const char* new_mode = new_use_ssl ? "HTTPS" : "HTTP";
// Check SSL certificates if needed
if (new_use_ssl) {
if (!GloVars.global.ssl_key_pem_mem || !GloVars.global.ssl_cert_pem_mem) {
proxy_error("MCP: Cannot start server in SSL mode - SSL certificates not loaded. "
"Please configure ssl_key_fp and ssl_cert_fp, or set mcp_use_ssl=false.\n");
// Leave server stopped
} else {
proxy_info("MCP: Starting %s server on port %d\n", new_mode, new_port);
GloMCPH->mcp_server = new ProxySQL_MCP_Server(new_port, GloMCPH);
if (GloMCPH->mcp_server) {
GloMCPH->mcp_server->start();
proxy_info("MCP: Server restarted successfully\n");
} else {
proxy_error("MCP: Failed to create server instance\n");
}
}
} else {
// HTTP mode - no SSL certificates needed
proxy_info("MCP: Starting %s server on port %d (unencrypted)\n", new_mode, new_port);
GloMCPH->mcp_server = new ProxySQL_MCP_Server(new_port, GloMCPH);
if (GloMCPH->mcp_server) {
GloMCPH->mcp_server->start();
proxy_info("MCP: Server restarted successfully\n");
} else {
proxy_error("MCP: Failed to create server instance\n");
}
}
} else {
proxy_info("MCP: Server already running, no configuration changes detected\n");
}
}
} else {
// Stop the server if running
if (GloMCPH->mcp_server != NULL) {
const char* mode = GloMCPH->variables.mcp_use_ssl ? "HTTPS" : "HTTP";
proxy_info("MCP: Stopping %s server\n", mode);
delete GloMCPH->mcp_server;
GloMCPH->mcp_server = NULL;
proxy_info("MCP: Server stopped successfully\n");
}
}
// Manage MCP server state
load_mcp_server();
if (lock) wrunlock();
delete resultset;
@ -1587,80 +1479,6 @@ void ProxySQL_Admin::flush_mcp_variables___runtime_to_database(SQLite3DB* db, bo
free(qualified_name);
}
proxy_info("MCP: Finished processing %d variables\n", var_count);
// Handle server start/stop based on mcp_enabled when runtime=true
// This ensures the server state matches the enabled flag after loading to runtime
if (runtime) {
bool enabled = GloMCPH->variables.mcp_enabled;
proxy_info("MCP: mcp_enabled=%d, managing server state\n", enabled);
if (enabled) {
// Start the server if not already running
if (GloMCPH->mcp_server == NULL) {
// Only check SSL certificates if SSL mode is enabled
if (GloMCPH->variables.mcp_use_ssl) {
if (!GloVars.global.ssl_key_pem_mem || !GloVars.global.ssl_cert_pem_mem) {
proxy_error("MCP: Cannot start server in SSL mode - SSL certificates not loaded. "
"Please configure ssl_key_fp and ssl_cert_fp, or set mcp_use_ssl=false.\n");
} else {
int port = GloMCPH->variables.mcp_port;
const char* mode = GloMCPH->variables.mcp_use_ssl ? "HTTPS" : "HTTP";
proxy_info("MCP: Starting %s server on port %d\n", mode, port);
GloMCPH->mcp_server = new ProxySQL_MCP_Server(port, GloMCPH);
if (GloMCPH->mcp_server) {
GloMCPH->mcp_server->start();
proxy_info("MCP: Server started successfully\n");
} else {
proxy_error("MCP: Failed to create server instance\n");
}
}
} else {
// HTTP mode - start without SSL certificates
int port = GloMCPH->variables.mcp_port;
proxy_info("MCP: Starting HTTP server on port %d (unencrypted)\n", port);
GloMCPH->mcp_server = new ProxySQL_MCP_Server(port, GloMCPH);
if (GloMCPH->mcp_server) {
GloMCPH->mcp_server->start();
proxy_info("MCP: Server started successfully\n");
} else {
proxy_error("MCP: Failed to create server instance\n");
}
}
} else {
// Server is already running - need to stop, delete server, and recreate everything
proxy_info("MCP: Server already running, reinitializing\n");
// Delete the old server - its destructor will clean up all handlers
// (mysql_tool_handler, config_tool_handler, query_tool_handler,
// admin_tool_handler, cache_tool_handler, observe_tool_handler)
proxy_info("MCP: Stopping and deleting old server\n");
delete GloMCPH->mcp_server;
GloMCPH->mcp_server = NULL;
// All handlers are now deleted and set to NULL by the destructor
proxy_info("MCP: Old server deleted\n");
// Create and start new server with current configuration
// The server constructor will recreate all handlers with updated settings
proxy_info("MCP: Creating and starting new server\n");
int port = GloMCPH->variables.mcp_port;
GloMCPH->mcp_server = new ProxySQL_MCP_Server(port, GloMCPH);
if (GloMCPH->mcp_server) {
GloMCPH->mcp_server->start();
proxy_info("MCP: New server created and started successfully\n");
} else {
proxy_error("MCP: Failed to create new server instance\n");
}
}
} else {
// Stop the server if running
if (GloMCPH->mcp_server != NULL) {
const char* mode = GloMCPH->variables.mcp_use_ssl ? "HTTPS" : "HTTP";
proxy_info("MCP: Stopping %s server\n", mode);
delete GloMCPH->mcp_server;
GloMCPH->mcp_server = NULL;
proxy_info("MCP: Server stopped successfully\n");
}
}
}
if (use_lock) {
proxy_info("MCP: Releasing lock\n");

@ -40,6 +40,8 @@ bool MCP_JSONRPC_Resource::authenticate_request(const httpserver::http_request&
expected_token = handler->variables.mcp_admin_endpoint_auth;
} else if (endpoint_name == "cache") {
expected_token = handler->variables.mcp_cache_endpoint_auth;
} else if (endpoint_name == "rag") {
expected_token = handler->variables.mcp_rag_endpoint_auth;
} else {
proxy_error("MCP authentication on %s: unknown endpoint\n", endpoint_name.c_str());
return false;

@ -23,6 +23,7 @@ static const char* mcp_thread_variables_names[] = {
"query_endpoint_auth",
"admin_endpoint_auth",
"cache_endpoint_auth",
"rag_endpoint_auth",
"timeout_ms",
// MySQL Tool Handler configuration
"mysql_hosts",
@ -48,6 +49,7 @@ MCP_Threads_Handler::MCP_Threads_Handler() {
variables.mcp_query_endpoint_auth = strdup("");
variables.mcp_admin_endpoint_auth = strdup("");
variables.mcp_cache_endpoint_auth = strdup("");
variables.mcp_rag_endpoint_auth = strdup("");
variables.mcp_timeout_ms = 30000;
// MySQL Tool Handler default values
variables.mcp_mysql_hosts = strdup("127.0.0.1");
@ -83,6 +85,8 @@ MCP_Threads_Handler::~MCP_Threads_Handler() {
free(variables.mcp_admin_endpoint_auth);
if (variables.mcp_cache_endpoint_auth)
free(variables.mcp_cache_endpoint_auth);
if (variables.mcp_rag_endpoint_auth)
free(variables.mcp_rag_endpoint_auth);
// Free MySQL Tool Handler variables
if (variables.mcp_mysql_hosts)
free(variables.mcp_mysql_hosts);
@ -198,6 +202,10 @@ int MCP_Threads_Handler::get_variable(const char* name, char* val) {
sprintf(val, "%s", variables.mcp_cache_endpoint_auth ? variables.mcp_cache_endpoint_auth : "");
return 0;
}
if (!strcmp(name, "rag_endpoint_auth")) {
sprintf(val, "%s", variables.mcp_rag_endpoint_auth ? variables.mcp_rag_endpoint_auth : "");
return 0;
}
if (!strcmp(name, "timeout_ms")) {
sprintf(val, "%d", variables.mcp_timeout_ms);
return 0;
@ -291,6 +299,12 @@ int MCP_Threads_Handler::set_variable(const char* name, const char* value) {
variables.mcp_cache_endpoint_auth = strdup(value);
return 0;
}
if (!strcmp(name, "rag_endpoint_auth")) {
if (variables.mcp_rag_endpoint_auth)
free(variables.mcp_rag_endpoint_auth);
variables.mcp_rag_endpoint_auth = strdup(value);
return 0;
}
if (!strcmp(name, "timeout_ms")) {
int timeout = atoi(value);
if (timeout >= 0) {

@ -95,6 +95,9 @@ HEADERS := ../include/*.h ../include/*.hpp
%.ko: %.cpp $(HEADERS)
$(CXX) -fPIC -c -o $@ $< $(MYCXXFLAGS) $(CXXFLAGS)
$(ODIR)/proxy_sqlite3_symbols.oo: proxy_sqlite3_symbols.cpp $(HEADERS)
$(CXX) -fPIC -c -o $@ $< $(MYCXXFLAGS) $(CXXFLAGS) -DSQLITE_CORE -DSQLITE_VEC_STATIC
$(ODIR)/%.oo: %.cpp $(HEADERS)
$(CXX) -fPIC -c -o $@ $< $(MYCXXFLAGS) $(CXXFLAGS)

@ -45,6 +45,7 @@ using json = nlohmann::json;
#include "MySQL_Logger.hpp"
#include "PgSQL_Logger.hpp"
#include "MCP_Thread.h"
#include "ProxySQL_MCP_Server.hpp"
#include "SQLite3_Server.h"
#include "Web_Interface.hpp"
@ -3251,6 +3252,123 @@ void ProxySQL_Admin::load_restapi_server() {
}
}
void ProxySQL_Admin::load_mcp_server() {
if (!all_modules_started) { return; }
if (GloMCPH == NULL) { return; }
// 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);
if (port_free == false) {
if (e_port_check == -1) {
proxy_error("Unable to start MCP Server, failed to set 'SO_REUSEADDR' to check port availability.\n");
} else if (e_port_check == -2) {
proxy_error("Unable to start MCP Server, invalid port check parameters.\n");
} else {
proxy_error("Unable to start MCP Server, port '%d' already in use.\n", port);
}
}
};
// Check if MCP server is enabled and needs management
bool enabled = GloMCPH->variables.mcp_enabled;
if (enabled) {
if (GloMCPH->mcp_server == NULL) {
// Start the server if not running
int port = GloMCPH->variables.mcp_port;
bool use_ssl = GloMCPH->variables.mcp_use_ssl;
// Check SSL certificates if SSL mode is enabled
if (use_ssl) {
if (!GloVars.global.ssl_key_pem_mem || !GloVars.global.ssl_cert_pem_mem) {
proxy_error("MCP: Cannot start server in SSL mode - SSL certificates not loaded. "
"Please configure ssl_key_fp and ssl_cert_fp, or set mcp_use_ssl=false.\n");
return;
}
}
// Check port availability
bool port_free = false;
check_mcp_port(port, port_free);
if (port_free) {
proxy_info("MCP: Starting server on port %d\n", port);
GloMCPH->mcp_server = new ProxySQL_MCP_Server(port, GloMCPH);
if (GloMCPH->mcp_server) {
GloMCPH->mcp_server->start();
proxy_info("MCP: Server started successfully\n");
} else {
proxy_error("MCP: Failed to create server instance\n");
}
}
} else {
// Server is already running, check if restart is needed
int current_port = GloMCPH->variables.mcp_port;
int server_port = GloMCPH->mcp_server->get_port();
bool current_use_ssl = GloMCPH->variables.mcp_use_ssl;
bool server_use_ssl = GloMCPH->mcp_server->is_using_ssl();
bool needs_restart = false;
std::string restart_reason;
if (current_port != server_port) {
needs_restart = true;
restart_reason += "port (" + std::to_string(server_port) + " -> " + std::to_string(current_port) + ") ";
}
if (current_use_ssl != server_use_ssl) {
needs_restart = true;
restart_reason += "SSL mode";
}
if (needs_restart) {
proxy_info("MCP: Configuration changed (%s), restarting server...\n", restart_reason.c_str());
proxy_info("MCP: Stopping server on port %d\n", server_port);
delete GloMCPH->mcp_server;
GloMCPH->mcp_server = NULL;
proxy_info("MCP: Old server deleted\n");
// Check SSL certificates if SSL mode is enabled
if (current_use_ssl) {
if (!GloVars.global.ssl_key_pem_mem || !GloVars.global.ssl_cert_pem_mem) {
proxy_error("MCP: Cannot start server in SSL mode - SSL certificates not loaded. "
"Please configure ssl_key_fp and ssl_cert_fp, or set mcp_use_ssl=false.\n");
return;
}
}
// Check port availability before starting new server
bool port_free = false;
check_mcp_port(current_port, port_free);
if (port_free) {
proxy_info("MCP: Starting server on port %d\n", current_port);
GloMCPH->mcp_server = new ProxySQL_MCP_Server(current_port, GloMCPH);
if (GloMCPH->mcp_server) {
GloMCPH->mcp_server->start();
proxy_info("MCP: Server restarted successfully\n");
} else {
proxy_error("MCP: Failed to create server instance\n");
}
}
} else {
proxy_info("MCP: Server already running, no configuration changes detected\n");
}
}
} else {
// Stop the server if running and disabled
if (GloMCPH->mcp_server != NULL) {
proxy_info("MCP: Stopping server\n");
delete GloMCPH->mcp_server;
GloMCPH->mcp_server = NULL;
proxy_info("MCP: Server stopped successfully\n");
}
}
}
void ProxySQL_Admin::load_http_server() {
if (!all_modules_started) { return; }

@ -1180,12 +1180,12 @@ json RAG_Tool_Handler::execute_tool(const std::string& tool_name, const json& ar
// Build FTS query with filters
std::string sql = "SELECT c.chunk_id, c.doc_id, c.source_id, "
"(SELECT name FROM rag_sources WHERE source_id = c.source_id) as source_name, "
"c.title, bm25(f) as score_fts_raw, "
"c.title, bm25(rag_fts_chunks) as score_fts_raw, "
"c.metadata_json, c.body "
"FROM rag_fts_chunks f "
"JOIN rag_chunks c ON c.chunk_id = f.chunk_id "
"JOIN rag_documents d ON d.doc_id = c.doc_id "
"WHERE f MATCH '" + escape_fts_query(query) + "'";
"WHERE rag_fts_chunks MATCH '" + escape_fts_query(query) + "'";
// Apply filters using consolidated filter building function
if (!build_sql_filters(filters, sql)) {
@ -1536,12 +1536,12 @@ json RAG_Tool_Handler::execute_tool(const std::string& tool_name, const json& ar
// Run FTS search with filters
std::string fts_sql = "SELECT c.chunk_id, c.doc_id, c.source_id, "
"(SELECT name FROM rag_sources WHERE source_id = c.source_id) as source_name, "
"c.title, bm25(f) as score_fts_raw, "
"c.title, bm25(rag_fts_chunks) as score_fts_raw, "
"c.metadata_json "
"FROM rag_fts_chunks f "
"JOIN rag_chunks c ON c.chunk_id = f.chunk_id "
"JOIN rag_documents d ON d.doc_id = c.doc_id "
"WHERE f MATCH '" + escape_fts_query(query) + "'";
"WHERE rag_fts_chunks MATCH '" + escape_fts_query(query) + "'";
// Apply filters using consolidated filter building function
if (!build_sql_filters(filters, fts_sql)) {
@ -1919,7 +1919,7 @@ json RAG_Tool_Handler::execute_tool(const std::string& tool_name, const json& ar
"FROM rag_fts_chunks f "
"JOIN rag_chunks c ON c.chunk_id = f.chunk_id "
"JOIN rag_documents d ON d.doc_id = c.doc_id "
"WHERE f MATCH '" + escape_fts_query(query) + "'";
"WHERE rag_fts_chunks MATCH '" + escape_fts_query(query) + "'";
// Apply filters using consolidated filter building function
if (!build_sql_filters(filters, fts_sql)) {
@ -2002,7 +2002,7 @@ json RAG_Tool_Handler::execute_tool(const std::string& tool_name, const json& ar
fts_sql += " AND json_extract(d.metadata_json, '$.CreationDate') <= '" + created_before + "'";
}
fts_sql += " ORDER BY bm25(f) "
fts_sql += " ORDER BY bm25(rag_fts_chunks) "
"LIMIT " + std::to_string(candidates_k);
SQLite3_result* fts_result = execute_query(fts_sql.c_str());

@ -1,4 +1,5 @@
#include "sqlite3.h"
#include "sqlite-vec.h"
#include <cstddef>
#include "sqlite3db.h"
// Forward declarations for proxy types
@ -50,9 +51,10 @@ int (*proxy_sqlite3_prepare_v2)(sqlite3*, const char*, int, sqlite3_stmt**, cons
int (*proxy_sqlite3_open_v2)(const char*, sqlite3**, int, const char*) = sqlite3_open_v2;
int (*proxy_sqlite3_exec)(sqlite3*, const char*, int (*)(void*,int,char**,char**), void*, char**) = sqlite3_exec;
// Optional hooks used by sqlite-vec (function pointers will be set by LoadPlugin or remain NULL)
void (*proxy_sqlite3_vec_init)(sqlite3*, char**, const sqlite3_api_routines*) = NULL;
void (*proxy_sqlite3_rembed_init)(sqlite3*, char**, const sqlite3_api_routines*) = NULL;
// Hooks for sqlite-vec and sqlite-rembed
int (*proxy_sqlite3_vec_init)(sqlite3*, char**, const sqlite3_api_routines*) = sqlite3_vec_init;
// TODO: Fix sqlite-rembed header inclusion and assign the function pointer properly
int (*proxy_sqlite3_rembed_init)(sqlite3*, char**, const sqlite3_api_routines*) = NULL;
// Internal helpers used by admin stats batching; keep defaults as NULL

Loading…
Cancel
Save