doc/MCP/Architecture.md
This document describes the architecture of the MCP (Model Context Protocol) module in ProxySQL, including endpoint design and tool handler implementation.
The MCP module implements JSON-RPC 2.0 over HTTPS for LLM (Large Language Model) integration with ProxySQL. It provides multiple endpoints, each designed to serve specific purposes while sharing a single HTTPS server.
/mcp/config, /mcp/query) that implements MCP protocoltools/list method (MCP protocol standard)┌─────────────────────────────────────────────────────────────────────────────┐
│ ProxySQL Process │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ MCP_Threads_Handler │ │
│ │ - Configuration variables (mcp-*) │ │
│ │ - Status variables │ │
│ │ - mcp_server (ProxySQL_MCP_Server) │ │
│ │ - config_tool_handler (NEW) │ │
│ │ - query_tool_handler (NEW) │ │
│ │ - admin_tool_handler (NEW) │ │
│ │ - cache_tool_handler (NEW) │ │
│ │ - stats_tool_handler (NEW) │ │
│ │ - ai_tool_handler (NEW) │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ ProxySQL_MCP_Server │ │
│ │ (Single HTTPS Server) │ │
│ │ │ │
│ │ Port: mcp-port (default 6071) │ │
│ │ SSL: Uses ProxySQL's certificates │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────┬──────────────┼──────────────┬──────────────┬─────────┐ │
│ ▼ ▼ ▼ ▼ ▼ ▼ │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌───┐│
│ │conf│ │sts │ │qry │ │adm │ │cach│ │ai ││
│ │TH │ │TH │ │TH │ │TH │ │TH │ │TH ││
│ └─┬──┘ └─┬──┘ └─┬──┘ └─┬──┘ └─┬──┘ └─┬─┘│
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │
│ Tools: Tools: Tools: Tools: Tools: │ │
│ - get_config - list_ - list_ - admin_ - get_ │ │
│ - set_config stats schemas - set_ cache │ │
│ - reload - show_ - list_ - reload - set_ │ │
│ metrics tables - invalidate │ │
│ - query │ │
│ │ │
│ ┌────────────────────────────────────────────┐ │
│ │ MySQL Backend │ │
│ │ (Connection Pool) │ │
│ └────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
Where:
TH = Tool Handlerinclude/
├── MCP_Thread.h # MCP_Threads_Handler class definition
├── MCP_Endpoint.h # MCP_JSONRPC_Resource class definition
├── MCP_Tool_Handler.h # Base class for all tool handlers
├── Config_Tool_Handler.h # Configuration endpoint tool handler
├── Query_Tool_Handler.h # Query endpoint tool handler (includes discovery tools)
├── Admin_Tool_Handler.h # Administration endpoint tool handler
├── Cache_Tool_Handler.h # Cache endpoint tool handler
├── Stats_Tool_Handler.h # Stats endpoint tool handler
├── AI_Tool_Handler.h # AI endpoint tool handler
├── Discovery_Schema.h # Discovery catalog implementation
├── Static_Harvester.h # Static database harvester for discovery
└── ProxySQL_MCP_Server.hpp # ProxySQL_MCP_Server class definition
lib/
├── MCP_Thread.cpp # MCP_Threads_Handler implementation
├── MCP_Endpoint.cpp # MCP_JSONRPC_Resource implementation
├── MCP_Tool_Handler.cpp # Base class implementation
├── Config_Tool_Handler.cpp # Configuration endpoint implementation
├── Query_Tool_Handler.cpp # Query endpoint implementation
├── Admin_Tool_Handler.cpp # Administration endpoint implementation
├── Cache_Tool_Handler.cpp # Cache endpoint implementation
├── Stats_Tool_Handler.cpp # Stats endpoint implementation
├── AI_Tool_Handler.cpp # AI endpoint implementation
├── Discovery_Schema.cpp # Discovery catalog implementation
├── Static_Harvester.cpp # Static database harvester implementation
└── ProxySQL_MCP_Server.cpp # HTTPS server implementation
1. LLM Client → POST /mcp/{endpoint} → HTTPS Server (port 6071)
2. HTTPS Server → MCP_JSONRPC_Resource::render_POST()
3. MCP_JSONRPC_Resource → handle_jsonrpc_request()
4. Route based on JSON-RPC method:
- initialize/ping → Handled directly
- tools/list → handle_tools_list()
- tools/describe → handle_tools_describe()
- tools/call → handle_tools_call() → Dedicated Tool Handler
5. Dedicated Tool Handler → MySQL Backend (via connection pool)
6. Return JSON-RPC response
Each MCP endpoint has its own dedicated tool handler with specific tools designed for that endpoint's purpose. This allows for:
/mcp/config - Configuration EndpointPurpose: Runtime configuration and management of ProxySQL
Tools:
get_config - Get current configuration valuesset_config - Modify configuration valuesreload_config - Reload configuration from disk/memorylist_variables - List all available variablesget_status - Get server status informationUse Cases:
Authentication: mcp-config_endpoint_auth (Bearer token)
/mcp/stats - Observability EndpointPurpose: Real-time metrics, statistics, and monitoring data
Tools:
list_stats - List available statisticsget_stats - Get specific statisticsshow_connections - Show active connectionsshow_queries - Show query statisticsget_health - Get health check informationshow_metrics - Show performance metricsUse Cases:
Authentication: mcp-stats_endpoint_auth (Bearer token)
/mcp/query - Query EndpointPurpose: Safe database exploration and query execution
Tools:
list_targets - List logical backend targets (hostgroup-backed routing handles)list_schemas - List databaseslist_tables - List tables in schemadescribe_table - Get table structureget_constraints - Get foreign keys and constraintssample_rows - Get sample datarun_sql_readonly - Execute read-only SQLexplain_sql - Explain query execution plansuggest_joins - Suggest join paths between tablesfind_reference_candidates - Find potential foreign key relationshipstable_profile - Get table statistics and data distributioncolumn_profile - Get column statistics and data distributionsample_distinct - Get distinct values from a columncatalog_get - Get entry from discovery catalogcatalog_upsert - Insert or update entry in discovery catalogcatalog_delete - Delete entry from discovery catalogcatalog_search - Search entries in discovery catalogcatalog_list - List all entries in discovery catalogcatalog_clear - Clear all entries from discovery catalogdiscovery.run_static - Run static database discovery (Phase 1)agent.* - Agent coordination tools for discoveryllm.* - LLM interaction tools for discoveryUse Cases:
target_id values instead of direct host/protocol detailsAuthentication: mcp-query_endpoint_auth (Bearer token)
/mcp/query now supports a dynamic routing model based on logical targets:
list_targets to discover routable targets.target_id (opaque identifier)description (human-readable summary)capabilities (for example inventory, readonly_sql, explain)run_sql_readonly, explain_sql, list_tables) accept an optional target_id.target_id is omitted, the server uses a default executable target when available.Internally, the server resolves each target_id to runtime hostgroup metadata, a configured auth profile, and protocol-specific execution paths.
Credential separation model:
runtime_mcp_target_profilesruntime_mcp_auth_profilestarget_id + auth_profile_id.Current execution support:
mysql targets: list_tables, run_sql_readonly, explain_sqlpgsql targets: list_tables, run_sql_readonly, explain_sqlMCP query rules support routing-aware filters so one endpoint can enforce different policies per logical target:
username: backend DB username from the resolved auth profiletarget_id: logical MCP target selected by the client (or resolved default target)schemaname: optional schema/database contexttool_name: MCP tool name (for example run_sql_readonly)The runtime and stats views expose these fields:
runtime_mcp_query_rules: includes username and target_id columnsstats_mcp_query_rules: includes rule_id, username, target_id, hitsThis allows policy verification and hit analysis per logical route without exposing backend host/protocol details to MCP clients.
/mcp/admin - Administration EndpointPurpose: Administrative operations
Tools:
admin_list_users - List MySQL usersadmin_create_user - Create MySQL useradmin_grant_permissions - Grant permissionsadmin_show_processes - Show running processesadmin_kill_query - Kill a running queryadmin_flush_cache - Flush various cachesadmin_reload - Reload users/serversUse Cases:
Authentication: mcp-admin_endpoint_auth (Bearer token, most restrictive)
/mcp/cache - Cache EndpointPurpose: Query cache management
Tools:
get_cache_stats - Get cache statisticsinvalidate_cache - Invalidate cache entriesset_cache_ttl - Set cache TTLclear_cache - Clear all cachewarm_cache - Warm up cache with queriesget_cache_entries - List cached queriesUse Cases:
Authentication: mcp-cache_endpoint_auth (Bearer token)
/mcp/ai - AI EndpointPurpose: AI and LLM features
Tools:
llm.query - Query LLM with database contextllm.analyze - Analyze data with LLMllm.generate - Generate content with LLManomaly.detect - Detect anomalies in dataanomaly.list - List detected anomaliesrecommendation.get - Get AI recommendationsUse Cases:
Authentication: mcp-ai_endpoint_auth (Bearer token)
MCP clients should discover available tools dynamically:
1. Client → POST /mcp/config → {"method": "tools/list", ...}
2. Server → {"result": {"tools": [
{"name": "get_config", "description": "..."},
{"name": "set_config", "description": "..."},
...
]}}
3. Client → POST /mcp/query → {"method": "tools/list", ...}
4. Server → {"result": {"tools": [
{"name": "list_schemas", "description": "..."},
{"name": "list_tables", "description": "..."},
...
]}}
Example Discovery:
# Discover tools on /mcp/query endpoint
curl -k -X POST https://127.0.0.1:6071/mcp/query \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"jsonrpc": "2.0", "method": "tools/list", "id": 1}'
All tool handlers will inherit from a common base class:
class MCP_Tool_Handler {
public:
virtual ~MCP_Tool_Handler() = default;
// Tool discovery
virtual json get_tool_list() = 0;
virtual json get_tool_description(const std::string& tool_name) = 0;
virtual json execute_tool(const std::string& tool_name, const json& arguments) = 0;
// Lifecycle
virtual int init() = 0;
virtual void close() = 0;
};
Each endpoint validates its own Bearer token. The implementation is complete and supports:
Authorization header?token=xxx) for simple testingbool MCP_JSONRPC_Resource::authenticate_request(const http_request& req) {
// Get the expected auth token for this endpoint
char* expected_token = nullptr;
if (endpoint_name == "config") {
expected_token = handler->variables.mcp_config_endpoint_auth;
} else if (endpoint_name == "stats") {
expected_token = handler->variables.mcp_stats_endpoint_auth;
} else if (endpoint_name == "query") {
expected_token = handler->variables.mcp_query_endpoint_auth;
} else if (endpoint_name == "admin") {
expected_token = handler->variables.mcp_admin_endpoint_auth;
} else if (endpoint_name == "cache") {
expected_token = handler->variables.mcp_cache_endpoint_auth;
}
// If no auth token is configured, allow the request
if (!expected_token || strlen(expected_token) == 0) {
return true; // No authentication required
}
// Try to get Bearer token from Authorization header
std::string auth_header = req.get_header("Authorization");
if (auth_header.empty()) {
// Fallback: try getting from query parameter
const std::map<std::string, std::string, http::arg_comparator>& args = req.get_args();
auto it = args.find("token");
if (it != args.end()) {
auth_header = "Bearer " + it->second;
}
}
if (auth_header.empty()) {
return false; // No authentication provided
}
// Check if it's a Bearer token
const std::string bearer_prefix = "Bearer ";
if (auth_header.length() <= bearer_prefix.length() ||
auth_header.compare(0, bearer_prefix.length(), bearer_prefix) != 0) {
return false; // Invalid format
}
// Extract and validate token
std::string provided_token = auth_header.substr(bearer_prefix.length());
// Trim whitespace
size_t start = provided_token.find_first_not_of(" \t\n\r");
size_t end = provided_token.find_last_not_of(" \t\n\r");
if (start != std::string::npos && end != std::string::npos) {
provided_token = provided_token.substr(start, end - start + 1);
}
return (provided_token == expected_token);
}
Status: ✅ Implemented (lib/MCP_Endpoint.cpp)
Each tool handler manages its own connection pool:
class Config_Tool_Handler : public MCP_Tool_Handler {
private:
std::vector<MYSQL*> config_connection_pool; // For ProxySQL admin
pthread_mutex_t pool_lock;
};
MCP_Tool_Handler base classMCP_Threads_Handler to manage all handlersProxySQL_MCP_Server to pass handlers to endpointsThe migration to multiple tool handlers has been completed while maintaining backward compatibility:
mysql_tool_handler has been replaced by query_tool_handler/mcp/query✅ Step 1: Add new base class and stub handlers (no behavior change)
✅ Step 2: Implement /mcp/config endpoint (new functionality)
✅ Step 3: Move MySQL tools to /mcp/query (existing tools migrate)
✅ Step 4: Implement /mcp/admin (new functionality)
✅ Step 5: Implement /mcp/cache (new functionality)
✅ Step 6: Implement /mcp/stats (new functionality)
✅ Step 7: Enable per-endpoint auth
✅ Step 8: Add /mcp/ai endpoint (new AI functionality)