|
48 | 48 |
|
49 | 49 | _SOURCE_PATTERN = re.compile(r"https://modelcontextprotocol\.io/specification/.+|sdk|issue:#\d+") |
50 | 50 |
|
| 51 | +_TASKS_DEFERRAL = ( |
| 52 | + "Tasks have been removed from the draft spec and from this SDK; they are expected to return " |
| 53 | + "as a separate MCP extension. These 2025-11-25 requirements are tracked but intentionally " |
| 54 | + "unimplemented." |
| 55 | +) |
| 56 | + |
51 | 57 |
|
52 | 58 | @dataclass(frozen=True, kw_only=True) |
53 | 59 | class Divergence: |
@@ -357,6 +363,11 @@ def __post_init__(self) -> None: |
357 | 363 | source=f"{SPEC_BASE_URL}/basic#responses", |
358 | 364 | behavior="A request whose method has no registered handler is answered with a METHOD_NOT_FOUND error.", |
359 | 365 | ), |
| 366 | + "protocol:meta:related-task": Requirement( |
| 367 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#related-task-metadata", |
| 368 | + behavior="Messages may carry related-task _meta associating them with a task.", |
| 369 | + deferred=_TASKS_DEFERRAL, |
| 370 | + ), |
360 | 371 | "meta:request-to-handler": Requirement( |
361 | 372 | source=f"{SPEC_BASE_URL}/basic#_meta", |
362 | 373 | behavior="The _meta object the client attaches to a request is visible to the server handler.", |
@@ -1536,6 +1547,190 @@ def __post_init__(self) -> None: |
1536 | 1547 | ), |
1537 | 1548 | ), |
1538 | 1549 | # ═══════════════════════════════════════════════════════════════════════════ |
| 1550 | + # Tasks (experimental) |
| 1551 | + # ═══════════════════════════════════════════════════════════════════════════ |
| 1552 | + "tasks:auth:context-isolation": Requirement( |
| 1553 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#task-isolation-and-access-control", |
| 1554 | + behavior=( |
| 1555 | + "When an authorization context is available, task operations are scoped to the context that " |
| 1556 | + "created the task: other contexts cannot get it, retrieve its result, cancel it, or see it in " |
| 1557 | + "tasks/list." |
| 1558 | + ), |
| 1559 | + transports=("streamable-http",), |
| 1560 | + deferred=_TASKS_DEFERRAL, |
| 1561 | + ), |
| 1562 | + "tasks:bidirectional": Requirement( |
| 1563 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#definitions", |
| 1564 | + behavior="Task APIs are bidirectional: the server may create, get, list, and cancel tasks on the client.", |
| 1565 | + deferred=_TASKS_DEFERRAL, |
| 1566 | + ), |
| 1567 | + "tasks:cancel:no-handler-abort": Requirement( |
| 1568 | + source="sdk", |
| 1569 | + behavior=( |
| 1570 | + "tasks/cancel marks the task cancelled without aborting the originating request handler " |
| 1571 | + "(the spec says receivers SHOULD attempt to stop execution)." |
| 1572 | + ), |
| 1573 | + deferred=_TASKS_DEFERRAL, |
| 1574 | + ), |
| 1575 | + "tasks:cancel:remains-cancelled": Requirement( |
| 1576 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#task-cancellation", |
| 1577 | + behavior=( |
| 1578 | + "After tasks/cancel, the task remains cancelled even if the underlying handler subsequently " |
| 1579 | + "completes or fails." |
| 1580 | + ), |
| 1581 | + deferred=_TASKS_DEFERRAL, |
| 1582 | + ), |
| 1583 | + "tasks:cancel:terminal-rejected": Requirement( |
| 1584 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#task-cancellation", |
| 1585 | + behavior="tasks/cancel on a task already in a terminal state returns Invalid params (-32602).", |
| 1586 | + deferred=_TASKS_DEFERRAL, |
| 1587 | + ), |
| 1588 | + "tasks:cancel:working": Requirement( |
| 1589 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#task-cancellation", |
| 1590 | + behavior="tasks/cancel on a working task transitions it to cancelled and returns the updated task.", |
| 1591 | + deferred=_TASKS_DEFERRAL, |
| 1592 | + ), |
| 1593 | + "tasks:create:ttl-honored": Requirement( |
| 1594 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#ttl-and-resource-management", |
| 1595 | + behavior=( |
| 1596 | + "tasks/get responses include the actual ttl applied by the receiver (or null for unlimited); " |
| 1597 | + "the create-task result carries the same value." |
| 1598 | + ), |
| 1599 | + deferred=_TASKS_DEFERRAL, |
| 1600 | + ), |
| 1601 | + "tasks:create:via-tool-call": Requirement( |
| 1602 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#creating-tasks", |
| 1603 | + behavior="A task-augmented tools/call returns a create-task result instead of the tool result.", |
| 1604 | + deferred=_TASKS_DEFERRAL, |
| 1605 | + ), |
| 1606 | + "tasks:get": Requirement( |
| 1607 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#getting-tasks", |
| 1608 | + behavior="tasks/get returns the task's current status, ttl, timestamps, and status message.", |
| 1609 | + deferred=_TASKS_DEFERRAL, |
| 1610 | + ), |
| 1611 | + "tasks:lifecycle:initial-working": Requirement( |
| 1612 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#task-status-lifecycle", |
| 1613 | + behavior="A newly created task has status 'working'.", |
| 1614 | + deferred=_TASKS_DEFERRAL, |
| 1615 | + ), |
| 1616 | + "tasks:lifecycle:input-required": Requirement( |
| 1617 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#input-required-status", |
| 1618 | + behavior=( |
| 1619 | + "While a task awaits a side-channel client response its status is input_required; once the " |
| 1620 | + "response arrives the task leaves input_required (typically returning to working)." |
| 1621 | + ), |
| 1622 | + deferred=_TASKS_DEFERRAL, |
| 1623 | + ), |
| 1624 | + "tasks:list:invalid-cursor": Requirement( |
| 1625 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#protocol-errors", |
| 1626 | + behavior="tasks/list with an invalid cursor returns Invalid params (-32602).", |
| 1627 | + deferred=_TASKS_DEFERRAL, |
| 1628 | + ), |
| 1629 | + "tasks:list:pagination": Requirement( |
| 1630 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#listing-tasks", |
| 1631 | + behavior="tasks/list returns created tasks and supports cursor pagination.", |
| 1632 | + deferred=_TASKS_DEFERRAL, |
| 1633 | + ), |
| 1634 | + "tasks:no-capability:ignore-task-param": Requirement( |
| 1635 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#task-support-and-handling", |
| 1636 | + behavior=( |
| 1637 | + "A receiver that did not declare task capability for a request type processes the request " |
| 1638 | + "normally and returns the ordinary result, ignoring the task augmentation." |
| 1639 | + ), |
| 1640 | + deferred=_TASKS_DEFERRAL, |
| 1641 | + ), |
| 1642 | + "tasks:progress:after-create": Requirement( |
| 1643 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#task-progress-notifications", |
| 1644 | + behavior=( |
| 1645 | + "After the create-task result, progress notifications keyed to the original progress token " |
| 1646 | + "continue to reach the caller until the task is terminal." |
| 1647 | + ), |
| 1648 | + deferred=_TASKS_DEFERRAL, |
| 1649 | + ), |
| 1650 | + "tasks:request-cancel:no-task-cancel": Requirement( |
| 1651 | + source="sdk", |
| 1652 | + behavior="A cancellation notification for the originating request does not auto-cancel the created task.", |
| 1653 | + deferred=_TASKS_DEFERRAL, |
| 1654 | + ), |
| 1655 | + "tasks:result:failed": Requirement( |
| 1656 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#task-execution-errors", |
| 1657 | + behavior="tasks/result for a failed task returns the failure result (isError true).", |
| 1658 | + deferred=_TASKS_DEFERRAL, |
| 1659 | + ), |
| 1660 | + "tasks:result:related-task-meta": Requirement( |
| 1661 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#related-task-metadata", |
| 1662 | + behavior="The tasks/result response carries related-task _meta naming the requested task.", |
| 1663 | + deferred=_TASKS_DEFERRAL, |
| 1664 | + ), |
| 1665 | + "tasks:result:terminal": Requirement( |
| 1666 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#result-retrieval", |
| 1667 | + behavior="tasks/result for a completed task returns the stored result of the original request type.", |
| 1668 | + deferred=_TASKS_DEFERRAL, |
| 1669 | + ), |
| 1670 | + "tasks:side-channel:drain-fifo": Requirement( |
| 1671 | + source="sdk", |
| 1672 | + behavior="tasks/result drains queued related-task messages in FIFO order before returning the final result.", |
| 1673 | + deferred=_TASKS_DEFERRAL, |
| 1674 | + ), |
| 1675 | + "tasks:side-channel:drop-on-cancel": Requirement( |
| 1676 | + source="sdk", |
| 1677 | + behavior="When a task is cancelled before tasks/result, queued related-task messages are dropped.", |
| 1678 | + deferred=_TASKS_DEFERRAL, |
| 1679 | + ), |
| 1680 | + "tasks:side-channel:elicitation": Requirement( |
| 1681 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#input-required-status", |
| 1682 | + behavior=( |
| 1683 | + "An elicitation issued mid-task is delivered through the tasks/result side-channel, and the " |
| 1684 | + "client's response routes back to the handler." |
| 1685 | + ), |
| 1686 | + deferred=_TASKS_DEFERRAL, |
| 1687 | + ), |
| 1688 | + "tasks:side-channel:queue": Requirement( |
| 1689 | + source="sdk", |
| 1690 | + behavior=( |
| 1691 | + "Server-to-client requests with related-task metadata sent while no tasks/result is open are queued." |
| 1692 | + ), |
| 1693 | + deferred=_TASKS_DEFERRAL, |
| 1694 | + ), |
| 1695 | + "tasks:side-channel:sampling": Requirement( |
| 1696 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#input-required-status", |
| 1697 | + behavior=( |
| 1698 | + "A sampling request issued mid-task is delivered through the tasks/result side-channel, and " |
| 1699 | + "the client's response routes back to the task." |
| 1700 | + ), |
| 1701 | + deferred=_TASKS_DEFERRAL, |
| 1702 | + ), |
| 1703 | + "tasks:side-channel:stream": Requirement( |
| 1704 | + source="sdk", |
| 1705 | + behavior=( |
| 1706 | + "Calling tasks/result while the task is working streams related-task messages as they are " |
| 1707 | + "produced, then returns the result." |
| 1708 | + ), |
| 1709 | + deferred=_TASKS_DEFERRAL, |
| 1710 | + ), |
| 1711 | + "tasks:status-notification": Requirement( |
| 1712 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#task-status-notification", |
| 1713 | + behavior="Task status notifications deliver status updates carrying the full task fields.", |
| 1714 | + deferred=_TASKS_DEFERRAL, |
| 1715 | + ), |
| 1716 | + "tasks:tool-level:forbidden-with-task-32601": Requirement( |
| 1717 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#tool-level-negotiation", |
| 1718 | + behavior=( |
| 1719 | + "A task-augmented tools/call on a tool that does not support tasks returns Method not found (-32601)." |
| 1720 | + ), |
| 1721 | + deferred=_TASKS_DEFERRAL, |
| 1722 | + ), |
| 1723 | + "tasks:tool-level:required-no-task-32601": Requirement( |
| 1724 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#tool-level-negotiation", |
| 1725 | + behavior=("A plain tools/call on a tool that requires task augmentation returns Method not found (-32601)."), |
| 1726 | + deferred=_TASKS_DEFERRAL, |
| 1727 | + ), |
| 1728 | + "tasks:unknown-id": Requirement( |
| 1729 | + source=f"{SPEC_BASE_URL}/basic/utilities/tasks#protocol-errors", |
| 1730 | + behavior="tasks/get, tasks/result, and tasks/cancel for an unknown task id return Invalid params (-32602).", |
| 1731 | + deferred=_TASKS_DEFERRAL, |
| 1732 | + ), |
| 1733 | + # ═══════════════════════════════════════════════════════════════════════════ |
1539 | 1734 | # Transports (in-suite coverage) |
1540 | 1735 | # ═══════════════════════════════════════════════════════════════════════════ |
1541 | 1736 | "transport:streamable-http:stateful": Requirement( |
|
0 commit comments