contextvm-sdk 0.1.0

Rust SDK for the ContextVM protocol — MCP over Nostr
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
---
title: CEP-22 Oversized Payload Transfer
description: Bounded oversized payload transfer for ContextVM using progress-notification framing
---

# Oversized Payload Transfer

**Status:** Draft
**Author:** @contextvm-org
**Type:** Standards Track

## Abstract

This CEP defines a bounded reassembly profile for ContextVM messages that are too large to publish as a single relay event. It reuses MCP [`notifications/progress`](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/progress) as the framing envelope and uses the request `progressToken` as the transfer identifier.

The receiver reconstructs the exact serialized JSON-RPC message, validates a SHA-256 digest announced in `start`, and only then materializes the synthetic final request or response.

This CEP also defines an optional `accept` step for stateless client-to-server bootstrap.

## Specification

### Overview

ContextVM currently carries MCP JSON-RPC payloads inside Nostr events. This works well for ordinary request and response sizes, but large payloads may exceed relay event size limits and fail to publish even when the logical MCP operation completed correctly.

This CEP defines a bounded oversized-transfer profile that:

- reuses the existing single-kind ContextVM transport model
- reuses MCP [`notifications/progress`]https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/progress as the transfer envelope
- uses the request `progressToken` as the transfer identifier
- supports ordered `start`, `accept`, `chunk`, `end`, and `abort` frames
- preserves normal MCP semantics through rendered reconstruction after bounded reassembly completes

This CEP is focused on **bounded** transfer only. It does not define open-ended streams, selective retransmission, or chunk repair procedures.

### Capability Advertisement and Negotiation

Support MAY be advertised through the same additive discovery surfaces used by ContextVM features, following [`src/content/docs/spec/ceps/cep-6.md`](src/content/docs/spec/ceps/cep-6.md) and [`src/content/docs/spec/ceps/cep-19.md`](src/content/docs/spec/ceps/cep-19.md).

Peers MAY advertise support using `support_oversized_transfer` tags.

Example:

```json
[["support_oversized_transfer"]]
```

Advertisement surfaces:

- public announcements
- MCP initialization
- first exchanged request or response in stateless operation

`support_oversized_transfer` indicates support for this profile. Implementations that advertise it MUST support `completionMode: "render"`.

### Request-Level Activation

Oversized transfer is available only when the initiating request includes a valid MCP `progressToken`.

Rules:

- Clients that want to permit oversized transfer for a request MUST include a `progressToken`.
- Servers MUST NOT start an oversized transfer for a request that did not include a `progressToken`.
- When no `progressToken` is present, peers MUST use ordinary non-fragmented behavior or fail cleanly.

The `progressToken` identifies the transfer session.

### Sender Behavior

For a logical message associated with a request carrying a `progressToken`:

- the sender SHOULD proactively use oversized transfer when it can predict that direct publication is likely to exceed relay limits
- the sender MAY reactively switch to oversized transfer when direct publication fails with a size-indicative error
- the sender MUST NOT assume that an ambiguous publish failure is caused by payload size

When oversized transfer is used, the sender MUST emit an ordered sequence of MCP [`notifications/progress`](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/progress) messages carrying ContextVM transfer frames.

If the sender already knows the receiver supports this CEP for the exchange, it MAY proceed directly from `start` to `chunk`. Otherwise it MUST wait for `accept` before sending `chunk` frames.

### Progress Notification Framing

Oversized-transfer frames are carried inside MCP `notifications/progress` params.

Example:

```json
{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": {
    "progressToken": "req-123",
    "progress": 1,
    "message": "starting oversized transfer",
    "cvm": {
      "type": "oversized-transfer",
      "frameType": "start",
      "completionMode": "render"
    }
  }
}
```

`progress` values MUST increase monotonically across the transfer.

### Frame Types

This CEP defines five frame types:

- `start`
- `accept`
- `chunk`
- `end`
- `abort`

#### Common Fields

All oversized-transfer frames MUST include a ContextVM-specific transport object with:

- `type`: MUST be `oversized-transfer`
- `frameType`: one of `start`, `accept`, `chunk`, `end`, `abort`

The outer MCP progress params MUST include:

- `progressToken`
- `progress`

The outer MCP `total` and `message` fields MAY be used for progress reporting and UX hints, but they do not define transfer correctness.

#### `start` Frame

The `start` frame begins the transfer.

Required fields:

- `completionMode`: `render`
- `digest`
- `totalBytes`
- `totalChunks`

Rules:

- If `completionMode` is omitted, receivers MAY reject the transfer; senders SHOULD always provide it.
- In this CEP version, senders MUST use `completionMode: "render"`.
- Receivers MUST reject unknown or unsupported completion modes.
- `digest` MUST be the SHA-256 of the exact serialized JSON-RPC message string, encoded as UTF-8.
- `totalBytes` MUST equal the exact byte length of the serialized JSON-RPC message string encoded as UTF-8.
- `totalChunks` MUST equal the number of `chunk` frames that the sender intends to transmit before `end`.
- Receivers MAY reject `start` immediately when `totalBytes` or `totalChunks` exceeds local policy limits.

