@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,429 lines (1,413 loc) • 76.2 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name3 in all)
__defProp(target, name3, { get: all[name3], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
ElicitResultSchema: () => ElicitResultSchema,
ElicitationRequestSchema: () => ElicitationRequestSchema,
UnauthorizedError: () => UnauthorizedError,
auth: () => auth,
createMCPClient: () => createMCPClient,
experimental_createMCPClient: () => createMCPClient
});
module.exports = __toCommonJS(index_exports);
// src/tool/mcp-client.ts
var import_provider_utils5 = require("@ai-sdk/provider-utils");
// src/error/mcp-client-error.ts
var import_provider = require("@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 = import_provider.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 import_provider.AISDKError.hasMarker(error, marker);
}
};
// src/tool/mcp-sse-transport.ts
var import_provider_utils3 = require("@ai-sdk/provider-utils");
// src/tool/json-rpc-message.ts
var import_provider_utils = require("@ai-sdk/provider-utils");
var import_v42 = require("zod/v4");
// src/tool/types.ts
var import_v4 = require("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 = import_v4.z.optional(import_v4.z.record(import_v4.z.string(), import_v4.z.unknown()));
var ClientOrServerImplementationSchema = import_v4.z.looseObject({
name: import_v4.z.string(),
version: import_v4.z.string(),
title: import_v4.z.optional(import_v4.z.string())
});
var BaseParamsSchema = import_v4.z.looseObject({
_meta: import_v4.z.optional(import_v4.z.object({}).loose())
});
var ResultSchema = BaseParamsSchema;
var RequestSchema = import_v4.z.object({
method: import_v4.z.string(),
params: import_v4.z.optional(BaseParamsSchema)
});
var ElicitationCapabilitySchema = import_v4.z.object({
applyDefaults: import_v4.z.optional(import_v4.z.boolean())
}).loose();
var ServerCapabilitiesSchema = import_v4.z.looseObject({
experimental: import_v4.z.optional(import_v4.z.object({}).loose()),
logging: import_v4.z.optional(import_v4.z.object({}).loose()),
prompts: import_v4.z.optional(
import_v4.z.looseObject({
listChanged: import_v4.z.optional(import_v4.z.boolean())
})
),
resources: import_v4.z.optional(
import_v4.z.looseObject({
subscribe: import_v4.z.optional(import_v4.z.boolean()),
listChanged: import_v4.z.optional(import_v4.z.boolean())
})
),
tools: import_v4.z.optional(
import_v4.z.looseObject({
listChanged: import_v4.z.optional(import_v4.z.boolean())
})
),
elicitation: import_v4.z.optional(ElicitationCapabilitySchema)
});
var ClientCapabilitiesSchema = import_v4.z.object({
elicitation: import_v4.z.optional(ElicitationCapabilitySchema)
}).loose();
var InitializeResultSchema = ResultSchema.extend({
protocolVersion: import_v4.z.string(),
capabilities: ServerCapabilitiesSchema,
serverInfo: ClientOrServerImplementationSchema,
instructions: import_v4.z.optional(import_v4.z.string())
});
var PaginatedResultSchema = ResultSchema.extend({
nextCursor: import_v4.z.optional(import_v4.z.string())
});
var ToolSchema = import_v4.z.object({
name: import_v4.z.string(),
/**
* @see https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool
*/
title: import_v4.z.optional(import_v4.z.string()),
description: import_v4.z.optional(import_v4.z.string()),
inputSchema: import_v4.z.object({
type: import_v4.z.literal("object"),
properties: import_v4.z.optional(import_v4.z.object({}).loose())
}).loose(),
/**
* @see https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema
*/
outputSchema: import_v4.z.optional(import_v4.z.object({}).loose()),
annotations: import_v4.z.optional(
import_v4.z.object({
title: import_v4.z.optional(import_v4.z.string())
}).loose()
),
_meta: ToolMetaSchema
}).loose();
var ListToolsResultSchema = PaginatedResultSchema.extend({
tools: import_v4.z.array(ToolSchema)
});
var TextContentSchema = import_v4.z.object({
type: import_v4.z.literal("text"),
text: import_v4.z.string()
}).loose();
var ImageContentSchema = import_v4.z.object({
type: import_v4.z.literal("image"),
data: import_v4.z.base64(),
mimeType: import_v4.z.string()
}).loose();
var ResourceSchema = import_v4.z.object({
uri: import_v4.z.string(),
name: import_v4.z.string(),
title: import_v4.z.optional(import_v4.z.string()),
description: import_v4.z.optional(import_v4.z.string()),
mimeType: import_v4.z.optional(import_v4.z.string()),
size: import_v4.z.optional(import_v4.z.number())
}).loose();
var ListResourcesResultSchema = PaginatedResultSchema.extend({
resources: import_v4.z.array(ResourceSchema)
});
var ResourceContentsSchema = import_v4.z.object({
/**
* The URI of this resource.
*/
uri: import_v4.z.string(),
/**
* Optional display name of the resource content.
*/
name: import_v4.z.optional(import_v4.z.string()),
/**
* Optional human readable title.
*/
title: import_v4.z.optional(import_v4.z.string()),
/**
* The MIME type of this resource, if known.
*/
mimeType: import_v4.z.optional(import_v4.z.string())
}).loose();
var TextResourceContentsSchema = ResourceContentsSchema.extend({
text: import_v4.z.string()
});
var BlobResourceContentsSchema = ResourceContentsSchema.extend({
blob: import_v4.z.base64()
});
var EmbeddedResourceSchema = import_v4.z.object({
type: import_v4.z.literal("resource"),
resource: import_v4.z.union([TextResourceContentsSchema, BlobResourceContentsSchema])
}).loose();
var ResourceLinkContentSchema = import_v4.z.object({
type: import_v4.z.literal("resource_link"),
uri: import_v4.z.string(),
name: import_v4.z.string(),
description: import_v4.z.optional(import_v4.z.string()),
mimeType: import_v4.z.optional(import_v4.z.string())
}).loose();
var CallToolResultSchema = ResultSchema.extend({
content: import_v4.z.array(
import_v4.z.union([
TextContentSchema,
ImageContentSchema,
EmbeddedResourceSchema,
ResourceLinkContentSchema
])
),
/**
* @see https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content
*/
structuredContent: import_v4.z.optional(import_v4.z.unknown()),
isError: import_v4.z.boolean().default(false).optional()
}).or(
ResultSchema.extend({
toolResult: import_v4.z.unknown()
})
);
var ResourceTemplateSchema = import_v4.z.object({
uriTemplate: import_v4.z.string(),
name: import_v4.z.string(),
title: import_v4.z.optional(import_v4.z.string()),
description: import_v4.z.optional(import_v4.z.string()),
mimeType: import_v4.z.optional(import_v4.z.string())
}).loose();
var ListResourceTemplatesResultSchema = ResultSchema.extend({
resourceTemplates: import_v4.z.array(ResourceTemplateSchema)
});
var ReadResourceResultSchema = ResultSchema.extend({
contents: import_v4.z.array(
import_v4.z.union([TextResourceContentsSchema, BlobResourceContentsSchema])
)
});
var PromptArgumentSchema = import_v4.z.object({
name: import_v4.z.string(),
description: import_v4.z.optional(import_v4.z.string()),
required: import_v4.z.optional(import_v4.z.boolean())
}).loose();
var PromptSchema = import_v4.z.object({
name: import_v4.z.string(),
title: import_v4.z.optional(import_v4.z.string()),
description: import_v4.z.optional(import_v4.z.string()),
arguments: import_v4.z.optional(import_v4.z.array(PromptArgumentSchema))
}).loose();
var ListPromptsResultSchema = PaginatedResultSchema.extend({
prompts: import_v4.z.array(PromptSchema)
});
var PromptMessageSchema = import_v4.z.object({
role: import_v4.z.union([import_v4.z.literal("user"), import_v4.z.literal("assistant")]),
content: import_v4.z.union([
TextContentSchema,
ImageContentSchema,
EmbeddedResourceSchema,
ResourceLinkContentSchema
])
}).loose();
var GetPromptResultSchema = ResultSchema.extend({
description: import_v4.z.optional(import_v4.z.string()),
messages: import_v4.z.array(PromptMessageSchema)
});
var ElicitationRequestParamsSchema = BaseParamsSchema.extend({
message: import_v4.z.string(),
requestedSchema: import_v4.z.unknown()
});
var ElicitationRequestSchema = RequestSchema.extend({
method: import_v4.z.literal("elicitation/create"),
params: ElicitationRequestParamsSchema
});
var ElicitResultSchema = ResultSchema.extend({
action: import_v4.z.union([
import_v4.z.literal("accept"),
import_v4.z.literal("decline"),
import_v4.z.literal("cancel")
]),
content: import_v4.z.optional(import_v4.z.record(import_v4.z.string(), import_v4.z.unknown()))
});
// src/tool/json-rpc-message.ts
var JSONRPC_VERSION = "2.0";
var JSONRPCRequestSchema = import_v42.z.object({
jsonrpc: import_v42.z.literal(JSONRPC_VERSION),
id: import_v42.z.union([import_v42.z.string(), import_v42.z.number().int()])
}).merge(RequestSchema).strict();
var JSONRPCResponseSchema = import_v42.z.object({
jsonrpc: import_v42.z.literal(JSONRPC_VERSION),
id: import_v42.z.union([import_v42.z.string(), import_v42.z.number().int()]),
result: ResultSchema
}).strict();
var JSONRPCErrorSchema = import_v42.z.object({
jsonrpc: import_v42.z.literal(JSONRPC_VERSION),
id: import_v42.z.union([import_v42.z.string(), import_v42.z.number().int()]),
error: import_v42.z.object({
code: import_v42.z.number().int(),
message: import_v42.z.string(),
data: import_v42.z.optional(import_v42.z.unknown())
})
}).strict();
var JSONRPCNotificationSchema = import_v42.z.object({
jsonrpc: import_v42.z.literal(JSONRPC_VERSION)
}).merge(
import_v42.z.object({
method: import_v42.z.string(),
params: import_v42.z.optional(BaseParamsSchema)
})
).strict();
var JSONRPCMessageSchema = import_v42.z.union([
JSONRPCRequestSchema,
JSONRPCNotificationSchema,
JSONRPCResponseSchema,
JSONRPCErrorSchema
]);
async function parseJSONRPCMessage(text) {
return JSONRPCMessageSchema.parse(await (0, import_provider_utils.parseJSON)({ text }));
}
// src/version.ts
var VERSION = typeof __PACKAGE_VERSION__ !== "undefined" ? __PACKAGE_VERSION__ : "0.0.0-test";
// src/tool/oauth.ts
var import_pkce_challenge = __toESM(require("pkce-challenge"));
// src/tool/oauth-types.ts
var import_v43 = require("zod/v4");
var OAuthTokensSchema = import_v43.z.object({
access_token: import_v43.z.string(),
id_token: import_v43.z.string().optional(),
// Optional for OAuth 2.1, but necessary in OpenID Connect
token_type: import_v43.z.string(),
expires_in: import_v43.z.number().optional(),
scope: import_v43.z.string().optional(),
refresh_token: import_v43.z.string().optional()
}).strip();
var SafeUrlSchema = import_v43.z.string().url().superRefine((val, ctx) => {
if (!URL.canParse(val)) {
ctx.addIssue({
code: import_v43.z.ZodIssueCode.custom,
message: "URL must be parseable",
fatal: true
});
return import_v43.z.NEVER;
}
}).refine(
(url) => {
const u = new URL(url);
return u.protocol !== "javascript:" && u.protocol !== "data:" && u.protocol !== "vbscript:";
},
{ message: "URL cannot use javascript:, data:, or vbscript: scheme" }
);
var OAuthProtectedResourceMetadataSchema = import_v43.z.object({
resource: import_v43.z.string().url(),
authorization_servers: import_v43.z.array(SafeUrlSchema).optional(),
jwks_uri: import_v43.z.string().url().optional(),
scopes_supported: import_v43.z.array(import_v43.z.string()).optional(),
bearer_methods_supported: import_v43.z.array(import_v43.z.string()).optional(),
resource_signing_alg_values_supported: import_v43.z.array(import_v43.z.string()).optional(),
resource_name: import_v43.z.string().optional(),
resource_documentation: import_v43.z.string().optional(),
resource_policy_uri: import_v43.z.string().url().optional(),
resource_tos_uri: import_v43.z.string().url().optional(),
tls_client_certificate_bound_access_tokens: import_v43.z.boolean().optional(),
authorization_details_types_supported: import_v43.z.array(import_v43.z.string()).optional(),
dpop_signing_alg_values_supported: import_v43.z.array(import_v43.z.string()).optional(),
dpop_bound_access_tokens_required: import_v43.z.boolean().optional()
}).passthrough();
var OAuthMetadataSchema = import_v43.z.object({
issuer: import_v43.z.string(),
authorization_endpoint: SafeUrlSchema,
token_endpoint: SafeUrlSchema,
registration_endpoint: SafeUrlSchema.optional(),
scopes_supported: import_v43.z.array(import_v43.z.string()).optional(),
response_types_supported: import_v43.z.array(import_v43.z.string()),
grant_types_supported: import_v43.z.array(import_v43.z.string()).optional(),
code_challenge_methods_supported: import_v43.z.array(import_v43.z.string()),
token_endpoint_auth_methods_supported: import_v43.z.array(import_v43.z.string()).optional(),
token_endpoint_auth_signing_alg_values_supported: import_v43.z.array(import_v43.z.string()).optional()
}).passthrough();
var OpenIdProviderMetadataSchema = import_v43.z.object({
issuer: import_v43.z.string(),
authorization_endpoint: SafeUrlSchema,
token_endpoint: SafeUrlSchema,
userinfo_endpoint: SafeUrlSchema.optional(),
jwks_uri: SafeUrlSchema,
registration_endpoint: SafeUrlSchema.optional(),
scopes_supported: import_v43.z.array(import_v43.z.string()).optional(),
response_types_supported: import_v43.z.array(import_v43.z.string()),
grant_types_supported: import_v43.z.array(import_v43.z.string()).optional(),
subject_types_supported: import_v43.z.array(import_v43.z.string()),
id_token_signing_alg_values_supported: import_v43.z.array(import_v43.z.string()),
claims_supported: import_v43.z.array(import_v43.z.string()).optional(),
token_endpoint_auth_methods_supported: import_v43.z.array(import_v43.z.string()).optional()
}).passthrough();
var OpenIdProviderDiscoveryMetadataSchema = OpenIdProviderMetadataSchema.merge(
OAuthMetadataSchema.pick({
code_challenge_methods_supported: true
})
);
var OAuthClientInformationSchema = import_v43.z.object({
client_id: import_v43.z.string(),
client_secret: import_v43.z.string().optional(),
client_id_issued_at: import_v43.z.number().optional(),
client_secret_expires_at: import_v43.z.number().optional()
}).strip();
var OAuthClientMetadataSchema = import_v43.z.object({
redirect_uris: import_v43.z.array(SafeUrlSchema),
token_endpoint_auth_method: import_v43.z.string().optional(),
grant_types: import_v43.z.array(import_v43.z.string()).optional(),
response_types: import_v43.z.array(import_v43.z.string()).optional(),
client_name: import_v43.z.string().optional(),
client_uri: SafeUrlSchema.optional(),
logo_uri: SafeUrlSchema.optional(),
scope: import_v43.z.string().optional(),
contacts: import_v43.z.array(import_v43.z.string()).optional(),
tos_uri: SafeUrlSchema.optional(),
policy_uri: import_v43.z.string().optional(),
jwks_uri: SafeUrlSchema.optional(),
jwks: import_v43.z.any().optional(),
software_id: import_v43.z.string().optional(),
software_version: import_v43.z.string().optional(),
software_statement: import_v43.z.string().optional()
}).strip();
var OAuthErrorResponseSchema = import_v43.z.object({
error: import_v43.z.string(),
error_description: import_v43.z.string().optional(),
error_uri: import_v43.z.string().optional()
});
var OAuthClientInformationFullSchema = OAuthClientMetadataSchema.merge(
OAuthClientInformationSchema
);
// src/error/oauth-error.ts
var import_provider2 = require("@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 = import_provider2.AISDKError, _a2 = symbol2, _b2) {
constructor({
name: name3 = "MCPClientOAuthError",
message,
cause
}) {
super({ name: name3, message, cause });
this[_a2] = true;
}
static isInstance(error) {
return import_provider2.AISDKError.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
var import_provider_utils2 = require("@ai-sdk/provider-utils");
var UnauthorizedError = class extends Error {
constructor(message = "Unauthorized") {
super(message);
this.name = "UnauthorizedError";
}
};
function extractResourceMetadataUrl(response) {
var _a3;
const header = (_a3 = response.headers.get("www-authenticate")) != null ? _a3 : response.headers.get("WWW-Authenticate");
if (!header) {
return void 0;
}
const [type, scheme] = header.split(" ");
if (type.toLowerCase() !== "bearer" || !scheme) {
return void 0;
}
const regex = /resource_metadata="([^"]*)"/;
const match = header.match(regex);
if (!match) {
return void 0;
}
try {
return new URL(match[1]);
} catch (e) {
return void 0;
}
}
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 urlsToTry = [];
if (!hasPath) {
urlsToTry.push({
url: new URL("/.well-known/oauth-authorization-server", url.origin),
type: "oauth"
});
urlsToTry.push({
url: new URL("/.well-known/openid-configuration", url.origin),
type: "oidc"
});
return urlsToTry;
}
let pathname = url.pathname;
if (pathname.endsWith("/")) {
pathname = pathname.slice(0, -1);
}
urlsToTry.push({
url: new URL(
`/.well-known/oauth-authorization-server${pathname}`,
url.origin
),
type: "oauth"
});
urlsToTry.push({
url: new URL("/.well-known/oauth-authorization-server", url.origin),
type: "oauth"
});
urlsToTry.push({
url: new URL(`/.well-known/openid-configuration${pathname}`, url.origin),
type: "oidc"
});
urlsToTry.push({
url: new URL(`${pathname}/.well-known/openid-configuration`, url.origin),
type: "oidc"
});
return urlsToTry;
}
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 } 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") {
return OAuthMetadataSchema.parse(await response.json());
} else {
const metadata = OpenIdProviderDiscoveryMetadataSchema.parse(
await response.json()
);
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 (0, import_pkce_challenge.default)();
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 (0, import_provider_utils2.parseJSON)({ 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
}) {
let resourceMetadata;
let authorizationServerUrl;
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
);
const metadata = await discoverAuthorizationServerMetadata(
authorizationServerUrl,
{
fetchFn
}
);
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
});
await provider.saveClientInformation(fullInformation);
clientInformation = fullInformation;
}
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 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(tokens2);
return "AUTHORIZED";
}
const tokens = await provider.tokens();
if (tokens == null ? void 0 : tokens.refresh_token) {
try {
const newTokens = await refreshAuthorization(authorizationServerUrl, {
metadata,
clientInformation,
refreshToken: tokens.refresh_token,
resource,
addClientAuthentication: provider.addClientAuthentication,
fetchFn
});
await provider.saveTokens(newTokens);
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: scope || provider.clientMetadata.scope,
resource
}
);
await provider.saveCodeVerifier(codeVerifier);
await provider.redirectToAuthorization(authorizationUrl);
return "REDIRECT";
}
// src/tool/mcp-sse-transport.ts
var SseMCPTransport = class {
constructor({
url,
headers,
authProvider,
redirect = "follow",
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;
}
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 (0, import_provider_utils3.withUserAgentSuffix)(
headers,
`ai-sdk/${VERSION}`,
(0, import_provider_utils3.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) {
this.resourceMetadataUrl = extractResourceMetadataUrl(response);
try {
const result = await auth(this.authProvider, {
serverUrl: this.url,
resourceMetadataUrl: this.resourceMetadataUrl,
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 import_provider_utils3.EventSourceParserStream());
const reader = stream.getReader();
const processEvents = async () => {
var _a4, _b4, _c2;
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") {
this.endpoint = new URL(data, this.url);
if (this.endpoint.origin !== this.url.origin) {
throw new MCPClientError({
message: `MCP SSE Transport Error: Endpoint origin does not match connection origin: ${this.endpoint.origin}`
});
}
this.connected = true;
resolve();
} else if (event === "message") {
try {
const message = await parseJSONRPCMessage(data);
(_a4 = this.onmessage) == null ? void 0 : _a4.call(this, message);
} catch (error) {
const e = new MCPClientError({
message: "MCP SSE Transport Error: Failed to parse message",
cause: error
});
(_b4 = this.onerror) == null ? void 0 : _b4.call(this, e);
}
}
}
} catch (error) {
if (error instanceof Error && error.name === "AbortError") {
return;
}
(_c2 = this.onerror) == null ? void 0 : _c2.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;
(_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) {
if (!this.endpoint || !this.connected) {
throw new MCPClientError({
message: "MCP SSE Transport Error: Not connected"
});
}
const endpoint = this.endpoint;
const attempt = async (triedAuth = false) => {
var _a3, _b3, _c, _d, _e;
try {
const headers = await this.commonHeaders({
"Content-Type": "application/json"
});
const init = {
method: "POST",
headers,
body: JSON.stringify(message),
signal: (_a3 = this.abortController) == null ? void 0 : _a3.signal,
redirect: this.redirectMode
};
const response = await this.fetchFn(endpoint.href, init);
if (response.status === 401 && this.authProvider && !triedAuth) {
this.resourceMetadataUrl = extractResourceMetadataUrl(response);
try {
const result = await auth(this.authProvider, {
serverUrl: this.url,
resourceMetadataUrl: this.resourceMetadataUrl,
fetchFn: this.fetchFn
});
if (result !== "AUTHORIZED") {
const error = new UnauthorizedError();
(_b3 = this.onerror) == null ? void 0 : _b3.call(this, error);
return;
}
} catch (error) {
(_c = this.onerror) == null ? void 0 : _c.call(this, error);
return;
}
return attempt(true);
}
if (!response.ok) {
const text = await response.text().catch(() => null);
const error = new MCPClientError({
message: `MCP SSE Transport Error: POSTing to endpoint (HTTP ${response.status}): ${text}`
});
(_d = this.onerror) == null ? void 0 : _d.call(this, error);
return;
}
} catch (error) {
(_e = this.onerror) == null ? void 0 : _e.call(this, error);
return;
}
};
await attempt();
}
};
// src/tool/mcp-http-transport.ts
var import_provider_utils4 = require("@ai-sdk/provider-utils");
var HttpMCPTransport = class {
constructor({
url,
headers,
authProvider,
redirect = "follow",
fetch: fetchFn
}) {
this.inboundReconnectAttempts = 0;
this.reconnectionOptions = {
initialReconnectionDelay: 1e3,
maxReconnectionDelay: 3e4,
reconnectionDelayGrowFactor: 1.5,
maxRetries: 2
};
this.url = new URL(url);
this.headers = headers;
this.authProvider = authProvider;
this.redirectMode = redirect;
this.fetchFn = fetchFn != null ? fetchFn : globalThis.fetch;
}
async commonHeaders(base) {
var _a3;
const headers = {
...this.headers,
...base,
"mcp-protocol-version": (_a3 = this.protocolVersion) != null ? _a3 : LATEST_PROTOCOL_VERSION
};
if (this.sessionId) {
headers["mcp-session-id"] = this.sessionId;
}
if (this.authProvider) {
const tokens = await this.authProvider.tokens();
if (tokens == null ? void 0 : tokens.access_token) {
headers["Authorization"] = `Bearer ${tokens.access_token}`;
}
}
return (0, import_provider_utils4.withUserAgentSuffix)(
headers,
`ai-sdk/${VERSION}`,
(0, import_provider_utils4.getRuntimeEnvironmentUserAgent)()
);
}
/**
* Runs a single OAuth recovery flow for concurrent 401 responses.
*/
authorizeOnce(resourceMetadataUrl) {
if (!this.authProvider) {
return Promise.resolve("REDIRECT");
}
if (!this.authPromise) {
this.authPromise = auth(this.authProvider, {
serverUrl: this.url,
resourceMetadataUrl,
fetchFn: this.fetchFn
}).finally(() => {
this.authPromise = void 0;
});
}
return this.authPromise;
}
async start() {
if (this.abortController) {
throw new MCPClientError({
message: "MCP HTTP Transport Error: Transport already started. Note: client.connect() calls start() automatically."
});
}
this.abortController = new AbortController();
void this.openInboundSse();
}
async close() {
var _a3, _b3, _c;
(_a3 = this.inboundSseConnection) == null ? void 0 : _a3.close();
try {
if (this.sessionId && this.abortController && !this.abortController.signal.aborted) {
const headers = await this.commonHeaders({});
await this.fetchFn(this.url.href, {
method: "DELETE",
headers,
signal: this.abortController.signal,
redirect: this.redirectMode
}).catch(() => void 0);
}
} catch (e) {
}
(_b3 = this.abortController) == null ? void 0 : _b3.abort();
(_c = this.onclose) == null ? void 0 : _c.call(this);
}
async send(message) {
const attempt = async (triedAuth = false) => {
var _a3, _b3, _c, _d, _e, _f, _g;
try {
const headers = await this.commonHeaders({
"Content-Type": "application/json",
Accept: "application/json, text/event-stream"
});
const init = {
method: "POST",
headers,
body: JSON.stringify(message),
signal: (_a3 = this.abortController) == null ? void 0 : _a3.signal,
redirect: this.redirectMode