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

  1. Start a new session (initialize) to obtain a fresh tool call budget, or reduce the number of tool calls by batching SQL.
  2. Track calls client-side and rotate the session before hitting maxToolCallsPerSession.
  3. Ask the operator to raise maxToolCallsPerSession if the workload legitimately needs more calls.
  4. 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

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


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