@ai-sdk/mcp 2.0.79 → 2.0.81

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.
@@ -76,6 +76,16 @@ interface MCPTransport {
76
76
  * explicitly opt in.
77
77
  */
78
78
  supportsProtocolVersionDiscovery?: boolean;
79
+ /**
80
+ * Whether the client waits for the `server/discover` response instead of
81
+ * treating a slow response as a legacy server.
82
+ *
83
+ * Transports that always deliver a response, such as Streamable HTTP, set
84
+ * this so a slow modern server is not downgraded to `initialize`. Other
85
+ * transports keep a short probe timeout because a legacy server may never
86
+ * answer the unknown method.
87
+ */
88
+ waitsForProtocolVersionDiscovery?: boolean;
79
89
  /**
80
90
  * Whether this transport mirrors x-mcp-header tool parameters into request
81
91
  * headers.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/tool/json-rpc-message.ts","../../src/tool/mcp-transport.ts","../../src/tool/mcp-stdio/mcp-stdio-transport.ts"],"mappings":";;;;;;cAsDa,sBAAoB,EAAA,mBAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAK/B,EAAA,KAAA;KAEU,iBAAiB,EAAE,aAAa;;;;;;;KCjDhC;;;;EAIV,SAAS;;;;EAKT,UAAU;;;;EAKV;;;;EAKA;;;;EAKA,qBAAqB;;KAGX;;;;EAIV,SAAS;;UAGM;;;;;;;EAOf;;;;;EAMA;;;;EAKA,SAAS;;;;;;EAOT,KACE,SAAS,gBACT,UAAU,0BACT;;;;;EAMH,MAAM,UAAU,2BAA2B;;;;EAK3C;;;;EAKA,WAAW,OAAO;;;;EAKlB,aAAa,SAAS;;;;EAKtB;;;;EAKA,oBAAoB;;;;UClGL;EACf;EACA;EACA,MAAM;EACN,SAAS,SAAS;EAClB;EACA;;cAGW,6BAA6B;WAC/B;UACD;UACA;UACA;UACA;EAER;EACA,WAAW;EACX,aAAa,SAAS;EAEV,YAAA,QAAQ;EAId,SAAS;UAsDD;EAgBR,SAAS;EAMf,KAAK,SAAS,iBAAiB"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/tool/json-rpc-message.ts","../../src/tool/mcp-transport.ts","../../src/tool/mcp-stdio/mcp-stdio-transport.ts"],"mappings":";;;;;;cAsDa,sBAAoB,EAAA,mBAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAK/B,EAAA,KAAA;KAEU,iBAAiB,EAAE,aAAa;;;;;;;KCjDhC;;;;EAIV,SAAS;;;;EAKT,UAAU;;;;EAKV;;;;EAKA;;;;EAKA,qBAAqB;;KAGX;;;;EAIV,SAAS;;UAGM;;;;;;;EAOf;;;;;;;;;;EAWA;;;;;EAMA;;;;EAKA,SAAS;;;;;;EAOT,KACE,SAAS,gBACT,UAAU,0BACT;;;;;EAMH,MAAM,UAAU,2BAA2B;;;;EAK3C;;;;EAKA,WAAW,OAAO;;;;EAKlB,aAAa,SAAS;;;;EAKtB;;;;EAKA,oBAAoB;;;;UC7GL;EACf;EACA;EACA,MAAM;EACN,SAAS,SAAS;EAClB;EACA;;cAGW,6BAA6B;WAC/B;UACD;UACA;UACA;UACA;EAER;EACA,WAAW;EACX,aAAa,SAAS;EAEV,YAAA,QAAQ;EAId,SAAS;UAsDD;EAgBR,SAAS;EAMf,KAAK,SAAS,iBAAiB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/mcp",
3
- "version": "2.0.79",
3
+ "version": "2.0.81",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -31,8 +31,8 @@
31
31
  }
32
32
  },
33
33
  "dependencies": {
34
- "@ai-sdk/provider": "4.0.30",
35
- "@ai-sdk/provider-utils": "5.0.63",
34
+ "@ai-sdk/provider": "4.0.31",
35
+ "@ai-sdk/provider-utils": "5.0.65",
36
36
  "cross-spawn": "^7.0.6",
37
37
  "pkce-challenge": "^5.0.1"
38
38
  },
@@ -41,6 +41,7 @@
41
41
  "@types/cross-spawn": "^6.0.6",
42
42
  "@types/node": "22.19.19",
43
43
  "@vercel/ai-tsconfig": "0.0.0",
44
+ "@vercel/ai-tsdown-config": "0.0.0",
44
45
  "tsdown": "^0.23.0",
45
46
  "typescript": "6.0.3",
46
47
  "vitest": "^4.1.11",
@@ -685,7 +685,9 @@ class DefaultMCPClient implements Omit<MCPClient, 'experimental_events'> {
685
685
  resultSchema: DiscoverResultSchema,
686
686
  options: {
687
687
  signal,
688
- timeout: DEFAULT_PROTOCOL_DISCOVERY_TIMEOUT,
688
+ timeout: this.transport.waitsForProtocolVersionDiscovery
689
+ ? undefined
690
+ : DEFAULT_PROTOCOL_DISCOVERY_TIMEOUT,
689
691
  },
690
692
  });
691
693
 
@@ -38,6 +38,7 @@ function isMessageEvent(event: string | undefined): boolean {
38
38
  */
39
39
  export class HttpMCPTransport implements MCPTransport {
40
40
  readonly supportsProtocolVersionDiscovery = true;
41
+ readonly waitsForProtocolVersionDiscovery = true;
41
42
  readonly supportsMcpToolParameterHeaders = true;
42
43
  private url: URL;
43
44
  private abortController?: AbortController;
@@ -53,6 +53,17 @@ export interface MCPTransport {
53
53
  */
54
54
  supportsProtocolVersionDiscovery?: boolean;
55
55
 
56
+ /**
57
+ * Whether the client waits for the `server/discover` response instead of
58
+ * treating a slow response as a legacy server.
59
+ *
60
+ * Transports that always deliver a response, such as Streamable HTTP, set
61
+ * this so a slow modern server is not downgraded to `initialize`. Other
62
+ * transports keep a short probe timeout because a legacy server may never
63
+ * answer the unknown method.
64
+ */
65
+ waitsForProtocolVersionDiscovery?: boolean;
66
+
56
67
  /**
57
68
  * Whether this transport mirrors x-mcp-header tool parameters into request
58
69
  * headers.
package/src/tool/oauth.ts CHANGED
@@ -94,6 +94,11 @@ export interface OAuthClientProvider {
94
94
  ): void | Promise<void>;
95
95
  get redirectUrl(): string | URL;
96
96
  get clientMetadata(): OAuthClientMetadata;
97
+ /**
98
+ * Returns client credentials. Pre-registered confidential clients must specify
99
+ * their authorization server's `issuer` or provide stored authorization server
100
+ * information. `validateAuthorizationServerURL` does not replace this binding.
101
+ */
97
102
  clientInformation():
98
103
  | OAuthClientInformation
99
104
  | undefined
@@ -121,6 +126,7 @@ export interface OAuthClientProvider {
121
126
  /**
122
127
  * Validates an authorization server URL discovered from MCP protected resource
123
128
  * metadata before the client fetches its OAuth metadata.
129
+ * This additional restriction does not bind client credentials to that server.
124
130
  */
125
131
  validateAuthorizationServerURL?(
126
132
  serverUrl: string | URL,
@@ -1299,11 +1305,20 @@ export async function auth(
1299
1305
  await provider.invalidateCredentials?.('all');
1300
1306
  return await authInternal(provider, options);
1301
1307
  } else if (error instanceof InvalidGrantError && refreshAttempt.tokens) {
1308
+ // Keep the binding validated before the refresh, even when deleting the
1309
+ // rejected tokens removes the provider's only copy of that binding.
1310
+ const retryAuthorizationServerInformation =
1311
+ getAuthorizationServerInformationFromCredentials(refreshAttempt.tokens);
1302
1312
  // Only invalidate the tokens used by a failed refresh.
1303
1313
  await provider.invalidateCredentials?.('tokens', {
1304
1314
  tokens: refreshAttempt.tokens,
1305
1315
  });
1306
- return await authInternal(provider, options);
1316
+ return await authInternal(
1317
+ provider,
1318
+ options,
1319
+ undefined,
1320
+ retryAuthorizationServerInformation,
1321
+ );
1307
1322
  }
1308
1323
 
1309
1324
  throw error;
@@ -1361,6 +1376,7 @@ async function authInternal(
1361
1376
  fetchFn?: FetchFunction;
1362
1377
  },
1363
1378
  refreshAttempt?: { tokens?: OAuthTokens },
1379
+ retryAuthorizationServerInformation?: OAuthAuthorizationServerInformation,
1364
1380
  ): Promise<AuthResult> {
1365
1381
  let resourceMetadata: OAuthProtectedResourceMetadata | undefined;
1366
1382
  let authorizationServerUrl: string | URL | undefined;
@@ -1451,6 +1467,12 @@ async function authInternal(
1451
1467
  );
1452
1468
  const currentAuthorizationServerInformation =
1453
1469
  createAuthorizationServerInformation(authorizationServerUrl, metadata);
1470
+ if (retryAuthorizationServerInformation) {
1471
+ assertAuthorizationServerInformationMatches({
1472
+ storedAuthorizationServerInformation: retryAuthorizationServerInformation,
1473
+ currentAuthorizationServerInformation,
1474
+ });
1475
+ }
1454
1476
  const clientMetadata = provider.clientMetadata;
1455
1477
  const selectedScope = selectScope({
1456
1478
  scope,
@@ -1462,19 +1484,58 @@ async function authInternal(
1462
1484
  if (authorizationCode === undefined) {
1463
1485
  clientInformation = await Promise.resolve(provider.clientInformation());
1464
1486
  }
1465
- if (clientInformation?.issuer != null) {
1487
+ const tokens =
1488
+ authorizationCode === undefined ? await provider.tokens() : undefined;
1489
+ if (clientInformation) {
1490
+ // A flow pin must never override the issuer attached to the credentials.
1491
+ if (
1492
+ clientInformation.issuer != null &&
1493
+ clientInformation.issuer !== currentAuthorizationServerInformation.issuer
1494
+ ) {
1495
+ throw new AuthorizationServerMismatchError({
1496
+ message:
1497
+ 'OAuth authorization server does not match the issuer of the client credentials',
1498
+ });
1499
+ }
1500
+
1501
+ const clientAuthorizationServerInformation =
1502
+ getAuthorizationServerInformationFromCredentials(clientInformation);
1503
+ if (clientAuthorizationServerInformation) {
1504
+ assertAuthorizationServerInformationMatches({
1505
+ storedAuthorizationServerInformation:
1506
+ clientAuthorizationServerInformation,
1507
+ currentAuthorizationServerInformation,
1508
+ });
1509
+ }
1510
+
1466
1511
  const storedAuthorizationServerInformation =
1467
1512
  callbackAuthorizationServerInformation ??
1468
1513
  (await getStoredAuthorizationServerInformation({
1469
1514
  provider,
1470
1515
  clientInformation,
1471
- }));
1516
+ tokens,
1517
+ })) ??
1518
+ retryAuthorizationServerInformation;
1472
1519
  if (storedAuthorizationServerInformation) {
1473
1520
  assertAuthorizationServerInformationMatches({
1474
1521
  storedAuthorizationServerInformation,
1475
1522
  currentAuthorizationServerInformation,
1476
1523
  });
1477
1524
  }
1525
+
1526
+ // Do not turn an MCP server's choice of AS into a trust binding for an
1527
+ // existing secret. Dynamic registration below binds newly issued credentials.
1528
+ if (
1529
+ (clientInformation.client_secret != null ||
1530
+ provider.addClientAuthentication) &&
1531
+ clientInformation.issuer == null &&
1532
+ !storedAuthorizationServerInformation
1533
+ ) {
1534
+ throw new AuthorizationServerMismatchError({
1535
+ message:
1536
+ 'OAuth client credentials must be bound to an authorization server: configure their issuer or store authorization server information',
1537
+ });
1538
+ }
1478
1539
  }
1479
1540
 
1480
1541
  if (!clientInformation) {
@@ -1562,8 +1623,6 @@ async function authInternal(
1562
1623
  return 'AUTHORIZED';
1563
1624
  }
1564
1625
 
1565
- const tokens = await provider.tokens();
1566
-
1567
1626
  /** Refresh only when stored credentials match the current AS pin */
1568
1627
  if (tokens?.refresh_token) {
1569
1628
  const storedAuthorizationServerInformation =