Oversized Transfer
--- title: Oversized Transfer description: Bounded oversized payload transfer for ContextVM using MCP progress-notification framing --- # Oversized Transfer Oversized transfer is the SDK feature that automatically switches a message to the [CEP-22: Oversized Payload Transfer](/spec/ceps/cep-22) 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. ## Default Behavior Oversized transfer is enabled by default on both [`Nostr Client Transport`](/ts-sdk/transports/nostr-client-transport) and [`Nostr Server Transport`](/ts-sdk/transports/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. ## Request Activation and `progressToken` 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](/spec/ceps/cep-22). 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. ### Recommended MCP client usage When you expect a request may run for a while or may trigger CEP-22 fragmentation, prefer enabling progress handling explicitly: ```typescript 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 ### 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. ### Low-level transport usage If you are working below the usual MCP client helpers, such as constructing raw requests for [`NostrClientTransport`](/ts-sdk/transports/nostr-client-transport), 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. ## When You Would Change It 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 ## Configuration Use the `oversizedTransfer` transport option to enable, disable, or tune the feature. ```typescript 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: ```typescript import { NostrClientTransport } from '@contextvm/sdk'; const transport = new NostrClientTransport({ signer, relayHandler, oversizedTransfer: { enabled: false, }, }); ``` ### Options | Option | Default | Meaning | | ----------------- | ---------------------- | --------------------------------------------------------------------------------- | | `enabled` | `true` | Enables automatic CEP-22 oversized transfer support | | `thresholdBytes` | `48_000` | Size at which the sender proactively switches to oversized transfer | | `chunkSizeBytes` | `48_000` | Maximum payload bytes placed in each emitted chunk | | `acceptTimeoutMs` | `30000` on the client | How long to wait for `accept` when handshake is required | | `policy` | built-in safe defaults | Receiver-side limits for bytes, chunks, concurrency, ordering window, and timeout | ## How to Think About the Options - 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. ## Receiver Policy Defaults 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. ## Errors You May See If an oversized transfer fails, the transport may surface one of these errors: | Error | Description | | ---------------------------------- | ---------------------------------------------------------------- | | `OversizedTransferAbortError` | The remote side aborted the transfer | | `OversizedTransferPolicyError` | The transfer exceeded local safety limits | | `OversizedTransferDigestError` | Reassembled payload integrity validation failed | | `OversizedTransferReassemblyError` | The transfer completed with missing or inconsistent payload data | | `OversizedTransferSequenceError` | The transfer violated CEP-22 frame ordering rules | ## Recommendations - 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](/spec/ceps/cep-22) for protocol-level behavior. ## Related Documentation - [CEP-22: Oversized Payload Transfer](/spec/ceps/cep-22) - [Nostr Client Transport](/ts-sdk/transports/nostr-client-transport) - [Nostr Server Transport](/ts-sdk/transports/nostr-server-transport)Oversized Transfer
Section titled “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.
Default Behavior
Section titled “Default Behavior”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.
Request Activation and progressToken
Section titled “Request Activation and progressToken”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
onprogresscallback, the SDK assigns theprogressTokenautomatically - if you build raw JSON-RPC requests manually or bypass the usual MCP client helpers, you must ensure a
progressTokenis present yourself - when no
progressTokenis 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.
Recommended MCP client usage
Section titled “Recommended MCP client usage”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
progressTokenautomatically - 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
Why resetTimeoutOnProgress: true is strongly recommended
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.
Low-level transport usage
Section titled “Low-level transport usage”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
onprogressusually 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: trueshould be paired withonprogress; providing your own raw token alone does not enable the SDK-managed timeout reset path.
When You Would Change It
Section titled “When You Would Change It”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
Configuration
Section titled “Configuration”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, },});Options
Section titled “Options”| Option | Default | Meaning |
|---|---|---|
enabled | true | Enables automatic CEP-22 oversized transfer support |
thresholdBytes | 48_000 | Size at which the sender proactively switches to oversized transfer |
chunkSizeBytes | 48_000 | Maximum payload bytes placed in each emitted chunk |
acceptTimeoutMs | 30000 on the client | How long to wait for accept when handshake is required |
policy | built-in safe defaults | Receiver-side limits for bytes, chunks, concurrency, ordering window, and timeout |
How to Think About the Options
Section titled “How to Think About the Options”- Use
enabledwhen you want a clear on/off switch. - Use
thresholdByteswhen your relays reject large events earlier than expected, or when you want earlier proactive fragmentation. - Use
chunkSizeByteswhen you need more headroom for relay-specific event overhead. - Use
acceptTimeoutMswhen stateless bootstrap flows need a shorter or longer wait window. - Use
policywhen you need tighter safety bounds for inbound traffic.
Receiver Policy Defaults
Section titled “Receiver Policy Defaults”The default receiver policy is intentionally bounded:
maxTransferBytes:100 MiBmaxTransferChunks:10_000maxConcurrentTransfers:64maxOutOfOrderWindow:21maxOutOfOrderChunks:42transferTimeoutMs:5 minutes
These defaults are appropriate for most consumers. Adjust them only if your deployment has stricter resource constraints or different trust assumptions.
Errors You May See
Section titled “Errors You May See”If an oversized transfer fails, the transport may surface one of these errors:
| Error | Description |
|---|---|
OversizedTransferAbortError | The remote side aborted the transfer |
OversizedTransferPolicyError | The transfer exceeded local safety limits |
OversizedTransferDigestError | Reassembled payload integrity validation failed |
OversizedTransferReassemblyError | The transfer completed with missing or inconsistent payload data |
OversizedTransferSequenceError | The transfer violated CEP-22 frame ordering rules |
Recommendations
Section titled “Recommendations”- 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
onprogressso the SDK can assign aprogressTokenautomatically. - Set
resetTimeoutOnProgress: truefor requests that may emit progress or trigger CEP-22 oversized transfer. - Tighten
policyvalues if you operate in a more adversarial or resource-constrained environment. - Refer to CEP-22: Oversized Payload Transfer for protocol-level behavior.