apache/shardingsphere · error · MCPToolCallLimitExceededException
MCP session exceeded the maximum tool call quota of %d.
Error message
MCP session exceeded the maximum tool call quota of %d.
What it means
MCPToolCallLimiter counts tool invocations per session with an AtomicInteger; when a session's count exceeds maxToolCallsPerSession it throws MCPToolCallLimitExceededException with the session id, tool name, and quota. The quota is a runtime-protection cap that bounds how much work one MCP session can trigger. releaseSession(sessionId) clears the counter, so a closed session's budget is freed.
Source
Thrown at mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/MCPToolCallLimiter.java:48
*/
public final class MCPToolCallLimiter {
private final int maxToolCallsPerSession = MCPRuntimeProtectionPolicy.getMaxToolCallsPerSession();
private final Map<String, AtomicInteger> sessionToolCallCounts = new ConcurrentHashMap<>();
/**
* Acquire one tool call budget slot.
*
* @param sessionId session identifier
* @param toolName tool name
* @throws MCPToolCallLimitExceededException when session tool call quota is exhausted
*/
public void acquire(final String sessionId, final String toolName) {
String actualSessionId = normalizeSessionId(sessionId);
int callCount = sessionToolCallCounts.computeIfAbsent(actualSessionId, ignored -> new AtomicInteger()).incrementAndGet();
if (callCount > maxToolCallsPerSession) {
throw new MCPToolCallLimitExceededException(actualSessionId, Objects.toString(toolName, ""), maxToolCallsPerSession);
}
}
/**
* Release all tracked budget state for one session.
*
* @param sessionId session identifier
*/
public void releaseSession(final String sessionId) {
sessionToolCallCounts.remove(normalizeSessionId(sessionId));
}
private String normalizeSessionId(final String sessionId) {
String result = Objects.toString(sessionId, "").trim();
return result.isEmpty() ? "anonymous" : result;
}
}
View on GitHub (pinned to e952770a21)
Solutions
- Start a new session (initialize) to obtain a fresh tool call budget, or reduce the number of tool calls by batching SQL.
- Track calls client-side and rotate the session before hitting maxToolCallsPerSession.
- Ask the operator to raise maxToolCallsPerSession if the workload legitimately needs more calls.
- Fix retry logic that hammers the same tool on failure.
Example fix
// before
for (const sql of oneThousandStatements) {
await mcp.callTool(sessionId, 'database_gateway_execute_update', { sql, execution_mode: 'execute' });
} // blows the per-session quota
// after: batch statements and rotate sessions
for (let i = 0; i < oneThousandStatements.length; i += BATCH) {
if (callsThisSession + BATCH > QUOTA) sessionId = await mcp.initialize();
await mcp.callTool(sessionId, 'database_gateway_execute_update', batchArgs(oneThousandStatements.slice(i, i + BATCH)));
} Defensive patterns
Strategy: validation
Validate before calling
// Track calls per session and rotate before the quota
let callsThisSession = 0;
async function budgetedCall(tool, args) {
if (callsThisSession >= MAX_TOOL_CALLS_PER_SESSION) {
sessionId = (await mcp.initialize()).sessionId;
callsThisSession = 0;
}
callsThisSession++;
return mcp.callTool(sessionId, tool, args);
} Try / catch
try {
return await mcp.callTool(sessionId, tool, args);
} catch (e) {
if (/maximum tool call quota/.test(e.message)) {
sessionId = (await mcp.initialize()).sessionId; // fresh budget
return mcp.callTool(sessionId, tool, args);
}
throw e;
} Prevention
- Budget tool calls client-side; rotate sessions before reaching the quota.
- Batch multiple statements into fewer execute_update calls.
- Cap retry attempts so failures don't burn quota.
- Ask operators to size maxToolCallsPerSession to the real workload.
When it happens
Trigger: More than maxToolCallsPerSession tools/call requests through a single session id, e.g. an agent loop calling database_gateway_execute_query hundreds of times without re-initializing, or a runaway client retry storm.
Common situations: Autonomous agent loops with no call budget; low operator-configured quota for the deployment; session left open across many batch iterations; retries after transient errors consuming budget.
Related errors
- Completion request limit of %d per minute exceeded for MCP s
- Session attribution does not match this MCP session.
- Session does not exist.
- Failed to start %s server.
- MCP configuration file `%s` does not exist.
AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14).
Data as JSON: /api/errors/42fbe7905876a646.
Report an issue: GitHub.