WebSocketClientTransport.js•4.16 kB
import { JSONRPCMessageSchema, } from "@modelcontextprotocol/sdk/types.js";
import { WebSocket } from "ws";
const SUBPROTOCOL = "mcp";
/**
* Client transport for WebSocket: this will connect to a server over the WebSocket protocol.
*/
export class WebSocketClientTransport {
url;
options;
_socket;
_url;
//这里不能先设定sessionId,否则会影响初始化
sessionId;
onclose;
onerror;
//@ts-ignore
onmessage;
constructor(url, options) {
this.url = url;
this.options = options;
this._url = url;
}
async start() {
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, this.options?.protocols ?? SUBPROTOCOL, this.options);
this._socket.onerror = (event) => {
const error = "error" in event
? event.error
: new Error(`WebSocket error: ${JSON.stringify(event)}`);
reject(error);
this.onerror?.(error);
this.options?.onError?.(error);
};
this._socket.onopen = () => {
//@ts-ignore
this.options?.onOpen?.(this._socket);
resolve();
};
this._socket.onclose = () => {
this.onclose?.();
//@ts-ignore
this.options?.onClose?.(this._socket);
};
this._socket.onmessage = (event) => {
console.log("WebSocketClientTransport message", event.data.toString());
try {
if (typeof event.data !== "string") {
throw new Error("WebSocket message must be a string");
}
}
catch (error) {
this.onerror?.(error);
this.options?.onError?.(error);
return;
}
let message;
try {
message = Object.assign(JSONRPCMessageSchema.parse(JSON.parse(event.data)));
// if (
// message?.sessionId !== undefined &&
// message?.sessionId !== this.sessionId
// ) {
// this.sessionId = message.sessionId;
// }
}
catch (error) {
console.error("WebSocketClientTransport message error", error);
this.onerror?.(error);
this.options?.onError?.(error);
return;
}
this.options?.onMessage?.(message, {
// sessionId: message.sessionId,
//@ts-ignore
requestInfo: { headers: this.options?.headers ?? {} },
});
this.onmessage?.(message, {
// sessionId: message.sessionId,
//@ts-ignore
requestInfo: { headers: this.options?.headers ?? {} },
});
};
});
}
async close() {
this._socket?.close();
}
send(message, options) {
console.log("send WebSocketClientTransport", JSON.stringify(message, null, 4));
return new Promise((resolve, reject) => {
if (!this._socket) {
reject(new Error("Not connected"));
return;
}
if (this._socket.readyState !== WebSocket.OPEN) {
reject(new Error("WebSocket is not open"));
return;
}
this._socket?.send(JSON.stringify(Object.assign(message, {
// sessionId: options?.relatedRequestId ?? this.sessionId,
})));
console.log("send WebSocketClientTransport", JSON.stringify(message, null, 4));
resolve();
});
}
}
//# sourceMappingURL=WebSocketClientTransport.js.map