@ai-sdk/mcp 2.0.64 → 2.0.66

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": "2.0.64",
3
+ "version": "2.0.66",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -42,6 +42,24 @@ export class UnauthorizedClientError extends MCPClientOAuthError {
42
42
  static errorCode = 'unauthorized_client';
43
43
  }
44
44
 
45
+ const mismatchName = 'AI_AuthorizationServerMismatchError';
46
+ const mismatchMarker = `vercel.ai.error.${mismatchName}`;
47
+ const mismatchSymbol = Symbol.for(mismatchMarker);
48
+
49
+ export class AuthorizationServerMismatchError extends MCPClientOAuthError {
50
+ private readonly [mismatchSymbol] = true;
51
+
52
+ static errorCode = 'authorization_server_mismatch';
53
+
54
+ constructor({ message, cause }: { message: string; cause?: unknown }) {
55
+ super({ name: 'AuthorizationServerMismatchError', message, cause });
56
+ }
57
+
58
+ static isInstance(error: unknown): error is AuthorizationServerMismatchError {
59
+ return AISDKError.hasMarker(error, mismatchMarker);
60
+ }
61
+ }
62
+
45
63
  export const OAUTH_ERRORS = {
46
64
  [ServerError.errorCode]: ServerError,
47
65
  [InvalidClientError.errorCode]: InvalidClientError,
package/src/index.ts CHANGED
@@ -41,6 +41,10 @@ export type {
41
41
  ClientCapabilities as MCPClientCapabilities,
42
42
  } from './tool/types';
43
43
  export { auth, UnauthorizedError } from './tool/oauth';
44
+ export {
45
+ MCPClientOAuthError,
46
+ AuthorizationServerMismatchError,
47
+ } from './error/oauth-error';
44
48
  export type {
45
49
  OAuthAuthorizationServerInformation,
46
50
  OAuthClientProvider,
package/src/tool/oauth.ts CHANGED
@@ -20,6 +20,7 @@ import {
20
20
  InvalidClientError,
21
21
  InvalidGrantError,
22
22
  UnauthorizedClientError,
23
+ AuthorizationServerMismatchError,
23
24
  } from '../error/oauth-error';
24
25
  import {
25
26
  resourceUrlFromServerUrl,
@@ -81,9 +82,15 @@ export interface OAuthClientProvider {
81
82
  * If implemented, provides a way for the client to invalidate (e.g. delete) the specified
82
83
  * credentials, in the case where the server has indicated that they are no longer valid.
83
84
  * This avoids requiring the user to intervene manually.
85
+ *
86
+ * For token invalidation, context identifies the token generation being invalidated
87
+ * when available. Providers sharing storage must atomically compare and delete
88
+ * that generation so a concurrent refresh's tokens are preserved. Omitting or
89
+ * ignoring context retains unconditional invalidation.
84
90
  */
85
91
  invalidateCredentials?(
86
92
  scope: 'all' | 'client' | 'tokens' | 'verifier',
93
+ context?: { tokens: OAuthTokens },
87
94
  ): void | Promise<void>;
88
95
  get redirectUrl(): string | URL;
89
96
  get clientMetadata(): OAuthClientMetadata;
@@ -354,7 +361,7 @@ function assertAuthorizationServerInformationMatches({
354
361
  storedAuthorizationServerInformation.tokenEndpoint !==
355
362
  currentAuthorizationServerInformation.tokenEndpoint
356
363
  ) {
357
- throw new MCPClientOAuthError({
364
+ throw new AuthorizationServerMismatchError({
358
365
  message:
359
366
  'OAuth authorization server metadata does not match the metadata that issued the stored credentials',
360
367
  });
@@ -1261,8 +1268,9 @@ export async function auth(
1261
1268
  fetchFn?: FetchFunction;
1262
1269
  },
1263
1270
  ): Promise<AuthResult> {
1271
+ const refreshAttempt: { tokens?: OAuthTokens } = {};
1264
1272
  try {
1265
- return await authInternal(provider, options);
1273
+ return await authInternal(provider, options, refreshAttempt);
1266
1274
  } catch (error) {
1267
1275
  if (
1268
1276
  error instanceof InvalidClientError ||
@@ -1278,7 +1286,13 @@ export async function auth(
1278
1286
  await provider.invalidateCredentials?.('all');
1279
1287
  return await authInternal(provider, options);
1280
1288
  } else if (error instanceof InvalidGrantError) {
1281
- await provider.invalidateCredentials?.('tokens');
1289
+ if (refreshAttempt.tokens) {
1290
+ await provider.invalidateCredentials?.('tokens', {
1291
+ tokens: refreshAttempt.tokens,
1292
+ });
1293
+ } else {
1294
+ await provider.invalidateCredentials?.('tokens');
1295
+ }
1282
1296
  return await authInternal(provider, options);
1283
1297
  }
1284
1298
 
@@ -1336,6 +1350,7 @@ async function authInternal(
1336
1350
  resourceMetadataUrl?: URL;
1337
1351
  fetchFn?: FetchFunction;
1338
1352
  },
1353
+ refreshAttempt?: { tokens?: OAuthTokens },
1339
1354
  ): Promise<AuthResult> {
1340
1355
  let resourceMetadata: OAuthProtectedResourceMetadata | undefined;
1341
1356
  let authorizationServerUrl: string | URL | undefined;
@@ -1554,11 +1569,16 @@ async function authInternal(
1554
1569
  currentAuthorizationServerInformation,
1555
1570
  });
1556
1571
  } else {
1557
- await provider.invalidateCredentials?.('tokens');
1572
+ await provider.invalidateCredentials?.('tokens', {
1573
+ tokens: { ...tokens },
1574
+ });
1558
1575
  }
1559
1576
 
1560
1577
  try {
1561
1578
  if (storedAuthorizationServerInformation) {
1579
+ if (refreshAttempt) {
1580
+ refreshAttempt.tokens = { ...tokens };
1581
+ }
1562
1582
  // Attempt to refresh the token
1563
1583
  const newTokens = await refreshAuthorization(authorizationServerUrl, {
1564
1584
  metadata,