From 7f7261852cf4a51b48fccda520bcc55b98857bb6 Mon Sep 17 00:00:00 2001 From: Max Isbey <224885523+maxisbey@users.noreply.github.com> Date: Fri, 19 Jun 2026 14:37:02 +0000 Subject: [PATCH 1/2] Re-vendor 2026-07-28 schema and absorb spec #2907 error-code renumber Bumps the draft schema pin from spec 6d441518 to 2852f30e, picking up spec PRs #2889/#2890/#2891/#2907, and updates the hand-written code that referenced the old shapes: - mcp.types.jsonrpc: HEADER_MISMATCH=-32020 added, MISSING_REQUIRED_CLIENT_CAPABILITY -32003->-32021, UNSUPPORTED_PROTOCOL_VERSION -32004->-32022, allocation-policy comment rewritten per #2907. - mcp.types.methods: drop the 2026-07-28 rows for notifications/elicitation/complete (spec #2891) and notifications/progress (spec #2889). - gen_surface_types.py: NotificationMetaObject is an open _meta carrier. - expected-failures comments: -32001 HeaderMismatch -> -32020. SUPPORTED_PROTOCOL_VERSIONS / LATEST_PROTOCOL_VERSION are unchanged; the renumbered constants are not used on any 2025-era code path. --- .../expected-failures.2026-07-28.yml | 2 +- .../actions/conformance/expected-failures.yml | 2 +- schema/2026-07-28.json | 167 ++--- schema/PINNED.json | 4 +- scripts/gen_surface_types.py | 4 +- src/mcp/types/__init__.py | 2 + src/mcp/types/_types.py | 4 +- src/mcp/types/jsonrpc.py | 23 +- src/mcp/types/methods.py | 6 +- src/mcp/types/v2026_07_28/__init__.py | 592 ++++++++++-------- tests/server/test_runner.py | 8 +- tests/types/test_methods.py | 4 +- tests/types/test_parity.py | 7 +- 13 files changed, 469 insertions(+), 356 deletions(-) diff --git a/.github/actions/conformance/expected-failures.2026-07-28.yml b/.github/actions/conformance/expected-failures.2026-07-28.yml index 76793dd635..c155d4afe8 100644 --- a/.github/actions/conformance/expected-failures.2026-07-28.yml +++ b/.github/actions/conformance/expected-failures.2026-07-28.yml @@ -132,7 +132,7 @@ server: - input-required-result-capability-check # SEP-2549 (caching): no ttlMs/cacheScope support. - caching - # SEP-2243 (HTTP header standardization): -32001 HeaderMismatch handling and + # SEP-2243 (HTTP header standardization): -32020 HeaderMismatch handling and # case-insensitive/whitespace-trimmed header validation not implemented. - http-header-validation - http-custom-header-server-validation diff --git a/.github/actions/conformance/expected-failures.yml b/.github/actions/conformance/expected-failures.yml index 78ef609c50..674fa78eeb 100644 --- a/.github/actions/conformance/expected-failures.yml +++ b/.github/actions/conformance/expected-failures.yml @@ -68,7 +68,7 @@ server: # SEP-2549 (caching): no ttlMs/cacheScope support; scenario also hits the # stateful-mode "Missing session ID" error. - caching - # SEP-2243 (HTTP header standardization): -32001 HeaderMismatch handling and + # SEP-2243 (HTTP header standardization): -32020 HeaderMismatch handling and # case-insensitive/whitespace-trimmed header validation not implemented. - http-header-validation - http-custom-header-server-validation diff --git a/schema/2026-07-28.json b/schema/2026-07-28.json index 2bfbcfa233..7025ee7d48 100644 --- a/schema/2026-07-28.json +++ b/schema/2026-07-28.json @@ -126,7 +126,7 @@ "$ref": "#/$defs/MetaObject" }, "cacheScope": { - "description": "Indicates the intended scope of the cached response, analogous to HTTP\n`Cache-Control: public` vs `Cache-Control: private`.\n\n- `\"public\"`: Any client or intermediary (e.g., shared gateway, proxy)\n MAY cache the response and serve it to any user.\n- `\"private\"`: Only the requesting user's client MAY cache the response.\n Shared caches (e.g., multi-tenant gateways) MUST NOT serve a cached\n copy to a different user.", + "description": "Indicates the intended scope of the cached response, analogous to HTTP\n`Cache-Control: public` vs `Cache-Control: private`.\n\n- `\"public\"`: The response does not contain user-specific data. Any\n client or intermediary (e.g., shared gateway, caching proxy) MAY cache\n the response and serve it across authorization contexts.\n- `\"private\"`: The response MAY be cached and reused only within the\n same authorization context. Caches MUST NOT be shared across\n authorization contexts (e.g., a different access token requires a\n different cache).", "enum": [ "private", "public" @@ -264,7 +264,7 @@ "type": "object" }, "CancelledNotification": { - "description": "This notification can be sent by either side to indicate that it is cancelling a previously-issued request.\n\nThe request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished.\n\nThis notification indicates that the result will be unused, so any associated processing SHOULD cease.", + "description": "This notification is sent by the client to indicate that it is cancelling a request it previously issued.\n\nOn stdio, the server also sends this notification, solely to terminate a {@link SubscriptionsListenRequestsubscriptions/listen} stream: it references the ID of the `subscriptions/listen` request that opened the stream. Servers MUST NOT use this notification to cancel any other request.\n\nThe request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished.\n\nThis notification indicates that the result will be unused, so any associated processing SHOULD cease.", "properties": { "jsonrpc": { "const": "2.0", @@ -289,7 +289,7 @@ "description": "Parameters for a `notifications/cancelled` notification.", "properties": { "_meta": { - "$ref": "#/$defs/MetaObject" + "$ref": "#/$defs/NotificationMetaObject" }, "reason": { "description": "An optional string describing the reason for the cancellation. This MAY be logged or presented to the user.", @@ -297,9 +297,12 @@ }, "requestId": { "$ref": "#/$defs/RequestId", - "description": "The ID of the request to cancel.\n\nThis MUST correspond to the ID of a request previously issued in the same direction." + "description": "The ID of the request to cancel.\n\nThis MUST correspond to the ID of a request the client previously issued." } }, + "required": [ + "requestId" + ], "type": "object" }, "ClientCapabilities": { @@ -328,7 +331,7 @@ "additionalProperties": { "$ref": "#/$defs/JSONObject" }, - "description": "Optional MCP extensions that the client supports. Keys are extension identifiers\n(e.g., \"io.modelcontextprotocol/oauth-client-credentials\"), and values are\nper-extension settings objects. An empty object indicates support with no settings.", + "description": "Optional MCP extensions that the client supports. Keys are extension identifiers\n(e.g., \"io.modelcontextprotocol/oauth-client-credentials\"), and values are\nper-extension settings objects. An empty object indicates support with no settings.\n\nKeys MUST follow the {@link MetaObject`_meta` key naming rules}, with a\nmandatory prefix.", "type": "object" }, "roots": { @@ -354,14 +357,26 @@ "type": "object" }, "ClientNotification": { - "anyOf": [ - { - "$ref": "#/$defs/CancelledNotification" + "description": "This notification is sent by the client to indicate that it is cancelling a request it previously issued.\n\nOn stdio, the server also sends this notification, solely to terminate a {@link SubscriptionsListenRequestsubscriptions/listen} stream: it references the ID of the `subscriptions/listen` request that opened the stream. Servers MUST NOT use this notification to cancel any other request.\n\nThe request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished.\n\nThis notification indicates that the result will be unused, so any associated processing SHOULD cease.", + "properties": { + "jsonrpc": { + "const": "2.0", + "type": "string" }, - { - "$ref": "#/$defs/ProgressNotification" + "method": { + "const": "notifications/cancelled", + "type": "string" + }, + "params": { + "$ref": "#/$defs/CancelledNotificationParams" } - ] + }, + "required": [ + "jsonrpc", + "method", + "params" + ], + "type": "object" }, "ClientRequest": { "anyOf": [ @@ -728,7 +743,7 @@ "$ref": "#/$defs/MetaObject" }, "cacheScope": { - "description": "Indicates the intended scope of the cached response, analogous to HTTP\n`Cache-Control: public` vs `Cache-Control: private`.\n\n- `\"public\"`: Any client or intermediary (e.g., shared gateway, proxy)\n MAY cache the response and serve it to any user.\n- `\"private\"`: Only the requesting user's client MAY cache the response.\n Shared caches (e.g., multi-tenant gateways) MUST NOT serve a cached\n copy to a different user.", + "description": "Indicates the intended scope of the cached response, analogous to HTTP\n`Cache-Control: public` vs `Cache-Control: private`.\n\n- `\"public\"`: The response does not contain user-specific data. Any\n client or intermediary (e.g., shared gateway, caching proxy) MAY cache\n the response and serve it across authorization contexts.\n- `\"private\"`: The response MAY be cached and reused only within the\n same authorization context. Caches MUST NOT be shared across\n authorization contexts (e.g., a different access token requires a\n different cache).", "enum": [ "private", "public" @@ -874,10 +889,6 @@ "ElicitRequestURLParams": { "description": "The parameters for a request to elicit information from the user via a URL in the client.", "properties": { - "elicitationId": { - "description": "The ID of the elicitation, which must be unique within the context of the server.\nThe client MUST treat this ID as an opaque value.", - "type": "string" - }, "message": { "description": "The message to present to the user explaining why the interaction is needed.", "type": "string" @@ -894,7 +905,6 @@ } }, "required": [ - "elicitationId", "message", "mode", "url" @@ -940,37 +950,6 @@ ], "type": "object" }, - "ElicitationCompleteNotification": { - "description": "An optional notification from the server to the client, informing it of a completion of a out-of-band elicitation request.", - "properties": { - "jsonrpc": { - "const": "2.0", - "type": "string" - }, - "method": { - "const": "notifications/elicitation/complete", - "type": "string" - }, - "params": { - "properties": { - "elicitationId": { - "description": "The ID of the elicitation that completed.", - "type": "string" - } - }, - "required": [ - "elicitationId" - ], - "type": "object" - } - }, - "required": [ - "jsonrpc", - "method", - "params" - ], - "type": "object" - }, "EmbeddedResource": { "description": "The contents of a resource, embedded into a prompt or tool call result.\n\nIt is up to the client how best to render embedded resources for the benefit\nof the LLM and/or the user.", "properties": { @@ -1156,6 +1135,42 @@ ], "type": "object" }, + "HeaderMismatchError": { + "description": "Returned when a server rejects a request because the values in the HTTP\nheaders do not match the corresponding values in the request body, or\nbecause required headers are missing or malformed. For HTTP, the response\nstatus code MUST be `400 Bad Request`.", + "properties": { + "error": { + "allOf": [ + { + "$ref": "#/$defs/Error" + }, + { + "properties": { + "code": { + "const": -32020, + "type": "integer" + } + }, + "required": [ + "code" + ], + "type": "object" + } + ] + }, + "id": { + "$ref": "#/$defs/RequestId" + }, + "jsonrpc": { + "const": "2.0", + "type": "string" + } + }, + "required": [ + "error", + "jsonrpc" + ], + "type": "object" + }, "Icon": { "description": "An optionally-sized icon that can be displayed in a user interface.", "properties": { @@ -1632,7 +1647,7 @@ "$ref": "#/$defs/MetaObject" }, "cacheScope": { - "description": "Indicates the intended scope of the cached response, analogous to HTTP\n`Cache-Control: public` vs `Cache-Control: private`.\n\n- `\"public\"`: Any client or intermediary (e.g., shared gateway, proxy)\n MAY cache the response and serve it to any user.\n- `\"private\"`: Only the requesting user's client MAY cache the response.\n Shared caches (e.g., multi-tenant gateways) MUST NOT serve a cached\n copy to a different user.", + "description": "Indicates the intended scope of the cached response, analogous to HTTP\n`Cache-Control: public` vs `Cache-Control: private`.\n\n- `\"public\"`: The response does not contain user-specific data. Any\n client or intermediary (e.g., shared gateway, caching proxy) MAY cache\n the response and serve it across authorization contexts.\n- `\"private\"`: The response MAY be cached and reused only within the\n same authorization context. Caches MUST NOT be shared across\n authorization contexts (e.g., a different access token requires a\n different cache).", "enum": [ "private", "public" @@ -1721,7 +1736,7 @@ "$ref": "#/$defs/MetaObject" }, "cacheScope": { - "description": "Indicates the intended scope of the cached response, analogous to HTTP\n`Cache-Control: public` vs `Cache-Control: private`.\n\n- `\"public\"`: Any client or intermediary (e.g., shared gateway, proxy)\n MAY cache the response and serve it to any user.\n- `\"private\"`: Only the requesting user's client MAY cache the response.\n Shared caches (e.g., multi-tenant gateways) MUST NOT serve a cached\n copy to a different user.", + "description": "Indicates the intended scope of the cached response, analogous to HTTP\n`Cache-Control: public` vs `Cache-Control: private`.\n\n- `\"public\"`: The response does not contain user-specific data. Any\n client or intermediary (e.g., shared gateway, caching proxy) MAY cache\n the response and serve it across authorization contexts.\n- `\"private\"`: The response MAY be cached and reused only within the\n same authorization context. Caches MUST NOT be shared across\n authorization contexts (e.g., a different access token requires a\n different cache).", "enum": [ "private", "public" @@ -1810,7 +1825,7 @@ "$ref": "#/$defs/MetaObject" }, "cacheScope": { - "description": "Indicates the intended scope of the cached response, analogous to HTTP\n`Cache-Control: public` vs `Cache-Control: private`.\n\n- `\"public\"`: Any client or intermediary (e.g., shared gateway, proxy)\n MAY cache the response and serve it to any user.\n- `\"private\"`: Only the requesting user's client MAY cache the response.\n Shared caches (e.g., multi-tenant gateways) MUST NOT serve a cached\n copy to a different user.", + "description": "Indicates the intended scope of the cached response, analogous to HTTP\n`Cache-Control: public` vs `Cache-Control: private`.\n\n- `\"public\"`: The response does not contain user-specific data. Any\n client or intermediary (e.g., shared gateway, caching proxy) MAY cache\n the response and serve it across authorization contexts.\n- `\"private\"`: The response MAY be cached and reused only within the\n same authorization context. Caches MUST NOT be shared across\n authorization contexts (e.g., a different access token requires a\n different cache).", "enum": [ "private", "public" @@ -1874,7 +1889,12 @@ "type": "string" }, "params": { - "$ref": "#/$defs/RequestParams" + "properties": { + "_meta": { + "$ref": "#/$defs/MetaObject" + } + }, + "type": "object" } }, "required": [ @@ -1930,7 +1950,7 @@ "$ref": "#/$defs/MetaObject" }, "cacheScope": { - "description": "Indicates the intended scope of the cached response, analogous to HTTP\n`Cache-Control: public` vs `Cache-Control: private`.\n\n- `\"public\"`: Any client or intermediary (e.g., shared gateway, proxy)\n MAY cache the response and serve it to any user.\n- `\"private\"`: Only the requesting user's client MAY cache the response.\n Shared caches (e.g., multi-tenant gateways) MUST NOT serve a cached\n copy to a different user.", + "description": "Indicates the intended scope of the cached response, analogous to HTTP\n`Cache-Control: public` vs `Cache-Control: private`.\n\n- `\"public\"`: The response does not contain user-specific data. Any\n client or intermediary (e.g., shared gateway, caching proxy) MAY cache\n the response and serve it across authorization contexts.\n- `\"private\"`: The response MAY be cached and reused only within the\n same authorization context. Caches MUST NOT be shared across\n authorization contexts (e.g., a different access token requires a\n different cache).", "enum": [ "private", "public" @@ -2026,7 +2046,7 @@ "description": "Parameters for a `notifications/message` notification.", "properties": { "_meta": { - "$ref": "#/$defs/MetaObject" + "$ref": "#/$defs/NotificationMetaObject" }, "data": { "description": "The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here." @@ -2051,7 +2071,7 @@ "type": "object" }, "MethodNotFoundError": { - "description": "A JSON-RPC error indicating that the requested method does not exist or is not available.\n\nIn MCP, a server returns this error when a client invokes a method the server does not implement — either a genuinely unknown method, or one gated behind a server capability the server did not advertise (e.g., calling `prompts/list` when the `prompts` capability was not advertised).\n\nA request that requires a client capability the client did not declare is signalled instead by {@link MissingRequiredClientCapabilityError} (`-32003`).", + "description": "A JSON-RPC error indicating that the requested method does not exist or is not available.\n\nIn MCP, a server returns this error when a client invokes a method the server does not implement — either a genuinely unknown method, or one gated behind a server capability the server did not advertise (e.g., calling `prompts/list` when the `prompts` capability was not advertised).\n\nA request that requires a client capability the client did not declare is signalled instead by {@link MissingRequiredClientCapabilityError} (`-32021`).", "properties": { "code": { "const": -32601, @@ -2083,7 +2103,7 @@ { "properties": { "code": { - "const": -32003, + "const": -32021, "type": "integer" }, "data": { @@ -2187,11 +2207,21 @@ ], "type": "object" }, + "NotificationMetaObject": { + "description": "Extends {@link MetaObject} with additional notification-specific fields. All key naming rules from `MetaObject` apply.", + "properties": { + "io.modelcontextprotocol/subscriptionId": { + "$ref": "#/$defs/RequestId", + "description": "Identifies the subscription stream a notification was delivered on. The\nserver MUST include this key on every notification delivered via a\n{@link SubscriptionsListenRequestsubscriptions/listen} stream, so the\nclient can correlate the notification with the originating subscription.\nThe key is absent on notifications not delivered via a subscription\nstream (e.g. progress notifications for an in-flight request), which is\nwhy it is optional here.\n\nThe value is the JSON-RPC ID of the `subscriptions/listen` request that\nopened the stream." + } + }, + "type": "object" + }, "NotificationParams": { "description": "Common params for any notification.", "properties": { "_meta": { - "$ref": "#/$defs/MetaObject" + "$ref": "#/$defs/NotificationMetaObject" } }, "type": "object" @@ -2362,7 +2392,7 @@ "description": "Parameters for a {@link ProgressNotificationnotifications/progress} notification.", "properties": { "_meta": { - "$ref": "#/$defs/MetaObject" + "$ref": "#/$defs/NotificationMetaObject" }, "message": { "description": "An optional message describing the current progress.", @@ -2458,7 +2488,7 @@ "type": "object" }, "PromptListChangedNotification": { - "description": "An optional notification from the server to the client, informing it that the list of prompts it offers has changed. This may be issued by servers without any previous subscription from the client.", + "description": "An optional notification from the server to the client, informing it that the list of prompts it offers has changed. This is only delivered on a {@link SubscriptionsListenRequestsubscriptions/listen} stream when the client requested it via the `promptsListChanged` filter field.", "properties": { "jsonrpc": { "const": "2.0", @@ -2573,7 +2603,7 @@ "$ref": "#/$defs/MetaObject" }, "cacheScope": { - "description": "Indicates the intended scope of the cached response, analogous to HTTP\n`Cache-Control: public` vs `Cache-Control: private`.\n\n- `\"public\"`: Any client or intermediary (e.g., shared gateway, proxy)\n MAY cache the response and serve it to any user.\n- `\"private\"`: Only the requesting user's client MAY cache the response.\n Shared caches (e.g., multi-tenant gateways) MUST NOT serve a cached\n copy to a different user.", + "description": "Indicates the intended scope of the cached response, analogous to HTTP\n`Cache-Control: public` vs `Cache-Control: private`.\n\n- `\"public\"`: The response does not contain user-specific data. Any\n client or intermediary (e.g., shared gateway, caching proxy) MAY cache\n the response and serve it across authorization contexts.\n- `\"private\"`: The response MAY be cached and reused only within the\n same authorization context. Caches MUST NOT be shared across\n authorization contexts (e.g., a different access token requires a\n different cache).", "enum": [ "private", "public" @@ -2829,7 +2859,7 @@ "type": "object" }, "ResourceListChangedNotification": { - "description": "An optional notification from the server to the client, informing it that the list of resources it can read from has changed. This may be issued by servers without any previous subscription from the client.", + "description": "An optional notification from the server to the client, informing it that the list of resources it can read from has changed. This is only delivered on a {@link SubscriptionsListenRequestsubscriptions/listen} stream when the client requested it via the `resourcesListChanged` filter field.", "properties": { "jsonrpc": { "const": "2.0", @@ -2957,7 +2987,7 @@ "description": "Parameters for a `notifications/resources/updated` notification.", "properties": { "_meta": { - "$ref": "#/$defs/MetaObject" + "$ref": "#/$defs/NotificationMetaObject" }, "uri": { "description": "The URI of the resource that has been updated. This might be a sub-resource of the one that the client actually subscribed to.", @@ -3098,7 +3128,7 @@ "additionalProperties": { "$ref": "#/$defs/JSONObject" }, - "description": "Optional MCP extensions that the server supports. Keys are extension identifiers\n(e.g., \"io.modelcontextprotocol/tasks\"), and values are per-extension settings\nobjects. An empty object indicates support with no settings.", + "description": "Optional MCP extensions that the server supports. Keys are extension identifiers\n(e.g., \"io.modelcontextprotocol/tasks\"), and values are per-extension settings\nobjects. An empty object indicates support with no settings.\n\nKeys MUST follow the {@link MetaObject`_meta` key naming rules}, with a\nmandatory prefix.", "type": "object" }, "logging": { @@ -3167,9 +3197,6 @@ }, { "$ref": "#/$defs/LoggingMessageNotification" - }, - { - "$ref": "#/$defs/ElicitationCompleteNotification" } ] }, @@ -3307,7 +3334,7 @@ "description": "Parameters for a {@link SubscriptionsAcknowledgedNotificationnotifications/subscriptions/acknowledged} notification.", "properties": { "_meta": { - "$ref": "#/$defs/MetaObject" + "$ref": "#/$defs/NotificationMetaObject" }, "notifications": { "$ref": "#/$defs/SubscriptionFilter", @@ -3549,7 +3576,7 @@ }, "inputSchema": { "additionalProperties": {}, - "description": "A JSON Schema object defining the expected parameters for the tool.\n\nTool arguments are always JSON objects, so `type: \"object\"` is required at the root.\nBeyond that, any JSON Schema 2020-12 keyword may appear alongside `type` — including\ncomposition keywords (`oneOf`, `anyOf`, `allOf`, `not`), conditional keywords\n(`if`/`then`/`else`), reference keywords (`$ref`, `$defs`, `$anchor`), and any other\nstandard validation or annotation keywords.\n\nDefaults to JSON Schema 2020-12 when no explicit `$schema` is provided.", + "description": "A JSON Schema object defining the expected parameters for the tool.\n\nTool arguments are always JSON objects, so `type: \"object\"` is required at the root.\nBeyond that, any JSON Schema 2020-12 keyword may appear alongside `type` — including\ncomposition keywords (`oneOf`, `anyOf`, `allOf`, `not`), conditional keywords\n(`if`/`then`/`else`), reference keywords (`$ref`, `$defs`, `$anchor`), and any other\nstandard validation or annotation keywords.\n\nProperty schemas may carry an `x-mcp-header` annotation to mirror the\nargument value into an HTTP header on the Streamable HTTP transport. See\nthe Streamable HTTP transport specification for the validity and\nextraction rules.\n\nDefaults to JSON Schema 2020-12 when no explicit `$schema` is provided.", "properties": { "$schema": { "type": "string" @@ -3631,7 +3658,7 @@ "type": "object" }, "ToolListChangedNotification": { - "description": "An optional notification from the server to the client, informing it that the list of tools it offers has changed. This may be issued by servers without any previous subscription from the client.", + "description": "An optional notification from the server to the client, informing it that the list of tools it offers has changed. This is only delivered on a {@link SubscriptionsListenRequestsubscriptions/listen} stream when the client requested it via the `toolsListChanged` filter field.", "properties": { "jsonrpc": { "const": "2.0", @@ -3732,7 +3759,7 @@ { "properties": { "code": { - "const": -32004, + "const": -32022, "type": "integer" }, "data": { diff --git a/schema/PINNED.json b/schema/PINNED.json index f99c75abf0..e4022e86c4 100644 --- a/schema/PINNED.json +++ b/schema/PINNED.json @@ -8,7 +8,7 @@ { "protocol_version": "2026-07-28", "source_path_in_spec_repo": "schema/draft/schema.json", - "spec_commit": "6d441518de8a9d5adbab0b10a76a667a63f90665", - "sha256": "bce2e7c9622bb0b449475ba6d8d80228c71190a09250e75dabd502b280ecf3cb" + "spec_commit": "2852f30e26ca5fb779565741ec042094cb110abd", + "sha256": "ed1ad4ba94aaeb2068b78969ef901b1150f7b2f06cf86472b3032abee1380b6a" } ] diff --git a/scripts/gen_surface_types.py b/scripts/gen_surface_types.py index a02b3b02f5..bbc5993172 100644 --- a/scripts/gen_surface_types.py +++ b/scripts/gen_surface_types.py @@ -84,7 +84,9 @@ # reuses class names across versions for unrelated schemas (e.g. `Data`). OPEN_CLASSES: dict[str, frozenset[str]] = { "2025-11-25": frozenset({"Meta", "InputSchema", "OutputSchema", "Result", "GetTaskPayloadResult", "Data"}), - "2026-07-28": frozenset({"MetaObject", "RequestMetaObject", "InputSchema", "OutputSchema", "Result"}), + "2026-07-28": frozenset( + {"MetaObject", "NotificationMetaObject", "RequestMetaObject", "InputSchema", "OutputSchema", "Result"} + ), } # Hand-written union aliases the wire-method maps reference by value; the schema diff --git a/src/mcp/types/__init__.py b/src/mcp/types/__init__.py index 93b9e9c319..992d584687 100644 --- a/src/mcp/types/__init__.py +++ b/src/mcp/types/__init__.py @@ -199,6 +199,7 @@ # Re-export JSONRPC types from mcp.types.jsonrpc import ( CONNECTION_CLOSED, + HEADER_MISMATCH, INTERNAL_ERROR, INVALID_PARAMS, INVALID_REQUEST, @@ -423,6 +424,7 @@ "server_result_adapter", # JSON-RPC types "CONNECTION_CLOSED", + "HEADER_MISMATCH", "INTERNAL_ERROR", "INVALID_PARAMS", "INVALID_REQUEST", diff --git a/src/mcp/types/_types.py b/src/mcp/types/_types.py index e689d54189..390c24e81d 100644 --- a/src/mcp/types/_types.py +++ b/src/mcp/types/_types.py @@ -397,7 +397,7 @@ class ClientCapabilities(MCPModel): class UnsupportedProtocolVersionErrorData(MCPModel): - """Error data for the -32004 unsupported-protocol-version error (2026-07-28).""" + """Error data for the -32022 unsupported-protocol-version error (2026-07-28).""" supported: list[str] """Protocol versions the server supports; the client should pick one and retry.""" @@ -406,7 +406,7 @@ class UnsupportedProtocolVersionErrorData(MCPModel): class MissingRequiredClientCapabilityErrorData(MCPModel): - """Error data for the -32003 missing-required-client-capability error (2026-07-28).""" + """Error data for the -32021 missing-required-client-capability error (2026-07-28).""" required_capabilities: ClientCapabilities """The capabilities the server requires from the client to process this request.""" diff --git a/src/mcp/types/jsonrpc.py b/src/mcp/types/jsonrpc.py index 990973833b..f6799988d4 100644 --- a/src/mcp/types/jsonrpc.py +++ b/src/mcp/types/jsonrpc.py @@ -41,19 +41,26 @@ class JSONRPCResponse(BaseModel): result: dict[str, Any] -# MCP-specific error codes in the range [-32000, -32099] -URL_ELICITATION_REQUIRED = -32042 -"""A URL-mode elicitation is required before the request can be processed (protocol 2025-11-25 only).""" +# MCP error codes occupy the JSON-RPC server-error range -32000..-32099. +# Per the 2026-07-28 spec's allocation policy: +# -32000..-32019 implementation-defined (SDK-local; never on the wire as a spec code) +# -32020..-32099 reserved for spec-defined codes, allocated sequentially from -32020 +# -32002, -32042 reserved-never-reused (retired by earlier protocol versions) + +HEADER_MISMATCH = -32020 +"""HTTP headers do not match the request body, or required headers are missing/malformed (protocol 2026-07-28).""" -MISSING_REQUIRED_CLIENT_CAPABILITY = -32003 +MISSING_REQUIRED_CLIENT_CAPABILITY = -32021 """The server requires a client capability the request did not declare (protocol 2026-07-28).""" -UNSUPPORTED_PROTOCOL_VERSION = -32004 +UNSUPPORTED_PROTOCOL_VERSION = -32022 """The request's protocol version is not supported by the server (protocol 2026-07-28).""" -# SDK error codes: SDK-internal allocations in the JSON-RPC server-error range -# [-32000, -32099]; not defined by the MCP schema. New values must avoid codes -# the spec has allocated above. +URL_ELICITATION_REQUIRED = -32042 +"""A URL-mode elicitation is required before the request can be processed (protocol 2025-11-25 only).""" + +# SDK error codes: SDK-internal allocations in the implementation-defined band +# -32000..-32019; not defined by the MCP schema. CONNECTION_CLOSED = -32000 """SDK-only: the connection closed before a response arrived; never emitted on the wire.""" diff --git a/src/mcp/types/methods.py b/src/mcp/types/methods.py index 6e218a515e..10bded166d 100644 --- a/src/mcp/types/methods.py +++ b/src/mcp/types/methods.py @@ -144,9 +144,8 @@ ("notifications/initialized", "2025-11-25"): v2025.InitializedNotification, ("notifications/progress", "2025-11-25"): v2025.ProgressNotification, ("notifications/roots/list_changed", "2025-11-25"): v2025.RootsListChangedNotification, - # 2026-07-28 (initialized and roots/list_changed removed) + # 2026-07-28 (initialized, progress and roots/list_changed removed) ("notifications/cancelled", "2026-07-28"): v2026.CancelledNotification, - ("notifications/progress", "2026-07-28"): v2026.ProgressNotification, } ) @@ -212,9 +211,8 @@ ("notifications/resources/list_changed", "2025-11-25"): v2025.ResourceListChangedNotification, ("notifications/resources/updated", "2025-11-25"): v2025.ResourceUpdatedNotification, ("notifications/tools/list_changed", "2025-11-25"): v2025.ToolListChangedNotification, - # 2026-07-28 (adds subscriptions/acknowledged) + # 2026-07-28 (adds subscriptions/acknowledged; elicitation/complete removed) ("notifications/cancelled", "2026-07-28"): v2026.CancelledNotification, - ("notifications/elicitation/complete", "2026-07-28"): v2026.ElicitationCompleteNotification, ("notifications/message", "2026-07-28"): v2026.LoggingMessageNotification, ("notifications/progress", "2026-07-28"): v2026.ProgressNotification, ("notifications/prompts/list_changed", "2026-07-28"): v2026.PromptListChangedNotification, diff --git a/src/mcp/types/v2026_07_28/__init__.py b/src/mcp/types/v2026_07_28/__init__.py index c18e8bd075..c0b4d88929 100644 --- a/src/mcp/types/v2026_07_28/__init__.py +++ b/src/mcp/types/v2026_07_28/__init__.py @@ -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 `bce2e7c9622bb0b449475ba6d8d80228c71190a09250e75dabd502b280ecf3cb`).""" +(sha256 `ed1ad4ba94aaeb2068b78969ef901b1150f7b2f06cf86472b3032abee1380b6a`).""" # pyright: reportIncompatibleVariableOverride=false, reportGeneralTypeIssues=false from __future__ import annotations @@ -148,11 +148,6 @@ class ElicitRequestURLParams(WireModel): model_config = ConfigDict( extra="ignore", ) - elicitation_id: Annotated[str, Field(alias="elicitationId")] - """ - The ID of the elicitation, which must be unique within the context of the server. - The client MUST treat this ID as an opaque value. - """ message: str """ The message to present to the user explaining why the interaction is needed. @@ -190,29 +185,6 @@ class ElicitResult(WireModel): """ -class Params(WireModel): - model_config = ConfigDict( - extra="ignore", - ) - elicitation_id: Annotated[str, Field(alias="elicitationId")] - """ - The ID of the elicitation that completed. - """ - - -class ElicitationCompleteNotification(WireModel): - """ - An optional notification from the server to the client, informing it of a completion of a out-of-band elicitation request. - """ - - model_config = ConfigDict( - extra="ignore", - ) - jsonrpc: Literal["2.0"] - method: Literal["notifications/elicitation/complete"] - params: Params - - class Error(WireModel): model_config = ConfigDict( extra="ignore", @@ -231,6 +203,16 @@ class Error(WireModel): """ +class Error1(Error): + model_config = ConfigDict( + extra="ignore", + ) + code: Literal[-32020] + """ + The error type that occurred. + """ + + class Icon(WireModel): """ An optionally-sized icon that can be displayed in a user interface. @@ -507,7 +489,7 @@ class MethodNotFoundError(WireModel): In MCP, a server returns this error when a client invokes a method the server does not implement — either a genuinely unknown method, or one gated behind a server capability the server did not advertise (e.g., calling `prompts/list` when the `prompts` capability was not advertised). - A request that requires a client capability the client did not declare is signalled instead by {@link MissingRequiredClientCapabilityError} (`-32003`). + A request that requires a client capability the client did not declare is signalled instead by {@link MissingRequiredClientCapabilityError} (`-32021`). """ model_config = ConfigDict( @@ -608,17 +590,6 @@ class Notification(WireModel): params: dict[str, Any] | None = None -class NotificationParams(WireModel): - """ - Common params for any notification. - """ - - model_config = ConfigDict( - extra="ignore", - ) - meta: Annotated[MetaObject | None, Field(alias="_meta")] = None - - class NumberSchema(WireModel): model_config = ConfigDict( extra="ignore", @@ -713,19 +684,6 @@ class PromptArgument(WireModel): """ -class PromptListChangedNotification(WireModel): - """ - An optional notification from the server to the client, informing it that the list of prompts it offers has changed. This may be issued by servers without any previous subscription from the client. - """ - - model_config = ConfigDict( - extra="ignore", - ) - jsonrpc: Literal["2.0"] - method: Literal["notifications/prompts/list_changed"] - params: NotificationParams | None = None - - class PromptReference(WireModel): """ Identifies a prompt. @@ -784,19 +742,6 @@ class ResourceContents(WireModel): """ -class ResourceListChangedNotification(WireModel): - """ - An optional notification from the server to the client, informing it that the list of resources it can read from has changed. This may be issued by servers without any previous subscription from the client. - """ - - model_config = ConfigDict( - extra="ignore", - ) - jsonrpc: Literal["2.0"] - method: Literal["notifications/resources/list_changed"] - params: NotificationParams | None = None - - class ResourceTemplateReference(WireModel): """ A reference to a resource or resource template definition. @@ -812,21 +757,6 @@ class ResourceTemplateReference(WireModel): """ -class ResourceUpdatedNotificationParams(WireModel): - """ - Parameters for a `notifications/resources/updated` notification. - """ - - model_config = ConfigDict( - extra="ignore", - ) - meta: Annotated[MetaObject | None, Field(alias="_meta")] = None - uri: str - """ - The URI of the resource that has been updated. This might be a sub-resource of the one that the client actually subscribed to. - """ - - class Result(WireModel): """ Common result fields. @@ -979,24 +909,6 @@ class SubscriptionFilter(WireModel): """ -class SubscriptionsAcknowledgedNotificationParams(WireModel): - """ - Parameters for a {@link SubscriptionsAcknowledgedNotificationnotifications/subscriptions/acknowledged} notification. - """ - - model_config = ConfigDict( - extra="ignore", - ) - meta: Annotated[MetaObject | None, Field(alias="_meta")] = None - notifications: SubscriptionFilter - """ - The subset of requested notification types the server agreed to honor. - Only includes notification types the server actually supports; if the - client requested an unsupported type (e.g., `promptsListChanged` when - the server has no prompts), it is omitted from this set. - """ - - class TextResourceContents(WireModel): model_config = ConfigDict( extra="ignore", @@ -1130,6 +1042,11 @@ class InputSchema(WireModel): (`if`/`then`/`else`), reference keywords (`$ref`, `$defs`, `$anchor`), and any other standard validation or annotation keywords. + Property schemas may carry an `x-mcp-header` annotation to mirror the + argument value into an HTTP header on the Streamable HTTP transport. See + the Streamable HTTP transport specification for the validity and + extraction rules. + Defaults to JSON Schema 2020-12 when no explicit `$schema` is provided. """ @@ -1225,19 +1142,6 @@ class ToolChoice(WireModel): """ -class ToolListChangedNotification(WireModel): - """ - An optional notification from the server to the client, informing it that the list of tools it offers has changed. This may be issued by servers without any previous subscription from the client. - """ - - model_config = ConfigDict( - extra="ignore", - ) - jsonrpc: Literal["2.0"] - method: Literal["notifications/tools/list_changed"] - params: NotificationParams | None = None - - class ToolUseContent(WireModel): """ A request from the assistant to call a tool. @@ -1287,11 +1191,11 @@ class Data1(WireModel): """ -class Error2(Error): +class Error3(Error): model_config = ConfigDict( extra="ignore", ) - code: Literal[-32004] + code: Literal[-32022] """ The error type that occurred. """ @@ -1312,7 +1216,7 @@ class UnsupportedProtocolVersionError(WireModel): model_config = ConfigDict( extra="ignore", ) - error: Error2 + error: Error3 id: RequestId | None = None jsonrpc: Literal["2.0"] @@ -1484,11 +1388,13 @@ class CacheableResult(WireModel): Indicates the intended scope of the cached response, analogous to HTTP `Cache-Control: public` vs `Cache-Control: private`. - - `"public"`: Any client or intermediary (e.g., shared gateway, proxy) - MAY cache the response and serve it to any user. - - `"private"`: Only the requesting user's client MAY cache the response. - Shared caches (e.g., multi-tenant gateways) MUST NOT serve a cached - copy to a different user. + - `"public"`: The response does not contain user-specific data. Any + client or intermediary (e.g., shared gateway, caching proxy) MAY cache + the response and serve it across authorization contexts. + - `"private"`: The response MAY be cached and reused only within the + same authorization context. Caches MUST NOT be shared across + authorization contexts (e.g., a different access token requires a + different cache). """ result_type: Annotated[str, Field(alias="resultType")] """ @@ -1513,27 +1419,6 @@ class CacheableResult(WireModel): """ -class CancelledNotificationParams(WireModel): - """ - Parameters for a `notifications/cancelled` notification. - """ - - model_config = ConfigDict( - extra="ignore", - ) - meta: Annotated[MetaObject | None, Field(alias="_meta")] = None - reason: str | None = None - """ - An optional string describing the reason for the cancellation. This MAY be logged or presented to the user. - """ - request_id: Annotated[RequestId | None, Field(alias="requestId")] = None - """ - The ID of the request to cancel. - - This MUST correspond to the ID of a request previously issued in the same direction. - """ - - class ClientResult(RootModel[Result]): root: Result """ @@ -1628,6 +1513,22 @@ class EnumSchema( ) +class HeaderMismatchError(WireModel): + """ + Returned when a server rejects a request because the values in the HTTP + headers do not match the corresponding values in the request body, or + because required headers are missing or malformed. For HTTP, the response + status code MUST be `400 Bad Request`. + """ + + model_config = ConfigDict( + extra="ignore", + ) + error: Error1 + id: RequestId | None = None + jsonrpc: Literal["2.0"] + + class ImageContent(WireModel): """ An image provided to or from an LLM. @@ -1692,6 +1593,31 @@ class JSONRPCResultResponse(WireModel): result: Result +class Params(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + + +class ListRootsRequest(WireModel): + """ + Sent from the server to request a list of root URIs from the client. Roots allow + servers to ask for specific directories or files to operate on. A common example + for roots is providing a set of repositories or directories a server should operate + on. + + This request is typically used when the server needs to understand the file system + structure or access specific locations that the client has permission to read from. + """ + + model_config = ConfigDict( + extra="ignore", + ) + method: Literal["roots/list"] + params: Params | None = None + + class ListRootsResult(WireModel): """ The result returned by the client for a {@link ListRootsRequestroots/list} request. @@ -1705,31 +1631,44 @@ class ListRootsResult(WireModel): roots: list[Root] -class LoggingMessageNotificationParams(WireModel): +class MultiSelectEnumSchema(RootModel[UntitledMultiSelectEnumSchema | TitledMultiSelectEnumSchema]): + root: UntitledMultiSelectEnumSchema | TitledMultiSelectEnumSchema + + +class NotificationMetaObject(WireModel): """ - Parameters for a `notifications/message` notification. + Extends {@link MetaObject} with additional notification-specific fields. All key naming rules from `MetaObject` apply. """ model_config = ConfigDict( - extra="ignore", + extra="allow", ) - meta: Annotated[MetaObject | None, Field(alias="_meta")] = None - data: Any - """ - The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here. - """ - level: LoggingLevel + io_modelcontextprotocol_subscription_id: Annotated[ + RequestId | None, Field(alias="io.modelcontextprotocol/subscriptionId") + ] = None """ - The severity of this log message. + Identifies the subscription stream a notification was delivered on. The + server MUST include this key on every notification delivered via a + {@link SubscriptionsListenRequestsubscriptions/listen} stream, so the + client can correlate the notification with the originating subscription. + The key is absent on notifications not delivered via a subscription + stream (e.g. progress notifications for an in-flight request), which is + why it is optional here. + + The value is the JSON-RPC ID of the `subscriptions/listen` request that + opened the stream. """ - logger: str | None = None + + +class NotificationParams(WireModel): """ - An optional name of the logger issuing this message. + Common params for any notification. """ - -class MultiSelectEnumSchema(RootModel[UntitledMultiSelectEnumSchema | TitledMultiSelectEnumSchema]): - root: UntitledMultiSelectEnumSchema | TitledMultiSelectEnumSchema + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[NotificationMetaObject | None, Field(alias="_meta")] = None class PrimitiveSchemaDefinition( @@ -1768,7 +1707,7 @@ class ProgressNotificationParams(WireModel): model_config = ConfigDict( extra="ignore", ) - meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + meta: Annotated[NotificationMetaObject | None, Field(alias="_meta")] = None message: str | None = None """ An optional message describing the current progress. @@ -1831,6 +1770,19 @@ class Prompt(WireModel): """ +class PromptListChangedNotification(WireModel): + """ + An optional notification from the server to the client, informing it that the list of prompts it offers has changed. This is only delivered on a {@link SubscriptionsListenRequestsubscriptions/listen} stream when the client requested it via the `promptsListChanged` filter field. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/prompts/list_changed"] + params: NotificationParams | None = None + + class ReadResourceResult(WireModel): """ The result returned by the server for a {@link ReadResourceRequestresources/read} request. @@ -1845,11 +1797,13 @@ class ReadResourceResult(WireModel): Indicates the intended scope of the cached response, analogous to HTTP `Cache-Control: public` vs `Cache-Control: private`. - - `"public"`: Any client or intermediary (e.g., shared gateway, proxy) - MAY cache the response and serve it to any user. - - `"private"`: Only the requesting user's client MAY cache the response. - Shared caches (e.g., multi-tenant gateways) MUST NOT serve a cached - copy to a different user. + - `"public"`: The response does not contain user-specific data. Any + client or intermediary (e.g., shared gateway, caching proxy) MAY cache + the response and serve it across authorization contexts. + - `"private"`: The response MAY be cached and reused only within the + same authorization context. Caches MUST NOT be shared across + authorization contexts (e.g., a different access token requires a + different cache). """ contents: list[TextResourceContents | BlobResourceContents] result_type: Annotated[str, Field(alias="resultType")] @@ -1998,6 +1952,19 @@ class ResourceLink(WireModel): """ +class ResourceListChangedNotification(WireModel): + """ + An optional notification from the server to the client, informing it that the list of resources it can read from has changed. This is only delivered on a {@link SubscriptionsListenRequestsubscriptions/listen} stream when the client requested it via the `resourcesListChanged` filter field. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/resources/list_changed"] + params: NotificationParams | None = None + + class ResourceTemplate(WireModel): """ A template description for resources available on the server. @@ -2052,37 +2019,41 @@ class ResourceTemplate(WireModel): """ -class ResourceUpdatedNotification(WireModel): +class ResourceUpdatedNotificationParams(WireModel): """ - A notification from the server to the client, informing it that a resource has changed and may need to be read again. This is only sent for resources the client opted in to via the `resourceSubscriptions` field of a {@link SubscriptionsListenRequestsubscriptions/listen} request. + Parameters for a `notifications/resources/updated` notification. """ model_config = ConfigDict( extra="ignore", ) - jsonrpc: Literal["2.0"] - method: Literal["notifications/resources/updated"] - params: ResourceUpdatedNotificationParams + meta: Annotated[NotificationMetaObject | None, Field(alias="_meta")] = None + uri: str + """ + The URI of the resource that has been updated. This might be a sub-resource of the one that the client actually subscribed to. + """ class SingleSelectEnumSchema(RootModel[UntitledSingleSelectEnumSchema | TitledSingleSelectEnumSchema]): root: UntitledSingleSelectEnumSchema | TitledSingleSelectEnumSchema -class SubscriptionsAcknowledgedNotification(WireModel): +class SubscriptionsAcknowledgedNotificationParams(WireModel): """ - Sent by the server as the first message on a - {@link SubscriptionsListenRequestsubscriptions/listen} stream to acknowledge - that the subscription has been established and to report which notification - types it agreed to honor. + Parameters for a {@link SubscriptionsAcknowledgedNotificationnotifications/subscriptions/acknowledged} notification. """ model_config = ConfigDict( extra="ignore", ) - jsonrpc: Literal["2.0"] - method: Literal["notifications/subscriptions/acknowledged"] - params: SubscriptionsAcknowledgedNotificationParams + meta: Annotated[NotificationMetaObject | None, Field(alias="_meta")] = None + notifications: SubscriptionFilter + """ + The subset of requested notification types the server agreed to honor. + Only includes notification types the server actually supports; if the + client requested an unsupported type (e.g., `promptsListChanged` when + the server has no prompts), it is omitted from this set. + """ class TextContent(WireModel): @@ -2148,6 +2119,11 @@ class Tool(WireModel): (`if`/`then`/`else`), reference keywords (`$ref`, `$defs`, `$anchor`), and any other standard validation or annotation keywords. + Property schemas may carry an `x-mcp-header` annotation to mirror the + argument value into an HTTP header on the Streamable HTTP transport. See + the Streamable HTTP transport specification for the validity and + extraction rules. + Defaults to JSON Schema 2020-12 when no explicit `$schema` is provided. """ name: str @@ -2172,9 +2148,45 @@ class Tool(WireModel): """ -class CancelledNotification(WireModel): +class ToolListChangedNotification(WireModel): + """ + An optional notification from the server to the client, informing it that the list of tools it offers has changed. This is only delivered on a {@link SubscriptionsListenRequestsubscriptions/listen} stream when the client requested it via the `toolsListChanged` filter field. """ - This notification can be sent by either side to indicate that it is cancelling a previously-issued request. + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/tools/list_changed"] + params: NotificationParams | None = None + + +class CancelledNotificationParams(WireModel): + """ + Parameters for a `notifications/cancelled` notification. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[NotificationMetaObject | None, Field(alias="_meta")] = None + reason: str | None = None + """ + An optional string describing the reason for the cancellation. This MAY be logged or presented to the user. + """ + request_id: Annotated[RequestId, Field(alias="requestId")] + """ + The ID of the request to cancel. + + This MUST correspond to the ID of a request the client previously issued. + """ + + +class ClientNotification(WireModel): + """ + This notification is sent by the client to indicate that it is cancelling a request it previously issued. + + On stdio, the server also sends this notification, solely to terminate a {@link SubscriptionsListenRequestsubscriptions/listen} stream: it references the ID of the `subscriptions/listen` request that opened the stream. Servers MUST NOT use this notification to cancel any other request. The request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished. @@ -2233,11 +2245,13 @@ class ListPromptsResult(WireModel): Indicates the intended scope of the cached response, analogous to HTTP `Cache-Control: public` vs `Cache-Control: private`. - - `"public"`: Any client or intermediary (e.g., shared gateway, proxy) - MAY cache the response and serve it to any user. - - `"private"`: Only the requesting user's client MAY cache the response. - Shared caches (e.g., multi-tenant gateways) MUST NOT serve a cached - copy to a different user. + - `"public"`: The response does not contain user-specific data. Any + client or intermediary (e.g., shared gateway, caching proxy) MAY cache + the response and serve it across authorization contexts. + - `"private"`: The response MAY be cached and reused only within the + same authorization context. Caches MUST NOT be shared across + authorization contexts (e.g., a different access token requires a + different cache). """ next_cursor: Annotated[str | None, Field(alias="nextCursor")] = None """ @@ -2295,11 +2309,13 @@ class ListResourceTemplatesResult(WireModel): Indicates the intended scope of the cached response, analogous to HTTP `Cache-Control: public` vs `Cache-Control: private`. - - `"public"`: Any client or intermediary (e.g., shared gateway, proxy) - MAY cache the response and serve it to any user. - - `"private"`: Only the requesting user's client MAY cache the response. - Shared caches (e.g., multi-tenant gateways) MUST NOT serve a cached - copy to a different user. + - `"public"`: The response does not contain user-specific data. Any + client or intermediary (e.g., shared gateway, caching proxy) MAY cache + the response and serve it across authorization contexts. + - `"private"`: The response MAY be cached and reused only within the + same authorization context. Caches MUST NOT be shared across + authorization contexts (e.g., a different access token requires a + different cache). """ next_cursor: Annotated[str | None, Field(alias="nextCursor")] = None """ @@ -2357,11 +2373,13 @@ class ListResourcesResult(WireModel): Indicates the intended scope of the cached response, analogous to HTTP `Cache-Control: public` vs `Cache-Control: private`. - - `"public"`: Any client or intermediary (e.g., shared gateway, proxy) - MAY cache the response and serve it to any user. - - `"private"`: Only the requesting user's client MAY cache the response. - Shared caches (e.g., multi-tenant gateways) MUST NOT serve a cached - copy to a different user. + - `"public"`: The response does not contain user-specific data. Any + client or intermediary (e.g., shared gateway, caching proxy) MAY cache + the response and serve it across authorization contexts. + - `"private"`: The response MAY be cached and reused only within the + same authorization context. Caches MUST NOT be shared across + authorization contexts (e.g., a different access token requires a + different cache). """ next_cursor: Annotated[str | None, Field(alias="nextCursor")] = None """ @@ -2419,11 +2437,13 @@ class ListToolsResult(WireModel): Indicates the intended scope of the cached response, analogous to HTTP `Cache-Control: public` vs `Cache-Control: private`. - - `"public"`: Any client or intermediary (e.g., shared gateway, proxy) - MAY cache the response and serve it to any user. - - `"private"`: Only the requesting user's client MAY cache the response. - Shared caches (e.g., multi-tenant gateways) MUST NOT serve a cached - copy to a different user. + - `"public"`: The response does not contain user-specific data. Any + client or intermediary (e.g., shared gateway, caching proxy) MAY cache + the response and serve it across authorization contexts. + - `"private"`: The response MAY be cached and reused only within the + same authorization context. Caches MUST NOT be shared across + authorization contexts (e.g., a different access token requires a + different cache). """ next_cursor: Annotated[str | None, Field(alias="nextCursor")] = None """ @@ -2467,17 +2487,27 @@ class ListToolsResultResponse(WireModel): result: ListToolsResult -class LoggingMessageNotification(WireModel): +class LoggingMessageNotificationParams(WireModel): """ - JSONRPCNotification of a log message passed from server to client. The client opts in by setting `"io.modelcontextprotocol/logLevel"` in a request's `_meta`. + Parameters for a `notifications/message` notification. """ model_config = ConfigDict( extra="ignore", ) - jsonrpc: Literal["2.0"] - method: Literal["notifications/message"] - params: LoggingMessageNotificationParams + meta: Annotated[NotificationMetaObject | None, Field(alias="_meta")] = None + data: Any + """ + The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here. + """ + level: LoggingLevel + """ + The severity of this log message. + """ + logger: str | None = None + """ + An optional name of the logger issuing this message. + """ class ProgressNotification(WireModel): @@ -2508,30 +2538,33 @@ class PromptMessage(WireModel): role: Role -class ServerNotification( - RootModel[ - CancelledNotification - | ProgressNotification - | ResourceListChangedNotification - | SubscriptionsAcknowledgedNotification - | ResourceUpdatedNotification - | PromptListChangedNotification - | ToolListChangedNotification - | LoggingMessageNotification - | ElicitationCompleteNotification - ] -): - root: ( - CancelledNotification - | ProgressNotification - | ResourceListChangedNotification - | SubscriptionsAcknowledgedNotification - | ResourceUpdatedNotification - | PromptListChangedNotification - | ToolListChangedNotification - | LoggingMessageNotification - | ElicitationCompleteNotification +class ResourceUpdatedNotification(WireModel): + """ + A notification from the server to the client, informing it that a resource has changed and may need to be read again. This is only sent for resources the client opted in to via the `resourceSubscriptions` field of a {@link SubscriptionsListenRequestsubscriptions/listen} request. + """ + + model_config = ConfigDict( + extra="ignore", ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/resources/updated"] + params: ResourceUpdatedNotificationParams + + +class SubscriptionsAcknowledgedNotification(WireModel): + """ + Sent by the server as the first message on a + {@link SubscriptionsListenRequestsubscriptions/listen} stream to acknowledge + that the subscription has been established and to report which notification + types it agreed to honor. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/subscriptions/acknowledged"] + params: SubscriptionsAcknowledgedNotificationParams class ToolResultContent(WireModel): @@ -2624,8 +2657,23 @@ class CallToolResult(WireModel): """ -class ClientNotification(RootModel[CancelledNotification | ProgressNotification]): - root: CancelledNotification | ProgressNotification +class CancelledNotification(WireModel): + """ + This notification is sent by the client to indicate that it is cancelling a request it previously issued. + + On stdio, the server also sends this notification, solely to terminate a {@link SubscriptionsListenRequestsubscriptions/listen} stream: it references the ID of the `subscriptions/listen` request that opened the stream. Servers MUST NOT use this notification to cancel any other request. + + The request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished. + + This notification indicates that the result will be unused, so any associated processing SHOULD cease. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/cancelled"] + params: CancelledNotificationParams class GetPromptResult(WireModel): @@ -2654,12 +2702,49 @@ class GetPromptResult(WireModel): """ +class LoggingMessageNotification(WireModel): + """ + JSONRPCNotification of a log message passed from server to client. The client opts in by setting `"io.modelcontextprotocol/logLevel"` in a request's `_meta`. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/message"] + params: LoggingMessageNotificationParams + + class SamplingMessageContentBlock( RootModel[TextContent | ImageContent | AudioContent | ToolUseContent | ToolResultContent] ): root: TextContent | ImageContent | AudioContent | ToolUseContent | ToolResultContent +class ServerNotification( + RootModel[ + CancelledNotification + | ProgressNotification + | ResourceListChangedNotification + | SubscriptionsAcknowledgedNotification + | ResourceUpdatedNotification + | PromptListChangedNotification + | ToolListChangedNotification + | LoggingMessageNotification + ] +): + root: ( + CancelledNotification + | ProgressNotification + | ResourceListChangedNotification + | SubscriptionsAcknowledgedNotification + | ResourceUpdatedNotification + | PromptListChangedNotification + | ToolListChangedNotification + | LoggingMessageNotification + ) + + class CreateMessageResult(WireModel): """ The result returned by the client for a {@link CreateMessageRequestsampling/createMessage} request. @@ -2832,6 +2917,9 @@ class ClientCapabilities(WireModel): Optional MCP extensions that the client supports. Keys are extension identifiers (e.g., "io.modelcontextprotocol/oauth-client-credentials"), and values are per-extension settings objects. An empty object indicates support with no settings. + + Keys MUST follow the {@link MetaObject`_meta` key naming rules}, with a + mandatory prefix. """ roots: dict[str, Any] | None = None """ @@ -2974,11 +3062,13 @@ class DiscoverResult(WireModel): Indicates the intended scope of the cached response, analogous to HTTP `Cache-Control: public` vs `Cache-Control: private`. - - `"public"`: Any client or intermediary (e.g., shared gateway, proxy) - MAY cache the response and serve it to any user. - - `"private"`: Only the requesting user's client MAY cache the response. - Shared caches (e.g., multi-tenant gateways) MUST NOT serve a cached - copy to a different user. + - `"public"`: The response does not contain user-specific data. Any + client or intermediary (e.g., shared gateway, caching proxy) MAY cache + the response and serve it across authorization contexts. + - `"private"`: The response MAY be cached and reused only within the + same authorization context. Caches MUST NOT be shared across + authorization contexts (e.g., a different access token requires a + different cache). """ capabilities: ServerCapabilities """ @@ -3163,24 +3253,6 @@ class ListResourcesRequest(WireModel): params: PaginatedRequestParams -class ListRootsRequest(WireModel): - """ - Sent from the server to request a list of root URIs from the client. Roots allow - servers to ask for specific directories or files to operate on. A common example - for roots is providing a set of repositories or directories a server should operate - on. - - This request is typically used when the server needs to understand the file system - structure or access specific locations that the client has permission to read from. - """ - - model_config = ConfigDict( - extra="ignore", - ) - method: Literal["roots/list"] - params: RequestParams | None = None - - class ListToolsRequest(WireModel): """ Sent from the client to request a list of tools the server has. @@ -3209,11 +3281,11 @@ class Data(WireModel): """ -class Error1(Error): +class Error2(Error): model_config = ConfigDict( extra="ignore", ) - code: Literal[-32003] + code: Literal[-32021] """ The error type that occurred. """ @@ -3233,7 +3305,7 @@ class MissingRequiredClientCapabilityError(WireModel): model_config = ConfigDict( extra="ignore", ) - error: Error1 + error: Error2 id: RequestId | None = None jsonrpc: Literal["2.0"] @@ -3405,6 +3477,9 @@ class ServerCapabilities(WireModel): Optional MCP extensions that the server supports. Keys are extension identifiers (e.g., "io.modelcontextprotocol/tasks"), and values are per-extension settings objects. An empty object indicates support with no settings. + + Keys MUST follow the {@link MetaObject`_meta` key naming rules}, with a + mandatory prefix. """ logging: JSONObject | None = None """ @@ -3457,6 +3532,10 @@ class SubscriptionsListenRequestParams(WireModel): """ +class InputRequest(RootModel[CreateMessageRequest | ListRootsRequest | ElicitRequest]): + root: CreateMessageRequest | ListRootsRequest | ElicitRequest + + class ServerResult( RootModel[ Result @@ -3487,10 +3566,6 @@ class ServerResult( ) -class InputRequest(RootModel[CreateMessageRequest | ListRootsRequest | ElicitRequest]): - root: CreateMessageRequest | ListRootsRequest | ElicitRequest - - class ClientRequest( RootModel[ DiscoverRequest @@ -3565,7 +3640,6 @@ class JSONValue(RootModel[Union[JSONObject, list["JSONValue"], str | int | float ListPromptsRequest.model_rebuild() ListResourceTemplatesRequest.model_rebuild() ListResourcesRequest.model_rebuild() -ListRootsRequest.model_rebuild() ListToolsRequest.model_rebuild() PaginatedRequest.model_rebuild() PaginatedRequestParams.model_rebuild() diff --git a/tests/server/test_runner.py b/tests/server/test_runner.py index 5d61a676a8..c4d10aea08 100644 --- a/tests/server/test_runner.py +++ b/tests/server/test_runner.py @@ -320,16 +320,18 @@ async def test_runner_on_notify_drops_a_spec_notification_absent_at_the_negotiat async def dropped(ctx: Ctx, params: NotificationParams) -> None: raise NotImplementedError # the version gate drops the notification first - async def on_progress(ctx: Ctx, params: ProgressNotificationParams) -> None: + async def on_barrier(ctx: Ctx, params: NotificationParams) -> None: barrier.set() server.add_notification_handler("notifications/roots/list_changed", NotificationParams, dropped) - server.add_notification_handler("notifications/progress", ProgressNotificationParams, on_progress) + # A custom (non-spec) method bypasses the version gate, so it reaches its + # handler regardless of which spec notifications exist at the pinned version. + server.add_notification_handler("custom/barrier", NotificationParams, on_barrier) with caplog.at_level("DEBUG", logger="mcp.server.runner"): async with connected_runner(server) as (client, runner): runner.connection.protocol_version = "2026-07-28" await client.notify("notifications/roots/list_changed", None) - await client.notify("notifications/progress", {"progressToken": 1, "progress": 0.5}) + await client.notify("custom/barrier", None) await barrier.wait() assert "dropped 'notifications/roots/list_changed': not defined at 2026-07-28" in caplog.text diff --git a/tests/types/test_methods.py b/tests/types/test_methods.py index 4800b5acb8..1a467f5079 100644 --- a/tests/types/test_methods.py +++ b/tests/types/test_methods.py @@ -190,13 +190,12 @@ "tools/list", } ), - "CLIENT_NOTIFICATIONS": frozenset({"notifications/cancelled", "notifications/progress"}), + "CLIENT_NOTIFICATIONS": frozenset({"notifications/cancelled"}), # No standalone server-to-client request channel at this version. "SERVER_REQUESTS": frozenset(), "SERVER_NOTIFICATIONS": frozenset( { "notifications/cancelled", - "notifications/elicitation/complete", "notifications/message", "notifications/progress", "notifications/prompts/list_changed", @@ -366,7 +365,6 @@ v2025.RootsListChangedNotification: None, v2025.ToolListChangedNotification: None, v2026.CancelledNotification: {"requestId": 1}, - v2026.ElicitationCompleteNotification: {"elicitationId": "e1"}, v2026.LoggingMessageNotification: {"level": "info", "data": "x"}, v2026.ProgressNotification: {"progressToken": 1, "progress": 0.5}, v2026.PromptListChangedNotification: None, diff --git a/tests/types/test_parity.py b/tests/types/test_parity.py index 16ed5f7c7d..e5d305cf95 100644 --- a/tests/types/test_parity.py +++ b/tests/types/test_parity.py @@ -51,8 +51,6 @@ "v2026_07_28.Data": monolith.MissingRequiredClientCapabilityErrorData, "v2026_07_28.Data1": monolith.UnsupportedProtocolVersionErrorData, "v2026_07_28.Elicitation": monolith.ElicitationCapability, - "v2026_07_28.ElicitationCompleteNotification": monolith.ElicitCompleteNotification, - "v2026_07_28.Params": monolith.ElicitCompleteNotificationParams, "v2026_07_28.Error": monolith.ErrorData, "v2026_07_28.JSONRPCErrorResponse": monolith.JSONRPCError, "v2026_07_28.JSONRPCResultResponse": monolith.JSONRPCResponse, @@ -91,11 +89,14 @@ "v2026_07_28.AnyOfItem", "v2026_07_28.BooleanSchema", "v2026_07_28.CallToolResultResponse", + "v2026_07_28.ClientNotification", "v2026_07_28.CompleteResultResponse", "v2026_07_28.DiscoverResultResponse", "v2026_07_28.Error1", "v2026_07_28.Error2", + "v2026_07_28.Error3", "v2026_07_28.GetPromptResultResponse", + "v2026_07_28.HeaderMismatchError", "v2026_07_28.Icons", "v2026_07_28.InputSchema", "v2026_07_28.InternalError", @@ -111,9 +112,11 @@ "v2026_07_28.MetaObject", "v2026_07_28.MethodNotFoundError", "v2026_07_28.MissingRequiredClientCapabilityError", + "v2026_07_28.NotificationMetaObject", "v2026_07_28.NumberSchema", "v2026_07_28.OneOfItem", "v2026_07_28.OutputSchema", + "v2026_07_28.Params", "v2026_07_28.ParseError", "v2026_07_28.ReadResourceResultResponse", "v2026_07_28.RequestMetaObject", From 79fb5edb326882b6d291a89853d389ed9382ae25 Mon Sep 17 00:00:00 2001 From: Max Isbey <224885523+maxisbey@users.noreply.github.com> Date: Fri, 19 Jun 2026 14:40:41 +0000 Subject: [PATCH 2/2] Drop misleading parenthetical from the implementation-defined band comment CONNECTION_CLOSED and REQUEST_TIMEOUT can reach the wire via jsonrpc_dispatcher's _write_error path; the band is implementation-defined per spec, not SDK-internal-only. --- src/mcp/types/jsonrpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mcp/types/jsonrpc.py b/src/mcp/types/jsonrpc.py index f6799988d4..fcc3317d86 100644 --- a/src/mcp/types/jsonrpc.py +++ b/src/mcp/types/jsonrpc.py @@ -43,7 +43,7 @@ class JSONRPCResponse(BaseModel): # MCP error codes occupy the JSON-RPC server-error range -32000..-32099. # Per the 2026-07-28 spec's allocation policy: -# -32000..-32019 implementation-defined (SDK-local; never on the wire as a spec code) +# -32000..-32019 implementation-defined # -32020..-32099 reserved for spec-defined codes, allocated sequentially from -32020 # -32002, -32042 reserved-never-reused (retired by earlier protocol versions)