{"record":{"id":"577c4172e01ca491","repo":"apache/shardingsphere","slug":"s-must-be-an-integer-between-d-and-d","errorCode":null,"errorMessage":"%s must be an integer between %d and %d.","messagePattern":"(.+?) must be an integer between (.+?) and (.+?)\\.","errorType":"validation","errorClass":"MCPInvalidToolArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLExecutionToolHandlerSupport.java","lineNumber":102,"sourceCode":"        if (database.isEmpty()) {\n            return \"\";\n        }\n        List<ShardingSphereSchema> schemas = requestContext.getMetadataQueryFacade().querySchemas(database);\n        return 1 == schemas.size() ? schemas.iterator().next().getName() : \"\";\n    }\n    \n    private static int resolveMaxRows(final MCPToolArguments toolArguments, final String sourceTool) {\n        int result = getIntegerArgument(toolArguments, sourceTool, \"max_rows\", MCPRuntimeProtectionPolicy.DEFAULT_MAX_ROWS, 0, MCPRuntimeProtectionPolicy.MAX_ROWS_LIMIT,\n                MCPRuntimeProtectionPolicy.DEFAULT_MAX_ROWS);\n        return 0 == result ? MCPRuntimeProtectionPolicy.DEFAULT_MAX_ROWS : result;\n    }\n    \n    private static int getIntegerArgument(final MCPToolArguments toolArguments, final String sourceTool, final String argumentPath, final int defaultValue, final int minimumValue,\n                                          final int maximumValue, final int suggestedValue) {\n        try {\n            return toolArguments.getIntegerArgument(argumentPath, defaultValue, minimumValue, maximumValue);\n        } catch (final MCPInvalidRequestException ex) {\n            throw new MCPInvalidToolArgumentException(sourceTool, sourceTool, argumentPath, minimumValue, maximumValue, suggestedValue, ex);\n        }\n    }\n    \n    static void putIfNotEmpty(final Map<String, Object> target, final String key, final String value) {\n        if (!value.isEmpty()) {\n            target.put(key, value);\n        }\n    }\n}\n","sourceCodeStart":84,"sourceCodeEnd":112,"githubUrl":"https://github.com/apache/shardingsphere/blob/e952770a215630a3659c75d64369168cd3e26b82/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLExecutionToolHandlerSupport.java#L84-L112","documentation":"SQLExecutionToolHandlerSupport.getIntegerArgument catches the MCPInvalidRequestException raised by MCPToolArguments.getIntegerArgument for numeric arguments (e.g. max_rows) and rethrows MCPInvalidToolArgumentException: '%s must be an integer between %d and %d.' with the tool name, minimum, and maximum. Bounds for max_rows are 0..MCPRuntimeProtectionPolicy.MAX_ROWS_LIMIT (0 meaning 'use the default'). This validates numeric tool arguments before any SQL is analyzed or executed.","triggerScenarios":"Passing max_rows (or another numeric argument) as a non-integer (string like 'all', float), an integer below the minimum, or above the configured MAX_ROWS_LIMIT.","commonSituations":"LLM emitting \"max_rows\": \"100\" as a string or \"max_rows\": 1000000 exceeding the cap; UI default of -1; JSON coercion surprises where numbers arrive as strings.","solutions":["Send max_rows as an integer within [0, MAX_ROWS_LIMIT], or omit it to get the default.","Coerce and clamp client-side before invoking the tool.","If a higher limit is truly needed, have the operator raise MCPRuntimeProtectionPolicy.MAX_ROWS_LIMIT."],"exampleFix":"// before\nawait tools.call('database_gateway_execute_query', { sql, max_rows: 'all' }); // not an integer -> error\n\n// after\nawait tools.call('database_gateway_execute_query', { sql, max_rows: 200 });","handlingStrategy":"validation","validationCode":"function normalizeMaxRows(raw) {\n  const n = Number(raw);\n  if (!Number.isInteger(n) || n < 0 || n > MAX_ROWS_LIMIT) {\n    return DEFAULT_MAX_ROWS; // omit to use server default, or throw early with a clear message\n  }\n  return n;\n}\nconst args = { sql };\nconst maxRows = normalizeMaxRows(userMaxRows);\nif (maxRows !== DEFAULT_MAX_ROWS) args.max_rows = maxRows;","typeGuard":"function isValidMaxRows(value) {\n  return Number.isInteger(value) && value >= 0 && value <= MAX_ROWS_LIMIT;\n}","tryCatchPattern":"try {\n  return await tools.call('database_gateway_execute_query', { sql, max_rows });\n} catch (e) {\n  if (/must be an integer between/.test(e.message)) {\n    const [, min, max] = e.message.match(/between (\\d+) and (\\d+)/) ?? [];\n    return tools.call('database_gateway_execute_query', { sql, max_rows: Math.min(Math.max(0, max_rows|0), Number(max)) });\n  }\n  throw e;\n}","preventionTips":["Coerce numeric tool arguments to integers client-side; never send strings.","Clamp max_rows into [0, limit] or omit it for the default.","Parse the min/max from the error message to self-correct once.","Learn the deployed MAX_ROWS_LIMIT before promising large result pages."],"tags":["mcp","tool-arguments","validation","numeric","limits"],"backgroundTag":null,"analyzedSha":"e952770a215630a3659c75d64369168cd3e26b82","analyzedAt":"2026-08-14T13:54:53.392Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}