Skip to content

Commit 9719d4a

Browse files
authored
Merge pull request #5752 from sysown/genai_5534
Add config query, protobuf vendoring, and RAG source fetch
2 parents 9399628 + cf0b1d3 commit 9719d4a

17 files changed

Lines changed: 1550 additions & 119 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,4 @@ test-scripts/deps/
222222
.worktrees/
223223
test/tap/tests/parsersql_digest_test
224224
test/tap/tests/setparser_parsersql_test
225+
deps/protobuf/protobuf-*/

deps/Makefile

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ ifeq ($(UNAME_S),Linux)
4646
targets += coredumper
4747
endif
4848

49+
ifeq ($(PROXYSQL40),1)
50+
targets += protobuf
51+
endif
52+
4953
ifeq ($(PROXYSQLCLICKHOUSE),1)
5054
targets += clickhouse-cpp
5155
endif
@@ -170,6 +174,30 @@ zstd/zstd/lib/libzstd.a:
170174
zstd: zstd/zstd/lib/libzstd.a
171175

172176

177+
PROTOBUF_VERSION := 3.21.12
178+
PROTOBUF_SRC_DIR := $(PROXYSQL_PATH)/deps/protobuf/protobuf-$(PROTOBUF_VERSION)
179+
PROTOBUF_INSTALL_DIR := $(PROTOBUF_SRC_DIR)/install
180+
PROTOBUF_LIB := $(PROTOBUF_INSTALL_DIR)/lib/libprotobuf.a
181+
182+
$(PROTOBUF_LIB):
183+
cd protobuf && rm -rf protobuf-*/ || true
184+
cd protobuf && tar -zxf protobuf-$(PROTOBUF_VERSION).tar.gz
185+
cd protobuf/protobuf-$(PROTOBUF_VERSION) && CC=${CC} CXX=${CXX} cmake -S . -B build \
186+
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
187+
-DCMAKE_INSTALL_PREFIX=$(PROTOBUF_INSTALL_DIR) \
188+
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
189+
-Dprotobuf_BUILD_TESTS=OFF \
190+
-Dprotobuf_BUILD_CONFORMANCE=OFF \
191+
-Dprotobuf_BUILD_EXAMPLES=OFF \
192+
-Dprotobuf_BUILD_PROTOC_BINARIES=OFF \
193+
-Dprotobuf_BUILD_LIBPROTOC=OFF \
194+
-Dprotobuf_BUILD_SHARED_LIBS=OFF \
195+
-Dprotobuf_WITH_ZLIB=OFF
196+
cd protobuf/protobuf-$(PROTOBUF_VERSION) && CC=${CC} CXX=${CXX} ${MAKE} -C build install
197+
198+
protobuf: $(PROTOBUF_LIB)
199+
200+
173201
clickhouse-cpp/clickhouse-cpp/clickhouse/libclickhouse-cpp-lib.a:
174202
cd clickhouse-cpp && rm -rf clickhouse-cpp-*/ || true
175203
cd clickhouse-cpp && tar -zxf clickhouse-cpp-*.tar.gz
@@ -399,6 +427,7 @@ cleanpart:
399427
cd sqlite3 && rm -rf sqlite-amalgamation-*/ || true
400428
cd postgresql && rm -rf postgresql-*/ || true
401429
cd postgresql && rm -rf postgres-*/ || true
430+
cd protobuf && rm -rf protobuf-*/ || true
402431
.PHONY: cleanpart
403432

404433
cleanall:
@@ -425,7 +454,7 @@ cleanall:
425454
cd libusual && rm -rf libusual-*/ || true
426455
cd libscram && rm -rf lib/* obj/* || true
427456
cd json && rm -rf json-*/ || true
457+
cd protobuf && rm -rf protobuf-*/ || true
428458
cd zstd && rm -rf zstd-*/ || true
429459
cd parsersql && rm -rf parsersql-*/ ParserSQL-*/ || true
430460
.PHONY: cleanall
431-
4.9 MB
Binary file not shown.

doc/MCP/Architecture.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,13 @@ Each MCP endpoint has its own dedicated tool handler with specific tools designe
139139
- `reload_config` - Reload configuration from disk/memory
140140
- `list_variables` - List all available variables
141141
- `get_status` - Get server status information
142+
- `query` - Execute constrained SQL against the admin/config database
143+
144+
**Query policy**:
145+
- Allows read/write SQL that starts with `SELECT`, `WITH`, `INSERT`, `UPDATE`, `DELETE`, `REPLACE`, or `VALUES`
146+
- Rejects DDL and dangerous control statements, including `PRAGMA`, `ATTACH`, `DETACH`, `LOAD_EXTENSION`, transaction control, and schema-altering statements
147+
- Rejects multi-statement input
148+
- Returns structured JSON with the SQL text, `rows_affected`, `row_count`, `columns`, and `rows`
142149

143150
**Use Cases**:
144151
- LLM assistants that need to configure ProxySQL
@@ -250,6 +257,33 @@ The runtime and stats views expose these fields:
250257

