22 KiB
MCP Architecture
This document describes the architecture of the MCP (Model Context Protocol) module in ProxySQL, including endpoint design, tool handler implementation, and future architectural direction.
Overview
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.
Key Concepts
- MCP Endpoint: A distinct HTTPS endpoint (e.g.,
/mcp/config,/mcp/query) that implements MCP protocol - Tool Handler: A C++ class that implements specific tools available to LLMs
- Tool Discovery: Dynamic discovery via
tools/listmethod (MCP protocol standard) - Endpoint Authentication: Per-endpoint Bearer token authentication
- Connection Pooling: MySQL connection pooling for efficient database access
Current Architecture
Component Diagram
┌─────────────────────────────────────────────────────────────────────────────┐
│ ProxySQL Process │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ MCP_Threads_Handler │ │
│ │ - Configuration variables (mcp-*) │ │
│ │ - Status variables │ │
│ │ - mcp_server (ProxySQL_MCP_Server) │ │
│ │ - mysql_tool_handler (MySQL_Tool_Handler) │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ ProxySQL_MCP_Server │ │
│ │ (Single HTTPS Server) │ │
│ │ │ │
│ │ Port: mcp-port (default 6071) │ │
│ │ SSL: Uses ProxySQL's certificates │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────┼─────────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐ │
│ │ /mcp/config │ │ /mcp/observe │ │ /mcp/query │ │
│ │ MCP_JSONRPC_ │ │ MCP_JSONRPC_ │ │ MCP_JSONRPC_ │ │
│ │ Resource │ │ Resource │ │ Resource │ │
│ └─────────┬─────────┘ └─────────┬─────────┘ └─────────┬─────────┘ │
│ │ │ │ │
│ └─────────────────────┼─────────────────────┘ │
│ ▼ │
│ ┌────────────────────────────────────────────┐ │
│ │ MySQL_Tool_Handler (Shared) │ │
│ │ │ │
│ │ Tools: │ │
│ │ - list_schemas │ │
│ │ - list_tables │ │
│ │ - describe_table │ │
│ │ - get_constraints │ │
│ │ - table_profile │ │
│ │ - column_profile │ │
│ │ - sample_rows │ │
│ │ - run_sql_readonly │ │
│ │ - catalog_* (6 tools) │ │
│ └────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────┐ │
│ │ MySQL Backend │ │
│ │ (Connection Pool) │ │
│ └────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
Current Limitations
- All endpoints share the same tool handler - No differentiation between endpoints
- Same tools available everywhere - No specialized tools per endpoint
- Single connection pool - All queries use the same MySQL connections
- No per-endpoint authentication in code - Variables exist but not implemented
File Structure
include/
├── MCP_Thread.h # MCP_Threads_Handler class definition
├── MCP_Endpoint.h # MCP_JSONRPC_Resource class definition
├── MySQL_Tool_Handler.h # MySQL_Tool_Handler class definition
├── MySQL_Catalog.h # SQLite catalog for LLM memory
└── ProxySQL_MCP_Server.hpp # ProxySQL_MCP_Server class definition
lib/
├── MCP_Thread.cpp # MCP_Threads_Handler implementation
├── MCP_Endpoint.cpp # MCP_JSONRPC_Resource implementation
├── MySQL_Tool_Handler.cpp # MySQL_Tool_Handler implementation
├── MySQL_Catalog.cpp # SQLite catalog implementation
└── ProxySQL_MCP_Server.cpp # HTTPS server implementation
Request Flow (Current)
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() → MySQL_Tool_Handler
5. MySQL_Tool_Handler → MySQL Backend (via connection pool)
6. Return JSON-RPC response
Future Architecture: Multiple Tool Handlers
Goal
Each MCP endpoint will have its own dedicated tool handler with specific tools designed for that endpoint's purpose. This allows for:
- Specialized tools - Different tools for different purposes
- Isolated resources - Separate connection pools per endpoint
- Independent authentication - Per-endpoint credentials
- Clear separation of concerns - Each endpoint has a well-defined purpose
Target Architecture
┌─────────────────────────────────────────────────────────────────────────────┐
│ ProxySQL Process │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ MCP_Threads_Handler │ │
│ │ - Configuration variables │ │
│ │ - Status variables │ │
│ │ - mcp_server │ │
│ │ - config_tool_handler (NEW) │ │
│ │ - query_tool_handler (NEW) │ │
│ │ - admin_tool_handler (NEW) │ │
│ │ - cache_tool_handler (NEW) │ │
│ │ - observe_tool_handler (NEW) │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ ProxySQL_MCP_Server │ │
│ │ (Single HTTPS Server) │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────┬──────────────┼──────────────┬──────────────┬─────────┐ │
│ ▼ ▼ ▼ ▼ ▼ ▼ │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌───┐│
│ │conf│ │obs │ │qry │ │adm │ │cach│ │cat││
│ │TH │ │TH │ │TH │ │TH │ │TH │ │log│││
│ └─┬──┘ └─┬──┘ └─┬──┘ └─┬──┘ └─┬──┘ └─┬─┘│
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │
│ 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 │ │
│ │ │
└─────────────────────────────────────────────────────────────────────────────┘
Where:
TH= Tool Handler
Endpoint Specifications
/mcp/config - Configuration Endpoint
Purpose: 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 information
Use Cases:
- LLM assistants that need to configure ProxySQL
- Automated configuration management
- Dynamic tuning based on workload
Authentication: mcp-config_endpoint_auth (Bearer token)
/mcp/observe - Observability Endpoint
Purpose: 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 metrics
Use Cases:
- LLM assistants for monitoring and observability
- Automated alerting and health checks
- Performance analysis
Authentication: mcp-observe_endpoint_auth (Bearer token)
/mcp/query - Query Endpoint
Purpose: Safe database exploration and query execution
Tools:
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 plan
Use Cases:
- LLM assistants for database exploration
- Data analysis and discovery
- Query optimization assistance
Authentication: mcp-query_endpoint_auth (Bearer token)
/mcp/admin - Administration Endpoint
Purpose: 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/servers
Use Cases:
- LLM assistants for administration tasks
- Automated user management
- Emergency operations
Authentication: mcp-admin_endpoint_auth (Bearer token, most restrictive)
/mcp/cache - Cache Endpoint
Purpose: 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 queries
Use Cases:
- LLM assistants for cache optimization
- Automated cache management
- Performance tuning
Authentication: mcp-cache_endpoint_auth (Bearer token)
Tool Discovery Flow
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}'
Tool Handler Base Class
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;
};
Per-Endpoint Authentication
Each endpoint validates its own Bearer token. The implementation is complete and supports:
- Bearer token from
Authorizationheader - Query parameter fallback (
?token=xxx) for simple testing - No authentication when token is not configured (backward compatible)
bool 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 == "observe") {
expected_token = handler->variables.mcp_observe_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)
Connection Pooling Strategy
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;
};
Implementation Roadmap
Phase 1: Base Infrastructure
- Create
MCP_Tool_Handlerbase class - Create stub implementations for all 5 tool handlers
- Update
MCP_Threads_Handlerto manage all handlers - Update
ProxySQL_MCP_Serverto pass handlers to endpoints
Phase 2: Tool Implementation
- Implement Config_Tool_Handler tools
- Implement Query_Tool_Handler tools (move from MySQL_Tool_Handler)
- Implement Admin_Tool_Handler tools
- Implement Cache_Tool_Handler tools
- Implement Observe_Tool_Handler tools
Phase 3: Authentication & Testing
- ✅ Implement per-endpoint authentication
- ⚠️ Update test scripts to use dynamic tool discovery
- ⚠️ Add integration tests for each endpoint
- ⚠️ Documentation updates
Migration Strategy
Backward Compatibility
The migration to multiple tool handlers will maintain backward compatibility:
- The existing
mysql_tool_handlerwill be renamed toquery_tool_handler - Existing tools will continue to work on
/mcp/query - New endpoints will be added incrementally
- Deprecation warnings for accessing tools on wrong endpoints
Gradual Migration
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/observe (new functionality)
Step 7: Enable per-endpoint auth
Related Documentation
- VARIABLES.md - Configuration variables reference
- README.md - Module overview and setup
Version
- MCP Thread Version: 0.1.0
- Architecture Version: 1.0 (design document)
- Last Updated: 2025-01-12