@mcp-utils/retry
v1.0.0
Published
Retry with exponential backoff for MCP tool handlers — powered by vurb.
Maintainers
Readme
@mcp-utils/retry
External APIs fail. Networks are unreliable. A single transient error — a 503 from your payment gateway, a database connection blip — is enough to break the entire agent interaction.
LLM agents don't retry automatically. When your tool throws, the AI gives up.
Here's how to make your tools resilient in one line:
import { withRetry } from '@mcp-utils/retry';
// Before: one transient error = broken tool call = broken agent
handler: async (ctx, args) => success(await paymentApi.charge(args))
// After: survives failures, recovers automatically
handler: withRetry(
async (ctx, args) => success(await paymentApi.charge(args)),
{ attempts: 3, backoff: 'exponential' }
)Install
npm install @mcp-utils/retryUsage
Exponential backoff (recommended for APIs)
import { withRetry } from '@mcp-utils/retry';
const chargePayment = withRetry(
async (ctx, args) => success(await paymentApi.charge(args)),
{
attempts: 3,
backoff: 'exponential', // 300ms → 600ms → 1200ms
baseDelayMs: 300,
},
);Retry only when it makes sense
Don't retry auth failures or business logic errors — only transient failures:
const fetchData = withRetry(handler, {
attempts: 5,
retryOn: (err) => {
if (err instanceof AuthError) return false; // don't retry — permanent
if (err instanceof RateLimitError) return true; // retry — transient
return isNetworkError(err);
},
});Shared policy across handlers
import { createRetry } from '@mcp-utils/retry';
const resilient = createRetry({ attempts: 3, backoff: 'exponential' });
const getUser = resilient(async (ctx, args) => success(await api.users.get(args['id'])));
const getOrg = resilient(async (ctx, args) => success(await api.orgs.get(args['id'])));Combine with timeout
import { withTimeout } from '@mcp-utils/timeout';
// Each attempt gets 5 seconds, up to 3 attempts
const handler = withRetry(
withTimeout(
async (ctx, args, _p, signal) => success(await api.call(args, signal)),
5_000,
),
{ attempts: 3 },
);API
withRetry(handler, options)
| Option | Type | Default | Description |
|---|---|---|---|
| attempts | number | 3 | Total attempts including the first |
| backoff | 'exponential' \| 'linear' \| 'none' | 'exponential' | Delay strategy between attempts |
| baseDelayMs | number | 300 | Base delay in milliseconds |
| maxDelayMs | number | 10_000 | Maximum delay cap |
| retryOn | (err, attempt) => boolean | always retry | Predicate controlling whether to retry |
- Only thrown exceptions trigger a retry —
isError: truedomain errors are returned as-is - Stops between attempts if the request has been cancelled via
AbortSignal - Returns a structured
toolErrorwith full diagnostic context after all attempts fail
createRetry(options)
Returns a decorator: (handler) => wrappedHandler. Same options as withRetry.
Part of @mcp-utils — production-grade utilities for MCP server development.
License
Apache-2.0
