@ai-sdk/mcp 2.0.0-beta.6 → 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 +490 -8
- package/README.md +134 -0
- package/dist/index.d.ts +140 -2
- package/dist/index.js +750 -345
- 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 +72 -24
- package/src/tool/mcp-sse-transport.ts +42 -16
- 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 +21 -3
- 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 -516
- package/dist/index.mjs +0 -2137
- 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
|
*
|
|
@@ -31,6 +41,8 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
31
41
|
private sessionId?: string;
|
|
32
42
|
private inboundSseConnection?: { close: () => void };
|
|
33
43
|
private redirectMode: RequestRedirect;
|
|
44
|
+
private fetchFn: FetchFunction;
|
|
45
|
+
private authPromise?: Promise<AuthResult>;
|
|
34
46
|
|
|
35
47
|
// Inbound SSE resumption and reconnection state
|
|
36
48
|
private lastInboundEventId?: string;
|
|
@@ -45,22 +57,30 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
45
57
|
onclose?: () => void;
|
|
46
58
|
onerror?: (error: unknown) => void;
|
|
47
59
|
onmessage?: (message: JSONRPCMessage) => void;
|
|
60
|
+
protocolVersion?: string;
|
|
48
61
|
|
|
49
62
|
constructor({
|
|
50
63
|
url,
|
|
51
64
|
headers,
|
|
52
65
|
authProvider,
|
|
53
|
-
redirect = '
|
|
66
|
+
redirect = 'error',
|
|
67
|
+
fetch: fetchFn,
|
|
54
68
|
}: {
|
|
55
69
|
url: string;
|
|
56
70
|
headers?: Record<string, string>;
|
|
57
71
|
authProvider?: OAuthClientProvider;
|
|
58
72
|
redirect?: 'follow' | 'error';
|
|
73
|
+
fetch?: FetchFunction;
|
|
59
74
|
}) {
|
|
60
75
|
this.url = new URL(url);
|
|
61
76
|
this.headers = headers;
|
|
62
77
|
this.authProvider = authProvider;
|
|
63
78
|
this.redirectMode = redirect;
|
|
79
|
+
this.fetchFn = fetchFn ?? globalThis.fetch;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
setProtocolVersion(version: string): void {
|
|
83
|
+
this.protocolVersion = version;
|
|
64
84
|
}
|
|
65
85
|
|
|
66
86
|
private async commonHeaders(
|
|
@@ -69,7 +89,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
69
89
|
const headers: Record<string, string> = {
|
|
70
90
|
...this.headers,
|
|
71
91
|
...base,
|
|
72
|
-
'mcp-protocol-version': LATEST_PROTOCOL_VERSION,
|
|
92
|
+
'mcp-protocol-version': this.protocolVersion ?? LATEST_PROTOCOL_VERSION,
|
|
73
93
|
};
|
|
74
94
|
|
|
75
95
|
if (this.sessionId) {
|
|
@@ -90,6 +110,27 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
90
110
|
);
|
|
91
111
|
}
|
|
92
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
|
+
|
|
93
134
|
async start(): Promise<void> {
|
|
94
135
|
if (this.abortController) {
|
|
95
136
|
throw new MCPClientError({
|
|
@@ -111,7 +152,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
111
152
|
!this.abortController.signal.aborted
|
|
112
153
|
) {
|
|
113
154
|
const headers = await this.commonHeaders({});
|
|
114
|
-
await
|
|
155
|
+
await this.fetchFn(this.url.href, {
|
|
115
156
|
method: 'DELETE',
|
|
116
157
|
headers,
|
|
117
158
|
signal: this.abortController.signal,
|
|
@@ -140,7 +181,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
140
181
|
redirect: this.redirectMode,
|
|
141
182
|
} satisfies RequestInit;
|
|
142
183
|
|
|
143
|
-
const response = await
|
|
184
|
+
const response = await this.fetchFn(this.url.href, init);
|
|
144
185
|
|
|
145
186
|
const sessionId = response.headers.get('mcp-session-id');
|
|
146
187
|
if (sessionId) {
|
|
@@ -150,10 +191,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
150
191
|
if (response.status === 401 && this.authProvider && !triedAuth) {
|
|
151
192
|
this.resourceMetadataUrl = extractResourceMetadataUrl(response);
|
|
152
193
|
try {
|
|
153
|
-
const result = await
|
|
154
|
-
serverUrl: this.url,
|
|
155
|
-
resourceMetadataUrl: this.resourceMetadataUrl,
|
|
156
|
-
});
|
|
194
|
+
const result = await this.authorizeOnce(this.resourceMetadataUrl);
|
|
157
195
|
if (result !== 'AUTHORIZED') {
|
|
158
196
|
const error = new UnauthorizedError();
|
|
159
197
|
throw error;
|
|
@@ -187,6 +225,9 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
187
225
|
|
|
188
226
|
const error = new MCPClientError({
|
|
189
227
|
message: errorMessage,
|
|
228
|
+
statusCode: response.status,
|
|
229
|
+
url: this.url.href,
|
|
230
|
+
responseBody: text ?? undefined,
|
|
190
231
|
});
|
|
191
232
|
this.onerror?.(error);
|
|
192
233
|
throw error;
|
|
@@ -203,9 +244,13 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
203
244
|
if (contentType.includes('application/json')) {
|
|
204
245
|
const data = await response.json();
|
|
205
246
|
const messages: JSONRPCMessage[] = Array.isArray(data)
|
|
206
|
-
? data.map((
|
|
247
|
+
? data.map((message: unknown) =>
|
|
248
|
+
JSONRPCMessageSchema.parse(message),
|
|
249
|
+
)
|
|
207
250
|
: [JSONRPCMessageSchema.parse(data)];
|
|
208
|
-
for (const
|
|
251
|
+
for (const jsonRpcMessage of messages) {
|
|
252
|
+
this.onmessage?.(jsonRpcMessage);
|
|
253
|
+
}
|
|
209
254
|
return;
|
|
210
255
|
}
|
|
211
256
|
|
|
@@ -214,6 +259,8 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
214
259
|
const error = new MCPClientError({
|
|
215
260
|
message:
|
|
216
261
|
'MCP HTTP Transport Error: text/event-stream response without body',
|
|
262
|
+
statusCode: response.status,
|
|
263
|
+
url: this.url.href,
|
|
217
264
|
});
|
|
218
265
|
this.onerror?.(error);
|
|
219
266
|
throw error;
|
|
@@ -230,10 +277,10 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
230
277
|
const { done, value } = await reader.read();
|
|
231
278
|
if (done) return;
|
|
232
279
|
const { event, data } = value;
|
|
233
|
-
if (event
|
|
280
|
+
if (isMessageEvent(event)) {
|
|
234
281
|
try {
|
|
235
|
-
const
|
|
236
|
-
this.onmessage?.(
|
|
282
|
+
const jsonRpcMessage = await parseJSONRPCMessage(data);
|
|
283
|
+
this.onmessage?.(jsonRpcMessage);
|
|
237
284
|
} catch (error) {
|
|
238
285
|
const e = new MCPClientError({
|
|
239
286
|
message:
|
|
@@ -258,6 +305,8 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
258
305
|
|
|
259
306
|
const error = new MCPClientError({
|
|
260
307
|
message: `MCP HTTP Transport Error: Unexpected content type: ${contentType}`,
|
|
308
|
+
statusCode: response.status,
|
|
309
|
+
url: this.url.href,
|
|
261
310
|
});
|
|
262
311
|
this.onerror?.(error);
|
|
263
312
|
throw error;
|
|
@@ -314,7 +363,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
314
363
|
headers['last-event-id'] = resumeToken;
|
|
315
364
|
}
|
|
316
365
|
|
|
317
|
-
const response = await
|
|
366
|
+
const response = await this.fetchFn(this.url.href, {
|
|
318
367
|
method: 'GET',
|
|
319
368
|
headers,
|
|
320
369
|
signal: this.abortController?.signal,
|
|
@@ -329,10 +378,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
329
378
|
if (response.status === 401 && this.authProvider && !triedAuth) {
|
|
330
379
|
this.resourceMetadataUrl = extractResourceMetadataUrl(response);
|
|
331
380
|
try {
|
|
332
|
-
const result = await
|
|
333
|
-
serverUrl: this.url,
|
|
334
|
-
resourceMetadataUrl: this.resourceMetadataUrl,
|
|
335
|
-
});
|
|
381
|
+
const result = await this.authorizeOnce(this.resourceMetadataUrl);
|
|
336
382
|
if (result !== 'AUTHORIZED') {
|
|
337
383
|
const error = new UnauthorizedError();
|
|
338
384
|
this.onerror?.(error);
|
|
@@ -352,6 +398,8 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
352
398
|
if (!response.ok || !response.body) {
|
|
353
399
|
const error = new MCPClientError({
|
|
354
400
|
message: `MCP HTTP Transport Error: GET SSE failed: ${response.status} ${response.statusText}`,
|
|
401
|
+
statusCode: response.status,
|
|
402
|
+
url: this.url.href,
|
|
355
403
|
});
|
|
356
404
|
this.onerror?.(error);
|
|
357
405
|
return;
|
|
@@ -377,10 +425,10 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
377
425
|
this.lastInboundEventId = id;
|
|
378
426
|
}
|
|
379
427
|
|
|
380
|
-
if (event
|
|
428
|
+
if (isMessageEvent(event)) {
|
|
381
429
|
try {
|
|
382
|
-
const
|
|
383
|
-
this.onmessage?.(
|
|
430
|
+
const jsonRpcMessage = await parseJSONRPCMessage(data);
|
|
431
|
+
this.onmessage?.(jsonRpcMessage);
|
|
384
432
|
} catch (error) {
|
|
385
433
|
const e = new MCPClientError({
|
|
386
434
|
message: 'MCP HTTP Transport Error: Failed to parse message',
|