Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/fix-transport-exact-optional-property-types.md
Original file line number Diff line number Diff line change
@@ -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.
10 changes: 5 additions & 5 deletions packages/core/src/shared/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -114,7 +114,7 @@ export interface Transport {
*
* The {@linkcode MessageExtraInfo.requestInfo | requestInfo} can be used to get the original request information (headers, etc.)
*/
onmessage?: <T extends JSONRPCMessage>(message: T, extra?: MessageExtraInfo) => void;
onmessage?: (<T extends JSONRPCMessage>(message: T, extra?: MessageExtraInfo) => void) | undefined;

/**
* The session ID generated for this connection.
Expand All @@ -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;
}
36 changes: 36 additions & 0 deletions packages/core/test/shared/transport.types.test.ts
Original file line number Diff line number Diff line change
@@ -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?: (<T extends JSONRPCMessage>(message: T, extra?: MessageExtraInfo) => void) | undefined;
setProtocolVersion?: ((version: string) => void) | undefined;
setSupportedProtocolVersions?: ((versions: string[]) => void) | undefined;

async start(): Promise<void> {}
async close(): Promise<void> {}
async send(_message: JSONRPCMessage): Promise<void> {}
}

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();
});
Loading