From a83129201226a49feaba004a0080de98f27631d8 Mon Sep 17 00:00:00 2001 From: rechedev9 Date: Wed, 11 Mar 2026 19:18:24 +0100 Subject: [PATCH] fix(core): add | undefined to Transport optional callback types for exactOptionalPropertyTypes compatibility Resolves TS2420 errors when users implement the Transport interface in projects with `exactOptionalPropertyTypes: true` enabled. Optional callback properties (onclose, onerror, onmessage, setProtocolVersion, setSupportedProtocolVersions) now include explicit `| undefined` in their types, satisfying the stricter assignability rules. Fixes #1314 --- ...transport-exact-optional-property-types.md | 7 ++++ packages/core/src/shared/transport.ts | 10 +++--- .../core/test/shared/transport.types.test.ts | 36 +++++++++++++++++++ 3 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 .changeset/fix-transport-exact-optional-property-types.md create mode 100644 packages/core/test/shared/transport.types.test.ts diff --git a/.changeset/fix-transport-exact-optional-property-types.md b/.changeset/fix-transport-exact-optional-property-types.md new file mode 100644 index 000000000..37e624183 --- /dev/null +++ b/.changeset/fix-transport-exact-optional-property-types.md @@ -0,0 +1,7 @@ +--- +'@modelcontextprotocol/core': patch +--- + +Fix TS2420 error when implementing Transport with exactOptionalPropertyTypes enabled + +Optional callback properties on the Transport interface (`onclose`, `onerror`, `onmessage`, `setProtocolVersion`, `setSupportedProtocolVersions`) now explicitly include `| undefined` in their type signature. This makes the interface compatible with TypeScript's `exactOptionalPropertyTypes` compiler option, which was previously causing TS2420 "Class incorrectly implements interface" errors for users with that flag enabled. diff --git a/packages/core/src/shared/transport.ts b/packages/core/src/shared/transport.ts index a04e054ba..06d474de5 100644 --- a/packages/core/src/shared/transport.ts +++ b/packages/core/src/shared/transport.ts @@ -98,14 +98,14 @@ export interface Transport { * * This should be invoked when {@linkcode Transport.close | close()} is called as well. */ - onclose?: () => void; + onclose?: (() => void) | undefined; /** * Callback for when an error occurs. * * Note that errors are not necessarily fatal; they are used for reporting any kind of exceptional condition out of band. */ - onerror?: (error: Error) => void; + onerror?: ((error: Error) => void) | undefined; /** * Callback for when a message (request or response) is received over the connection. @@ -114,7 +114,7 @@ export interface Transport { * * The {@linkcode MessageExtraInfo.requestInfo | requestInfo} can be used to get the original request information (headers, etc.) */ - onmessage?: (message: T, extra?: MessageExtraInfo) => void; + onmessage?: ((message: T, extra?: MessageExtraInfo) => void) | undefined; /** * The session ID generated for this connection. @@ -124,11 +124,11 @@ export interface Transport { /** * Sets the protocol version used for the connection (called when the initialize response is received). */ - setProtocolVersion?: (version: string) => void; + setProtocolVersion?: ((version: string) => void) | undefined; /** * Sets the supported protocol versions for header validation (called during connect). * This allows the server to pass its supported versions to the transport. */ - setSupportedProtocolVersions?: (versions: string[]) => void; + setSupportedProtocolVersions?: ((versions: string[]) => void) | undefined; } diff --git a/packages/core/test/shared/transport.types.test.ts b/packages/core/test/shared/transport.types.test.ts new file mode 100644 index 000000000..53842e2cd --- /dev/null +++ b/packages/core/test/shared/transport.types.test.ts @@ -0,0 +1,36 @@ +/** + * Compile-time type checks for the Transport interface. + * + * Verifies that a class declaring optional callback properties as `T | undefined` + * (the pattern required by `exactOptionalPropertyTypes: true`) is assignable to + * Transport without TS2420 errors. + * + * See: https://github.com/modelcontextprotocol/typescript-sdk/issues/1314 + */ +import { test } from 'vitest'; + +import type { Transport } from '../../src/shared/transport.js'; +import type { JSONRPCMessage, MessageExtraInfo } from '../../src/types/types.js'; + +// A concrete class that uses the explicit `| undefined` union form for optional callbacks. +// With the old Transport interface (no `| undefined` on callbacks), this class would produce +// TS2420 under `exactOptionalPropertyTypes: true`. +class ExplicitUndefinedTransport implements Transport { + sessionId?: string | undefined; + onclose?: (() => void) | undefined; + onerror?: ((error: Error) => void) | undefined; + onmessage?: ((message: T, extra?: MessageExtraInfo) => void) | undefined; + setProtocolVersion?: ((version: string) => void) | undefined; + setSupportedProtocolVersions?: ((versions: string[]) => void) | undefined; + + async start(): Promise {} + async close(): Promise {} + async send(_message: JSONRPCMessage): Promise {} +} + +test('Transport allows explicit | undefined on optional callback properties', () => { + const transport: Transport = new ExplicitUndefinedTransport(); + // The mere fact this file compiles is the assertion. + // We also verify runtime assignability here. + expect(transport).toBeDefined(); +});