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
+186Lines changed: 186 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -236,6 +236,192 @@ 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
+
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.
243
+
244
+
To invoke this tool, the client makes a `CallToolRequest` as follows:
245
+
246
+
```json
247
+
{
248
+
"jsonrpc": "2.0",
249
+
"id": 2,
250
+
"method": "tools/call",
251
+
"params": {
252
+
"name": "hello_world",
253
+
"arguments": {}
254
+
},
255
+
"task": {
256
+
"ttl": 30000
257
+
}
258
+
}
259
+
```
260
+
261
+
The server recognizes this as a task-augmented request and immediately returns a `CreateTaskResult`:
262
+
263
+
```json
264
+
{
265
+
"jsonrpc": "2.0",
266
+
"id": 2,
267
+
"result": {
268
+
"task": {
269
+
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840",
270
+
"status": "working",
271
+
"createdAt": "2025-11-25T10:30:00Z",
272
+
"lastUpdatedAt": "2025-11-25T10:50:00Z",
273
+
"ttl": 30000,
274
+
"pollInterval": 5000
275
+
}
276
+
}
277
+
}
278
+
```
279
+
280
+
Once the client receives the `CreateTaskResult`, it begins polling `tasks/continue`:
281
+
282
+
```json
283
+
{
284
+
"jsonrpc": "2.0",
285
+
"id": 3,
286
+
"method": "tasks/continue",
287
+
"params": {
288
+
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840"
289
+
}
290
+
}
291
+
```
292
+
293
+
On each request while the task is in a `"working"` status, the server returns a regular task response:
294
+
295
+
```json
296
+
{
297
+
"jsonrpc": "2.0",
298
+
"id": 3,
299
+
"result": {
300
+
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840",
301
+
"status": "working",
302
+
"createdAt": "2025-11-25T10:30:00Z",
303
+
"lastUpdatedAt": "2025-11-25T10:50:00Z",
304
+
"ttl": 30000,
305
+
"pollInterval": 5000
306
+
}
307
+
}
308
+
```
309
+
310
+
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`).
311
+
312
+
```json
313
+
{
314
+
"jsonrpc": "2.0",
315
+
"id": 4,
316
+
"method": "tasks/continue",
317
+
"params": {
318
+
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840"
319
+
}
320
+
}
321
+
```
322
+
323
+
```json
324
+
{
325
+
"id": 4,
326
+
"jsonrpc": "2.0",
327
+
"result": {
328
+
"inputRequests": {
329
+
"name": {
330
+
"method": "elicitation/create",
331
+
"params": {
332
+
"mode": "form",
333
+
"message": "Please enter your name.",
334
+
"requestedSchema": {
335
+
"type": "object",
336
+
"properties": {
337
+
"name": { "type": "string" }
338
+
},
339
+
"required": ["name"]
340
+
}
341
+
}
342
+
}
343
+
}
344
+
}
345
+
}
346
+
```
347
+
348
+
The user enters their name, and the client repeats the `tasks/continue` request with the satisfied information:
349
+
350
+
```json
351
+
{
352
+
"jsonrpc": "2.0",
353
+
"id": 5,
354
+
"method": "tasks/continue",
355
+
"params": {
356
+
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840",
357
+
"inputResponses": {
358
+
"name": {
359
+
"action": "accept",
360
+
"content": {
361
+
"input": "Luca"
362
+
}
363
+
}
364
+
}
365
+
}
366
+
}
367
+
```
368
+
369
+
With the elicitation fulfilled and no other outstanding requests to send, the server moves the task back into the `"working"` status:
370
+
371
+
```json
372
+
{
373
+
"jsonrpc": "2.0",
374
+
"id": 5,
375
+
"result": {
376
+
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840",
377
+
"status": "working",
378
+
"createdAt": "2025-11-25T10:30:00Z",
379
+
"lastUpdatedAt": "2025-11-25T10:50:00Z",
380
+
"ttl": 30000,
381
+
"pollInterval": 5000
382
+
}
383
+
}
384
+
```
385
+
386
+
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:
387
+
388
+
```json
389
+
{
390
+
"jsonrpc": "2.0",
391
+
"id": 6,
392
+
"method": "tasks/continue",
393
+
"params": {
394
+
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840"
395
+
}
396
+
}
397
+
```
398
+
399
+
```json
400
+
{
401
+
"jsonrpc": "2.0",
402
+
"id": 6,
403
+
"result": {
404
+
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840",
405
+
"status": "completed",
406
+
"createdAt": "2025-11-25T10:30:00Z",
407
+
"lastUpdatedAt": "2025-11-25T10:50:00Z",
408
+
"ttl": 30000,
409
+
"pollInterval": 5000,
410
+
"result": {
411
+
"content": [
412
+
{
413
+
"type": "text",
414
+
"text": "Hello, Luca!"
415
+
}
416
+
],
417
+
"isError": false
418
+
}
419
+
}
420
+
}
421
+
```
422
+
423
+
</details>
424
+
239
425
### Keeping `tasks/get`
240
426
241
427
If we're introducing a new method that encapsulates the entire task-polling lifecycle, **why should we keep `tasks/get`**?
0 commit comments