{"record":{"id":"7f883de31d520ad7","repo":"apache/shardingsphere","slug":"statement-is-banned-by-the-mcp-contract","errorCode":null,"errorMessage":"Statement is banned by the MCP contract.","messagePattern":"Statement is banned by the MCP contract\\.","errorType":"validation","errorClass":"MCPBannedSQLStatementException","httpStatus":null,"severity":"error","filePath":"mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementSafetyValidator.java","lineNumber":54,"sourceCode":"import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.SelectStatement;\n\nimport java.util.List;\nimport 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()) {","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/apache/shardingsphere/blob/e952770a215630a3659c75d64369168cd3e26b82/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementSafetyValidator.java#L36-L72","documentation":"SQLStatementSafetyValidator.checkLeadingStatement bans entire statement families before parsing: executable comments, and SQL starting with USE, SET, COPY, LOAD, CALL, or ALTER SYSTEM throw MCPBannedSQLStatementException ('Statement is banned by the MCP contract.'). These statements alter session/connection state, bulk-load data, invoke procedures, or reconfigure the server, so they are excluded outright (note metadata-introspection statements get their own distinct exception). The walker also inspects parsed statements for banned types and banned side-effecting functions.","triggerScenarios":"Sending SET foreign_key_checks=0, USE mydb, COPY t FROM stdin, LOAD DATA INFILE ..., CALL proc(), ALTER SYSTEM ..., or SQL prefixed with an executable comment (/*! ... */) to any MCP SQL execution tool.","commonSituations":"Dump/restore scripts replayed through MCP; ORM session-setup statements (SET names/timeout); stored-procedure invocations; MySQL dumps full of /*!40001 ... */ executable comments; attempting schema switching via USE instead of the schema argument.","solutions":["Remove SET/USE/COPY/LOAD/CALL/ALTER SYSTEM statements from MCP-bound SQL; configure session settings at the datasource.","Select databases/schemas via the tool's database and schema arguments instead of USE.","Strip executable comment directives (/*! ... */) from dump-derived SQL before sending.","Invoke stored procedures through your own database client, not the MCP tools."],"exampleFix":"// before\nawait tools.call('database_gateway_execute_update', { sql: 'SET FOREIGN_KEY_CHECKS=0; TRUNCATE t;', execution_mode: 'execute' }); // banned leading SET\n\n// after\nawait tools.call('database_gateway_execute_update', { sql: 'TRUNCATE t', execution_mode: 'execute' }); // FK checks managed on the datasource","handlingStrategy":"validation","validationCode":"const BANNED_PREFIX_RE = /^\\s*(USE|SET|COPY|LOAD|CALL)\\b|^\\s*ALTER\\s+SYSTEM\\b/i;\nconst EXECUTABLE_COMMENT_RE = /\\/\\*!|\\/\\*\\s*\\+/;\nfunction screenBannedSql(sql) {\n  const stripped = sql.replace(/\\/\\*[!+]?.*?\\*\\//gs, '').trim();\n  if (EXECUTABLE_COMMENT_RE.test(sql)) throw new Error('Executable comments are banned by the MCP contract');\n  if (BANNED_PREFIX_RE.test(stripped)) throw new Error(`Statement family banned by the MCP contract: ${stripped.slice(0, 20)}`);\n}\nscreenBannedSql(sql);","typeGuard":"function isBannedLeadingStatement(sql) {\n  const s = sql.trimStart().toUpperCase();\n  return s.startsWith('USE ') || s.startsWith('SET ') || s.startsWith('COPY ') || s.startsWith('LOAD ')\n      || s.startsWith('CALL ') || s.startsWith('ALTER SYSTEM ') || /^\\/\\*[!+]/.test(sql.trimStart());\n}","tryCatchPattern":"try {\n  return await tools.call('database_gateway_execute_update', { sql, execution_mode });\n} catch (e) {\n  if (/banned by the MCP contract/.test(e.message)) {\n    // strip banned leading statements / executable comments, keep the rest, retry once\n    const cleaned = sql.replace(/\\/\\*[!+]?.*?\\*\\//gs, '').replace(/^\\s*(USE|SET|COPY|LOAD|CALL)\\b[^;]*;?\\s*/i, '');\n    return cleaned ? tools.call('database_gateway_execute_update', { sql: cleaned, execution_mode }) : undefined;\n  }\n  throw e;\n}","preventionTips":["Strip SET/USE/COPY/LOAD/CALL and executable comments from SQL before MCP submission.","Select schema via tool arguments, never via USE.","Manage session variables at the datasource level.","Run stored procedures and bulk loads through your own database client."],"tags":["mcp","sql","security","banned-statements","validation"],"backgroundTag":null,"analyzedSha":"e952770a215630a3659c75d64369168cd3e26b82","analyzedAt":"2026-08-14T13:54:53.392Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}