UNPKG

@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,502 lines (1,490 loc) 98.5 kB
// src/tool/json-rpc-message.ts import { parseJSON } from "@ai-sdk/provider-utils"; import { z as z2 } from "zod/v4"; // src/tool/types.ts import { z } from "zod/v4"; var LATEST_PROTOCOL_VERSION = "2025-11-25"; var SUPPORTED_PROTOCOL_VERSIONS = [ LATEST_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05" ]; var ToolMetaSchema = z.optional(z.record(z.string(), z.unknown())); var ClientOrServerImplementationSchema = z.looseObject({ name: z.string(), version: z.string(), title: z.optional(z.string()) }); var BaseParamsSchema = z.looseObject({ _meta: z.optional(z.object({}).loose()) }); var ResultSchema = BaseParamsSchema; var RequestSchema = z.object({ method: z.string(), params: z.optional(BaseParamsSchema) }); var ElicitationCapabilitySchema = z.object({ applyDefaults: z.optional(z.boolean()) }).loose(); var ServerCapabilitiesSchema = z.looseObject({ experimental: z.optional(z.object({}).loose()), logging: z.optional(z.object({}).loose()), completions: z.optional(z.object({}).loose()), prompts: z.optional( z.looseObject({ listChanged: z.optional(z.boolean()) }) ), resources: z.optional( z.looseObject({ subscribe: z.optional(z.boolean()), listChanged: z.optional(z.boolean()) }) ), tools: z.optional( z.looseObject({ listChanged: z.optional(z.boolean()) }) ), elicitation: z.optional(ElicitationCapabilitySchema) }); var ClientCapabilitiesSchema = z.object({ elicitation: z.optional(ElicitationCapabilitySchema) }).loose(); var InitializeResultSchema = ResultSchema.extend({ protocolVersion: z.string(), capabilities: ServerCapabilitiesSchema, serverInfo: ClientOrServerImplementationSchema, instructions: z.optional(z.string()) }); var PaginatedResultSchema = ResultSchema.extend({ nextCursor: z.optional(z.string()) }); var ToolSchema = z.object({ name: z.string(), /** * @see https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool */ title: z.optional(z.string()), description: z.optional(z.string()), inputSchema: z.object({ type: z.literal("object"), properties: z.optional(z.object({}).loose()) }).loose(), /** * @see https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema */ outputSchema: z.optional(z.object({}).loose()), annotations: z.optional( z.object({ title: z.optional(z.string()) }).loose() ), _meta: ToolMetaSchema }).loose(); var ListToolsResultSchema = PaginatedResultSchema.extend({ tools: z.array(ToolSchema) }); var TextContentSchema = z.object({ type: z.literal("text"), text: z.string() }).loose(); var ImageContentSchema = z.object({ type: z.literal("image"), data: z.base64(), mimeType: z.string() }).loose(); var ResourceSchema = z.object({ uri: z.string(), name: z.string(), title: z.optional(z.string()), description: z.optional(z.string()), mimeType: z.optional(z.string()), size: z.optional(z.number()) }).loose(); var ListResourcesResultSchema = PaginatedResultSchema.extend({ resources: z.array(ResourceSchema) }); var ResourceContentsSchema = z.object({ /** * The URI of this resource. */ uri: z.string(), /** * Optional display name of the resource content. */ name: z.optional(z.string()), /** * Optional human readable title. */ title: z.optional(z.string()), /** * The MIME type of this resource, if known. */ mimeType: z.optional(z.string()) }).loose(); var TextResourceContentsSchema = ResourceContentsSchema.extend({ text: z.string() }); var BlobResourceContentsSchema = ResourceContentsSchema.extend({ blob: z.base64() }); var EmbeddedResourceSchema = z.object({ type: z.literal("resource"), resource: z.union([TextResourceContentsSchema, BlobResourceContentsSchema]) }).loose(); var ResourceLinkContentSchema = z.object({ type: z.literal("resource_link"), uri: z.string(), name: z.string(), description: z.optional(z.string()), mimeType: z.optional(z.string()) }).loose(); var CallToolResultSchema = ResultSchema.extend({ content: z.array( z.union([ TextContentSchema, ImageContentSchema, EmbeddedResourceSchema, ResourceLinkContentSchema ]) ), /** * @see https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content */ structuredContent: z.optional(z.unknown()), isError: z.boolean().default(false).optional() }).or( ResultSchema.extend({ toolResult: z.unknown() }) ); var ResourceTemplateSchema = z.object({ uriTemplate: z.string(), name: z.string(), title: z.optional(z.string()), description: z.optional(z.string()), mimeType: z.optional(z.string()) }).loose(); var ListResourceTemplatesResultSchema = ResultSchema.extend({ resourceTemplates: z.array(ResourceTemplateSchema) }); var ReadResourceResultSchema = ResultSchema.extend({ contents: z.array( z.union([TextResourceContentsSchema, BlobResourceContentsSchema]) ) }); var PromptReferenceSchema = z.object({ type: z.literal("ref/prompt"), name: z.string() }).loose(); var ResourceReferenceSchema = z.object({ type: z.literal("ref/resource"), uri: z.string() }).loose(); var CompletionArgumentSchema = z.object({ name: z.string(), value: z.string() }).loose(); var CompleteRequestParamsSchema = BaseParamsSchema.extend({ ref: z.union([PromptReferenceSchema, ResourceReferenceSchema]), argument: CompletionArgumentSchema, context: z.optional( z.object({ arguments: z.record(z.string(), z.string()) }).loose() ) }); var CompleteResultSchema = ResultSchema.extend({ completion: z.object({ values: z.array(z.string()).max(100), total: z.optional(z.number().int()), hasMore: z.optional(z.boolean()) }).loose() }); var PromptArgumentSchema = z.object({ name: z.string(), description: z.optional(z.string()), required: z.optional(z.boolean()) }).loose(); var PromptSchema = z.object({ name: z.string(), title: z.optional(z.string()), description: z.optional(z.string()), arguments: z.optional(z.array(PromptArgumentSchema)) }).loose(); var ListPromptsResultSchema = PaginatedResultSchema.extend({ prompts: z.array(PromptSchema) }); var PromptMessageSchema = z.object({ role: z.union([z.literal("user"), z.literal("assistant")]), content: z.union([ TextContentSchema, ImageContentSchema, EmbeddedResourceSchema, ResourceLinkContentSchema ]) }).loose(); var GetPromptResultSchema = ResultSchema.extend({ description: z.optional(z.string()), messages: z.array(PromptMessageSchema) }); var ElicitationRequestParamsSchema = BaseParamsSchema.extend({ message: z.string(), requestedSchema: z.unknown() }); var ElicitationRequestSchema = RequestSchema.extend({ method: z.literal("elicitation/create"), params: ElicitationRequestParamsSchema }); var ElicitResultSchema = ResultSchema.extend({ action: z.union([ z.literal("accept"), z.literal("decline"), z.literal("cancel") ]), content: z.optional(z.record(z.string(), z.unknown())) }); // src/tool/json-rpc-message.ts var JSONRPC_VERSION = "2.0"; var JSONRPCRequestSchema = z2.object({ jsonrpc: z2.literal(JSONRPC_VERSION), id: z2.union([z2.string(), z2.number().int()]) }).merge(RequestSchema).strict(); var JSONRPCResponseSchema = z2.object({ jsonrpc: z2.literal(JSONRPC_VERSION), id: z2.union([z2.string(), z2.number().int()]), result: ResultSchema }).strict(); var JSONRPCErrorSchema = z2.object({ jsonrpc: z2.literal(JSONRPC_VERSION), id: z2.union([z2.string(), z2.number().int()]), error: z2.object({ code: z2.number().int(), message: z2.string(), data: z2.optional(z2.unknown()) }) }).strict(); var JSONRPCNotificationSchema = z2.object({ jsonrpc: z2.literal(JSONRPC_VERSION) }).merge( z2.object({ method: z2.string(), params: z2.optional(BaseParamsSchema) }) ).strict(); var JSONRPCMessageSchema = z2.union([ JSONRPCRequestSchema, JSONRPCNotificationSchema, JSONRPCResponseSchema, JSONRPCErrorSchema ]); function validateJSONRPCMessage(message) { return JSONRPCMessageSchema.parse(message); } async function parseJSONRPCMessage(text) { return validateJSONRPCMessage(await parseJSON({ text })); } // src/tool/mcp-client.ts import { asSchema, dynamicTool, jsonSchema, retryWithExponentialBackoff, safeParseJSON, safeValidateTypes, tool } from "@ai-sdk/provider-utils"; // src/error/mcp-client-error.ts import { AISDKError } from "@ai-sdk/provider"; var name = "AI_MCPClientError"; var marker = `vercel.ai.error.${name}`; var symbol = Symbol.for(marker); var _a, _b; var MCPClientError = class extends (_b = AISDKError, _a = symbol, _b) { constructor({ name: name3 = "MCPClientError", message, cause, data, code, statusCode, url, responseBody }) { super({ name: name3, message, cause }); this[_a] = true; this.data = data; this.code = code; this.statusCode = statusCode; this.url = url; this.responseBody = responseBody; } static isInstance(error) { return AISDKError.hasMarker(error, marker); } }; // src/tool/mcp-sse-transport.ts import { EventSourceParserStream, withUserAgentSuffix, getRuntimeEnvironmentUserAgent } from "@ai-sdk/provider-utils"; // src/version.ts var VERSION = typeof __PACKAGE_VERSION__ !== "undefined" ? __PACKAGE_VERSION__ : "0.0.0-test"; // src/tool/oauth.ts import pkceChallenge from "pkce-challenge"; // src/tool/oauth-types.ts import { z as z3 } from "zod/v4"; var SafeUrlSchema = z3.string().url().superRefine((val, ctx) => { if (!URL.canParse(val)) { ctx.addIssue({ code: z3.ZodIssueCode.custom, message: "URL must be parseable", fatal: true }); return z3.NEVER; } }).refine( (url) => { const parsedUrl = new URL(url); return parsedUrl.protocol !== "javascript:" && parsedUrl.protocol !== "data:" && parsedUrl.protocol !== "vbscript:"; }, { message: "URL cannot use javascript:, data:, or vbscript: scheme" } ); var OAuthTokensSchema = z3.object({ access_token: z3.string(), id_token: z3.string().optional(), // Optional for OAuth 2.1, but necessary in OpenID Connect token_type: z3.string(), expires_in: z3.number().optional(), scope: z3.string().optional(), refresh_token: z3.string().optional(), authorization_server: SafeUrlSchema.optional(), token_endpoint: SafeUrlSchema.optional() }).strip(); var OAuthProtectedResourceMetadataSchema = z3.looseObject({ resource: z3.string().url(), authorization_servers: z3.array(SafeUrlSchema).optional(), jwks_uri: z3.string().url().optional(), scopes_supported: z3.array(z3.string()).optional(), bearer_methods_supported: z3.array(z3.string()).optional(), resource_signing_alg_values_supported: z3.array(z3.string()).optional(), resource_name: z3.string().optional(), resource_documentation: z3.string().optional(), resource_policy_uri: z3.string().url().optional(), resource_tos_uri: z3.string().url().optional(), tls_client_certificate_bound_access_tokens: z3.boolean().optional(), authorization_details_types_supported: z3.array(z3.string()).optional(), dpop_signing_alg_values_supported: z3.array(z3.string()).optional(), dpop_bound_access_tokens_required: z3.boolean().optional() }); var OAuthMetadataSchema = z3.looseObject({ issuer: z3.string(), authorization_endpoint: SafeUrlSchema, token_endpoint: SafeUrlSchema, registration_endpoint: SafeUrlSchema.optional(), scopes_supported: z3.array(z3.string()).optional(), response_types_supported: z3.array(z3.string()), grant_types_supported: z3.array(z3.string()).optional(), code_challenge_methods_supported: z3.array(z3.string()).optional(), token_endpoint_auth_methods_supported: z3.array(z3.string()).optional(), token_endpoint_auth_signing_alg_values_supported: z3.array(z3.string()).optional() }); var OpenIdProviderMetadataSchema = z3.looseObject({ issuer: z3.string(), authorization_endpoint: SafeUrlSchema, token_endpoint: SafeUrlSchema, userinfo_endpoint: SafeUrlSchema.optional(), jwks_uri: SafeUrlSchema, registration_endpoint: SafeUrlSchema.optional(), scopes_supported: z3.array(z3.string()).optional(), response_types_supported: z3.array(z3.string()), grant_types_supported: z3.array(z3.string()).optional(), subject_types_supported: z3.array(z3.string()), id_token_signing_alg_values_supported: z3.array(z3.string()), claims_supported: z3.array(z3.string()).optional(), token_endpoint_auth_methods_supported: z3.array(z3.string()).optional() }); var OpenIdProviderDiscoveryMetadataSchema = OpenIdProviderMetadataSchema.merge( OAuthMetadataSchema.pick({ code_challenge_methods_supported: true }) ); var OAuthClientInformationSchema = z3.object({ client_id: z3.string(), client_secret: z3.string().optional(), client_id_issued_at: z3.number().optional(), client_secret_expires_at: z3.number().optional(), authorization_server: SafeUrlSchema.optional(), token_endpoint: SafeUrlSchema.optional() }).strip(); var OAuthClientMetadataSchema = z3.object({ redirect_uris: z3.array(SafeUrlSchema), token_endpoint_auth_method: z3.string().optional(), grant_types: z3.array(z3.string()).optional(), response_types: z3.array(z3.string()).optional(), client_name: z3.string().optional(), client_uri: SafeUrlSchema.optional(), logo_uri: SafeUrlSchema.optional(), scope: z3.string().optional(), contacts: z3.array(z3.string()).optional(), tos_uri: SafeUrlSchema.optional(), policy_uri: z3.string().optional(), jwks_uri: SafeUrlSchema.optional(), jwks: z3.any().optional(), software_id: z3.string().optional(), software_version: z3.string().optional(), software_statement: z3.string().optional() }).strip(); var OAuthErrorResponseSchema = z3.object({ error: z3.string(), error_description: z3.string().optional(), error_uri: z3.string().optional() }); var OAuthClientInformationFullSchema = OAuthClientMetadataSchema.merge( OAuthClientInformationSchema ); // src/error/oauth-error.ts import { AISDKError as AISDKError2 } from "@ai-sdk/provider"; var name2 = "AI_MCPClientOAuthError"; var marker2 = `vercel.ai.error.${name2}`; var symbol2 = Symbol.for(marker2); var _a2, _b2; var MCPClientOAuthError = class extends (_b2 = AISDKError2, _a2 = symbol2, _b2) { constructor({ name: name3 = "MCPClientOAuthError", message, cause }) { super({ name: name3, message, cause }); this[_a2] = true; } static isInstance(error) { return AISDKError2.hasMarker(error, marker2); } }; var ServerError = class extends MCPClientOAuthError { }; ServerError.errorCode = "server_error"; var InvalidClientError = class extends MCPClientOAuthError { }; InvalidClientError.errorCode = "invalid_client"; var InvalidGrantError = class extends MCPClientOAuthError { }; InvalidGrantError.errorCode = "invalid_grant"; var UnauthorizedClientError = class extends MCPClientOAuthError { }; UnauthorizedClientError.errorCode = "unauthorized_client"; var OAUTH_ERRORS = { [ServerError.errorCode]: ServerError, [InvalidClientError.errorCode]: InvalidClientError, [InvalidGrantError.errorCode]: InvalidGrantError, [UnauthorizedClientError.errorCode]: UnauthorizedClientError }; // src/util/oauth-util.ts function resourceUrlFromServerUrl(url) { const resourceURL = typeof url === "string" ? new URL(url) : new URL(url.href); resourceURL.hash = ""; return resourceURL; } function resourceUrlStripSlash(resource) { const href = resource.href; if (resource.pathname === "/" && href.endsWith("/")) { return href.slice(0, -1); } return href; } function checkResourceAllowed({ requestedResource, configuredResource }) { const requested = typeof requestedResource === "string" ? new URL(requestedResource) : new URL(requestedResource.href); const configured = typeof configuredResource === "string" ? new URL(configuredResource) : new URL(configuredResource.href); if (requested.origin !== configured.origin) { return false; } if (requested.pathname.length < configured.pathname.length) { return false; } const requestedPath = requested.pathname.endsWith("/") ? requested.pathname : requested.pathname + "/"; const configuredPath = configured.pathname.endsWith("/") ? configured.pathname : configured.pathname + "/"; return requestedPath.startsWith(configuredPath); } // src/tool/oauth.ts import { parseJSON as parseJSON2 } from "@ai-sdk/provider-utils"; var UnauthorizedError = class extends Error { constructor(message = "Unauthorized") { super(message); this.name = "UnauthorizedError"; } }; function normalizeUrl(url) { return new URL(url).href; } function createAuthorizationServerInformation(authorizationServerUrl, metadata) { return { authorizationServerUrl: normalizeUrl(authorizationServerUrl), tokenEndpoint: normalizeUrl( (metadata == null ? void 0 : metadata.token_endpoint) ? new URL(metadata.token_endpoint) : new URL("/token", authorizationServerUrl) ) }; } function addAuthorizationServerInformationToTokens(tokens, authorizationServerInformation) { return { ...tokens, authorization_server: authorizationServerInformation.authorizationServerUrl, token_endpoint: authorizationServerInformation.tokenEndpoint }; } function addAuthorizationServerInformationToClientInformation(clientInformation, authorizationServerInformation) { return { ...clientInformation, authorization_server: authorizationServerInformation.authorizationServerUrl, token_endpoint: authorizationServerInformation.tokenEndpoint }; } function getAuthorizationServerInformationFromCredentials(credentials) { if (!(credentials == null ? void 0 : credentials.authorization_server) || !credentials.token_endpoint) { return void 0; } return { authorizationServerUrl: normalizeUrl(credentials.authorization_server), tokenEndpoint: normalizeUrl(credentials.token_endpoint) }; } async function getStoredAuthorizationServerInformation({ provider, clientInformation, tokens }) { var _a3; const tokenAuthorizationServerInformation = getAuthorizationServerInformationFromCredentials(tokens); if (tokenAuthorizationServerInformation) { return tokenAuthorizationServerInformation; } const providerAuthorizationServerInformation = await ((_a3 = provider.authorizationServerInformation) == null ? void 0 : _a3.call(provider)); if (providerAuthorizationServerInformation) { return { authorizationServerUrl: normalizeUrl( providerAuthorizationServerInformation.authorizationServerUrl ), tokenEndpoint: normalizeUrl( providerAuthorizationServerInformation.tokenEndpoint ) }; } return getAuthorizationServerInformationFromCredentials(clientInformation); } async function saveAuthorizationServerInformation({ provider, clientInformation, authorizationServerInformation }) { if (provider.saveAuthorizationServerInformation) { await provider.saveAuthorizationServerInformation( authorizationServerInformation ); return true; } if (provider.saveClientInformation) { await provider.saveClientInformation( addAuthorizationServerInformationToClientInformation( clientInformation, authorizationServerInformation ) ); return true; } return false; } function assertResourceMetadataUrlSameOrigin(serverUrl, resourceMetadataUrl) { if (!resourceMetadataUrl) { return; } const expectedOrigin = new URL(serverUrl).origin; if (resourceMetadataUrl.origin !== expectedOrigin) { throw new MCPClientOAuthError({ message: `OAuth protected resource metadata URL ${resourceMetadataUrl.href} must have the same origin as the MCP server URL ${expectedOrigin}` }); } } function assertAuthorizationServerInformationMatches({ storedAuthorizationServerInformation, currentAuthorizationServerInformation }) { if (storedAuthorizationServerInformation.authorizationServerUrl !== currentAuthorizationServerInformation.authorizationServerUrl || storedAuthorizationServerInformation.tokenEndpoint !== currentAuthorizationServerInformation.tokenEndpoint) { throw new MCPClientOAuthError({ message: "OAuth authorization server metadata does not match the metadata that issued the stored credentials" }); } } function extractWWWAuthenticateParams(response) { var _a3, _b3; const header = (_a3 = response.headers.get("www-authenticate")) != null ? _a3 : response.headers.get("WWW-Authenticate"); if (!header) { return {}; } const [type, scheme] = header.split(" "); if (type.toLowerCase() !== "bearer" || !scheme) { return {}; } const resourceMetadataMatch = header.match( /(?:^|[,\s])resource_metadata="([^"]*)"/i ); const scope = (_b3 = header.match(/(?:^|[,\s])scope="([^"]*)"/i)) == null ? void 0 : _b3[1]; let resourceMetadataUrl; try { resourceMetadataUrl = resourceMetadataMatch ? new URL(resourceMetadataMatch[1]) : void 0; } catch (e) { } return { resourceMetadataUrl, scope }; } function selectScope({ scope, resourceMetadata, clientMetadata }) { var _a3; if (scope) { return scope; } const resourceScopes = (_a3 = resourceMetadata == null ? void 0 : resourceMetadata.scopes_supported) == null ? void 0 : _a3.join(" "); if (resourceScopes) { return resourceScopes; } return clientMetadata.scope; } function buildWellKnownPath(wellKnownPrefix, pathname = "", options = {}) { if (pathname.endsWith("/")) { pathname = pathname.slice(0, -1); } return options.prependPathname ? `${pathname}/.well-known/${wellKnownPrefix}` : `/.well-known/${wellKnownPrefix}${pathname}`; } async function fetchWithCorsRetry(url, headers, fetchFn = fetch) { try { return await fetchFn(url, { headers }); } catch (error) { if (error instanceof TypeError) { if (headers) { return fetchWithCorsRetry(url, void 0, fetchFn); } else { return void 0; } } throw error; } } async function tryMetadataDiscovery(url, protocolVersion, fetchFn = fetch) { const headers = { "MCP-Protocol-Version": protocolVersion }; return await fetchWithCorsRetry(url, headers, fetchFn); } function shouldAttemptFallback(response, pathname) { return !response || response.status >= 400 && response.status < 500 && pathname !== "/"; } async function discoverMetadataWithFallback(serverUrl, wellKnownType, fetchFn, opts) { var _a3, _b3; const issuer = new URL(serverUrl); const protocolVersion = (_a3 = opts == null ? void 0 : opts.protocolVersion) != null ? _a3 : LATEST_PROTOCOL_VERSION; let url; if (opts == null ? void 0 : opts.metadataUrl) { url = new URL(opts.metadataUrl); } else { const wellKnownPath = buildWellKnownPath(wellKnownType, issuer.pathname); url = new URL(wellKnownPath, (_b3 = opts == null ? void 0 : opts.metadataServerUrl) != null ? _b3 : issuer); url.search = issuer.search; } let response = await tryMetadataDiscovery(url, protocolVersion, fetchFn); if (!(opts == null ? void 0 : opts.metadataUrl) && shouldAttemptFallback(response, issuer.pathname)) { const rootUrl = new URL(`/.well-known/${wellKnownType}`, issuer); response = await tryMetadataDiscovery(rootUrl, protocolVersion, fetchFn); } return response; } async function discoverOAuthProtectedResourceMetadata(serverUrl, opts, fetchFn = fetch) { const response = await discoverMetadataWithFallback( serverUrl, "oauth-protected-resource", fetchFn, { protocolVersion: opts == null ? void 0 : opts.protocolVersion, metadataUrl: opts == null ? void 0 : opts.resourceMetadataUrl } ); if (!response || response.status === 404) { throw new Error( `Resource server does not implement OAuth 2.0 Protected Resource Metadata.` ); } if (!response.ok) { throw new Error( `HTTP ${response.status} trying to load well-known OAuth protected resource metadata.` ); } return OAuthProtectedResourceMetadataSchema.parse(await response.json()); } function buildDiscoveryUrls(authorizationServerUrl) { const url = typeof authorizationServerUrl === "string" ? new URL(authorizationServerUrl) : authorizationServerUrl; const hasPath = url.pathname !== "/"; const rootIssuer = url.origin; const urlsToTry = []; if (!hasPath) { urlsToTry.push({ url: new URL("/.well-known/oauth-authorization-server", url.origin), type: "oauth", expectedIssuer: rootIssuer }); urlsToTry.push({ url: new URL("/.well-known/openid-configuration", url.origin), type: "oidc", expectedIssuer: rootIssuer }); return urlsToTry; } let pathname = url.pathname; if (pathname.endsWith("/")) { pathname = pathname.slice(0, -1); } const pathIssuer = `${url.origin}${pathname}`; urlsToTry.push({ url: new URL( `/.well-known/oauth-authorization-server${pathname}`, url.origin ), type: "oauth", expectedIssuer: pathIssuer }); urlsToTry.push({ url: new URL("/.well-known/oauth-authorization-server", url.origin), type: "oauth", expectedIssuer: rootIssuer }); urlsToTry.push({ url: new URL(`/.well-known/openid-configuration${pathname}`, url.origin), type: "oidc", expectedIssuer: pathIssuer }); urlsToTry.push({ url: new URL(`${pathname}/.well-known/openid-configuration`, url.origin), type: "oidc", expectedIssuer: pathIssuer }); return urlsToTry; } function assertMetadataIssuerMatches(metadata, expectedIssuer) { if (metadata.issuer !== expectedIssuer) { throw new MCPClientOAuthError({ message: `OAuth authorization server metadata issuer ${metadata.issuer} does not match expected issuer ${expectedIssuer}` }); } } async function discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn = fetch, protocolVersion = LATEST_PROTOCOL_VERSION } = {}) { var _a3; const headers = { "MCP-Protocol-Version": protocolVersion }; const urlsToTry = buildDiscoveryUrls(authorizationServerUrl); for (const { url: endpointUrl, type, expectedIssuer } of urlsToTry) { const response = await fetchWithCorsRetry(endpointUrl, headers, fetchFn); if (!response) { continue; } if (!response.ok) { if (response.status >= 400 && response.status < 500) { continue; } throw new Error( `HTTP ${response.status} trying to load ${type === "oauth" ? "OAuth" : "OpenID provider"} metadata from ${endpointUrl}` ); } if (type === "oauth") { const metadata = OAuthMetadataSchema.parse(await response.json()); assertMetadataIssuerMatches(metadata, expectedIssuer); return metadata; } else { const metadata = OpenIdProviderDiscoveryMetadataSchema.parse( await response.json() ); assertMetadataIssuerMatches(metadata, expectedIssuer); if (!((_a3 = metadata.code_challenge_methods_supported) == null ? void 0 : _a3.includes("S256"))) { throw new Error( `Incompatible OIDC provider at ${endpointUrl}: does not support S256 code challenge method required by MCP specification` ); } return metadata; } } return void 0; } async function startAuthorization(authorizationServerUrl, { metadata, clientInformation, redirectUrl, scope, state, resource }) { const responseType = "code"; const codeChallengeMethod = "S256"; let authorizationUrl; if (metadata) { authorizationUrl = new URL(metadata.authorization_endpoint); if (!metadata.response_types_supported.includes(responseType)) { throw new Error( `Incompatible auth server: does not support response type ${responseType}` ); } if (!metadata.code_challenge_methods_supported || !metadata.code_challenge_methods_supported.includes(codeChallengeMethod)) { throw new Error( `Incompatible auth server: does not support code challenge method ${codeChallengeMethod}` ); } } else { authorizationUrl = new URL("/authorize", authorizationServerUrl); } const challenge = await pkceChallenge(); const codeVerifier = challenge.code_verifier; const codeChallenge = challenge.code_challenge; authorizationUrl.searchParams.set("response_type", responseType); authorizationUrl.searchParams.set("client_id", clientInformation.client_id); authorizationUrl.searchParams.set("code_challenge", codeChallenge); authorizationUrl.searchParams.set( "code_challenge_method", codeChallengeMethod ); authorizationUrl.searchParams.set("redirect_uri", String(redirectUrl)); if (state) { authorizationUrl.searchParams.set("state", state); } if (scope) { authorizationUrl.searchParams.set("scope", scope); } if (scope == null ? void 0 : scope.includes("offline_access")) { authorizationUrl.searchParams.append("prompt", "consent"); } if (resource) { authorizationUrl.searchParams.set( "resource", resourceUrlStripSlash(resource) ); } return { authorizationUrl, codeVerifier }; } function selectClientAuthMethod(clientInformation, supportedMethods) { const hasClientSecret = clientInformation.client_secret !== void 0; if (supportedMethods.length === 0) { return hasClientSecret ? "client_secret_post" : "none"; } if (hasClientSecret && supportedMethods.includes("client_secret_basic")) { return "client_secret_basic"; } if (hasClientSecret && supportedMethods.includes("client_secret_post")) { return "client_secret_post"; } if (supportedMethods.includes("none")) { return "none"; } return hasClientSecret ? "client_secret_post" : "none"; } function applyClientAuthentication(method, clientInformation, headers, params) { const { client_id, client_secret } = clientInformation; switch (method) { case "client_secret_basic": applyBasicAuth(client_id, client_secret, headers); return; case "client_secret_post": applyPostAuth(client_id, client_secret, params); return; case "none": applyPublicAuth(client_id, params); return; default: throw new Error(`Unsupported client authentication method: ${method}`); } } function applyBasicAuth(clientId, clientSecret, headers) { if (!clientSecret) { throw new Error( "client_secret_basic authentication requires a client_secret" ); } const credentials = btoa(`${clientId}:${clientSecret}`); headers.set("Authorization", `Basic ${credentials}`); } function applyPostAuth(clientId, clientSecret, params) { params.set("client_id", clientId); if (clientSecret) { params.set("client_secret", clientSecret); } } function applyPublicAuth(clientId, params) { params.set("client_id", clientId); } async function parseErrorResponse(input) { const statusCode = input instanceof Response ? input.status : void 0; const body = input instanceof Response ? await input.text() : input; try { const result = OAuthErrorResponseSchema.parse( await parseJSON2({ text: body }) ); const { error, error_description, error_uri } = result; const errorClass = OAUTH_ERRORS[error] || ServerError; return new errorClass({ message: error_description || "", cause: error_uri }); } catch (error) { const errorMessage = `${statusCode ? `HTTP ${statusCode}: ` : ""}Invalid OAuth error response: ${error}. Raw body: ${body}`; return new ServerError({ message: errorMessage }); } } async function exchangeAuthorization(authorizationServerUrl, { metadata, clientInformation, authorizationCode, codeVerifier, redirectUri, resource, addClientAuthentication, fetchFn }) { var _a3; const grantType = "authorization_code"; const tokenUrl = (metadata == null ? void 0 : metadata.token_endpoint) ? new URL(metadata.token_endpoint) : new URL("/token", authorizationServerUrl); if ((metadata == null ? void 0 : metadata.grant_types_supported) && !metadata.grant_types_supported.includes(grantType)) { throw new Error( `Incompatible auth server: does not support grant type ${grantType}` ); } const headers = new Headers({ "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" }); const params = new URLSearchParams({ grant_type: grantType, code: authorizationCode, code_verifier: codeVerifier, redirect_uri: String(redirectUri) }); if (addClientAuthentication) { await addClientAuthentication( headers, params, authorizationServerUrl, metadata ); } else { const supportedMethods = (_a3 = metadata == null ? void 0 : metadata.token_endpoint_auth_methods_supported) != null ? _a3 : []; const authMethod = selectClientAuthMethod( clientInformation, supportedMethods ); applyClientAuthentication(authMethod, clientInformation, headers, params); } if (resource) { params.set("resource", resourceUrlStripSlash(resource)); } const response = await (fetchFn != null ? fetchFn : fetch)(tokenUrl, { method: "POST", headers, body: params }); if (!response.ok) { throw await parseErrorResponse(response); } return OAuthTokensSchema.parse(await response.json()); } async function refreshAuthorization(authorizationServerUrl, { metadata, clientInformation, refreshToken, resource, addClientAuthentication, fetchFn }) { var _a3; const grantType = "refresh_token"; let tokenUrl; if (metadata) { tokenUrl = new URL(metadata.token_endpoint); if (metadata.grant_types_supported && !metadata.grant_types_supported.includes(grantType)) { throw new Error( `Incompatible auth server: does not support grant type ${grantType}` ); } } else { tokenUrl = new URL("/token", authorizationServerUrl); } const headers = new Headers({ "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" }); const params = new URLSearchParams({ grant_type: grantType, refresh_token: refreshToken }); if (addClientAuthentication) { await addClientAuthentication( headers, params, authorizationServerUrl, metadata ); } else { const supportedMethods = (_a3 = metadata == null ? void 0 : metadata.token_endpoint_auth_methods_supported) != null ? _a3 : []; const authMethod = selectClientAuthMethod( clientInformation, supportedMethods ); applyClientAuthentication(authMethod, clientInformation, headers, params); } if (resource) { params.set("resource", resourceUrlStripSlash(resource)); } const response = await (fetchFn != null ? fetchFn : fetch)(tokenUrl, { method: "POST", headers, body: params }); if (!response.ok) { throw await parseErrorResponse(response); } return OAuthTokensSchema.parse({ refresh_token: refreshToken, ...await response.json() }); } async function registerClient(authorizationServerUrl, { metadata, clientMetadata, fetchFn }) { let registrationUrl; if (metadata) { if (!metadata.registration_endpoint) { throw new Error( "Incompatible auth server: does not support dynamic client registration" ); } registrationUrl = new URL(metadata.registration_endpoint); } else { registrationUrl = new URL("/register", authorizationServerUrl); } const response = await (fetchFn != null ? fetchFn : fetch)(registrationUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(clientMetadata) }); if (!response.ok) { throw await parseErrorResponse(response); } return OAuthClientInformationFullSchema.parse(await response.json()); } async function auth(provider, options) { var _a3, _b3; try { return await authInternal(provider, options); } catch (error) { if (error instanceof InvalidClientError || error instanceof UnauthorizedClientError) { await ((_a3 = provider.invalidateCredentials) == null ? void 0 : _a3.call(provider, "all")); return await authInternal(provider, options); } else if (error instanceof InvalidGrantError) { await ((_b3 = provider.invalidateCredentials) == null ? void 0 : _b3.call(provider, "tokens")); return await authInternal(provider, options); } throw error; } } async function selectResourceURL(serverUrl, provider, resourceMetadata) { const defaultResource = resourceUrlFromServerUrl(serverUrl); if (provider.validateResourceURL) { return await provider.validateResourceURL( defaultResource, resourceMetadata == null ? void 0 : resourceMetadata.resource ); } if (!resourceMetadata) { return void 0; } if (!checkResourceAllowed({ requestedResource: defaultResource, configuredResource: resourceMetadata.resource })) { throw new Error( `Protected resource ${resourceMetadata.resource} does not match expected ${defaultResource} (or origin)` ); } return new URL(resourceMetadata.resource); } async function authInternal(provider, { serverUrl, authorizationCode, callbackState, scope, resourceMetadataUrl, fetchFn }) { var _a3, _b3; let resourceMetadata; let authorizationServerUrl; assertResourceMetadataUrlSameOrigin(serverUrl, resourceMetadataUrl); try { resourceMetadata = await discoverOAuthProtectedResourceMetadata( serverUrl, { resourceMetadataUrl }, fetchFn ); if (resourceMetadata.authorization_servers && resourceMetadata.authorization_servers.length > 0) { authorizationServerUrl = resourceMetadata.authorization_servers[0]; } } catch (e) { } if (!authorizationServerUrl) { authorizationServerUrl = serverUrl; } const resource = await selectResourceURL( serverUrl, provider, resourceMetadata ); await ((_a3 = provider.validateAuthorizationServerURL) == null ? void 0 : _a3.call( provider, serverUrl, authorizationServerUrl )); const metadata = await discoverAuthorizationServerMetadata( authorizationServerUrl, { fetchFn } ); const currentAuthorizationServerInformation = createAuthorizationServerInformation(authorizationServerUrl, metadata); let clientInformation = await Promise.resolve(provider.clientInformation()); if (!clientInformation) { if (authorizationCode !== void 0) { throw new Error( "Existing OAuth client information is required when exchanging an authorization code" ); } if (!provider.saveClientInformation) { throw new Error( "OAuth client information must be saveable for dynamic registration" ); } const fullInformation = await registerClient(authorizationServerUrl, { metadata, clientMetadata: provider.clientMetadata, fetchFn }); clientInformation = addAuthorizationServerInformationToClientInformation( fullInformation, currentAuthorizationServerInformation ); await provider.saveClientInformation(clientInformation); } if (authorizationCode !== void 0) { if (provider.storedState) { const expectedState = await provider.storedState(); if (expectedState !== void 0 && expectedState !== callbackState) { throw new Error( "OAuth state parameter mismatch - possible CSRF attack" ); } } const storedAuthorizationServerInformation = await getStoredAuthorizationServerInformation({ provider, clientInformation }); if (!storedAuthorizationServerInformation) { throw new MCPClientOAuthError({ message: "Stored OAuth authorization server metadata is required when exchanging an authorization code" }); } assertAuthorizationServerInformationMatches({ storedAuthorizationServerInformation, currentAuthorizationServerInformation }); const codeVerifier2 = await provider.codeVerifier(); const tokens2 = await exchangeAuthorization(authorizationServerUrl, { metadata, clientInformation, authorizationCode, codeVerifier: codeVerifier2, redirectUri: provider.redirectUrl, resource, addClientAuthentication: provider.addClientAuthentication, fetchFn }); await provider.saveTokens( addAuthorizationServerInformationToTokens( tokens2, currentAuthorizationServerInformation ) ); return "AUTHORIZED"; } const tokens = await provider.tokens(); if (tokens == null ? void 0 : tokens.refresh_token) { const storedAuthorizationServerInformation = await getStoredAuthorizationServerInformation({ provider, clientInformation, tokens }); if (storedAuthorizationServerInformation) { assertAuthorizationServerInformationMatches({ storedAuthorizationServerInformation, currentAuthorizationServerInformation }); } else { await ((_b3 = provider.invalidateCredentials) == null ? void 0 : _b3.call(provider, "tokens")); } try { if (storedAuthorizationServerInformation) { const newTokens = await refreshAuthorization(authorizationServerUrl, { metadata, clientInformation, refreshToken: tokens.refresh_token, resource, addClientAuthentication: provider.addClientAuthentication, fetchFn }); await provider.saveTokens( addAuthorizationServerInformationToTokens( newTokens, currentAuthorizationServerInformation ) ); return "AUTHORIZED"; } } catch (error) { if ( // If this is a ServerError, or an unknown type, log it out and try to continue. Otherwise, escalate so we can fix things and retry. !(error instanceof MCPClientOAuthError) || error instanceof ServerError ) { } else { throw error; } } } const state = provider.state ? await provider.state() : void 0; if (state && provider.saveState) { await provider.saveState(state); } const { authorizationUrl, codeVerifier } = await startAuthorization( authorizationServerUrl, { metadata, clientInformation, state, redirectUrl: provider.redirectUrl, scope: selectScope({ scope, resourceMetadata, clientMetadata: provider.clientMetadata }), resource } ); const savedAuthorizationServerInformation = await saveAuthorizationServerInformation({ provider, clientInformation, authorizationServerInformation: currentAuthorizationServerInformation }); if (!savedAuthorizationServerInformation) { throw new MCPClientOAuthError({ message: "OAuth authorization server metadata must be saveable before starting authorization" }); } await provider.saveCodeVerifier(codeVerifier); await provider.redirectToAuthorization(authorizationUrl); return "REDIRECT"; } // src/tool/mcp-sse-transport.ts function isMessageEvent(event) { return event === void 0 || event === "message"; } var SseMCPTransport = class { constructor({ url, headers, authProvider, redirect = "error", fetch: fetchFn }) { this.connected = false; this.url = new URL(url); this.headers = headers; this.authProvider = authProvider; this.redirectMode = redirect; this.fetchFn = fetchFn != null ? fetchFn : globalThis.fetch; } setProtocolVersion(version) { this.protocolVersion = version; } async commonHeaders(base) { var _a3; const headers = { ...this.headers, ...base, "mcp-protocol-version": (_a3 = this.protocolVersion) != null ? _a3 : LATEST_PROTOCOL_VERSION }; if (this.authProvider) { const tokens = await this.authProvider.tokens(); if (tokens == null ? void 0 : tokens.access_token) { headers["Authorization"] = `Bearer ${tokens.access_token}`; } } return withUserAgentSuffix( headers, `ai-sdk/${VERSION}`, getRuntimeEnvironmentUserAgent() ); } async start() { return new Promise((resolve, reject) => { if (this.connected) { return resolve(); } this.abortController = new AbortController(); const establishConnection = async (triedAuth = false) => { var _a3, _b3, _c, _d, _e; try { const headers = await this.commonHeaders({ Accept: "text/event-stream" }); const response = await this.fetchFn(this.url.href, { headers, signal: (_a3 = this.abortController) == null ? void 0 : _a3.signal, redirect: this.redirectMode }); if (response.status === 401 && this.authProvider && !triedAuth) { const { resourceMetadataUrl, scope } = extractWWWAuthenticateParams(response); this.resourceMetadataUrl = resourceMetadataUrl; try { const result = await auth(this.authProvider, { serverUrl: this.url, resourceMetadataUrl: this.resourceMetadataUrl, scope, fetchFn: this.fetchFn }); if (result !== "AUTHORIZED") { const error = new UnauthorizedError(); (_b3 = this.onerror) == null ? void 0 : _b3.call(this, error); return reject(error); } } catch (error) { (_c = this.onerror) == null ? void 0 : _c.call(this, error); return reject(error); } return establishConnection(true); } if (!response.ok || !response.body) { let errorMessage = `MCP SSE Transport Error: ${response.status} ${response.statusText}`; if (response.status === 405) { errorMessage += ". This server does not support SSE transport. Try using `http` transport instead"; } const error = new MCPClientError({ message: errorMessage }); (_d = this.onerror) == null ? void 0 : _d.call(this, error); return reject(error); } const stream = response.body.pipeThrough(new TextDecoderStream()).pipeThrough(new EventSourceParserStream()); const reader = stream.getReader(); const processEvents = async () => { var _a4, _b4, _c2, _d2, _e2; try { while (true) { const { done, value } = await reader.read(); if (done) { if (this.connected) { this.connected = false; throw new MCPClientError({ message: "MCP SSE Transport Error: Connection closed unexpectedly" }); } return; } const { event, data } = value; if (event === "endpoint") { if (this.endpoint) { continue; } const endpoint = new URL(data, this.url); if (endpoint.origin !== this.url.origin) { this.connected = false; this.endpoint = void 0; (_a4 = this.sseConnection) == null ? void 0 : _a4.close(); (_b4 = this.abortController) == null ? void 0 : _b4.abort(); throw new MCPClientError({ message: `MCP SSE Transport Error: Endpoint origin does not match connection origin: ${endpoint.origin}` }); } this.endpoint = endpoint; this.connected = true; resolve(); } else if (isMessageEvent(event)) { try { const message = await parseJSONRPCMessage(data); (_c2 = this.onmessage) == null ? void 0 : _c2.call(this, message); } catch (error) { const e = new MCPClientError({ message: "MCP SSE Transport Error: Failed to parse message", cause: error }); (_d2 = this.onerror) == null ? void 0 : _d2.call(this, e); } } } } catch (error) { if (error instanceof Error && error.name === "AbortError") { return; } (_e2 = this.onerror) == null ? void 0 : _e2.call(this, error); reject(error); } }; this.sseConnection = { close: () => reader.cancel() }; processEvents(); } catch (error) { if (error instanceof Error && error.name === "AbortError") { return; } (_e = this.onerror) == null ? void 0 : _e.call(this, error); reject(error); } }; void establishConnection(); }); } async close() { var _a3, _b3, _c; this.connected = false; this.endpoint = void 0; (_a3 = this.sseConnection) == null ? void 0 : _a3.close(); (_b3 = this.abortController) == null ? void 0 : _b3.abort(); (_c = this.onclose) == null ? void 0 : _c.call(this); } async send(message, options) { var _a3, _b3; (_a3 = options == null ? void 0 : options.signal) == null ? void 0 : _a3.throwIfAborted(); if (!this.endpoint || !this.connected) { throw new MCPClientError({ message: "MCP SSE Transport Error: Not connected" }); } const endpoint = this.endpoint; const transportSignal = (_b3 = this.abortController) == null ? void 0 : _b3.signal; const requestSignal = (options == null ? void 0 : options.signal) == null ? transportSignal : transportSignal == null ? options.signal : AbortSignal.any([transportSignal, options.signal]); const attempt = async (triedAuth = false) => { var _a4, _b4, _c, _d, _e; try { const headers = await this.commonHeade