apache/shardingsphere · error · MCPUnsupportedSQLStatementException
Statement is not supported by the MCP contract.
Error message
Statement is not supported by the MCP contract.
What it means
In MCPStatementAnalyzer.analyze, a statement that parses successfully but turns out to be a TCLStatement (transaction control other than the recognized leading-keyword forms handled earlier, e.g. SET TRANSACTION or dialect-specific transaction SQL) throws MCPUnsupportedSQLStatementException ('Statement is not supported by the MCP contract.'). Only BEGIN/START TRANSACTION/COMMIT/ROLLBACK and SAVEPOINT forms recognized by the leading-keyword fast paths are supported; other TCL shapes fall through to this guard.
Source
Thrown at mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPStatementAnalyzer.java:82
ClassificationResult analyze(final String sql, final MCPDatabaseCapability databaseCapability) {
String databaseType = databaseCapability.getDatabaseType();
SQLStatementScanner scanner = new SQLStatementScanner(databaseType, sql);
String actualSql = scanner.sql();
String leadingSql = scanner.leadingSql();
String upperLeadingSql = leadingSql.toUpperCase(Locale.ENGLISH);
SQLStatementSafetyValidator safetyValidator = new SQLStatementSafetyValidator();
safetyValidator.checkLeadingStatement(upperLeadingSql, scanner.containsExecutableComment());
if (isSavepointStatement(upperLeadingSql)) {
return analyzeSavepointStatement(actualSql, leadingSql, upperLeadingSql);
}
if (isTransactionControlStatement(upperLeadingSql)) {
return createTCLResult(SupportedMCPStatement.TRANSACTION_CONTROL, extractTransactionStatementType(upperLeadingSql), actualSql, "");
}
SQLStatement sqlStatement = parse(actualSql, databaseType);
safetyValidator.checkParsedStatement(sqlStatement);
if (sqlStatement instanceof TCLStatement) {
throw new MCPUnsupportedSQLStatementException();
}
boolean ruleDistSQL = isRuleDistSQL(sqlStatement);
String leadingKeyword = scanner.extractLeadingKeyword();
SupportedMCPStatement statementClass = resolveStatementClass(sqlStatement, leadingKeyword, ruleDistSQL);
String statementType = resolveStatementType(sqlStatement, statementClass, leadingKeyword);
return new ClassificationResult(statementClass, statementType, actualSql, "", new SQLStatementObjectExtractor().extract(sqlStatement), ruleDistSQL);
}
private ClassificationResult analyzeSavepointStatement(final String actualSql, final String leadingSql, final String upperLeadingSql) {
String savepointName = extractSavepointName(leadingSql);
ShardingSpherePreconditions.checkState(!savepointName.isEmpty(), () -> new MCPInvalidRequestException("Savepoint name is required."));
return createTCLResult(SupportedMCPStatement.SAVEPOINT, extractTransactionStatementType(upperLeadingSql), actualSql, savepointName);
}
private ClassificationResult createTCLResult(final SupportedMCPStatement statementClass, final String statementType, final String sql, final String savepointName) {
return new ClassificationResult(statementClass, statementType, sql, savepointName, Set.of(), false);
}
View on GitHub (pinned to e952770a21)
Solutions
- Remove SET TRANSACTION / session-configuration statements from the SQL sent to MCP execution tools.
- Manage isolation and transaction boundaries through the dedicated transaction control tool/path (COMMIT/ROLLBACK/SAVEPOINT forms).
- Configure isolation at the datasource level instead of per-statement.
Example fix
// before
await tools.call('database_gateway_execute_update', { sql: 'SET TRANSACTION ISOLATION LEVEL READ COMMITTED', execution_mode: 'execute' }); // unsupported
// after: set isolation on the datasource, not via MCP SQL
await tools.call('database_gateway_execute_query', { sql: 'SELECT * FROM t' }); Defensive patterns
Strategy: validation
Validate before calling
const UNSUPPORTED_TCL = /^\s*SET\s+(TRANSACTION|SESSION|CHARACTERISTICS|CONSTRAINT|CONSTRAINTS)\b/i;
if (UNSUPPORTED_TCL.test(sql)) {
throw new Error('Session/transaction configuration SQL is unsupported; configure the datasource instead.');
}
return tools.call('database_gateway_execute_update', { sql, execution_mode }); Type guard
function isSupportedTransactionSql(sql) {
return /^\s*(BEGIN(\s+WORK)?|START\s+TRANSACTION|COMMIT(\s+WORK)?|ROLLBACK(\s+WORK)?(\s+TO\s+SAVEPOINT\s+\S+)?|SAVEPOINT\s+\S+|RELEASE\s+SAVEPOINT\s+\S+)\s*;?\s*$/i.test(sql);
} Try / catch
try {
return await tools.call('database_gateway_execute_update', { sql, execution_mode });
} catch (e) {
if (/not supported by the MCP contract/.test(e.message) && /^\s*SET\s/i.test(sql)) {
// drop the SET TRANSACTION statement; move setting to datasource config
return { skipped: sql };
}
throw e;
} Prevention
- Keep transaction/session configuration out of MCP-bound SQL.
- Use only the recognized BEGIN/COMMIT/ROLLBACK/SAVEPOINT forms via the transaction tool.
- Configure isolation levels on the JDBC datasource.
When it happens
Trigger: Sending SET TRANSACTION ..., SET SESSION CHARACTERISTICS, or other parseable TCL statements that bypass isTransactionControlStatement/isSavepointStatement keyword checks and surface as parsed TCLStatement instances.
Common situations: Boilerplate connection scripts that issue SET TRANSACTION ISOLATION LEVEL ...; ORM-generated session setup SQL; copying transaction management SQL into MCP tool calls instead of using the transaction tools.
Related errors
- Statement class is not supported.
- Statement is not supported by the MCP contract.
- database_gateway_execute_query only supports parser-approved
- database_gateway_execute_update does not accept read-only SQ
- Query did not return a result set.
AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14).
Data as JSON: /api/errors/81284ee6235d522e.
Report an issue: GitHub.