@ai-sdk/mcp 1.0.46 → 1.0.48
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 +15 -0
- package/dist/index.d.mts +20 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +197 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +197 -24
- package/dist/index.mjs.map +1 -1
- package/dist/mcp-stdio/index.d.mts +4 -0
- package/dist/mcp-stdio/index.d.ts +4 -0
- package/package.json +4 -4
- package/src/index.ts +4 -1
- package/src/tool/mcp-client.ts +5 -1
- package/src/tool/mcp-http-transport.ts +4 -0
- package/src/tool/mcp-sse-transport.ts +4 -0
- package/src/tool/mcp-transport.ts +5 -0
- package/src/tool/oauth-types.ts +18 -14
- package/src/tool/oauth.ts +265 -19
|
@@ -66,6 +66,10 @@ interface MCPTransport {
|
|
|
66
66
|
* The protocol version negotiated during initialization.
|
|
67
67
|
*/
|
|
68
68
|
protocolVersion?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Set the protocol version negotiated during initialization.
|
|
71
|
+
*/
|
|
72
|
+
setProtocolVersion?(version: string): void;
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
interface StdioConfig {
|
|
@@ -66,6 +66,10 @@ interface MCPTransport {
|
|
|
66
66
|
* The protocol version negotiated during initialization.
|
|
67
67
|
*/
|
|
68
68
|
protocolVersion?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Set the protocol version negotiated during initialization.
|
|
71
|
+
*/
|
|
72
|
+
setProtocolVersion?(version: string): void;
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
interface StdioConfig {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.48",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"pkce-challenge": "^5.0.0",
|
|
36
36
|
"@ai-sdk/provider": "3.0.10",
|
|
37
|
-
"@ai-sdk/provider-utils": "4.0.
|
|
37
|
+
"@ai-sdk/provider-utils": "4.0.28"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "20.17.24",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"typescript": "5.8.3",
|
|
43
43
|
"vitest": "^4.1.0",
|
|
44
44
|
"zod": "3.25.76",
|
|
45
|
-
"@ai-
|
|
46
|
-
"@
|
|
45
|
+
"@vercel/ai-tsconfig": "0.0.0",
|
|
46
|
+
"@ai-sdk/test-server": "1.0.5"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"zod": "^3.25.76 || ^4.1.8"
|
package/src/index.ts
CHANGED
|
@@ -21,7 +21,10 @@ export type {
|
|
|
21
21
|
ClientCapabilities as MCPClientCapabilities,
|
|
22
22
|
} from './tool/types';
|
|
23
23
|
export { auth, UnauthorizedError } from './tool/oauth';
|
|
24
|
-
export type {
|
|
24
|
+
export type {
|
|
25
|
+
OAuthAuthorizationServerInformation,
|
|
26
|
+
OAuthClientProvider,
|
|
27
|
+
} from './tool/oauth';
|
|
25
28
|
export type {
|
|
26
29
|
OAuthClientInformation,
|
|
27
30
|
OAuthClientMetadata,
|
package/src/tool/mcp-client.ts
CHANGED
|
@@ -312,8 +312,12 @@ class DefaultMCPClient implements MCPClient {
|
|
|
312
312
|
|
|
313
313
|
this.serverCapabilities = result.capabilities;
|
|
314
314
|
this._serverInfo = result.serverInfo;
|
|
315
|
+
if (this.transport.setProtocolVersion) {
|
|
316
|
+
this.transport.setProtocolVersion(result.protocolVersion);
|
|
317
|
+
} else {
|
|
318
|
+
this.transport.protocolVersion = result.protocolVersion;
|
|
319
|
+
}
|
|
315
320
|
this._serverInstructions = result.instructions;
|
|
316
|
-
this.transport.protocolVersion = result.protocolVersion;
|
|
317
321
|
|
|
318
322
|
// Complete initialization handshake:
|
|
319
323
|
await this.notification({
|
|
@@ -75,6 +75,10 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
75
75
|
this.fetchFn = fetchFn ?? globalThis.fetch;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
setProtocolVersion(version: string): void {
|
|
79
|
+
this.protocolVersion = version;
|
|
80
|
+
}
|
|
81
|
+
|
|
78
82
|
private async commonHeaders(
|
|
79
83
|
base: Record<string, string>,
|
|
80
84
|
): Promise<Record<string, string>> {
|
|
@@ -55,6 +55,10 @@ export class SseMCPTransport implements MCPTransport {
|
|
|
55
55
|
this.fetchFn = fetchFn ?? globalThis.fetch;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
setProtocolVersion(version: string): void {
|
|
59
|
+
this.protocolVersion = version;
|
|
60
|
+
}
|
|
61
|
+
|
|
58
62
|
private async commonHeaders(
|
|
59
63
|
base: Record<string, string>,
|
|
60
64
|
): Promise<Record<string, string>> {
|
|
@@ -45,6 +45,11 @@ export interface MCPTransport {
|
|
|
45
45
|
* The protocol version negotiated during initialization.
|
|
46
46
|
*/
|
|
47
47
|
protocolVersion?: string;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Set the protocol version negotiated during initialization.
|
|
51
|
+
*/
|
|
52
|
+
setProtocolVersion?(version: string): void;
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
export type MCPTransportConfig = {
|
package/src/tool/oauth-types.ts
CHANGED
|
@@ -1,18 +1,4 @@
|
|
|
1
1
|
import { z } from 'zod/v4';
|
|
2
|
-
/**
|
|
3
|
-
* OAuth 2.1 token response
|
|
4
|
-
*/
|
|
5
|
-
export const OAuthTokensSchema = z
|
|
6
|
-
.object({
|
|
7
|
-
access_token: z.string(),
|
|
8
|
-
id_token: z.string().optional(), // Optional for OAuth 2.1, but necessary in OpenID Connect
|
|
9
|
-
token_type: z.string(),
|
|
10
|
-
expires_in: z.number().optional(),
|
|
11
|
-
scope: z.string().optional(),
|
|
12
|
-
refresh_token: z.string().optional(),
|
|
13
|
-
})
|
|
14
|
-
.strip();
|
|
15
|
-
|
|
16
2
|
/**
|
|
17
3
|
* Reusable URL validation that disallows javascript: scheme
|
|
18
4
|
*/
|
|
@@ -42,6 +28,22 @@ export const SafeUrlSchema = z
|
|
|
42
28
|
{ message: 'URL cannot use javascript:, data:, or vbscript: scheme' },
|
|
43
29
|
);
|
|
44
30
|
|
|
31
|
+
/**
|
|
32
|
+
* OAuth 2.1 token response
|
|
33
|
+
*/
|
|
34
|
+
export const OAuthTokensSchema = z
|
|
35
|
+
.object({
|
|
36
|
+
access_token: z.string(),
|
|
37
|
+
id_token: z.string().optional(), // Optional for OAuth 2.1, but necessary in OpenID Connect
|
|
38
|
+
token_type: z.string(),
|
|
39
|
+
expires_in: z.number().optional(),
|
|
40
|
+
scope: z.string().optional(),
|
|
41
|
+
refresh_token: z.string().optional(),
|
|
42
|
+
authorization_server: SafeUrlSchema.optional(),
|
|
43
|
+
token_endpoint: SafeUrlSchema.optional(),
|
|
44
|
+
})
|
|
45
|
+
.strip();
|
|
46
|
+
|
|
45
47
|
export const OAuthProtectedResourceMetadataSchema = z
|
|
46
48
|
.object({
|
|
47
49
|
resource: z.string().url(),
|
|
@@ -118,6 +120,8 @@ export const OAuthClientInformationSchema = z
|
|
|
118
120
|
client_secret: z.string().optional(),
|
|
119
121
|
client_id_issued_at: z.number().optional(),
|
|
120
122
|
client_secret_expires_at: z.number().optional(),
|
|
123
|
+
authorization_server: SafeUrlSchema.optional(),
|
|
124
|
+
token_endpoint: SafeUrlSchema.optional(),
|
|
121
125
|
})
|
|
122
126
|
.strip();
|
|
123
127
|
|
package/src/tool/oauth.ts
CHANGED
|
@@ -31,6 +31,11 @@ import { parseJSON, type FetchFunction } from '@ai-sdk/provider-utils';
|
|
|
31
31
|
|
|
32
32
|
export type AuthResult = 'AUTHORIZED' | 'REDIRECT';
|
|
33
33
|
|
|
34
|
+
export interface OAuthAuthorizationServerInformation {
|
|
35
|
+
authorizationServerUrl: string;
|
|
36
|
+
tokenEndpoint: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
34
39
|
export interface OAuthClientProvider {
|
|
35
40
|
/**
|
|
36
41
|
* Returns current access token if present; undefined otherwise.
|
|
@@ -83,6 +88,21 @@ export interface OAuthClientProvider {
|
|
|
83
88
|
saveClientInformation?(
|
|
84
89
|
clientInformation: OAuthClientInformation,
|
|
85
90
|
): void | Promise<void>;
|
|
91
|
+
authorizationServerInformation?():
|
|
92
|
+
| OAuthAuthorizationServerInformation
|
|
93
|
+
| undefined
|
|
94
|
+
| Promise<OAuthAuthorizationServerInformation | undefined>;
|
|
95
|
+
saveAuthorizationServerInformation?(
|
|
96
|
+
authorizationServerInformation: OAuthAuthorizationServerInformation,
|
|
97
|
+
): void | Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Validates an authorization server URL discovered from MCP protected resource
|
|
100
|
+
* metadata before the client fetches its OAuth metadata.
|
|
101
|
+
*/
|
|
102
|
+
validateAuthorizationServerURL?(
|
|
103
|
+
serverUrl: string | URL,
|
|
104
|
+
authorizationServerUrl: string | URL,
|
|
105
|
+
): void | Promise<void>;
|
|
86
106
|
state?(): string | Promise<string>;
|
|
87
107
|
saveState?(state: string): void | Promise<void>;
|
|
88
108
|
storedState?(): string | undefined | Promise<string | undefined>;
|
|
@@ -99,6 +119,158 @@ export class UnauthorizedError extends Error {
|
|
|
99
119
|
}
|
|
100
120
|
}
|
|
101
121
|
|
|
122
|
+
function normalizeUrl(url: string | URL): string {
|
|
123
|
+
return new URL(url).href;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function createAuthorizationServerInformation(
|
|
127
|
+
authorizationServerUrl: string | URL,
|
|
128
|
+
metadata?: AuthorizationServerMetadata,
|
|
129
|
+
): OAuthAuthorizationServerInformation {
|
|
130
|
+
return {
|
|
131
|
+
authorizationServerUrl: normalizeUrl(authorizationServerUrl),
|
|
132
|
+
tokenEndpoint: normalizeUrl(
|
|
133
|
+
metadata?.token_endpoint
|
|
134
|
+
? new URL(metadata.token_endpoint)
|
|
135
|
+
: new URL('/token', authorizationServerUrl),
|
|
136
|
+
),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function addAuthorizationServerInformationToTokens(
|
|
141
|
+
tokens: OAuthTokens,
|
|
142
|
+
authorizationServerInformation: OAuthAuthorizationServerInformation,
|
|
143
|
+
): OAuthTokens {
|
|
144
|
+
return {
|
|
145
|
+
...tokens,
|
|
146
|
+
authorization_server: authorizationServerInformation.authorizationServerUrl,
|
|
147
|
+
token_endpoint: authorizationServerInformation.tokenEndpoint,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function addAuthorizationServerInformationToClientInformation<
|
|
152
|
+
CLIENT_INFORMATION extends OAuthClientInformation,
|
|
153
|
+
>(
|
|
154
|
+
clientInformation: CLIENT_INFORMATION,
|
|
155
|
+
authorizationServerInformation: OAuthAuthorizationServerInformation,
|
|
156
|
+
): CLIENT_INFORMATION {
|
|
157
|
+
return {
|
|
158
|
+
...clientInformation,
|
|
159
|
+
authorization_server: authorizationServerInformation.authorizationServerUrl,
|
|
160
|
+
token_endpoint: authorizationServerInformation.tokenEndpoint,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function getAuthorizationServerInformationFromCredentials(credentials?: {
|
|
165
|
+
authorization_server?: string;
|
|
166
|
+
token_endpoint?: string;
|
|
167
|
+
}): OAuthAuthorizationServerInformation | undefined {
|
|
168
|
+
if (!credentials?.authorization_server || !credentials.token_endpoint) {
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
authorizationServerUrl: normalizeUrl(credentials.authorization_server),
|
|
174
|
+
tokenEndpoint: normalizeUrl(credentials.token_endpoint),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function getStoredAuthorizationServerInformation({
|
|
179
|
+
provider,
|
|
180
|
+
clientInformation,
|
|
181
|
+
tokens,
|
|
182
|
+
}: {
|
|
183
|
+
provider: OAuthClientProvider;
|
|
184
|
+
clientInformation: OAuthClientInformation;
|
|
185
|
+
tokens?: OAuthTokens;
|
|
186
|
+
}): Promise<OAuthAuthorizationServerInformation | undefined> {
|
|
187
|
+
const tokenAuthorizationServerInformation =
|
|
188
|
+
getAuthorizationServerInformationFromCredentials(tokens);
|
|
189
|
+
if (tokenAuthorizationServerInformation) {
|
|
190
|
+
return tokenAuthorizationServerInformation;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const providerAuthorizationServerInformation =
|
|
194
|
+
await provider.authorizationServerInformation?.();
|
|
195
|
+
if (providerAuthorizationServerInformation) {
|
|
196
|
+
return {
|
|
197
|
+
authorizationServerUrl: normalizeUrl(
|
|
198
|
+
providerAuthorizationServerInformation.authorizationServerUrl,
|
|
199
|
+
),
|
|
200
|
+
tokenEndpoint: normalizeUrl(
|
|
201
|
+
providerAuthorizationServerInformation.tokenEndpoint,
|
|
202
|
+
),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return getAuthorizationServerInformationFromCredentials(clientInformation);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function saveAuthorizationServerInformation({
|
|
210
|
+
provider,
|
|
211
|
+
clientInformation,
|
|
212
|
+
authorizationServerInformation,
|
|
213
|
+
}: {
|
|
214
|
+
provider: OAuthClientProvider;
|
|
215
|
+
clientInformation: OAuthClientInformation;
|
|
216
|
+
authorizationServerInformation: OAuthAuthorizationServerInformation;
|
|
217
|
+
}): Promise<boolean> {
|
|
218
|
+
if (provider.saveAuthorizationServerInformation) {
|
|
219
|
+
await provider.saveAuthorizationServerInformation(
|
|
220
|
+
authorizationServerInformation,
|
|
221
|
+
);
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (provider.saveClientInformation) {
|
|
226
|
+
await provider.saveClientInformation(
|
|
227
|
+
addAuthorizationServerInformationToClientInformation(
|
|
228
|
+
clientInformation,
|
|
229
|
+
authorizationServerInformation,
|
|
230
|
+
),
|
|
231
|
+
);
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function assertResourceMetadataUrlSameOrigin(
|
|
239
|
+
serverUrl: string | URL,
|
|
240
|
+
resourceMetadataUrl?: URL,
|
|
241
|
+
): void {
|
|
242
|
+
if (!resourceMetadataUrl) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const expectedOrigin = new URL(serverUrl).origin;
|
|
247
|
+
if (resourceMetadataUrl.origin !== expectedOrigin) {
|
|
248
|
+
throw new MCPClientOAuthError({
|
|
249
|
+
message: `OAuth protected resource metadata URL ${resourceMetadataUrl.href} must have the same origin as the MCP server URL ${expectedOrigin}`,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function assertAuthorizationServerInformationMatches({
|
|
255
|
+
storedAuthorizationServerInformation,
|
|
256
|
+
currentAuthorizationServerInformation,
|
|
257
|
+
}: {
|
|
258
|
+
storedAuthorizationServerInformation: OAuthAuthorizationServerInformation;
|
|
259
|
+
currentAuthorizationServerInformation: OAuthAuthorizationServerInformation;
|
|
260
|
+
}): void {
|
|
261
|
+
if (
|
|
262
|
+
storedAuthorizationServerInformation.authorizationServerUrl !==
|
|
263
|
+
currentAuthorizationServerInformation.authorizationServerUrl ||
|
|
264
|
+
storedAuthorizationServerInformation.tokenEndpoint !==
|
|
265
|
+
currentAuthorizationServerInformation.tokenEndpoint
|
|
266
|
+
) {
|
|
267
|
+
throw new MCPClientOAuthError({
|
|
268
|
+
message:
|
|
269
|
+
'OAuth authorization server metadata does not match the metadata that issued the stored credentials',
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
102
274
|
/**
|
|
103
275
|
* Extracts the OAuth 2.0 Protected Resource Metadata URL from a WWW-Authenticate header (RFC9728).
|
|
104
276
|
* Looks for a resource="..." parameter.
|
|
@@ -920,6 +1092,11 @@ async function authInternal(
|
|
|
920
1092
|
): Promise<AuthResult> {
|
|
921
1093
|
let resourceMetadata: OAuthProtectedResourceMetadata | undefined;
|
|
922
1094
|
let authorizationServerUrl: string | URL | undefined;
|
|
1095
|
+
|
|
1096
|
+
/** Reject Protected Resource Metadata URLs outside the configured MCP server origin. */
|
|
1097
|
+
assertResourceMetadataUrlSameOrigin(serverUrl, resourceMetadataUrl);
|
|
1098
|
+
|
|
1099
|
+
/** Discover PRM and select its advertised authorization server. */
|
|
923
1100
|
try {
|
|
924
1101
|
resourceMetadata = await discoverOAuthProtectedResourceMetadata(
|
|
925
1102
|
serverUrl,
|
|
@@ -934,27 +1111,35 @@ async function authInternal(
|
|
|
934
1111
|
}
|
|
935
1112
|
} catch {}
|
|
936
1113
|
|
|
937
|
-
/**
|
|
938
|
-
* If we don't get a valid authorization server metadata from protected resource metadata,
|
|
939
|
-
* fallback to the legacy MCP spec's implementation (version 2025-03-26): MCP server acts as the Authorization server.
|
|
940
|
-
*/
|
|
1114
|
+
/** Fall back to legacy MCP behavior where the MCP server is the Authorization Server */
|
|
941
1115
|
if (!authorizationServerUrl) {
|
|
942
1116
|
authorizationServerUrl = serverUrl;
|
|
943
1117
|
}
|
|
944
1118
|
|
|
1119
|
+
/** Validate and select the resource value sent to the AS */
|
|
945
1120
|
const resource: URL | undefined = await selectResourceURL(
|
|
946
1121
|
serverUrl,
|
|
947
1122
|
provider,
|
|
948
1123
|
resourceMetadata,
|
|
949
1124
|
);
|
|
950
1125
|
|
|
1126
|
+
/** Let applications constrain discovered AS URLs before metadata fetches. */
|
|
1127
|
+
await provider.validateAuthorizationServerURL?.(
|
|
1128
|
+
serverUrl,
|
|
1129
|
+
authorizationServerUrl,
|
|
1130
|
+
);
|
|
1131
|
+
|
|
1132
|
+
/** Discover AS metadata and derive the credential pin for this flow */
|
|
951
1133
|
const metadata = await discoverAuthorizationServerMetadata(
|
|
952
1134
|
authorizationServerUrl,
|
|
953
1135
|
{
|
|
954
1136
|
fetchFn,
|
|
955
1137
|
},
|
|
956
1138
|
);
|
|
1139
|
+
const currentAuthorizationServerInformation =
|
|
1140
|
+
createAuthorizationServerInformation(authorizationServerUrl, metadata);
|
|
957
1141
|
|
|
1142
|
+
/** Load or register client credentials with the AS pin attached. */
|
|
958
1143
|
let clientInformation = await Promise.resolve(provider.clientInformation());
|
|
959
1144
|
if (!clientInformation) {
|
|
960
1145
|
if (authorizationCode !== undefined) {
|
|
@@ -975,11 +1160,14 @@ async function authInternal(
|
|
|
975
1160
|
fetchFn,
|
|
976
1161
|
});
|
|
977
1162
|
|
|
978
|
-
|
|
979
|
-
|
|
1163
|
+
clientInformation = addAuthorizationServerInformationToClientInformation(
|
|
1164
|
+
fullInformation,
|
|
1165
|
+
currentAuthorizationServerInformation,
|
|
1166
|
+
);
|
|
1167
|
+
await provider.saveClientInformation(clientInformation);
|
|
980
1168
|
}
|
|
981
1169
|
|
|
982
|
-
|
|
1170
|
+
/** On callback, validate state and AS pin before code exchange */
|
|
983
1171
|
if (authorizationCode !== undefined) {
|
|
984
1172
|
if (provider.storedState) {
|
|
985
1173
|
const expectedState = await provider.storedState();
|
|
@@ -990,6 +1178,22 @@ async function authInternal(
|
|
|
990
1178
|
}
|
|
991
1179
|
}
|
|
992
1180
|
|
|
1181
|
+
const storedAuthorizationServerInformation =
|
|
1182
|
+
await getStoredAuthorizationServerInformation({
|
|
1183
|
+
provider,
|
|
1184
|
+
clientInformation,
|
|
1185
|
+
});
|
|
1186
|
+
if (!storedAuthorizationServerInformation) {
|
|
1187
|
+
throw new MCPClientOAuthError({
|
|
1188
|
+
message:
|
|
1189
|
+
'Stored OAuth authorization server metadata is required when exchanging an authorization code',
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
assertAuthorizationServerInformationMatches({
|
|
1193
|
+
storedAuthorizationServerInformation,
|
|
1194
|
+
currentAuthorizationServerInformation,
|
|
1195
|
+
});
|
|
1196
|
+
|
|
993
1197
|
const codeVerifier = await provider.codeVerifier();
|
|
994
1198
|
const tokens = await exchangeAuthorization(authorizationServerUrl, {
|
|
995
1199
|
metadata,
|
|
@@ -1002,27 +1206,55 @@ async function authInternal(
|
|
|
1002
1206
|
fetchFn: fetchFn,
|
|
1003
1207
|
});
|
|
1004
1208
|
|
|
1005
|
-
await provider.saveTokens(
|
|
1209
|
+
await provider.saveTokens(
|
|
1210
|
+
addAuthorizationServerInformationToTokens(
|
|
1211
|
+
tokens,
|
|
1212
|
+
currentAuthorizationServerInformation,
|
|
1213
|
+
),
|
|
1214
|
+
);
|
|
1006
1215
|
return 'AUTHORIZED';
|
|
1007
1216
|
}
|
|
1008
1217
|
|
|
1009
1218
|
const tokens = await provider.tokens();
|
|
1010
1219
|
|
|
1011
|
-
|
|
1220
|
+
/** Refresh only when stored credentials match the current AS pin */
|
|
1012
1221
|
if (tokens?.refresh_token) {
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
metadata,
|
|
1222
|
+
const storedAuthorizationServerInformation =
|
|
1223
|
+
await getStoredAuthorizationServerInformation({
|
|
1224
|
+
provider,
|
|
1017
1225
|
clientInformation,
|
|
1018
|
-
|
|
1019
|
-
resource,
|
|
1020
|
-
addClientAuthentication: provider.addClientAuthentication,
|
|
1021
|
-
fetchFn,
|
|
1226
|
+
tokens,
|
|
1022
1227
|
});
|
|
1023
1228
|
|
|
1024
|
-
|
|
1025
|
-
|
|
1229
|
+
if (storedAuthorizationServerInformation) {
|
|
1230
|
+
assertAuthorizationServerInformationMatches({
|
|
1231
|
+
storedAuthorizationServerInformation,
|
|
1232
|
+
currentAuthorizationServerInformation,
|
|
1233
|
+
});
|
|
1234
|
+
} else {
|
|
1235
|
+
await provider.invalidateCredentials?.('tokens');
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
try {
|
|
1239
|
+
if (storedAuthorizationServerInformation) {
|
|
1240
|
+
// Attempt to refresh the token
|
|
1241
|
+
const newTokens = await refreshAuthorization(authorizationServerUrl, {
|
|
1242
|
+
metadata,
|
|
1243
|
+
clientInformation,
|
|
1244
|
+
refreshToken: tokens.refresh_token,
|
|
1245
|
+
resource,
|
|
1246
|
+
addClientAuthentication: provider.addClientAuthentication,
|
|
1247
|
+
fetchFn,
|
|
1248
|
+
});
|
|
1249
|
+
|
|
1250
|
+
await provider.saveTokens(
|
|
1251
|
+
addAuthorizationServerInformationToTokens(
|
|
1252
|
+
newTokens,
|
|
1253
|
+
currentAuthorizationServerInformation,
|
|
1254
|
+
),
|
|
1255
|
+
);
|
|
1256
|
+
return 'AUTHORIZED';
|
|
1257
|
+
}
|
|
1026
1258
|
} catch (error) {
|
|
1027
1259
|
if (
|
|
1028
1260
|
// If this is a ServerError, or an unknown type, log it out and try to continue. Otherwise, escalate so we can fix things and retry.
|
|
@@ -1037,6 +1269,7 @@ async function authInternal(
|
|
|
1037
1269
|
}
|
|
1038
1270
|
}
|
|
1039
1271
|
|
|
1272
|
+
/** Start authorization and persist the AS pin before redirecting */
|
|
1040
1273
|
const state = provider.state ? await provider.state() : undefined;
|
|
1041
1274
|
if (state && provider.saveState) {
|
|
1042
1275
|
await provider.saveState(state);
|
|
@@ -1055,6 +1288,19 @@ async function authInternal(
|
|
|
1055
1288
|
},
|
|
1056
1289
|
);
|
|
1057
1290
|
|
|
1291
|
+
const savedAuthorizationServerInformation =
|
|
1292
|
+
await saveAuthorizationServerInformation({
|
|
1293
|
+
provider,
|
|
1294
|
+
clientInformation,
|
|
1295
|
+
authorizationServerInformation: currentAuthorizationServerInformation,
|
|
1296
|
+
});
|
|
1297
|
+
if (!savedAuthorizationServerInformation) {
|
|
1298
|
+
throw new MCPClientOAuthError({
|
|
1299
|
+
message:
|
|
1300
|
+
'OAuth authorization server metadata must be saveable before starting authorization',
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1058
1304
|
await provider.saveCodeVerifier(codeVerifier);
|
|
1059
1305
|
await provider.redirectToAuthorization(authorizationUrl);
|
|
1060
1306
|
return 'REDIRECT';
|