@ai-sdk/mcp 2.0.0-beta.5 → 2.0.0-beta.66
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 +496 -8
- package/README.md +134 -0
- package/dist/index.d.ts +146 -1
- package/dist/index.js +764 -350
- package/dist/index.js.map +1 -1
- package/dist/mcp-stdio/index.d.ts +8 -0
- package/dist/mcp-stdio/index.js +170 -172
- package/dist/mcp-stdio/index.js.map +1 -1
- package/package.json +18 -19
- package/src/error/mcp-client-error.ts +40 -0
- package/src/index.ts +16 -1
- package/src/tool/index.ts +1 -0
- package/src/tool/json-rpc-message.ts +7 -0
- package/src/tool/mcp-apps.ts +254 -0
- package/src/tool/mcp-client.ts +128 -43
- package/src/tool/mcp-http-transport.ts +78 -23
- package/src/tool/mcp-sse-transport.ts +47 -15
- package/src/tool/mcp-stdio/create-child-process.ts +2 -2
- package/src/tool/mcp-stdio/mcp-stdio-transport.ts +17 -14
- package/src/tool/mcp-transport.ts +28 -2
- package/src/tool/mock-mcp-transport.ts +8 -9
- package/src/tool/oauth-types.ts +22 -18
- package/src/tool/oauth.ts +324 -37
- package/src/tool/types.ts +27 -3
- package/src/util/oauth-util.ts +13 -0
- package/dist/index.d.mts +0 -509
- package/dist/index.mjs +0 -2128
- package/dist/index.mjs.map +0 -1
- package/dist/mcp-stdio/index.d.mts +0 -89
- package/dist/mcp-stdio/index.mjs +0 -426
- package/dist/mcp-stdio/index.mjs.map +0 -1
package/src/tool/mcp-client.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { JSONSchema7, JSONValue } from '@ai-sdk/provider';
|
|
1
|
+
import type { JSONObject, JSONSchema7, JSONValue } from '@ai-sdk/provider';
|
|
2
2
|
import {
|
|
3
3
|
asSchema,
|
|
4
4
|
dynamicTool,
|
|
5
|
-
FlexibleSchema,
|
|
6
5
|
jsonSchema,
|
|
7
6
|
safeParseJSON,
|
|
8
7
|
safeValidateTypes,
|
|
9
|
-
Tool,
|
|
10
8
|
tool,
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
type FlexibleSchema,
|
|
10
|
+
type Tool,
|
|
11
|
+
type ToolExecutionOptions,
|
|
12
|
+
type ToolResultOutput,
|
|
13
13
|
} from '@ai-sdk/provider-utils';
|
|
14
|
-
import { z } from 'zod/v4';
|
|
14
|
+
import type { z } from 'zod/v4';
|
|
15
15
|
import { MCPClientError } from '../error/mcp-client-error';
|
|
16
|
-
import {
|
|
16
|
+
import type {
|
|
17
17
|
JSONRPCError,
|
|
18
18
|
JSONRPCNotification,
|
|
19
19
|
JSONRPCRequest,
|
|
@@ -22,43 +22,45 @@ import {
|
|
|
22
22
|
import {
|
|
23
23
|
createMcpTransport,
|
|
24
24
|
isCustomMcpTransport,
|
|
25
|
-
MCPTransport,
|
|
26
|
-
MCPTransportConfig,
|
|
25
|
+
type MCPTransport,
|
|
26
|
+
type MCPTransportConfig,
|
|
27
27
|
} from './mcp-transport';
|
|
28
|
+
import { getMCPAppToolMeta, MCP_APP_MIME_TYPE } from './mcp-apps';
|
|
28
29
|
import {
|
|
29
|
-
CallToolResult,
|
|
30
30
|
CallToolResultSchema,
|
|
31
|
-
ClientCapabilities,
|
|
32
|
-
Configuration as ClientConfiguration,
|
|
33
|
-
ElicitationRequest,
|
|
34
31
|
ElicitationRequestSchema,
|
|
35
|
-
ElicitResult,
|
|
36
32
|
ElicitResultSchema,
|
|
37
33
|
InitializeResultSchema,
|
|
38
34
|
LATEST_PROTOCOL_VERSION,
|
|
39
|
-
ListResourceTemplatesResult,
|
|
40
35
|
ListResourceTemplatesResultSchema,
|
|
41
|
-
ListResourcesResult,
|
|
42
36
|
ListResourcesResultSchema,
|
|
43
|
-
ListPromptsResult,
|
|
44
37
|
ListPromptsResultSchema,
|
|
45
|
-
ListToolsResult,
|
|
46
38
|
ListToolsResultSchema,
|
|
47
|
-
McpToolSet,
|
|
48
|
-
Notification,
|
|
49
|
-
PaginatedRequest,
|
|
50
|
-
ReadResourceResult,
|
|
51
39
|
ReadResourceResultSchema,
|
|
52
|
-
GetPromptResult,
|
|
53
40
|
GetPromptResultSchema,
|
|
54
|
-
Request,
|
|
55
|
-
RequestOptions,
|
|
56
|
-
ServerCapabilities,
|
|
57
41
|
SUPPORTED_PROTOCOL_VERSIONS,
|
|
58
|
-
|
|
59
|
-
|
|
42
|
+
type CallToolResult,
|
|
43
|
+
type ClientCapabilities,
|
|
44
|
+
type Configuration,
|
|
45
|
+
type Configuration as ClientConfiguration,
|
|
46
|
+
type ElicitationRequest,
|
|
47
|
+
type ElicitResult,
|
|
48
|
+
type ListResourceTemplatesResult,
|
|
49
|
+
type ListResourcesResult,
|
|
50
|
+
type ListPromptsResult,
|
|
51
|
+
type ListToolsResult,
|
|
52
|
+
type McpToolSet,
|
|
53
|
+
type Notification,
|
|
54
|
+
type PaginatedRequest,
|
|
55
|
+
type ReadResourceResult,
|
|
56
|
+
type GetPromptResult,
|
|
57
|
+
type Request,
|
|
58
|
+
type RequestOptions,
|
|
59
|
+
type ServerCapabilities,
|
|
60
|
+
type ToolSchemas,
|
|
61
|
+
type ToolMeta,
|
|
62
|
+
type McpProviderMetadata,
|
|
60
63
|
} from './types';
|
|
61
|
-
|
|
62
64
|
const CLIENT_VERSION = '1.0.0';
|
|
63
65
|
|
|
64
66
|
function mcpToModelOutput({
|
|
@@ -81,9 +83,9 @@ function mcpToModelOutput({
|
|
|
81
83
|
}
|
|
82
84
|
if (part.type === 'image' && 'data' in part && 'mimeType' in part) {
|
|
83
85
|
return {
|
|
84
|
-
type: '
|
|
85
|
-
data: part.data as string,
|
|
86
|
+
type: 'file' as const,
|
|
86
87
|
mediaType: part.mimeType as string,
|
|
88
|
+
data: { type: 'data' as const, data: part.data as string },
|
|
87
89
|
};
|
|
88
90
|
}
|
|
89
91
|
return { type: 'text' as const, text: JSON.stringify(part) };
|
|
@@ -99,6 +101,12 @@ export interface MCPClientConfig {
|
|
|
99
101
|
/** Optional callback for uncaught errors */
|
|
100
102
|
onUncaughtError?: (error: unknown) => void;
|
|
101
103
|
/** Optional client name, defaults to 'ai-sdk-mcp-client' */
|
|
104
|
+
clientName?: string;
|
|
105
|
+
/**
|
|
106
|
+
* Optional client name, defaults to 'ai-sdk-mcp-client'
|
|
107
|
+
*
|
|
108
|
+
* @deprecated Use `clientName` instead.
|
|
109
|
+
*/
|
|
102
110
|
name?: string;
|
|
103
111
|
/** Optional client version, defaults to '1.0.0' */
|
|
104
112
|
version?: string;
|
|
@@ -119,6 +127,22 @@ export async function createMCPClient(
|
|
|
119
127
|
}
|
|
120
128
|
|
|
121
129
|
export interface MCPClient {
|
|
130
|
+
/**
|
|
131
|
+
* Information about the connected MCP server, as reported during initialization.
|
|
132
|
+
* @see https://modelcontextprotocol.io/specification/2025-11-25/schema#implementation
|
|
133
|
+
*/
|
|
134
|
+
readonly serverInfo: Configuration;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Optional instructions provided by the server during the initialize handshake.
|
|
138
|
+
*
|
|
139
|
+
* These describe how to use the server and its features, and can be used by clients
|
|
140
|
+
* to improve LLM interactions (e.g. by including them in the system prompt).
|
|
141
|
+
*
|
|
142
|
+
* @see https://modelcontextprotocol.io/specification/2025-11-25/schema#initializeresult
|
|
143
|
+
*/
|
|
144
|
+
readonly instructions?: string;
|
|
145
|
+
|
|
122
146
|
tools<TOOL_SCHEMAS extends ToolSchemas = 'automatic'>(options?: {
|
|
123
147
|
schemas?: TOOL_SCHEMAS;
|
|
124
148
|
}): Promise<McpToolSet<TOOL_SCHEMAS>>;
|
|
@@ -131,6 +155,15 @@ export interface MCPClient {
|
|
|
131
155
|
options?: RequestOptions;
|
|
132
156
|
}): Promise<ListToolsResult>;
|
|
133
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Calls a tool on the MCP server.
|
|
160
|
+
*/
|
|
161
|
+
callTool(args: {
|
|
162
|
+
name: string;
|
|
163
|
+
arguments?: Record<string, unknown>;
|
|
164
|
+
options?: RequestOptions;
|
|
165
|
+
}): Promise<CallToolResult>;
|
|
166
|
+
|
|
134
167
|
/**
|
|
135
168
|
* Creates AI SDK tools from tool definitions.
|
|
136
169
|
*/
|
|
@@ -201,6 +234,8 @@ class DefaultMCPClient implements MCPClient {
|
|
|
201
234
|
(response: JSONRPCResponse | Error) => void
|
|
202
235
|
> = new Map();
|
|
203
236
|
private serverCapabilities: ServerCapabilities = {};
|
|
237
|
+
private _serverInfo: Configuration = { name: '', version: '' };
|
|
238
|
+
private _serverInstructions?: string;
|
|
204
239
|
private isClosed = true;
|
|
205
240
|
private elicitationRequestHandler?: (
|
|
206
241
|
request: ElicitationRequest,
|
|
@@ -208,7 +243,8 @@ class DefaultMCPClient implements MCPClient {
|
|
|
208
243
|
|
|
209
244
|
constructor({
|
|
210
245
|
transport: transportConfig,
|
|
211
|
-
name
|
|
246
|
+
name,
|
|
247
|
+
clientName = name ?? 'ai-sdk-mcp-client',
|
|
212
248
|
version = CLIENT_VERSION,
|
|
213
249
|
onUncaughtError,
|
|
214
250
|
capabilities,
|
|
@@ -242,11 +278,19 @@ class DefaultMCPClient implements MCPClient {
|
|
|
242
278
|
};
|
|
243
279
|
|
|
244
280
|
this.clientInfo = {
|
|
245
|
-
name,
|
|
281
|
+
name: clientName,
|
|
246
282
|
version,
|
|
247
283
|
};
|
|
248
284
|
}
|
|
249
285
|
|
|
286
|
+
get serverInfo(): Configuration {
|
|
287
|
+
return this._serverInfo;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
get instructions(): string | undefined {
|
|
291
|
+
return this._serverInstructions;
|
|
292
|
+
}
|
|
293
|
+
|
|
250
294
|
async init(): Promise<this> {
|
|
251
295
|
try {
|
|
252
296
|
await this.transport.start();
|
|
@@ -277,6 +321,13 @@ class DefaultMCPClient implements MCPClient {
|
|
|
277
321
|
}
|
|
278
322
|
|
|
279
323
|
this.serverCapabilities = result.capabilities;
|
|
324
|
+
this._serverInfo = result.serverInfo;
|
|
325
|
+
if (this.transport.setProtocolVersion) {
|
|
326
|
+
this.transport.setProtocolVersion(result.protocolVersion);
|
|
327
|
+
} else {
|
|
328
|
+
this.transport.protocolVersion = result.protocolVersion;
|
|
329
|
+
}
|
|
330
|
+
this._serverInstructions = result.instructions;
|
|
280
331
|
|
|
281
332
|
// Complete initialization handshake:
|
|
282
333
|
await this.notification({
|
|
@@ -413,22 +464,20 @@ class DefaultMCPClient implements MCPClient {
|
|
|
413
464
|
});
|
|
414
465
|
}
|
|
415
466
|
|
|
416
|
-
|
|
467
|
+
async callTool({
|
|
417
468
|
name,
|
|
418
|
-
args,
|
|
469
|
+
arguments: args = {},
|
|
419
470
|
options,
|
|
420
471
|
}: {
|
|
421
472
|
name: string;
|
|
422
|
-
|
|
423
|
-
options?:
|
|
473
|
+
arguments?: Record<string, unknown>;
|
|
474
|
+
options?: RequestOptions;
|
|
424
475
|
}): Promise<CallToolResult> {
|
|
425
476
|
try {
|
|
426
477
|
return this.request({
|
|
427
478
|
request: { method: 'tools/call', params: { name, arguments: args } },
|
|
428
479
|
resultSchema: CallToolResultSchema,
|
|
429
|
-
options
|
|
430
|
-
signal: options?.abortSignal,
|
|
431
|
-
},
|
|
480
|
+
options,
|
|
432
481
|
});
|
|
433
482
|
} catch (error) {
|
|
434
483
|
throw error;
|
|
@@ -569,20 +618,45 @@ class DefaultMCPClient implements MCPClient {
|
|
|
569
618
|
_meta,
|
|
570
619
|
} of definitions.tools) {
|
|
571
620
|
const resolvedTitle = title ?? annotations?.title;
|
|
572
|
-
if (
|
|
621
|
+
if (
|
|
622
|
+
schemas !== 'automatic' &&
|
|
623
|
+
!Object.prototype.hasOwnProperty.call(schemas, name)
|
|
624
|
+
) {
|
|
573
625
|
continue;
|
|
574
626
|
}
|
|
575
627
|
|
|
576
628
|
const self = this;
|
|
577
629
|
const outputSchema =
|
|
578
630
|
schemas !== 'automatic' ? schemas[name]?.outputSchema : undefined;
|
|
631
|
+
const appMeta = getMCPAppToolMeta({ _meta });
|
|
632
|
+
const metadata = {
|
|
633
|
+
clientName: this.clientInfo.name,
|
|
634
|
+
toolName: name,
|
|
635
|
+
...(resolvedTitle != null ? { title: resolvedTitle } : {}),
|
|
636
|
+
...(appMeta?.resourceUri != null
|
|
637
|
+
? {
|
|
638
|
+
app: {
|
|
639
|
+
...appMeta,
|
|
640
|
+
mimeType: MCP_APP_MIME_TYPE,
|
|
641
|
+
} as JSONObject,
|
|
642
|
+
}
|
|
643
|
+
: {}),
|
|
644
|
+
} satisfies McpProviderMetadata;
|
|
579
645
|
|
|
580
646
|
const execute = async (
|
|
581
647
|
args: any,
|
|
582
|
-
options: ToolExecutionOptions
|
|
648
|
+
options: ToolExecutionOptions<{}>,
|
|
583
649
|
): Promise<unknown> => {
|
|
584
650
|
options?.abortSignal?.throwIfAborted();
|
|
585
|
-
const result = await self.callTool({
|
|
651
|
+
const result = await self.callTool({
|
|
652
|
+
name,
|
|
653
|
+
arguments: args,
|
|
654
|
+
options: { signal: options?.abortSignal },
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
if (result.isError) {
|
|
658
|
+
return result;
|
|
659
|
+
}
|
|
586
660
|
|
|
587
661
|
if (outputSchema != null) {
|
|
588
662
|
return self.extractStructuredContent(result, outputSchema, name);
|
|
@@ -596,6 +670,7 @@ class DefaultMCPClient implements MCPClient {
|
|
|
596
670
|
? dynamicTool({
|
|
597
671
|
description,
|
|
598
672
|
title: resolvedTitle,
|
|
673
|
+
metadata,
|
|
599
674
|
inputSchema: jsonSchema({
|
|
600
675
|
...inputSchema,
|
|
601
676
|
properties: inputSchema.properties ?? {},
|
|
@@ -607,6 +682,7 @@ class DefaultMCPClient implements MCPClient {
|
|
|
607
682
|
: tool({
|
|
608
683
|
description,
|
|
609
684
|
title: resolvedTitle,
|
|
685
|
+
metadata,
|
|
610
686
|
inputSchema: schemas[name].inputSchema,
|
|
611
687
|
...(outputSchema != null ? { outputSchema } : {}),
|
|
612
688
|
execute,
|
|
@@ -736,6 +812,15 @@ class DefaultMCPClient implements MCPClient {
|
|
|
736
812
|
|
|
737
813
|
private async onRequestMessage(request: JSONRPCRequest): Promise<void> {
|
|
738
814
|
try {
|
|
815
|
+
if (request.method === 'ping') {
|
|
816
|
+
await this.transport.send({
|
|
817
|
+
jsonrpc: '2.0',
|
|
818
|
+
id: request.id,
|
|
819
|
+
result: {},
|
|
820
|
+
});
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
|
|
739
824
|
if (request.method !== 'elicitation/create') {
|
|
740
825
|
await this.transport.send({
|
|
741
826
|
jsonrpc: '2.0',
|
|
@@ -2,19 +2,29 @@ import {
|
|
|
2
2
|
EventSourceParserStream,
|
|
3
3
|
withUserAgentSuffix,
|
|
4
4
|
getRuntimeEnvironmentUserAgent,
|
|
5
|
+
type FetchFunction,
|
|
5
6
|
} from '@ai-sdk/provider-utils';
|
|
6
7
|
import { MCPClientError } from '../error/mcp-client-error';
|
|
7
|
-
import {
|
|
8
|
-
|
|
8
|
+
import {
|
|
9
|
+
JSONRPCMessageSchema,
|
|
10
|
+
parseJSONRPCMessage,
|
|
11
|
+
type JSONRPCMessage,
|
|
12
|
+
} from './json-rpc-message';
|
|
13
|
+
import type { MCPTransport } from './mcp-transport';
|
|
9
14
|
import { VERSION } from '../version';
|
|
10
15
|
import {
|
|
11
|
-
OAuthClientProvider,
|
|
12
16
|
extractResourceMetadataUrl,
|
|
13
17
|
UnauthorizedError,
|
|
14
18
|
auth,
|
|
19
|
+
type AuthResult,
|
|
20
|
+
type OAuthClientProvider,
|
|
15
21
|
} from './oauth';
|
|
16
22
|
import { LATEST_PROTOCOL_VERSION } from './types';
|
|
17
23
|
|
|
24
|
+
function isMessageEvent(event: string | undefined): boolean {
|
|
25
|
+
return event === undefined || event === 'message';
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
/**
|
|
19
29
|
* HTTP MCP transport implementing the Streamable HTTP style.
|
|
20
30
|
*
|
|
@@ -30,6 +40,9 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
30
40
|
private resourceMetadataUrl?: URL;
|
|
31
41
|
private sessionId?: string;
|
|
32
42
|
private inboundSseConnection?: { close: () => void };
|
|
43
|
+
private redirectMode: RequestRedirect;
|
|
44
|
+
private fetchFn: FetchFunction;
|
|
45
|
+
private authPromise?: Promise<AuthResult>;
|
|
33
46
|
|
|
34
47
|
// Inbound SSE resumption and reconnection state
|
|
35
48
|
private lastInboundEventId?: string;
|
|
@@ -44,19 +57,30 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
44
57
|
onclose?: () => void;
|
|
45
58
|
onerror?: (error: unknown) => void;
|
|
46
59
|
onmessage?: (message: JSONRPCMessage) => void;
|
|
60
|
+
protocolVersion?: string;
|
|
47
61
|
|
|
48
62
|
constructor({
|
|
49
63
|
url,
|
|
50
64
|
headers,
|
|
51
65
|
authProvider,
|
|
66
|
+
redirect = 'error',
|
|
67
|
+
fetch: fetchFn,
|
|
52
68
|
}: {
|
|
53
69
|
url: string;
|
|
54
70
|
headers?: Record<string, string>;
|
|
55
71
|
authProvider?: OAuthClientProvider;
|
|
72
|
+
redirect?: 'follow' | 'error';
|
|
73
|
+
fetch?: FetchFunction;
|
|
56
74
|
}) {
|
|
57
75
|
this.url = new URL(url);
|
|
58
76
|
this.headers = headers;
|
|
59
77
|
this.authProvider = authProvider;
|
|
78
|
+
this.redirectMode = redirect;
|
|
79
|
+
this.fetchFn = fetchFn ?? globalThis.fetch;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
setProtocolVersion(version: string): void {
|
|
83
|
+
this.protocolVersion = version;
|
|
60
84
|
}
|
|
61
85
|
|
|
62
86
|
private async commonHeaders(
|
|
@@ -65,7 +89,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
65
89
|
const headers: Record<string, string> = {
|
|
66
90
|
...this.headers,
|
|
67
91
|
...base,
|
|
68
|
-
'mcp-protocol-version': LATEST_PROTOCOL_VERSION,
|
|
92
|
+
'mcp-protocol-version': this.protocolVersion ?? LATEST_PROTOCOL_VERSION,
|
|
69
93
|
};
|
|
70
94
|
|
|
71
95
|
if (this.sessionId) {
|
|
@@ -86,6 +110,27 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
86
110
|
);
|
|
87
111
|
}
|
|
88
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Runs a single OAuth recovery flow for concurrent 401 responses.
|
|
115
|
+
*/
|
|
116
|
+
private authorizeOnce(resourceMetadataUrl?: URL): Promise<AuthResult> {
|
|
117
|
+
if (!this.authProvider) {
|
|
118
|
+
return Promise.resolve('REDIRECT');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!this.authPromise) {
|
|
122
|
+
this.authPromise = auth(this.authProvider, {
|
|
123
|
+
serverUrl: this.url,
|
|
124
|
+
resourceMetadataUrl,
|
|
125
|
+
fetchFn: this.fetchFn,
|
|
126
|
+
}).finally(() => {
|
|
127
|
+
this.authPromise = undefined;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return this.authPromise;
|
|
132
|
+
}
|
|
133
|
+
|
|
89
134
|
async start(): Promise<void> {
|
|
90
135
|
if (this.abortController) {
|
|
91
136
|
throw new MCPClientError({
|
|
@@ -107,10 +152,11 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
107
152
|
!this.abortController.signal.aborted
|
|
108
153
|
) {
|
|
109
154
|
const headers = await this.commonHeaders({});
|
|
110
|
-
await
|
|
155
|
+
await this.fetchFn(this.url.href, {
|
|
111
156
|
method: 'DELETE',
|
|
112
157
|
headers,
|
|
113
158
|
signal: this.abortController.signal,
|
|
159
|
+
redirect: this.redirectMode,
|
|
114
160
|
}).catch(() => undefined);
|
|
115
161
|
}
|
|
116
162
|
} catch {}
|
|
@@ -132,9 +178,10 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
132
178
|
headers,
|
|
133
179
|
body: JSON.stringify(message),
|
|
134
180
|
signal: this.abortController?.signal,
|
|
181
|
+
redirect: this.redirectMode,
|
|
135
182
|
} satisfies RequestInit;
|
|
136
183
|
|
|
137
|
-
const response = await
|
|
184
|
+
const response = await this.fetchFn(this.url.href, init);
|
|
138
185
|
|
|
139
186
|
const sessionId = response.headers.get('mcp-session-id');
|
|
140
187
|
if (sessionId) {
|
|
@@ -144,10 +191,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
144
191
|
if (response.status === 401 && this.authProvider && !triedAuth) {
|
|
145
192
|
this.resourceMetadataUrl = extractResourceMetadataUrl(response);
|
|
146
193
|
try {
|
|
147
|
-
const result = await
|
|
148
|
-
serverUrl: this.url,
|
|
149
|
-
resourceMetadataUrl: this.resourceMetadataUrl,
|
|
150
|
-
});
|
|
194
|
+
const result = await this.authorizeOnce(this.resourceMetadataUrl);
|
|
151
195
|
if (result !== 'AUTHORIZED') {
|
|
152
196
|
const error = new UnauthorizedError();
|
|
153
197
|
throw error;
|
|
@@ -181,6 +225,9 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
181
225
|
|
|
182
226
|
const error = new MCPClientError({
|
|
183
227
|
message: errorMessage,
|
|
228
|
+
statusCode: response.status,
|
|
229
|
+
url: this.url.href,
|
|
230
|
+
responseBody: text ?? undefined,
|
|
184
231
|
});
|
|
185
232
|
this.onerror?.(error);
|
|
186
233
|
throw error;
|
|
@@ -197,9 +244,13 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
197
244
|
if (contentType.includes('application/json')) {
|
|
198
245
|
const data = await response.json();
|
|
199
246
|
const messages: JSONRPCMessage[] = Array.isArray(data)
|
|
200
|
-
? data.map((
|
|
247
|
+
? data.map((message: unknown) =>
|
|
248
|
+
JSONRPCMessageSchema.parse(message),
|
|
249
|
+
)
|
|
201
250
|
: [JSONRPCMessageSchema.parse(data)];
|
|
202
|
-
for (const
|
|
251
|
+
for (const jsonRpcMessage of messages) {
|
|
252
|
+
this.onmessage?.(jsonRpcMessage);
|
|
253
|
+
}
|
|
203
254
|
return;
|
|
204
255
|
}
|
|
205
256
|
|
|
@@ -208,6 +259,8 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
208
259
|
const error = new MCPClientError({
|
|
209
260
|
message:
|
|
210
261
|
'MCP HTTP Transport Error: text/event-stream response without body',
|
|
262
|
+
statusCode: response.status,
|
|
263
|
+
url: this.url.href,
|
|
211
264
|
});
|
|
212
265
|
this.onerror?.(error);
|
|
213
266
|
throw error;
|
|
@@ -224,10 +277,10 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
224
277
|
const { done, value } = await reader.read();
|
|
225
278
|
if (done) return;
|
|
226
279
|
const { event, data } = value;
|
|
227
|
-
if (event
|
|
280
|
+
if (isMessageEvent(event)) {
|
|
228
281
|
try {
|
|
229
|
-
const
|
|
230
|
-
this.onmessage?.(
|
|
282
|
+
const jsonRpcMessage = await parseJSONRPCMessage(data);
|
|
283
|
+
this.onmessage?.(jsonRpcMessage);
|
|
231
284
|
} catch (error) {
|
|
232
285
|
const e = new MCPClientError({
|
|
233
286
|
message:
|
|
@@ -252,6 +305,8 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
252
305
|
|
|
253
306
|
const error = new MCPClientError({
|
|
254
307
|
message: `MCP HTTP Transport Error: Unexpected content type: ${contentType}`,
|
|
308
|
+
statusCode: response.status,
|
|
309
|
+
url: this.url.href,
|
|
255
310
|
});
|
|
256
311
|
this.onerror?.(error);
|
|
257
312
|
throw error;
|
|
@@ -308,10 +363,11 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
308
363
|
headers['last-event-id'] = resumeToken;
|
|
309
364
|
}
|
|
310
365
|
|
|
311
|
-
const response = await
|
|
366
|
+
const response = await this.fetchFn(this.url.href, {
|
|
312
367
|
method: 'GET',
|
|
313
368
|
headers,
|
|
314
369
|
signal: this.abortController?.signal,
|
|
370
|
+
redirect: this.redirectMode,
|
|
315
371
|
});
|
|
316
372
|
|
|
317
373
|
const sessionId = response.headers.get('mcp-session-id');
|
|
@@ -322,10 +378,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
322
378
|
if (response.status === 401 && this.authProvider && !triedAuth) {
|
|
323
379
|
this.resourceMetadataUrl = extractResourceMetadataUrl(response);
|
|
324
380
|
try {
|
|
325
|
-
const result = await
|
|
326
|
-
serverUrl: this.url,
|
|
327
|
-
resourceMetadataUrl: this.resourceMetadataUrl,
|
|
328
|
-
});
|
|
381
|
+
const result = await this.authorizeOnce(this.resourceMetadataUrl);
|
|
329
382
|
if (result !== 'AUTHORIZED') {
|
|
330
383
|
const error = new UnauthorizedError();
|
|
331
384
|
this.onerror?.(error);
|
|
@@ -345,6 +398,8 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
345
398
|
if (!response.ok || !response.body) {
|
|
346
399
|
const error = new MCPClientError({
|
|
347
400
|
message: `MCP HTTP Transport Error: GET SSE failed: ${response.status} ${response.statusText}`,
|
|
401
|
+
statusCode: response.status,
|
|
402
|
+
url: this.url.href,
|
|
348
403
|
});
|
|
349
404
|
this.onerror?.(error);
|
|
350
405
|
return;
|
|
@@ -370,10 +425,10 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
370
425
|
this.lastInboundEventId = id;
|
|
371
426
|
}
|
|
372
427
|
|
|
373
|
-
if (event
|
|
428
|
+
if (isMessageEvent(event)) {
|
|
374
429
|
try {
|
|
375
|
-
const
|
|
376
|
-
this.onmessage?.(
|
|
430
|
+
const jsonRpcMessage = await parseJSONRPCMessage(data);
|
|
431
|
+
this.onmessage?.(jsonRpcMessage);
|
|
377
432
|
} catch (error) {
|
|
378
433
|
const e = new MCPClientError({
|
|
379
434
|
message: 'MCP HTTP Transport Error: Failed to parse message',
|