apache/shardingsphere · error · MCPInvalidRequestException
Cross-schema SQL is not supported for database `%s`: `%s`.
Error message
Cross-schema SQL is not supported for database `%s`: `%s`.
What it means
MCPSQLExecutionFacade.checkCrossSchemaSql rejects statements that reference objects outside the target database's schema when the database capability does not use BEST_EFFORT schema semantics: for every referenced object that is qualified (or a namespace target) and whose first identifier does not match the metadata schema, it throws MCPInvalidRequestException naming the database and object. This keeps each MCP execution pinned to the schema selected via the database/schema arguments.
Source
Thrown at mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPSQLExecutionFacade.java:116
private SQLExecutionResult executeInternal(final SQLExecutionRequest executionRequest, final ClassificationResult classificationResult,
final MCPDatabaseCapability databaseCapability) {
ShardingSpherePreconditions.checkContains(databaseCapability.getSupportedStatementClasses(), classificationResult.getStatementClass(),
StatementClassNotSupportedException::new);
checkCrossSchemaSql(executionRequest, databaseCapability, classificationResult);
return switch (classificationResult.getStatementClass()) {
case TRANSACTION_CONTROL, SAVEPOINT -> transactionStatementExecutor.execute(
executionRequest.getSessionId(), executionRequest.getDatabase(), databaseCapability, classificationResult);
case QUERY, EXPLAIN, DML, DDL, DCL -> statementExecutor.execute(executionRequest, classificationResult, databaseCapability);
};
}
private void checkCrossSchemaSql(final SQLExecutionRequest executionRequest, final MCPDatabaseCapability databaseCapability, final ClassificationResult classificationResult) {
if (SchemaExecutionSemantics.BEST_EFFORT == databaseCapability.getSchemaExecutionSemantics()) {
return;
}
for (SQLStatementObjectName each : classificationResult.getReferencedObjects()) {
if (isCrossSchemaReference(each, executionRequest.getDatabase(), databaseCapability.getIdentifierContext())) {
throw new MCPInvalidRequestException(String.format("Cross-schema SQL is not supported for database `%s`: `%s`.", executionRequest.getDatabase(), each.getObjectName()));
}
}
}
private boolean isCrossSchemaReference(final SQLStatementObjectName objectName, final String databaseName, final DatabaseIdentifierContext identifierContext) {
return (objectName.isQualified() || objectName.isNamespaceTarget())
&& !identifierContext.matchesMetaData(IdentifierScope.SCHEMA, databaseName,
new IdentifierValue(objectName.getFirstIdentifier(), objectName.getFirstIdentifierQuoteCharacter()));
}
}
View on GitHub (pinned to e952770a21)
Solutions
- Remove schema qualifiers from the SQL and rely on the database/schema tool arguments to select the target schema.
- Or pass the database/schema arguments matching the qualified prefix so the reference resolves to the target schema.
- Create or refresh metadata so the referenced schema is visible to the target database, if cross-schema access is intended.
- If cross-schema execution is a hard requirement, use a database type whose capability declares BEST_EFFORT schema semantics.
Example fix
// before
await tools.call('database_gateway_execute_query', { sql: 'SELECT * FROM sales_schema.orders WHERE id = 1', schema: 'reporting' }); // cross-schema -> error
// after
await tools.call('database_gateway_execute_query', { sql: 'SELECT * FROM orders WHERE id = 1', schema: 'sales_schema' }); Defensive patterns
Strategy: validation
Validate before calling
// Reject qualified names that don't match the selected schema before sending
function usesForeignSchema(sql, targetSchema) {
const refs = sql.match(/\b(from|join|into|update|table)\s+([A-Za-z_][\w$]*(\.[A-Za-z_][\w$]*)+)/gi) ?? [];
return refs.some(r => {
const qualifier = r.split(/\s+/)[1].split('.')[0];
return qualifier.toLowerCase() !== String(targetSchema).toLowerCase();
});
}
if (usesForeignSchema(sql, schema)) throw new Error('Cross-schema reference in SQL; select the right schema argument instead.'); Type guard
function isSchemaQualified(name) {
return /^[A-Za-z_][\w$]*\.[A-Za-z_][\w$]*$/.test(name.trim());
} Try / catch
try {
return await tools.call('database_gateway_execute_query', { sql, database, schema });
} catch (e) {
if (/Cross-schema SQL is not supported/.test(e.message)) {
const { objectName } = parseCrossSchemaObject(e.message);
return tools.call('database_gateway_execute_query', { sql: sql.replaceAll(objectName, unqualify(objectName)), database, schema: qualifierOf(objectName) });
}
throw e;
} Prevention
- Send unqualified object names and choose the schema via tool arguments.
- Normalize copied application SQL to strip schema prefixes.
- Keep one schema per session/request instead of mixing.
- Verify the first identifier of qualified names matches the target database/schema.
When it happens
Trigger: Executing SQL like SELECT * FROM other_schema.users or INSERT INTO db2.orders ... through the MCP execution tools when the resolved database's SchemaExecutionSemantics is not BEST_EFFORT and the qualified prefix does not resolve to the target schema in metadata.
Common situations: Cross-schema joins or fully-qualified object names copied from existing application SQL; multi-tenant databases where each MCP session is bound to one schema; database name vs schema name confusion (e.g. using the catalog name as prefix on engines where they differ).
Related errors
- database_gateway_execute_query only supports parser-approved
- database_gateway_execute_update does not accept read-only SQ
- Statement is banned by the MCP contract.
- Completion argument `%s` is not declared for %s `%s`.
- %s execution_mode is required.
AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14).
Data as JSON: /api/errors/1c228e94c9ae7e53.
Report an issue: GitHub.