@ai-sdk/mcp 2.0.36 → 2.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @ai-sdk/mcp
2
2
 
3
+ ## 2.0.39
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [3e125ba]
8
+ - @ai-sdk/provider-utils@5.0.32
9
+
10
+ ## 2.0.38
11
+
12
+ ### Patch Changes
13
+
14
+ - bf591f0: Apply MCP scope selection to dynamic client registration as well as authorization.
15
+ - fe69342: fix(mcp): reject private OAuth endpoints before sending credentials
16
+ - Updated dependencies [a9782e1]
17
+ - Updated dependencies [35841f5]
18
+ - Updated dependencies [d2f3353]
19
+ - @ai-sdk/provider-utils@5.0.31
20
+
21
+ ## 2.0.37
22
+
23
+ ### Patch Changes
24
+
25
+ - 1175434: Fetch all paginated tool definitions when creating an MCP tool set.
26
+ - Updated dependencies [591d25b]
27
+ - @ai-sdk/provider@4.0.8
28
+ - @ai-sdk/provider-utils@5.0.30
29
+
3
30
  ## 2.0.36
4
31
 
5
32
  ### Patch Changes
package/dist/index.js CHANGED
@@ -542,7 +542,10 @@ function checkResourceAllowed({
542
542
  }
543
543
 
544
544
  // src/tool/oauth.ts
545
- import { parseJSON as parseJSON2 } from "@ai-sdk/provider-utils";
545
+ import {
546
+ parseJSON as parseJSON2,
547
+ validateDownloadUrl
548
+ } from "@ai-sdk/provider-utils";
546
549
  var UnauthorizedError = class extends Error {
547
550
  constructor(message = "Unauthorized") {
548
551
  super(message);
@@ -552,6 +555,23 @@ var UnauthorizedError = class extends Error {
552
555
  function normalizeUrl(url) {
553
556
  return new URL(url).href;
554
557
  }
558
+ function isOAuthLoopbackHost(hostname) {
559
+ const normalized = hostname.toLowerCase().replace(/\.+$/, "");
560
+ return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "127.0.0.1" || normalized === "[::1]" || normalized === "::1";
561
+ }
562
+ function assertSafeOAuthEndpoint(endpointUrl) {
563
+ if ((endpointUrl.protocol === "http:" || endpointUrl.protocol === "https:") && isOAuthLoopbackHost(endpointUrl.hostname)) {
564
+ return;
565
+ }
566
+ try {
567
+ validateDownloadUrl(endpointUrl.href);
568
+ } catch (error) {
569
+ throw new MCPClientOAuthError({
570
+ message: `OAuth endpoint URL is not allowed: ${endpointUrl.href}`,
571
+ cause: error
572
+ });
573
+ }
574
+ }
555
575
  function validateAuthorizationResponseIssuer({
556
576
  callbackIssuer,
557
577
  expectedIssuer
@@ -1000,6 +1020,7 @@ async function exchangeAuthorization(authorizationServerUrl, {
1000
1020
  var _a3;
1001
1021
  const grantType = "authorization_code";
1002
1022
  const tokenUrl = (metadata == null ? void 0 : metadata.token_endpoint) ? new URL(metadata.token_endpoint) : new URL("/token", authorizationServerUrl);
1023
+ assertSafeOAuthEndpoint(tokenUrl);
1003
1024
  if ((metadata == null ? void 0 : metadata.grant_types_supported) && !metadata.grant_types_supported.includes(grantType)) {
1004
1025
  throw new Error(
1005
1026
  `Incompatible auth server: does not support grant type ${grantType}`
@@ -1036,7 +1057,8 @@ async function exchangeAuthorization(authorizationServerUrl, {
1036
1057
  const response = await (fetchFn != null ? fetchFn : fetch)(tokenUrl, {
1037
1058
  method: "POST",
1038
1059
  headers,
1039
- body: params
1060
+ body: params,
1061
+ redirect: "error"
1040
1062
  });
1041
1063
  if (!response.ok) {
1042
1064
  throw await parseErrorResponse(response);
@@ -1064,6 +1086,7 @@ async function refreshAuthorization(authorizationServerUrl, {
1064
1086
  } else {
1065
1087
  tokenUrl = new URL("/token", authorizationServerUrl);
1066
1088
  }
1089
+ assertSafeOAuthEndpoint(tokenUrl);
1067
1090
  const headers = new Headers({
1068
1091
  "Content-Type": "application/x-www-form-urlencoded",
1069
1092
  Accept: "application/json"
@@ -1093,7 +1116,8 @@ async function refreshAuthorization(authorizationServerUrl, {
1093
1116
  const response = await (fetchFn != null ? fetchFn : fetch)(tokenUrl, {
1094
1117
  method: "POST",
1095
1118
  headers,
1096
- body: params
1119
+ body: params,
1120
+ redirect: "error"
1097
1121
  });
1098
1122
  if (!response.ok) {
1099
1123
  throw await parseErrorResponse(response);
@@ -1120,6 +1144,7 @@ async function registerClient(authorizationServerUrl, {
1120
1144
  } else {
1121
1145
  registrationUrl = new URL("/register", authorizationServerUrl);
1122
1146
  }
1147
+ assertSafeOAuthEndpoint(registrationUrl);
1123
1148
  const applicationType = (_a3 = clientMetadata.application_type) != null ? _a3 : inferOAuthApplicationType(clientMetadata.redirect_uris);
1124
1149
  const response = await (fetchFn != null ? fetchFn : fetch)(registrationUrl, {
1125
1150
  method: "POST",
@@ -1129,7 +1154,8 @@ async function registerClient(authorizationServerUrl, {
1129
1154
  body: JSON.stringify({
1130
1155
  ...clientMetadata,
1131
1156
  application_type: applicationType
1132
- })
1157
+ }),
1158
+ redirect: "error"
1133
1159
  });
1134
1160
  if (!response.ok) {
1135
1161
  throw await parseErrorResponse(response);
@@ -1139,7 +1165,7 @@ async function registerClient(authorizationServerUrl, {
1139
1165
  function inferOAuthApplicationType(redirectUris) {
1140
1166
  const isNativeRedirectUri = (redirectUri) => {
1141
1167
  const url = new URL(redirectUri);
1142
- return (url.protocol === "http:" || url.protocol === "https:") && (url.hostname === "localhost" || url.hostname.endsWith(".localhost") || url.hostname === "127.0.0.1" || url.hostname === "[::1]") || url.protocol !== "http:" && url.protocol !== "https:";
1168
+ return (url.protocol === "http:" || url.protocol === "https:") && isOAuthLoopbackHost(url.hostname) || url.protocol !== "http:" && url.protocol !== "https:";
1143
1169
  };
1144
1170
  return redirectUris.every(isNativeRedirectUri) ? "native" : "web";
1145
1171
  }
@@ -1223,6 +1249,12 @@ async function authInternal(provider, {
1223
1249
  }
1224
1250
  );
1225
1251
  const currentAuthorizationServerInformation = createAuthorizationServerInformation(authorizationServerUrl, metadata);
1252
+ const clientMetadata = provider.clientMetadata;
1253
+ const selectedScope = selectScope({
1254
+ scope,
1255
+ resourceMetadata,
1256
+ clientMetadata
1257
+ });
1226
1258
  let clientInformation = await Promise.resolve(provider.clientInformation());
1227
1259
  if ((clientInformation == null ? void 0 : clientInformation.issuer) != null) {
1228
1260
  const storedAuthorizationServerInformation = await getStoredAuthorizationServerInformation({
@@ -1249,7 +1281,10 @@ async function authInternal(provider, {
1249
1281
  }
1250
1282
  const fullInformation = await registerClient(authorizationServerUrl, {
1251
1283
  metadata,
1252
- clientMetadata: provider.clientMetadata,
1284
+ clientMetadata: {
1285
+ ...clientMetadata,
1286
+ scope: selectedScope
1287
+ },
1253
1288
  fetchFn
1254
1289
  });
1255
1290
  clientInformation = addAuthorizationServerInformationToClientInformation(
@@ -1357,11 +1392,7 @@ async function authInternal(provider, {
1357
1392
  clientInformation,
1358
1393
  state,
1359
1394
  redirectUrl: provider.redirectUrl,
1360
- scope: selectScope({
1361
- scope,
1362
- resourceMetadata,
1363
- clientMetadata: provider.clientMetadata
1364
- }),
1395
+ scope: selectedScope,
1365
1396
  resource
1366
1397
  }
1367
1398
  );
@@ -3051,8 +3082,15 @@ var DefaultMCPClient = class {
3051
3082
  async tools({
3052
3083
  schemas = "automatic"
3053
3084
  } = {}) {
3054
- const definitions = await this.listTools();
3055
- return this.toolsFromDefinitions(definitions, {
3085
+ let definitions = await this.listTools();
3086
+ const tools = [...definitions.tools];
3087
+ while (definitions.nextCursor != null) {
3088
+ definitions = await this.listTools({
3089
+ params: { cursor: definitions.nextCursor }
3090
+ });
3091
+ tools.push(...definitions.tools);
3092
+ }
3093
+ return this.toolsFromDefinitions({ ...definitions, tools }, {
3056
3094
  schemas
3057
3095
  });
3058
3096
  }