Skip to content
Merged
854 changes: 854 additions & 0 deletions doc/MCP/FTS_USER_GUIDE.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions include/MCP_Thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class MCP_Threads_Handler
char* mcp_mysql_password; ///< MySQL password for tool connections
char* mcp_mysql_schema; ///< Default schema/database
char* mcp_catalog_path; ///< Path to catalog SQLite database
char* mcp_fts_path; ///< Path to FTS SQLite database
} variables;

/**
Expand Down
204 changes: 204 additions & 0 deletions include/MySQL_FTS.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
#ifndef CLASS_MYSQL_FTS_H
#define CLASS_MYSQL_FTS_H

#include "sqlite3db.h"
#include <string>
#include <vector>

// Forward declaration
class MySQL_Tool_Handler;

/**
* @brief MySQL Full Text Search (FTS) for Fast Data Discovery
*
* This class manages a dedicated SQLite database that provides:
* - Full-text search indexes for MySQL tables
* - Fast data discovery before querying the actual MySQL database
* - Cross-table search capabilities
* - BM25 ranking with FTS5
*
* The FTS system serves as a fast local cache for AI agents to quickly
* find relevant data before making targeted queries to MySQL backend.
*/
class MySQL_FTS {
private:
SQLite3DB* db;
std::string db_path;

/**
* @brief Initialize FTS schema
* @return 0 on success, -1 on error
*/
int init_schema();

/**
* @brief Create FTS metadata tables
* @return 0 on success, -1 on error
*/
int create_tables();

/**
* @brief Create per-index tables (data and FTS5 virtual table)
* @param schema Schema name
* @param table Table name
* @return 0 on success, -1 on error
*/
int create_index_tables(const std::string& schema, const std::string& table);

/**
* @brief Get sanitized data table name for a schema.table
* @param schema Schema name
* @param table Table name
* @return Sanitized table name
*/
std::string get_data_table_name(const std::string& schema, const std::string& table);

/**
* @brief Get FTS search table name for a schema.table
* @param schema Schema name
* @param table Table name
* @return Sanitized FTS table name
*/
std::string get_fts_table_name(const std::string& schema, const std::string& table);

/**
* @brief Sanitize a name for use as SQLite table name
* @param name Name to sanitize
* @return Sanitized name
*/
std::string sanitize_name(const std::string& name);

/**
* @brief Escape single quotes for SQL
* @param str String to escape
* @return Escaped string
*/
std::string escape_sql(const std::string& str);

/**
* @brief Escape identifier for SQLite (double backticks)
* @param identifier Identifier to escape
* @return Escaped identifier
*/
std::string escape_identifier(const std::string& identifier);

public:
/**
* @brief Constructor
* @param path Path to the FTS database file
*/
MySQL_FTS(const std::string& path);

// Prevent copy and move (class owns raw pointer)
MySQL_FTS(const MySQL_FTS&) = delete;
MySQL_FTS& operator=(const MySQL_FTS&) = delete;
MySQL_FTS(MySQL_FTS&&) = delete;
MySQL_FTS& operator=(MySQL_FTS&&) = delete;

/**
* @brief Destructor
*/
~MySQL_FTS();

/**
* @brief Initialize the FTS database
* @return 0 on success, -1 on error
*/
int init();

/**
* @brief Close the FTS database
*/
void close();

/**
* @brief Check if an index exists for a schema.table
* @param schema Schema name
* @param table Table name
* @return true if exists, false otherwise
*/
bool index_exists(const std::string& schema, const std::string& table);

/**
* @brief Create and populate an FTS index for a MySQL table
*
* @param schema Schema name
* @param table Table name
* @param columns JSON array of column names to index
* @param primary_key Primary key column name
* @param where_clause Optional WHERE clause for filtering
* @param mysql_handler Pointer to MySQL_Tool_Handler for executing queries
* @return JSON result with success status and metadata
*/
std::string index_table(
const std::string& schema,
const std::string& table,
const std::string& columns,
const std::string& primary_key,
const std::string& where_clause,
MySQL_Tool_Handler* mysql_handler
);

/**
* @brief Search indexed data using FTS5
*
* @param query FTS5 search query
* @param schema Optional schema filter
* @param table Optional table filter
* @param limit Max results (default 100)
* @param offset Pagination offset (default 0)
* @return JSON result with matches and snippets
*/
std::string search(
const std::string& query,
const std::string& schema = "",
const std::string& table = "",
int limit = 100,
int offset = 0
);

/**
* @brief List all FTS indexes with metadata
* @return JSON array of indexes
*/
std::string list_indexes();

/**
* @brief Remove an FTS index
*
* @param schema Schema name
* @param table Table name
* @return JSON result
*/
std::string delete_index(const std::string& schema, const std::string& table);

/**
* @brief Refresh an index with fresh data (full rebuild)
*
* @param schema Schema name
* @param table Table name
* @param mysql_handler Pointer to MySQL_Tool_Handler for executing queries
* @return JSON result
*/
std::string reindex(
const std::string& schema,
const std::string& table,
MySQL_Tool_Handler* mysql_handler
);

/**
* @brief Rebuild ALL FTS indexes with fresh data
*
* @param mysql_handler Pointer to MySQL_Tool_Handler for executing queries
* @return JSON result with summary
*/
std::string rebuild_all(MySQL_Tool_Handler* mysql_handler);

/**
* @brief Get database handle for direct access
* @return SQLite3DB pointer
*/
SQLite3DB* get_db() { return db; }
};

