Align draft schema with spec docs (subscriptionId, HeaderMismatch, notification directions)#2889
Align draft schema with spec docs (subscriptionId, HeaderMismatch, notification directions)#2889pja-ant wants to merge 6 commits into
Conversation
- Declare the reserved io.modelcontextprotocol/subscriptionId _meta key via a new NotificationMetaObject type, including the rule for deriving the value from the subscriptions/listen request's JSON-RPC ID - Rewrite the three list_changed notification doc comments to reflect the opt-in subscriptions model instead of unsolicited delivery - Add the HEADER_MISMATCH (-32001) error code and HeaderMismatchError type required by the Streamable HTTP transport's header validation - Update cacheScope JSDoc to the authorization-context caching model used by the caching utility doc - Make CancelledNotificationParams.requestId required - Describe cancellation as client-initiated, with one server-side use: on stdio a server sends notifications/cancelled solely to terminate a subscriptions/listen stream - Give ListRootsRequest a minimal params shape instead of RequestParams, matching other server-initiated input requests - Remove ProgressNotification from ClientNotification: only clients issue requests, so only servers report progress Regenerated schema.json and schema.mdx.
- Cancellation: describe cancellation as client-to-server, with the server-side exception for subscriptions/listen stream teardown on stdio - Progress: describe progress notifications as server-to-client only, and use Client/Server in the sequence diagram - Subscriptions: state how io.modelcontextprotocol/subscriptionId is derived from the subscriptions/listen request's JSON-RPC ID (decimal string for numeric IDs, verbatim for string IDs)
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
| cancellation flows in one direction as well: the client sends a cancellation notification | ||
| to indicate that a request it previously issued should be terminated. | ||
|
|
||
| There is one exception: on stdio, a server **MUST** send `notifications/cancelled` |
There was a problem hiding this comment.
Just being explicit about the exception it took me a couple reads to figure it out.
| There is one exception: on stdio, a server **MUST** send `notifications/cancelled` | |
| There is one exception, where cancellations are sent from server to client: on stdio, a server **MUST** send `notifications/cancelled` |
There was a problem hiding this comment.
Also why do we do this? seems a bit odd to only support this in STDIO @kurtisvg is the complexity of the asymmetry high value enough to have this?
There was a problem hiding this comment.
On HTTP you just close the connection.
| cancellation flows in one direction as well: the client sends a cancellation notification | ||
| to indicate that a request it previously issued should be terminated. | ||
|
|
||
| There is one exception: on stdio, a server **MUST** send `notifications/cancelled` |
There was a problem hiding this comment.
Also why do we do this? seems a bit odd to only support this in STDIO @kurtisvg is the complexity of the asymmetry high value enough to have this?
| * request that opened the stream: the decimal string representation of the | ||
| * ID if it is a number, or the ID verbatim if it is a string. | ||
| */ | ||
| "io.modelcontextprotocol/subscriptionId"?: string; |
There was a problem hiding this comment.
Why is this optional shouldn't it be required?
| "io.modelcontextprotocol/subscriptionId"?: string; | |
| "io.modelcontextprotocol/subscriptionId": string; |
There was a problem hiding this comment.
Not all notifications need this (e.g. a progress notification on a regular tool call).
… and error example - Make the server-to-client direction of the stdio cancellation exception explicit in cancellation.mdx - Document why io.modelcontextprotocol/subscriptionId is optional on NotificationMetaObject (the type covers all notifications, not just subscription-stream deliveries) - Add a HeaderMismatchError example so -32001 renders in the schema Error section
| - The request cannot be cancelled | ||
| 1. The sender of the cancellation notification **SHOULD** ignore any response to the | ||
| request that arrives afterward | ||
| 1. The client **SHOULD** ignore any response to the cancelled request that arrives |
There was a problem hiding this comment.
maybe, but things like "afterwards" are always tricky in distributed systems - e.g. sending cancellation at the same time that a response starts its handler.
| `subscriptions/listen` request that opened the stream. The value is always a string, | ||
| derived from the JSON-RPC ID of the `subscriptions/listen` request: the decimal string | ||
| representation of the ID if it is a number, or the ID verbatim if it is a string. In | ||
| the examples above, the request used `"id": 1`, so the acknowledgment and all | ||
| subsequent notifications carry the subscription ID `"1"`. On stdio, where all messages |
There was a problem hiding this comment.
Why is it alwys a string and not of the same type as the id field (so RequestId = number | string)?
There was a problem hiding this comment.
Hmm, yeah that's probably better. The examples in the spec where using id as number and subscriptionId as string, but those should be updated instead.
Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com>
The _meta value is now typed as RequestId (string | number) instead of string, so no numeric-to-string conversion rule is needed. Update the subscriptions and resources doc examples to show a numeric ID passed through unchanged, and fix a verb agreement typo in cancellation.mdx.
What this does
Fixes a set of inconsistencies between
schema/draft/schema.ts(the source of truth) and the draft specification prose, found during a pre-release consistency review.Why now
The draft is heading to release; each of these is a place where an implementer reading the schema and one reading the prose would build incompatible behavior.
Changes
io.modelcontextprotocol/subscriptionId_metakey in a newNotificationMetaObjecttype, and specify the derivation rule (decimal string of a numeric JSON-RPC ID, verbatim for a string ID). Previously the key existed only in prose and one example, and the docs showed numeric id1acknowledged as string"1"with no conversion rule — a strict comparison silently drops all subscription notifications on stdio.*ListChangedNotificationdoc comments that still said notifications "may be issued by servers without any previous subscription", contradicting the opt-in subscriptions model (servers MUST NOT send unrequested types).HEADER_MISMATCH = -32001and aHeaderMismatchErrorinterface. The transport requires servers to emit this code, but the schema had no definition, so generated SDKs could not recognize it.cacheScope: "private"doc comment with caching.mdx's authorization-context model (the two previously described different cache-sharing rules — a security boundary).CancelledNotificationParams.requestIdrequired (the optionality was a leftover from removed task cancellation, and an id-less cancellation has no defined meaning).ListRootsRequestminimal params instead ofRequestParams, which forced servers to fabricate client-identifying_metaon a server-initiated input request.ProgressNotificationfromClientNotification(clients no longer receive requests, so they have nothing to report progress on).CancelledNotificationstays inServerNotificationbut is now scoped: servers send it solely to terminate asubscriptions/listenstream on stdio, matching the teardown rule from (chore): sep-to-spec consistency pass #2863. cancellation.mdx and progress.mdx are rewritten to match the directional model.Verification
npm run generate:schemaartifacts committed;npm run prepandnpm run checkpass clean.