You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: seps/2339-task-continuity.md
+187Lines changed: 187 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -236,6 +236,193 @@ And finally, the server will accept that client request with a standard task sta
236
236
}
237
237
```
238
238
239
+
A complete example of a task-augmented tool call with an elicitation under MRTR is provided in more detail below:
240
+
241
+
<details>
242
+
243
+
Consider a simple task-augmented tool call, `hello_world`, requiring an elicitation for the user to provide their name. The tool itself takes no arguments.
244
+
245
+
To invoke this tool, the client makes a `CallToolRequest` as follows:
246
+
247
+
```json
248
+
{
249
+
"jsonrpc": "2.0",
250
+
"id": 2,
251
+
"method": "tools/call",
252
+
"params": {
253
+
"name": "hello_world",
254
+
"arguments": {}
255
+
},
256
+
"task": {
257
+
"ttl": 30000
258
+
}
259
+
}
260
+
```
261
+
262
+
The server recognizes this as a task-augmented request and immediately returns a `CreateTaskResult`:
263
+
264
+
```json
265
+
{
266
+
"jsonrpc": "2.0",
267
+
"id": 2,
268
+
"result": {
269
+
"task": {
270
+
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840",
271
+
"status": "working",
272
+
"createdAt": "2025-11-25T10:30:00Z",
273
+
"lastUpdatedAt": "2025-11-25T10:50:00Z",
274
+
"ttl": 30000,
275
+
"pollInterval": 5000
276
+
}
277
+
}
278
+
}
279
+
```
280
+
281
+
Once the client receives the `CreateTaskResult`, it begins polling `tasks/continue`:
282
+
283
+
```json
284
+
{
285
+
"jsonrpc": "2.0",
286
+
"id": 3,
287
+
"method": "tasks/continue",
288
+
"params": {
289
+
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840"
290
+
}
291
+
}
292
+
```
293
+
294
+
On each request while the task is in a `"working"` status, the server returns a regular task response:
295
+
296
+
```json
297
+
{
298
+
"jsonrpc": "2.0",
299
+
"id": 3,
300
+
"result": {
301
+
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840",
302
+
"status": "working",
303
+
"createdAt": "2025-11-25T10:30:00Z",
304
+
"lastUpdatedAt": "2025-11-25T10:50:00Z",
305
+
"ttl": 30000,
306
+
"pollInterval": 5000
307
+
}
308
+
}
309
+
```
310
+
311
+
Eventually, the server reaches the point at which it needs to send an elicitation to the user. It sets the task status to `"input_required"` to signal this. On the next `tasks/continue` request from the client, the server sends the elicitation payload via an `IncompleteResult`, following standard MRTR semantics. Note that the task info is not present in this response, which conforms to typical behavior under MRTR. If the client were to send a `tasks/get` request during this time, it would see a regular status response with `"input_required"` (without the embedded `inputRequests`).
312
+
313
+
```json
314
+
{
315
+
"jsonrpc": "2.0",
316
+
"id": 4,
317
+
"method": "tasks/continue",
318
+
"params": {
319
+
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840"
320
+
}
321
+
}
322
+
```
323
+
324
+
```json
325
+
{
326
+
"id": 4,
327
+
"jsonrpc": "2.0",
328
+
"result": {
329
+
"inputRequests": {
330
+
"name": {
331
+
"method": "elicitation/create",
332
+
"params": {
333
+
"mode": "form",
334
+
"message": "Please enter your name.",
335
+
"requestedSchema": {
336
+
"type": "object",
337
+
"properties": {
338
+
"name": { "type": "string" }
339
+
},
340
+
"required": ["name"]
341
+
}
342
+
}
343
+
}
344
+
}
345
+
}
346
+
}
347
+
```
348
+
349
+
The user enters their name, and the client repeats the `tasks/continue` request with the satisfied information:
350
+
351
+
```json
352
+
{
353
+
"jsonrpc": "2.0",
354
+
"id": 5,
355
+
"method": "tasks/continue",
356
+
"params": {
357
+
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840",
358
+
"inputResponses": {
359
+
"name": {
360
+
"action": "accept",
361
+
"content": {
362
+
"input": "Luca"
363
+
}
364
+
}
365
+
}
366
+
}
367
+
}
368
+
```
369
+
370
+
With the elicitation fulfilled and no other outstanding requests to send, the server moves the task back into the `"working"` status:
371
+
372
+
```json
373
+
{
374
+
"jsonrpc": "2.0",
375
+
"id": 5,
376
+
"result": {
377
+
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840",
378
+
"status": "working",
379
+
"createdAt": "2025-11-25T10:30:00Z",
380
+
"lastUpdatedAt": "2025-11-25T10:50:00Z",
381
+
"ttl": 30000,
382
+
"pollInterval": 5000
383
+
}
384
+
}
385
+
```
386
+
387
+
Eventually, the server completes the request, so it stores the final `CallToolResult` and moves the task into the `"completed"` status. On the next `tasks/continue` request, the server sends the final tool result inlined into the task object:
388
+
389
+
```json
390
+
{
391
+
"jsonrpc": "2.0",
392
+
"id": 6,
393
+
"method": "tasks/continue",
394
+
"params": {
395
+
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840"
396
+
}
397
+
}
398
+
```
399
+
400
+
```json
401
+
{
402
+
"jsonrpc": "2.0",
403
+
"id": 6,
404
+
"result": {
405
+
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840",
406
+
"status": "completed",
407
+
"createdAt": "2025-11-25T10:30:00Z",
408
+
"lastUpdatedAt": "2025-11-25T10:50:00Z",
409
+
"ttl": 30000,
410
+
"pollInterval": 5000,
411
+
"result": {
412
+
"content": [
413
+
{
414
+
"type": "text",
415
+
"text": "Hello, Luca!"
416
+
}
417
+
],
418
+
"isError": false
419
+
}
420
+
}
421
+
}
422
+
```
423
+
424
+
</details>
425
+
239
426
### Keeping `tasks/get`
240
427
241
428
If we're introducing a new method that encapsulates the entire task-polling lifecycle, **why should we keep `tasks/get`**?
0 commit comments