MCPcopy
hub / github.com/benborla/mcp-server-mysql / executeReadOnlyQuery

Function executeReadOnlyQuery

src/db/index.ts:200–536  ·  view source on GitHub ↗
(sql: string)

Source from the content-addressed store, hash-verified

198}
199
200async function executeReadOnlyQuery<T>(sql: string): Promise<T> {
201 let connection;
202 try {
203 // PII redaction works hand-in-hand with explicit column projection: the
204 // schema endpoint hides redacted columns, so the LLM should never need
205 // SELECT *. Refusing wildcard projections here prevents the LLM from
206 // accidentally pulling redacted columns it never saw in the schema.
207 if (
208 ENABLE_PII_REDACTION &&
209 !PII_ALLOW_SELECT_STAR &&
210 containsSelectStar(sql)
211 ) {
212 log(
213 "error",
214 "Refusing query with SELECT * while PII redaction is enabled; project explicit columns instead.",
215 );
216 return {
217 content: [
218 {
219 type: "text",
220 text:
221 "Error: SELECT * (and `table.*`) is not permitted while PII redaction is enabled. " +
222 "Project an explicit column list (e.g. SELECT col1, col2 FROM ...) so redacted columns are not accidentally returned. " +
223 "Set PII_ALLOW_SELECT_STAR=true to override this policy.",
224 },
225 ],
226 isError: true,
227 } as T;
228 }
229
230 // Introspection guard. Three sub-policies under ENABLE_PII_REDACTION:
231 // - filterable (SHOW COLUMNS / DESCRIBE / SHOW INDEX): execute, then
232 // drop PII rows from the result.
233 // - passthrough (SHOW TABLES / SHOW DATABASES / charset / collation /
234 // etc.): execute unchanged. These expose only schema topology (table
235 // and database names), no column-level PII.
236 // - rejected (SHOW CREATE TABLE, information_schema.*, mysql.*, plus
237 // any unrecognised SHOW): blocked, because we can't safely filter
238 // them and they can leak column names verbatim.
239 // PII_ALLOW_INTROSPECTION=true bypasses the guard entirely.
240 // PII_BLOCK_INTROSPECTION=true restores the old hard-block behaviour for
241 // every introspection kind, including filterable and passthrough.
242 let introspectionFilterKind: FilterableIntrospectionKind | null = null;
243 let isIntrospectionPassThrough = false;
244 if (ENABLE_PII_REDACTION && !PII_ALLOW_INTROSPECTION) {
245 const intro = isIntrospectionQuery(sql);
246 if (intro.kind) {
247 const filterable: FilterableIntrospectionKind | null =
248 intro.kind === "show_columns" ||
249 intro.kind === "describe" ||
250 intro.kind === "show_index"
251 ? intro.kind
252 : null;
253 const passthrough = intro.kind === "show_passthrough";
254
255 if (PII_BLOCK_INTROSPECTION || (!filterable && !passthrough)) {
256 log(
257 "error",

Callers 5

createMcpServerFunction · 0.85
mysql.test.tsFile · 0.85
query.test.tsFile · 0.85

Calls 15

containsSelectStarFunction · 0.85
isIntrospectionQueryFunction · 0.85
findPIIColumnReferencesFunction · 0.85
isPIIColumnFunction · 0.85
getQueryTypesFunction · 0.85
extractSchemaFromQueryFunction · 0.85
isInsertAllowedForSchemaFunction · 0.85
isUpdateAllowedForSchemaFunction · 0.85
isDeleteAllowedForSchemaFunction · 0.85
isDDLAllowedForSchemaFunction · 0.85
executeWriteQueryFunction · 0.85
getPoolFunction · 0.85

Tested by

no test coverage detected