Skip to content

chore: update spec.types.ts from upstream#2027

Open
github-actions[bot] wants to merge 1 commit into
mainfrom
update-spec-types
Open

chore: update spec.types.ts from upstream#2027
github-actions[bot] wants to merge 1 commit into
mainfrom
update-spec-types

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented May 7, 2026

This PR updates packages/core/src/types/spec.types.ts from the Model Context Protocol specification.

Source file: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/de6d76fba3078eda957dadb3cec51ca8ab851b5c/schema/draft/schema.ts

This is an automated update triggered by the nightly cron job.

@github-actions github-actions Bot requested a review from a team as a code owner May 7, 2026 05:08
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 7, 2026

⚠️ No Changeset found

Latest commit: 879139a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions github-actions Bot force-pushed the update-spec-types branch from fe613a8 to 5f450ff Compare May 8, 2026 05:01
Comment on lines 3284 to +3291
| TaskStatusNotification;

/** @internal */
export type ClientResult =
| EmptyResult
| CreateMessageResult
| ListRootsResult
| ElicitResult
| GetTaskResult
| GetTaskPayloadResult
| ListTasksResult
| CancelTaskResult;
export type ClientResult = EmptyResult | GetTaskResult | GetTaskPayloadResult | ListTasksResult | CancelTaskResult;

/* Server messages */
/** @internal */
export type ServerRequest =
| PingRequest
| CreateMessageRequest
| ListRootsRequest
| ElicitRequest
| GetTaskRequest
| GetTaskPayloadRequest
| ListTasksRequest
| CancelTaskRequest;
export type ServerRequest = PingRequest | GetTaskRequest | GetTaskPayloadRequest | ListTasksRequest | CancelTaskRequest;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 The spec has restructured sampling/elicitation/roots out of the JSON-RPC request/response model: CreateMessageRequest/ElicitRequest/ListRootsRequest no longer extend JSONRPCRequest, their results no longer extend Result, the *ResultResponse wrappers are deleted, and all six are dropped from ServerRequest/ClientResult — they are now payloads embedded inside the new InputRequiredResult flow. The SDK still models these as standalone server→client wire requests (ServerRequestSchema/ClientResultSchema in schemas.ts:2107-2123, plus the server.createMessage()/elicitInput()/listRoots() APIs), so beyond the immediate spec.types.test.ts compile failures (lines 156/160/168/172/225/301/476/499 and hard references to the deleted SpecTypes.*ResultResponse types at 745-763 & 1019-1024), this sync leaves the SDK architecturally divergent from the protocol. Unlike the new-type additions, this isn't a mechanical "add schemas" fix — it requires redesigning the SDK's server-initiated-request APIs around InputRequiredResult/inputResponses, so this PR should be held until that companion work is ready.

Extended reasoning...

What changed

The upstream spec has fundamentally changed how a server obtains sampling, elicitation, and roots from the client. Previously these were ordinary server→client JSON-RPC requests: CreateMessageRequest, ElicitRequest, and ListRootsRequest each extended JSONRPCRequest, their results extended Result, each had a *ResultResponse envelope, and they were members of the ServerRequest / ClientResult unions. In this diff:

  • CreateMessageRequest / ElicitRequest / ListRootsRequest drop extends JSONRPCRequest (and their *Params drop TaskAugmentedRequestParams/RequestParams).
  • CreateMessageResult / ElicitResult / ListRootsResult drop extends Result.
  • CreateMessageResultResponse, ElicitResultResponse, ListRootsResultResponse are deleted.
  • All six are removed from the ServerRequest and ClientResult unions (lines 3284-3291).
  • They are now referenced only via the new InputRequest / InputResponse aliases, which are carried inside InputRequiredResult.inputRequests and echoed back via InputResponseRequestParams.inputResponses (or tasks/input_response).

In other words, the spec no longer models "server asks client for sampling" as a standalone JSON-RPC request — it is now an embedded sub-request inside a resultType: 'input_required' result that the client must fulfill and replay on the next call.

Immediate breakage in this PR

packages/core/test/spec.types.test.ts fails to compile in two distinct ways:

  1. Bidirectional assignability checks at lines 155-172, 223-225, 301, 476, 499 fail because the spec types no longer carry jsonrpc/id (requests) or the Result index signature (results), and the ServerRequest/ClientResult unions no longer line up with the SDK's.
  2. Hard references to deleted types: lines 745-763 and 1019-1024 reference SpecTypes.CreateMessageResultResponse, SpecTypes.ListRootsResultResponse, and SpecTypes.ElicitResultResponse, which no longer exist — these are unconditional TS2694/TS2339-class errors, not just assignability mismatches.

So the package will not build / CI will be red on merge.

