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

Thrown by SQLStatementScanner.extractLeadingKeyword as MCPUnsupportedSQLStatementException when the scanner cannot extract any leading alphabetic keyword from the statement. If the first visible token starts with a non-letter (digit, punctuation, paren) or the statement has no visible tokens at all, keyword extraction yields an empty span and the statement is declared unsupported.

Source

Thrown at mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementScanner.java:106

        return sql;
    }
    
    SQLStatementScanner scan(final String sql) {
        return new SQLStatementScanner(lexerClass, parserClass, sql);
    }
    
    String leadingSql() {
        return sql.substring(visibleTokens.isEmpty() ? sql.length() : visibleTokens.get(0).getStartIndex()).trim();
    }
    
    String extractLeadingKeyword() {
        int startIndex = visibleTokens.isEmpty() ? sql.length() : visibleTokens.get(0).getStartIndex();
        int stopIndex = startIndex;
        while (stopIndex < sql.length() && Character.isLetter(sql.charAt(stopIndex))) {
            stopIndex++;
        }
        if (startIndex == stopIndex) {
            throw new MCPUnsupportedSQLStatementException();
        }
        return sql.substring(startIndex, stopIndex).toUpperCase(Locale.ENGLISH);
    }
    
    boolean containsExecutableComment() {
        return executableComment;
    }
    
    private boolean containsExecutableComment(final String sql, final List<Token> tokens, final boolean lineCommentsHandledByLexer) {
        int nextIndex = 0;
        int skippedCommentEnd = -1;
        for (int index = 0; index < tokens.size(); index++) {
            Token each = tokens.get(index);
            if (each.getStartIndex() <= skippedCommentEnd) {
                continue;
            }
            if (each.getStartIndex() > nextIndex && containsExecutableCommentMarker(sql.substring(nextIndex, each.getStartIndex()))) {
                return true;

View on GitHub (pinned to e952770a21)

Solutions

  1. Rewrite the statement so it begins with a SQL keyword: unwrap `(SELECT ...)` to `SELECT ...`, drop leading literals
  2. Ensure the payload actually contains a statement, not only comments or whitespace
  3. If you need expression evaluation, wrap it as `SELECT <expr>` before submitting

Example fix

-- before
(SELECT * FROM t_order) LIMIT 10

-- after
SELECT * FROM t_order LIMIT 10
Defensive patterns

Strategy: validation

Validate before calling

// Require the first non-space, non-comment character to be a letter
String stripped = sql.strip();
if (stripped.isEmpty() || !Character.isLetter(stripped.charAt(0))) {
    throw new IllegalArgumentException("Statement must start with a SQL keyword; wrap expressions in SELECT");
}

Try / catch

try {
    executeSql(sql);
} catch (MCPUnsupportedSQLStatementException e) {
    executeSql("SELECT " + sql.strip().replaceFirst("^\\(|\\)$", "")); // normalize leading keyword
}

Prevention

When it happens

Trigger: visibleTokens is empty (comment-only or whitespace-only input), or the character at the first visible token's start index is not a letter — e.g. `(SELECT 1)`, `123 + 1`, `@x`, `$func$ ...`, `"quoted" ...` as the leading text. startIndex == stopIndex triggers the throw.

Common situations: Statements wrapped in parentheses for precedence; expressions starting with literals or variables; empty/comment-only payloads sent by an agent loop; strings whose leading quote/backtick survives as the first token (also caught separately by delimiter validation).

Related errors


AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14). Data as JSON: /api/errors/9ba823324d7a95fd. Report an issue: GitHub.