@@ -51,6 +51,7 @@ class ToolboxClient {
5151 #url: string ;
5252 #transport: ITransport ;
5353 #clientHeaders: ClientHeadersConfig ;
54+ #supportedProtocols: string [ ] | undefined ;
5455
5556 /**
5657 * The negotiated protocol version currently in use.
@@ -72,21 +73,53 @@ class ToolboxClient {
7273 url : string ,
7374 session ?: AxiosInstance | null ,
7475 clientHeaders ?: ClientHeadersConfig | null ,
75- protocol : Protocol = Protocol . MCP ,
76+ protocol : Protocol | Protocol [ ] | string [ ] | string = Protocol . MCP ,
7677 clientName ?: string ,
7778 clientVersion ?: string ,
7879 ) {
7980 this . #url = url ;
8081 this . #clientHeaders = clientHeaders || { } ;
8182 warnIfHttpAndHeaders ( url , this . #clientHeaders) ;
82- if ( ! getSupportedMcpVersions ( ) . includes ( protocol ) ) {
83- throw new Error ( `Unsupported protocol version: ${ protocol } ` ) ;
83+
84+ let initialProtocol : Protocol ;
85+ if ( Array . isArray ( protocol ) ) {
86+ if ( protocol . length === 0 ) {
87+ throw new Error ( 'Protocol array cannot be empty' ) ;
88+ }
89+
90+ const globalSupported = getSupportedMcpVersions ( ) ;
91+
92+ for ( const p of protocol ) {
93+ if ( ! globalSupported . includes ( p as Protocol ) ) {
94+ throw new Error ( `Invalid protocol version '${ p } '. Must be one of: ${ globalSupported . join ( ', ' ) } ` ) ;
95+ }
96+ }
97+
98+ const sorted : string [ ] = [ ] ;
99+
100+ for ( const globalVer of globalSupported ) {
101+ if ( protocol . includes ( globalVer as any ) ) {
102+ sorted . push ( globalVer ) ;
103+ }
104+ }
105+
106+ if ( sorted . length === 0 ) {
107+ throw new Error ( 'None of the provided protocols are supported' ) ;
108+ }
109+
110+ this . #supportedProtocols = sorted ;
111+ initialProtocol = sorted [ 0 ] as Protocol ; // Start with the highest requested version
112+ } else {
113+ initialProtocol = protocol as Protocol ;
114+ if ( ! getSupportedMcpVersions ( ) . includes ( initialProtocol ) ) {
115+ throw new Error ( `Unsupported protocol version: ${ initialProtocol } ` ) ;
116+ }
84117 }
85118
86- this . #transport = this . #createTransport (
119+ this . #transport = this . #createTransportWithProtocols (
87120 url ,
88121 session || undefined ,
89- protocol ,
122+ initialProtocol ,
90123 clientName ,
91124 clientVersion ,
92125 ) ;
@@ -146,6 +179,20 @@ class ToolboxClient {
146179 }
147180 }
148181
182+ #createTransportWithProtocols(
183+ url : string ,
184+ session : AxiosInstance | undefined ,
185+ protocol : Protocol ,
186+ clientName ?: string ,
187+ clientVersion ?: string ,
188+ ) : ITransport {
189+ const transport = this . #createTransport( url , session , protocol , clientName , clientVersion ) ;
190+ if ( this . #supportedProtocols) {
191+ transport . supportedProtocols = this . #supportedProtocols;
192+ }
193+ return transport ;
194+ }
195+
149196 /**
150197 * Resolves client headers from their provider functions.
151198 * @returns {Promise<Record<string, string>> } A promise that resolves to the resolved headers.
@@ -217,6 +264,42 @@ class ToolboxClient {
217264 return { tool, usedAuthKeys, usedBoundKeys} ;
218265 }
219266
267+ async #executeWithFallback< T > ( action : ( ) => Promise < T > ) : Promise < T > {
268+ try {
269+ return await action ( ) ;
270+ } catch ( e : unknown ) {
271+ if ( e instanceof ProtocolNegotiationError ) {
272+ const serverVersion = e . fallbackVersion as string ;
273+ let mutuallySupported : string [ ] | null = null ;
274+
275+ if ( this . #supportedProtocols. includes ( serverVersion as Protocol ) ) {
276+ mutuallySupported = [ serverVersion ] ;
277+ } else {
278+ const allVersions = getSupportedMcpVersions ( ) ;
279+ if ( allVersions . includes ( serverVersion as Protocol ) ) {
280+ const idx = allVersions . indexOf ( serverVersion as Protocol ) ;
281+ const serverSupported = allVersions . slice ( idx ) ;
282+ mutuallySupported = this . #supportedProtocols. filter ( v =>
283+ serverSupported . includes ( v ) ,
284+ ) ;
285+ }
286+ }
287+
288+ if ( ! mutuallySupported || mutuallySupported . length === 0 ) {
289+ throw new Error ( 'No mutually supported protocol version' ) ;
290+ }
291+
292+ this . #transport = this . #createTransportWithProtocols(
293+ this . #url,
294+ undefined ,
295+ mutuallySupported [ 0 ] as Protocol ,
296+ ) ;
297+ return await action ( ) ;
298+ }
299+ throw e ;
300+ }
301+ }
302+
220303 /**
221304 * Asynchronously loads a tool from the server.
222305 * Retrieves the schema for the specified tool from the Toolbox server and
@@ -238,21 +321,10 @@ class ToolboxClient {
238321 ) : Promise < ToolboxTool > {
239322 warnIfHttpAndHeaders ( this . #transport. baseUrl , authTokenGetters ) ;
240323 const headers = await this . #resolveClientHeaders( ) ;
241- let manifest ;
242- try {
243- manifest = await this . #transport. toolGet ( name , headers ) ;
244- } catch ( e : unknown ) {
245- if ( e instanceof ProtocolNegotiationError ) {
246- this . #transport = this . #createTransport(
247- this . #url,
248- undefined ,
249- e . fallbackVersion as Protocol ,
250- ) ;
251- manifest = await this . #transport. toolGet ( name , headers ) ;
252- } else {
253- throw e ;
254- }
255- }
324+ const manifest = await this . #executeWithFallback( ( ) =>
325+ this . #transport. toolGet ( name , headers ) ,
326+ ) ;
327+
256328
257329 if (
258330 manifest . tools &&
@@ -323,21 +395,10 @@ class ToolboxClient {
323395 const toolsetName = name || '' ;
324396 const headers = await this . #resolveClientHeaders( ) ;
325397
326- let manifest ;
327- try {
328- manifest = await this . #transport. toolsList ( toolsetName , headers ) ;
329- } catch ( e : unknown ) {
330- if ( e instanceof ProtocolNegotiationError ) {
331- this . #transport = this . #createTransport(
332- this . #url,
333- undefined ,
334- e . fallbackVersion as Protocol ,
335- ) ;
336- manifest = await this . #transport. toolsList ( toolsetName , headers ) ;
337- } else {
338- throw e ;
339- }
340- }
398+ const manifest = await this . #executeWithFallback( ( ) =>
399+ this . #transport. toolsList ( toolsetName , headers ) ,
400+ ) ;
401+
341402 const tools : ToolboxTool [ ] = [ ] ;
342403
343404 const overallUsedAuthKeys : Set < string > = new Set ( ) ;
0 commit comments