#endif /* CLASS_MYSQL_FTS_H */
101 changes: 93 additions & 8 deletions include/MySQL_Tool_Handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define CLASS_MYSQL_TOOL_HANDLER_H

#include "MySQL_Catalog.h"
#include "MySQL_FTS.h"
#include "cpp.h"
#include <string>
#include <memory>
Expand Down Expand Up @@ -51,6 +52,10 @@ class MySQL_Tool_Handler {
// Catalog for LLM memory
MySQL_Catalog* catalog; ///< SQLite catalog for LLM discoveries

// FTS for fast data discovery
MySQL_FTS* fts; ///< SQLite FTS for full-text search
pthread_mutex_t fts_lock; ///< Mutex protecting FTS lifecycle/usage

// Query guardrails
int max_rows; ///< Maximum rows to return (default 200)
int timeout_ms; ///< Query timeout in milliseconds (default 2000)
Expand All @@ -74,13 +79,6 @@ class MySQL_Tool_Handler {
*/
void return_connection(MYSQL* mysql);

/**
* @brief Execute a query and return results as JSON
* @param query SQL query to execute
* @return JSON with results or error
*/
std::string execute_query(const std::string& query);

/**
* @brief Validate SQL is read-only
* @param query SQL to validate
Expand Down Expand Up @@ -111,16 +109,25 @@ class MySQL_Tool_Handler {
* @param password MySQL password
* @param schema Default schema/database
* @param catalog_path Path to catalog database
* @param fts_path Path to FTS database
*/
MySQL_Tool_Handler(
const std::string& hosts,
const std::string& ports,
const std::string& user,
const std::string& password,
const std::string& schema,
const std::string& catalog_path
const std::string& catalog_path,
const std::string& fts_path = ""
);

/**
* @brief Reset FTS database path at runtime
* @param path New SQLite FTS database path
* @return true on success, false on error
*/
bool reset_fts_path(const std::string& path);

/**
* @brief Destructor
*/
Expand All @@ -137,6 +144,13 @@ class MySQL_Tool_Handler {
*/
void close();

/**
* @brief Execute a query and return results as JSON
* @param query SQL query to execute
* @return JSON with results or error
*/
std::string execute_query(const std::string& query);

// ========== Inventory Tools ==========

/**
Expand Down Expand Up @@ -389,6 +403,77 @@ class MySQL_Tool_Handler {
* @return JSON result
*/
std::string catalog_delete(const std::string& kind, const std::string& key);

// ========== FTS Tools (Full Text Search) ==========

/**
* @brief Create and populate an FTS index for a MySQL table
* @param schema Schema name
* @param table Table name
* @param columns JSON array of column names to index
* @param primary_key Primary key column name
* @param where_clause Optional WHERE clause for filtering
* @return JSON result with success status and metadata
*/
std::string fts_index_table(
const std::string& schema,
const std::string& table,
const std::string& columns,
const std::string& primary_key,
const std::string& where_clause = ""
);

/**
* @brief Search indexed data using FTS5
* @param query FTS5 search query
* @param schema Optional schema filter
* @param table Optional table filter
* @param limit Max results (default 100)
* @param offset Pagination offset (default 0)
* @return JSON result with matches and snippets
*/
std::string fts_search(
const std::string& query,
const std::string& schema = "",
const std::string& table = "",
int limit = 100,
int offset = 0
);

/**
* @brief List all FTS indexes with metadata
* @return JSON array of indexes
*/
std::string fts_list_indexes();

/**
* @brief Remove an FTS index
* @param schema Schema name
* @param table Table name
* @return JSON result
*/
std::string fts_delete_index(const std::string& schema, const std::string& table);

/**
* @brief Refresh an index with fresh data (full rebuild)
* @param schema Schema name
* @param table Table name
* @return JSON result
*/
std::string fts_reindex(const std::string& schema, const std::string& table);

/**
* @brief Rebuild ALL FTS indexes with fresh data
* @return JSON result with summary
*/
std::string fts_rebuild_all();

/**
* @brief Reinitialize FTS handler with a new database path
* @param fts_path New path to FTS database
* @return 0 on success, -1 on error
*/
int reinit_fts(const std::string& fts_path);
};

#endif /* CLASS_MYSQL_TOOL_HANDLER_H */
Loading