251258
This allows policy verification and hit analysis per logical route without exposing backend host/protocol details to MCP clients.
252259

260+
#### `/mcp/rag` - Retrieval Endpoint
261+
262+
**Purpose**: RAG search, retrieval, and source re-fetch operations
263+
264+
**Tools**:
265+
- `rag.search_fts` - Keyword search over chunked content
266+
- `rag.search_vector` - Semantic search over embeddings
267+
- `rag.search_hybrid` - Combined FTS + vector search
268+
- `rag.get_chunks` - Fetch chunk content by `chunk_id`
269+
- `rag.get_docs` - Fetch document content by `doc_id`
270+
- `rag.fetch_from_source` - Re-fetch authoritative rows from the configured source backend
271+
- `rag.admin.stats` - Operational statistics for the RAG subsystem
272+
273+
**Fetch policy**:
274+
- Looks up each requested `doc_id` from the vector database metadata tables
275+
- Uses the source row's `backend_type` to connect to MySQL or PostgreSQL backends
276+
- Reconstructs the source `SELECT` from the stored `table_name`, primary-key metadata, and optional `where_sql`
277+
- Returns one result row per requested document, with per-row errors when a document is missing or the backend cannot be reached
278+
- Rejects unsupported backend types instead of guessing a connection path
279+
280+
**Use Cases**:
281+
- LLM assistants that need indexed retrieval over documents
282+
- Semantic search and chunk inspection
283+
- Operational recovery by re-reading source-of-truth rows
284+
285+
**Authentication**: `mcp-rag_endpoint_auth` (Bearer token)
286+
253287
---
254288

