@ai-sdk/mcp
Version:
The **Model Context Protocol (MCP) client** for the [AI SDK](https://ai-sdk.dev/docs) lets you connect to MCP servers and use their tools with AI SDK functions like `generateText` and `streamText`.
1 lines • 166 kB
Source Map (JSON)
{"version":3,"sources":["../src/tool/mcp-client.ts","../src/error/mcp-client-error.ts","../src/tool/mcp-sse-transport.ts","../src/tool/json-rpc-message.ts","../src/tool/types.ts","../src/version.ts","../src/tool/oauth.ts","../src/tool/oauth-types.ts","../src/error/oauth-error.ts","../src/util/oauth-util.ts","../src/tool/mcp-http-transport.ts","../src/tool/mcp-transport.ts"],"sourcesContent":["import type { JSONSchema7, JSONValue } from '@ai-sdk/provider';\nimport {\n asSchema,\n dynamicTool,\n jsonSchema,\n safeParseJSON,\n safeValidateTypes,\n tool,\n type FlexibleSchema,\n type Tool,\n type ToolExecutionOptions,\n type ToolResultOutput,\n} from '@ai-sdk/provider-utils';\nimport type { z } from 'zod/v4';\nimport { MCPClientError } from '../error/mcp-client-error';\nimport type {\n JSONRPCError,\n JSONRPCNotification,\n JSONRPCRequest,\n JSONRPCResponse,\n} from './json-rpc-message';\nimport {\n createMcpTransport,\n isCustomMcpTransport,\n type MCPTransport,\n type MCPTransportConfig,\n} from './mcp-transport';\nimport {\n CallToolResultSchema,\n ElicitationRequestSchema,\n ElicitResultSchema,\n InitializeResultSchema,\n LATEST_PROTOCOL_VERSION,\n ListResourceTemplatesResultSchema,\n ListResourcesResultSchema,\n ListPromptsResultSchema,\n ListToolsResultSchema,\n ReadResourceResultSchema,\n GetPromptResultSchema,\n SUPPORTED_PROTOCOL_VERSIONS,\n type CallToolResult,\n type ClientCapabilities,\n type Configuration,\n type Configuration as ClientConfiguration,\n type ElicitationRequest,\n type ElicitResult,\n type ListResourceTemplatesResult,\n type ListResourcesResult,\n type ListPromptsResult,\n type ListToolsResult,\n type McpToolSet,\n type Notification,\n type PaginatedRequest,\n type ReadResourceResult,\n type GetPromptResult,\n type Request,\n type RequestOptions,\n type ServerCapabilities,\n type ToolSchemas,\n type ToolMeta,\n} from './types';\n\nconst CLIENT_VERSION = '1.0.0';\n\nfunction mcpToModelOutput({\n output,\n}: {\n toolCallId: string;\n input: unknown;\n output: unknown;\n}): ToolResultOutput {\n const result = output as CallToolResult;\n\n if (!('content' in result) || !Array.isArray(result.content)) {\n return { type: 'json', value: result as JSONValue };\n }\n\n const convertedContent = result.content.map(\n (part: { type: string; [key: string]: unknown }) => {\n if (part.type === 'text' && 'text' in part) {\n return { type: 'text' as const, text: part.text as string };\n }\n if (part.type === 'image' && 'data' in part && 'mimeType' in part) {\n return {\n type: 'image-data' as const,\n data: part.data as string,\n mediaType: part.mimeType as string,\n };\n }\n return { type: 'text' as const, text: JSON.stringify(part) };\n },\n );\n\n return { type: 'content', value: convertedContent };\n}\n\nexport interface MCPClientConfig {\n /** Transport configuration for connecting to the MCP server */\n transport: MCPTransportConfig | MCPTransport;\n /** Optional callback for uncaught errors */\n onUncaughtError?: (error: unknown) => void;\n /** Optional client name, defaults to 'ai-sdk-mcp-client' */\n clientName?: string;\n /**\n * Optional client name, defaults to 'ai-sdk-mcp-client'\n *\n * @deprecated Use `clientName` instead.\n */\n name?: string;\n /** Optional client version, defaults to '1.0.0' */\n version?: string;\n /**\n * Optional client capabilities to advertise during initialization\n *\n * NOTE: It is up to the client application to handle the requests properly. This parameter just helps surface the request from the server\n */\n capabilities?: ClientCapabilities;\n}\n\nexport async function createMCPClient(\n config: MCPClientConfig,\n): Promise<MCPClient> {\n const client = new DefaultMCPClient(config);\n await client.init();\n return client;\n}\n\nexport interface MCPClient {\n /**\n * Information about the connected MCP server, as reported during initialization.\n * @see https://modelcontextprotocol.io/specification/2025-11-25/schema#implementation\n */\n readonly serverInfo: Configuration;\n\n /**\n * Optional instructions provided by the server during the initialize handshake.\n *\n * These describe how to use the server and its features, and can be used by clients\n * to improve LLM interactions (e.g. by including them in the system prompt).\n *\n * @see https://modelcontextprotocol.io/specification/2025-11-25/schema#initializeresult\n */\n readonly instructions?: string;\n\n tools<TOOL_SCHEMAS extends ToolSchemas = 'automatic'>(options?: {\n schemas?: TOOL_SCHEMAS;\n }): Promise<McpToolSet<TOOL_SCHEMAS>>;\n\n /**\n * Lists available tools from the MCP server.\n */\n listTools(options?: {\n params?: PaginatedRequest['params'];\n options?: RequestOptions;\n }): Promise<ListToolsResult>;\n\n /**\n * Creates AI SDK tools from tool definitions.\n */\n toolsFromDefinitions<TOOL_SCHEMAS extends ToolSchemas = 'automatic'>(\n definitions: ListToolsResult,\n options?: { schemas?: TOOL_SCHEMAS },\n ): McpToolSet<TOOL_SCHEMAS>;\n\n listResources(options?: {\n params?: PaginatedRequest['params'];\n options?: RequestOptions;\n }): Promise<ListResourcesResult>;\n\n readResource(args: {\n uri: string;\n options?: RequestOptions;\n }): Promise<ReadResourceResult>;\n\n listResourceTemplates(options?: {\n options?: RequestOptions;\n }): Promise<ListResourceTemplatesResult>;\n\n experimental_listPrompts(options?: {\n params?: PaginatedRequest['params'];\n options?: RequestOptions;\n }): Promise<ListPromptsResult>;\n\n experimental_getPrompt(args: {\n name: string;\n arguments?: Record<string, unknown>;\n options?: RequestOptions;\n }): Promise<GetPromptResult>;\n\n onElicitationRequest(\n schema: typeof ElicitationRequestSchema,\n handler: (\n request: ElicitationRequest,\n ) => Promise<ElicitResult> | ElicitResult,\n ): void;\n\n close: () => Promise<void>;\n}\n\n/**\n * A lightweight MCP Client implementation\n *\n * The primary purpose of this client is tool conversion between MCP<>AI SDK\n * but can later be extended to support other MCP features\n *\n * Tool parameters are automatically inferred from the server's JSON schema\n * if not explicitly provided in the tools configuration\n *\n * This client is meant to be used to communicate with a single server. To communicate and fetch tools across multiple servers, it's recommended to create a new client instance per server.\n *\n * Not supported:\n * - Accepting notifications\n * - Session management (when passing a sessionId to an instance of the Streamable HTTP transport)\n * - Resumable SSE streams\n */\nclass DefaultMCPClient implements MCPClient {\n private transport: MCPTransport;\n private onUncaughtError?: (error: unknown) => void;\n private clientInfo: ClientConfiguration;\n private clientCapabilities: ClientCapabilities;\n private requestMessageId = 0;\n private responseHandlers: Map<\n number,\n (response: JSONRPCResponse | Error) => void\n > = new Map();\n private serverCapabilities: ServerCapabilities = {};\n private _serverInfo: Configuration = { name: '', version: '' };\n private _serverInstructions?: string;\n private isClosed = true;\n private elicitationRequestHandler?: (\n request: ElicitationRequest,\n ) => Promise<ElicitResult> | ElicitResult;\n\n constructor({\n transport: transportConfig,\n name,\n clientName = name ?? 'ai-sdk-mcp-client',\n version = CLIENT_VERSION,\n onUncaughtError,\n capabilities,\n }: MCPClientConfig) {\n this.onUncaughtError = onUncaughtError;\n this.clientCapabilities = capabilities ?? {};\n\n if (isCustomMcpTransport(transportConfig)) {\n this.transport = transportConfig;\n } else {\n this.transport = createMcpTransport(transportConfig);\n }\n\n this.transport.onclose = () => this.onClose();\n this.transport.onerror = (error: Error) => this.onError(error);\n this.transport.onmessage = message => {\n if ('method' in message) {\n if ('id' in message) {\n this.onRequestMessage(message);\n } else {\n this.onError(\n new MCPClientError({\n message: 'Unsupported message type',\n }),\n );\n }\n return;\n }\n\n this.onResponse(message);\n };\n\n this.clientInfo = {\n name: clientName,\n version,\n };\n }\n\n get serverInfo(): Configuration {\n return this._serverInfo;\n }\n\n get instructions(): string | undefined {\n return this._serverInstructions;\n }\n\n async init(): Promise<this> {\n try {\n await this.transport.start();\n this.isClosed = false;\n\n const result = await this.request({\n request: {\n method: 'initialize',\n params: {\n protocolVersion: LATEST_PROTOCOL_VERSION,\n capabilities: this.clientCapabilities,\n clientInfo: this.clientInfo,\n },\n },\n resultSchema: InitializeResultSchema,\n });\n\n if (result === undefined) {\n throw new MCPClientError({\n message: 'Server sent invalid initialize result',\n });\n }\n\n if (!SUPPORTED_PROTOCOL_VERSIONS.includes(result.protocolVersion)) {\n throw new MCPClientError({\n message: `Server's protocol version is not supported: ${result.protocolVersion}`,\n });\n }\n\n this.serverCapabilities = result.capabilities;\n this._serverInfo = result.serverInfo;\n if (this.transport.setProtocolVersion) {\n this.transport.setProtocolVersion(result.protocolVersion);\n } else {\n this.transport.protocolVersion = result.protocolVersion;\n }\n this._serverInstructions = result.instructions;\n\n // Complete initialization handshake:\n await this.notification({\n method: 'notifications/initialized',\n });\n\n return this;\n } catch (error) {\n await this.close();\n throw error;\n }\n }\n\n async close(): Promise<void> {\n if (this.isClosed) return;\n await this.transport?.close();\n this.onClose();\n }\n\n private assertCapability(method: string): void {\n switch (method) {\n case 'initialize':\n break;\n case 'tools/list':\n case 'tools/call':\n if (!this.serverCapabilities.tools) {\n throw new MCPClientError({\n message: `Server does not support tools`,\n });\n }\n break;\n case 'resources/list':\n case 'resources/read':\n case 'resources/templates/list':\n if (!this.serverCapabilities.resources) {\n throw new MCPClientError({\n message: `Server does not support resources`,\n });\n }\n break;\n case 'prompts/list':\n case 'prompts/get':\n if (!this.serverCapabilities.prompts) {\n throw new MCPClientError({\n message: `Server does not support prompts`,\n });\n }\n break;\n default:\n throw new MCPClientError({\n message: `Unsupported method: ${method}`,\n });\n }\n }\n\n private async request<T extends z.ZodType<object>>({\n request,\n resultSchema,\n options,\n }: {\n request: Request;\n resultSchema: T;\n options?: RequestOptions;\n }): Promise<z.infer<T>> {\n return new Promise((resolve, reject) => {\n if (this.isClosed) {\n return reject(\n new MCPClientError({\n message: 'Attempted to send a request from a closed client',\n }),\n );\n }\n\n this.assertCapability(request.method);\n\n const signal = options?.signal;\n signal?.throwIfAborted();\n\n const messageId = this.requestMessageId++;\n const jsonrpcRequest: JSONRPCRequest = {\n ...request,\n jsonrpc: '2.0',\n id: messageId,\n };\n\n const cleanup = () => {\n this.responseHandlers.delete(messageId);\n };\n\n this.responseHandlers.set(messageId, response => {\n if (signal?.aborted) {\n return reject(\n new MCPClientError({\n message: 'Request was aborted',\n cause: signal.reason,\n }),\n );\n }\n\n if (response instanceof Error) {\n return reject(response);\n }\n\n try {\n const result = resultSchema.parse(response.result);\n resolve(result);\n } catch (error) {\n const parseError = new MCPClientError({\n message: 'Failed to parse server response',\n cause: error,\n });\n reject(parseError);\n }\n });\n\n this.transport.send(jsonrpcRequest).catch(error => {\n cleanup();\n reject(error);\n });\n });\n }\n\n async listTools({\n params,\n options,\n }: {\n params?: PaginatedRequest['params'];\n options?: RequestOptions;\n } = {}): Promise<ListToolsResult> {\n return this.request({\n request: { method: 'tools/list', params },\n resultSchema: ListToolsResultSchema,\n options,\n });\n }\n\n private async callTool({\n name,\n args,\n options,\n }: {\n name: string;\n args: Record<string, unknown>;\n options?: ToolExecutionOptions;\n }): Promise<CallToolResult> {\n try {\n return this.request({\n request: { method: 'tools/call', params: { name, arguments: args } },\n resultSchema: CallToolResultSchema,\n options: {\n signal: options?.abortSignal,\n },\n });\n } catch (error) {\n throw error;\n }\n }\n\n private async listResourcesInternal({\n params,\n options,\n }: {\n params?: PaginatedRequest['params'];\n options?: RequestOptions;\n } = {}): Promise<ListResourcesResult> {\n try {\n return this.request({\n request: { method: 'resources/list', params },\n resultSchema: ListResourcesResultSchema,\n options,\n });\n } catch (error) {\n throw error;\n }\n }\n\n private async readResourceInternal({\n uri,\n options,\n }: {\n uri: string;\n options?: RequestOptions;\n }): Promise<ReadResourceResult> {\n try {\n return this.request({\n request: { method: 'resources/read', params: { uri } },\n resultSchema: ReadResourceResultSchema,\n options,\n });\n } catch (error) {\n throw error;\n }\n }\n\n private async listResourceTemplatesInternal({\n options,\n }: {\n options?: RequestOptions;\n } = {}): Promise<ListResourceTemplatesResult> {\n try {\n return this.request({\n request: { method: 'resources/templates/list' },\n resultSchema: ListResourceTemplatesResultSchema,\n options,\n });\n } catch (error) {\n throw error;\n }\n }\n\n private async listPromptsInternal({\n params,\n options,\n }: {\n params?: PaginatedRequest['params'];\n options?: RequestOptions;\n } = {}): Promise<ListPromptsResult> {\n try {\n return this.request({\n request: { method: 'prompts/list', params },\n resultSchema: ListPromptsResultSchema,\n options,\n });\n } catch (error) {\n throw error;\n }\n }\n\n private async getPromptInternal({\n name,\n args,\n options,\n }: {\n name: string;\n args?: Record<string, unknown>;\n options?: RequestOptions;\n }): Promise<GetPromptResult> {\n try {\n return this.request({\n request: { method: 'prompts/get', params: { name, arguments: args } },\n resultSchema: GetPromptResultSchema,\n options,\n });\n } catch (error) {\n throw error;\n }\n }\n\n private async notification(notification: Notification): Promise<void> {\n const jsonrpcNotification: JSONRPCNotification = {\n ...notification,\n jsonrpc: '2.0',\n };\n await this.transport.send(jsonrpcNotification);\n }\n\n /**\n * Returns a set of AI SDK tools from the MCP server.\n * This fetches tool definitions and wraps them with execute functions.\n * @returns A record of tool names to their implementations\n */\n async tools<TOOL_SCHEMAS extends ToolSchemas = 'automatic'>({\n schemas = 'automatic',\n }: {\n schemas?: TOOL_SCHEMAS;\n } = {}): Promise<McpToolSet<TOOL_SCHEMAS>> {\n const definitions = await this.listTools();\n return this.toolsFromDefinitions(definitions, {\n schemas,\n } as { schemas?: TOOL_SCHEMAS });\n }\n\n /**\n * Creates AI SDK tools from tool definitions without fetching from the server.\n */\n toolsFromDefinitions<TOOL_SCHEMAS extends ToolSchemas = 'automatic'>(\n definitions: ListToolsResult,\n { schemas = 'automatic' }: { schemas?: TOOL_SCHEMAS } = {} as {\n schemas?: TOOL_SCHEMAS;\n },\n ): McpToolSet<TOOL_SCHEMAS> {\n const tools: Record<string, Tool & { _meta?: ToolMeta }> = {};\n\n for (const {\n name,\n title,\n description,\n inputSchema,\n annotations,\n _meta,\n } of definitions.tools) {\n const resolvedTitle = title ?? annotations?.title;\n if (schemas !== 'automatic' && !(name in schemas)) {\n continue;\n }\n\n const self = this;\n const outputSchema =\n schemas !== 'automatic' ? schemas[name]?.outputSchema : undefined;\n\n const execute = async (\n args: any,\n options: ToolExecutionOptions,\n ): Promise<unknown> => {\n options?.abortSignal?.throwIfAborted();\n const result = await self.callTool({ name, args, options });\n\n if (result.isError) {\n return result;\n }\n\n if (outputSchema != null) {\n return self.extractStructuredContent(result, outputSchema, name);\n }\n\n return result;\n };\n\n const toolWithExecute =\n schemas === 'automatic'\n ? dynamicTool({\n description,\n title: resolvedTitle,\n metadata: {\n clientName: this.clientInfo.name,\n },\n inputSchema: jsonSchema({\n ...inputSchema,\n properties: inputSchema.properties ?? {},\n additionalProperties: false,\n } as JSONSchema7),\n execute,\n toModelOutput: mcpToModelOutput,\n })\n : tool({\n description,\n title: resolvedTitle,\n metadata: {\n clientName: this.clientInfo.name,\n },\n inputSchema: schemas[name].inputSchema,\n ...(outputSchema != null ? { outputSchema } : {}),\n execute,\n toModelOutput: mcpToModelOutput,\n });\n\n tools[name] = { ...toolWithExecute, _meta };\n }\n\n return tools as McpToolSet<TOOL_SCHEMAS>;\n }\n\n /**\n * Extracts and validates structuredContent from a tool result.\n */\n private async extractStructuredContent(\n result: CallToolResult,\n outputSchema: FlexibleSchema<unknown>,\n toolName: string,\n ): Promise<unknown> {\n if ('structuredContent' in result && result.structuredContent != null) {\n const validationResult = await safeValidateTypes({\n value: result.structuredContent,\n schema: asSchema(outputSchema),\n });\n\n if (!validationResult.success) {\n throw new MCPClientError({\n message: `Tool \"${toolName}\" returned structuredContent that does not match the expected outputSchema`,\n cause: validationResult.error,\n });\n }\n\n return validationResult.value;\n }\n\n // Fallback\n if ('content' in result && Array.isArray(result.content)) {\n const textContent = result.content.find(c => c.type === 'text');\n if (textContent && 'text' in textContent) {\n const parseResult = await safeParseJSON({\n text: textContent.text,\n schema: outputSchema,\n });\n\n if (!parseResult.success) {\n throw new MCPClientError({\n message: `Tool \"${toolName}\" returned content that does not match the expected outputSchema`,\n cause: parseResult.error,\n });\n }\n\n return parseResult.value;\n }\n }\n\n throw new MCPClientError({\n message: `Tool \"${toolName}\" did not return structuredContent or parseable text content`,\n });\n }\n\n listResources({\n params,\n options,\n }: {\n params?: PaginatedRequest['params'];\n options?: RequestOptions;\n } = {}): Promise<ListResourcesResult> {\n return this.listResourcesInternal({ params, options });\n }\n\n readResource({\n uri,\n options,\n }: {\n uri: string;\n options?: RequestOptions;\n }): Promise<ReadResourceResult> {\n return this.readResourceInternal({ uri, options });\n }\n\n listResourceTemplates({\n options,\n }: {\n options?: RequestOptions;\n } = {}): Promise<ListResourceTemplatesResult> {\n return this.listResourceTemplatesInternal({ options });\n }\n\n experimental_listPrompts({\n params,\n options,\n }: {\n params?: PaginatedRequest['params'];\n options?: RequestOptions;\n } = {}): Promise<ListPromptsResult> {\n return this.listPromptsInternal({ params, options });\n }\n\n experimental_getPrompt({\n name,\n arguments: args,\n options,\n }: {\n name: string;\n arguments?: Record<string, unknown>;\n options?: RequestOptions;\n }): Promise<GetPromptResult> {\n return this.getPromptInternal({ name, args, options });\n }\n\n onElicitationRequest(\n schema: typeof ElicitationRequestSchema,\n handler: (\n request: ElicitationRequest,\n ) => Promise<ElicitResult> | ElicitResult,\n ): void {\n if (schema !== ElicitationRequestSchema) {\n throw new MCPClientError({\n message:\n 'Unsupported request schema. Only ElicitationRequestSchema is supported.',\n });\n }\n\n this.elicitationRequestHandler = handler;\n }\n\n private async onRequestMessage(request: JSONRPCRequest): Promise<void> {\n try {\n if (request.method === 'ping') {\n await this.transport.send({\n jsonrpc: '2.0',\n id: request.id,\n result: {},\n });\n return;\n }\n\n if (request.method !== 'elicitation/create') {\n await this.transport.send({\n jsonrpc: '2.0',\n id: request.id,\n error: {\n code: -32601,\n message: `Unsupported request method: ${request.method}`,\n },\n });\n return;\n }\n\n if (!this.elicitationRequestHandler) {\n await this.transport.send({\n jsonrpc: '2.0',\n id: request.id,\n error: {\n code: -32601,\n message: 'No elicitation handler registered on client',\n },\n });\n return;\n }\n\n const parsedRequest = ElicitationRequestSchema.safeParse({\n method: request.method,\n params: request.params,\n });\n\n if (!parsedRequest.success) {\n await this.transport.send({\n jsonrpc: '2.0',\n id: request.id,\n error: {\n code: -32602,\n message: `Invalid elicitation request: ${parsedRequest.error.message}`,\n data: parsedRequest.error.issues,\n },\n });\n return;\n }\n\n try {\n const result = await this.elicitationRequestHandler(parsedRequest.data);\n const validatedResult = ElicitResultSchema.parse(result);\n\n await this.transport.send({\n jsonrpc: '2.0',\n id: request.id,\n result: validatedResult,\n });\n } catch (error) {\n await this.transport.send({\n jsonrpc: '2.0',\n id: request.id,\n error: {\n code: -32603,\n message:\n error instanceof Error\n ? error.message\n : 'Failed to handle elicitation request',\n },\n });\n this.onError(error);\n }\n } catch (error) {\n this.onError(error);\n }\n }\n\n private onClose(): void {\n if (this.isClosed) return;\n\n this.isClosed = true;\n const error = new MCPClientError({\n message: 'Connection closed',\n });\n\n for (const handler of this.responseHandlers.values()) {\n handler(error);\n }\n\n this.responseHandlers.clear();\n }\n\n private onError(error: unknown): void {\n if (this.onUncaughtError) {\n this.onUncaughtError(error);\n }\n }\n\n private onResponse(response: JSONRPCResponse | JSONRPCError): void {\n const messageId = Number(response.id);\n const handler = this.responseHandlers.get(messageId);\n\n if (handler === undefined) {\n throw new MCPClientError({\n message: `Protocol error: Received a response for an unknown message ID: ${JSON.stringify(\n response,\n )}`,\n });\n }\n\n this.responseHandlers.delete(messageId);\n\n handler(\n 'result' in response\n ? response\n : new MCPClientError({\n message: response.error.message,\n code: response.error.code,\n data: response.error.data,\n cause: response.error,\n }),\n );\n }\n}\n","import { AISDKError } from '@ai-sdk/provider';\n\nconst name = 'AI_MCPClientError';\nconst marker = `vercel.ai.error.${name}`;\nconst symbol = Symbol.for(marker);\n\n/**\n * An error occurred with the MCP client.\n */\nexport class MCPClientError extends AISDKError {\n private readonly [symbol] = true;\n readonly data?: unknown;\n\n /**\n * JSON-RPC error code from the server response, per the JSON-RPC 2.0\n * spec (e.g. `-32601` method-not-found, `-32602` invalid-params, or\n * MCP-specific codes such as `-32002` resource-not-found). This is the\n * application-level error code populated from `error.code` in the\n * server's JSON-RPC error payload. Distinct from `statusCode`, which\n * is the HTTP transport status.\n */\n readonly code?: number;\n\n /**\n * HTTP status code from the failed response, when the error originated\n * from the streamable HTTP transport. Undefined for stdio transport\n * errors and for failures that do not have an associated response\n * status (e.g. network errors, abort). Distinct from `code`, which is\n * the JSON-RPC application error code.\n */\n readonly statusCode?: number;\n\n /**\n * URL of the MCP endpoint the failing request was sent to, when the\n * error originated from an HTTP transport failure.\n */\n readonly url?: string;\n\n /**\n * Body of the failing HTTP response, decoded as text, when available.\n * Undefined when the body could not be read or the error did not have\n * an associated response.\n */\n readonly responseBody?: string;\n\n constructor({\n name = 'MCPClientError',\n message,\n cause,\n data,\n code,\n statusCode,\n url,\n responseBody,\n }: {\n name?: string;\n message: string;\n cause?: unknown;\n data?: unknown;\n code?: number;\n statusCode?: number;\n url?: string;\n responseBody?: string;\n }) {\n super({ name, message, cause });\n this.data = data;\n this.code = code;\n this.statusCode = statusCode;\n this.url = url;\n this.responseBody = responseBody;\n }\n\n static isInstance(error: unknown): error is MCPClientError {\n return AISDKError.hasMarker(error, marker);\n }\n}\n","import {\n EventSourceParserStream,\n withUserAgentSuffix,\n getRuntimeEnvironmentUserAgent,\n type FetchFunction,\n} from '@ai-sdk/provider-utils';\nimport { MCPClientError } from '../error/mcp-client-error';\nimport { parseJSONRPCMessage, type JSONRPCMessage } from './json-rpc-message';\nimport type { MCPTransport } from './mcp-transport';\nimport { VERSION } from '../version';\nimport {\n extractResourceMetadataUrl,\n UnauthorizedError,\n auth,\n type OAuthClientProvider,\n} from './oauth';\nimport { LATEST_PROTOCOL_VERSION } from './types';\n\nexport class SseMCPTransport implements MCPTransport {\n private endpoint?: URL;\n private abortController?: AbortController;\n private url: URL;\n private connected = false;\n private sseConnection?: {\n close: () => void;\n };\n private headers?: Record<string, string>;\n private authProvider?: OAuthClientProvider;\n private resourceMetadataUrl?: URL;\n private redirectMode: RequestRedirect;\n private fetchFn: FetchFunction;\n\n onclose?: () => void;\n onerror?: (error: unknown) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n protocolVersion?: string;\n\n constructor({\n url,\n headers,\n authProvider,\n redirect = 'follow',\n fetch: fetchFn,\n }: {\n url: string;\n headers?: Record<string, string>;\n authProvider?: OAuthClientProvider;\n redirect?: 'follow' | 'error';\n fetch?: FetchFunction;\n }) {\n this.url = new URL(url);\n this.headers = headers;\n this.authProvider = authProvider;\n this.redirectMode = redirect;\n this.fetchFn = fetchFn ?? globalThis.fetch;\n }\n\n setProtocolVersion(version: string): void {\n this.protocolVersion = version;\n }\n\n private async commonHeaders(\n base: Record<string, string>,\n ): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n ...this.headers,\n ...base,\n 'mcp-protocol-version': this.protocolVersion ?? LATEST_PROTOCOL_VERSION,\n };\n\n if (this.authProvider) {\n const tokens = await this.authProvider.tokens();\n if (tokens?.access_token) {\n headers['Authorization'] = `Bearer ${tokens.access_token}`;\n }\n }\n\n return withUserAgentSuffix(\n headers,\n `ai-sdk/${VERSION}`,\n getRuntimeEnvironmentUserAgent(),\n );\n }\n\n async start(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (this.connected) {\n return resolve();\n }\n\n this.abortController = new AbortController();\n\n const establishConnection = async (triedAuth: boolean = false) => {\n try {\n const headers = await this.commonHeaders({\n Accept: 'text/event-stream',\n });\n const response = await this.fetchFn(this.url.href, {\n headers,\n signal: this.abortController?.signal,\n redirect: this.redirectMode,\n });\n\n if (response.status === 401 && this.authProvider && !triedAuth) {\n this.resourceMetadataUrl = extractResourceMetadataUrl(response);\n try {\n const result = await auth(this.authProvider, {\n serverUrl: this.url,\n resourceMetadataUrl: this.resourceMetadataUrl,\n fetchFn: this.fetchFn,\n });\n if (result !== 'AUTHORIZED') {\n const error = new UnauthorizedError();\n this.onerror?.(error);\n return reject(error);\n }\n } catch (error) {\n this.onerror?.(error);\n return reject(error);\n }\n return establishConnection(true);\n }\n\n if (!response.ok || !response.body) {\n let errorMessage = `MCP SSE Transport Error: ${response.status} ${response.statusText}`;\n\n if (response.status === 405) {\n errorMessage +=\n '. This server does not support SSE transport. Try using `http` transport instead';\n }\n\n const error = new MCPClientError({\n message: errorMessage,\n });\n this.onerror?.(error);\n return reject(error);\n }\n\n const stream = response.body\n .pipeThrough(new TextDecoderStream())\n .pipeThrough(new EventSourceParserStream());\n\n const reader = stream.getReader();\n\n const processEvents = async () => {\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n if (this.connected) {\n this.connected = false;\n throw new MCPClientError({\n message:\n 'MCP SSE Transport Error: Connection closed unexpectedly',\n });\n }\n return;\n }\n\n const { event, data } = value;\n\n if (event === 'endpoint') {\n this.endpoint = new URL(data, this.url);\n\n if (this.endpoint.origin !== this.url.origin) {\n throw new MCPClientError({\n message: `MCP SSE Transport Error: Endpoint origin does not match connection origin: ${this.endpoint.origin}`,\n });\n }\n\n this.connected = true;\n resolve();\n } else if (event === 'message') {\n try {\n const message = await parseJSONRPCMessage(data);\n this.onmessage?.(message);\n } catch (error) {\n const e = new MCPClientError({\n message:\n 'MCP SSE Transport Error: Failed to parse message',\n cause: error,\n });\n this.onerror?.(e);\n // We do not throw here so we continue processing events after reporting the error\n }\n }\n }\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n return;\n }\n\n this.onerror?.(error);\n reject(error);\n }\n };\n\n this.sseConnection = {\n close: () => reader.cancel(),\n };\n\n processEvents();\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n return;\n }\n\n this.onerror?.(error);\n reject(error);\n }\n };\n\n void establishConnection();\n });\n }\n\n async close(): Promise<void> {\n this.connected = false;\n this.sseConnection?.close();\n this.abortController?.abort();\n this.onclose?.();\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n if (!this.endpoint || !this.connected) {\n throw new MCPClientError({\n message: 'MCP SSE Transport Error: Not connected',\n });\n }\n\n const endpoint = this.endpoint as URL;\n\n const attempt = async (triedAuth: boolean = false): Promise<void> => {\n try {\n const headers = await this.commonHeaders({\n 'Content-Type': 'application/json',\n });\n const init = {\n method: 'POST',\n headers,\n body: JSON.stringify(message),\n signal: this.abortController?.signal,\n redirect: this.redirectMode,\n };\n\n const response = await this.fetchFn(endpoint.href, init);\n\n if (response.status === 401 && this.authProvider && !triedAuth) {\n this.resourceMetadataUrl = extractResourceMetadataUrl(response);\n try {\n const result = await auth(this.authProvider, {\n serverUrl: this.url,\n resourceMetadataUrl: this.resourceMetadataUrl,\n fetchFn: this.fetchFn,\n });\n if (result !== 'AUTHORIZED') {\n const error = new UnauthorizedError();\n this.onerror?.(error);\n return;\n }\n } catch (error) {\n this.onerror?.(error);\n return;\n }\n return attempt(true);\n }\n\n if (!response.ok) {\n const text = await response.text().catch(() => null);\n const error = new MCPClientError({\n message: `MCP SSE Transport Error: POSTing to endpoint (HTTP ${response.status}): ${text}`,\n });\n this.onerror?.(error);\n return;\n }\n } catch (error) {\n this.onerror?.(error);\n return;\n }\n };\n await attempt();\n }\n}\n\nexport async function deserializeMessage(\n line: string,\n): Promise<JSONRPCMessage> {\n return parseJSONRPCMessage(line);\n}\n","import { parseJSON } from '@ai-sdk/provider-utils';\nimport { z } from 'zod/v4';\nimport { BaseParamsSchema, RequestSchema, ResultSchema } from './types';\n\nconst JSONRPC_VERSION = '2.0';\n\nconst JSONRPCRequestSchema = z\n .object({\n jsonrpc: z.literal(JSONRPC_VERSION),\n id: z.union([z.string(), z.number().int()]),\n })\n .merge(RequestSchema)\n .strict();\n\nexport type JSONRPCRequest = z.infer<typeof JSONRPCRequestSchema>;\n\nconst JSONRPCResponseSchema = z\n .object({\n jsonrpc: z.literal(JSONRPC_VERSION),\n id: z.union([z.string(), z.number().int()]),\n result: ResultSchema,\n })\n .strict();\n\nexport type JSONRPCResponse = z.infer<typeof JSONRPCResponseSchema>;\n\nconst JSONRPCErrorSchema = z\n .object({\n jsonrpc: z.literal(JSONRPC_VERSION),\n id: z.union([z.string(), z.number().int()]),\n error: z.object({\n code: z.number().int(),\n message: z.string(),\n data: z.optional(z.unknown()),\n }),\n })\n .strict();\n\nexport type JSONRPCError = z.infer<typeof JSONRPCErrorSchema>;\n\nconst JSONRPCNotificationSchema = z\n .object({\n jsonrpc: z.literal(JSONRPC_VERSION),\n })\n .merge(\n z.object({\n method: z.string(),\n params: z.optional(BaseParamsSchema),\n }),\n )\n .strict();\n\nexport type JSONRPCNotification = z.infer<typeof JSONRPCNotificationSchema>;\n\nexport const JSONRPCMessageSchema = z.union([\n JSONRPCRequestSchema,\n JSONRPCNotificationSchema,\n JSONRPCResponseSchema,\n JSONRPCErrorSchema,\n]);\n\nexport type JSONRPCMessage = z.infer<typeof JSONRPCMessageSchema>;\n\nexport async function parseJSONRPCMessage(\n text: string,\n): Promise<JSONRPCMessage> {\n return JSONRPCMessageSchema.parse(await parseJSON({ text }));\n}\n","import { z } from 'zod/v4';\nimport type { JSONObject } from '@ai-sdk/provider';\nimport type { FlexibleSchema, Tool } from '@ai-sdk/provider-utils';\n\nexport const LATEST_PROTOCOL_VERSION = '2025-11-25';\nexport const SUPPORTED_PROTOCOL_VERSIONS = [\n LATEST_PROTOCOL_VERSION,\n '2025-06-18',\n '2025-03-26',\n '2024-11-05',\n];\n\n/** MCP tool metadata - keys should follow MCP _meta key format specification */\nconst ToolMetaSchema = z.optional(z.record(z.string(), z.unknown()));\nexport type ToolMeta = z.infer<typeof ToolMetaSchema>;\n\nexport type ToolSchemas =\n | Record<\n string,\n {\n inputSchema: FlexibleSchema<JSONObject | unknown>;\n outputSchema?: FlexibleSchema<JSONObject | unknown>;\n }\n >\n | 'automatic'\n | undefined;\n\n/** Base MCP tool type with execute and _meta */\ntype McpToolBase<INPUT = unknown, OUTPUT = CallToolResult> = Tool<\n INPUT,\n OUTPUT\n> &\n Required<Pick<Tool<INPUT, OUTPUT>, 'execute'>> & {\n _meta?: ToolMeta;\n };\n\nexport type McpToolSet<TOOL_SCHEMAS extends ToolSchemas = 'automatic'> =\n TOOL_SCHEMAS extends Record<\n string,\n { inputSchema: FlexibleSchema<any>; outputSchema?: FlexibleSchema<any> }\n >\n ? {\n [K in keyof TOOL_SCHEMAS]: TOOL_SCHEMAS[K] extends {\n inputSchema: FlexibleSchema<infer INPUT>;\n outputSchema: FlexibleSchema<infer OUTPUT>;\n }\n ? McpToolBase<INPUT, OUTPUT>\n : TOOL_SCHEMAS[K] extends {\n inputSchema: FlexibleSchema<infer INPUT>;\n }\n ? McpToolBase<INPUT, CallToolResult>\n : never;\n }\n : Record<string, McpToolBase<unknown, CallToolResult>>;\n\nconst ClientOrServerImplementationSchema = z.looseObject({\n name: z.string(),\n version: z.string(),\n title: z.optional(z.string()),\n});\n\n// Maps to `Implementation` in the MCP specification\nexport type Configuration = z.infer<typeof ClientOrServerImplementationSchema>;\n\nexport const BaseParamsSchema = z.looseObject({\n _meta: z.optional(z.object({}).loose()),\n});\ntype BaseParams = z.infer<typeof BaseParamsSchema>;\nexport const ResultSchema = BaseParamsSchema;\n\nexport const RequestSchema = z.object({\n method: z.string(),\n params: z.optional(BaseParamsSchema),\n});\nexport type Request = z.infer<typeof RequestSchema>;\nexport type RequestOptions = {\n signal?: AbortSignal;\n timeout?: number;\n maxTotalTimeout?: number;\n};\n\nexport type Notification = z.infer<typeof RequestSchema>;\n\n/** @see https://modelcontextprotocol.io/specification/2025-06-18/client/elicitation */\nconst ElicitationCapabilitySchema = z\n .object({\n applyDefaults: z.optional(z.boolean()),\n })\n .loose();\n\nconst ServerCapabilitiesSchema = z.looseObject({\n experimental: z.optional(z.object({}).loose()),\n logging: z.optional(z.object({}).loose()),\n prompts: z.optional(\n z.looseObject({\n listChanged: z.optional(z.boolean()),\n }),\n ),\n resources: z.optional(\n z.looseObject({\n subscribe: z.optional(z.boolean()),\n listChanged: z.optional(z.boolean()),\n }),\n ),\n tools: z.optional(\n z.looseObject({\n listChanged: z.optional(z.boolean()),\n }),\n ),\n elicitation: z.optional(ElicitationCapabilitySchema),\n});\n\nexport type ServerCapabilities = z.infer<typeof ServerCapabilitiesSchema>;\nexport const ClientCapabilitiesSchema = z\n .object({\n elicitation: z.optional(ElicitationCapabilitySchema),\n })\n .loose();\n\nexport type ClientCapabilities = z.infer<typeof ClientCapabilitiesSchema>;\nexport type ElicitationCapability = z.infer<typeof ElicitationCapabilitySchema>;\n\nexport const InitializeResultSchema = ResultSchema.extend({\n protocolVersion: z.string(),\n capabilities: ServerCapabilitiesSchema,\n serverInfo: ClientOrServerImplementationSchema,\n instructions: z.optional(z.string()),\n});\nexport type InitializeResult = z.infer<typeof InitializeResultSchema>;\n\nexport type PaginatedRequest = Request & {\n params?: BaseParams & {\n cursor?: string;\n };\n};\n\nconst PaginatedResultSchema = ResultSchema.extend({\n nextCursor: z.optional(z.string()),\n});\n\nconst ToolSchema = z\n .object({\n name: z.string(),\n /**\n * @see https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool\n */\n title: z.optional(z.string()),\n description: z.optional(z.string()),\n inputSchema: z\n .object({\n type: z.literal('object'),\n properties: z.optional(z.object({}).loose()),\n })\n .loose(),\n /**\n * @see https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema\n */\n outputSchema: z.optional(z.object({}).loose()),\n annotations: z.optional(\n z\n .object({\n title: z.optional(z.string()),\n })\n .loose(),\n ),\n _meta: ToolMetaSchema,\n })\n .loose();\nexport type MCPTool = z.infer<typeof ToolSchema>;\nexport const ListToolsResultSchema = PaginatedResultSchema.extend({\n tools: z.array(ToolSchema),\n});\nexport type ListToolsResult = z.infer<typeof ListToolsResultSchema>;\n\nconst TextContentSchema = z\n .object({\n type: z.literal('text'),\n text: z.string(),\n })\n .loose();\nconst ImageContentSchema = z\n .object({\n type: z.literal('image'),\n data: z.base64(),\n mimeType: z.string(),\n })\n .loose();\nexport const ResourceSchema = z\n .object({\n uri: z.string(),\n name: z.string(),\n title: z.optional(z.string()),\n description: z.optional(z.string()),\n mimeType: z.optional(z.string()),\n size: z.optional(z.number()),\n })\n .loose();\nexport type MCPResource = z.infer<typeof ResourceSchema>;\n\nexport const ListResourcesResultSchema = PaginatedResultSchema.extend({\n resources: z.array(ResourceSchema),\n});\nexport type ListResourcesResult = z.infer<typeof ListResourcesResultSchema>;\n\nconst ResourceContentsSchema = z\n .object({\n /**\n * The URI of this resource.\n */\n uri: z.string(),\n /**\n * Optional display name of the resource content.\n */\n name: z.optional(z.string()),\n /**\n * Optional human readable title.\n */\n title: z.optional(z.string()),\n /**\n * The MIME type of this resource, if known.\n */\n mimeType: z.optional(z.string()),\n })\n .loose();\nconst TextResourceContentsSchema = ResourceContentsSchema.extend({\n text: z.string(),\n});\nconst BlobResourceContentsSchema = ResourceContentsSchema.extend({\n blob: z.base64(),\n});\nconst EmbeddedResourceSchema = z\n .object({\n type: z.literal('resource'),\n resource: z.union([TextResourceContentsSchema, BlobResourceContentsSchema]),\n })\n .loose();\nconst ResourceLinkContentSchema = z\n .object({\n type: z.literal('resource_link'),\n uri: z.string(),\n name: z.string(),\n description: z.optional(z.string()),\n mimeType: z.optional(z.string()),\n })\n .loose();\n\nexport const CallToolResultSchema = ResultSchema.extend({\n content: z.array(\n z.union([\n TextContentSchema,\n ImageContentSchema,\n EmbeddedResourceSchema,\n ResourceLinkContentSchema,\n ]),\n ),\n /**\n * @see https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content\n */\n structuredContent: z.optional(z.unknown()),\n isError: z.boolean().default(false).optional(),\n}).or(\n ResultSchema.extend({\n toolResult: z.unknown(),\n }),\n);\nexport type CallToolResult = z.infer<typeof CallToolResultSchema>;\n\nconst ResourceTemplateSchema = z\n .object({\n uriTemplate: z.string(),\n name: z.string(),\n title: z.optional(z.string()),\n description: z.optional(z.string()),\n mimeType: z.optional(z.string()),\n })\n .loose();\n\nexport const ListResourceTemplatesResultSchema = ResultSchema.extend({\n resourceTemplates: z.array(ResourceTemplateSchema),\n});\nexport type ListResourceTemplatesResult = z.infer<\n typeof ListResourceTemplatesResultSchema\n>;\n\nexport const ReadResourceResultSchema = ResultSchema.extend({\n contents: z.array(\n z.union([TextResourceContentsSchema, BlobResourceContentsSchema]),\n ),\n});\nexport type ReadResourceResult = z.infer<typeof ReadResourceResultSchema>;\n\n// Prompts\nconst PromptArgumentSchema = z\n .object({\n name: z.string(),\n description: z.optional(z.string()),\n required: z.optional(z.boolean()),\n })\n .loose();\n\nexport const PromptSchema = z\n .object({\n name: z.string(),\n title: z.optional(z.string()),\n description: z.optional(z.string()),\n arguments: z.optional(z.array(PromptArgumentSchema)),\n })\n .loose();\nexport type MCPPrompt = z.infer<typeof PromptSchema>;\n\nexport const ListPromptsResultSchema = PaginatedResultSchema.extend({\n prompts: z.array(PromptSchema),\n});\nexport type ListPromptsResult = z.infer<typeof ListPromptsResultSchema>;\n\nconst PromptMessageSchema = z\n .object({\n role: z.union([z.literal('user'), z.literal('assistant')]),\n content: z.union([\n TextContentSchema,\n ImageContentSchema,\n EmbeddedResourceSchema,\n ResourceLinkContentSchema,\n ]),\n })\n .loose();\nexport type MCPPromptMessage = z.infer<typeof PromptMessageSchema>;\n\nexport const GetPromptResultSchema = ResultSchema.extend({\n description: z.optional(z.string()),\n messages: z.array(PromptMessageSchema),\n});\nexport type GetPromptResult = z.infer<typeof GetPromptResultSchema>;\n\nconst ElicitationRequestParamsSchema = BaseParamsSchema.extend({\n message: z.string(),\n requestedSchema: z.unknown(),\n});\n\nexport const ElicitationRequestSchema = RequestSchema.extend({\n method: z.literal('elicitation/create'),\n params: ElicitationRequestParamsSchema,\n});\n\nexport type ElicitationRequest = z.infer<typeof ElicitationRequestSchema>;\n\nexport const ElicitResultSchema = ResultSchema.extend({\n action: z.union([\n z.literal('accept'),\n z.literal('decline'),\n z.literal('cancel'),\n ]),\n content: z.optional(z.record(z.string(), z.unknown())),\n});\n\nexport type ElicitResult = z.infer<typeof ElicitResultSchema>;\n","declare const __PACKAGE_VERSION__: string | undefined;\nexport const VERSION: string =\n typeof __PACKAGE_VERSION__ !== 'undefined'\n ? __PACKAGE_VERSION__\n : '0.0.0-test';\n","import pkceChallenge from 'pkce-challenge';\nimport {\n OAuthProtectedResourceMetadataSchema,\n OAuthMetadataSchema,\n OpenIdProviderDiscoveryMetadataSchema,\n OAuthTokensSchema,\n OAuthErrorResponseSchema,\n OAuthClientInformationFullSchema,\n type OAuthTokens,\n type OAuthProtectedResourceMetadata,\n type AuthorizationServerMetadata,\n type OAuthClientInformation,\n type OAuthClientMetadata,\n type OAuthClientInformationFull,\n} from './oauth-types';\nimport {\n MCPClientOAuthError,\n ServerError,\n OAUTH_ERRORS,\n InvalidClientError,\n InvalidGrantError,\n UnauthorizedClientError,\n} from '../error/oauth-error';\nimport {\n resourceUrlFromServerUrl,\n checkResourceAllowed,\n resourceUrlStripSlash,\n} from '../util/oauth-util';\nimport { LATEST_PROTOCOL_VERSION } from './types';\nimport { parseJSON, type FetchFunction } from '@ai-sdk/provider-utils';\n\nexport type AuthResult = 'AUTHORIZED' | 'REDIRECT';\n\nexport interface OAuthAuthorizationServerInformation {\n authorizationServerUrl: string;\n tokenEndpoi