Skip to content

Oversized Transfer

Oversized transfer is the SDK feature that automatically switches a message to the CEP-22: Oversized Payload Transfer flow when a normal relay event would likely be too large.

In normal use, application code does not send chunks or manage transfer state directly. The transport handles that internally.

Oversized transfer is enabled by default on both Nostr Client Transport and Nostr Server Transport.

With defaults:

  • the SDK automatically detects when a message crosses the oversized threshold
  • the SDK fragments and reassembles the payload automatically
  • the SDK enforces bounded receiver-side limits for safety
  • no extra application logic is required for ordinary usage

This means most consumers do not need to configure anything unless they want stricter limits or different relay-size margins.

CEP-22 oversized transfer is request-scoped. It is only available for a logical exchange when the originating MCP request includes a progressToken, as described in CEP-22: Oversized Payload Transfer.

In practice, this matters most on the client side:

  • if you use the MCP TypeScript SDK in the normal high-level way and provide an onprogress callback, the SDK assigns the progressToken automatically
  • if you build raw JSON-RPC requests manually or bypass the usual MCP client helpers, you must ensure a progressToken is present yourself
  • when no progressToken is present, oversized transfer cannot activate for that request, so the exchange falls back to ordinary non-fragmented behavior

For the MCP TypeScript SDK’s low-level client.request() path specifically, resetTimeoutOnProgress: true only works when an onprogress callback is provided. In that code path the SDK registers the progress handler and manages the request progressToken automatically.

This is why oversized transfer usually works transparently for normal MCP client calls, but low-level transport integrations must still understand the request-level activation rule.

When you expect a request may run for a while or may trigger CEP-22 fragmentation, prefer enabling progress handling explicitly:

const result = await client.callTool(
{
name: 'long-task',
arguments: {},
},
CallToolResultSchema,
{
onprogress: (progress) => {
console.log(
`${progress.progress}/${progress.total}: ${progress.message}`,
);
},
timeout: 30_000,
resetTimeoutOnProgress: true,
},
);

With this pattern:

  • the MCP TypeScript SDK creates the progressToken automatically
  • the application receives ordinary MCP progress updates when they are emitted
  • ContextVM transports can also use the same MCP progress channel for CEP-22 transfer frames
Section titled “Why resetTimeoutOnProgress: true is strongly recommended”

CEP-22 frames are carried over MCP notifications/progress. That means a valid oversized transfer is itself ongoing request activity, not idle time.

Setting resetTimeoutOnProgress: true is therefore strongly recommended because it allows each valid progress notification to extend the request timeout window. Without that, a client may time out a healthy oversized transfer simply because the response is arriving as multiple progress-framed transfer messages before the final JSON-RPC result.

When using the MCP TypeScript SDK, remember that this timeout-reset behavior is tied to onprogress. In practice, use both together.

If you are working below the usual MCP client helpers, such as constructing raw requests for NostrClientTransport, treat progressToken as part of the activation contract for CEP-22.

  • High-level MCP SDK usage with onprogress usually handles this automatically.
  • Manual raw requests must include the token explicitly in request metadata.
  • If you omit it, the transport cannot correlate an oversized transfer session for that exchange.
  • For low-level MCP TS SDK client.request() calls, resetTimeoutOnProgress: true should be paired with onprogress; providing your own raw token alone does not enable the SDK-managed timeout reset path.

Most projects should keep the default behavior.

Tuning is mainly useful when:

  • you want to disable the feature completely
  • your relay set accepts smaller or larger practical event sizes
  • you want stricter memory and concurrency limits on inbound transfers
  • you want a different timeout for accept-gated request flows

Use the oversizedTransfer transport option to enable, disable, or tune the feature.

import { NostrClientTransport } from '@contextvm/sdk';
const transport = new NostrClientTransport({
signer,
relayHandler,
oversizedTransfer: {
enabled: true,
},
});

Disable it explicitly when you want the transport to avoid CEP-22 fragmentation entirely:

import { NostrClientTransport } from '@contextvm/sdk';
const transport = new NostrClientTransport({
signer,
relayHandler,
oversizedTransfer: {
enabled: false,
},
});
OptionDefaultMeaning
enabledtrueEnables automatic CEP-22 oversized transfer support
thresholdBytes48_000Size at which the sender proactively switches to oversized transfer
chunkSizeBytes48_000Maximum payload bytes placed in each emitted chunk
acceptTimeoutMs30000 on the clientHow long to wait for accept when handshake is required
policybuilt-in safe defaultsReceiver-side limits for bytes, chunks, concurrency, ordering window, and timeout
  • Use enabled when you want a clear on/off switch.
  • Use thresholdBytes when your relays reject large events earlier than expected, or when you want earlier proactive fragmentation.
  • Use chunkSizeBytes when you need more headroom for relay-specific event overhead.
  • Use acceptTimeoutMs when stateless bootstrap flows need a shorter or longer wait window.
  • Use policy when you need tighter safety bounds for inbound traffic.

The default receiver policy is intentionally bounded:

  • maxTransferBytes: 100 MiB
  • maxTransferChunks: 10_000
  • maxConcurrentTransfers: 64
  • maxOutOfOrderWindow: 21
  • maxOutOfOrderChunks: 42
  • transferTimeoutMs: 5 minutes

These defaults are appropriate for most consumers. Adjust them only if your deployment has stricter resource constraints or different trust assumptions.

If an oversized transfer fails, the transport may surface one of these errors:

ErrorDescription
OversizedTransferAbortErrorThe remote side aborted the transfer
OversizedTransferPolicyErrorThe transfer exceeded local safety limits
OversizedTransferDigestErrorReassembled payload integrity validation failed
OversizedTransferReassemblyErrorThe transfer completed with missing or inconsistent payload data
OversizedTransferSequenceErrorThe transfer violated CEP-22 frame ordering rules
  • Leave oversized transfer enabled unless you have a specific reason not to.
  • Keep defaults unless you know your relay environment needs different thresholds.
  • Prefer high-level MCP client calls with onprogress so the SDK can assign a progressToken automatically.
  • Set resetTimeoutOnProgress: true for requests that may emit progress or trigger CEP-22 oversized transfer.
  • Tighten policy values if you operate in a more adversarial or resource-constrained environment.
  • Refer to CEP-22: Oversized Payload Transfer for protocol-level behavior.