255289
#### `/mcp/admin` - Administration Endpoint
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
# Config Query Tool Implementation Plan
2+
3+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4+
5+
**Goal:** Add a server-enforced SQL query tool to `/mcp/config` so MCP clients can inspect and modify ProxySQL configuration without a growing pile of dedicated verbs.
6+
7+
**Architecture:** Keep the `/mcp/config` endpoint as the admin/config surface, but add a single `query` tool that executes SQL against the admin database through `admindb->execute_statement()`. The MCP server will reject unsafe statements before execution using a local policy gate that blocks DDL, attachment, pragma, and other hazardous statements while still allowing controlled reads and writes.
8+
9+
**Tech Stack:** C++17, `SQLite3DB`, `nlohmann::json`, existing MCP tool-handler framework, TAP tests.
10+
11+
---
12+
13+
### Task 1: Add the config query tool contract
14+
15+
**Files:**
16+
- Modify: `plugins/genai/include/Config_Tool_Handler.h`
17+
- Modify: `plugins/genai/src/tool_handlers/Config_Tool_Handler.cpp`
18+
19+
- [ ] **Step 1: Update the tool list and dispatch**
20+
21+
Add a new `query` tool to `/mcp/config` with this input schema:
22+
23+
```cpp
24+
tools.push_back(create_tool_description(
25+
"query",
26+
"Execute constrained SQL against the ProxySQL admin/config database",
27+
{
28+
{"type", "object"},
29+
{"properties", {
30+
{"sql", {
31+
{"type", "string"},
32+
{"description", "Single SQL statement to execute"}
33+
}},
34+
{"limit", {
35+
{"type", "integer"},
36+
{"description", "Optional row limit for result sets"}
37+
}}
38+
}},
39+
{"required", {"sql"}}
40+
}
41+
));
42+
```
43+
44+
Route `execute_tool("query", ...)` to a new private helper that validates the SQL text, runs it through `GloAdmin->admindb`, and returns a structured result.
45+
46+
- [ ] **Step 2: Add the execution helper**
47+
48+
Implement a helper in `Config_Tool_Handler.cpp` that:
49+
50+
```cpp
51+
json handle_query(const std::string& sql, int limit);
52+
```
53+
54+
The helper should:
55+
56+
- reject empty SQL
57+
- reject multi-statement SQL
58+
- reject forbidden statement classes
59+
- execute the statement with `GloAdmin->admindb->execute_statement(...)`
60+
- convert any resultset with `MCP_Tool_Handler::resultset_to_json(...)`
61+
- return a JSON object containing at least:
62+
- `sql`
63+
- `rows_affected`
64+
- `columns`
65+
- `rows`
66+
- `message` for non-row statements
67+
68+
- [ ] **Step 3: Keep existing config verbs intact**
69+
70+
Leave `get_config`, `set_config`, `list_variables`, and `get_status` in place for convenience. Keep `reload_config` for now, but do not expand it in this task.
71+
72+
- [ ] **Step 4: Commit**
73+
74+
```bash
75+
git add plugins/genai/include/Config_Tool_Handler.h plugins/genai/src/tool_handlers/Config_Tool_Handler.cpp
76+
git commit -m "feat(genai): add constrained config query tool"
77+
```
78+
79+
### Task 2: Enforce SQL safety on the server side
80+
81+
**Files:**
82+
- Modify: `plugins/genai/src/tool_handlers/Config_Tool_Handler.cpp`
83+
84+
- [ ] **Step 1: Add a SQL policy helper**
85+
86+
Implement a local helper that validates the SQL string before execution. The first pass should reject:
87+
88+
```cpp
89+
PRAGMA
90+
ATTACH
91+
DETACH
92+
DROP
93+
ALTER
94+
CREATE
95+
TRUNCATE
96+
VACUUM
97+
REINDEX
98+
LOAD_EXTENSION
99+
```
100+
101+
Also reject:
102+
103+
- empty statements
104+
- semicolon-separated multi-statements
105+
- leading SQL comments that hide a forbidden first token
106+
107+
The helper should accept controlled DML and queries such as `SELECT`, `WITH`, `INSERT`, `UPDATE`, `DELETE`, and `REPLACE`.
108+
109+
```cpp
110+
bool is_allowed_config_sql(const std::string& sql, std::string& error);
111+
```
112+
113+
- [ ] **Step 2: Add execution guardrails**
114+
115+
Before execution, apply the policy helper and return an error response when the SQL is blocked. Use a clear error message that names the blocked token/class so the client can adapt.
116+
117+
Also apply a hard row cap to result sets if the caller passes a `limit`, and clamp the value to a sane upper bound inside the handler.
118+
119+
- [ ] **Step 3: Commit**
120+
121+
```bash
122+
git add plugins/genai/src/tool_handlers/Config_Tool_Handler.cpp
123+
git commit -m "feat(genai): enforce config query sql policy"
124+
```
125+
126+
### Task 3: Add tests for allowed and blocked SQL
127+
128+
**Files:**
129+
- Create: `test/tap/tests/unit/genai_config_tool_unit-t.cpp`
130+
- Modify: `test/tap/tests/unit/Makefile`
131+
132+
- [ ] **Step 1: Write the unit test**
133+
134+
Add a unit test that instantiates `Config_Tool_Handler` with a minimal MCP handler and checks these cases:
135+
136+
```cpp
137+
ok(handler.execute_tool("query", json{{"sql", "SELECT variable_name FROM global_variables LIMIT 1"}})["success"] == true,
138+
"SELECT is allowed");
139+
ok(handler.execute_tool("query", json{{"sql", "UPDATE global_variables SET variable_value='1' WHERE variable_name='x'"}})["success"] == true,
140+
"UPDATE is allowed");
141+
ok(handler.execute_tool("query", json{{"sql", "PRAGMA journal_mode"}})["success"] == false,
142+
"PRAGMA is blocked");
143+
ok(handler.execute_tool("query", json{{"sql", "DROP TABLE global_variables"}})["success"] == false,
144+
"DROP is blocked");
145+
ok(handler.execute_tool("query", json{{"sql", "SELECT 1; SELECT 2"}})["success"] == false,
146+
"multi-statement input is blocked");
147+
```
148+
149+
- [ ] **Step 2: Build and run the test**
150+
151+
Run the targeted unit test binary from `test/tap/tests/unit/Makefile` and verify the new assertions pass.
152+
153+
- [ ] **Step 3: Commit**
154+
155+
```bash
156+
git add test/tap/tests/unit/genai_config_tool_unit-t.cpp test/tap/tests/unit/Makefile
157+
git commit -m "test(genai): cover config query policy"
158+
```
159+
160+
### Task 4: Verify the endpoint contract end to end
161+
162+
**Files:**
163+
- Modify: `plugins/genai/src/ProxySQL_MCP_Server.cpp` only if the config tool name or endpoint wiring needs adjustment
164+
- Test: existing MCP integration tests or a new TAP integration test under `test/tap/tests/`
165+
166+
- [ ] **Step 1: Verify endpoint registration**
167+
168+
Confirm `/mcp/config` still registers through the existing server wiring and that the new `query` tool appears in `tools/list`.
169+
170+
- [ ] **Step 2: Add an integration smoke test**
171+
172+
Add a TAP test that:
173+
174+
```cpp
175+
handler.execute_tool("get_config", json{{"variable_name", "mcp_enabled"}});
176+
handler.execute_tool("query", json{{"sql", "SELECT variable_name FROM global_variables LIMIT 1"}});
177+
handler.execute_tool("query", json{{"sql", "PRAGMA journal_mode"}});
178+
```
179+
180+
and checks that the first two succeed and the last one is rejected.
181+
182+
- [ ] **Step 3: Commit**
183+
184+
```bash
185+
git add plugins/genai/src/ProxySQL_MCP_Server.cpp test/tap/tests/<new-or-existing-integration-test>
186+
git commit -m "test(genai): verify config query endpoint"
187+
```
188+
189+
---
190+
191+
### Coverage Check
192+
193+
- `/mcp/config` query tool: Task 1
194+
- Server-side SQL policy: Task 2
195+
- Regression coverage: Task 3
196+
- End-to-end endpoint behavior: Task 4
197+

0 commit comments

Comments
 (0)