@ai-sdk/mcp 2.0.32 → 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.
@@ -10,7 +10,7 @@ import {
10
10
  validateJSONRPCMessage,
11
11
  type JSONRPCMessage,
12
12
  } from './json-rpc-message';
13
- import type { MCPTransport } from './mcp-transport';
13
+ import type { MCPTransport, MCPTransportSendOptions } from './mcp-transport';
14
14
  import { VERSION } from '../version';
15
15
  import {
16
16
  extractWWWAuthenticateParams,
@@ -19,7 +19,11 @@ import {
19
19
  type AuthResult,
20
20
  type OAuthClientProvider,
21
21
  } from './oauth';
22
- import { LATEST_PROTOCOL_VERSION } from './types';
22
+ import {
23
+ LATEST_LEGACY_PROTOCOL_VERSION,
24
+ LATEST_PROTOCOL_VERSION,
25
+ } from './types';
26
+ import { encodeMCPHeaderValue } from './mcp-http-headers';
23
27
 
24
28
  function isMessageEvent(event: string | undefined): boolean {
25
29
  return event === undefined || event === 'message';
@@ -33,6 +37,8 @@ function isMessageEvent(event: string | undefined): boolean {
33
37
  * for receiving messages.
34
38
  */
35
39
  export class HttpMCPTransport implements MCPTransport {
40
+ readonly supportsProtocolVersionDiscovery = true;
41
+ readonly supportsMcpToolParameterHeaders = true;
36
42
  private url: URL;
37
43
  private abortController?: AbortController;
38
44
  private headers?: Record<string, string>;
@@ -90,7 +96,8 @@ export class HttpMCPTransport implements MCPTransport {
90
96
  this.authProvider = authProvider;
91
97
  this.redirectMode = redirect;
92
98
  this.sessionId = initialSessionId;
93
- this.protocolVersion = initialProtocolVersion;
99
+ this.protocolVersion =
100
+ initialProtocolVersion ?? LATEST_LEGACY_PROTOCOL_VERSION;
94
101
  this.onSessionIdChange = onSessionIdChange;
95
102
  this.onSessionExpired = onSessionExpired;
96
103
  this.terminateSessionOnClose = terminateSessionOnClose;
@@ -99,6 +106,27 @@ export class HttpMCPTransport implements MCPTransport {
99
106
 
100
107
  setProtocolVersion(version: string): void {
101
108
  this.protocolVersion = version;
109
+
110
+ if (!this.abortController) {
111
+ return;
112
+ }
113
+
114
+ if (this.isModernProtocol()) {
115
+ this.inboundSseConnection?.close();
116
+ this.inboundSseConnection = undefined;
117
+ return;
118
+ }
119
+
120
+ if (!this.inboundSseConnection) {
121
+ this.startInboundSse();
122
+ }
123
+ }
124
+
125
+ private isModernProtocol(): boolean {
126
+ return (
127
+ (this.protocolVersion ?? LATEST_PROTOCOL_VERSION) ===
128
+ LATEST_PROTOCOL_VERSION
129
+ );
102
130
  }
103
131
 
104
132
  private async commonHeaders({
@@ -111,10 +139,11 @@ export class HttpMCPTransport implements MCPTransport {
111
139
  const headers: Record<string, string> = {
112
140
  ...this.headers,
113
141
  ...base,
114
- 'mcp-protocol-version': this.protocolVersion ?? LATEST_PROTOCOL_VERSION,
142
+ 'mcp-protocol-version':
143
+ this.protocolVersion ?? LATEST_LEGACY_PROTOCOL_VERSION,
115
144
  };
116
145
 
117
- if (includeSessionId && this.sessionId) {
146
+ if (!this.isModernProtocol() && includeSessionId && this.sessionId) {
118
147
  headers['mcp-session-id'] = this.sessionId;
119
148
  }
120
149
 
@@ -142,6 +171,10 @@ export class HttpMCPTransport implements MCPTransport {
142
171
  }
143
172
 
144
173
  private applySessionIdFromResponse(response: Response): void {
174
+ if (this.isModernProtocol()) {
175
+ return;
176
+ }
177
+
145
178
  const sessionId = response.headers.get('mcp-session-id');
146
179
  if (sessionId) {
147
180
  this.setSessionId(sessionId);
@@ -190,7 +223,12 @@ export class HttpMCPTransport implements MCPTransport {
190
223
  }
191
224
  this.abortController = new AbortController();
192
225
 
193
- this.startInboundSse();
226
+ if (
227
+ this.protocolVersion != null &&
228
+ this.protocolVersion !== LATEST_PROTOCOL_VERSION
229
+ ) {
230
+ this.startInboundSse();
231
+ }
194
232
  }
195
233
 
196
234
  async close(options?: { signal?: AbortSignal }): Promise<void> {
@@ -199,6 +237,7 @@ export class HttpMCPTransport implements MCPTransport {
199
237
 
200
238
  try {
201
239
  if (
240
+ !this.isModernProtocol() &&
202
241
  this.sessionId &&
203
242
  this.terminateSessionOnClose &&
204
243
  this.abortController
@@ -220,7 +259,7 @@ export class HttpMCPTransport implements MCPTransport {
220
259
 
221
260
  async send(
222
261
  message: JSONRPCMessage,
223
- options?: { signal?: AbortSignal },
262
+ options?: MCPTransportSendOptions,
224
263
  ): Promise<void> {
225
264
  options?.signal?.throwIfAborted();
226
265
 
@@ -243,6 +282,12 @@ export class HttpMCPTransport implements MCPTransport {
243
282
  base: {
244
283
  'Content-Type': 'application/json',
245
284
  Accept: 'application/json, text/event-stream',
285
+ ...(this.isModernProtocol() ? options?.headers : {}),
286
+ ...(this.isModernProtocol() &&
287
+ 'method' in message &&
288
+ 'id' in message
289
+ ? this.getStandardRequestHeaders(message)
290
+ : {}),
246
291
  },
247
292
  includeSessionId: !isInitializeRequest,
248
293
  });
@@ -283,7 +328,7 @@ export class HttpMCPTransport implements MCPTransport {
283
328
  if (response.status === 202) {
284
329
  // If inbound SSE was not available earlier (e.g. 405 before init), try again now
285
330
  // Do not await to avoid blocking send()
286
- if (!this.inboundSseConnection) {
331
+ if (!this.isModernProtocol() && !this.inboundSseConnection) {
287
332
  this.startInboundSse();
288
333
  }
289
334
  return;
@@ -291,15 +336,30 @@ export class HttpMCPTransport implements MCPTransport {
291
336
 
292
337
  if (!response.ok) {
293
338
  const text = await response.text().catch(() => null);
339
+
340
+ if ('id' in message && text != null) {
341
+ const jsonRpcMessage = await parseJSONRPCMessage(text).catch(
342
+ () => undefined,
343
+ );
344
+ if (jsonRpcMessage != null && 'error' in jsonRpcMessage) {
345
+ this.onmessage?.(
346
+ jsonRpcMessage.id == null
347
+ ? { ...jsonRpcMessage, id: message.id }
348
+ : jsonRpcMessage,
349
+ );
350
+ return;
351
+ }
352
+ }
353
+
294
354
  let errorMessage = `MCP HTTP Transport Error: POSTing to endpoint (HTTP ${response.status}): ${text}`;
295
355
 
296
356
  if (response.status === 404) {
297
- if (sessionIdForRequest) {
357
+ if (!this.isModernProtocol() && sessionIdForRequest) {
298
358
  this.expireSessionId(sessionIdForRequest);
299
359
 
300
360
  errorMessage +=
301
361
  '. The MCP session expired. Create a new client without `initialSessionId` to start a fresh session';
302
- } else {
362
+ } else if (!this.isModernProtocol()) {
303
363
  errorMessage +=
304
364
  '. This server does not support HTTP transport. Try using `sse` transport instead';
305
365
  }
@@ -413,6 +473,27 @@ export class HttpMCPTransport implements MCPTransport {
413
473
  await attempt();
414
474
  }
415
475
 
476
+ private getStandardRequestHeaders(
477
+ message: Extract<JSONRPCMessage, { method: string; id: unknown }>,
478
+ ): Record<string, string> {
479
+ const headers: Record<string, string> = {
480
+ 'Mcp-Method': message.method,
481
+ };
482
+ const params = message.params;
483
+ const name =
484
+ message.method === 'resources/read'
485
+ ? params?.uri
486
+ : message.method === 'tools/call' || message.method === 'prompts/get'
487
+ ? params?.name
488
+ : undefined;
489
+
490
+ if (typeof name === 'string') {
491
+ headers['Mcp-Name'] = encodeMCPHeaderValue(name);
492
+ }
493
+
494
+ return headers;
495
+ }
496
+
416
497
  private getNextReconnectionDelay(attempt: number): number {
417
498
  const {
418
499
  initialReconnectionDelay,
@@ -448,6 +529,10 @@ export class HttpMCPTransport implements MCPTransport {
448
529
  triedAuth: boolean = false,
449
530
  resumeToken?: string,
450
531
  ): void {
532
+ if (this.isModernProtocol()) {
533
+ return;
534
+ }
535
+
451
536
  void this.openInboundSse(triedAuth, resumeToken).catch(error => {
452
537
  if (error instanceof Error && error.name === 'AbortError') {
453
538
  return;
@@ -461,6 +546,10 @@ export class HttpMCPTransport implements MCPTransport {
461
546
  triedAuth: boolean = false,
462
547
  resumeToken?: string,
463
548
  ): Promise<void> {
549
+ if (this.isModernProtocol()) {
550
+ return;
551
+ }
552
+
464
553
  try {
465
554
  const sessionIdForRequest = this.sessionId;
466
555
  const headers = await this.commonHeaders({
@@ -14,7 +14,7 @@ import {
14
14
  auth,
15
15
  type OAuthClientProvider,
16
16
  } from './oauth';
17
- import { LATEST_PROTOCOL_VERSION } from './types';
17
+ import { LATEST_LEGACY_PROTOCOL_VERSION } from './types';
18
18
 
19
19
  function isMessageEvent(event: string | undefined): boolean {
20
20
  return event === undefined || event === 'message';
@@ -69,7 +69,8 @@ export class SseMCPTransport implements MCPTransport {
69
69
  const headers: Record<string, string> = {
70
70
  ...this.headers,
71
71
  ...base,
72
- 'mcp-protocol-version': this.protocolVersion ?? LATEST_PROTOCOL_VERSION,
72
+ 'mcp-protocol-version':
73
+ this.protocolVersion ?? LATEST_LEGACY_PROTOCOL_VERSION,
73
74
  };
74
75
 
75
76
  if (this.authProvider) {
@@ -14,6 +14,7 @@ export interface StdioConfig {
14
14
  }
15
15
 
16
16
  export class StdioMCPTransport implements MCPTransport {
17
+ readonly supportsProtocolVersionDiscovery = true;
17
18
  private process?: ChildProcess;
18
19
  private abortController: AbortController = new AbortController();
19
20
  private readBuffer: ReadBuffer = new ReadBuffer();
@@ -4,6 +4,7 @@ import type { JSONRPCMessage } from './json-rpc-message';
4
4
  import { SseMCPTransport } from './mcp-sse-transport';
5
5
  import { HttpMCPTransport } from './mcp-http-transport';
6
6
  import type { OAuthClientProvider } from './oauth';
7
+ import { LATEST_PROTOCOL_VERSION } from './types';
7
8
 
8
9
  /**
9
10
  * Transport interface for MCP (Model Context Protocol) communication.
@@ -15,6 +16,11 @@ export type MCPTransportSendOptions = {
15
16
  */
16
17
  signal?: AbortSignal;
17
18
 
19
+ /**
20
+ * Request-specific HTTP headers produced from MCP tool parameters.
21
+ */
22
+ headers?: Record<string, string>;
23
+
18
24
  /**
19
25
  * Associates an outgoing message with an incoming request.
20
26
  */
@@ -39,6 +45,20 @@ export type MCPTransportCloseOptions = {
39
45
  };
40
46
 
41
47
  export interface MCPTransport {
48
+ /**
49
+ * Whether this transport can probe for stateless MCP protocol versions.
50
+ *
51
+ * Custom transports default to the legacy initialization flow unless they
52
+ * explicitly opt in.
53
+ */
54
+ supportsProtocolVersionDiscovery?: boolean;
55
+
56
+ /**
57
+ * Whether this transport mirrors x-mcp-header tool parameters into request
58
+ * headers.
59
+ */
60
+ supportsMcpToolParameterHeaders?: boolean;
61
+
42
62
  /**
43
63
  * Initialize and start the transport
44
64
  */
@@ -161,7 +181,11 @@ export function createMcpTransport(config: MCPTransportConfig): MCPTransport {
161
181
  case 'sse':
162
182
  return new SseMCPTransport(config);
163
183
  case 'http':
164
- return new HttpMCPTransport(config);
184
+ return new HttpMCPTransport({
185
+ ...config,
186
+ initialProtocolVersion:
187
+ config.initialProtocolVersion ?? LATEST_PROTOCOL_VERSION,
188
+ });
165
189
  default:
166
190
  throw new MCPClientError({
167
191
  message:
@@ -2,7 +2,7 @@ import { delay } from '@ai-sdk/provider-utils';
2
2
  import type { JSONRPCMessage } from './json-rpc-message';
3
3
  import type { MCPTransport } from './mcp-transport';
4
4
  import {
5
- LATEST_PROTOCOL_VERSION,
5
+ LATEST_LEGACY_PROTOCOL_VERSION,
6
6
  type MCPTool,
7
7
  type MCPResource,
8
8
  type MCPPrompt,
@@ -163,7 +163,7 @@ export class MockMCPTransport implements MCPTransport {
163
163
  jsonrpc: '2.0',
164
164
  id: message.id,
165
165
  result: this.initializeResult || {
166
- protocolVersion: LATEST_PROTOCOL_VERSION,
166
+ protocolVersion: LATEST_LEGACY_PROTOCOL_VERSION,
167
167
  serverInfo: {
168
168
  name: 'mock-mcp-server',
169
169
  version: '1.0.0',
@@ -39,6 +39,7 @@ export const OAuthTokensSchema = z
39
39
  expires_in: z.number().optional(),
40
40
  scope: z.string().optional(),
41
41
  refresh_token: z.string().optional(),
42
+ issuer: SafeUrlSchema.optional(),
42
43
  authorization_server: SafeUrlSchema.optional(),
43
44
  token_endpoint: SafeUrlSchema.optional(),
44
45
  })
@@ -66,6 +67,8 @@ export const OAuthMetadataSchema = z.looseObject({
66
67
  authorization_endpoint: SafeUrlSchema,
67
68
  token_endpoint: SafeUrlSchema,
68
69
  registration_endpoint: SafeUrlSchema.optional(),
70
+ authorization_response_iss_parameter_supported: z.boolean().optional(),
71
+ client_id_metadata_document_supported: z.boolean().optional(),
69
72
  scopes_supported: z.array(z.string()).optional(),
70
73
  response_types_supported: z.array(z.string()),
71
74
  grant_types_supported: z.array(z.string()).optional(),
@@ -87,6 +90,8 @@ export const OpenIdProviderMetadataSchema = z.looseObject({
87
90
  userinfo_endpoint: SafeUrlSchema.optional(),
88
91
  jwks_uri: SafeUrlSchema,
89
92
  registration_endpoint: SafeUrlSchema.optional(),
93
+ authorization_response_iss_parameter_supported: z.boolean().optional(),
94
+ client_id_metadata_document_supported: z.boolean().optional(),
90
95
  scopes_supported: z.array(z.string()).optional(),
91
96
  response_types_supported: z.array(z.string()),
92
97
  grant_types_supported: z.array(z.string()).optional(),
@@ -114,6 +119,7 @@ export const OAuthClientInformationSchema = z
114
119
  client_secret: z.string().optional(),
115
120
  client_id_issued_at: z.number().optional(),
116
121
  client_secret_expires_at: z.number().optional(),
122
+ issuer: SafeUrlSchema.optional(),
117
123
  authorization_server: SafeUrlSchema.optional(),
118
124
  token_endpoint: SafeUrlSchema.optional(),
119
125
  })
@@ -122,6 +128,9 @@ export const OAuthClientInformationSchema = z
122
128
  export const OAuthClientMetadataSchema = z
123
129
  .object({
124
130
  redirect_uris: z.array(SafeUrlSchema),
131
+ application_type: z
132
+ .union([z.literal('native'), z.literal('web')])
133
+ .optional(),
125
134
  token_endpoint_auth_method: z.string().optional(),
126
135
  grant_types: z.array(z.string()).optional(),
127
136
  response_types: z.array(z.string()).optional(),
package/src/tool/oauth.ts CHANGED
@@ -31,6 +31,7 @@ import { parseJSON, type FetchFunction } from '@ai-sdk/provider-utils';
31
31
  export type AuthResult = 'AUTHORIZED' | 'REDIRECT';
32
32
 
33
33
  export interface OAuthAuthorizationServerInformation {
34
+ issuer?: string;
34
35
  authorizationServerUrl: string;
35
36
  tokenEndpoint: string;
36
37
  }
@@ -122,11 +123,26 @@ function normalizeUrl(url: string | URL): string {
122
123
  return new URL(url).href;
123
124
  }
124
125
 
126
+ function validateAuthorizationResponseIssuer({
127
+ callbackIssuer,
128
+ expectedIssuer,
129
+ }: {
130
+ callbackIssuer: string | undefined;
131
+ expectedIssuer: string;
132
+ }): void {
133
+ if (callbackIssuer != null && callbackIssuer !== expectedIssuer) {
134
+ throw new MCPClientOAuthError({
135
+ message: `OAuth authorization response issuer ${callbackIssuer} does not match expected issuer ${expectedIssuer}`,
136
+ });
137
+ }
138
+ }
139
+
125
140
  function createAuthorizationServerInformation(
126
141
  authorizationServerUrl: string | URL,
127
142
  metadata?: AuthorizationServerMetadata,
128
143
  ): OAuthAuthorizationServerInformation {
129
144
  return {
145
+ issuer: metadata?.issuer ?? String(authorizationServerUrl),
130
146
  authorizationServerUrl: normalizeUrl(authorizationServerUrl),
131
147
  tokenEndpoint: normalizeUrl(
132
148
  metadata?.token_endpoint
@@ -142,6 +158,7 @@ function addAuthorizationServerInformationToTokens(
142
158
  ): OAuthTokens {
143
159
  return {
144
160
  ...tokens,
161
+ issuer: authorizationServerInformation.issuer,
145
162
  authorization_server: authorizationServerInformation.authorizationServerUrl,
146
163
  token_endpoint: authorizationServerInformation.tokenEndpoint,
147
164
  };
@@ -155,12 +172,14 @@ function addAuthorizationServerInformationToClientInformation<
155
172
  ): CLIENT_INFORMATION {
156
173
  return {
157
174
  ...clientInformation,
175
+ issuer: authorizationServerInformation.issuer,
158
176
  authorization_server: authorizationServerInformation.authorizationServerUrl,
159
177
  token_endpoint: authorizationServerInformation.tokenEndpoint,
160
178
  };
161
179
  }
162
180
 
163
181
  function getAuthorizationServerInformationFromCredentials(credentials?: {
182
+ issuer?: string;
164
183
  authorization_server?: string;
165
184
  token_endpoint?: string;
166
185
  }): OAuthAuthorizationServerInformation | undefined {
@@ -169,6 +188,7 @@ function getAuthorizationServerInformationFromCredentials(credentials?: {
169
188
  }
170
189
 
171
190
  return {
191
+ issuer: credentials.issuer,
172
192
  authorizationServerUrl: normalizeUrl(credentials.authorization_server),
173
193
  tokenEndpoint: normalizeUrl(credentials.token_endpoint),
174
194
  };
@@ -193,6 +213,7 @@ async function getStoredAuthorizationServerInformation({
193
213
  await provider.authorizationServerInformation?.();
194
214
  if (providerAuthorizationServerInformation) {
195
215
  return {
216
+ issuer: providerAuthorizationServerInformation.issuer,
196
217
  authorizationServerUrl: normalizeUrl(
197
218
  providerAuthorizationServerInformation.authorizationServerUrl,
198
219
  ),
@@ -258,6 +279,10 @@ function assertAuthorizationServerInformationMatches({
258
279
  currentAuthorizationServerInformation: OAuthAuthorizationServerInformation;
259
280
  }): void {
260
281
  if (
282
+ (storedAuthorizationServerInformation.issuer != null &&
283
+ currentAuthorizationServerInformation.issuer != null &&
284
+ storedAuthorizationServerInformation.issuer !==
285
+ currentAuthorizationServerInformation.issuer) ||
261
286
  storedAuthorizationServerInformation.authorizationServerUrl !==
262
287
  currentAuthorizationServerInformation.authorizationServerUrl ||
263
288
  storedAuthorizationServerInformation.tokenEndpoint !==
@@ -1050,12 +1075,18 @@ export async function registerClient(
1050
1075
  registrationUrl = new URL('/register', authorizationServerUrl);
1051
1076
  }
1052
1077
 
1078
+ const applicationType =
1079
+ clientMetadata.application_type ??
1080
+ inferOAuthApplicationType(clientMetadata.redirect_uris);
1053
1081
  const response = await (fetchFn ?? fetch)(registrationUrl, {
1054
1082
  method: 'POST',
1055
1083
  headers: {
1056
1084
  'Content-Type': 'application/json',
1057
1085
  },
1058
- body: JSON.stringify(clientMetadata),
1086
+ body: JSON.stringify({
1087
+ ...clientMetadata,
1088
+ application_type: applicationType,
1089
+ }),
1059
1090
  });
1060
1091
 
1061
1092
  if (!response.ok) {
@@ -1065,12 +1096,32 @@ export async function registerClient(
1065
1096
  return OAuthClientInformationFullSchema.parse(await response.json());
1066
1097
  }
1067
1098
 
1099
+ function inferOAuthApplicationType(redirectUris: string[]): 'native' | 'web' {
1100
+ const isNativeRedirectUri = (redirectUri: string): boolean => {
1101
+ const url = new URL(redirectUri);
1102
+ return (
1103
+ ((url.protocol === 'http:' || url.protocol === 'https:') &&
1104
+ (url.hostname === 'localhost' ||
1105
+ url.hostname.endsWith('.localhost') ||
1106
+ url.hostname === '127.0.0.1' ||
1107
+ url.hostname === '[::1]')) ||
1108
+ (url.protocol !== 'http:' && url.protocol !== 'https:')
1109
+ );
1110
+ };
1111
+
1112
+ return redirectUris.every(isNativeRedirectUri) ? 'native' : 'web';
1113
+ }
1114
+
1068
1115
  export async function auth(
1069
1116
  provider: OAuthClientProvider,
1070
1117
  options: {
1071
1118
  serverUrl: string | URL;
1072
1119
  authorizationCode?: string;
1073
1120
  callbackState?: string;
1121
+ /**
1122
+ * Value of the `iss` parameter from the authorization response.
1123
+ */
1124
+ callbackIssuer?: string;
1074
1125
  scope?: string;
1075
1126
  resourceMetadataUrl?: URL;
1076
1127
  fetchFn?: FetchFunction;
@@ -1131,6 +1182,7 @@ async function authInternal(
1131
1182
  serverUrl,
1132
1183
  authorizationCode,
1133
1184
  callbackState,
1185
+ callbackIssuer,
1134
1186
  scope,
1135
1187
  resourceMetadataUrl,
1136
1188
  fetchFn,
@@ -1138,6 +1190,7 @@ async function authInternal(
1138
1190
  serverUrl: string | URL;
1139
1191
  authorizationCode?: string;
1140
1192
  callbackState?: string;
1193
+ callbackIssuer?: string;
1141
1194
  scope?: string;
1142
1195
  resourceMetadataUrl?: URL;
1143
1196
  fetchFn?: FetchFunction;
@@ -1194,6 +1247,20 @@ async function authInternal(
1194
1247
 
1195
1248
  /** Load or register client credentials with the AS pin attached. */
1196
1249
  let clientInformation = await Promise.resolve(provider.clientInformation());
1250
+ if (clientInformation?.issuer != null) {
1251
+ const storedAuthorizationServerInformation =
1252
+ await getStoredAuthorizationServerInformation({
1253
+ provider,
1254
+ clientInformation,
1255
+ });
1256
+ if (storedAuthorizationServerInformation) {
1257
+ assertAuthorizationServerInformationMatches({
1258
+ storedAuthorizationServerInformation,
1259
+ currentAuthorizationServerInformation,
1260
+ });
1261
+ }
1262
+ }
1263
+
1197
1264
  if (!clientInformation) {
1198
1265
  if (authorizationCode !== undefined) {
1199
1266
  throw new Error(
@@ -1242,6 +1309,13 @@ async function authInternal(
1242
1309
  'Stored OAuth authorization server metadata is required when exchanging an authorization code',
1243
1310
  });
1244
1311
  }
1312
+ validateAuthorizationResponseIssuer({
1313
+ callbackIssuer,
1314
+ expectedIssuer:
1315
+ storedAuthorizationServerInformation.issuer ??
1316
+ metadata?.issuer ??
1317
+ String(authorizationServerUrl),
1318
+ });
1245
1319
  assertAuthorizationServerInformationMatches({
1246
1320
  storedAuthorizationServerInformation,
1247
1321
  currentAuthorizationServerInformation,
package/src/tool/types.ts CHANGED
@@ -2,9 +2,11 @@ import { z } from 'zod/v4';
2
2
  import type { JSONObject } from '@ai-sdk/provider';
3
3
  import type { FlexibleSchema, Tool } from '@ai-sdk/provider-utils';
4
4
 
5
- export const LATEST_PROTOCOL_VERSION = '2025-11-25';
5
+ export const LATEST_PROTOCOL_VERSION = '2026-07-28';
6
+ export const LATEST_LEGACY_PROTOCOL_VERSION = '2025-11-25';
6
7
  export const SUPPORTED_PROTOCOL_VERSIONS = [
7
8
  LATEST_PROTOCOL_VERSION,
9
+ LATEST_LEGACY_PROTOCOL_VERSION,
8
10
  '2025-06-18',
9
11
  '2025-03-26',
10
12
  '2024-11-05',
@@ -73,7 +75,9 @@ export const BaseParamsSchema = z.looseObject({
73
75
  _meta: z.optional(z.object({}).loose()),
74
76
  });
75
77
  type BaseParams = z.infer<typeof BaseParamsSchema>;
76
- export const ResultSchema = BaseParamsSchema;
78
+ export const ResultSchema = BaseParamsSchema.extend({
79
+ resultType: z.optional(z.string()),
80
+ });
77
81
 
78
82
  export const RequestSchema = z.object({
79
83
  method: z.string(),
@@ -128,6 +132,15 @@ export const ClientCapabilitiesSchema = z
128
132
  export type ClientCapabilities = z.infer<typeof ClientCapabilitiesSchema>;
129
133
  export type ElicitationCapability = z.infer<typeof ElicitationCapabilitySchema>;
130
134
 
135
+ export const DiscoverResultSchema = ResultSchema.extend({
136
+ supportedVersions: z.array(z.string()),
137
+ capabilities: ServerCapabilitiesSchema,
138
+ instructions: z.optional(z.string()),
139
+ ttlMs: z.optional(z.number()),
140
+ cacheScope: z.optional(z.union([z.literal('public'), z.literal('private')])),
141
+ });
142
+ export type DiscoverResult = z.infer<typeof DiscoverResultSchema>;
143
+
131
144
  export const InitializeResultSchema = ResultSchema.extend({
132
145
  protocolVersion: z.string(),
133
146
  capabilities: ServerCapabilitiesSchema,
@@ -154,12 +167,10 @@ const ToolSchema = z
154
167
  */
155
168
  title: z.optional(z.string()),
156
169
  description: z.optional(z.string()),
157
- inputSchema: z
158
- .object({
159
- type: z.literal('object'),
160
- properties: z.optional(z.object({}).loose()),
161
- })
162
- .loose(),
170
+ inputSchema: z.looseObject({
171
+ type: z.optional(z.unknown()),
172
+ properties: z.optional(z.object({}).loose()),
173
+ }),
163
174
  /**
164
175
  * @see https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema
165
176
  */