@ai-sdk/mcp 1.0.70 → 1.0.72

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,19 @@
1
1
  # @ai-sdk/mcp
2
2
 
3
+ ## 1.0.72
4
+
5
+ ### Patch Changes
6
+
7
+ - 0075ed5: fix(mcp): reject SSE requests when POST responses are unsuccessful
8
+
9
+ ## 1.0.71
10
+
11
+ ### Patch Changes
12
+
13
+ - b0e77c0: Select MCP OAuth authorization scopes from `WWW-Authenticate` challenges or Protected Resource Metadata.
14
+ - Updated dependencies [31205a4]
15
+ - @ai-sdk/provider-utils@4.0.46
16
+
3
17
  ## 1.0.70
4
18
 
5
19
  ### Patch Changes
package/dist/index.js CHANGED
@@ -661,26 +661,41 @@ function assertAuthorizationServerInformationMatches({
661
661
  });
662
662
  }
663
663
  }
664
- function extractResourceMetadataUrl(response) {
665
- var _a3;
664
+ function extractWWWAuthenticateParams(response) {
665
+ var _a3, _b3;
666
666
  const header = (_a3 = response.headers.get("www-authenticate")) != null ? _a3 : response.headers.get("WWW-Authenticate");
667
667
  if (!header) {
668
- return void 0;
668
+ return {};
669
669
  }
670
670
  const [type, scheme] = header.split(" ");
671
- if (type.toLowerCase() !== "bearer" || !scheme) {
672
- return void 0;
673
- }
674
- const regex = /resource_metadata="([^"]*)"/;
675
- const match = header.match(regex);
676
- if (!match) {
677
- return void 0;
671
+ if (type.replace(/,$/, "").toLowerCase() !== "bearer" || !scheme) {
672
+ return {};
678
673
  }
674
+ const resourceMetadataMatch = header.match(
675
+ /(?:^|[,\s])resource_metadata="([^"]*)"/i
676
+ );
677
+ const scope = (_b3 = header.match(/(?:^|[,\s])scope="([^"]*)"/i)) == null ? void 0 : _b3[1];
678
+ let resourceMetadataUrl;
679
679
  try {
680
- return new URL(match[1]);
680
+ resourceMetadataUrl = resourceMetadataMatch ? new URL(resourceMetadataMatch[1]) : void 0;
681
681
  } catch (e) {
682
- return void 0;
683
682
  }
683
+ return { resourceMetadataUrl, scope };
684
+ }
685
+ function selectScope({
686
+ scope,
687
+ resourceMetadata,
688
+ clientMetadata
689
+ }) {
690
+ var _a3;
691
+ if (scope) {
692
+ return scope;
693
+ }
694
+ const resourceScopes = (_a3 = resourceMetadata == null ? void 0 : resourceMetadata.scopes_supported) == null ? void 0 : _a3.join(" ");
695
+ if (resourceScopes) {
696
+ return resourceScopes;
697
+ }
698
+ return clientMetadata.scope;
684
699
  }
