modelcontextprotocol / modelcontextprotocol/typescript-sdk

Type inconsistency: StreamableHTTPServerTransport.onclose widens Transport interface contract under exactOptionalPropertyTypes

Open Beginner friendly
#2,083 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

potentially close
Dominant language
TypeScript
Stars
13.4k
Forks
2.2k
Avg merge
3d 15h
Merged PRs (30d)
4

Description

SDK version: 1.29.0. TypeScript version: 5.9.3. tsconfig flags: strict true, exactOptionalPropertyTypes true, noUncheckedIndexedAccess true, target ES2022, module NodeNext.

Problem: the Transport interface in shared/transport.d.ts declares onclose as optional with type () => void (no explicit | undefined). The concrete StreamableHTTPServerTransport class in server/streamableHttp.d.ts declares onclose via getter/setter typed (() => void) | undefined. Under exactOptionalPropertyTypes, these two declarations are incompatible: the concrete class widens the optional property to include undefined, but the interface contract does not. As a result, passing a StreamableHTTPServerTransport instance into McpServer.connect(transport: Transport) fails typecheck with TS2379.

Reproduction: create a TypeScript project with the flag set above, import StreamableHTTPServerTransport and McpServer, attempt server.connect(new StreamableHTTPServerTransport()).

Expected: typechecks cleanly because the SDK's own concrete transport class should be assignable to the SDK's own Transport interface.

Actual: TS2379 on the property variance.

Asymmetry note: the client-side StreamableHTTPClientTransport declares onclose with the narrower () => void shape and typechecks cleanly under EOPT. Only the server-side transport hits this.

Suggested fix: align StreamableHTTPServerTransport.onclose to () => void to match the Transport interface declaration, OR widen Transport.onclose explicitly to (() => void) | undefined. Either direction resolves the variance for EOPT consumers.

Workaround consumers must use today: targeted cast at the connect call site, e.g. server.connect(transport as Transport).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Compare shared/transport.d.ts with server/streamableHttp.d.ts, focusing on the onclose declarations, then reproduce the issue with TypeScript 5.9.3 and exactOptionalPropertyTypes enabled. Done means the StreamableHTTPServerTransport instance passes to McpServer.connect without TS2379, while the client-side declaration remains unaffected.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.