Skip to content

Resilience

Every Discord REST call passes through one shared policy:

bulkhead → circuit breaker → retry → timeout → Discord REST

The pipeline meta-tool executes steps sequentially. Its parent invocation does not reserve a REST slot; each leaf REST request enters this policy independently.

LayerEnvironment variablesDefault
RetryMCP_RETRY_ENABLED, MCP_RETRY_MAX_ATTEMPTS, MCP_RETRY_BASE_DELAY_MS, MCP_RETRY_MAX_DELAY_MS, MCP_RETRY_JITTERenabled; 3 extra retries; 200–10000 ms; full jitter
TimeoutMCP_TIMEOUT_DEFAULT_MS30000 ms per attempt
Circuit breakerMCP_CIRCUIT_ENABLED, MCP_CIRCUIT_FAILURE_THRESHOLD, MCP_CIRCUIT_HALF_OPEN_AFTER_MSenabled; 10 failures; 60000 ms
BulkheadMCP_BULKHEAD_LIMIT100 in-flight REST calls; no queue

MCP_RETRY_MAX_ATTEMPTS is Cockatiel’s number of retries, not the total number of calls. The default 3 permits one initial call plus three retries. Setting it to 1 still permits one retry; use MCP_RETRY_ENABLED=false to disable this layer.

The classifier retries selected transient failures such as server errors and Discord rate limits. Idempotent HTTP methods can be replayed. Non-idempotent requests are protected from replay when the outcome is ambiguous, because a second POST could duplicate the action.

Discord’s REST client still owns route-bucket scheduling and 429 handling. discord-mcp passes { retries: 0 } to its constructor, which disables that constructor retry count; it does not remove the client’s queue or every internal rate-limit behavior. The outer policy can also retry a surfaced 429, subject to replay safety and its retry budget.

See Rate limits for the ownership boundary.

buildPolicy() creates one breaker shared by all calls using that policy. It is not partitioned per Discord route. Once the failure threshold opens it, subsequent calls fast-fail with CIRCUIT_OPEN until the half-open probe window.

Tune the threshold only after observing real traffic. A global breaker is a coarse upstream-protection mechanism: one failing route can affect other REST calls that share it.

The bulkhead limits concurrent REST calls across tools and rejects overflow immediately with BULKHEAD_FULL; its queue size is zero.

MCP_BULKHEAD_LIMIT=1 is valid. It does not deadlock the pipeline: steps are sequential, the parent holds no REST slot, and each leaf releases its slot before the next step begins.

  1. Disable retry with MCP_RETRY_ENABLED=false when you need the first failure.
  2. Treat MCP_RETRY_MAX_ATTEMPTS as extra retries when estimating latency.
  3. Keep non-idempotent calls out of manual retry loops unless their result is known.
  4. Expect CIRCUIT_OPEN to affect the shared policy, not a single route.
  5. Back off and reduce concurrency when a call returns BULKHEAD_FULL.