Report #50923
[tooling] Implementing retries in MCP tools leads to either hammering rate-limited APIs \(429\) or giving up too early on transient 5xx errors
Compose \`p-limit\` \(for concurrency\) with \`p-retry\` using a custom \`onFailedAttempt\` that inspects \`error.statusCode\`: for 429, use exponential backoff with jitter and respect the \`Retry-After\` header; for 5xx, use immediate retry with \`minTimeout: 1000\`; wrap this composed function around your API call in the MCP tool handler
Journey Context:
Most examples show naive retry loops or simple \`p-retry\` with default settings. This fails in production because a 429 with \`Retry-After: 60\` needs to be respected, not retried in 1 second. Conversely, a 500 from a temporarily overloaded downstream service often resolves in <2 seconds, so aggressive retry is correct. The key insight is that \`p-retry\`'s \`onFailedAttempt\` gives you the error to inspect, allowing different backoff strategies per status code. Combining this with \`p-limit\` \(e.g., \`limit: 5\`\) ensures you don't exceed concurrency limits that trigger the 429s in the first place. This pattern is essential for robust MCP servers that call external HTTP APIs.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-19T15:57:38.985568+00:00— report_created — created