#### `chunk` Frame

The `chunk` frame carries one ordered fragment.

Required fields:

- `data`: chunk payload

Rules:

- For oversized-transfer frames, MCP `progress` is the normative ordering field.
- Each `chunk` frame MUST use a `progress` value greater than the preceding transfer frame's `progress` value.
- The payload represented by `data` is an ordered fragment of the exact serialized logical message associated with the transfer.
- Receivers MAY accept out-of-order arrival of valid `chunk` frames and buffer them in memory for later assembly, provided they still reconstruct the payload strictly by increasing `progress` order.
- Receivers SHOULD enforce bounded buffering limits for out-of-order chunks and MAY abort when those limits are exceeded.
- Receivers MAY track gaps between observed `progress` values while awaiting delayed chunks, but MUST NOT treat a gap alone as terminal failure before `end` or local timeout/policy conditions are reached.

#### `accept` Frame

The `accept` frame confirms that the sender may begin transmitting `chunk` frames.

Rules:

- A receiver MAY send `accept` after `start`.
- A sender that is required to wait for confirmation MUST NOT send `chunk` frames before receiving `accept`.
- `accept` does not change completion semantics.
- `accept` SHOULD remain minimal and does not negotiate additional transfer parameters in v1.

#### `end` Frame

The `end` frame signals successful sender-side completion.

Rules:

- `end` is required for successful completion.

#### `abort` Frame

The `abort` frame signals that the transfer did not complete successfully.

Optional fields:

- `reason`

Rules:

- Receivers MUST treat `abort` as terminal for the transfer.
- `reason` is advisory only.

### Completion Mode

This CEP defines one completion mode: `render`.

In `render` mode:

- the chunk sequence represents one bounded logical JSON-RPC message
- the receiver MUST reassemble chunks in `progress` order
- the receiver MAY temporarily buffer valid out-of-order `chunk` frames before assembly, subject to local bounded-memory policy
- the receiver MUST NOT surface partial payloads upward
- the receiver MUST materialize a synthetic final request or response only after validation succeeds

The `completionMode` field remains part of the `start` frame as an extension surface for future CEPs, but this CEP defines only `render`.

### Validation Rules

#### Ordering and Completeness

Receivers MUST validate transfer ordering using MCP `progress`.

Rules:

- a transfer MUST begin with `start`
- if confirmation is required for the transfer, `accept` MUST be received before the first `chunk`
- `progress` values for oversized-transfer frames MUST increase monotonically across the transfer
- receivers MUST treat `progress` as the canonical assembly index, not as a guarantee of arrival order from relays
- receivers MAY buffer valid out-of-order `chunk` frames within bounded local limits and later assemble them by `progress` order
- receivers MAY track missing `progress` positions as provisional gaps while the transfer remains in flight
- receivers MUST fail the transfer if the received chunk set cannot satisfy the declared `totalChunks` and `totalBytes` from `start`
- successful completion requires `end`
- if `end` arrives after malformed or non-monotonic transfer ordering, the transfer MUST fail
- if `end` arrives while provisional gaps remain unresolved, the transfer MUST fail

This CEP does not define selective retransmission or repair.

#### Digest Validation for `render`

Integrity is validated using SHA-256.

Rules:

- the sender MUST compute the digest over the exact serialized JSON-RPC message string
- the sender MUST encode that string as UTF-8 before hashing
- the `start` frame MUST carry the digest value
- the `start` frame MUST carry `totalBytes` and `totalChunks`
- the receiver MUST reconstruct the exact serialized string in `progress` order
- the receiver MUST verify that the reconstructed UTF-8 byte length equals `totalBytes`
- the receiver MUST verify that exactly `totalChunks` `chunk` frames were assembled before accepting `end`
- the receiver MUST compute SHA-256 over the reconstructed UTF-8 byte sequence
- the receiver MUST compare the result to the advertised digest before materializing the synthetic message

The digest is over the serialized message string, not an abstract JSON object.

The same digest rule applies symmetrically to:

- oversized client-to-server requests
- oversized server-to-client responses

### Receiver Behavior

Receivers that support this CEP:

- MUST track transfer state by `progressToken`
- MUST process frames in bounded transfer order
- MUST reject or fail malformed frame sequences
- MUST treat `abort` as terminal
- MUST evaluate declared `totalBytes` and `totalChunks` against local limits before committing unbounded reassembly state
- MUST fail a transfer if `end` is received before a valid monotonic `progress` sequence has been observed
- SHOULD enforce a bounded out-of-order window or equivalent memory policy for unresolved chunk gaps
- MUST fail a transfer when local timeout or bounded-buffer policy makes successful completion no longer possible

Receivers MUST only surface the synthetic final request or response after digest validation succeeds.

