Back to Proxysql

ProxySQL GenAI Plugin — Reference

plugins/genai/README.md

4.0.1010.9 KB
Original Source

ProxySQL GenAI Plugin — Reference

1. Overview

The genai plugin is a dynamically loaded plugin that adds GenAI, MCP (Model Context Protocol), RAG (Retrieval-Augmented Generation), and SQL anomaly-detection capabilities to ProxySQL. It hosts the MCP HTTP listener, the LLM bridge (OpenAI / Anthropic / generic / local llama.cpp), the FTS- and vector-backed schema discovery cache, and a pre-execution query hook that feeds the anomaly detector.

This is a v4.0+ plugin: it requires the chassis (PROXYSQL40=1) and is built only when the operator explicitly opts in. Without the flag, none of this code ships in proxysql — the binary stays a plain MySQL/PgSQL proxy.

What the plugin owns

SubsystemClassHeader
MCP listener + lifecycleMCP_Threads_HandlerMCP_Thread.h
MCP HTTP serverProxySQL_MCP_ServerProxySQL_MCP_Server.hpp
GenAI worker poolGenAI_Threads_HandlerGenAI_Thread.h
AI features managerAI_Features_ManagerAI_Features_Manager.h
Anomaly detectionAnomaly_DetectorAnomaly_Detector.h
Schema discoveryDiscovery_SchemaDiscovery_Schema.h
Vector storageAI_Vector_StorageAI_Vector_Storage.h
MySQL / PgSQL static harvestersStatic_Harvester, PgSQL_Static_Harvester*Static_Harvester.h
LLM provider bridgeLLM_Bridge, LLM_ClientsLLM_Bridge.h
FTS index over MySQL catalogsMySQL_FTS, MySQL_CatalogMySQL_FTS.h / MySQL_Catalog.h
Tool handlers (per MCP endpoint)Admin_Tool_Handler, Cache_Tool_Handler, Config_Tool_Handler, MCP_Tool_Handler, MySQL_Tool_Handler, Observe_Tool_Handler, Query_Tool_Handler, RAG_Tool_Handler, Stats_Tool_Handlertool_handlers/*.h
Backend connection helperbackend_clientbackend_client.h

For deeper architecture see doc/MCP/Architecture.md and doc/LLM_Bridge/ARCHITECTURE.md in the repo root; this document covers only the plugin lifecycle, configuration, and operational contract.

2. Building

bash
PROXYSQL40=1 make            # builds proxysql + the .so
PROXYSQL40=1 make install    # installs both

PROXYSQL40=1 cascades to PROXYSQL31=1PROXYSQLFFTO=1 + PROXYSQLTSDB=1. The plugin .so lands at /usr/lib/proxysql/ProxySQL_GenAI_Plugin.so.

The build also pulls sqlite-vec from deps/ and links vec.o into libproxysql.a so the plugin's AI_Vector_Storage can register the extension via proxy_sqlite3_vec_init. v3.0 builds (no PROXYSQL40) skip both.

3. Loading

Add the plugin path to the plugins array in proxysql.cnf:

plugins = (
    "/usr/lib/proxysql/ProxySQL_GenAI_Plugin.so"
)

The plugins= line must be present before ProxySQL starts; plugins cannot be loaded after startup. Removing the line and restarting unloads the plugin cleanly.

4. Lifecycle

The chassis runs the plugin through five phases:

PhaseCallbackWhat the genai plugin does
A. load(chassis dlopen)reads the descriptor (abi_version=3)
B. register_schemasgenai_register_schemascalls genai_register_admin_tables to publish the MCP table set + the three runtime_mcp_* projections via services->register_runtime_view
C. admin bootstrap(chassis)materialises the registered SQLite schemas
D. initgenai_initPrometheus counters, query hook, constructs MCP_Threads_Handler / GenAI_Threads_Handler / AI_Features_Manager / Anomaly_Detector, registers admin SQL verbs
E. startgenai_startreads mcp-* and genai-* from main.global_variables, installs profiles, starts the MCP listener if mcp-enabled=true, kicks off genai_refresh_runtime_components

Teardown (genai_stop) is the reverse, in dependency order: listener → tool handlers → AI features → GenAI workers → atomic clear of the embed-fn hook → anomaly detector. Counters stay registered against the shared Prometheus registry (prometheus-cpp has no unregister API).

5. Admin SQL surface

Registered with the chassis command registry; chassis admin dispatcher routes by canonical name + alias.

CommandEffect
LOAD MCP VARIABLES TO RUNTIMEpush mcp-* from main.global_variables into MCP_Threads_Handler
LOAD MCP VARIABLES FROM DISKsync disk.global_variablesmain.global_variables (mcp-* slice), then implicit reload
LOAD MCP VARIABLES FROM CONFIGre-read mcp block from proxysql.cnf
SAVE MCP VARIABLES TO MEMORY / ... TO DISKreverse direction
LOAD MCP PROFILES TO RUNTIMEatomic install of main.mcp_auth_profiles + main.mcp_target_profiles into the in-memory snapshot, rebuilds joined target_auth_map
SAVE MCP PROFILES TO MEMORYatomic dump of in-memory snapshot back to both editable tables in one transaction
LOAD MCP QUERY RULES TO RUNTIMEinstall main.mcp_query_rules snapshot, attach to Discovery_Schema if listener up
SAVE MCP QUERY RULES TO MEMORYdump in-memory snapshot back to main.mcp_query_rules
LOAD GENAI VARIABLES TO RUNTIME / FROM CONFIGreload genai-* and reinit the GenAI/AI runtime stack
SAVE GENAI VARIABLES TO MEMORYdump runtime genai-* back to main.global_variables
LOAD/SAVE *PROFILES* / *QUERY RULES* TO/FROM DISKsync disk.*main.*

ABI 3 contract: runtime_mcp_* tables are chassis-projected views of module state. The plugin's project_*_to_runtime_view callbacks are the only writers; LOAD commands install snapshots into the module without touching runtime_<X>, and a SELECT against runtime_mcp_<X> triggers a fresh projection from the snapshot before returning rows.

6. Configuration

All mcp-* and genai-* keys live in main.global_variables. Canonical reference (with defaults and runtime semantics) is in doc/MCP/VARIABLES.md. The plugin re-reads on every LOAD ... TO RUNTIME command.

Tables owned by the plugin (registered in Phase B):

DBTablePurpose
adminmcp_query_ruleseditable query rules
adminmcp_auth_profileseditable backend auth profiles
adminmcp_target_profileseditable MCP target → hostgroup mapping
adminruntime_mcp_query_rules / runtime_mcp_auth_profiles / runtime_mcp_target_profileschassis-projected views (no persistent rows; refreshed per SELECT from module snapshot)
configpersistent copies of the three editable tables abovefor LOAD ... FROM DISK
statsstats_mcp_query_digest / stats_mcp_query_digest_resetMCP tool call digest statistics (periodic + reset variants)
statsstats_mcp_query_tools_counters / stats_mcp_query_tools_counters_resetper-endpoint tool invocation counters
statsstats_mcp_query_rulesrule hit counts
statsstats_genai_globalaggregated GenAI/MCP/AI status variables (21 counters)

7. Observability

Prometheus counters

Registered against the shared services->get_prometheus_registry():

CounterIncrements when
proxysql_genai_detected_anomalies_totalthe anomaly detector flags a query (any risk)
proxysql_genai_blocked_queries_totalthe detector blocks a query (DENY returned to client)

SQL status variables

SELECT * FROM stats_genai_global ORDER BY Variable_name;

Returns 21 counters aggregated from three handler classes:

Variable_name prefixSourceCount
genai_*GenAI_Threads_Handler4
mcp_*MCP_Threads_Handler3
llm_*AI_Features_Manager10
anomaly_*AI_Features_Manager3
daily_cloud_spend_usdAI_Features_Manager1

MCP query digest

SELECT * FROM stats_mcp_query_digest; — MCP tool call digest statistics with tool_name, run_id, fingerprint, count_star, timing (min/max/sum). Stats are persisted to SQLite and survive restarts. stats_mcp_query_digest_reset variant clears stats on read.

Plugin log lines route through services->log_message (see genai_log() in plugin_main.cpp). Severity follows syslog levels; the chassis writes to proxysql.log.

8. Concurrency notes

  • genai_anomaly_embed_fn is std::atomic with acquire/release semantics; the embedding back-end pointer's lifetime is paired with the GloGATH global it dereferences (see the long comment in Anomaly_Detector.cpp).
  • MCP_Threads_Handler variable accessors (get_variable, set_variable, has_variable, get_variables_list) all serialize on the handler's pthread_rwlock.
  • genai_refresh_runtime_components is currently called from the admin SQL thread without quiescing readers — operators triggering LOAD GENAI VARIABLES TO RUNTIME while traffic is hot should expect brief reload-window blips. Tracked as a follow-up — see the detailed contract comment on the declaration in genai_plugin.h.
  • Profile triplet: install_profiles_from_admin and save_profiles_to_admin_table are atomic across both tables: one wrlock for both swaps + one BEGIN/COMMIT for both writes, FK-aware delete order. Per-table install/save methods exist but only rebuild one half of the joined target_auth_map; prefer the combined variants from new code.

9. Testing

SuitePathCoverage
Plugin lifecycletest/tap/tests/unit/genai_plugin_load_unit-t.cppload → Phase B → init → start → LOAD MCP PROFILES → runtime view projection → SAVE round-trip → stop
Anomaly detectorgenai_plugin_anomaly_unit-t.cppnormalize_query, check_sql_injection (private-via-friend)
Backend clientgenai_plugin_backend_client_unit-t.cppparse + dial guards
FTS string utilsgenai_fts_string_unit-t.cppsanitize_name / escape_*
MCP variable accessorsgenai_mcp_thread_unit-t.cppget_variable / set_variable round-trip + concurrency
Discovery schemagenai_discovery_schema_unit-t.cppcatalog cache shape
LLM clientsgenai_llm_clients_unit-t.cppHTTP client wiring
Chassis runtime-view dispatchplugin_runtime_views_unit-t.cppend-to-end via real plugin .so

Integration tests live in the ai-g1 TAP group (run via test/infra/control/run-tests-isolated.bash TAP_GROUP=ai-g1); they spin up real MySQL 8.4 + PgSQL 16 backends and exercise the MCP endpoints + LLM surface end-to-end.

Known unit-test gaps

Admin_Tool_Handler, Cache_Tool_Handler, Config_Tool_Handler, Observe_Tool_Handler, and RAG_Tool_Handler have no dedicated unit test — coverage relies on ai-g1 integration tests. RAG is the most worth closing given vector-storage complexity.

10. Known gaps

  • genai_refresh_runtime_components race: see Concurrency notes above; needs a proper rwlock around GloGATH/GloAI consumers.
  • Tool-handler unit tests: see Testing section.

11. History

The plugin is the result of an 8-step carve-out (Steps 1–8b on this branch) that moved ~28K LOC out of ProxySQL core. The original design and per-step plans live in docs/superpowers/specs/2026-04-16-genai-plugin-carveout-design.md and adjacent files; the consolidated PR is #5701.