685
700
  function buildWellKnownPath(wellKnownPrefix, pathname = "", options = {}) {
686
701
  if (pathname.endsWith("/")) {
@@ -1309,7 +1324,11 @@ async function authInternal(provider, {
1309
1324
  clientInformation,
1310
1325
  state,
1311
1326
  redirectUrl: provider.redirectUrl,
1312
- scope: scope || provider.clientMetadata.scope,
1327
+ scope: selectScope({
1328
+ scope,
1329
+ resourceMetadata,
1330
+ clientMetadata: provider.clientMetadata
1331
+ }),
1313
1332
  resource
1314
1333
  }
1315
1334
  );
@@ -1387,11 +1406,13 @@ var SseMCPTransport = class {
1387
1406
  redirect: this.redirectMode
1388
1407
  });
1389
1408
  if (response.status === 401 && this.authProvider && !triedAuth) {
1390
- this.resourceMetadataUrl = extractResourceMetadataUrl(response);
1409
+ const { resourceMetadataUrl, scope } = extractWWWAuthenticateParams(response);
1410
+ this.resourceMetadataUrl = resourceMetadataUrl;
1391
1411
  try {
1392
1412
  const result = await auth(this.authProvider, {
1393
1413
  serverUrl: this.url,
1394
1414
  resourceMetadataUrl: this.resourceMetadataUrl,
1415
+ scope,
1395
1416
  fetchFn: this.fetchFn
1396
1417
  });
1397
1418
  if (result !== "AUTHORIZED") {
@@ -1506,7 +1527,7 @@ var SseMCPTransport = class {
1506
1527
  const transportSignal = (_b3 = this.abortController) == null ? void 0 : _b3.signal;
1507
1528
  const requestSignal = (options == null ? void 0 : options.signal) == null ? transportSignal : transportSignal == null ? options.signal : AbortSignal.any([transportSignal, options.signal]);
1508
1529
  const attempt = async (triedAuth = false) => {
1509
- var _a4, _b4, _c, _d, _e;
1530
+ var _a4, _b4;
1510
1531
  try {
1511
1532
  const headers = await this.commonHeaders({
1512
1533
  "Content-Type": "application/json"
@@ -1520,38 +1541,35 @@ var SseMCPTransport = class {
1520
1541
  };
1521
1542
  const response = await this.fetchFn(endpoint.href, init);
1522
1543
  if (response.status === 401 && this.authProvider && !triedAuth) {
1523
- this.resourceMetadataUrl = extractResourceMetadataUrl(response);
1524
- try {
1525
- const result = await auth(this.authProvider, {
1526
- serverUrl: this.url,
1527
- resourceMetadataUrl: this.resourceMetadataUrl,
1528
- fetchFn: this.fetchFn
1529
- });
1530
- if (result !== "AUTHORIZED") {
1531
- const error = new UnauthorizedError();
1532
- (_a4 = this.onerror) == null ? void 0 : _a4.call(this, error);
1533
- return;
1534
- }
1535
- } catch (error) {
1536
- (_b4 = this.onerror) == null ? void 0 : _b4.call(this, error);
1537
- return;
1544
+ const { resourceMetadataUrl, scope } = extractWWWAuthenticateParams(response);
1545
+ this.resourceMetadataUrl = resourceMetadataUrl;
1546
+ const result = await auth(this.authProvider, {
1547
+ serverUrl: this.url,
1548
+ resourceMetadataUrl: this.resourceMetadataUrl,
1549
+ scope,
1550
+ fetchFn: this.fetchFn
1551
+ });
1552
+ if (result !== "AUTHORIZED") {
1553
+ throw new UnauthorizedError();
1538
1554
  }
1539
1555
  return attempt(true);
1540
1556
  }
1541
1557
  if (!response.ok) {
1542
1558
  const text = await response.text().catch(() => null);
1543
1559
  const error = new MCPClientError({
1544
- message: `MCP SSE Transport Error: POSTing to endpoint (HTTP ${response.status}): ${text}`
1560
+ message: `MCP SSE Transport Error: POSTing to endpoint (HTTP ${response.status}): ${text}`,
1561
+ statusCode: response.status,
1562
+ url: endpoint.href,
1563
+ responseBody: text != null ? text : void 0
1545
1564
  });
1546
- (_c = this.onerror) == null ? void 0 : _c.call(this, error);
1547
- return;
1565
+ throw error;
1548
1566
  }
1549
1567
  } catch (error) {
1550
- if ((_d = options == null ? void 0 : options.signal) == null ? void 0 : _d.aborted) {
1568
+ if ((_a4 = options == null ? void 0 : options.signal) == null ? void 0 : _a4.aborted) {
1551
1569
  throw error;
1552
1570
  }
1553
- (_e = this.onerror) == null ? void 0 : _e.call(this, error);
1554
- return;
1571
+ (_b4 = this.onerror) == null ? void 0 : _b4.call(this, error);
1572
+ throw error;
1555
1573
  }
1556
1574
  };
1557
1575
  await attempt();
@@ -1612,7 +1630,7 @@ var HttpMCPTransport = class {
1612
1630
  /**
1613
1631
  * Runs a single OAuth recovery flow for concurrent 401 responses.
1614
1632
  */
1615
- authorizeOnce(resourceMetadataUrl) {
1633
+ authorizeOnce(resourceMetadataUrl, scope) {
1616
1634
  if (!this.authProvider) {
1617
1635
  return Promise.resolve("REDIRECT");
1618
1636
  }
@@ -1620,6 +1638,7 @@ var HttpMCPTransport = class {
1620
1638
  this.authPromise = auth(this.authProvider, {
1621
1639
  serverUrl: this.url,
1622
1640
  resourceMetadataUrl,
1641
+ scope,
1623
1642
  fetchFn: this.fetchFn
1624
1643
  }).finally(() => {
1625
1644
  this.authPromise = void 0;
@@ -1681,9 +1700,13 @@ var HttpMCPTransport = class {
1681
1700
  this.sessionId = sessionId;
1682
1701
  }
1683
1702
  if (response.status === 401 && this.authProvider && !triedAuth) {
1684
- this.resourceMetadataUrl = extractResourceMetadataUrl(response);
1703
+ const { resourceMetadataUrl, scope } = extractWWWAuthenticateParams(response);
1704
+ this.resourceMetadataUrl = resourceMetadataUrl;
1685
1705
  try {
1686
- const result = await this.authorizeOnce(this.resourceMetadataUrl);
1706
+ const result = await this.authorizeOnce(
1707
+ this.resourceMetadataUrl,
1708
+ scope
1709
+ );
1687
1710
  if (result !== "AUTHORIZED") {
1688
1711
  const error2 = new UnauthorizedError();
1689
1712
  throw error2;
@@ -1854,9 +1877,13 @@ var HttpMCPTransport = class {
1854
1877
  this.sessionId = sessionId;
1855
1878
  }
1856
1879
  if (response.status === 401 && this.authProvider && !triedAuth) {
1857
- this.resourceMetadataUrl = extractResourceMetadataUrl(response);
1880
+ const { resourceMetadataUrl, scope } = extractWWWAuthenticateParams(response);
1881
+ this.resourceMetadataUrl = resourceMetadataUrl;
1858
1882
  try {
1859
- const result = await this.authorizeOnce(this.resourceMetadataUrl);
1883
+ const result = await this.authorizeOnce(
1884
+ this.resourceMetadataUrl,
1885
+ scope
1886
+ );
1860
1887
  if (result !== "AUTHORIZED") {
1861
1888
  const error = new UnauthorizedError();
1862
1889
  (_b3 = this.onerror) == null ? void 0 : _b3.call(this, error);