Architectural divergence (why this isn't just a test fix)

The SDK runtime does not import spec.types.ts (only the test does), so src/ still typechecks — but that is precisely the problem the drift guard exists to surface. schemas.ts:2107 (ClientResultSchema) and schemas.ts:2119 (ServerRequestSchema) still include CreateMessageRequestSchema / ElicitRequestSchema / ListRootsRequestSchema and their results, and the public SDK surface (server.createMessage(), ctx.elicitInput(), roots listing) still issues these as wire-level JSON-RPC requests. After this sync, an SDK server speaking to a spec-compliant client would send a sampling/createMessage JSON-RPC request that the client is no longer obligated to handle as a top-level request; conversely, the SDK has no machinery to emit InputRequiredResult from a tool/prompt/resource handler or to accept inputResponses on the retry.

Step-by-step proof

  1. Before: spec.types.ts had interface CreateMessageRequest extends JSONRPCRequest { method: 'sampling/createMessage'; ... } and ServerRequest = PingRequest | CreateMessageRequest | ListRootsRequest | ElicitRequest | ....
  2. After (this diff, ~line 2311): interface CreateMessageRequest { method: 'sampling/createMessage'; params: ... } — no jsonrpc, no id. ServerRequest (line 3291) is now PingRequest | GetTaskRequest | GetTaskPayloadRequest | ListTasksRequest | CancelTaskRequest only.
  3. spec.types.test.ts:476 does (sdk: WithJSONRPCRequest<SDKTypes.CreateMessageRequest>, spec: SpecTypes.CreateMessageRequest) => { sdk = spec; }. spec has no jsonrpc/id, so sdk = spec fails: Type 'CreateMessageRequest' is missing the following properties: jsonrpc, id.
  4. spec.types.test.ts:756 does spec: SpecTypes.ListRootsResultResponse — that export no longer exists → Namespace has no exported member 'ListRootsResultResponse'.
  5. Meanwhile schemas.ts:2119-2123 still builds ServerRequestSchema from CreateMessageRequestSchema | ElicitRequestSchema | ListRootsRequestSchema | ..., so SDKTypes.ServerRequestSpecTypes.ServerRequest and the union check at ~499 fails too.

Why this is distinct from the "new types need schemas" comment

A separate comment notes that ResultType, InputRequiredResult, InputResponseRequestParams, TaskInputResponseRequest, etc. need new Zod schemas and specTypeSchema allowlist entries. That is additive, mechanical work. This finding is about the removal/restructuring of existing types and unions, whose remediation is not mechanical: you cannot simply add a schema — you have to (a) decide whether the SDK keeps server.createMessage()/elicitInput()/listRoots() as a compatibility shim or replaces them with an InputRequiredResult-returning API, (b) remove these from ServerRequestSchema/ClientResultSchema, and (c) rewrite the bidirectional test entries (drop the WithJSONRPCRequest<...> wrapper for these three, delete the *ResultResponse checks). The refutation that this is "the same finding" conflates detection mechanism (both trip spec.types.test.ts) with root cause and fix; reviewers need to see this called out separately because it is the part that requires API design, not a 10-line schema patch.

Recommended action

Hold this automated sync until a companion PR implements the InputRequiredResult flow in the SDK (new result/param schemas, updated ServerRequestSchema/ClientResultSchema, reworked createMessage/elicitInput/listRoots plumbing, and updated spec.types.test.ts entries for these six types plus removal of the three deleted *ResultResponse checks). Merging this alone breaks the build and publishes a spec snapshot the SDK cannot honor.

Comment on lines +393 to +418
export interface InputRequiredResult extends Result {
/* Requests issued by the server that must be complete before the
* client can retry the original request.
*/
inputRequests?: InputRequests;
/* Request state to be passed back to the server when the client
* retries the original request.
* Note: The client must treat this as an opaque blob; it must not
* interpret it in any way.
*/
requestState?: string;
}

/* Request parameter type that includes input responses and request state.
* These parameters may be included in any client-initiated request.
*/
export interface InputResponseRequestParams extends RequestParams {
/* New field to carry the responses for the server's requests from the
* InputRequiredResult message. For each key in the response's inputRequests
* field, the same key must appear here with the associated response.
*/
inputResponses?: InputResponses;
/* Request state passed back to the server from the client.
*/
requestState?: string;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 This spec sync adds 10 new Multi Round-Trip types (ResultType, InputRequest/InputResponse/InputRequests/InputResponses, InputRequiredResult, InputResponseRequestParams, TaskInputResponseRequest/TaskInputResponseRequestParams/TaskInputResponseResultResponse) and adds tasks/input_response to ClientRequest, but none of these have corresponding entries in schemas.ts, types.ts, or the sdkTypeChecks map — so spec.types.test.ts fails on the hardcoded type count (176 vs 182), the "comprehensive compatibility tests" coverage check, and the ClientRequest bidirectional assignability check. This automated cron PR needs companion SDK schema/type/test updates before it can land.

Extended reasoning...

What the bug is

This PR is the nightly automated sync of spec.types.ts from upstream. The upstream commit it pulls in introduces the new Multi Round-Trip machinery: ResultType, InputRequest, InputResponse, InputRequests, InputResponses, InputRequiredResult, InputResponseRequestParams, TaskInputResponseRequest, TaskInputResponseRequestParams, and TaskInputResponseResultResponse, plus a new tasks/input_response member of the ClientRequest union. None of these names appear anywhere else in packages/core/srcschemas.ts has no Zod schemas for them, types.ts has no SDK-side type aliases, and test/spec.types.test.ts has no sdkTypeChecks entries for them. The repo's spec↔SDK drift guard catches exactly this situation and fails the build.

Code path that triggers it

packages/core/test/spec.types.test.ts enforces three invariants that all break:

  1. Type count — line ~1089 hardcodes expect(specTypes).toHaveLength(176). After this sync the file exports 182 types (10 added, 4 removed: URLElicitationRequiredError, CreateMessageResultResponse, ListRootsResultResponse, ElicitResultResponse), so this assertion fails.
  2. Coverage check — the should have comprehensive compatibility tests test (lines ~1098-1108) parses every exported name from spec.types.ts, skips the MISSING_SDK_TYPES allowlist (currently only ['Error', 'URLElicitationRequiredError']), and asserts each remaining name has an entry in sdkTypeChecks. The 10 new types have no entry, so 10 assertions fail. Additionally, URLElicitationRequiredError was removed from the spec but is still in MISSING_SDK_TYPES, which will fail the "Missing SDK Types" suite that asserts every allowlisted name actually exists in the spec file.
  3. ClientRequest bidirectional checktsc shows the assignability check at ~line 496 failing because the spec ClientRequest union now contains TaskInputResponseRequest while the SDK ClientRequest union does not.

Why existing code doesn't prevent it

That's by design — spec.types.test.ts is the guard that turns an automated spec pull into a forcing function for SDK updates. The cron job only updates spec.types.ts; it does not (and cannot) generate the matching Zod schemas, SDK type aliases, or sdkTypeChecks entries. Note: the specTypeSchemas allowlist drift guard from #1993 (specTypeSchema.test.ts:162) does not trip here — that guard compares schemas.ts exports against the SPEC_SCHEMA_KEYS allowlist, not against spec.types.ts. The guard that trips is the runtime coverage check in spec.types.test.ts.

Step-by-step proof

Running cd packages/core && pnpm vitest run test/spec.types.test.ts on this branch:

  • should define some expected typesFAIL: expected 182 to be 176
  • should have comprehensive compatibility testsFAIL: ResultType, InputRequest, InputResponse, InputRequests, InputResponses, InputRequiredResult, InputResponseRequestParams, TaskInputResponseRequest, TaskInputResponseRequestParams, TaskInputResponseResultResponse each produce expected undefined to be defined

And tsc --noEmit on packages/core fails because spec.types.test.ts still references the now-removed CreateMessageResultResponse / ListRootsResultResponse / ElicitResultResponse / URLElicitationRequiredError (lines ~745-760, ~1019-1024, ~1069), and because spec.ClientRequest is no longer assignable to sdk.ClientRequest once TaskInputResponseRequest is in the union.

Impact

CI is red on this PR. Merging it as-is would break main. Beyond the test failures, downstream consumers using isSpecType / specTypeSchemas (#1887) would have no runtime validators for any of the new Multi Round-Trip messages, and the SDK ClientRequest union would be out of sync with the wire protocol.

How to fix

This needs a companion commit on top of the cron sync:

  • Add Zod schemas for ResultType, InputRequests, InputResponses, InputRequiredResult, InputResponseRequestParams, TaskInputResponseRequest, TaskInputResponseRequestParams, TaskInputResponseResultResponse (and InputRequest/InputResponse aliases) in schemas.ts, and register them in specTypeSchemas.
  • Add the corresponding SDK types in types.ts and add TaskInputResponseRequest to the SDK ClientRequest union.
  • In spec.types.test.ts: bump the toHaveLength(176) count, add sdkTypeChecks entries for all 10 new types, remove the entries/references for CreateMessageResultResponse / ListRootsResultResponse / ElicitResultResponse / URLElicitationRequiredError, and drop URLElicitationRequiredError from MISSING_SDK_TYPES.

(This is separate from the resultType required-field assignability breakage tracked elsewhere — even if resultType is made optional upstream, every failure listed here still occurs.)

Comment on lines 156 to 165
export interface Result {
_meta?: MetaObject;
/**
* Indicates the type of the result, which allows the client to determine
* how to parse the result object.
*
* @default "complete"
*/
resultType: ResultType;
[key: string]: unknown;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 The new resultType: ResultType field on the base Result interface is declared as required even though the JSDoc says @default "complete" — this makes every Result subtype (InitializeResult, CallToolResult, ReadResourceResult, GetPromptResult, EmptyResult, all the Paginated/Task results, etc.) require resultType. The SDK's ResultSchema in schemas.ts does not produce this field, so tsc --noEmit on @modelcontextprotocol/core now fails with 30+ TS2741 errors in spec.types.test.ts and CI will not pass. Given the @default annotation, the upstream spec almost certainly intended resultType?: ResultType; either the upstream schema.ts needs to be fixed before this is re-pulled, or this PR needs an accompanying SDK-side ResultSchema change before it can land.

Extended reasoning...

What the bug is

This automated spec sync adds a new field to the base Result interface in packages/core/src/types/spec.types.ts:

export interface Result {
    _meta?: MetaObject;
    /**
     * Indicates the type of the result, which allows the client to determine
     * how to parse the result object.
     *
     * @default "complete"
     */
    resultType: ResultType;
    [key: string]: unknown;
}

The field is declared without a ?, making it required on Result and on every interface that extends Result. The @default "complete" JSDoc strongly implies the spec author intended it to be optional and defaulted server-side, but the TypeScript declaration does not reflect that.

How it manifests / proof

The SDK maintains bidirectional-assignability checks between the spec types and the SDK's Zod-inferred types in packages/core/test/spec.types.test.ts (e.g. Result: (sdk, spec) => { sdk = spec; spec = sdk; }). The SDK's ResultSchema is defined in packages/core/src/types/schemas.ts:111 as:

export const ResultSchema = z.looseObject({
    _meta: ...
});

It has no resultType field, and a grep for resultType under packages/core/src finds it only in spec.types.ts — no SDK schema, handler, or result-construction site sets it.

Running pnpm tsc --noEmit -p packages/core/tsconfig.json on this branch produces 30+ errors of the form:

test/spec.types.test.ts: error TS2741: Property 'resultType' is missing in type
'{ _meta?: ...; }' but required in type 'Result'.

repeated for Result, EmptyResult, PaginatedResult, CompleteResult, ListToolsResult, CallToolResult, ListResourcesResult, ListResourceTemplatesResult, ReadResourceResult, ListPromptsResult, GetPromptResult, InitializeResult, CreateTaskResult, GetTaskResult, ListTasksResult, CancelTaskResult, GetTaskPayloadResult, JSONRPCResultResponse, JSONRPCMessage, ServerResult, ClientResult, and more.

Step-by-step

  1. spec.types.ts now says Result requires resultType: 'complete' | 'input_required'.
  2. schemas.ts infers the SDK Result type from ResultSchema = z.looseObject({ _meta: ... }), which has no resultType key.
  3. spec.types.test.ts does spec = sdk; for Result and every derived result type.
  4. TypeScript rejects the assignment because { _meta?: ... } lacks the required resultType property → TS2741.
  5. The same failure cascades to every extends Result interface and to JSONRPCResultResponse (whose result: Result field inherits the requirement).

Why existing code doesn't prevent it

Result has an index signature [key: string]: unknown, but index signatures do not satisfy explicitly-declared required properties — TypeScript still demands resultType be present by name. Nothing in this PR touches schemas.ts or any handler to add the field on the SDK side.

Impact

  • CI typecheck fails, so this PR cannot merge as-is.
  • Even if the typecheck were bypassed, every SDK consumer constructing a result literal (e.g. return { content: [...] } from a tool handler) would no longer satisfy Spec.CallToolResult, and downstream consumers typing against the spec types would see the same breakage.

How to fix

The cleanest fix is upstream: change the spec's schema.ts to resultType?: ResultType; (matching the @default "complete" semantics) and re-run pnpm run fetch:spec-types. If the spec genuinely intends the field to be required on the wire, then this PR must be accompanied by an SDK change that adds resultType: z.enum(['complete', 'input_required']).default('complete') (or equivalent) to ResultSchema and updates all result-construction sites — but that is a much larger, non-mechanical change that should not land in an automated nightly sync.

@github-actions github-actions Bot force-pushed the update-spec-types branch 2 times, most recently from a64a23e to af35bb5 Compare May 10, 2026 05:12
Comment on lines +2050 to +2056
export interface TaskInputResponseRequestParams extends RequestParams {
/**
* The client's responses to the server's input requests from
* the {@link InputRequiredResult} returned by {@link GetTaskPayloadRequest | tasks/result}.
*/
inputResponses: InputResponses;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 TaskInputResponseRequestParams carries only inputResponses — it has no taskId (and no requestState, since it extends bare RequestParams rather than InputResponseRequestParams), unlike every other tasks/* method (tasks/get, tasks/result, tasks/cancel), which all take { taskId: string }. A server with more than one task in input_required status has no spec-defined way to route the responses to the correct task. This is the same class of upstream schema.ts defect as the resultType-required finding — it should be fixed upstream (add taskId: string) and re-pulled before the SDK encodes this shape into a Zod schema.

Extended reasoning...

What the bug is

The new tasks/input_response request's params type is:

export interface TaskInputResponseRequestParams extends RequestParams {
    inputResponses: InputResponses;
}

RequestParams (spec.types.ts:112) contains only _meta?: RequestMetaObject, so the only data this request carries on the wire is { inputResponses, _meta? }. There is no taskId, and because it extends bare RequestParams rather than InputResponseRequestParams, there is also no requestState — so neither of the two routing mechanisms available elsewhere in the Multi Round-Trip flow is present here.

Why this is inconsistent with the rest of tasks/*

Every other task-targeting method in the spec identifies the task explicitly:

method params line
tasks/get { taskId: string } spec.types.ts:1957
tasks/result { taskId: string } spec.types.ts:1992
tasks/cancel { taskId: string } spec.types.ts:2078
tasks/input_response { inputResponses } — no taskId spec.types.ts:2050

The InputRequests JSDoc says only that keys are "server-assigned identifiers"; it does not require them to be globally unique across tasks. Relying on the server to embed task identity in those keys would be an undocumented implicit contract that contradicts the explicit-taskId convention used by every sibling method.

Step-by-step proof

  1. Client creates two tasks; both reach status: 'input_required'.
  2. Client calls tasks/result with { taskId: 'task-A' } → server returns InputRequiredResult { inputRequests: { 'req-1': <ElicitRequest> } }.
  3. Client calls tasks/result with { taskId: 'task-B' } → server returns InputRequiredResult { inputRequests: { 'req-1': <ElicitRequest> } } (nothing in the spec forbids reusing 'req-1' per-task).
  4. Client now sends tasks/input_response with params: { inputResponses: { 'req-1': <ElicitResult> } }.
  5. The server receives { inputResponses: { 'req-1': … } } and has no field telling it whether this fulfills task-A or task-B. InputRequiredResult itself carries no taskId, and TaskInputResponseRequestParams carries no taskId and no requestState. The request is unroutable by spec.

Why existing code doesn't prevent it

The synchronous path is fine: CallToolRequestParams / GetPromptRequestParams / ReadResourceRequestParams now extend InputResponseRequestParams, which carries requestState — the server can stash routing info there. But TaskInputResponseRequestParams extends RequestParams, not InputResponseRequestParams, so it gets neither requestState nor a taskId. And the SDK's task plumbing (packages/core/src/shared/taskManager.ts) keys everything by taskId, so when the companion work from comment #2 adds a tasks/input_response handler, it will have nothing to look up.

Impact

This doesn't independently break CI (unlike resultType-required), but it blocks correct implementation of the companion SDK work this PR demands. If the SDK adds TaskInputResponseRequestSchema matching this shape, it bakes an unroutable request into the public surface and will need a breaking change once upstream adds taskId.

How to fix

Same remediation path as the resultType-required finding: file upstream against modelcontextprotocol/schema/draft/schema.ts to add taskId: string to TaskInputResponseRequestParams (and likely requestState?: string, or have it extend InputResponseRequestParams), then re-run pnpm run fetch:spec-types. Don't add a Zod schema for this type in its current shape.

Comment on lines +406 to +418
/* Request parameter type that includes input responses and request state.
* These parameters may be included in any client-initiated request.
*/
export interface InputResponseRequestParams extends RequestParams {
/* New field to carry the responses for the server's requests from the
* InputRequiredResult message. For each key in the response's inputRequests
* field, the same key must appear here with the associated response.
*/
inputResponses?: InputResponses;
/* Request state passed back to the server from the client.
*/
requestState?: string;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Nit (upstream): the field/interface comments here use plain /* ... */ instead of /** ... */ JSDoc, so the TS language service / TypeDoc will not surface them — and InputResponseRequestParams ends up with no doc comment and no @category Multi Round-Trip tag at all, unlike every sibling type in this section. Since spec.types.ts is not re-exported from this package the SDK's own docs are unaffected, but it's worth folding into the same upstream schema.ts fix as the resultType issue so the spec repo's generated docs and the forthcoming hand-written schemas.ts mirror get proper descriptions.

Extended reasoning...

What the issue is

In the new Multi Round-Trip block, several comments use plain block-comment syntax /* ... */ instead of JSDoc syntax /** ... */:

  • InputRequiredResult.inputRequests (lines 394-396)
  • InputRequiredResult.requestState (lines 398-402)
  • the interface-level comment on InputResponseRequestParams (lines 406-408)
  • InputResponseRequestParams.inputResponses (lines 410-413)
  • InputResponseRequestParams.requestState (lines 415-416)

Because the opening delimiter is /* (single asterisk) rather than /**, TypeScript's language service and TypeDoc treat these as ordinary comments, not documentation. Additionally, since InputResponseRequestParams has no /** */ block at all, it also has no @category Multi Round-Trip tag — every other exported type in this section (InputRequests, InputResponses, InputRequiredResult, TaskInputResponseRequest, etc.) carries that tag.

Why this is an upstream slip, not intentional

This is not the file's convention. Every surrounding declaration in the same diff hunk uses proper /** ... */ JSDoc: ResultType (148-156), InputRequests (354-362), InputResponses (366-375), InputRequiredResult itself (380-392), TaskInputResponseRequest (2031-2039), TaskInputResponseRequestParams (2046-2056). The pre-existing /* Empty result */ and /* Cancellation */ lines are one-line section dividers, not API documentation, so they are not precedent for multi-line field descriptions. The five blocks above are the only multi-line API descriptions in the file using /* — a clear authoring inconsistency in the upstream commit.

Addressing the "not SDK-public" objection

It is true that spec.types.ts is not part of this SDK's public surface — packages/core/src/types/index.ts re-exports constants/enums/errors/guards/schemas/specTypeSchema/types but not spec.types, and the only importer in the package is test/spec.types.test.ts. So this has zero effect on the typescript-sdk's generated docs or consumer .d.ts, and CLAUDE.md's "JSDoc for public APIs" rule does not directly apply to this file in this repo.

The reason it is still worth a (nit-level) mention is that this file is a verbatim mirror of the spec repo's schema/draft/schema.ts, which is the source for the protocol's own TypeDoc site. In the upstream output, InputResponseRequestParams will render with no description and will be uncategorised (it will not appear under the "Multi Round-Trip" group), and the normative "client must treat this as an opaque blob" guidance on requestState will be invisible in IDE hover for anyone consuming the spec types. The fix lives in the same upstream file that already needs editing for the resultType? optionality issue flagged elsewhere on this PR, so the marginal cost of including it in that upstream report is near zero.

Step-by-step proof

  1. Hover InputResponseRequestParams at line 409 in VS Code → tooltip shows only interface InputResponseRequestParams extends RequestParams with no description, because lines 406-408 start with /* not /**.
  2. Hover InputRequiredResult at line 393 → tooltip shows the full "An InputRequiredResult sent by the server…" text, because lines 380-392 start with /**.
  3. Hover requestState at line 403 → no description; the "client must treat this as an opaque blob" note (398-402) is dropped.
  4. Run TypeDoc over upstream schema.tsInputRequests, InputResponses, InputRequiredResult are grouped under Multi Round-Trip; InputResponseRequestParams is not (no @category tag, because there is no JSDoc block to carry one).

How to fix

In upstream schema.ts, change /*/** on the five blocks listed above and add @category Multi Round-Trip to the InputResponseRequestParams doc comment, then re-run pnpm run fetch:spec-types. No change is appropriate in this repo directly (the file header says DO NOT EDIT). This is purely documentation rendering — it does not affect type-checking, the drift guard, or runtime behaviour — hence nit, raised only because an upstream schema.ts fix is already on the table for this sync.

Comment on lines +393 to +404
export interface InputRequiredResult extends Result {
/* Requests issued by the server that must be complete before the
* client can retry the original request.
*/
inputRequests?: InputRequests;
/* Request state to be passed back to the server when the client
* retries the original request.
* Note: The client must treat this as an opaque blob; it must not
* interpret it in any way.
*/
requestState?: string;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Upstream design gap worth flagging alongside the other schema.ts feedback: InputRequiredResult extends Result without redeclaring resultType: 'input_required', and none of the "complete" subtypes (CallToolResult/ReadResourceResult/GetPromptResult/GetTaskPayloadResult) narrow to resultType: 'complete' either — so the new result: CallToolResult | InputRequiredResult unions (lines 1122/1455/1650/2028) are not TypeScript discriminated unions and if (r.resultType === 'input_required') will not narrow. This is orthogonal to the "resultType is required" comment above (fixing one doesn't fix the other) and constrains the SDK from modeling these with z.discriminatedUnion('resultType', ...) while keeping the bidirectional spec↔SDK assignability check green.

Extended reasoning...

What the bug is

The spec introduces ResultType = 'complete' | 'input_required' and adds resultType: ResultType to the base Result interface (line 165), with the JSDoc explicitly stating its purpose is to "allow the client to determine how to parse the result object." It then defines InputRequiredResult extends Result (line 393) and unions it into four response envelopes — CallToolResultResponse.result: CallToolResult | InputRequiredResult (1650), and likewise for ReadResourceResultResponse (1122), GetPromptResultResponse (1455), and GetTaskPayloadResultResponse (2028).

However, InputRequiredResult does not redeclare resultType: 'input_required', and none of CallToolResult / ReadResourceResult / GetPromptResult / GetTaskPayloadResult redeclare resultType: 'complete'. A grep confirms resultType appears exactly once in spec.types.ts — only on the base Result. So every arm of every X | InputRequiredResult union has resultType typed as the full 'complete' | 'input_required', and TypeScript's discriminated-union narrowing does not engage.

Step-by-step proof

  1. Result.resultType: 'complete' | 'input_required' (line 165).
  2. InputRequiredResult extends Result { inputRequests?: ...; requestState?: ... } (line 393) — inherits resultType: 'complete' | 'input_required' unchanged.
  3. CallToolResult extends Result { content: ...; ... } — also inherits resultType: 'complete' | 'input_required' unchanged.
  4. Given declare const r: CallToolResult | InputRequiredResult:
    if (r.resultType === 'input_required') {
      r.inputRequests; // ❌ TS error: Property 'inputRequests' does not exist on type 'CallToolResult | InputRequiredResult'
    }
    TypeScript cannot eliminate CallToolResult from the union because CallToolResult['resultType'] also includes 'input_required'. Narrowing requires the discriminant property to have disjoint literal types across union members.
  5. Additionally, since Result carries [key: string]: unknown and InputRequiredResult's only additions (inputRequests?, requestState?) are optional, InputRequiredResult is structurally a subtype of CallToolResult — the union is effectively degenerate at the type level.

Why this is distinct from the existing "resultType is required" comment

The comment on line 165 is about resultType being declared required despite @default "complete", which breaks SDK→spec assignability for every result. This finding is about resultType not being narrowed on subtypes, which breaks discriminated-union narrowing within the spec types themselves. They are orthogonal: making resultType optional on Result does not give InputRequiredResult a narrowed discriminant, and adding resultType: 'input_required' to InputRequiredResult does not make the base field optional. Both should be raised upstream together.

Impact on the SDK

The companion schemas.ts work (already requested in another comment on this PR) will need to define InputRequiredResultSchema and the four result: X | InputRequiredResult unions. The natural Zod encoding is z.discriminatedUnion('resultType', [...]), which requires each arm to declare a z.literal(...) discriminant. But if the SDK narrows InputRequiredResultSchema to resultType: z.literal('input_required'), the spec→SDK direction of the bidirectional assignability check in spec.types.test.ts fails: the spec's InputRequiredResult['resultType'] is 'complete' | 'input_required', which is not assignable to the SDK's 'input_required'. So the SDK is forced to either (a) use a non-discriminated z.union and lose narrowing, or (b) narrow anyway and add a carve-out in the bidirectional test — neither is great, and both go away if upstream narrows the discriminant.

At runtime the wire-level discriminator still works (a client can string-compare resultType and cast), so this is a type-ergonomics / SDK-modeling defect rather than a protocol-correctness one.

How to fix

Upstream in schema.ts: add resultType: 'input_required'; to InputRequiredResult, and add resultType?: 'complete'; (or required 'complete', depending on how the optionality question is resolved) to each concrete "complete" result that participates in an | InputRequiredResult union — at minimum CallToolResult, ReadResourceResult, GetPromptResult, GetTaskPayloadResult. Then re-run pnpm run fetch:spec-types. Batch this with the resultType? optionality fix and the other upstream feedback already noted on this PR.

@github-actions github-actions Bot force-pushed the update-spec-types branch from af35bb5 to 879139a Compare May 11, 2026 05:19
Comment on lines +339 to +349
/* Empty result */
/**
* A result that indicates success but carries no data.
*
* @category Common Types
*/
export type EmptyResult = Result;

/** @internal */
export type InputRequest = CreateMessageRequest | ListRootsRequest | ElicitRequest;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 🔴 The spec deletes URL_ELICITATION_REQUIRED = -32042 / URLElicitationRequiredError (replaced by the InputRequiredResult flow), but the SDK still ships this mechanism as public API: ProtocolErrorCode.UrlElicitationRequired (enums.ts:15), the UrlElicitationRequiredError class + ProtocolError.fromError() special-case (errors.ts:23-48), the public re-export (exports/public/index.ts:103), the tool-handler rethrow at packages/server/src/server/mcp.ts:211, and the examples/{client,server}/src/elicitationUrlExample.ts apps (plus docs/README links and test/integration/test/server/mcp.test.ts:1892-1937). None of these import spec.types.ts, so unlike the other findings on this PR they are silent — no typecheck or drift-guard failure surfaces them. The companion work needs to deprecate/remove the public error class + enum member (breaking change → migration.md), strip the mcp.ts special-case, and rewrite/remove the elicitationUrlExample apps.

Extended reasoning...

What changed in the spec

This sync deletes the entire -32042 error-response mechanism from the protocol. Before, the spec defined an implementation-specific JSON-RPC error code URL_ELICITATION_REQUIRED = -32042 and a URLElicitationRequiredError interface (a JSONRPCErrorResponse whose error.data.elicitations carried ElicitRequestURLParams[]). A server could respond to tools/call with this error to demand that the client open a browser URL before retrying. The diff removes both the constant and the interface; the replacement is the new InputRequiredResult result (with resultType: 'input_required' and inputRequests), which is delivered as a successful response, not an error. The error path is gone from the protocol entirely.

What the SDK still ships

The SDK implemented the -32042 flow end-to-end and exported it publicly. All of the following survive untouched after this PR:

  • packages/core/src/types/enums.ts:15ProtocolErrorCode.UrlElicitationRequired = -32_042, an error code the spec no longer defines.
  • packages/core/src/types/errors.ts:21-48ProtocolError.fromError() special-cases code -32042 and constructs a UrlElicitationRequiredError; the UrlElicitationRequiredError class itself wraps elicitations: ElicitRequestURLParams[] into error.data.
  • packages/core/src/exports/public/index.ts:103export { ProtocolError, UrlElicitationRequiredError } puts the class on the package's public surface.
  • packages/server/src/server/mcp.ts:211-213 — the tools/call handler catches thrown errors and, if error.code === ProtocolErrorCode.UrlElicitationRequired, rethrows so the framework serialises it onto the wire as a JSON-RPC error response instead of wrapping it into a CallToolResult with isError: true. This is runtime behaviour that emits a non-spec error code.
  • examples/server/src/elicitationUrlExample.ts:58,102 — tool handlers throw new UrlElicitationRequiredError([...]).
  • examples/client/src/elicitationUrlExample.ts:26,741 — client catches UrlElicitationRequiredError and drives the browser flow.
  • test/integration/test/server/mcp.test.ts:1892-1937 — integration test asserting the round-trip.
  • docs/client.md:471,626, docs/server.md:477, examples/{client,server}/README.md — documentation linking to the example apps.

Why this is silent (and distinct from the other comments)

Every other finding on this PR is surfaced by spec.types.test.ts or tsc because the affected code references spec.types.ts. This one is not: enums.ts, errors.ts, mcp.ts, and the example apps do not import spec.types.ts. The enum member is a hand-written numeric literal (-32_042); the error class is a hand-written subclass of ProtocolError; the mcp.ts rethrow checks the enum, not the spec interface. Nothing in CI fails when URL_ELICITATION_REQUIRED and URLElicitationRequiredError are removed from spec.types.ts. A reviewer addressing only the existing comments would fix the schemas, the unions, and the test allowlists — and ship a release that still publicly exports an error class for a protocol mechanism that no longer exists.

This is also not a duplicate of the existing comments. Comment #3206453743 (line 3291) covers the server→client request restructuring (createMessage/elicitInput/listRoots becoming InputRequiredResult payloads) and its remediation list is scoped to ServerRequestSchema/ClientResultSchema and the server.createMessage()/elicitInput()/listRoots() APIs — it never mentions the -32042 error-response path, which is a completely separate code path (a tool handler throws, mcp.ts rethrows onto the wire as a JSON-RPC error). Comment #3206453749 (line 418) mentions URLElicitationRequiredError only as a stale entry in the MISSING_SDK_TYPES allowlist affecting the spec.types.test.ts type count; it does not mention enums.ts, errors.ts, the public re-export, mcp.ts:211, or the example apps.

Step-by-step proof

  1. Before (spec.types.ts pre-diff): export const URL_ELICITATION_REQUIRED = -32042; and export interface URLElicitationRequiredError extends Omit<JSONRPCErrorResponse, 'error'> { error: Error & { code: typeof URL_ELICITATION_REQUIRED; data: { elicitations: ElicitRequestURLParams[]; ... } } }.
  2. After (this diff): both are deleted; the only remaining server-side mechanism for "need browser-based input before continuing" is to return { resultType: 'input_required', inputRequests: { ... } } as a successful result.
  3. SDK runtime (mcp.ts:200-215): a tool handler runs, throws new UrlElicitationRequiredError([...]). The catch block at line 211 sees error.code === ProtocolErrorCode.UrlElicitationRequired (i.e., -32_042) and rethrows. Protocol then serialises this into a JSON-RPC error response with error.code = -32042 and error.data.elicitations = [...].
  4. Wire: the SDK is now emitting an error code and error-data shape that the protocol no longer defines. A spec-compliant client built against the post-sync schema has no URLElicitationRequiredError type to deserialise this into and no -32042 constant to switch on.
  5. Public surface: import { UrlElicitationRequiredError } from '@modelcontextprotocol/sdk' still works (exports/public/index.ts:103), and examples/server/src/elicitationUrlExample.ts actively demonstrates throwing it from a tool handler — teaching consumers a pattern the protocol just removed.
  6. No CI signal: grep -r 'spec.types' packages/core/src/types/{enums,errors}.ts packages/server/src/server/mcp.ts returns nothing — these files have no compile-time link to the spec snapshot, so neither tsc nor spec.types.test.ts flags them.

Impact

After this sync the SDK publicly exports, documents, demonstrates in two example apps, and special-cases at runtime a protocol mechanism that the spec has removed. New consumers following docs/server.md and elicitationUrlExample.ts will build servers that emit non-spec error responses; spec-compliant clients will see an unrecognised -32042 error instead of an InputRequiredResult they know how to fulfil.

How to fix

This is non-mechanical, public-API companion work that must accompany the sync (or its companion PR):

  • Decide deprecate-vs-remove for UrlElicitationRequiredError and ProtocolErrorCode.UrlElicitationRequired. Either remove them outright (breaking → add a migration.md entry and a major changeset) or mark them @deprecated with JSDoc pointing at the InputRequiredResult replacement and schedule removal.
  • Strip the mcp.ts:211-213 rethrow special-case (and the ProtocolError.fromError() branch at errors.ts:23-28).
  • Rewrite or remove examples/{client,server}/src/elicitationUrlExample.ts to use the InputRequiredResult / inputResponses flow (this dovetails with the redesign already requested in comment #3206453743), and update the four README/docs links that point at them.
  • Delete test/integration/test/server/mcp.test.ts:1892-1937 (or rewrite it for the new flow).

* @category `sampling/createMessage`
*/
export interface CreateMessageRequestParams extends TaskAugmentedRequestParams {
export interface CreateMessageRequestParams {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Upstream spec inconsistency to batch with the other schema.ts feedback: this sync strips extends TaskAugmentedRequestParams from CreateMessageRequestParams/ElicitRequestFormParams/ElicitRequestURLParams (and extends JSONRPCRequest from their requests), so task-augmented sampling/elicitation no longer exists in the protocol — yet ClientCapabilities.tasks.requests.{sampling.createMessage, elicitation.create} (spec.types.ts:628-647, untouched by this diff) still advertise support for it. The SDK mirrors these flags in ClientTasksCapabilitySchema (schemas.ts:349-365) and gates on them at runtime in assertClientRequestTaskCapability (experimental/tasks/helpers.ts:78-96), so after the InputRequiredResult redesign those schema fields and switch arms become unreachable. Should be raised upstream so schema.ts drops tasks.requests.{sampling,elicitation}, after which the SDK can prune the dead capability schema and helper branches.

Extended reasoning...

What the bug is

This sync removes the mechanism for task-augmented sampling/elicitation but leaves the capability advertisement for it in place. Concretely, the diff drops extends TaskAugmentedRequestParams from CreateMessageRequestParams (line 2245), ElicitRequestFormParams (2883) and ElicitRequestURLParams (2916), and drops extends JSONRPCRequest from CreateMessageRequest/ElicitRequest/ListRootsRequest — sampling and elicitation are no longer wire-level requests at all, and can no longer carry a params.task field. They now exist solely as payloads embedded inside InputRequiredResult.inputRequests (per comment #1).

But ClientCapabilities.tasks.requests at spec.types.ts:628-647 — not touched by this diff — still defines:

requests?: {
    sampling?: {
        /** Whether the client supports task-augmented `sampling/createMessage` requests. */
        createMessage?: JSONObject;
    };
    elicitation?: {
        /** Whether the client supports task-augmented elicitation/create requests. */
        create?: JSONObject;
    };
};

These two are the only members of tasks.requests, and their JSDoc explicitly references "task-augmented sampling/elicitation requests" — a flow this same upstream commit just deleted. The spec is now internally inconsistent: it advertises a capability for a mechanism it no longer defines.

Why this isn't just dead prose — concrete SDK surface affected

The SDK mirrors and acts on these flags:

  • ClientTasksCapabilitySchema (schemas.ts:349-365) defines requests.sampling.createMessage / requests.elicitation.create as Zod schema fields.
  • assertClientRequestTaskCapability (experimental/tasks/helpers.ts:69-97) switches on method, with arms for 'sampling/createMessage' (checks requests.sampling?.createMessage, line 79-87) and 'elicitation/create' (checks requests.elicitation?.create, line 89-96), throwing SdkErrorCode.CapabilityNotSupported if absent.
  • That helper is called from server.ts:414 and client.ts:709, and re-exported from the public surface (exports/public/index.ts:122).

After the InputRequiredResult redesign, no caller can ever reach those switch arms with a task-augmented request — CreateMessageRequest/ElicitRequest are no longer JSON-RPC requests, so the SDK will never dispatch them through the path that calls assertClientRequestTaskCapability. The capability flags, the schema fields, and the two switch arms all become unreachable protocol surface.

Step-by-step proof

  1. Before this sync: CreateMessageRequestParams extends TaskAugmentedRequestParams → a server could send { method: 'sampling/createMessage', params: { task: {...}, messages: [...] } } as a wire request. The client advertises support via capabilities.tasks.requests.sampling.createMessage = {}, and assertClientRequestTaskCapability(caps.tasks?.requests, 'sampling/createMessage', ...) validates it before sending.
  2. After this sync: CreateMessageRequestParams has no extends clause (line 2245) → no task field. CreateMessageRequest has no extends JSONRPCRequest (line 2314) → not a wire request. It is now only reachable as a value inside InputRequests (line 348: InputRequest = CreateMessageRequest | ListRootsRequest | ElicitRequest), which is embedded in InputRequiredResult.inputRequests.
  3. Yet ClientCapabilities.tasks.requests.sampling.createMessage (line 636) is unchanged. There is no longer any request shape in the spec for which this flag could meaningfully gate behaviour.
  4. Therefore assertClientRequestTaskCapability's case 'sampling/createMessage' and case 'elicitation/create' arms (helpers.ts:79-96) can never be reached by a spec-compliant flow, and ClientTasksCapabilitySchema.requests (schemas.ts:349-365) validates fields the protocol no longer uses.

Why this is distinct from the existing comments

Comment #1 covers the SDK's request/result-handler divergence (ServerRequestSchema/ClientResultSchema, server.createMessage()/elicitInput() APIs) — i.e., the SDK still treating these as wire requests. This finding is about the spec being internally inconsistent with itself: the same upstream commit that removed task-augmented sampling/elicitation left the capability advertisement for it in ClientCapabilities. None of comments #1-#6 mention ClientCapabilities.tasks.requests or assertClientRequestTaskCapability. It's the same class of upstream-defect feedback as comment #4 (taskId missing) and #6 (discriminant not narrowed): doesn't break CI (all fields optional), but identifies something schema.ts should fix before the SDK encodes the new shape.

Impact

No CI breakage — tasks.requests and its children are all optional, and this diff doesn't touch ClientCapabilities, so the bidirectional assignability check still passes for that type. But it leaves orphaned protocol surface that will confuse implementers (a client setting tasks.requests.sampling.createMessage is now a no-op), and it identifies concrete SDK code that the companion work should prune rather than carry forward.

How to fix

Upstream in schema.ts: drop requests.sampling and requests.elicitation from ClientCapabilities.tasks (which empties tasks.requests entirely — it can be removed too unless other request types are planned). Batch with the resultType? (#3), taskId (#4), JSDoc (#5), and discriminant-narrowing (#6) feedback. Once re-pulled, the SDK companion work can delete ClientTasksCapabilitySchema.shape.requests (schemas.ts:349-365) and the 'sampling/createMessage' / 'elicitation/create' arms of assertClientRequestTaskCapability (helpers.ts:79-96).

Comment on lines +348 to +351
export type InputRequest = CreateMessageRequest | ListRootsRequest | ElicitRequest;

/** @internal */
export const URL_ELICITATION_REQUIRED = -32042;
export type InputResponse = CreateMessageResult | ListRootsResult | ElicitResult;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Upstream design gap to batch with the other schema.ts feedback: InputResponse = CreateMessageResult | ListRootsResult | ElicitResult admits only success payloads, and the InputResponseRequestParams.inputResponses comment says every inputRequests key "must appear here". In the pre-sync model these were full JSON-RPC requests so a client could return a JSONRPCErrorResponse for user-denied sampling / LLM provider error / no roots configured; that error channel is gone with no replacement (ElicitResult has action: 'decline'|'cancel', but CreateMessageResult and ListRootsResult have no refusal field). The SDK redesign called for in the earlier comment will have no spec-defined way to propagate per-request failures and would have to invent an out-of-spec workaround that breaks once upstream adds a real error variant — suggest upstream add an InputResponseError arm (mirroring the JSON-RPC {code, message, data} shape) or relax the must-appear rule.

Extended reasoning...

What the bug is

InputResponse (spec.types.ts:351) is defined as CreateMessageResult | ListRootsResult | ElicitResult — a union of three success payloads only. InputResponses (line 376-378) is { [key: string]: InputResponse }, and the normative-sounding comment on InputResponseRequestParams.inputResponses (lines 410-413) says: "For each key in the response's inputRequests field, the same key must appear here with the associated response." So per the spec text, the client must supply a value for every requested key, and that value must be one of the three success result shapes.

In the pre-sync model these three exchanges were ordinary server→client JSON-RPC requests (CreateMessageRequest extends JSONRPCRequest, etc.), so a client that could not or would not fulfil one returned a JSONRPCErrorResponse carrying { code: number; message: string; data?: unknown }. By stripping extends JSONRPCRequest / extends Result and embedding the exchange inside the InputRequests/InputResponses maps, the spec discarded that error channel without adding a replacement.

Why the existing types don't cover it

  • ElicitResult happens to carry action: 'accept' | 'decline' | 'cancel', so elicitation can encode refusal in-band.
  • CreateMessageResult (line ~2335) is SamplingMessage & { model: string; stopReason?: ... } — required model/role/content, no refusal/error field, no index signature now that extends Result is dropped.
  • ListRootsResult (line ~2826) is bare { roots: Root[] } — same story.

The asymmetry (only ElicitResult has a decline arm) suggests this is an oversight rather than an intentional "refusal-via-abandonment" design — if abandoning the retry were the intended refusal signal, ElicitResult would not need action: 'decline' either.

Step-by-step proof

  1. Server returns CallToolResultResponse with result: InputRequiredResult { inputRequests: { 's1': <CreateMessageRequest>, 'e1': <ElicitRequest> } }.
  2. Client presents the elicitation; user accepts → { action: 'accept', content: {...} }.
  3. Client attempts the sampling call; the LLM provider returns HTTP 429 / content-policy refusal / the user denies the sampling-consent prompt.
  4. Client must now construct inputResponses for the retry. Per lines 410-413, both 's1' and 'e1' must appear. 'e1' is fine. For 's1' the only spec-permitted shapes are CreateMessageResult | ListRootsResult | ElicitResult — none of which can express "this request failed with code X / message Y".
  5. The client's only options are: (a) omit 's1' — textually non-compliant with the must-appear rule; (b) abandon the whole retry / call tasks/cancel — loses the successful elicitation and gives the server no error code/message to act on; (c) stuff a fake CreateMessageResult or an off-spec { error: {...} } object into 's1' — out of spec.

Why this matters for the SDK companion work

Today a client-side setRequestHandler(CreateMessageRequestSchema, …) can throw / reject and the SDK serialises a JSONRPCErrorResponse; the server's await ctx.sample() rejects with a ProtocolError carrying the code and message, and the tool handler can catch it and fall back. The redesign called for in comment #1 must preserve some equivalent of those semantics, but there is no spec-defined wire shape to carry them. Whatever workaround the SDK picks (drop the key, cancel the whole flow, invent an envelope) becomes public API that breaks once upstream adds a real error variant.

This is distinct from comment #1 (about the SDK still issuing these as wire requests), comment #4 (about tasks/input_response lacking taskId), and comment #6 (about the resultType discriminant) — none of those address the missing error arm in the InputResponse payload union itself.

Addressing "all-or-nothing is the intended design"

It is plausible the spec intends atomic fulfilment (client either satisfies every input or abandons). But (a) that loses the error code/message — the server cannot distinguish rate-limit vs. content-policy vs. user-denied, which is a real expressiveness regression vs. the JSON-RPC model; (b) it is contradicted by ElicitResult.action: 'decline'|'cancel' existing at all; and (c) even under that reading, the SDK still has to pick a public error-propagation contract for ctx.sample() rejection in the new flow, and it is better to do so against a spec-defined error variant than to guess.

How to fix

Raise upstream alongside the resultType? / taskId / discriminant fixes: either add an error arm, e.g.

export interface InputResponseError {
    code: number;
    message: string;
    data?: unknown;
}
export type InputResponse = CreateMessageResult | ListRootsResult | ElicitResult | InputResponseError;

(mirroring the removed JSON-RPC Error shape), or relax the "must appear" wording on inputResponses and define omission as refusal. Then re-run pnpm run fetch:spec-types. Don't encode InputResponsesSchema in schemas.ts until this is settled. Filed as a nit because it doesn't independently break CI and abandon-the-flow is a viable (if lossy) fallback — same tier as comments #5/#6.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants