@ai-sdk/mcp 1.0.90 → 1.0.92

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/mcp",
3
- "version": "1.0.90",
3
+ "version": "1.0.92",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
package/src/tool/oauth.ts CHANGED
@@ -80,9 +80,15 @@ export interface OAuthClientProvider {
80
80
  * If implemented, provides a way for the client to invalidate (e.g. delete) the specified
81
81
  * credentials, in the case where the server has indicated that they are no longer valid.
82
82
  * This avoids requiring the user to intervene manually.
83
+ *
84
+ * For token invalidation, context identifies the token generation being invalidated
85
+ * when available. Providers sharing storage must atomically compare and delete
86
+ * that generation so a concurrent refresh's tokens are preserved. Omitting or
87
+ * ignoring context retains unconditional invalidation.
83
88
  */
84
89
  invalidateCredentials?(
85
90
  scope: 'all' | 'client' | 'tokens' | 'verifier',
91
+ context?: { tokens: OAuthTokens },
86
92
  ): void | Promise<void>;
87
93
  get redirectUrl(): string | URL;
88
94
  get clientMetadata(): OAuthClientMetadata;
@@ -625,8 +631,12 @@ export function buildDiscoveryUrls(
625
631
  expectedIssuer: pathIssuer,
626
632
  });
627
633
 
634
+ // Assign the path rather than resolving it as a URL reference: a pathname
635
+ // beginning with // would otherwise replace the authorization server host.
636
+ const oidcDiscoveryUrl = new URL(url.origin);
637
+ oidcDiscoveryUrl.pathname = `${pathname}/.well-known/openid-configuration`;
628
638
  urlsToTry.push({
629
- url: new URL(`${pathname}/.well-known/openid-configuration`, url.origin),
639
+ url: oidcDiscoveryUrl,
630
640
  type: 'oidc',
631
641
  expectedIssuer: pathIssuer,
632
642
  });
@@ -664,8 +674,17 @@ export async function discoverAuthorizationServerMetadata(
664
674
  ): Promise<AuthorizationServerMetadata | undefined> {
665
675
  const headers = { 'MCP-Protocol-Version': protocolVersion };
666
676
 
677
+ const authorizationServerOrigin = new URL(authorizationServerUrl).origin;
667
678
  const urlsToTry = buildDiscoveryUrls(authorizationServerUrl);
668
679
 
680
+ // Check all candidates before any request so discovery cannot bypass an
681
+ // application's authorization server validation by changing the origin.
682
+ if (urlsToTry.some(({ url }) => url.origin !== authorizationServerOrigin)) {
683
+ throw new MCPClientOAuthError({
684
+ message: 'OAuth discovery URL changed authorization-server origin',
685
+ });
686
+ }
687
+
669
688
  for (const { url: endpointUrl, type, expectedIssuer } of urlsToTry) {
670
689
  const response = await fetchWithCorsRetry(
671
690
  endpointUrl,
@@ -1214,8 +1233,9 @@ export async function auth(
1214
1233
  fetchFn?: FetchFunction;
1215
1234
  },
1216
1235
  ): Promise<AuthResult> {
1236
+ const refreshAttempt: { tokens?: OAuthTokens } = {};
1217
1237
  try {
1218
- return await authInternal(provider, options);
1238
+ return await authInternal(provider, options, refreshAttempt);
1219
1239
  } catch (error) {
1220
1240
  if (
1221
1241
  error instanceof InvalidClientError ||
@@ -1230,8 +1250,11 @@ export async function auth(
1230
1250
 
1231
1251
  await provider.invalidateCredentials?.('all');
1232
1252
  return await authInternal(provider, options);
1233
- } else if (error instanceof InvalidGrantError) {
1234
- await provider.invalidateCredentials?.('tokens');
1253
+ } else if (error instanceof InvalidGrantError && refreshAttempt.tokens) {
1254
+ // Only invalidate the tokens used by a failed refresh.
1255
+ await provider.invalidateCredentials?.('tokens', {
1256
+ tokens: refreshAttempt.tokens,
1257
+ });
1235
1258
  return await authInternal(provider, options);
1236
1259
  }
1237
1260
 
@@ -1287,6 +1310,7 @@ async function authInternal(
1287
1310
  resourceMetadataUrl?: URL;
1288
1311
  fetchFn?: FetchFunction;
1289
1312
  },
1313
+ refreshAttempt?: { tokens?: OAuthTokens },
1290
1314
  ): Promise<AuthResult> {
1291
1315
  let resourceMetadata: OAuthProtectedResourceMetadata | undefined;
1292
1316
  let authorizationServerUrl: string | URL | undefined;
@@ -1483,11 +1507,16 @@ async function authInternal(
1483
1507
  currentAuthorizationServerInformation,
1484
1508
  });
1485
1509
  } else {
1486
- await provider.invalidateCredentials?.('tokens');
1510
+ await provider.invalidateCredentials?.('tokens', {
1511
+ tokens: { ...tokens },
1512
+ });
1487
1513
  }
1488
1514
 
1489
1515
  try {
1490
1516
  if (storedAuthorizationServerInformation) {
1517
+ if (refreshAttempt) {
1518
+ refreshAttempt.tokens = { ...tokens };
1519
+ }
1491
1520
  // Attempt to refresh the token
1492
1521
  const newTokens = await refreshAuthorization(authorizationServerUrl, {
1493
1522
  metadata,