apache/shardingsphere · error · MetadataIntrospectionSQLStatementException
Metadata introspection SQL should use MCP metadata resources
Error message
Metadata introspection SQL should use MCP metadata resources.
What it means
Thrown by SQLStatementSafetyValidator when an MCP execute-SQL request contains a metadata introspection statement (SHOW, DESCRIBE, or DESC as the leading keyword). The MCP contract forbids using raw SQL for catalog/metadata queries; the server exposes dedicated MCP metadata resources/tools instead, so clients get structured, safe output rather than dialect-specific result sets.
Source
Thrown at mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementSafetyValidator.java:57
import java.util.Locale;
final class SQLStatementSafetyValidator {
private static final List<String> SIDE_EFFECTING_FUNCTION_NAMES = List.of("NEXTVAL", "NEXT VALUE FOR", "SETVAL", "GET_LOCK", "RELEASE_LOCK", "RELEASE_ALL_LOCKS",
"PG_ADVISORY_LOCK", "PG_ADVISORY_XACT_LOCK", "PG_TRY_ADVISORY_LOCK", "PG_TRY_ADVISORY_XACT_LOCK", "PG_ADVISORY_UNLOCK", "PG_ADVISORY_UNLOCK_ALL",
"SET_CONFIG", "PG_REPLICATION_SLOT_ADVANCE", "PG_LOGICAL_SLOT_GET_CHANGES", "PG_LOGICAL_SLOT_GET_BINARY_CHANGES", "PG_LOGICAL_EMIT_MESSAGE", "PG_SWITCH_WAL",
"PG_RELOAD_CONF", "PG_CANCEL_BACKEND", "PG_TERMINATE_BACKEND");
private static final List<String> METADATA_LOOKUP_FUNCTION_NAMES = List.of("TO_REGCLASS", "TO_REGTYPE", "TO_REGPROC", "TO_REGPROCEDURE", "TO_REGOPER",
"TO_REGOPERATOR", "TO_REGNAMESPACE", "TO_REGROLE", "OBJECT_ID");
void checkLeadingStatement(final String upperSql, final boolean executableComment) {
if (executableComment || upperSql.startsWith("USE ") || upperSql.startsWith("SET ") || upperSql.startsWith("COPY ") || upperSql.startsWith("LOAD ")
|| upperSql.startsWith("CALL ") || isAlterSystemStatement(upperSql)) {
throw new MCPBannedSQLStatementException();
}
if (isMetadataIntrospectionStatement(upperSql)) {
throw new MetadataIntrospectionSQLStatementException(extractStatementType(upperSql));
}
}
void checkParsedStatement(final SQLStatement sqlStatement) {
new SQLStatementTreeWalker(this::checkStatement, this::checkExpression).walk(sqlStatement);
}
private boolean isBannedStatementType(final SQLStatement sqlStatement) {
return sqlStatement instanceof SetStatement || sqlStatement instanceof CallStatement
|| sqlStatement instanceof CreateUserStatement || sqlStatement instanceof AlterUserStatement || sqlStatement instanceof DropUserStatement
|| sqlStatement instanceof CreateRoleStatement || sqlStatement instanceof AlterRoleStatement || sqlStatement instanceof DropRoleStatement;
}
private boolean containsExecutableComment(final SQLStatement sqlStatement) {
for (CommentSegment each : sqlStatement.getComments()) {
String text = each.getText().trim();
if (text.startsWith("/*!") || text.toUpperCase(Locale.ENGLISH).startsWith("/*M!")) {
return true;View on GitHub (pinned to e952770a21)
Solutions
- Use the MCP metadata tool/resources (e.g. the object_types-based metadata listing tool) instead of SHOW/DESCRIBE to inspect schemas, tables, and columns
- If you only need row data, rewrite the statement as an information-preserving SELECT against user tables
- If you maintain the server and must allow it, adjust SQLStatementSafetyValidator.isMetadataIntrospectionStatement — but note this weakens the MCP read-only metadata contract
Example fix
// before
execute_sql("SHOW TABLES")
execute_sql("DESC t_order")
// after
list_metadata({"object_types": ["table"]})
execute_sql("SELECT * FROM t_order LIMIT 1") Defensive patterns
Strategy: validation
Validate before calling
// Java — reject before calling the MCP execute tool
String upper = sql.strip().toUpperCase(Locale.ENGLISH);
if (upper.equals("SHOW") || upper.startsWith("SHOW ")
|| upper.equals("DESCRIBE") || upper.startsWith("DESCRIBE ")
|| upper.equals("DESC") || upper.startsWith("DESC ")) {
throw new IllegalArgumentException("Use MCP metadata resources, not SHOW/DESC: " + sql);
} Try / catch
try {
executeSql(sql);
} catch (MetadataIntrospectionSQLStatementException e) {
// fall back to the metadata tool with equivalent scope
return listMetadata(Map.of("object_types", List.of("table")));
} Prevention
- Route all schema discovery through MCP metadata tools, never execute_sql
- Lint agent-generated SQL for leading SHOW/DESC keywords before submission
When it happens
Trigger: checkLeadingStatement() sees upperSql equal to or starting with "SHOW " / "SHOW", "DESCRIBE " / "DESCRIBE", or "DESC " / "DESC" — e.g. `SHOW TABLES`, `DESCRIBE t_order`, `DESC t_order`. Any leading keyword match throws MetadataIntrospectionSQLStatementException with the extracted statement type.
Common situations: LLM agents or scripts habitually call `SHOW TABLES` / `DESC table` to explore schema through the execute_sql MCP tool; porting existing DBA scripts that mix DML with SHOW commands; MySQL workbench-style discovery queries.
Related errors
- Locking read statements such as SELECT ... FOR UPDATE are no
- getHoldability
- Statement is not a transaction command.
- Statement is not supported by the MCP contract.
- Only one SQL statement is allowed.
AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14).
Data as JSON: /api/errors/dc6f52e02cd4c5f2.
Report an issue: GitHub.