From ff429d57da2a7b4c243c6a7ee3643bf824f7551f Mon Sep 17 00:00:00 2001 From: rechedev9 Date: Mon, 9 Mar 2026 23:23:07 +0100 Subject: [PATCH 1/4] test: add compile-time key-parity assertions for spec type checks Add 151 compile-time key-parity assertions to spec.types.test.ts that verify SDK and spec types have exactly the same set of named property keys. This catches missing .optional() parameters that the existing mutual-assignability checks cannot detect due to TypeScript structural subtyping. Introduces three utility types: - KnownKeys: strips index signatures, keeps named keys only - AssertExactKeys: resolves to true on match, descriptive error type on mismatch - Assert: constraint that forces compile error Coverage: 151 of 174 sdkTypeChecks entries. 23 exclusions documented (15 union types, 8 primitive aliases). Discovered 7 genuine SDK/spec key mismatches, documented with @ts-expect-error annotations as a living drift inventory. Closes #1298 --- packages/core/test/spec.types.test.ts | 226 ++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) diff --git a/packages/core/test/spec.types.test.ts b/packages/core/test/spec.types.test.ts index da6fab109..c47888390 100644 --- a/packages/core/test/spec.types.test.ts +++ b/packages/core/test/spec.types.test.ts @@ -763,6 +763,232 @@ const sdkTypeChecks = { } }; +// --------------------------------------------------------------------------- +// Key-level assertions: verify that each SDK type and its corresponding spec +// type expose exactly the same set of named property keys. This catches cases +// where a Zod schema marks a field as `.optional()` but the spec does not (or +// vice-versa), which the mutual-assignability checks above cannot detect +// because optional fields satisfy structural subtyping in both directions. +// --------------------------------------------------------------------------- + +/** Strip index signatures, keeping only explicitly-named keys. */ +type KnownKeys = keyof { + [K in keyof T as string extends K ? never + : number extends K ? never + : symbol extends K ? never + : K]: T[K]; +}; + +/** + * Assert that A and B have exactly the same set of known (named) keys. + * Resolves to `true` on match; a descriptive error type on mismatch. + */ +type AssertExactKeys< + A, B, + Extra extends PropertyKey = Exclude, KnownKeys>, + Missing extends PropertyKey = Exclude, KnownKeys> +> = [Extra, Missing] extends [never, never] + ? true + : { _brand: 'KeyMismatch'; extra: Extra; missing: Missing }; + +/** Constraint: T must resolve to `true`. */ +type Assert = T; + +/* + * Excluded from key-level assertions (23 entries): + * + * Union types — KnownKeys cannot meaningfully enumerate their members (15): + * ClientRequest, ServerRequest, ClientNotification, ServerNotification, + * ClientResult, ServerResult, JSONRPCMessage, JSONRPCResponse, ContentBlock, + * SamplingMessageContentBlock, ElicitRequestParams, PrimitiveSchemaDefinition, + * SingleSelectEnumSchema, MultiSelectEnumSchema, EnumSchema + * + * Primitive type aliases — no object keys to compare (8): + * JSONValue, JSONArray, Role, LoggingLevel, ProgressToken, RequestId, + * Cursor, TaskStatus + */ + +// -- Simple types (96) -- + +type _K_RequestParams = Assert>; +type _K_NotificationParams = Assert>; +type _K_CancelledNotificationParams = Assert>; +type _K_InitializeRequestParams = Assert>; +type _K_ProgressNotificationParams = Assert>; +type _K_ResourceRequestParams = Assert>; +type _K_ReadResourceRequestParams = Assert>; +type _K_SubscribeRequestParams = Assert>; +type _K_UnsubscribeRequestParams = Assert>; +type _K_ResourceUpdatedNotificationParams = Assert>; +type _K_GetPromptRequestParams = Assert>; +type _K_CallToolRequestParams = Assert>; +type _K_SetLevelRequestParams = Assert>; +type _K_LoggingMessageNotificationParams = Assert>; +type _K_CreateMessageRequestParams = Assert>; +type _K_CompleteRequestParams = Assert>; +type _K_ElicitRequestFormParams = Assert>; +type _K_ElicitRequestURLParams = Assert>; +type _K_PaginatedRequestParams = Assert>; +type _K_BaseMetadata = Assert>; +type _K_Implementation = Assert>; +type _K_PaginatedResult = Assert>; +type _K_ListRootsResult = Assert>; +type _K_Root = Assert>; +type _K_ElicitResult = Assert>; +type _K_CompleteResult = Assert>; +type _K_Request = Assert>; +type _K_Result = Assert>; +type _K_JSONRPCRequest = Assert>; +type _K_JSONRPCNotification = Assert>; +type _K_EmptyResult = Assert>; +type _K_Notification = Assert>; +type _K_ResourceTemplateReference = Assert>; +// @ts-expect-error Genuine mismatch: SDK PromptReference is missing 'title' from spec +type _K_PromptReference = Assert>; +type _K_ToolAnnotations = Assert>; +type _K_Tool = Assert>; +type _K_ListToolsResult = Assert>; +type _K_CallToolResult = Assert>; +type _K_ListResourcesResult = Assert>; +type _K_ListResourceTemplatesResult = Assert>; +type _K_ReadResourceResult = Assert>; +type _K_ResourceContents = Assert>; +type _K_TextResourceContents = Assert>; +type _K_BlobResourceContents = Assert>; +// @ts-expect-error Genuine mismatch: SDK Resource is missing 'size' from spec +type _K_Resource = Assert>; +// @ts-expect-error Genuine mismatch: SDK PromptArgument is missing 'title' from spec +type _K_PromptArgument = Assert>; +type _K_Prompt = Assert>; +type _K_ListPromptsResult = Assert>; +type _K_GetPromptResult = Assert>; +type _K_TextContent = Assert>; +type _K_ImageContent = Assert>; +type _K_AudioContent = Assert>; +type _K_EmbeddedResource = Assert>; +// @ts-expect-error Genuine mismatch: SDK ResourceLink is missing 'size' from spec +type _K_ResourceLink = Assert>; +type _K_PromptMessage = Assert>; +type _K_BooleanSchema = Assert>; +type _K_StringSchema = Assert>; +type _K_NumberSchema = Assert>; +type _K_UntitledSingleSelectEnumSchema = Assert>; +type _K_TitledSingleSelectEnumSchema = Assert>; +type _K_UntitledMultiSelectEnumSchema = Assert>; +type _K_TitledMultiSelectEnumSchema = Assert>; +type _K_LegacyTitledEnumSchema = Assert>; +type _K_JSONRPCErrorResponse = Assert>; +type _K_JSONRPCResultResponse = Assert>; +type _K_InitializeResult = Assert>; +// @ts-expect-error Genuine mismatch: SDK ClientCapabilities is missing 'extensions' from spec +type _K_ClientCapabilities = Assert>; +// @ts-expect-error Genuine mismatch: SDK ServerCapabilities is missing 'extensions' from spec +type _K_ServerCapabilities = Assert>; +type _K_SamplingMessage = Assert>; +type _K_Icon = Assert>; +type _K_Icons = Assert>; +type _K_ModelHint = Assert>; +type _K_ModelPreferences = Assert>; +type _K_ToolChoice = Assert>; +type _K_ToolUseContent = Assert>; +type _K_ToolResultContent = Assert>; +type _K_Annotations = Assert>; +type _K_TaskAugmentedRequestParams = Assert>; +type _K_ToolExecution = Assert>; +type _K_TaskMetadata = Assert>; +type _K_RelatedTaskMetadata = Assert>; +type _K_Task = Assert>; +type _K_CreateTaskResult = Assert>; +type _K_GetTaskResult = Assert>; +type _K_ListTasksResult = Assert>; +type _K_CancelTaskResult = Assert>; +type _K_GetTaskPayloadResult = Assert>; +type _K_TaskStatusNotificationParams = Assert>; +type _K_JSONObject = Assert>; +type _K_MetaObject = Assert>; +// @ts-expect-error Genuine mismatch: SDK RequestMetaObject has extra 'io.modelcontextprotocol/related-task' not in spec +type _K_RequestMetaObject = Assert>; +type _K_ParseError = Assert>; +type _K_InvalidRequestError = Assert>; +type _K_MethodNotFoundError = Assert>; +type _K_InvalidParamsError = Assert>; +type _K_InternalError = Assert>; + +// -- WithJSONRPC-wrapped notification types (11) -- +// SDK notification types do not include `jsonrpc` — the spec types do. We wrap +// with WithJSONRPC<> to add the missing field before comparing keys. + +type _K_ElicitationCompleteNotification = Assert, SpecTypes.ElicitationCompleteNotification>>; +type _K_CancelledNotification = Assert, SpecTypes.CancelledNotification>>; +type _K_ProgressNotification = Assert, SpecTypes.ProgressNotification>>; +type _K_ToolListChangedNotification = Assert, SpecTypes.ToolListChangedNotification>>; +type _K_ResourceListChangedNotification = Assert, SpecTypes.ResourceListChangedNotification>>; +type _K_PromptListChangedNotification = Assert, SpecTypes.PromptListChangedNotification>>; +type _K_RootsListChangedNotification = Assert, SpecTypes.RootsListChangedNotification>>; +type _K_ResourceUpdatedNotification = Assert, SpecTypes.ResourceUpdatedNotification>>; +type _K_LoggingMessageNotification = Assert, SpecTypes.LoggingMessageNotification>>; +type _K_InitializedNotification = Assert, SpecTypes.InitializedNotification>>; +type _K_TaskStatusNotification = Assert, SpecTypes.TaskStatusNotification>>; + +// -- WithJSONRPCRequest-wrapped request types (21) -- +// SDK request types do not include `jsonrpc` or `id` — the spec types do. We +// wrap with WithJSONRPCRequest<> to add the missing fields before comparing keys. + +type _K_SubscribeRequest = Assert, SpecTypes.SubscribeRequest>>; +type _K_UnsubscribeRequest = Assert, SpecTypes.UnsubscribeRequest>>; +type _K_PaginatedRequest = Assert, SpecTypes.PaginatedRequest>>; +type _K_ListRootsRequest = Assert, SpecTypes.ListRootsRequest>>; +type _K_ElicitRequest = Assert, SpecTypes.ElicitRequest>>; +type _K_CompleteRequest = Assert, SpecTypes.CompleteRequest>>; +type _K_ListToolsRequest = Assert, SpecTypes.ListToolsRequest>>; +type _K_CallToolRequest = Assert, SpecTypes.CallToolRequest>>; +type _K_SetLevelRequest = Assert, SpecTypes.SetLevelRequest>>; +type _K_PingRequest = Assert, SpecTypes.PingRequest>>; +type _K_ListResourcesRequest = Assert, SpecTypes.ListResourcesRequest>>; +type _K_ListResourceTemplatesRequest = Assert, SpecTypes.ListResourceTemplatesRequest>>; +type _K_ReadResourceRequest = Assert, SpecTypes.ReadResourceRequest>>; +type _K_ListPromptsRequest = Assert, SpecTypes.ListPromptsRequest>>; +type _K_GetPromptRequest = Assert, SpecTypes.GetPromptRequest>>; +type _K_CreateMessageRequest = Assert, SpecTypes.CreateMessageRequest>>; +type _K_InitializeRequest = Assert, SpecTypes.InitializeRequest>>; +type _K_GetTaskPayloadRequest = Assert, SpecTypes.GetTaskPayloadRequest>>; +type _K_ListTasksRequest = Assert, SpecTypes.ListTasksRequest>>; +type _K_CancelTaskRequest = Assert, SpecTypes.CancelTaskRequest>>; +type _K_GetTaskRequest = Assert, SpecTypes.GetTaskRequest>>; + +// -- TypedResultResponse-wrapped types (21) -- +// The spec defines typed *ResultResponse interfaces that pair JSONRPCResultResponse +// with a specific result. We compare TypedResultResponse against the +// spec's combined type. + +type _K_InitializeResultResponse = Assert, SpecTypes.InitializeResultResponse>>; +type _K_PingResultResponse = Assert, SpecTypes.PingResultResponse>>; +type _K_ListResourcesResultResponse = Assert, SpecTypes.ListResourcesResultResponse>>; +type _K_ListResourceTemplatesResultResponse = Assert, SpecTypes.ListResourceTemplatesResultResponse>>; +type _K_ReadResourceResultResponse = Assert, SpecTypes.ReadResourceResultResponse>>; +type _K_SubscribeResultResponse = Assert, SpecTypes.SubscribeResultResponse>>; +type _K_UnsubscribeResultResponse = Assert, SpecTypes.UnsubscribeResultResponse>>; +type _K_ListPromptsResultResponse = Assert, SpecTypes.ListPromptsResultResponse>>; +type _K_GetPromptResultResponse = Assert, SpecTypes.GetPromptResultResponse>>; +type _K_ListToolsResultResponse = Assert, SpecTypes.ListToolsResultResponse>>; +type _K_CallToolResultResponse = Assert, SpecTypes.CallToolResultResponse>>; +type _K_CreateTaskResultResponse = Assert, SpecTypes.CreateTaskResultResponse>>; +type _K_GetTaskResultResponse = Assert, SpecTypes.GetTaskResultResponse>>; +type _K_GetTaskPayloadResultResponse = Assert, SpecTypes.GetTaskPayloadResultResponse>>; +type _K_CancelTaskResultResponse = Assert, SpecTypes.CancelTaskResultResponse>>; +type _K_ListTasksResultResponse = Assert, SpecTypes.ListTasksResultResponse>>; +type _K_SetLevelResultResponse = Assert, SpecTypes.SetLevelResultResponse>>; +type _K_CreateMessageResultResponse = Assert, SpecTypes.CreateMessageResultResponse>>; +type _K_CompleteResultResponse = Assert, SpecTypes.CompleteResultResponse>>; +type _K_ListRootsResultResponse = Assert, SpecTypes.ListRootsResultResponse>>; +type _K_ElicitResultResponse = Assert, SpecTypes.ElicitResultResponse>>; + +// -- Name mismatches (2) -- +// SDK exports these under different names than the spec. + +type _K_CreateMessageResult = Assert>; +type _K_ResourceTemplate = Assert>; + // This file is .gitignore'd, and fetched by `npm run fetch:spec-types` (called by `npm run test`) const SPEC_TYPES_FILE = path.resolve(__dirname, '../src/types/spec.types.ts'); const SDK_TYPES_FILE = path.resolve(__dirname, '../src/types/types.ts'); From 29e72b16996bc1cb8b3b93421ffdb0c80ff4933e Mon Sep 17 00:00:00 2001 From: rechedev9 Date: Mon, 9 Mar 2026 23:50:27 +0100 Subject: [PATCH 2/4] style: format spec.types.test.ts with prettier Fix CI lint failure by applying Prettier formatting to the key-parity assertions test file. --- packages/core/test/spec.types.test.ts | 108 ++++++++++++++++++-------- 1 file changed, 76 insertions(+), 32 deletions(-) diff --git a/packages/core/test/spec.types.test.ts b/packages/core/test/spec.types.test.ts index c47888390..fce2a4a61 100644 --- a/packages/core/test/spec.types.test.ts +++ b/packages/core/test/spec.types.test.ts @@ -773,10 +773,7 @@ const sdkTypeChecks = { /** Strip index signatures, keeping only explicitly-named keys. */ type KnownKeys = keyof { - [K in keyof T as string extends K ? never - : number extends K ? never - : symbol extends K ? never - : K]: T[K]; + [K in keyof T as string extends K ? never : number extends K ? never : symbol extends K ? never : K]: T[K]; }; /** @@ -784,12 +781,11 @@ type KnownKeys = keyof { * Resolves to `true` on match; a descriptive error type on mismatch. */ type AssertExactKeys< - A, B, + A, + B, Extra extends PropertyKey = Exclude, KnownKeys>, Missing extends PropertyKey = Exclude, KnownKeys> -> = [Extra, Missing] extends [never, never] - ? true - : { _brand: 'KeyMismatch'; extra: Extra; missing: Missing }; +> = [Extra, Missing] extends [never, never] ? true : { _brand: 'KeyMismatch'; extra: Extra; missing: Missing }; /** Constraint: T must resolve to `true`. */ type Assert = T; @@ -819,11 +815,15 @@ type _K_ResourceRequestParams = Assert>; type _K_SubscribeRequestParams = Assert>; type _K_UnsubscribeRequestParams = Assert>; -type _K_ResourceUpdatedNotificationParams = Assert>; +type _K_ResourceUpdatedNotificationParams = Assert< + AssertExactKeys +>; type _K_GetPromptRequestParams = Assert>; type _K_CallToolRequestParams = Assert>; type _K_SetLevelRequestParams = Assert>; -type _K_LoggingMessageNotificationParams = Assert>; +type _K_LoggingMessageNotificationParams = Assert< + AssertExactKeys +>; type _K_CreateMessageRequestParams = Assert>; type _K_CompleteRequestParams = Assert>; type _K_ElicitRequestFormParams = Assert>; @@ -872,9 +872,15 @@ type _K_PromptMessage = Assert>; type _K_StringSchema = Assert>; type _K_NumberSchema = Assert>; -type _K_UntitledSingleSelectEnumSchema = Assert>; -type _K_TitledSingleSelectEnumSchema = Assert>; -type _K_UntitledMultiSelectEnumSchema = Assert>; +type _K_UntitledSingleSelectEnumSchema = Assert< + AssertExactKeys +>; +type _K_TitledSingleSelectEnumSchema = Assert< + AssertExactKeys +>; +type _K_UntitledMultiSelectEnumSchema = Assert< + AssertExactKeys +>; type _K_TitledMultiSelectEnumSchema = Assert>; type _K_LegacyTitledEnumSchema = Assert>; type _K_JSONRPCErrorResponse = Assert>; @@ -903,7 +909,9 @@ type _K_GetTaskResult = Assert>; type _K_CancelTaskResult = Assert>; type _K_GetTaskPayloadResult = Assert>; -type _K_TaskStatusNotificationParams = Assert>; +type _K_TaskStatusNotificationParams = Assert< + AssertExactKeys +>; type _K_JSONObject = Assert>; type _K_MetaObject = Assert>; // @ts-expect-error Genuine mismatch: SDK RequestMetaObject has extra 'io.modelcontextprotocol/related-task' not in spec @@ -918,15 +926,29 @@ type _K_InternalError = Assert to add the missing field before comparing keys. -type _K_ElicitationCompleteNotification = Assert, SpecTypes.ElicitationCompleteNotification>>; +type _K_ElicitationCompleteNotification = Assert< + AssertExactKeys, SpecTypes.ElicitationCompleteNotification> +>; type _K_CancelledNotification = Assert, SpecTypes.CancelledNotification>>; type _K_ProgressNotification = Assert, SpecTypes.ProgressNotification>>; -type _K_ToolListChangedNotification = Assert, SpecTypes.ToolListChangedNotification>>; -type _K_ResourceListChangedNotification = Assert, SpecTypes.ResourceListChangedNotification>>; -type _K_PromptListChangedNotification = Assert, SpecTypes.PromptListChangedNotification>>; -type _K_RootsListChangedNotification = Assert, SpecTypes.RootsListChangedNotification>>; -type _K_ResourceUpdatedNotification = Assert, SpecTypes.ResourceUpdatedNotification>>; -type _K_LoggingMessageNotification = Assert, SpecTypes.LoggingMessageNotification>>; +type _K_ToolListChangedNotification = Assert< + AssertExactKeys, SpecTypes.ToolListChangedNotification> +>; +type _K_ResourceListChangedNotification = Assert< + AssertExactKeys, SpecTypes.ResourceListChangedNotification> +>; +type _K_PromptListChangedNotification = Assert< + AssertExactKeys, SpecTypes.PromptListChangedNotification> +>; +type _K_RootsListChangedNotification = Assert< + AssertExactKeys, SpecTypes.RootsListChangedNotification> +>; +type _K_ResourceUpdatedNotification = Assert< + AssertExactKeys, SpecTypes.ResourceUpdatedNotification> +>; +type _K_LoggingMessageNotification = Assert< + AssertExactKeys, SpecTypes.LoggingMessageNotification> +>; type _K_InitializedNotification = Assert, SpecTypes.InitializedNotification>>; type _K_TaskStatusNotification = Assert, SpecTypes.TaskStatusNotification>>; @@ -945,13 +967,17 @@ type _K_CallToolRequest = Assert, SpecTypes.SetLevelRequest>>; type _K_PingRequest = Assert, SpecTypes.PingRequest>>; type _K_ListResourcesRequest = Assert, SpecTypes.ListResourcesRequest>>; -type _K_ListResourceTemplatesRequest = Assert, SpecTypes.ListResourceTemplatesRequest>>; +type _K_ListResourceTemplatesRequest = Assert< + AssertExactKeys, SpecTypes.ListResourceTemplatesRequest> +>; type _K_ReadResourceRequest = Assert, SpecTypes.ReadResourceRequest>>; type _K_ListPromptsRequest = Assert, SpecTypes.ListPromptsRequest>>; type _K_GetPromptRequest = Assert, SpecTypes.GetPromptRequest>>; type _K_CreateMessageRequest = Assert, SpecTypes.CreateMessageRequest>>; type _K_InitializeRequest = Assert, SpecTypes.InitializeRequest>>; -type _K_GetTaskPayloadRequest = Assert, SpecTypes.GetTaskPayloadRequest>>; +type _K_GetTaskPayloadRequest = Assert< + AssertExactKeys, SpecTypes.GetTaskPayloadRequest> +>; type _K_ListTasksRequest = Assert, SpecTypes.ListTasksRequest>>; type _K_CancelTaskRequest = Assert, SpecTypes.CancelTaskRequest>>; type _K_GetTaskRequest = Assert, SpecTypes.GetTaskRequest>>; @@ -961,24 +987,42 @@ type _K_GetTaskRequest = Assert against the // spec's combined type. -type _K_InitializeResultResponse = Assert, SpecTypes.InitializeResultResponse>>; +type _K_InitializeResultResponse = Assert< + AssertExactKeys, SpecTypes.InitializeResultResponse> +>; type _K_PingResultResponse = Assert, SpecTypes.PingResultResponse>>; -type _K_ListResourcesResultResponse = Assert, SpecTypes.ListResourcesResultResponse>>; -type _K_ListResourceTemplatesResultResponse = Assert, SpecTypes.ListResourceTemplatesResultResponse>>; -type _K_ReadResourceResultResponse = Assert, SpecTypes.ReadResourceResultResponse>>; +type _K_ListResourcesResultResponse = Assert< + AssertExactKeys, SpecTypes.ListResourcesResultResponse> +>; +type _K_ListResourceTemplatesResultResponse = Assert< + AssertExactKeys, SpecTypes.ListResourceTemplatesResultResponse> +>; +type _K_ReadResourceResultResponse = Assert< + AssertExactKeys, SpecTypes.ReadResourceResultResponse> +>; type _K_SubscribeResultResponse = Assert, SpecTypes.SubscribeResultResponse>>; type _K_UnsubscribeResultResponse = Assert, SpecTypes.UnsubscribeResultResponse>>; -type _K_ListPromptsResultResponse = Assert, SpecTypes.ListPromptsResultResponse>>; +type _K_ListPromptsResultResponse = Assert< + AssertExactKeys, SpecTypes.ListPromptsResultResponse> +>; type _K_GetPromptResultResponse = Assert, SpecTypes.GetPromptResultResponse>>; type _K_ListToolsResultResponse = Assert, SpecTypes.ListToolsResultResponse>>; type _K_CallToolResultResponse = Assert, SpecTypes.CallToolResultResponse>>; -type _K_CreateTaskResultResponse = Assert, SpecTypes.CreateTaskResultResponse>>; +type _K_CreateTaskResultResponse = Assert< + AssertExactKeys, SpecTypes.CreateTaskResultResponse> +>; type _K_GetTaskResultResponse = Assert, SpecTypes.GetTaskResultResponse>>; -type _K_GetTaskPayloadResultResponse = Assert, SpecTypes.GetTaskPayloadResultResponse>>; -type _K_CancelTaskResultResponse = Assert, SpecTypes.CancelTaskResultResponse>>; +type _K_GetTaskPayloadResultResponse = Assert< + AssertExactKeys, SpecTypes.GetTaskPayloadResultResponse> +>; +type _K_CancelTaskResultResponse = Assert< + AssertExactKeys, SpecTypes.CancelTaskResultResponse> +>; type _K_ListTasksResultResponse = Assert, SpecTypes.ListTasksResultResponse>>; type _K_SetLevelResultResponse = Assert, SpecTypes.SetLevelResultResponse>>; -type _K_CreateMessageResultResponse = Assert, SpecTypes.CreateMessageResultResponse>>; +type _K_CreateMessageResultResponse = Assert< + AssertExactKeys, SpecTypes.CreateMessageResultResponse> +>; type _K_CompleteResultResponse = Assert, SpecTypes.CompleteResultResponse>>; type _K_ListRootsResultResponse = Assert, SpecTypes.ListRootsResultResponse>>; type _K_ElicitResultResponse = Assert, SpecTypes.ElicitResultResponse>>; From 643febc07764a335d702720a4ff1ccb907a9173e Mon Sep 17 00:00:00 2001 From: rechedev9 Date: Fri, 27 Mar 2026 23:05:23 +0100 Subject: [PATCH 3/4] test(core): add completeness guard for key-parity assertions Ensures every sdkTypeChecks entry (minus excluded union/primitive types) has a corresponding _K_ compile-time assertion. The checked set is derived at test time by parsing _K_ type aliases from the file itself, so adding a new _K_ assertion automatically satisfies the guard. --- packages/core/test/spec.types.test.ts | 42 +++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/packages/core/test/spec.types.test.ts b/packages/core/test/spec.types.test.ts index fce2a4a61..7a07f94f8 100644 --- a/packages/core/test/spec.types.test.ts +++ b/packages/core/test/spec.types.test.ts @@ -1033,6 +1033,36 @@ type _K_ElicitResultResponse = Assert>; type _K_ResourceTemplate = Assert>; +// Types excluded from the key-parity completeness guard: union types and primitive aliases +// that cannot have meaningful AssertExactKeys assertions. +const KEY_PARITY_EXCLUDED = [ + // Union types (15) + 'ClientRequest', + 'ServerRequest', + 'ClientNotification', + 'ServerNotification', + 'ClientResult', + 'ServerResult', + 'JSONRPCMessage', + 'JSONRPCResponse', + 'ContentBlock', + 'SamplingMessageContentBlock', + 'ElicitRequestParams', + 'PrimitiveSchemaDefinition', + 'SingleSelectEnumSchema', + 'MultiSelectEnumSchema', + 'EnumSchema', + // Primitive aliases (8) + 'JSONValue', + 'JSONArray', + 'Role', + 'LoggingLevel', + 'ProgressToken', + 'RequestId', + 'Cursor', + 'TaskStatus' +]; + // This file is .gitignore'd, and fetched by `npm run fetch:spec-types` (called by `npm run test`) const SPEC_TYPES_FILE = path.resolve(__dirname, '../src/types/spec.types.ts'); const SDK_TYPES_FILE = path.resolve(__dirname, '../src/types/types.ts'); @@ -1048,6 +1078,10 @@ function extractExportedTypes(source: string): string[] { return matches.map(m => m[1]!); } +function extractKeyParityTypes(source: string): string[] { + return [...source.matchAll(/^type _K_(\w+)\s*=/gm)].map(m => m[1]!); +} + describe('Spec Types', () => { const specTypes = extractExportedTypes(fs.readFileSync(SPEC_TYPES_FILE, 'utf8')); const sdkTypes = extractExportedTypes(fs.readFileSync(SDK_TYPES_FILE, 'utf8')); @@ -1077,6 +1111,14 @@ describe('Spec Types', () => { expect(missingTests).toHaveLength(0); }); + it('should have key-parity assertions for all non-excluded compatibility tests', () => { + const thisSource = fs.readFileSync(__filename, 'utf8'); + const checked = new Set(extractKeyParityTypes(thisSource)); + const excluded = new Set(KEY_PARITY_EXCLUDED); + const missing = Object.keys(sdkTypeChecks).filter(name => !checked.has(name) && !excluded.has(name)); + expect(missing).toHaveLength(0); + }); + describe('Missing SDK Types', () => { it.each(MISSING_SDK_TYPES)('%s should not be present in MISSING_SDK_TYPES if it has a compatibility test', type => { expect(sdkTypeChecks[type as keyof typeof sdkTypeChecks]).toBeUndefined(); From fdff28933fb60281a3629e1c4f0e206615d2af47 Mon Sep 17 00:00:00 2001 From: rechedev9 Date: Sat, 28 Mar 2026 06:13:48 +0100 Subject: [PATCH 4/4] fix(test): remove stale @ts-expect-error directives for resolved spec mismatches --- packages/core/test/spec.types.test.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/core/test/spec.types.test.ts b/packages/core/test/spec.types.test.ts index 157569968..d26a4cd70 100644 --- a/packages/core/test/spec.types.test.ts +++ b/packages/core/test/spec.types.test.ts @@ -855,7 +855,6 @@ type _K_ReadResourceResult = Assert>; type _K_TextResourceContents = Assert>; type _K_BlobResourceContents = Assert>; -// @ts-expect-error Genuine mismatch: SDK Resource is missing 'size' from spec type _K_Resource = Assert>; // @ts-expect-error Genuine mismatch: SDK PromptArgument is missing 'title' from spec type _K_PromptArgument = Assert>; @@ -866,7 +865,6 @@ type _K_TextContent = Assert>; type _K_AudioContent = Assert>; type _K_EmbeddedResource = Assert>; -// @ts-expect-error Genuine mismatch: SDK ResourceLink is missing 'size' from spec type _K_ResourceLink = Assert>; type _K_PromptMessage = Assert>; type _K_BooleanSchema = Assert>; @@ -886,9 +884,7 @@ type _K_LegacyTitledEnumSchema = Assert>; type _K_JSONRPCResultResponse = Assert>; type _K_InitializeResult = Assert>; -// @ts-expect-error Genuine mismatch: SDK ClientCapabilities is missing 'extensions' from spec type _K_ClientCapabilities = Assert>; -// @ts-expect-error Genuine mismatch: SDK ServerCapabilities is missing 'extensions' from spec type _K_ServerCapabilities = Assert>; type _K_SamplingMessage = Assert>; type _K_Icon = Assert>;