@ai-sdk/mcp 2.0.31 → 2.0.33
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 +14 -0
- package/README.md +15 -0
- package/dist/index.d.ts +54 -3
- package/dist/index.js +472 -67
- package/dist/index.js.map +1 -1
- package/dist/mcp-stdio/index.d.ts +19 -1
- package/dist/mcp-stdio/index.js +15 -5
- package/dist/mcp-stdio/index.js.map +1 -1
- package/package.json +1 -1
- package/src/tool/json-rpc-message.ts +1 -1
- package/src/tool/mcp-client.ts +251 -22
- package/src/tool/mcp-http-headers.ts +161 -0
- package/src/tool/mcp-http-transport.ts +119 -16
- package/src/tool/mcp-sse-transport.ts +12 -5
- package/src/tool/mcp-stdio/mcp-stdio-transport.ts +1 -0
- package/src/tool/mcp-transport.ts +25 -1
- package/src/tool/mock-mcp-transport.ts +2 -2
- package/src/tool/oauth-types.ts +9 -0
- package/src/tool/oauth.ts +126 -20
- package/src/tool/types.ts +19 -8
package/dist/index.js
CHANGED
|
@@ -4,9 +4,11 @@ import { z as z2 } from "zod/v4";
|
|
|
4
4
|
|
|
5
5
|
// src/tool/types.ts
|
|
6
6
|
import { z } from "zod/v4";
|
|
7
|
-
var LATEST_PROTOCOL_VERSION = "
|
|
7
|
+
var LATEST_PROTOCOL_VERSION = "2026-07-28";
|
|
8
|
+
var LATEST_LEGACY_PROTOCOL_VERSION = "2025-11-25";
|
|
8
9
|
var SUPPORTED_PROTOCOL_VERSIONS = [
|
|
9
10
|
LATEST_PROTOCOL_VERSION,
|
|
11
|
+
LATEST_LEGACY_PROTOCOL_VERSION,
|
|
10
12
|
"2025-06-18",
|
|
11
13
|
"2025-03-26",
|
|
12
14
|
"2024-11-05"
|
|
@@ -20,7 +22,9 @@ var ClientOrServerImplementationSchema = z.looseObject({
|
|
|
20
22
|
var BaseParamsSchema = z.looseObject({
|
|
21
23
|
_meta: z.optional(z.object({}).loose())
|
|
22
24
|
});
|
|
23
|
-
var ResultSchema = BaseParamsSchema
|
|
25
|
+
var ResultSchema = BaseParamsSchema.extend({
|
|
26
|
+
resultType: z.optional(z.string())
|
|
27
|
+
});
|
|
24
28
|
var RequestSchema = z.object({
|
|
25
29
|
method: z.string(),
|
|
26
30
|
params: z.optional(BaseParamsSchema)
|
|
@@ -53,6 +57,13 @@ var ServerCapabilitiesSchema = z.looseObject({
|
|
|
53
57
|
var ClientCapabilitiesSchema = z.object({
|
|
54
58
|
elicitation: z.optional(ElicitationCapabilitySchema)
|
|
55
59
|
}).loose();
|
|
60
|
+
var DiscoverResultSchema = ResultSchema.extend({
|
|
61
|
+
supportedVersions: z.array(z.string()),
|
|
62
|
+
capabilities: ServerCapabilitiesSchema,
|
|
63
|
+
instructions: z.optional(z.string()),
|
|
64
|
+
ttlMs: z.optional(z.number()),
|
|
65
|
+
cacheScope: z.optional(z.union([z.literal("public"), z.literal("private")]))
|
|
66
|
+
});
|
|
56
67
|
var InitializeResultSchema = ResultSchema.extend({
|
|
57
68
|
protocolVersion: z.string(),
|
|
58
69
|
capabilities: ServerCapabilitiesSchema,
|
|
@@ -69,10 +80,10 @@ var ToolSchema = z.object({
|
|
|
69
80
|
*/
|
|
70
81
|
title: z.optional(z.string()),
|
|
71
82
|
description: z.optional(z.string()),
|
|
72
|
-
inputSchema: z.
|
|
73
|
-
type: z.
|
|
83
|
+
inputSchema: z.looseObject({
|
|
84
|
+
type: z.optional(z.unknown()),
|
|
74
85
|
properties: z.optional(z.object({}).loose())
|
|
75
|
-
})
|
|
86
|
+
}),
|
|
76
87
|
/**
|
|
77
88
|
* @see https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema
|
|
78
89
|
*/
|
|
@@ -261,7 +272,7 @@ var JSONRPCResponseSchema = z2.object({
|
|
|
261
272
|
}).strict();
|
|
262
273
|
var JSONRPCErrorSchema = z2.object({
|
|
263
274
|
jsonrpc: z2.literal(JSONRPC_VERSION),
|
|
264
|
-
id: z2.union([z2.string(), z2.number().int()]),
|
|
275
|
+
id: z2.optional(z2.union([z2.string(), z2.number().int()])),
|
|
265
276
|
error: z2.object({
|
|
266
277
|
code: z2.number().int(),
|
|
267
278
|
message: z2.string(),
|
|
@@ -369,6 +380,7 @@ var OAuthTokensSchema = z3.object({
|
|
|
369
380
|
expires_in: z3.number().optional(),
|
|
370
381
|
scope: z3.string().optional(),
|
|
371
382
|
refresh_token: z3.string().optional(),
|
|
383
|
+
issuer: SafeUrlSchema.optional(),
|
|
372
384
|
authorization_server: SafeUrlSchema.optional(),
|
|
373
385
|
token_endpoint: SafeUrlSchema.optional()
|
|
374
386
|
}).strip();
|
|
@@ -393,6 +405,8 @@ var OAuthMetadataSchema = z3.looseObject({
|
|
|
393
405
|
authorization_endpoint: SafeUrlSchema,
|
|
394
406
|
token_endpoint: SafeUrlSchema,
|
|
395
407
|
registration_endpoint: SafeUrlSchema.optional(),
|
|
408
|
+
authorization_response_iss_parameter_supported: z3.boolean().optional(),
|
|
409
|
+
client_id_metadata_document_supported: z3.boolean().optional(),
|
|
396
410
|
scopes_supported: z3.array(z3.string()).optional(),
|
|
397
411
|
response_types_supported: z3.array(z3.string()),
|
|
398
412
|
grant_types_supported: z3.array(z3.string()).optional(),
|
|
@@ -407,6 +421,8 @@ var OpenIdProviderMetadataSchema = z3.looseObject({
|
|
|
407
421
|
userinfo_endpoint: SafeUrlSchema.optional(),
|
|
408
422
|
jwks_uri: SafeUrlSchema,
|
|
409
423
|
registration_endpoint: SafeUrlSchema.optional(),
|
|
424
|
+
authorization_response_iss_parameter_supported: z3.boolean().optional(),
|
|
425
|
+
client_id_metadata_document_supported: z3.boolean().optional(),
|
|
410
426
|
scopes_supported: z3.array(z3.string()).optional(),
|
|
411
427
|
response_types_supported: z3.array(z3.string()),
|
|
412
428
|
grant_types_supported: z3.array(z3.string()).optional(),
|
|
@@ -425,11 +441,13 @@ var OAuthClientInformationSchema = z3.object({
|
|
|
425
441
|
client_secret: z3.string().optional(),
|
|
426
442
|
client_id_issued_at: z3.number().optional(),
|
|
427
443
|
client_secret_expires_at: z3.number().optional(),
|
|
444
|
+
issuer: SafeUrlSchema.optional(),
|
|
428
445
|
authorization_server: SafeUrlSchema.optional(),
|
|
429
446
|
token_endpoint: SafeUrlSchema.optional()
|
|
430
447
|
}).strip();
|
|
431
448
|
var OAuthClientMetadataSchema = z3.object({
|
|
432
449
|
redirect_uris: z3.array(SafeUrlSchema),
|
|
450
|
+
application_type: z3.union([z3.literal("native"), z3.literal("web")]).optional(),
|
|
433
451
|
token_endpoint_auth_method: z3.string().optional(),
|
|
434
452
|
grant_types: z3.array(z3.string()).optional(),
|
|
435
453
|
response_types: z3.array(z3.string()).optional(),
|
|
@@ -534,8 +552,20 @@ var UnauthorizedError = class extends Error {
|
|
|
534
552
|
function normalizeUrl(url) {
|
|
535
553
|
return new URL(url).href;
|
|
536
554
|
}
|
|
555
|
+
function validateAuthorizationResponseIssuer({
|
|
556
|
+
callbackIssuer,
|
|
557
|
+
expectedIssuer
|
|
558
|
+
}) {
|
|
559
|
+
if (callbackIssuer != null && callbackIssuer !== expectedIssuer) {
|
|
560
|
+
throw new MCPClientOAuthError({
|
|
561
|
+
message: `OAuth authorization response issuer ${callbackIssuer} does not match expected issuer ${expectedIssuer}`
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}
|
|
537
565
|
function createAuthorizationServerInformation(authorizationServerUrl, metadata) {
|
|
566
|
+
var _a3;
|
|
538
567
|
return {
|
|
568
|
+
issuer: (_a3 = metadata == null ? void 0 : metadata.issuer) != null ? _a3 : String(authorizationServerUrl),
|
|
539
569
|
authorizationServerUrl: normalizeUrl(authorizationServerUrl),
|
|
540
570
|
tokenEndpoint: normalizeUrl(
|
|
541
571
|
(metadata == null ? void 0 : metadata.token_endpoint) ? new URL(metadata.token_endpoint) : new URL("/token", authorizationServerUrl)
|
|
@@ -545,6 +575,7 @@ function createAuthorizationServerInformation(authorizationServerUrl, metadata)
|
|
|
545
575
|
function addAuthorizationServerInformationToTokens(tokens, authorizationServerInformation) {
|
|
546
576
|
return {
|
|
547
577
|
...tokens,
|
|
578
|
+
issuer: authorizationServerInformation.issuer,
|
|
548
579
|
authorization_server: authorizationServerInformation.authorizationServerUrl,
|
|
549
580
|
token_endpoint: authorizationServerInformation.tokenEndpoint
|
|
550
581
|
};
|
|
@@ -552,6 +583,7 @@ function addAuthorizationServerInformationToTokens(tokens, authorizationServerIn
|
|
|
552
583
|
function addAuthorizationServerInformationToClientInformation(clientInformation, authorizationServerInformation) {
|
|
553
584
|
return {
|
|
554
585
|
...clientInformation,
|
|
586
|
+
issuer: authorizationServerInformation.issuer,
|
|
555
587
|
authorization_server: authorizationServerInformation.authorizationServerUrl,
|
|
556
588
|
token_endpoint: authorizationServerInformation.tokenEndpoint
|
|
557
589
|
};
|
|
@@ -561,6 +593,7 @@ function getAuthorizationServerInformationFromCredentials(credentials) {
|
|
|
561
593
|
return void 0;
|
|
562
594
|
}
|
|
563
595
|
return {
|
|
596
|
+
issuer: credentials.issuer,
|
|
564
597
|
authorizationServerUrl: normalizeUrl(credentials.authorization_server),
|
|
565
598
|
tokenEndpoint: normalizeUrl(credentials.token_endpoint)
|
|
566
599
|
};
|
|
@@ -578,6 +611,7 @@ async function getStoredAuthorizationServerInformation({
|
|
|
578
611
|
const providerAuthorizationServerInformation = await ((_a3 = provider.authorizationServerInformation) == null ? void 0 : _a3.call(provider));
|
|
579
612
|
if (providerAuthorizationServerInformation) {
|
|
580
613
|
return {
|
|
614
|
+
issuer: providerAuthorizationServerInformation.issuer,
|
|
581
615
|
authorizationServerUrl: normalizeUrl(
|
|
582
616
|
providerAuthorizationServerInformation.authorizationServerUrl
|
|
583
617
|
),
|
|
@@ -625,32 +659,47 @@ function assertAuthorizationServerInformationMatches({
|
|
|
625
659
|
storedAuthorizationServerInformation,
|
|
626
660
|
currentAuthorizationServerInformation
|
|
627
661
|
}) {
|
|
628
|
-
if (storedAuthorizationServerInformation.authorizationServerUrl !== currentAuthorizationServerInformation.authorizationServerUrl || storedAuthorizationServerInformation.tokenEndpoint !== currentAuthorizationServerInformation.tokenEndpoint) {
|
|
662
|
+
if (storedAuthorizationServerInformation.issuer != null && currentAuthorizationServerInformation.issuer != null && storedAuthorizationServerInformation.issuer !== currentAuthorizationServerInformation.issuer || storedAuthorizationServerInformation.authorizationServerUrl !== currentAuthorizationServerInformation.authorizationServerUrl || storedAuthorizationServerInformation.tokenEndpoint !== currentAuthorizationServerInformation.tokenEndpoint) {
|
|
629
663
|
throw new MCPClientOAuthError({
|
|
630
664
|
message: "OAuth authorization server metadata does not match the metadata that issued the stored credentials"
|
|
631
665
|
});
|
|
632
666
|
}
|
|
633
667
|
}
|
|
634
|
-
function
|
|
635
|
-
var _a3;
|
|
668
|
+
function extractWWWAuthenticateParams(response) {
|
|
669
|
+
var _a3, _b3;
|
|
636
670
|
const header = (_a3 = response.headers.get("www-authenticate")) != null ? _a3 : response.headers.get("WWW-Authenticate");
|
|
637
671
|
if (!header) {
|
|
638
|
-
return
|
|
672
|
+
return {};
|
|
639
673
|
}
|
|
640
674
|
const [type, scheme] = header.split(" ");
|
|
641
675
|
if (type.toLowerCase() !== "bearer" || !scheme) {
|
|
642
|
-
return
|
|
643
|
-
}
|
|
644
|
-
const regex = /resource_metadata="([^"]*)"/;
|
|
645
|
-
const match = header.match(regex);
|
|
646
|
-
if (!match) {
|
|
647
|
-
return void 0;
|
|
676
|
+
return {};
|
|
648
677
|
}
|
|
678
|
+
const resourceMetadataMatch = header.match(
|
|
679
|
+
/(?:^|[,\s])resource_metadata="([^"]*)"/i
|
|
680
|
+
);
|
|
681
|
+
const scope = (_b3 = header.match(/(?:^|[,\s])scope="([^"]*)"/i)) == null ? void 0 : _b3[1];
|
|
682
|
+
let resourceMetadataUrl;
|
|
649
683
|
try {
|
|
650
|
-
|
|
684
|
+
resourceMetadataUrl = resourceMetadataMatch ? new URL(resourceMetadataMatch[1]) : void 0;
|
|
651
685
|
} catch (e) {
|
|
652
|
-
return void 0;
|
|
653
686
|
}
|
|
687
|
+
return { resourceMetadataUrl, scope };
|
|
688
|
+
}
|
|
689
|
+
function selectScope({
|
|
690
|
+
scope,
|
|
691
|
+
resourceMetadata,
|
|
692
|
+
clientMetadata
|
|
693
|
+
}) {
|
|
694
|
+
var _a3;
|
|
695
|
+
if (scope) {
|
|
696
|
+
return scope;
|
|
697
|
+
}
|
|
698
|
+
const resourceScopes = (_a3 = resourceMetadata == null ? void 0 : resourceMetadata.scopes_supported) == null ? void 0 : _a3.join(" ");
|
|
699
|
+
if (resourceScopes) {
|
|
700
|
+
return resourceScopes;
|
|
701
|
+
}
|
|
702
|
+
return clientMetadata.scope;
|
|
654
703
|
}
|
|
655
704
|
function buildWellKnownPath(wellKnownPrefix, pathname = "", options = {}) {
|
|
656
705
|
if (pathname.endsWith("/")) {
|
|
@@ -1059,6 +1108,7 @@ async function registerClient(authorizationServerUrl, {
|
|
|
1059
1108
|
clientMetadata,
|
|
1060
1109
|
fetchFn
|
|
1061
1110
|
}) {
|
|
1111
|
+
var _a3;
|
|
1062
1112
|
let registrationUrl;
|
|
1063
1113
|
if (metadata) {
|
|
1064
1114
|
if (!metadata.registration_endpoint) {
|
|
@@ -1070,18 +1120,29 @@ async function registerClient(authorizationServerUrl, {
|
|
|
1070
1120
|
} else {
|
|
1071
1121
|
registrationUrl = new URL("/register", authorizationServerUrl);
|
|
1072
1122
|
}
|
|
1123
|
+
const applicationType = (_a3 = clientMetadata.application_type) != null ? _a3 : inferOAuthApplicationType(clientMetadata.redirect_uris);
|
|
1073
1124
|
const response = await (fetchFn != null ? fetchFn : fetch)(registrationUrl, {
|
|
1074
1125
|
method: "POST",
|
|
1075
1126
|
headers: {
|
|
1076
1127
|
"Content-Type": "application/json"
|
|
1077
1128
|
},
|
|
1078
|
-
body: JSON.stringify(
|
|
1129
|
+
body: JSON.stringify({
|
|
1130
|
+
...clientMetadata,
|
|
1131
|
+
application_type: applicationType
|
|
1132
|
+
})
|
|
1079
1133
|
});
|
|
1080
1134
|
if (!response.ok) {
|
|
1081
1135
|
throw await parseErrorResponse(response);
|
|
1082
1136
|
}
|
|
1083
1137
|
return OAuthClientInformationFullSchema.parse(await response.json());
|
|
1084
1138
|
}
|
|
1139
|
+
function inferOAuthApplicationType(redirectUris) {
|
|
1140
|
+
const isNativeRedirectUri = (redirectUri) => {
|
|
1141
|
+
const url = new URL(redirectUri);
|
|
1142
|
+
return (url.protocol === "http:" || url.protocol === "https:") && (url.hostname === "localhost" || url.hostname.endsWith(".localhost") || url.hostname === "127.0.0.1" || url.hostname === "[::1]") || url.protocol !== "http:" && url.protocol !== "https:";
|
|
1143
|
+
};
|
|
1144
|
+
return redirectUris.every(isNativeRedirectUri) ? "native" : "web";
|
|
1145
|
+
}
|
|
1085
1146
|
async function auth(provider, options) {
|
|
1086
1147
|
var _a3, _b3;
|
|
1087
1148
|
try {
|
|
@@ -1122,11 +1183,12 @@ async function authInternal(provider, {
|
|
|
1122
1183
|
serverUrl,
|
|
1123
1184
|
authorizationCode,
|
|
1124
1185
|
callbackState,
|
|
1186
|
+
callbackIssuer,
|
|
1125
1187
|
scope,
|
|
1126
1188
|
resourceMetadataUrl,
|
|
1127
1189
|
fetchFn
|
|
1128
1190
|
}) {
|
|
1129
|
-
var _a3, _b3;
|
|
1191
|
+
var _a3, _b3, _c, _d;
|
|
1130
1192
|
let resourceMetadata;
|
|
1131
1193
|
let authorizationServerUrl;
|
|
1132
1194
|
assertResourceMetadataUrlSameOrigin(serverUrl, resourceMetadataUrl);
|
|
@@ -1162,6 +1224,18 @@ async function authInternal(provider, {
|
|
|
1162
1224
|
);
|
|
1163
1225
|
const currentAuthorizationServerInformation = createAuthorizationServerInformation(authorizationServerUrl, metadata);
|
|
1164
1226
|
let clientInformation = await Promise.resolve(provider.clientInformation());
|
|
1227
|
+
if ((clientInformation == null ? void 0 : clientInformation.issuer) != null) {
|
|
1228
|
+
const storedAuthorizationServerInformation = await getStoredAuthorizationServerInformation({
|
|
1229
|
+
provider,
|
|
1230
|
+
clientInformation
|
|
1231
|
+
});
|
|
1232
|
+
if (storedAuthorizationServerInformation) {
|
|
1233
|
+
assertAuthorizationServerInformationMatches({
|
|
1234
|
+
storedAuthorizationServerInformation,
|
|
1235
|
+
currentAuthorizationServerInformation
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1165
1239
|
if (!clientInformation) {
|
|
1166
1240
|
if (authorizationCode !== void 0) {
|
|
1167
1241
|
throw new Error(
|
|
@@ -1202,6 +1276,10 @@ async function authInternal(provider, {
|
|
|
1202
1276
|
message: "Stored OAuth authorization server metadata is required when exchanging an authorization code"
|
|
1203
1277
|
});
|
|
1204
1278
|
}
|
|
1279
|
+
validateAuthorizationResponseIssuer({
|
|
1280
|
+
callbackIssuer,
|
|
1281
|
+
expectedIssuer: (_c = (_b3 = storedAuthorizationServerInformation.issuer) != null ? _b3 : metadata == null ? void 0 : metadata.issuer) != null ? _c : String(authorizationServerUrl)
|
|
1282
|
+
});
|
|
1205
1283
|
assertAuthorizationServerInformationMatches({
|
|
1206
1284
|
storedAuthorizationServerInformation,
|
|
1207
1285
|
currentAuthorizationServerInformation
|
|
@@ -1238,7 +1316,7 @@ async function authInternal(provider, {
|
|
|
1238
1316
|
currentAuthorizationServerInformation
|
|
1239
1317
|
});
|
|
1240
1318
|
} else {
|
|
1241
|
-
await ((
|
|
1319
|
+
await ((_d = provider.invalidateCredentials) == null ? void 0 : _d.call(provider, "tokens"));
|
|
1242
1320
|
}
|
|
1243
1321
|
try {
|
|
1244
1322
|
if (storedAuthorizationServerInformation) {
|
|
@@ -1279,7 +1357,11 @@ async function authInternal(provider, {
|
|
|
1279
1357
|
clientInformation,
|
|
1280
1358
|
state,
|
|
1281
1359
|
redirectUrl: provider.redirectUrl,
|
|
1282
|
-
scope:
|
|
1360
|
+
scope: selectScope({
|
|
1361
|
+
scope,
|
|
1362
|
+
resourceMetadata,
|
|
1363
|
+
clientMetadata: provider.clientMetadata
|
|
1364
|
+
}),
|
|
1283
1365
|
resource
|
|
1284
1366
|
}
|
|
1285
1367
|
);
|
|
@@ -1325,7 +1407,7 @@ var SseMCPTransport = class {
|
|
|
1325
1407
|
const headers = {
|
|
1326
1408
|
...this.headers,
|
|
1327
1409
|
...base,
|
|
1328
|
-
"mcp-protocol-version": (_a3 = this.protocolVersion) != null ? _a3 :
|
|
1410
|
+
"mcp-protocol-version": (_a3 = this.protocolVersion) != null ? _a3 : LATEST_LEGACY_PROTOCOL_VERSION
|
|
1329
1411
|
};
|
|
1330
1412
|
if (this.authProvider) {
|
|
1331
1413
|
const tokens = await this.authProvider.tokens();
|
|
@@ -1357,11 +1439,13 @@ var SseMCPTransport = class {
|
|
|
1357
1439
|
redirect: this.redirectMode
|
|
1358
1440
|
});
|
|
1359
1441
|
if (response.status === 401 && this.authProvider && !triedAuth) {
|
|
1360
|
-
|
|
1442
|
+
const { resourceMetadataUrl, scope } = extractWWWAuthenticateParams(response);
|
|
1443
|
+
this.resourceMetadataUrl = resourceMetadataUrl;
|
|
1361
1444
|
try {
|
|
1362
1445
|
const result = await auth(this.authProvider, {
|
|
1363
1446
|
serverUrl: this.url,
|
|
1364
1447
|
resourceMetadataUrl: this.resourceMetadataUrl,
|
|
1448
|
+
scope,
|
|
1365
1449
|
fetchFn: this.fetchFn
|
|
1366
1450
|
});
|
|
1367
1451
|
if (result !== "AUTHORIZED") {
|
|
@@ -1490,11 +1574,13 @@ var SseMCPTransport = class {
|
|
|
1490
1574
|
};
|
|
1491
1575
|
const response = await this.fetchFn(endpoint.href, init);
|
|
1492
1576
|
if (response.status === 401 && this.authProvider && !triedAuth) {
|
|
1493
|
-
|
|
1577
|
+
const { resourceMetadataUrl, scope } = extractWWWAuthenticateParams(response);
|
|
1578
|
+
this.resourceMetadataUrl = resourceMetadataUrl;
|
|
1494
1579
|
try {
|
|
1495
1580
|
const result = await auth(this.authProvider, {
|
|
1496
1581
|
serverUrl: this.url,
|
|
1497
1582
|
resourceMetadataUrl: this.resourceMetadataUrl,
|
|
1583
|
+
scope,
|
|
1498
1584
|
fetchFn: this.fetchFn
|
|
1499
1585
|
});
|
|
1500
1586
|
if (result !== "AUTHORIZED") {
|
|
@@ -1534,6 +1620,107 @@ import {
|
|
|
1534
1620
|
withUserAgentSuffix as withUserAgentSuffix2,
|
|
1535
1621
|
getRuntimeEnvironmentUserAgent as getRuntimeEnvironmentUserAgent2
|
|
1536
1622
|
} from "@ai-sdk/provider-utils";
|
|
1623
|
+
|
|
1624
|
+
// src/tool/mcp-http-headers.ts
|
|
1625
|
+
import { convertUint8ArrayToBase64, isRecord } from "@ai-sdk/provider-utils";
|
|
1626
|
+
var HTTP_TOKEN_PATTERN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
1627
|
+
var BASE64_SENTINEL_PATTERN = /^=\?base64\?.*\?=$/;
|
|
1628
|
+
function encodeMCPHeaderValue(value) {
|
|
1629
|
+
const isPlainAscii = [...value].every((character) => {
|
|
1630
|
+
const code = character.charCodeAt(0);
|
|
1631
|
+
return code === 9 || code >= 32 && code <= 126;
|
|
1632
|
+
});
|
|
1633
|
+
if (isPlainAscii && value.trim() === value && !BASE64_SENTINEL_PATTERN.test(value)) {
|
|
1634
|
+
return value;
|
|
1635
|
+
}
|
|
1636
|
+
return `=?base64?${convertUint8ArrayToBase64(new TextEncoder().encode(value))}?=`;
|
|
1637
|
+
}
|
|
1638
|
+
function getMCPToolHeaderBindings(inputSchema) {
|
|
1639
|
+
if (!isRecord(inputSchema)) {
|
|
1640
|
+
return {
|
|
1641
|
+
success: false,
|
|
1642
|
+
error: "inputSchema must be a JSON Schema object"
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
const bindings = [];
|
|
1646
|
+
const headerNames = /* @__PURE__ */ new Set();
|
|
1647
|
+
let error;
|
|
1648
|
+
const visit = (value, path, staticallyReachable) => {
|
|
1649
|
+
if (error != null || !isRecord(value)) {
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
if ("x-mcp-header" in value) {
|
|
1653
|
+
if (!staticallyReachable || path.length === 0) {
|
|
1654
|
+
error = "x-mcp-header is not on a statically reachable property";
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
const headerName = value["x-mcp-header"];
|
|
1658
|
+
if (typeof headerName !== "string" || !HTTP_TOKEN_PATTERN.test(headerName)) {
|
|
1659
|
+
error = "x-mcp-header must be a non-empty HTTP token";
|
|
1660
|
+
return;
|
|
1661
|
+
}
|
|
1662
|
+
const normalizedHeaderName = headerName.toLowerCase();
|
|
1663
|
+
if (headerNames.has(normalizedHeaderName)) {
|
|
1664
|
+
error = `x-mcp-header value "${headerName}" is not unique`;
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
const valueType = value.type;
|
|
1668
|
+
if (valueType !== "boolean" && valueType !== "integer" && valueType !== "string") {
|
|
1669
|
+
error = "x-mcp-header can only annotate boolean, integer, or string properties";
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1672
|
+
headerNames.add(normalizedHeaderName);
|
|
1673
|
+
bindings.push({ headerName, path, valueType });
|
|
1674
|
+
}
|
|
1675
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1676
|
+
if (key === "x-mcp-header") {
|
|
1677
|
+
continue;
|
|
1678
|
+
}
|
|
1679
|
+
if (key === "properties" && isRecord(child)) {
|
|
1680
|
+
for (const [propertyName, propertySchema] of Object.entries(child)) {
|
|
1681
|
+
visit(propertySchema, [...path, propertyName], staticallyReachable);
|
|
1682
|
+
}
|
|
1683
|
+
} else {
|
|
1684
|
+
visit(child, path, false);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
};
|
|
1688
|
+
visit(inputSchema, [], true);
|
|
1689
|
+
return error == null ? { success: true, bindings } : { success: false, error };
|
|
1690
|
+
}
|
|
1691
|
+
function getValueAtPath(value, path) {
|
|
1692
|
+
let current = value;
|
|
1693
|
+
for (const segment of path) {
|
|
1694
|
+
if (!isRecord(current)) {
|
|
1695
|
+
return void 0;
|
|
1696
|
+
}
|
|
1697
|
+
current = current[segment];
|
|
1698
|
+
}
|
|
1699
|
+
return current;
|
|
1700
|
+
}
|
|
1701
|
+
function createMCPToolHeaders({
|
|
1702
|
+
bindings,
|
|
1703
|
+
args
|
|
1704
|
+
}) {
|
|
1705
|
+
const headers = {};
|
|
1706
|
+
for (const binding of bindings) {
|
|
1707
|
+
const value = getValueAtPath(args, binding.path);
|
|
1708
|
+
if (value == null) {
|
|
1709
|
+
continue;
|
|
1710
|
+
}
|
|
1711
|
+
if (binding.valueType === "string" && typeof value !== "string" || binding.valueType === "boolean" && typeof value !== "boolean" || binding.valueType === "integer" && !Number.isSafeInteger(value)) {
|
|
1712
|
+
throw new TypeError(
|
|
1713
|
+
`Tool argument "${binding.path.join(".")}" does not match its x-mcp-header type`
|
|
1714
|
+
);
|
|
1715
|
+
}
|
|
1716
|
+
headers[`Mcp-Param-${binding.headerName}`] = encodeMCPHeaderValue(
|
|
1717
|
+
String(value)
|
|
1718
|
+
);
|
|
1719
|
+
}
|
|
1720
|
+
return headers;
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
// src/tool/mcp-http-transport.ts
|
|
1537
1724
|
function isMessageEvent2(event) {
|
|
1538
1725
|
return event === void 0 || event === "message";
|
|
1539
1726
|
}
|
|
@@ -1550,6 +1737,8 @@ var HttpMCPTransport = class {
|
|
|
1550
1737
|
terminateSessionOnClose = true,
|
|
1551
1738
|
fetch: fetchFn
|
|
1552
1739
|
}) {
|
|
1740
|
+
this.supportsProtocolVersionDiscovery = true;
|
|
1741
|
+
this.supportsMcpToolParameterHeaders = true;
|
|
1553
1742
|
this.inboundReconnectAttempts = 0;
|
|
1554
1743
|
this.reconnectionOptions = {
|
|
1555
1744
|
initialReconnectionDelay: 1e3,
|
|
@@ -1562,14 +1751,30 @@ var HttpMCPTransport = class {
|
|
|
1562
1751
|
this.authProvider = authProvider;
|
|
1563
1752
|
this.redirectMode = redirect;
|
|
1564
1753
|
this.sessionId = initialSessionId;
|
|
1565
|
-
this.protocolVersion = initialProtocolVersion;
|
|
1754
|
+
this.protocolVersion = initialProtocolVersion != null ? initialProtocolVersion : LATEST_LEGACY_PROTOCOL_VERSION;
|
|
1566
1755
|
this.onSessionIdChange = onSessionIdChange;
|
|
1567
1756
|
this.onSessionExpired = onSessionExpired;
|
|
1568
1757
|
this.terminateSessionOnClose = terminateSessionOnClose;
|
|
1569
1758
|
this.fetchFn = fetchFn != null ? fetchFn : globalThis.fetch;
|
|
1570
1759
|
}
|
|
1571
1760
|
setProtocolVersion(version) {
|
|
1761
|
+
var _a3;
|
|
1572
1762
|
this.protocolVersion = version;
|
|
1763
|
+
if (!this.abortController) {
|
|
1764
|
+
return;
|
|
1765
|
+
}
|
|
1766
|
+
if (this.isModernProtocol()) {
|
|
1767
|
+
(_a3 = this.inboundSseConnection) == null ? void 0 : _a3.close();
|
|
1768
|
+
this.inboundSseConnection = void 0;
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1771
|
+
if (!this.inboundSseConnection) {
|
|
1772
|
+
this.startInboundSse();
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
isModernProtocol() {
|
|
1776
|
+
var _a3;
|
|
1777
|
+
return ((_a3 = this.protocolVersion) != null ? _a3 : LATEST_PROTOCOL_VERSION) === LATEST_PROTOCOL_VERSION;
|
|
1573
1778
|
}
|
|
1574
1779
|
async commonHeaders({
|
|
1575
1780
|
base,
|
|
@@ -1579,9 +1784,9 @@ var HttpMCPTransport = class {
|
|
|
1579
1784
|
const headers = {
|
|
1580
1785
|
...this.headers,
|
|
1581
1786
|
...base,
|
|
1582
|
-
"mcp-protocol-version": (_a3 = this.protocolVersion) != null ? _a3 :
|
|
1787
|
+
"mcp-protocol-version": (_a3 = this.protocolVersion) != null ? _a3 : LATEST_LEGACY_PROTOCOL_VERSION
|
|
1583
1788
|
};
|
|
1584
|
-
if (includeSessionId && this.sessionId) {
|
|
1789
|
+
if (!this.isModernProtocol() && includeSessionId && this.sessionId) {
|
|
1585
1790
|
headers["mcp-session-id"] = this.sessionId;
|
|
1586
1791
|
}
|
|
1587
1792
|
if (this.authProvider) {
|
|
@@ -1605,6 +1810,9 @@ var HttpMCPTransport = class {
|
|
|
1605
1810
|
(_a3 = this.onSessionIdChange) == null ? void 0 : _a3.call(this, sessionId);
|
|
1606
1811
|
}
|
|
1607
1812
|
applySessionIdFromResponse(response) {
|
|
1813
|
+
if (this.isModernProtocol()) {
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1608
1816
|
const sessionId = response.headers.get("mcp-session-id");
|
|
1609
1817
|
if (sessionId) {
|
|
1610
1818
|
this.setSessionId(sessionId);
|
|
@@ -1620,7 +1828,7 @@ var HttpMCPTransport = class {
|
|
|
1620
1828
|
/**
|
|
1621
1829
|
* Runs a single OAuth recovery flow for concurrent 401 responses.
|
|
1622
1830
|
*/
|
|
1623
|
-
authorizeOnce(resourceMetadataUrl) {
|
|
1831
|
+
authorizeOnce(resourceMetadataUrl, scope) {
|
|
1624
1832
|
if (!this.authProvider) {
|
|
1625
1833
|
return Promise.resolve("REDIRECT");
|
|
1626
1834
|
}
|
|
@@ -1628,6 +1836,7 @@ var HttpMCPTransport = class {
|
|
|
1628
1836
|
this.authPromise = auth(this.authProvider, {
|
|
1629
1837
|
serverUrl: this.url,
|
|
1630
1838
|
resourceMetadataUrl,
|
|
1839
|
+
scope,
|
|
1631
1840
|
fetchFn: this.fetchFn
|
|
1632
1841
|
}).finally(() => {
|
|
1633
1842
|
this.authPromise = void 0;
|
|
@@ -1642,14 +1851,16 @@ var HttpMCPTransport = class {
|
|
|
1642
1851
|
});
|
|
1643
1852
|
}
|
|
1644
1853
|
this.abortController = new AbortController();
|
|
1645
|
-
this.
|
|
1854
|
+
if (this.protocolVersion != null && this.protocolVersion !== LATEST_PROTOCOL_VERSION) {
|
|
1855
|
+
this.startInboundSse();
|
|
1856
|
+
}
|
|
1646
1857
|
}
|
|
1647
1858
|
async close(options) {
|
|
1648
1859
|
var _a3, _b3, _c, _d, _e;
|
|
1649
1860
|
(_a3 = this.inboundSseConnection) == null ? void 0 : _a3.close();
|
|
1650
1861
|
(_b3 = this.abortController) == null ? void 0 : _b3.abort();
|
|
1651
1862
|
try {
|
|
1652
|
-
if (this.sessionId && this.terminateSessionOnClose && this.abortController) {
|
|
1863
|
+
if (!this.isModernProtocol() && this.sessionId && this.terminateSessionOnClose && this.abortController) {
|
|
1653
1864
|
(_c = options == null ? void 0 : options.signal) == null ? void 0 : _c.throwIfAborted();
|
|
1654
1865
|
const headers = await this.commonHeaders({ base: {} });
|
|
1655
1866
|
(_d = options == null ? void 0 : options.signal) == null ? void 0 : _d.throwIfAborted();
|
|
@@ -1670,14 +1881,16 @@ var HttpMCPTransport = class {
|
|
|
1670
1881
|
const transportSignal = (_b3 = this.abortController) == null ? void 0 : _b3.signal;
|
|
1671
1882
|
const requestSignal = (options == null ? void 0 : options.signal) == null ? transportSignal : transportSignal == null ? options.signal : AbortSignal.any([transportSignal, options.signal]);
|
|
1672
1883
|
const attempt = async (triedAuth = false) => {
|
|
1673
|
-
var _a4, _b4, _c, _d, _e, _f, _g;
|
|
1884
|
+
var _a4, _b4, _c, _d, _e, _f, _g, _h;
|
|
1674
1885
|
try {
|
|
1675
1886
|
const isInitializeRequest = "method" in message && message.method === "initialize";
|
|
1676
1887
|
const sessionIdForRequest = isInitializeRequest ? void 0 : this.sessionId;
|
|
1677
1888
|
const headers = await this.commonHeaders({
|
|
1678
1889
|
base: {
|
|
1679
1890
|
"Content-Type": "application/json",
|
|
1680
|
-
Accept: "application/json, text/event-stream"
|
|
1891
|
+
Accept: "application/json, text/event-stream",
|
|
1892
|
+
...this.isModernProtocol() ? options == null ? void 0 : options.headers : {},
|
|
1893
|
+
...this.isModernProtocol() && "method" in message && "id" in message ? this.getStandardRequestHeaders(message) : {}
|
|
1681
1894
|
},
|
|
1682
1895
|
includeSessionId: !isInitializeRequest
|
|
1683
1896
|
});
|
|
@@ -1691,9 +1904,13 @@ var HttpMCPTransport = class {
|
|
|
1691
1904
|
const response = await this.fetchFn(this.url.href, init);
|
|
1692
1905
|
this.applySessionIdFromResponse(response);
|
|
1693
1906
|
if (response.status === 401 && this.authProvider && !triedAuth) {
|
|
1694
|
-
|
|
1907
|
+
const { resourceMetadataUrl, scope } = extractWWWAuthenticateParams(response);
|
|
1908
|
+
this.resourceMetadataUrl = resourceMetadataUrl;
|
|
1695
1909
|
try {
|
|
1696
|
-
const result = await this.authorizeOnce(
|
|
1910
|
+
const result = await this.authorizeOnce(
|
|
1911
|
+
this.resourceMetadataUrl,
|
|
1912
|
+
scope
|
|
1913
|
+
);
|
|
1697
1914
|
if (result !== "AUTHORIZED") {
|
|
1698
1915
|
const error2 = new UnauthorizedError();
|
|
1699
1916
|
throw error2;
|
|
@@ -1705,19 +1922,31 @@ var HttpMCPTransport = class {
|
|
|
1705
1922
|
return attempt(true);
|
|
1706
1923
|
}
|
|
1707
1924
|
if (response.status === 202) {
|
|
1708
|
-
if (!this.inboundSseConnection) {
|
|
1925
|
+
if (!this.isModernProtocol() && !this.inboundSseConnection) {
|
|
1709
1926
|
this.startInboundSse();
|
|
1710
1927
|
}
|
|
1711
1928
|
return;
|
|
1712
1929
|
}
|
|
1713
1930
|
if (!response.ok) {
|
|
1714
1931
|
const text = await response.text().catch(() => null);
|
|
1932
|
+
if ("id" in message && text != null) {
|
|
1933
|
+
const jsonRpcMessage = await parseJSONRPCMessage(text).catch(
|
|
1934
|
+
() => void 0
|
|
1935
|
+
);
|
|
1936
|
+
if (jsonRpcMessage != null && "error" in jsonRpcMessage) {
|
|
1937
|
+
(_b4 = this.onmessage) == null ? void 0 : _b4.call(
|
|
1938
|
+
this,
|
|
1939
|
+
jsonRpcMessage.id == null ? { ...jsonRpcMessage, id: message.id } : jsonRpcMessage
|
|
1940
|
+
);
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1715
1944
|
let errorMessage = `MCP HTTP Transport Error: POSTing to endpoint (HTTP ${response.status}): ${text}`;
|
|
1716
1945
|
if (response.status === 404) {
|
|
1717
|
-
if (sessionIdForRequest) {
|
|
1946
|
+
if (!this.isModernProtocol() && sessionIdForRequest) {
|
|
1718
1947
|
this.expireSessionId(sessionIdForRequest);
|
|
1719
1948
|
errorMessage += ". The MCP session expired. Create a new client without `initialSessionId` to start a fresh session";
|
|
1720
|
-
} else {
|
|
1949
|
+
} else if (!this.isModernProtocol()) {
|
|
1721
1950
|
errorMessage += ". This server does not support HTTP transport. Try using `sse` transport instead";
|
|
1722
1951
|
}
|
|
1723
1952
|
}
|
|
@@ -1727,7 +1956,7 @@ var HttpMCPTransport = class {
|
|
|
1727
1956
|
url: this.url.href,
|
|
1728
1957
|
responseBody: text != null ? text : void 0
|
|
1729
1958
|
});
|
|
1730
|
-
(
|
|
1959
|
+
(_c = this.onerror) == null ? void 0 : _c.call(this, error2);
|
|
1731
1960
|
throw error2;
|
|
1732
1961
|
}
|
|
1733
1962
|
const isNotification = !("id" in message);
|
|
@@ -1739,7 +1968,7 @@ var HttpMCPTransport = class {
|
|
|
1739
1968
|
const data = await response.json();
|
|
1740
1969
|
const messages = Array.isArray(data) ? data.map((message2) => validateJSONRPCMessage(message2)) : [validateJSONRPCMessage(data)];
|
|
1741
1970
|
for (const jsonRpcMessage of messages) {
|
|
1742
|
-
(
|
|
1971
|
+
(_d = this.onmessage) == null ? void 0 : _d.call(this, jsonRpcMessage);
|
|
1743
1972
|
}
|
|
1744
1973
|
return;
|
|
1745
1974
|
}
|
|
@@ -1750,7 +1979,7 @@ var HttpMCPTransport = class {
|
|
|
1750
1979
|
statusCode: response.status,
|
|
1751
1980
|
url: this.url.href
|
|
1752
1981
|
});
|
|
1753
|
-
(
|
|
1982
|
+
(_e = this.onerror) == null ? void 0 : _e.call(this, error2);
|
|
1754
1983
|
throw error2;
|
|
1755
1984
|
}
|
|
1756
1985
|
const stream = response.body.pipeThrough(new TextDecoderStream()).pipeThrough(new EventSourceParserStream2());
|
|
@@ -1796,18 +2025,29 @@ var HttpMCPTransport = class {
|
|
|
1796
2025
|
statusCode: response.status,
|
|
1797
2026
|
url: this.url.href
|
|
1798
2027
|
});
|
|
1799
|
-
(
|
|
2028
|
+
(_f = this.onerror) == null ? void 0 : _f.call(this, error);
|
|
1800
2029
|
throw error;
|
|
1801
2030
|
} catch (error) {
|
|
1802
|
-
if ((
|
|
2031
|
+
if ((_g = options == null ? void 0 : options.signal) == null ? void 0 : _g.aborted) {
|
|
1803
2032
|
throw error;
|
|
1804
2033
|
}
|
|
1805
|
-
(
|
|
2034
|
+
(_h = this.onerror) == null ? void 0 : _h.call(this, error);
|
|
1806
2035
|
throw error;
|
|
1807
2036
|
}
|
|
1808
2037
|
};
|
|
1809
2038
|
await attempt();
|
|
1810
2039
|
}
|
|
2040
|
+
getStandardRequestHeaders(message) {
|
|
2041
|
+
const headers = {
|
|
2042
|
+
"Mcp-Method": message.method
|
|
2043
|
+
};
|
|
2044
|
+
const params = message.params;
|
|
2045
|
+
const name3 = message.method === "resources/read" ? params == null ? void 0 : params.uri : message.method === "tools/call" || message.method === "prompts/get" ? params == null ? void 0 : params.name : void 0;
|
|
2046
|
+
if (typeof name3 === "string") {
|
|
2047
|
+
headers["Mcp-Name"] = encodeMCPHeaderValue(name3);
|
|
2048
|
+
}
|
|
2049
|
+
return headers;
|
|
2050
|
+
}
|
|
1811
2051
|
getNextReconnectionDelay(attempt) {
|
|
1812
2052
|
const {
|
|
1813
2053
|
initialReconnectionDelay,
|
|
@@ -1840,6 +2080,9 @@ var HttpMCPTransport = class {
|
|
|
1840
2080
|
}, delay);
|
|
1841
2081
|
}
|
|
1842
2082
|
startInboundSse(triedAuth = false, resumeToken) {
|
|
2083
|
+
if (this.isModernProtocol()) {
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
1843
2086
|
void this.openInboundSse(triedAuth, resumeToken).catch((error) => {
|
|
1844
2087
|
var _a3;
|
|
1845
2088
|
if (error instanceof Error && error.name === "AbortError") {
|
|
@@ -1851,6 +2094,9 @@ var HttpMCPTransport = class {
|
|
|
1851
2094
|
// Open optional inbound SSE stream; best-effort and resumable
|
|
1852
2095
|
async openInboundSse(triedAuth = false, resumeToken) {
|
|
1853
2096
|
var _a3, _b3, _c, _d, _e, _f;
|
|
2097
|
+
if (this.isModernProtocol()) {
|
|
2098
|
+
return;
|
|
2099
|
+
}
|
|
1854
2100
|
try {
|
|
1855
2101
|
const sessionIdForRequest = this.sessionId;
|
|
1856
2102
|
const headers = await this.commonHeaders({
|
|
@@ -1869,9 +2115,13 @@ var HttpMCPTransport = class {
|
|
|
1869
2115
|
});
|
|
1870
2116
|
this.applySessionIdFromResponse(response);
|
|
1871
2117
|
if (response.status === 401 && this.authProvider && !triedAuth) {
|
|
1872
|
-
|
|
2118
|
+
const { resourceMetadataUrl, scope } = extractWWWAuthenticateParams(response);
|
|
2119
|
+
this.resourceMetadataUrl = resourceMetadataUrl;
|
|
1873
2120
|
try {
|
|
1874
|
-
const result = await this.authorizeOnce(
|
|
2121
|
+
const result = await this.authorizeOnce(
|
|
2122
|
+
this.resourceMetadataUrl,
|
|
2123
|
+
scope
|
|
2124
|
+
);
|
|
1875
2125
|
if (result !== "AUTHORIZED") {
|
|
1876
2126
|
const error = new UnauthorizedError();
|
|
1877
2127
|
(_b3 = this.onerror) == null ? void 0 : _b3.call(this, error);
|
|
@@ -1969,11 +2219,15 @@ var HttpMCPTransport = class {
|
|
|
1969
2219
|
|
|
1970
2220
|
// src/tool/mcp-transport.ts
|
|
1971
2221
|
function createMcpTransport(config) {
|
|
2222
|
+
var _a3;
|
|
1972
2223
|
switch (config.type) {
|
|
1973
2224
|
case "sse":
|
|
1974
2225
|
return new SseMCPTransport(config);
|
|
1975
2226
|
case "http":
|
|
1976
|
-
return new HttpMCPTransport(
|
|
2227
|
+
return new HttpMCPTransport({
|
|
2228
|
+
...config,
|
|
2229
|
+
initialProtocolVersion: (_a3 = config.initialProtocolVersion) != null ? _a3 : LATEST_PROTOCOL_VERSION
|
|
2230
|
+
});
|
|
1977
2231
|
default:
|
|
1978
2232
|
throw new MCPClientError({
|
|
1979
2233
|
message: "Unsupported or invalid transport configuration. If you are using a custom transport, make sure it implements the MCPTransport interface."
|
|
@@ -2108,6 +2362,8 @@ async function readMCPAppResource({
|
|
|
2108
2362
|
// src/tool/mcp-client.ts
|
|
2109
2363
|
var CLIENT_VERSION = "1.0.0";
|
|
2110
2364
|
var DEFAULT_MAX_TOOL_CALL_RETRIES = 0;
|
|
2365
|
+
var DEFAULT_PROTOCOL_DISCOVERY_TIMEOUT = 1e3;
|
|
2366
|
+
var MODERN_PROTOCOL_ERROR_CODES = [-32020, -32021, -32022];
|
|
2111
2367
|
var DEFAULT_RETRY_ERROR_CODES = [
|
|
2112
2368
|
"ConnectionRefused",
|
|
2113
2369
|
"ConnectionClosed",
|
|
@@ -2233,23 +2489,28 @@ var DefaultMCPClient = class {
|
|
|
2233
2489
|
maxRetries,
|
|
2234
2490
|
capabilities,
|
|
2235
2491
|
initialInitializeResult,
|
|
2236
|
-
initializationOptions
|
|
2492
|
+
initializationOptions,
|
|
2493
|
+
protocolVersionDiscovery = true
|
|
2237
2494
|
}) {
|
|
2238
2495
|
this.requestMessageId = 0;
|
|
2239
2496
|
this.responseHandlers = /* @__PURE__ */ new Map();
|
|
2240
2497
|
this.serverCapabilities = {};
|
|
2241
2498
|
this._serverInfo = { name: "", version: "" };
|
|
2242
2499
|
this._initializeResult = {
|
|
2243
|
-
protocolVersion:
|
|
2500
|
+
protocolVersion: LATEST_LEGACY_PROTOCOL_VERSION,
|
|
2244
2501
|
capabilities: {},
|
|
2245
2502
|
serverInfo: this._serverInfo
|
|
2246
2503
|
};
|
|
2504
|
+
this.protocolEra = "legacy";
|
|
2505
|
+
this.protocolVersion = LATEST_LEGACY_PROTOCOL_VERSION;
|
|
2506
|
+
this.toolHeaderBindings = /* @__PURE__ */ new Map();
|
|
2247
2507
|
this.isClosed = true;
|
|
2248
2508
|
this.onUncaughtError = onUncaughtError;
|
|
2249
2509
|
this.maxRetries = prepareMaxRetries(maxRetries);
|
|
2250
2510
|
this.clientCapabilities = capabilities != null ? capabilities : {};
|
|
2251
2511
|
this.initialInitializeResult = initialInitializeResult;
|
|
2252
2512
|
this.initializationOptions = initializationOptions;
|
|
2513
|
+
this.protocolVersionDiscovery = protocolVersionDiscovery;
|
|
2253
2514
|
if (isCustomMcpTransport(transportConfig)) {
|
|
2254
2515
|
this.transport = transportConfig;
|
|
2255
2516
|
} else {
|
|
@@ -2313,11 +2574,20 @@ var DefaultMCPClient = class {
|
|
|
2313
2574
|
this.applyInitializeResult(result2);
|
|
2314
2575
|
return this;
|
|
2315
2576
|
}
|
|
2577
|
+
if (this.protocolVersionDiscovery && this.transport.supportsProtocolVersionDiscovery) {
|
|
2578
|
+
const discovered = await this.tryProtocolDiscovery(signal);
|
|
2579
|
+
if (discovered) {
|
|
2580
|
+
return this;
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
this.protocolEra = "legacy";
|
|
2584
|
+
this.protocolVersion = LATEST_LEGACY_PROTOCOL_VERSION;
|
|
2585
|
+
this.setTransportProtocolVersion(this.protocolVersion);
|
|
2316
2586
|
const result = await this.request({
|
|
2317
2587
|
request: {
|
|
2318
2588
|
method: "initialize",
|
|
2319
2589
|
params: {
|
|
2320
|
-
protocolVersion:
|
|
2590
|
+
protocolVersion: LATEST_LEGACY_PROTOCOL_VERSION,
|
|
2321
2591
|
capabilities: this.clientCapabilities,
|
|
2322
2592
|
clientInfo: this.clientInfo
|
|
2323
2593
|
}
|
|
@@ -2360,6 +2630,55 @@ var DefaultMCPClient = class {
|
|
|
2360
2630
|
}
|
|
2361
2631
|
}
|
|
2362
2632
|
}
|
|
2633
|
+
async tryProtocolDiscovery(signal) {
|
|
2634
|
+
this.protocolEra = "modern";
|
|
2635
|
+
this.protocolVersion = LATEST_PROTOCOL_VERSION;
|
|
2636
|
+
this.setTransportProtocolVersion(this.protocolVersion);
|
|
2637
|
+
try {
|
|
2638
|
+
const result = await this.request({
|
|
2639
|
+
request: { method: "server/discover" },
|
|
2640
|
+
resultSchema: DiscoverResultSchema,
|
|
2641
|
+
options: {
|
|
2642
|
+
signal,
|
|
2643
|
+
timeout: DEFAULT_PROTOCOL_DISCOVERY_TIMEOUT
|
|
2644
|
+
}
|
|
2645
|
+
});
|
|
2646
|
+
this.applyDiscoverResult(result);
|
|
2647
|
+
return true;
|
|
2648
|
+
} catch (error) {
|
|
2649
|
+
if (MCPClientError.isInstance(error) && error.code != null && MODERN_PROTOCOL_ERROR_CODES.includes(error.code)) {
|
|
2650
|
+
throw error;
|
|
2651
|
+
}
|
|
2652
|
+
return false;
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
applyDiscoverResult(result) {
|
|
2656
|
+
var _a3;
|
|
2657
|
+
if (!result.supportedVersions.includes(this.protocolVersion)) {
|
|
2658
|
+
throw new MCPClientError({
|
|
2659
|
+
message: `Server does not support the requested protocol version: ${this.protocolVersion}`
|
|
2660
|
+
});
|
|
2661
|
+
}
|
|
2662
|
+
const serverInfo = (_a3 = result._meta) == null ? void 0 : _a3["io.modelcontextprotocol/serverInfo"];
|
|
2663
|
+
if (serverInfo != null && typeof serverInfo === "object" && "name" in serverInfo && typeof serverInfo.name === "string" && "version" in serverInfo && typeof serverInfo.version === "string") {
|
|
2664
|
+
this._serverInfo = serverInfo;
|
|
2665
|
+
}
|
|
2666
|
+
this.serverCapabilities = result.capabilities;
|
|
2667
|
+
this._serverInstructions = result.instructions;
|
|
2668
|
+
this._initializeResult = {
|
|
2669
|
+
protocolVersion: this.protocolVersion,
|
|
2670
|
+
capabilities: result.capabilities,
|
|
2671
|
+
serverInfo: this._serverInfo,
|
|
2672
|
+
instructions: result.instructions
|
|
2673
|
+
};
|
|
2674
|
+
}
|
|
2675
|
+
setTransportProtocolVersion(version) {
|
|
2676
|
+
if (this.transport.setProtocolVersion) {
|
|
2677
|
+
this.transport.setProtocolVersion(version);
|
|
2678
|
+
} else {
|
|
2679
|
+
this.transport.protocolVersion = version;
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2363
2682
|
applyInitializeResult(result) {
|
|
2364
2683
|
if (!SUPPORTED_PROTOCOL_VERSIONS.includes(result.protocolVersion)) {
|
|
2365
2684
|
throw new MCPClientError({
|
|
@@ -2367,13 +2686,11 @@ var DefaultMCPClient = class {
|
|
|
2367
2686
|
});
|
|
2368
2687
|
}
|
|
2369
2688
|
this.serverCapabilities = result.capabilities;
|
|
2689
|
+
this.protocolEra = "legacy";
|
|
2690
|
+
this.protocolVersion = result.protocolVersion;
|
|
2370
2691
|
this._serverInfo = result.serverInfo;
|
|
2371
2692
|
this._initializeResult = result;
|
|
2372
|
-
|
|
2373
|
-
this.transport.setProtocolVersion(result.protocolVersion);
|
|
2374
|
-
} else {
|
|
2375
|
-
this.transport.protocolVersion = result.protocolVersion;
|
|
2376
|
-
}
|
|
2693
|
+
this.setTransportProtocolVersion(result.protocolVersion);
|
|
2377
2694
|
this._serverInstructions = result.instructions;
|
|
2378
2695
|
}
|
|
2379
2696
|
async close() {
|
|
@@ -2382,15 +2699,13 @@ var DefaultMCPClient = class {
|
|
|
2382
2699
|
await ((_a3 = this.transport) == null ? void 0 : _a3.close());
|
|
2383
2700
|
this.onClose();
|
|
2384
2701
|
}
|
|
2385
|
-
send(message,
|
|
2386
|
-
return this.transport.send(
|
|
2387
|
-
message,
|
|
2388
|
-
signal == null ? void 0 : { signal }
|
|
2389
|
-
);
|
|
2702
|
+
send(message, options) {
|
|
2703
|
+
return options == null ? this.transport.send(message) : this.transport.send(message, options);
|
|
2390
2704
|
}
|
|
2391
2705
|
assertCapability(method) {
|
|
2392
2706
|
switch (method) {
|
|
2393
2707
|
case "initialize":
|
|
2708
|
+
case "server/discover":
|
|
2394
2709
|
break;
|
|
2395
2710
|
case "completion/complete":
|
|
2396
2711
|
if (!this.serverCapabilities.completions) {
|
|
@@ -2436,6 +2751,7 @@ var DefaultMCPClient = class {
|
|
|
2436
2751
|
options
|
|
2437
2752
|
}) {
|
|
2438
2753
|
return new Promise((resolve, reject) => {
|
|
2754
|
+
var _a3;
|
|
2439
2755
|
if (this.isClosed) {
|
|
2440
2756
|
return reject(
|
|
2441
2757
|
new MCPClientError({
|
|
@@ -2451,11 +2767,24 @@ var DefaultMCPClient = class {
|
|
|
2451
2767
|
const transportSignal = signal == null ? timeoutController == null ? void 0 : timeoutController.signal : timeoutController == null ? signal : AbortSignal.any([signal, timeoutController.signal]);
|
|
2452
2768
|
let timeoutId;
|
|
2453
2769
|
const messageId = this.requestMessageId++;
|
|
2454
|
-
const
|
|
2770
|
+
const preparedRequest = this.protocolEra === "modern" ? {
|
|
2455
2771
|
...request,
|
|
2772
|
+
params: {
|
|
2773
|
+
...request.params,
|
|
2774
|
+
_meta: {
|
|
2775
|
+
...(_a3 = request.params) == null ? void 0 : _a3._meta,
|
|
2776
|
+
"io.modelcontextprotocol/protocolVersion": this.protocolVersion,
|
|
2777
|
+
"io.modelcontextprotocol/clientCapabilities": this.clientCapabilities,
|
|
2778
|
+
"io.modelcontextprotocol/clientInfo": this.clientInfo
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
} : request;
|
|
2782
|
+
const jsonrpcRequest = {
|
|
2783
|
+
...preparedRequest,
|
|
2456
2784
|
jsonrpc: "2.0",
|
|
2457
2785
|
id: messageId
|
|
2458
2786
|
};
|
|
2787
|
+
const headers = this.getToolRequestHeaders(preparedRequest);
|
|
2459
2788
|
const rejectWithAbortError = () => {
|
|
2460
2789
|
reject(
|
|
2461
2790
|
new MCPClientError({
|
|
@@ -2495,11 +2824,21 @@ var DefaultMCPClient = class {
|
|
|
2495
2824
|
return rejectAndCleanup(response);
|
|
2496
2825
|
}
|
|
2497
2826
|
try {
|
|
2827
|
+
if (this.protocolEra === "modern" && response.result.resultType == null) {
|
|
2828
|
+
throw new MCPClientError({
|
|
2829
|
+
message: "Modern MCP result is missing resultType"
|
|
2830
|
+
});
|
|
2831
|
+
}
|
|
2832
|
+
if (response.result.resultType === "input_required") {
|
|
2833
|
+
throw new MCPClientError({
|
|
2834
|
+
message: "Server requested additional input, but multi round-trip requests are not supported yet"
|
|
2835
|
+
});
|
|
2836
|
+
}
|
|
2498
2837
|
const result = resultSchema.parse(response.result);
|
|
2499
2838
|
cleanup();
|
|
2500
2839
|
resolve(result);
|
|
2501
2840
|
} catch (error) {
|
|
2502
|
-
const parseError = new MCPClientError({
|
|
2841
|
+
const parseError = MCPClientError.isInstance(error) ? error : new MCPClientError({
|
|
2503
2842
|
message: "Failed to parse server response",
|
|
2504
2843
|
cause: error
|
|
2505
2844
|
});
|
|
@@ -2510,7 +2849,11 @@ var DefaultMCPClient = class {
|
|
|
2510
2849
|
if (timeout != null) {
|
|
2511
2850
|
timeoutId = setTimeout(onTimeout, timeout);
|
|
2512
2851
|
}
|
|
2513
|
-
const
|
|
2852
|
+
const sendOptions = {
|
|
2853
|
+
...transportSignal == null ? {} : { signal: transportSignal },
|
|
2854
|
+
...headers == null ? {} : { headers }
|
|
2855
|
+
};
|
|
2856
|
+
const sendPromise = Object.keys(sendOptions).length === 0 ? this.send(jsonrpcRequest) : this.send(jsonrpcRequest, sendOptions);
|
|
2514
2857
|
sendPromise.catch((error) => {
|
|
2515
2858
|
rejectAndCleanup(error);
|
|
2516
2859
|
});
|
|
@@ -2520,11 +2863,59 @@ var DefaultMCPClient = class {
|
|
|
2520
2863
|
params,
|
|
2521
2864
|
options
|
|
2522
2865
|
} = {}) {
|
|
2523
|
-
|
|
2866
|
+
const result = await this.request({
|
|
2524
2867
|
request: { method: "tools/list", params },
|
|
2525
2868
|
resultSchema: ListToolsResultSchema,
|
|
2526
2869
|
options
|
|
2527
2870
|
});
|
|
2871
|
+
return this.prepareToolDefinitions(result, (params == null ? void 0 : params.cursor) == null);
|
|
2872
|
+
}
|
|
2873
|
+
prepareToolDefinitions(definitions, resetHeaderBindings = false) {
|
|
2874
|
+
if (this.protocolEra !== "modern" || !this.transport.supportsMcpToolParameterHeaders) {
|
|
2875
|
+
return definitions;
|
|
2876
|
+
}
|
|
2877
|
+
if (resetHeaderBindings) {
|
|
2878
|
+
this.toolHeaderBindings.clear();
|
|
2879
|
+
}
|
|
2880
|
+
const tools = definitions.tools.filter((toolDefinition) => {
|
|
2881
|
+
const result = getMCPToolHeaderBindings(toolDefinition.inputSchema);
|
|
2882
|
+
if (!result.success) {
|
|
2883
|
+
this.onError(
|
|
2884
|
+
new MCPClientError({
|
|
2885
|
+
message: `Ignoring MCP tool "${toolDefinition.name}": ${result.error}`
|
|
2886
|
+
})
|
|
2887
|
+
);
|
|
2888
|
+
return false;
|
|
2889
|
+
}
|
|
2890
|
+
this.toolHeaderBindings.set(toolDefinition.name, result.bindings);
|
|
2891
|
+
return true;
|
|
2892
|
+
});
|
|
2893
|
+
return { ...definitions, tools };
|
|
2894
|
+
}
|
|
2895
|
+
getToolRequestHeaders(request) {
|
|
2896
|
+
var _a3;
|
|
2897
|
+
if (this.protocolEra !== "modern" || request.method !== "tools/call" || typeof ((_a3 = request.params) == null ? void 0 : _a3.name) !== "string") {
|
|
2898
|
+
return void 0;
|
|
2899
|
+
}
|
|
2900
|
+
const bindings = this.toolHeaderBindings.get(request.params.name);
|
|
2901
|
+
if (bindings == null || bindings.length === 0) {
|
|
2902
|
+
return void 0;
|
|
2903
|
+
}
|
|
2904
|
+
const args = request.params.arguments;
|
|
2905
|
+
if (args == null || typeof args !== "object" || Array.isArray(args)) {
|
|
2906
|
+
return void 0;
|
|
2907
|
+
}
|
|
2908
|
+
try {
|
|
2909
|
+
return createMCPToolHeaders({
|
|
2910
|
+
bindings,
|
|
2911
|
+
args
|
|
2912
|
+
});
|
|
2913
|
+
} catch (error) {
|
|
2914
|
+
throw new MCPClientError({
|
|
2915
|
+
message: `Failed to create MCP headers for tool "${request.params.name}"`,
|
|
2916
|
+
cause: error
|
|
2917
|
+
});
|
|
2918
|
+
}
|
|
2528
2919
|
}
|
|
2529
2920
|
async callToolWithRetry({
|
|
2530
2921
|
options,
|
|
@@ -2650,7 +3041,10 @@ var DefaultMCPClient = class {
|
|
|
2650
3041
|
jsonrpc: "2.0"
|
|
2651
3042
|
};
|
|
2652
3043
|
await waitForAbort(
|
|
2653
|
-
this.send(
|
|
3044
|
+
this.send(
|
|
3045
|
+
jsonrpcNotification,
|
|
3046
|
+
(options == null ? void 0 : options.signal) == null ? void 0 : { signal: options.signal }
|
|
3047
|
+
),
|
|
2654
3048
|
options == null ? void 0 : options.signal
|
|
2655
3049
|
);
|
|
2656
3050
|
}
|
|
@@ -2672,6 +3066,7 @@ var DefaultMCPClient = class {
|
|
|
2672
3066
|
*/
|
|
2673
3067
|
toolsFromDefinitions(definitions, { schemas = "automatic" } = {}) {
|
|
2674
3068
|
var _a3, _b3;
|
|
3069
|
+
definitions = this.prepareToolDefinitions(definitions);
|
|
2675
3070
|
const tools = {};
|
|
2676
3071
|
for (const {
|
|
2677
3072
|
name: name3,
|
|
@@ -2905,6 +3300,16 @@ var DefaultMCPClient = class {
|
|
|
2905
3300
|
}
|
|
2906
3301
|
}
|
|
2907
3302
|
onResponse(response) {
|
|
3303
|
+
if (response.id == null) {
|
|
3304
|
+
this.onError(
|
|
3305
|
+
new MCPClientError({
|
|
3306
|
+
message: `Protocol error: Received a response without a message ID: ${JSON.stringify(
|
|
3307
|
+
response
|
|
3308
|
+
)}`
|
|
3309
|
+
})
|
|
3310
|
+
);
|
|
3311
|
+
return;
|
|
3312
|
+
}
|
|
2908
3313
|
const messageId = Number(response.id);
|
|
2909
3314
|
const handler = this.responseHandlers.get(messageId);
|
|
2910
3315
|
if (handler === void 0) {
|
|
@@ -2927,7 +3332,7 @@ var DefaultMCPClient = class {
|
|
|
2927
3332
|
};
|
|
2928
3333
|
|
|
2929
3334
|
// src/tool/mcp-app-fingerprint.ts
|
|
2930
|
-
import { convertUint8ArrayToBase64 } from "@ai-sdk/provider-utils";
|
|
3335
|
+
import { convertUint8ArrayToBase64 as convertUint8ArrayToBase642 } from "@ai-sdk/provider-utils";
|
|
2931
3336
|
var encoder = new TextEncoder();
|
|
2932
3337
|
function canonicalJSON(value) {
|
|
2933
3338
|
if (value == null || typeof value !== "object") {
|
|
@@ -2941,7 +3346,7 @@ function canonicalJSON(value) {
|
|
|
2941
3346
|
return `{${entries.join(",")}}`;
|
|
2942
3347
|
}
|
|
2943
3348
|
function toBase64url(bytes) {
|
|
2944
|
-
return
|
|
3349
|
+
return convertUint8ArrayToBase642(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
2945
3350
|
}
|
|
2946
3351
|
async function fingerprintMCPAppResource(resource) {
|
|
2947
3352
|
var _a3, _b3, _c, _d;
|