From 8ec66a392fd66e1dca392473374276755d72c18a Mon Sep 17 00:00:00 2001 From: Anubhav Dhawan Date: Sat, 4 Jul 2026 04:09:33 +0530 Subject: [PATCH] feat(core): implement reference support for Stateless MCP (SEP-2575) --- .../toolbox-core/src/toolbox_core/client.ts | 69 +++++++++++++++---- .../src/toolbox_core/errorUtils.ts | 10 +++ 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/packages/toolbox-core/src/toolbox_core/client.ts b/packages/toolbox-core/src/toolbox_core/client.ts index 9895d8e5..b284dc81 100644 --- a/packages/toolbox-core/src/toolbox_core/client.ts +++ b/packages/toolbox-core/src/toolbox_core/client.ts @@ -27,6 +27,7 @@ import {McpHttpTransportV20241105} from './mcp/v20241105/mcp.js'; import {McpHttpTransportV20250618} from './mcp/v20250618/mcp.js'; import {McpHttpTransportV20250326} from './mcp/v20250326/mcp.js'; import {McpHttpTransportV20251125} from './mcp/v20251125/mcp.js'; +import {ProtocolNegotiationError} from './errorUtils.js'; import { BoundParams, identifyAuthRequirements, @@ -80,43 +81,55 @@ class ToolboxClient { ); } + this.#transport = this.#createTransport( + url, + session || undefined, + protocol, + clientName, + clientVersion, + ); + } + + #createTransport( + url: string, + session: AxiosInstance | undefined, + protocol: Protocol, + clientName?: string, + clientVersion?: string, + ): ITransport { switch (protocol) { case Protocol.MCP_v20241105: - this.#transport = new McpHttpTransportV20241105( + return new McpHttpTransportV20241105( url, - session || undefined, + session, protocol, clientName, clientVersion, ); - break; case Protocol.MCP_v20250326: - this.#transport = new McpHttpTransportV20250326( + return new McpHttpTransportV20250326( url, - session || undefined, + session, protocol, clientName, clientVersion, ); - break; case Protocol.MCP_v20250618: - this.#transport = new McpHttpTransportV20250618( + return new McpHttpTransportV20250618( url, - session || undefined, + session, protocol, clientName, clientVersion, ); - break; case Protocol.MCP_v20251125: - this.#transport = new McpHttpTransportV20251125( + return new McpHttpTransportV20251125( url, - session || undefined, + session, protocol, clientName, clientVersion, ); - break; default: throw new Error(`Unsupported MCP protocol version: ${protocol}`); } @@ -214,7 +227,21 @@ class ToolboxClient { ): Promise { warnIfHttpAndHeaders(this.#transport.baseUrl, authTokenGetters); const headers = await this.#resolveClientHeaders(); - const manifest = await this.#transport.toolGet(name, headers); + let manifest; + try { + manifest = await this.#transport.toolGet(name, headers); + } catch (e: unknown) { + if (e instanceof ProtocolNegotiationError) { + this.#transport = this.#createTransport( + this.#transport.baseUrl, + undefined, + e.fallbackVersion as Protocol, + ); + manifest = await this.#transport.toolGet(name, headers); + } else { + throw e; + } + } if ( manifest.tools && @@ -285,7 +312,21 @@ class ToolboxClient { const toolsetName = name || ''; const headers = await this.#resolveClientHeaders(); - const manifest = await this.#transport.toolsList(toolsetName, headers); + let manifest; + try { + manifest = await this.#transport.toolsList(toolsetName, headers); + } catch (e: unknown) { + if (e instanceof ProtocolNegotiationError) { + this.#transport = this.#createTransport( + this.#transport.baseUrl, + undefined, + e.fallbackVersion as Protocol, + ); + manifest = await this.#transport.toolsList(toolsetName, headers); + } else { + throw e; + } + } const tools: ToolboxTool[] = []; const overallUsedAuthKeys: Set = new Set(); diff --git a/packages/toolbox-core/src/toolbox_core/errorUtils.ts b/packages/toolbox-core/src/toolbox_core/errorUtils.ts index e27f90fe..84fb4908 100644 --- a/packages/toolbox-core/src/toolbox_core/errorUtils.ts +++ b/packages/toolbox-core/src/toolbox_core/errorUtils.ts @@ -40,3 +40,13 @@ export function logApiError(baseMessage: string, error: unknown): void { } console.error(baseMessage, loggableDetails); } + +export class ProtocolNegotiationError extends Error { + fallbackVersion: string; + + constructor(fallbackVersion: string) { + super(`Server requires protocol fallback to ${fallbackVersion}`); + this.name = 'ProtocolNegotiationError'; + this.fallbackVersion = fallbackVersion; + } +}