Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions schema/2026-07-28.json
Original file line number Diff line number Diff line change
Expand Up @@ -3220,6 +3220,9 @@
{
"$ref": "#/$defs/ReadResourceResult"
},
{
"$ref": "#/$defs/SubscriptionsListenResult"
},
{
"$ref": "#/$defs/ListPromptsResult"
},
Expand Down Expand Up @@ -3389,6 +3392,36 @@
],
"type": "object"
},
"SubscriptionsListenResult": {
"description": "The response to a {@link SubscriptionsListenRequestsubscriptions/listen}\nrequest, signalling that the subscription has ended gracefully (for example,\nduring server shutdown). Because the listen stream is long-lived, this result\nis sent only when the server tears the subscription down; an abrupt transport\nclose carries no response. The result body is otherwise empty.",
"properties": {
"_meta": {
"$ref": "#/$defs/SubscriptionsListenResultMeta"
},
"resultType": {
"description": "Indicates the type of the result, which allows the client to determine\nhow to parse the result object.\n\nServers implementing this protocol version MUST include this field.\nFor backward compatibility, when a client receives a result from a\nserver implementing an earlier protocol version (which does not include\n`resultType`), the client MUST treat the absent field as `\"complete\"`.",
"type": "string"
}
},
"required": [
"_meta",
"resultType"
],
"type": "object"
},
"SubscriptionsListenResultMeta": {
"description": "Extends {@link MetaObject} with the subscription-stream identifier carried by a\n{@link SubscriptionsListenResult}. All key naming rules from `MetaObject` apply.",
"properties": {
"io.modelcontextprotocol/subscriptionId": {
"$ref": "#/$defs/RequestId",
"description": "Identifies the subscription stream this response closes, so the client can\ncorrelate it with the originating subscription — mirroring the same key on\nthe stream's notifications. The value is the JSON-RPC ID of the\n`subscriptions/listen` request that opened the stream (and equals this\nresponse's `id`)."
}
},
"required": [
"io.modelcontextprotocol/subscriptionId"
],
"type": "object"
},
"TextContent": {
"description": "Text provided to or from an LLM.",
"properties": {
Expand Down
4 changes: 2 additions & 2 deletions schema/PINNED.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
{
"protocol_version": "2026-07-28",
"source_path_in_spec_repo": "schema/draft/schema.json",
"spec_commit": "2852f30e26ca5fb779565741ec042094cb110abd",
"sha256": "ed1ad4ba94aaeb2068b78969ef901b1150f7b2f06cf86472b3032abee1380b6a"
"spec_commit": "ead35b59b4fda8b32e276810025d8f92bdcec1b6",
"sha256": "e00f675287e8cf078688c26c8a89d283ff2613da3b76d5cd15aff9d189df639c"
}
]
10 changes: 9 additions & 1 deletion scripts/gen_surface_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,15 @@
OPEN_CLASSES: dict[str, frozenset[str]] = {
"2025-11-25": frozenset({"Meta", "InputSchema", "OutputSchema", "Result", "GetTaskPayloadResult", "Data"}),
"2026-07-28": frozenset(
{"MetaObject", "NotificationMetaObject", "RequestMetaObject", "InputSchema", "OutputSchema", "Result"}
{
"MetaObject",
"NotificationMetaObject",
"RequestMetaObject",
"SubscriptionsListenResultMeta",
"InputSchema",
"OutputSchema",
"Result",
}
),
}

Expand Down
2 changes: 2 additions & 0 deletions src/mcp-types/mcp_types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
SubscriptionsAcknowledgedNotificationParams,
SubscriptionsListenRequest,
SubscriptionsListenRequestParams,
SubscriptionsListenResult,
Task,
TaskMetadata,
TasksCallCapability,
Expand Down Expand Up @@ -385,6 +386,7 @@
"ListTasksResult",
"ListToolsResult",
"ReadResourceResult",
"SubscriptionsListenResult",
# Error data payloads
"MissingRequiredClientCapabilityErrorData",
"UnsupportedProtocolVersionErrorData",
Expand Down
16 changes: 16 additions & 0 deletions src/mcp-types/mcp_types/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1063,6 +1063,21 @@ class SubscriptionsAcknowledgedNotification(
params: SubscriptionsAcknowledgedNotificationParams


class SubscriptionsListenResult(Result):
"""Signals that a `subscriptions/listen` stream has ended gracefully (2026-07-28).

Because the listen stream is long-lived, this result is sent only when the
server tears the subscription down (for example during shutdown); an abrupt
transport close carries no response. The body is otherwise empty: the
`_meta["io.modelcontextprotocol/subscriptionId"]` key is required on the
wire and equals the JSON-RPC id of the originating `subscriptions/listen`
request.
"""

result_type: ResultType = "complete"
"""See `ResultType`. Always serialized; older peers ignore it."""


class ListPromptsRequest(PaginatedRequest[Literal["prompts/list"]]):
"""Sent from the client to request a list of prompts and prompt templates the server has."""

Expand Down Expand Up @@ -2156,6 +2171,7 @@ def _require_one_field(self) -> Self:
| ReadResourceResult
| CallToolResult
| ListToolsResult
| SubscriptionsListenResult
| InputRequiredResult
)
"""Union of every result payload a server can return for a client request.
Expand Down
4 changes: 2 additions & 2 deletions src/mcp-types/mcp_types/methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@
("resources/read", "2026-07-28"): v2026.AnyReadResourceResult,
("resources/templates/list", "2026-07-28"): v2026.ListResourceTemplatesResult,
("server/discover", "2026-07-28"): v2026.DiscoverResult,
("subscriptions/listen", "2026-07-28"): v2026.EmptyResult,
("subscriptions/listen", "2026-07-28"): v2026.SubscriptionsListenResult,
("tools/call", "2026-07-28"): v2026.AnyCallToolResult,
("tools/list", "2026-07-28"): v2026.ListToolsResult,
}
Expand Down Expand Up @@ -396,7 +396,7 @@
# smart-union ties resolve leftmost. Pinned by tests/types/test_methods.py.
"sampling/createMessage": types.CreateMessageResult | types.CreateMessageResultWithTools,
"server/discover": types.DiscoverResult,
"subscriptions/listen": types.EmptyResult,
"subscriptions/listen": types.SubscriptionsListenResult,
"tools/call": types.CallToolResult | types.InputRequiredResult,
"tools/list": types.ListToolsResult,
}
Expand Down
48 changes: 47 additions & 1 deletion src/mcp-types/mcp_types/v2026_07_28/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Internal wire-shape models for protocol 2026-07-28. Generated; do not edit.

Regenerate with `scripts/gen_surface_types.py` from `schema/2026-07-28.json`
(sha256 `ed1ad4ba94aaeb2068b78969ef901b1150f7b2f06cf86472b3032abee1380b6a`)."""
(sha256 `e00f675287e8cf078688c26c8a89d283ff2613da3b76d5cd15aff9d189df639c`)."""
# pyright: reportIncompatibleVariableOverride=false, reportGeneralTypeIssues=false

from __future__ import annotations
Expand Down Expand Up @@ -909,6 +909,25 @@ class SubscriptionFilter(WireModel):
"""


class SubscriptionsListenResultMeta(WireModel):
"""
Extends {@link MetaObject} with the subscription-stream identifier carried by a
{@link SubscriptionsListenResult}. All key naming rules from `MetaObject` apply.
"""

model_config = ConfigDict(
extra="allow",
)
io_modelcontextprotocol_subscription_id: Annotated[RequestId, Field(alias="io.modelcontextprotocol/subscriptionId")]
"""
Identifies the subscription stream this response closes, so the client can
correlate it with the originating subscription — mirroring the same key on
the stream's notifications. The value is the JSON-RPC ID of the
`subscriptions/listen` request that opened the stream (and equals this
response's `id`).
"""


class TextResourceContents(WireModel):
model_config = ConfigDict(
extra="ignore",
Expand Down Expand Up @@ -2056,6 +2075,31 @@ class SubscriptionsAcknowledgedNotificationParams(WireModel):
"""


class SubscriptionsListenResult(WireModel):
"""
The response to a {@link SubscriptionsListenRequestsubscriptions/listen}
request, signalling that the subscription has ended gracefully (for example,
during server shutdown). Because the listen stream is long-lived, this result
is sent only when the server tears the subscription down; an abrupt transport
close carries no response. The result body is otherwise empty.
"""

model_config = ConfigDict(
extra="ignore",
)
meta: Annotated[SubscriptionsListenResultMeta, Field(alias="_meta")]
result_type: Annotated[str, Field(alias="resultType")]
"""
Indicates the type of the result, which allows the client to determine
how to parse the result object.

Servers implementing this protocol version MUST include this field.
For backward compatibility, when a client receives a result from a
server implementing an earlier protocol version (which does not include
`resultType`), the client MUST treat the absent field as `"complete"`.
"""


class TextContent(WireModel):
"""
Text provided to or from an LLM.
Expand Down Expand Up @@ -3544,6 +3588,7 @@ class ServerResult(
| ListResourcesResult
| ListResourceTemplatesResult
| ReadResourceResult
| SubscriptionsListenResult
| ListPromptsResult
| GetPromptResult
| ListToolsResult
Expand All @@ -3558,6 +3603,7 @@ class ServerResult(
| ListResourcesResult
| ListResourceTemplatesResult
| ReadResourceResult
| SubscriptionsListenResult
| ListPromptsResult
| GetPromptResult
| ListToolsResult
Expand Down
6 changes: 3 additions & 3 deletions src/mcp/server/lowlevel/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def __init__(
| None = None,
on_subscriptions_listen: Callable[
[ServerRequestContext[LifespanResultT], types.SubscriptionsListenRequestParams],
Awaitable[types.EmptyResult],
Awaitable[types.SubscriptionsListenResult],
]
| None = None,
on_list_prompts: Callable[
Expand Down Expand Up @@ -264,7 +264,7 @@ def __init__(
| None = None,
on_subscriptions_listen: Callable[
[ServerRequestContext[LifespanResultT], types.SubscriptionsListenRequestParams],
Awaitable[types.EmptyResult],
Awaitable[types.SubscriptionsListenResult],
]
| None = None,
on_list_prompts: Callable[
Expand Down Expand Up @@ -355,7 +355,7 @@ def __init__(
| None = None,
on_subscriptions_listen: Callable[
[ServerRequestContext[LifespanResultT], types.SubscriptionsListenRequestParams],
Awaitable[types.EmptyResult],
Awaitable[types.SubscriptionsListenResult],
]
| None = None,
on_list_prompts: Callable[
Expand Down
15 changes: 9 additions & 6 deletions tests/types/test_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@
("resources/read", "2026-07-28"): (v2026.ReadResourceResult, v2026.InputRequiredResult),
("resources/templates/list", "2026-07-28"): v2026.ListResourceTemplatesResult,
("server/discover", "2026-07-28"): v2026.DiscoverResult,
("subscriptions/listen", "2026-07-28"): v2026.EmptyResult,
("subscriptions/listen", "2026-07-28"): v2026.SubscriptionsListenResult,
("tools/call", "2026-07-28"): (v2026.CallToolResult, v2026.InputRequiredResult),
("tools/list", "2026-07-28"): v2026.ListToolsResult,
}
Expand All @@ -290,9 +290,7 @@
("sampling/createMessage", "2025-11-25"): v2025.CreateMessageResult,
}

EMPTY_SERVER_RESPONSE_METHODS = frozenset(
{"logging/setLevel", "ping", "resources/subscribe", "resources/unsubscribe", "subscriptions/listen"}
)
EMPTY_SERVER_RESPONSE_METHODS = frozenset({"logging/setLevel", "ping", "resources/subscribe", "resources/unsubscribe"})
EMPTY_CLIENT_RESPONSE_METHODS = frozenset({"ping"})

# Pre-2026 versions share the 2025-11-25 surface package.
Expand Down Expand Up @@ -404,7 +402,10 @@
"ttlMs": 0,
"cacheScope": "private",
},
v2026.EmptyResult: {"resultType": "complete"},
v2026.SubscriptionsListenResult: {
"resultType": "complete",
"_meta": {"io.modelcontextprotocol/subscriptionId": 1},
},
v2026.ListPromptsResult: {"prompts": [], "resultType": "complete", "ttlMs": 0, "cacheScope": "private"},
v2026.ListResourcesResult: {"resources": [], "resultType": "complete", "ttlMs": 0, "cacheScope": "private"},
v2026.ListResourceTemplatesResult: {
Expand Down Expand Up @@ -844,7 +845,9 @@ def test_validate_functions_accept_reject_and_gate_like_their_parse_siblings():
ttl_ms=0,
cache_scope="private",
),
"subscriptions/listen": types.EmptyResult(result_type="complete"),
"subscriptions/listen": types.SubscriptionsListenResult.model_validate(
{"_meta": {"io.modelcontextprotocol/subscriptionId": 1}}
),
"tools/call": types.CallToolResult(content=[]),
"tools/list": types.ListToolsResult(tools=[], ttl_ms=0, cache_scope="private"),
}
Expand Down
1 change: 1 addition & 0 deletions tests/types/test_parity.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@
"v2026_07_28.RequestedSchema",
"v2026_07_28.ResourceRequestParams",
"v2026_07_28.StringSchema",
"v2026_07_28.SubscriptionsListenResultMeta",
"v2026_07_28.TitledMultiSelectEnumSchema",
"v2026_07_28.TitledSingleSelectEnumSchema",
"v2026_07_28.UnsupportedProtocolVersionError",
Expand Down
Loading