@ai-sdk/mcp 2.0.30 → 2.0.32
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 +13 -0
- package/dist/index.js +51 -19
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/tool/mcp-http-transport.ts +20 -6
- package/src/tool/mcp-sse-transport.ts +9 -3
- package/src/tool/oauth.ts +51 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/mcp",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.32",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"pkce-challenge": "^5.0.1",
|
|
35
|
-
"@ai-sdk/provider": "
|
|
36
|
-
"@ai-sdk/provider
|
|
35
|
+
"@ai-sdk/provider-utils": "5.0.27",
|
|
36
|
+
"@ai-sdk/provider": "4.0.7"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/node": "22.19.19",
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import type { MCPTransport } from './mcp-transport';
|
|
14
14
|
import { VERSION } from '../version';
|
|
15
15
|
import {
|
|
16
|
-
|
|
16
|
+
extractWWWAuthenticateParams,
|
|
17
17
|
UnauthorizedError,
|
|
18
18
|
auth,
|
|
19
19
|
type AuthResult,
|
|
@@ -159,7 +159,10 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
159
159
|
/**
|
|
160
160
|
* Runs a single OAuth recovery flow for concurrent 401 responses.
|
|
161
161
|
*/
|
|
162
|
-
private authorizeOnce(
|
|
162
|
+
private authorizeOnce(
|
|
163
|
+
resourceMetadataUrl?: URL,
|
|
164
|
+
scope?: string,
|
|
165
|
+
): Promise<AuthResult> {
|
|
163
166
|
if (!this.authProvider) {
|
|
164
167
|
return Promise.resolve('REDIRECT');
|
|
165
168
|
}
|
|
@@ -168,6 +171,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
168
171
|
this.authPromise = auth(this.authProvider, {
|
|
169
172
|
serverUrl: this.url,
|
|
170
173
|
resourceMetadataUrl,
|
|
174
|
+
scope,
|
|
171
175
|
fetchFn: this.fetchFn,
|
|
172
176
|
}).finally(() => {
|
|
173
177
|
this.authPromise = undefined;
|
|
@@ -256,9 +260,14 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
256
260
|
this.applySessionIdFromResponse(response);
|
|
257
261
|
|
|
258
262
|
if (response.status === 401 && this.authProvider && !triedAuth) {
|
|
259
|
-
|
|
263
|
+
const { resourceMetadataUrl, scope } =
|
|
264
|
+
extractWWWAuthenticateParams(response);
|
|
265
|
+
this.resourceMetadataUrl = resourceMetadataUrl;
|
|
260
266
|
try {
|
|
261
|
-
const result = await this.authorizeOnce(
|
|
267
|
+
const result = await this.authorizeOnce(
|
|
268
|
+
this.resourceMetadataUrl,
|
|
269
|
+
scope,
|
|
270
|
+
);
|
|
262
271
|
if (result !== 'AUTHORIZED') {
|
|
263
272
|
const error = new UnauthorizedError();
|
|
264
273
|
throw error;
|
|
@@ -473,9 +482,14 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
473
482
|
this.applySessionIdFromResponse(response);
|
|
474
483
|
|
|
475
484
|
if (response.status === 401 && this.authProvider && !triedAuth) {
|
|
476
|
-
|
|
485
|
+
const { resourceMetadataUrl, scope } =
|
|
486
|
+
extractWWWAuthenticateParams(response);
|
|
487
|
+
this.resourceMetadataUrl = resourceMetadataUrl;
|
|
477
488
|
try {
|
|
478
|
-
const result = await this.authorizeOnce(
|
|
489
|
+
const result = await this.authorizeOnce(
|
|
490
|
+
this.resourceMetadataUrl,
|
|
491
|
+
scope,
|
|
492
|
+
);
|
|
479
493
|
if (result !== 'AUTHORIZED') {
|
|
480
494
|
const error = new UnauthorizedError();
|
|
481
495
|
this.onerror?.(error);
|
|
@@ -9,7 +9,7 @@ import { parseJSONRPCMessage, type JSONRPCMessage } from './json-rpc-message';
|
|
|
9
9
|
import type { MCPTransport } from './mcp-transport';
|
|
10
10
|
import { VERSION } from '../version';
|
|
11
11
|
import {
|
|
12
|
-
|
|
12
|
+
extractWWWAuthenticateParams,
|
|
13
13
|
UnauthorizedError,
|
|
14
14
|
auth,
|
|
15
15
|
type OAuthClientProvider,
|
|
@@ -106,11 +106,14 @@ export class SseMCPTransport implements MCPTransport {
|
|
|
106
106
|
});
|
|
107
107
|
|
|
108
108
|
if (response.status === 401 && this.authProvider && !triedAuth) {
|
|
109
|
-
|
|
109
|
+
const { resourceMetadataUrl, scope } =
|
|
110
|
+
extractWWWAuthenticateParams(response);
|
|
111
|
+
this.resourceMetadataUrl = resourceMetadataUrl;
|
|
110
112
|
try {
|
|
111
113
|
const result = await auth(this.authProvider, {
|
|
112
114
|
serverUrl: this.url,
|
|
113
115
|
resourceMetadataUrl: this.resourceMetadataUrl,
|
|
116
|
+
scope,
|
|
114
117
|
fetchFn: this.fetchFn,
|
|
115
118
|
});
|
|
116
119
|
if (result !== 'AUTHORIZED') {
|
|
@@ -273,11 +276,14 @@ export class SseMCPTransport implements MCPTransport {
|
|
|
273
276
|
const response = await this.fetchFn(endpoint.href, init);
|
|
274
277
|
|
|
275
278
|
if (response.status === 401 && this.authProvider && !triedAuth) {
|
|
276
|
-
|
|
279
|
+
const { resourceMetadataUrl, scope } =
|
|
280
|
+
extractWWWAuthenticateParams(response);
|
|
281
|
+
this.resourceMetadataUrl = resourceMetadataUrl;
|
|
277
282
|
try {
|
|
278
283
|
const result = await auth(this.authProvider, {
|
|
279
284
|
serverUrl: this.url,
|
|
280
285
|
resourceMetadataUrl: this.resourceMetadataUrl,
|
|
286
|
+
scope,
|
|
281
287
|
fetchFn: this.fetchFn,
|
|
282
288
|
});
|
|
283
289
|
if (result !== 'AUTHORIZED') {
|
package/src/tool/oauth.ts
CHANGED
|
@@ -270,37 +270,65 @@ function assertAuthorizationServerInformationMatches({
|
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
export function extractResourceMetadataUrl(
|
|
278
|
-
response: Response,
|
|
279
|
-
): URL | undefined {
|
|
273
|
+
export function extractWWWAuthenticateParams(response: Response): {
|
|
274
|
+
resourceMetadataUrl?: URL;
|
|
275
|
+
scope?: string;
|
|
276
|
+
} {
|
|
280
277
|
const header =
|
|
281
278
|
response.headers.get('www-authenticate') ??
|
|
282
279
|
response.headers.get('WWW-Authenticate');
|
|
283
280
|
if (!header) {
|
|
284
|
-
return
|
|
281
|
+
return {};
|
|
285
282
|
}
|
|
286
283
|
|
|
287
284
|
const [type, scheme] = header.split(' ');
|
|
288
285
|
if (type.toLowerCase() !== 'bearer' || !scheme) {
|
|
289
|
-
return
|
|
286
|
+
return {};
|
|
290
287
|
}
|
|
291
288
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
return undefined;
|
|
297
|
-
}
|
|
289
|
+
const resourceMetadataMatch = header.match(
|
|
290
|
+
/(?:^|[,\s])resource_metadata="([^"]*)"/i,
|
|
291
|
+
);
|
|
292
|
+
const scope = header.match(/(?:^|[,\s])scope="([^"]*)"/i)?.[1];
|
|
298
293
|
|
|
294
|
+
let resourceMetadataUrl: URL | undefined;
|
|
299
295
|
try {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
296
|
+
resourceMetadataUrl = resourceMetadataMatch
|
|
297
|
+
? new URL(resourceMetadataMatch[1])
|
|
298
|
+
: undefined;
|
|
299
|
+
} catch {}
|
|
300
|
+
|
|
301
|
+
return { resourceMetadataUrl, scope };
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Extracts the OAuth 2.0 Protected Resource Metadata URL from a WWW-Authenticate header (RFC9728).
|
|
306
|
+
*/
|
|
307
|
+
export function extractResourceMetadataUrl(
|
|
308
|
+
response: Response,
|
|
309
|
+
): URL | undefined {
|
|
310
|
+
return extractWWWAuthenticateParams(response).resourceMetadataUrl;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function selectScope({
|
|
314
|
+
scope,
|
|
315
|
+
resourceMetadata,
|
|
316
|
+
clientMetadata,
|
|
317
|
+
}: {
|
|
318
|
+
scope?: string;
|
|
319
|
+
resourceMetadata?: OAuthProtectedResourceMetadata;
|
|
320
|
+
clientMetadata: OAuthClientMetadata;
|
|
321
|
+
}): string | undefined {
|
|
322
|
+
if (scope) {
|
|
323
|
+
return scope;
|
|
303
324
|
}
|
|
325
|
+
|
|
326
|
+
const resourceScopes = resourceMetadata?.scopes_supported?.join(' ');
|
|
327
|
+
if (resourceScopes) {
|
|
328
|
+
return resourceScopes;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return clientMetadata.scope;
|
|
304
332
|
}
|
|
305
333
|
|
|
306
334
|
/**
|
|
@@ -1308,7 +1336,11 @@ async function authInternal(
|
|
|
1308
1336
|
clientInformation,
|
|
1309
1337
|
state,
|
|
1310
1338
|
redirectUrl: provider.redirectUrl,
|
|
1311
|
-
scope:
|
|
1339
|
+
scope: selectScope({
|
|
1340
|
+
scope,
|
|
1341
|
+
resourceMetadata,
|
|
1342
|
+
clientMetadata: provider.clientMetadata,
|
|
1343
|
+
}),
|
|
1312
1344
|
resource,
|
|
1313
1345
|
},
|
|
1314
1346
|
);
|