apache/shardingsphere · warning · MCPUnavailableException
Completion request limit of %d per minute exceeded for MCP s
Error message
Completion request limit of %d per minute exceeded for MCP session `%s`; retry after the current 60-second window ends.
What it means
MCPCompletionRateLimiter enforces a fixed window of max completion requests per 60 seconds per session; the exception (MCPUnavailableException) is thrown when a session's requestCount already reached maxRequestsPerWindow inside the current window. The window is keyed by sessionId and resets when now >= startedAt + 60s, or when releaseSession removes the session entry. The message includes the configured limit and the offending session id and instructs the client to retry after the window ends.
Source
Thrown at mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/completion/MCPCompletionRateLimiter.java:59
private final Clock clock;
private final Map<String, CompletionWindow> sessionWindows = new ConcurrentHashMap<>();
MCPCompletionRateLimiter() {
this(MCPRuntimeProtectionPolicy.getMaxCompletionRequestsPerMinute(), Clock.systemUTC());
}
void acquire(final String sessionId) {
Instant now = clock.instant();
sessionWindows.compute(sessionId, (ignored, currentWindow) -> acquire(sessionId, currentWindow, now));
}
private CompletionWindow acquire(final String sessionId, final CompletionWindow currentWindow, final Instant now) {
if (null == currentWindow || !now.isBefore(currentWindow.startedAt().plus(WINDOW_DURATION))) {
return new CompletionWindow(now, 1);
}
if (currentWindow.requestCount() >= maxRequestsPerWindow) {
throw new MCPUnavailableException(String.format(
"Completion request limit of %d per minute exceeded for MCP session `%s`; retry after the current 60-second window ends.", maxRequestsPerWindow, sessionId));
}
return new CompletionWindow(currentWindow.startedAt(), currentWindow.requestCount() + 1);
}
void releaseSession(final String sessionId) {
sessionWindows.remove(sessionId);
}
private record CompletionWindow(Instant startedAt, int requestCount) {
}
}
View on GitHub (pinned to e952770a21)
Solutions
- Throttle client-side to the configured N requests per 60 seconds per session (debounce completion triggers).
- Wait until the current 60-second window closes, then retry; the counter resets at window boundaries.
- Ask the operator to raise maxRequestsPerWindow if legitimate workloads exceed it.
- Call releaseSession (server closes session) when done so window state is cleaned up.
Example fix
// before: fire a completion on every input event
input.onChange(() => mcp.callTool('completion', args)); // exceeds N/min -> 429-style error
// after: debounce and cap to the server limit
let pending = 0;
input.onChange(debounce(async () => {
if (pending >= LIMIT_PER_MIN) return; // wait for window reset
pending++;
try { await mcp.callTool('completion', args); } finally {}
}, 250)); Defensive patterns
Strategy: retry
Validate before calling
// Client-side fixed-window limiter matching the server contract
function makeLimiter(max, windowMs = 60_000) {
let startedAt = 0, count = 0;
return {
tryAcquire(now = Date.now()) {
if (now - startedAt >= windowMs) { startedAt = now; count = 0; }
return count++ < max;
},
retryAfterMs(now = Date.now()) { return Math.max(0, startedAt + windowMs - now); }
};
}
const limiter = makeLimiter(SERVER_COMPLETION_LIMIT);
if (!limiter.tryAcquire()) scheduleRetry(limiter.retryAfterMs()); Try / catch
try {
return await mcp.callTool('completion', args);
} catch (e) {
if (/Completion request limit of \d+ per minute exceeded/.test(e.message)) {
await sleep(msUntilNextMinuteBoundary()); // window is a fixed 60s from first request
return mcp.callTool('completion', args);
}
throw e;
} Prevention
- Debounce completion triggers (250-500ms) before calling the tool.
- Mirror the server's N-per-60s limit client-side and back off at the boundary.
- Cache completion results for identical prefixes within a window.
- Close sessions when finished so server-side window state is released.
When it happens
Trigger: Issuing more completion/complete requests than the configured limit for one session within a sliding start-of-window 60-second period, e.g. an LLM client that fans out one completion per keystroke or per argument without client-side throttling.
Common situations: Aggressive editor-style autocomplete wired to the MCP completion tool; batch scripts looping completion calls; low maxRequestsPerWindow configured server-side; a stale window not released after a session closed (missing releaseSession) making the next session's count appear exhausted only if ids collide — normally just genuine overuse.
Related errors
- MCP session exceeded the maximum tool call quota of %d.
- Session attribution does not match this MCP session.
- Completion argument `%s` is not declared for %s `%s`.
- Session does not exist.
- Failed to start %s server.
AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14).
Data as JSON: /api/errors/bca6a1f9b9d42da2.
Report an issue: GitHub.