### Stateless Operation

This CEP is compatible with stateless ContextVM operation:

- peers MAY advertise support in tags on the first exchanged request or response
- transfer state is correlated by `progressToken`
- receivers MUST NOT rely on a persistent connection-local session beyond temporary bounded reassembly state

For stateless oversized client-to-server transfer where the client has not previously learned server support, the client MUST send `start` first and wait for `accept` before sending `chunk` frames.

### Timeout Semantics

Because frames are carried by MCP [`notifications/progress`](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/progress), receipt of valid transfer frames MAY be treated as progress activity for request timeout handling.

Implementations:

- MAY reset soft request timeouts upon receiving valid transfer frames
- SHOULD still enforce a hard maximum timeout for the underlying request

### Relay Size Guidance

Practical relay acceptance thresholds are often near a total serialized event size of approximately `64 KiB`.

Implementations:

- SHOULD treat relay size limits as applying to the full serialized Nostr event, not only to the JSON-RPC payload or event `content`
- SHOULD use conservative margin below common practical thresholds when deciding whether to fragment proactively
- SHOULD prefer proactive oversized transfer when they can predict that direct publication is likely to exceed common relay acceptance limits
- MUST NOT assume that all relays enforce the same threshold or rejection behavior

### Best Practices and Risks

Implementations should keep oversized transfer defensively bounded.

- Receivers SHOULD enforce strict limits on concurrent reassembly state, buffered bytes, and unresolved out-of-order chunks to reduce OOM risk.
- Receivers SHOULD bound the out-of-order buffer window and fail transfers that cannot plausibly complete within local resource policy.
- Senders SHOULD choose chunk sizes conservatively so each resulting Nostr event remains below practical relay acceptance limits.
- Receivers SHOULD treat declared `totalBytes` and `totalChunks` as admission-control inputs before committing memory.
- Implementations SHOULD use hard timeouts so incomplete transfers do not retain memory indefinitely.
- Implementations MUST assume relay delivery may be delayed, duplicated, or reordered, and MUST NOT infer successful completion until `end`, completeness checks, and digest validation all succeed.

### Example: `render` Response Transfer

Client request:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "large_tool",
    "arguments": {},
    "_meta": {
      "progressToken": "req-123"
    }
  }
}
```

Server `start`:

```json
{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": {
    "progressToken": "req-123",
    "progress": 1,
    "message": "starting oversized response",
    "cvm": {
      "type": "oversized-transfer",
      "frameType": "start",
      "completionMode": "render",
      "digest": "sha256:8d969eef6ecad3c29a3a629280e686cff8fabcd1...",
      "totalBytes": 49,
      "totalChunks": 2
    }
  }
}
```

Server `chunk` frames:

```json
{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": {
    "progressToken": "req-123",
    "progress": 2,
    "cvm": {
      "type": "oversized-transfer",
      "frameType": "chunk",
      "data": "{\"jsonrpc\":\"2.0\",\"id\":1,"
    }
  }
}
```

```json
{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": {
    "progressToken": "req-123",
    "progress": 3,
    "cvm": {
      "type": "oversized-transfer",
      "frameType": "chunk",
      "data": "\"result\":{\"content\":[...]}}"
    }
  }
}
```

Server `end`:

```json
{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": {
    "progressToken": "req-123",
    "progress": 4,
    "message": "oversized response complete",
    "cvm": {
      "type": "oversized-transfer",
      "frameType": "end"
    }
  }
}
```

The client reconstructs the exact serialized JSON-RPC response string, verifies the digest from `start`, and materializes a synthetic final response.

### Example: Stateless Client-to-Server Bootstrap

Client `start`:

```json
{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": {
    "progressToken": "req-789",
    "progress": 1,
    "message": "starting oversized request",
    "cvm": {
      "type": "oversized-transfer",
      "frameType": "start",
      "completionMode": "render",
      "digest": "sha256:8d969eef6ecad3c29a3a629280e686cff8fabcd1...",
      "totalBytes": 10485760,
      "totalChunks": 160
    }
  }
}
```

Server `accept`:

```json
{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": {
    "progressToken": "req-789",
    "progress": 2,
    "message": "oversized request accepted",
    "cvm": {
      "type": "oversized-transfer",
      "frameType": "accept"
    }
  }
}
```

After `accept`, the client sends `chunk` frames and finishes with `end`.

## Backward Compatibility

This CEP introduces no breaking changes:

- peers that do not advertise support continue using ordinary ContextVM request and response transport
- peers that do not include a `progressToken` on a request do not enable oversized transfer for that exchange
- future completion modes remain possible through `completionMode`, but this CEP defines only `render`

Implementations that ignore the new tags or do not understand the oversized-transfer framing continue to interoperate for ordinary non-fragmented messages.

## Dependencies

- [CEP-6: Public Server Announcements]/spec/ceps/cep-6

## Reference Implementation

A reference implementation is intended for the ContextVM SDK transport layer.