feat!: remove WebSocketClientTransport (#1783)
Co-authored-by: Konstantin Konstantinov <KKonstantinov@users.noreply.github.com>
This commit is contained in:
parent
4aec5f7906
commit
045c62a1e0
9 changed files with 28 additions and 120 deletions
5
.changeset/remove-websocket-transport.md
Normal file
5
.changeset/remove-websocket-transport.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@modelcontextprotocol/client': major
|
||||
---
|
||||
|
||||
Remove `WebSocketClientTransport`. WebSocket is not a spec-defined transport; use stdio or Streamable HTTP. The `Transport` interface remains exported for custom implementations. See #142.
|
||||
|
|
@ -43,7 +43,7 @@ Replace all `@modelcontextprotocol/sdk/...` imports using this table.
|
|||
| `@modelcontextprotocol/sdk/client/streamableHttp.js` | `@modelcontextprotocol/client` |
|
||||
| `@modelcontextprotocol/sdk/client/sse.js` | `@modelcontextprotocol/client` |
|
||||
| `@modelcontextprotocol/sdk/client/stdio.js` | `@modelcontextprotocol/client` |
|
||||
| `@modelcontextprotocol/sdk/client/websocket.js` | `@modelcontextprotocol/client` |
|
||||
| `@modelcontextprotocol/sdk/client/websocket.js` | REMOVED (use Streamable HTTP or stdio; implement `Transport` for custom needs) |
|
||||
|
||||
### Server imports
|
||||
|
||||
|
|
@ -96,6 +96,7 @@ Notes:
|
|||
| `ErrorCode.RequestTimeout` | `SdkErrorCode.RequestTimeout` |
|
||||
| `ErrorCode.ConnectionClosed` | `SdkErrorCode.ConnectionClosed` |
|
||||
| `StreamableHTTPError` | REMOVED (use `SdkError` with `SdkErrorCode.ClientHttp*`) |
|
||||
| `WebSocketClientTransport` | REMOVED (use `StreamableHTTPClientTransport` or `StdioClientTransport`) |
|
||||
|
||||
All other symbols from `@modelcontextprotocol/sdk/types.js` retain their original names (e.g., `CallToolResultSchema`, `ListToolsResultSchema`, etc.).
|
||||
|
||||
|
|
|
|||
|
|
@ -110,6 +110,26 @@ const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: ()
|
|||
|
||||
The SSE transport has been removed from the server. Servers should migrate to Streamable HTTP. The client-side SSE transport remains available for connecting to legacy SSE servers.
|
||||
|
||||
### `WebSocketClientTransport` removed
|
||||
|
||||
`WebSocketClientTransport` has been removed. WebSocket is not a spec-defined MCP transport, and keeping it in the SDK encouraged transport proliferation without a conformance baseline.
|
||||
|
||||
Use `StdioClientTransport` for local servers or `StreamableHTTPClientTransport` for remote servers. If you need WebSocket for a custom deployment, implement the `Transport` interface directly — it remains exported from `@modelcontextprotocol/client`.
|
||||
|
||||
**Before (v1):**
|
||||
|
||||
```typescript
|
||||
import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/websocket.js';
|
||||
const transport = new WebSocketClientTransport(new URL('ws://localhost:3000'));
|
||||
```
|
||||
|
||||
**After (v2):**
|
||||
|
||||
```typescript
|
||||
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
|
||||
const transport = new StreamableHTTPClientTransport(new URL('http://localhost:3000/mcp'));
|
||||
```
|
||||
|
||||
### Server auth removed
|
||||
|
||||
Server-side OAuth/auth has been removed entirely from the SDK. This includes `mcpAuthRouter`, `OAuthServerProvider`, `OAuthTokenVerifier`, `requireBearerAuth`, `authenticateClient`, `ProxyOAuthServerProvider`, `allowedMethods`, and all associated types.
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@
|
|||
"@types/express-serve-static-core": "catalog:devTools",
|
||||
"@types/node": "^24.10.1",
|
||||
"@types/supertest": "catalog:devTools",
|
||||
"@types/ws": "catalog:devTools",
|
||||
"@typescript/native-preview": "catalog:devTools",
|
||||
"eslint": "catalog:devTools",
|
||||
"eslint-config-prettier": "catalog:devTools",
|
||||
|
|
@ -75,7 +74,6 @@
|
|||
"typescript": "catalog:devTools",
|
||||
"typescript-eslint": "catalog:devTools",
|
||||
"vitest": "catalog:devTools",
|
||||
"ws": "catalog:devTools",
|
||||
"zod": "catalog:runtimeShared"
|
||||
},
|
||||
"resolutions": {
|
||||
|
|
|
|||
|
|
@ -1,74 +0,0 @@
|
|||
import type { JSONRPCMessage, Transport } from '@modelcontextprotocol/core';
|
||||
import { JSONRPCMessageSchema } from '@modelcontextprotocol/core';
|
||||
|
||||
const SUBPROTOCOL = 'mcp';
|
||||
|
||||
/**
|
||||
* Client transport for WebSocket: this will connect to a server over the WebSocket protocol.
|
||||
*/
|
||||
export class WebSocketClientTransport implements Transport {
|
||||
private _socket?: WebSocket;
|
||||
private _url: URL;
|
||||
|
||||
onclose?: () => void;
|
||||
onerror?: (error: Error) => void;
|
||||
onmessage?: (message: JSONRPCMessage) => void;
|
||||
|
||||
constructor(url: URL) {
|
||||
this._url = url;
|
||||
}
|
||||
|
||||
start(): Promise<void> {
|
||||
if (this._socket) {
|
||||
throw new Error(
|
||||
'WebSocketClientTransport already started! If using Client class, note that connect() calls start() automatically.'
|
||||
);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this._socket = new WebSocket(this._url, SUBPROTOCOL);
|
||||
|
||||
this._socket.onerror = event => {
|
||||
const error = 'error' in event ? (event.error as Error) : new Error(`WebSocket error: ${JSON.stringify(event)}`);
|
||||
reject(error);
|
||||
this.onerror?.(error);
|
||||
};
|
||||
|
||||
this._socket.onopen = () => {
|
||||
resolve();
|
||||
};
|
||||
|
||||
this._socket.onclose = () => {
|
||||
this.onclose?.();
|
||||
};
|
||||
|
||||
this._socket.onmessage = (event: MessageEvent) => {
|
||||
let message: JSONRPCMessage;
|
||||
try {
|
||||
message = JSONRPCMessageSchema.parse(JSON.parse(event.data));
|
||||
} catch (error) {
|
||||
this.onerror?.(error as Error);
|
||||
return;
|
||||
}
|
||||
|
||||
this.onmessage?.(message);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
this._socket?.close();
|
||||
}
|
||||
|
||||
send(message: JSONRPCMessage): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this._socket) {
|
||||
reject(new Error('Not connected'));
|
||||
return;
|
||||
}
|
||||
|
||||
this._socket?.send(JSON.stringify(message));
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -69,7 +69,6 @@ export type {
|
|||
StreamableHTTPReconnectionOptions
|
||||
} from './client/streamableHttp.js';
|
||||
export { StreamableHTTPClientTransport } from './client/streamableHttp.js';
|
||||
export { WebSocketClientTransport } from './client/websocket.js';
|
||||
|
||||
// experimental exports
|
||||
export { ExperimentalClientTasks } from './experimental/tasks/client.js';
|
||||
|
|
|
|||
33
pnpm-lock.yaml
generated
33
pnpm-lock.yaml
generated
|
|
@ -30,9 +30,6 @@ catalogs:
|
|||
'@types/supertest':
|
||||
specifier: ^6.0.2
|
||||
version: 6.0.3
|
||||
'@types/ws':
|
||||
specifier: ^8.5.12
|
||||
version: 8.18.1
|
||||
'@typescript/native-preview':
|
||||
specifier: ^7.0.0-dev.20251217.1
|
||||
version: 7.0.0-dev.20260327.2
|
||||
|
|
@ -84,9 +81,6 @@ catalogs:
|
|||
wrangler:
|
||||
specifier: ^4.14.4
|
||||
version: 4.78.0
|
||||
ws:
|
||||
specifier: ^8.18.0
|
||||
version: 8.20.0
|
||||
runtimeClientOnly:
|
||||
cross-spawn:
|
||||
specifier: ^7.0.5
|
||||
|
|
@ -185,9 +179,6 @@ importers:
|
|||
'@types/supertest':
|
||||
specifier: catalog:devTools
|
||||
version: 6.0.3
|
||||
'@types/ws':
|
||||
specifier: catalog:devTools
|
||||
version: 8.18.1
|
||||
'@typescript/native-preview':
|
||||
specifier: catalog:devTools
|
||||
version: 7.0.0-dev.20260327.2
|
||||
|
|
@ -233,9 +224,6 @@ importers:
|
|||
vitest:
|
||||
specifier: catalog:devTools
|
||||
version: 4.1.2(@opentelemetry/api@1.9.1)(@types/node@24.12.0)(vite@7.3.0(@types/node@24.12.0)(tsx@4.21.0)(yaml@2.8.3))
|
||||
ws:
|
||||
specifier: catalog:devTools
|
||||
version: 8.20.0
|
||||
zod:
|
||||
specifier: catalog:runtimeShared
|
||||
version: 4.3.6
|
||||
|
|
@ -2362,9 +2350,6 @@ packages:
|
|||
'@types/unist@3.0.3':
|
||||
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.57.2':
|
||||
resolution: {integrity: sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
|
@ -4866,18 +4851,6 @@ packages:
|
|||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
ws@8.20.0:
|
||||
resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
wsl-utils@0.3.1:
|
||||
resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==}
|
||||
engines: {node: '>=20'}
|
||||
|
|
@ -6022,10 +5995,6 @@ snapshots:
|
|||
|
||||
'@types/unist@3.0.3': {}
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
dependencies:
|
||||
'@types/node': 24.12.0
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.2
|
||||
|
|
@ -8762,8 +8731,6 @@ snapshots:
|
|||
|
||||
ws@8.18.0: {}
|
||||
|
||||
ws@8.20.0: {}
|
||||
|
||||
wsl-utils@0.3.1:
|
||||
dependencies:
|
||||
is-wsl: 3.1.1
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ catalogs:
|
|||
'@types/express': ^5.0.6
|
||||
'@types/express-serve-static-core': ^5.1.0
|
||||
'@types/supertest': ^6.0.2
|
||||
'@types/ws': ^8.5.12
|
||||
'@typescript/native-preview': ^7.0.0-dev.20251217.1
|
||||
eslint: ^9.39.2
|
||||
eslint-config-prettier: ^10.1.8
|
||||
|
|
@ -32,7 +31,6 @@ catalogs:
|
|||
typescript-eslint: ^8.48.1
|
||||
vite-tsconfig-paths: ^5.1.4
|
||||
vitest: ^4.0.15
|
||||
ws: ^8.18.0
|
||||
runtimeClientOnly:
|
||||
cross-spawn: ^7.0.5
|
||||
eventsource: ^3.0.2
|
||||
|
|
|
|||
|
|
@ -1,13 +1,7 @@
|
|||
import WebSocket from 'ws';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(global as any).WebSocket = WebSocket;
|
||||
|
||||
import express from 'express';
|
||||
import { Client } from '../src/client/index.js';
|
||||
import { SSEClientTransport } from '../src/client/sse.js';
|
||||
import { StdioClientTransport } from '../src/client/stdio.js';
|
||||
import { WebSocketClientTransport } from '../src/client/websocket.js';
|
||||
import { Server } from '../src/server/index.js';
|
||||
import { SSEServerTransport } from '../src/server/sse.js';
|
||||
import { StdioServerTransport } from '../src/server/stdio.js';
|
||||
|
|
@ -38,7 +32,7 @@ async function runClient(url_or_command: string, args: string[]) {
|
|||
if (url?.protocol === 'http:' || url?.protocol === 'https:') {
|
||||
clientTransport = new SSEClientTransport(new URL(url_or_command));
|
||||
} else if (url?.protocol === 'ws:' || url?.protocol === 'wss:') {
|
||||
clientTransport = new WebSocketClientTransport(new URL(url_or_command));
|
||||
throw new Error('WebSocket URLs are no longer supported. Use http(s) or stdio instead.');
|
||||
} else {
|
||||
clientTransport = new StdioClientTransport({
|
||||
command: url_or_command,
|
||||
|
|
|
|||
Loading…
Reference in a new issue