{"record":{"id":"dc6f52e02cd4c5f2","repo":"apache/shardingsphere","slug":"metadata-introspection-sql-should-use-mcp-metadata","errorCode":null,"errorMessage":"Metadata introspection SQL should use MCP metadata resources.","messagePattern":"Metadata introspection SQL should use MCP metadata resources\\.","errorType":"validation","errorClass":"MetadataIntrospectionSQLStatementException","httpStatus":null,"severity":"error","filePath":"mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementSafetyValidator.java","lineNumber":57,"sourceCode":"import java.util.Locale;\n\nfinal class SQLStatementSafetyValidator {\n    \n    private static final List<String> SIDE_EFFECTING_FUNCTION_NAMES = List.of(\"NEXTVAL\", \"NEXT VALUE FOR\", \"SETVAL\", \"GET_LOCK\", \"RELEASE_LOCK\", \"RELEASE_ALL_LOCKS\",\n            \"PG_ADVISORY_LOCK\", \"PG_ADVISORY_XACT_LOCK\", \"PG_TRY_ADVISORY_LOCK\", \"PG_TRY_ADVISORY_XACT_LOCK\", \"PG_ADVISORY_UNLOCK\", \"PG_ADVISORY_UNLOCK_ALL\",\n            \"SET_CONFIG\", \"PG_REPLICATION_SLOT_ADVANCE\", \"PG_LOGICAL_SLOT_GET_CHANGES\", \"PG_LOGICAL_SLOT_GET_BINARY_CHANGES\", \"PG_LOGICAL_EMIT_MESSAGE\", \"PG_SWITCH_WAL\",\n            \"PG_RELOAD_CONF\", \"PG_CANCEL_BACKEND\", \"PG_TERMINATE_BACKEND\");\n    \n    private static final List<String> METADATA_LOOKUP_FUNCTION_NAMES = List.of(\"TO_REGCLASS\", \"TO_REGTYPE\", \"TO_REGPROC\", \"TO_REGPROCEDURE\", \"TO_REGOPER\",\n            \"TO_REGOPERATOR\", \"TO_REGNAMESPACE\", \"TO_REGROLE\", \"OBJECT_ID\");\n    \n    void checkLeadingStatement(final String upperSql, final boolean executableComment) {\n        if (executableComment || upperSql.startsWith(\"USE \") || upperSql.startsWith(\"SET \") || upperSql.startsWith(\"COPY \") || upperSql.startsWith(\"LOAD \")\n                || upperSql.startsWith(\"CALL \") || isAlterSystemStatement(upperSql)) {\n            throw new MCPBannedSQLStatementException();\n        }\n        if (isMetadataIntrospectionStatement(upperSql)) {\n            throw new MetadataIntrospectionSQLStatementException(extractStatementType(upperSql));\n        }\n    }\n    \n    void checkParsedStatement(final SQLStatement sqlStatement) {\n        new SQLStatementTreeWalker(this::checkStatement, this::checkExpression).walk(sqlStatement);\n    }\n    \n    private boolean isBannedStatementType(final SQLStatement sqlStatement) {\n        return sqlStatement instanceof SetStatement || sqlStatement instanceof CallStatement\n                || sqlStatement instanceof CreateUserStatement || sqlStatement instanceof AlterUserStatement || sqlStatement instanceof DropUserStatement\n                || sqlStatement instanceof CreateRoleStatement || sqlStatement instanceof AlterRoleStatement || sqlStatement instanceof DropRoleStatement;\n    }\n    \n    private boolean containsExecutableComment(final SQLStatement sqlStatement) {\n        for (CommentSegment each : sqlStatement.getComments()) {\n            String text = each.getText().trim();\n            if (text.startsWith(\"/*!\") || text.toUpperCase(Locale.ENGLISH).startsWith(\"/*M!\")) {\n                return true;","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/apache/shardingsphere/blob/e952770a215630a3659c75d64369168cd3e26b82/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementSafetyValidator.java#L39-L75","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nexecute_sql(\"SHOW TABLES\")\nexecute_sql(\"DESC t_order\")\n\n// after\nlist_metadata({\"object_types\": [\"table\"]})\nexecute_sql(\"SELECT * FROM t_order LIMIT 1\")","handlingStrategy":"validation","validationCode":"// Java — reject before calling the MCP execute tool\nString upper = sql.strip().toUpperCase(Locale.ENGLISH);\nif (upper.equals(\"SHOW\") || upper.startsWith(\"SHOW \")\n        || upper.equals(\"DESCRIBE\") || upper.startsWith(\"DESCRIBE \")\n        || upper.equals(\"DESC\") || upper.startsWith(\"DESC \")) {\n    throw new IllegalArgumentException(\"Use MCP metadata resources, not SHOW/DESC: \" + sql);\n}","typeGuard":null,"tryCatchPattern":"try {\n    executeSql(sql);\n} catch (MetadataIntrospectionSQLStatementException e) {\n    // fall back to the metadata tool with equivalent scope\n    return listMetadata(Map.of(\"object_types\", List.of(\"table\")));\n}","preventionTips":["Route all schema discovery through MCP metadata tools, never execute_sql","Lint agent-generated SQL for leading SHOW/DESC keywords before submission"],"tags":["mcp","sql-validation","metadata","shardingsphere"],"backgroundTag":null,"analyzedSha":"e952770a215630a3659c75d64369168cd3e26b82","analyzedAt":"2026-08-14T13:54:53.392Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}