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
9 changes: 9 additions & 0 deletions .changeset/fix-transport-exact-optional-property-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@modelcontextprotocol/core': patch
---

Add explicit `| undefined` to optional properties on the `Transport` interface and `TransportSendOptions` (`onclose`, `onerror`, `onmessage`, `sessionId`, `setProtocolVersion`, `setSupportedProtocolVersions`, `onresumptiontoken`).

This fixes TS2420 errors for consumers using `exactOptionalPropertyTypes: true` without `skipLibCheck`, where the emitted `.d.ts` for implementing classes included `| undefined` but the interface did not.

Workaround for older SDK versions: enable `skipLibCheck: true` in your tsconfig.
14 changes: 7 additions & 7 deletions packages/core/src/shared/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export type TransportSendOptions = {
*
* This allows clients to persist the latest token for potential reconnection.
*/
onresumptiontoken?: (token: string) => void;
onresumptiontoken?: ((token: string) => void) | undefined;
};
Comment on lines 66 to 70
Copy link

Choose a reason for hiding this comment

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

🟡 The | undefined fix for exactOptionalPropertyTypes was applied to onresumptiontoken in TransportSendOptions but not to the other two optional properties (relatedRequestId on line 55 and resumptionToken on line 62). For consistency, these should also get | undefined — a consumer passing { resumptionToken: undefined } would still get a type error under exactOptionalPropertyTypes: true.

Extended reasoning...

What the bug is

The PR adds | undefined to all optional properties on the Transport interface (onclose, onerror, onmessage, sessionId, setProtocolVersion, setSupportedProtocolVersions) and to onresumptiontoken in TransportSendOptions. However, it misses the other two optional properties in TransportSendOptions: relatedRequestId?: RequestId (line 55) and resumptionToken?: string (line 62). The changeset description even explicitly mentions TransportSendOptions as being fixed.

Concrete example

Under exactOptionalPropertyTypes: true, prop?: T means the property can be omitted but cannot be explicitly set to undefined. To allow explicit undefined, you need prop?: T | undefined. After this PR, a consumer writing:

const opts: TransportSendOptions = {
  resumptionToken: undefined,
  onresumptiontoken: myCallback
};

would get a type error on resumptionToken: undefined because it is still typed as resumptionToken?: string (without | undefined), even though onresumptiontoken in the same object is now correctly typed. The test file at packages/client/test/client/streamableHttp.test.ts:1099 actually does pass resumptionToken: undefined in exactly this way.

Impact

The practical impact is low. TransportSendOptions is a type alias, not a class-implemented interface, so the primary TS2420 error that motivated this PR does not apply here. The issue only manifests if a consumer explicitly passes undefined for these properties (rather than simply omitting them) while using exactOptionalPropertyTypes: true. This is an uncommon pattern, but it is inconsistent to fix one property and not the others in the same type.

Suggested fix

Add | undefined to the remaining two optional properties in TransportSendOptions:

relatedRequestId?: RequestId | undefined;
resumptionToken?: string | undefined;

This is a one-line-each change that makes the fix complete and consistent with the stated goal of the PR.

/**
* Describes the minimal contract for an MCP transport that a client or server can communicate over.
Expand Down 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,21 +114,21 @@ 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.
*/
sessionId?: string;
sessionId?: string | undefined;

/**
* 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;
}
Loading