@ai-sdk/mcp 2.0.0-beta.9 → 2.0.0-canary.39
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 +253 -8
- package/dist/index.d.ts +27 -1
- package/dist/index.js +297 -282
- package/dist/index.js.map +1 -1
- package/dist/mcp-stdio/index.js +149 -170
- package/dist/mcp-stdio/index.js.map +1 -1
- package/package.json +14 -15
- package/src/index.ts +1 -0
- package/src/tool/json-rpc-message.ts +7 -0
- package/src/tool/mcp-client.ts +71 -33
- package/src/tool/mcp-http-transport.ts +19 -8
- package/src/tool/mcp-sse-transport.ts +17 -10
- 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 +10 -2
- package/src/tool/mock-mcp-transport.ts +6 -8
- package/src/tool/oauth.ts +17 -12
- package/src/tool/types.ts +4 -2
- 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/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/mcp",
|
|
3
|
-
"version": "2.0.0-
|
|
3
|
+
"version": "2.0.0-canary.39",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"license": "Apache-2.0",
|
|
5
6
|
"sideEffects": false,
|
|
6
7
|
"main": "./dist/index.js",
|
|
7
|
-
"module": "./dist/index.mjs",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"files": [
|
|
10
10
|
"dist/**/*",
|
|
@@ -21,28 +21,27 @@
|
|
|
21
21
|
"./package.json": "./package.json",
|
|
22
22
|
".": {
|
|
23
23
|
"types": "./dist/index.d.ts",
|
|
24
|
-
"import": "./dist/index.
|
|
25
|
-
"
|
|
24
|
+
"import": "./dist/index.js",
|
|
25
|
+
"default": "./dist/index.js"
|
|
26
26
|
},
|
|
27
27
|
"./mcp-stdio": {
|
|
28
28
|
"types": "./dist/mcp-stdio/index.d.ts",
|
|
29
|
-
"import": "./dist/mcp-stdio/index.
|
|
30
|
-
"
|
|
31
|
-
"require": "./dist/mcp-stdio/index.js"
|
|
29
|
+
"import": "./dist/mcp-stdio/index.js",
|
|
30
|
+
"default": "./dist/mcp-stdio/index.js"
|
|
32
31
|
}
|
|
33
32
|
},
|
|
34
33
|
"dependencies": {
|
|
35
34
|
"pkce-challenge": "^5.0.0",
|
|
36
|
-
"@ai-sdk/provider": "4.0.0-
|
|
37
|
-
"@ai-sdk/provider-utils": "5.0.0-
|
|
35
|
+
"@ai-sdk/provider": "4.0.0-canary.16",
|
|
36
|
+
"@ai-sdk/provider-utils": "5.0.0-canary.32"
|
|
38
37
|
},
|
|
39
38
|
"devDependencies": {
|
|
40
39
|
"@types/node": "20.17.24",
|
|
41
40
|
"tsup": "^8",
|
|
42
41
|
"typescript": "5.8.3",
|
|
43
|
-
"vitest": "^4.1.
|
|
42
|
+
"vitest": "^4.1.5",
|
|
44
43
|
"zod": "3.25.76",
|
|
45
|
-
"@ai-sdk/test-server": "2.0.0-
|
|
44
|
+
"@ai-sdk/test-server": "2.0.0-canary.4",
|
|
46
45
|
"@vercel/ai-tsconfig": "0.0.0"
|
|
47
46
|
},
|
|
48
47
|
"peerDependencies": {
|
|
@@ -52,12 +51,14 @@
|
|
|
52
51
|
"node": ">=18"
|
|
53
52
|
},
|
|
54
53
|
"publishConfig": {
|
|
55
|
-
"access": "public"
|
|
54
|
+
"access": "public",
|
|
55
|
+
"provenance": true
|
|
56
56
|
},
|
|
57
57
|
"homepage": "https://ai-sdk.dev/docs",
|
|
58
58
|
"repository": {
|
|
59
59
|
"type": "git",
|
|
60
|
-
"url": "
|
|
60
|
+
"url": "https://github.com/vercel/ai",
|
|
61
|
+
"directory": "packages/mcp"
|
|
61
62
|
},
|
|
62
63
|
"bugs": {
|
|
63
64
|
"url": "https://github.com/vercel/ai/issues"
|
|
@@ -70,9 +71,7 @@
|
|
|
70
71
|
"build": "pnpm clean && tsup --tsconfig tsconfig.build.json",
|
|
71
72
|
"build:watch": "pnpm clean && tsup --watch",
|
|
72
73
|
"clean": "rm -rf dist *.tsbuildinfo",
|
|
73
|
-
"lint": "eslint \"./**/*.ts*\"",
|
|
74
74
|
"type-check": "tsc --build",
|
|
75
|
-
"prettier-check": "prettier --check \"./**/*.ts*\"",
|
|
76
75
|
"test": "pnpm test:node && pnpm test:edge",
|
|
77
76
|
"test:update": "pnpm test:node -u",
|
|
78
77
|
"test:watch": "vitest --config vitest.node.config.js",
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseJSON } from '@ai-sdk/provider-utils';
|
|
1
2
|
import { z } from 'zod/v4';
|
|
2
3
|
import { BaseParamsSchema, RequestSchema, ResultSchema } from './types';
|
|
3
4
|
|
|
@@ -59,3 +60,9 @@ export const JSONRPCMessageSchema = z.union([
|
|
|
59
60
|
]);
|
|
60
61
|
|
|
61
62
|
export type JSONRPCMessage = z.infer<typeof JSONRPCMessageSchema>;
|
|
63
|
+
|
|
64
|
+
export async function parseJSONRPCMessage(
|
|
65
|
+
text: string,
|
|
66
|
+
): Promise<JSONRPCMessage> {
|
|
67
|
+
return JSONRPCMessageSchema.parse(await parseJSON({ text }));
|
|
68
|
+
}
|
package/src/tool/mcp-client.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { JSONSchema7, JSONValue } from '@ai-sdk/provider';
|
|
1
|
+
import type { 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,43 @@ 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
28
|
import {
|
|
29
|
-
CallToolResult,
|
|
30
29
|
CallToolResultSchema,
|
|
31
|
-
ClientCapabilities,
|
|
32
|
-
Configuration as ClientConfiguration,
|
|
33
|
-
ElicitationRequest,
|
|
34
30
|
ElicitationRequestSchema,
|
|
35
|
-
ElicitResult,
|
|
36
31
|
ElicitResultSchema,
|
|
37
32
|
InitializeResultSchema,
|
|
38
33
|
LATEST_PROTOCOL_VERSION,
|
|
39
|
-
ListResourceTemplatesResult,
|
|
40
34
|
ListResourceTemplatesResultSchema,
|
|
41
|
-
ListResourcesResult,
|
|
42
35
|
ListResourcesResultSchema,
|
|
43
|
-
ListPromptsResult,
|
|
44
36
|
ListPromptsResultSchema,
|
|
45
|
-
ListToolsResult,
|
|
46
37
|
ListToolsResultSchema,
|
|
47
|
-
McpToolSet,
|
|
48
|
-
Notification,
|
|
49
|
-
PaginatedRequest,
|
|
50
|
-
ReadResourceResult,
|
|
51
38
|
ReadResourceResultSchema,
|
|
52
|
-
GetPromptResult,
|
|
53
39
|
GetPromptResultSchema,
|
|
54
|
-
Request,
|
|
55
|
-
RequestOptions,
|
|
56
|
-
ServerCapabilities,
|
|
57
40
|
SUPPORTED_PROTOCOL_VERSIONS,
|
|
58
|
-
|
|
59
|
-
|
|
41
|
+
type CallToolResult,
|
|
42
|
+
type ClientCapabilities,
|
|
43
|
+
type Configuration,
|
|
44
|
+
type Configuration as ClientConfiguration,
|
|
45
|
+
type ElicitationRequest,
|
|
46
|
+
type ElicitResult,
|
|
47
|
+
type ListResourceTemplatesResult,
|
|
48
|
+
type ListResourcesResult,
|
|
49
|
+
type ListPromptsResult,
|
|
50
|
+
type ListToolsResult,
|
|
51
|
+
type McpToolSet,
|
|
52
|
+
type Notification,
|
|
53
|
+
type PaginatedRequest,
|
|
54
|
+
type ReadResourceResult,
|
|
55
|
+
type GetPromptResult,
|
|
56
|
+
type Request,
|
|
57
|
+
type RequestOptions,
|
|
58
|
+
type ServerCapabilities,
|
|
59
|
+
type ToolSchemas,
|
|
60
|
+
type ToolMeta,
|
|
60
61
|
} from './types';
|
|
61
|
-
|
|
62
62
|
const CLIENT_VERSION = '1.0.0';
|
|
63
63
|
|
|
64
64
|
function mcpToModelOutput({
|
|
@@ -81,9 +81,9 @@ function mcpToModelOutput({
|
|
|
81
81
|
}
|
|
82
82
|
if (part.type === 'image' && 'data' in part && 'mimeType' in part) {
|
|
83
83
|
return {
|
|
84
|
-
type: '
|
|
85
|
-
data: part.data as string,
|
|
84
|
+
type: 'file' as const,
|
|
86
85
|
mediaType: part.mimeType as string,
|
|
86
|
+
data: { type: 'data' as const, data: part.data as string },
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
89
|
return { type: 'text' as const, text: JSON.stringify(part) };
|
|
@@ -119,6 +119,22 @@ export async function createMCPClient(
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
export interface MCPClient {
|
|
122
|
+
/**
|
|
123
|
+
* Information about the connected MCP server, as reported during initialization.
|
|
124
|
+
* @see https://modelcontextprotocol.io/specification/2025-11-25/schema#implementation
|
|
125
|
+
*/
|
|
126
|
+
readonly serverInfo: Configuration;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Optional instructions provided by the server during the initialize handshake.
|
|
130
|
+
*
|
|
131
|
+
* These describe how to use the server and its features, and can be used by clients
|
|
132
|
+
* to improve LLM interactions (e.g. by including them in the system prompt).
|
|
133
|
+
*
|
|
134
|
+
* @see https://modelcontextprotocol.io/specification/2025-11-25/schema#initializeresult
|
|
135
|
+
*/
|
|
136
|
+
readonly instructions?: string;
|
|
137
|
+
|
|
122
138
|
tools<TOOL_SCHEMAS extends ToolSchemas = 'automatic'>(options?: {
|
|
123
139
|
schemas?: TOOL_SCHEMAS;
|
|
124
140
|
}): Promise<McpToolSet<TOOL_SCHEMAS>>;
|
|
@@ -201,6 +217,8 @@ class DefaultMCPClient implements MCPClient {
|
|
|
201
217
|
(response: JSONRPCResponse | Error) => void
|
|
202
218
|
> = new Map();
|
|
203
219
|
private serverCapabilities: ServerCapabilities = {};
|
|
220
|
+
private _serverInfo: Configuration = { name: '', version: '' };
|
|
221
|
+
private _serverInstructions?: string;
|
|
204
222
|
private isClosed = true;
|
|
205
223
|
private elicitationRequestHandler?: (
|
|
206
224
|
request: ElicitationRequest,
|
|
@@ -247,6 +265,14 @@ class DefaultMCPClient implements MCPClient {
|
|
|
247
265
|
};
|
|
248
266
|
}
|
|
249
267
|
|
|
268
|
+
get serverInfo(): Configuration {
|
|
269
|
+
return this._serverInfo;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
get instructions(): string | undefined {
|
|
273
|
+
return this._serverInstructions;
|
|
274
|
+
}
|
|
275
|
+
|
|
250
276
|
async init(): Promise<this> {
|
|
251
277
|
try {
|
|
252
278
|
await this.transport.start();
|
|
@@ -277,6 +303,8 @@ class DefaultMCPClient implements MCPClient {
|
|
|
277
303
|
}
|
|
278
304
|
|
|
279
305
|
this.serverCapabilities = result.capabilities;
|
|
306
|
+
this._serverInfo = result.serverInfo;
|
|
307
|
+
this._serverInstructions = result.instructions;
|
|
280
308
|
|
|
281
309
|
// Complete initialization handshake:
|
|
282
310
|
await this.notification({
|
|
@@ -420,7 +448,7 @@ class DefaultMCPClient implements MCPClient {
|
|
|
420
448
|
}: {
|
|
421
449
|
name: string;
|
|
422
450
|
args: Record<string, unknown>;
|
|
423
|
-
options?: ToolExecutionOptions
|
|
451
|
+
options?: ToolExecutionOptions<{}>;
|
|
424
452
|
}): Promise<CallToolResult> {
|
|
425
453
|
try {
|
|
426
454
|
return this.request({
|
|
@@ -579,11 +607,15 @@ class DefaultMCPClient implements MCPClient {
|
|
|
579
607
|
|
|
580
608
|
const execute = async (
|
|
581
609
|
args: any,
|
|
582
|
-
options: ToolExecutionOptions
|
|
610
|
+
options: ToolExecutionOptions<{}>,
|
|
583
611
|
): Promise<unknown> => {
|
|
584
612
|
options?.abortSignal?.throwIfAborted();
|
|
585
613
|
const result = await self.callTool({ name, args, options });
|
|
586
614
|
|
|
615
|
+
if (result.isError) {
|
|
616
|
+
return result;
|
|
617
|
+
}
|
|
618
|
+
|
|
587
619
|
if (outputSchema != null) {
|
|
588
620
|
return self.extractStructuredContent(result, outputSchema, name);
|
|
589
621
|
}
|
|
@@ -596,6 +628,9 @@ class DefaultMCPClient implements MCPClient {
|
|
|
596
628
|
? dynamicTool({
|
|
597
629
|
description,
|
|
598
630
|
title: resolvedTitle,
|
|
631
|
+
providerMetadata: {
|
|
632
|
+
mcp: { name: this.clientInfo.name },
|
|
633
|
+
},
|
|
599
634
|
inputSchema: jsonSchema({
|
|
600
635
|
...inputSchema,
|
|
601
636
|
properties: inputSchema.properties ?? {},
|
|
@@ -607,6 +642,9 @@ class DefaultMCPClient implements MCPClient {
|
|
|
607
642
|
: tool({
|
|
608
643
|
description,
|
|
609
644
|
title: resolvedTitle,
|
|
645
|
+
providerMetadata: {
|
|
646
|
+
mcp: { name: this.clientInfo.name },
|
|
647
|
+
},
|
|
610
648
|
inputSchema: schemas[name].inputSchema,
|
|
611
649
|
...(outputSchema != null ? { outputSchema } : {}),
|
|
612
650
|
execute,
|
|
@@ -2,16 +2,21 @@ 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 OAuthClientProvider,
|
|
15
20
|
} from './oauth';
|
|
16
21
|
import { LATEST_PROTOCOL_VERSION } from './types';
|
|
17
22
|
|
|
@@ -31,6 +36,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
31
36
|
private sessionId?: string;
|
|
32
37
|
private inboundSseConnection?: { close: () => void };
|
|
33
38
|
private redirectMode: RequestRedirect;
|
|
39
|
+
private fetchFn: FetchFunction;
|
|
34
40
|
|
|
35
41
|
// Inbound SSE resumption and reconnection state
|
|
36
42
|
private lastInboundEventId?: string;
|
|
@@ -51,16 +57,19 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
51
57
|
headers,
|
|
52
58
|
authProvider,
|
|
53
59
|
redirect = 'error',
|
|
60
|
+
fetch: fetchFn,
|
|
54
61
|
}: {
|
|
55
62
|
url: string;
|
|
56
63
|
headers?: Record<string, string>;
|
|
57
64
|
authProvider?: OAuthClientProvider;
|
|
58
65
|
redirect?: 'follow' | 'error';
|
|
66
|
+
fetch?: FetchFunction;
|
|
59
67
|
}) {
|
|
60
68
|
this.url = new URL(url);
|
|
61
69
|
this.headers = headers;
|
|
62
70
|
this.authProvider = authProvider;
|
|
63
71
|
this.redirectMode = redirect;
|
|
72
|
+
this.fetchFn = fetchFn ?? globalThis.fetch;
|
|
64
73
|
}
|
|
65
74
|
|
|
66
75
|
private async commonHeaders(
|
|
@@ -111,7 +120,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
111
120
|
!this.abortController.signal.aborted
|
|
112
121
|
) {
|
|
113
122
|
const headers = await this.commonHeaders({});
|
|
114
|
-
await
|
|
123
|
+
await this.fetchFn(this.url.href, {
|
|
115
124
|
method: 'DELETE',
|
|
116
125
|
headers,
|
|
117
126
|
signal: this.abortController.signal,
|
|
@@ -140,7 +149,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
140
149
|
redirect: this.redirectMode,
|
|
141
150
|
} satisfies RequestInit;
|
|
142
151
|
|
|
143
|
-
const response = await
|
|
152
|
+
const response = await this.fetchFn(this.url.href, init);
|
|
144
153
|
|
|
145
154
|
const sessionId = response.headers.get('mcp-session-id');
|
|
146
155
|
if (sessionId) {
|
|
@@ -153,6 +162,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
153
162
|
const result = await auth(this.authProvider, {
|
|
154
163
|
serverUrl: this.url,
|
|
155
164
|
resourceMetadataUrl: this.resourceMetadataUrl,
|
|
165
|
+
fetchFn: this.fetchFn,
|
|
156
166
|
});
|
|
157
167
|
if (result !== 'AUTHORIZED') {
|
|
158
168
|
const error = new UnauthorizedError();
|
|
@@ -232,7 +242,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
232
242
|
const { event, data } = value;
|
|
233
243
|
if (event === 'message') {
|
|
234
244
|
try {
|
|
235
|
-
const msg =
|
|
245
|
+
const msg = await parseJSONRPCMessage(data);
|
|
236
246
|
this.onmessage?.(msg);
|
|
237
247
|
} catch (error) {
|
|
238
248
|
const e = new MCPClientError({
|
|
@@ -314,7 +324,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
314
324
|
headers['last-event-id'] = resumeToken;
|
|
315
325
|
}
|
|
316
326
|
|
|
317
|
-
const response = await
|
|
327
|
+
const response = await this.fetchFn(this.url.href, {
|
|
318
328
|
method: 'GET',
|
|
319
329
|
headers,
|
|
320
330
|
signal: this.abortController?.signal,
|
|
@@ -332,6 +342,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
332
342
|
const result = await auth(this.authProvider, {
|
|
333
343
|
serverUrl: this.url,
|
|
334
344
|
resourceMetadataUrl: this.resourceMetadataUrl,
|
|
345
|
+
fetchFn: this.fetchFn,
|
|
335
346
|
});
|
|
336
347
|
if (result !== 'AUTHORIZED') {
|
|
337
348
|
const error = new UnauthorizedError();
|
|
@@ -379,7 +390,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
379
390
|
|
|
380
391
|
if (event === 'message') {
|
|
381
392
|
try {
|
|
382
|
-
const msg =
|
|
393
|
+
const msg = await parseJSONRPCMessage(data);
|
|
383
394
|
this.onmessage?.(msg);
|
|
384
395
|
} catch (error) {
|
|
385
396
|
const e = new MCPClientError({
|
|
@@ -2,16 +2,17 @@ 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
|
-
import { MCPTransport } from './mcp-transport';
|
|
8
|
+
import { parseJSONRPCMessage, type JSONRPCMessage } from './json-rpc-message';
|
|
9
|
+
import type { MCPTransport } from './mcp-transport';
|
|
9
10
|
import { VERSION } from '../version';
|
|
10
11
|
import {
|
|
11
|
-
OAuthClientProvider,
|
|
12
12
|
extractResourceMetadataUrl,
|
|
13
13
|
UnauthorizedError,
|
|
14
14
|
auth,
|
|
15
|
+
type OAuthClientProvider,
|
|
15
16
|
} from './oauth';
|
|
16
17
|
import { LATEST_PROTOCOL_VERSION } from './types';
|
|
17
18
|
|
|
@@ -27,6 +28,7 @@ export class SseMCPTransport implements MCPTransport {
|
|
|
27
28
|
private authProvider?: OAuthClientProvider;
|
|
28
29
|
private resourceMetadataUrl?: URL;
|
|
29
30
|
private redirectMode: RequestRedirect;
|
|
31
|
+
private fetchFn: FetchFunction;
|
|
30
32
|
|
|
31
33
|
onclose?: () => void;
|
|
32
34
|
onerror?: (error: unknown) => void;
|
|
@@ -37,16 +39,19 @@ export class SseMCPTransport implements MCPTransport {
|
|
|
37
39
|
headers,
|
|
38
40
|
authProvider,
|
|
39
41
|
redirect = 'error',
|
|
42
|
+
fetch: fetchFn,
|
|
40
43
|
}: {
|
|
41
44
|
url: string;
|
|
42
45
|
headers?: Record<string, string>;
|
|
43
46
|
authProvider?: OAuthClientProvider;
|
|
44
47
|
redirect?: 'follow' | 'error';
|
|
48
|
+
fetch?: FetchFunction;
|
|
45
49
|
}) {
|
|
46
50
|
this.url = new URL(url);
|
|
47
51
|
this.headers = headers;
|
|
48
52
|
this.authProvider = authProvider;
|
|
49
53
|
this.redirectMode = redirect;
|
|
54
|
+
this.fetchFn = fetchFn ?? globalThis.fetch;
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
private async commonHeaders(
|
|
@@ -85,7 +90,7 @@ export class SseMCPTransport implements MCPTransport {
|
|
|
85
90
|
const headers = await this.commonHeaders({
|
|
86
91
|
Accept: 'text/event-stream',
|
|
87
92
|
});
|
|
88
|
-
const response = await
|
|
93
|
+
const response = await this.fetchFn(this.url.href, {
|
|
89
94
|
headers,
|
|
90
95
|
signal: this.abortController?.signal,
|
|
91
96
|
redirect: this.redirectMode,
|
|
@@ -97,6 +102,7 @@ export class SseMCPTransport implements MCPTransport {
|
|
|
97
102
|
const result = await auth(this.authProvider, {
|
|
98
103
|
serverUrl: this.url,
|
|
99
104
|
resourceMetadataUrl: this.resourceMetadataUrl,
|
|
105
|
+
fetchFn: this.fetchFn,
|
|
100
106
|
});
|
|
101
107
|
if (result !== 'AUTHORIZED') {
|
|
102
108
|
const error = new UnauthorizedError();
|
|
@@ -162,9 +168,7 @@ export class SseMCPTransport implements MCPTransport {
|
|
|
162
168
|
resolve();
|
|
163
169
|
} else if (event === 'message') {
|
|
164
170
|
try {
|
|
165
|
-
const message =
|
|
166
|
-
JSON.parse(data),
|
|
167
|
-
);
|
|
171
|
+
const message = await parseJSONRPCMessage(data);
|
|
168
172
|
this.onmessage?.(message);
|
|
169
173
|
} catch (error) {
|
|
170
174
|
const e = new MCPClientError({
|
|
@@ -235,7 +239,7 @@ export class SseMCPTransport implements MCPTransport {
|
|
|
235
239
|
redirect: this.redirectMode,
|
|
236
240
|
};
|
|
237
241
|
|
|
238
|
-
const response = await
|
|
242
|
+
const response = await this.fetchFn(endpoint.href, init);
|
|
239
243
|
|
|
240
244
|
if (response.status === 401 && this.authProvider && !triedAuth) {
|
|
241
245
|
this.resourceMetadataUrl = extractResourceMetadataUrl(response);
|
|
@@ -243,6 +247,7 @@ export class SseMCPTransport implements MCPTransport {
|
|
|
243
247
|
const result = await auth(this.authProvider, {
|
|
244
248
|
serverUrl: this.url,
|
|
245
249
|
resourceMetadataUrl: this.resourceMetadataUrl,
|
|
250
|
+
fetchFn: this.fetchFn,
|
|
246
251
|
});
|
|
247
252
|
if (result !== 'AUTHORIZED') {
|
|
248
253
|
const error = new UnauthorizedError();
|
|
@@ -273,6 +278,8 @@ export class SseMCPTransport implements MCPTransport {
|
|
|
273
278
|
}
|
|
274
279
|
}
|
|
275
280
|
|
|
276
|
-
export function deserializeMessage(
|
|
277
|
-
|
|
281
|
+
export async function deserializeMessage(
|
|
282
|
+
line: string,
|
|
283
|
+
): Promise<JSONRPCMessage> {
|
|
284
|
+
return parseJSONRPCMessage(line);
|
|
278
285
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn, type ChildProcess } from 'node:child_process';
|
|
2
2
|
import { getEnvironment } from './get-environment';
|
|
3
|
-
import { StdioConfig } from './mcp-stdio-transport';
|
|
3
|
+
import type { StdioConfig } from './mcp-stdio-transport';
|
|
4
4
|
|
|
5
5
|
export function createChildProcess(
|
|
6
6
|
config: StdioConfig,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ChildProcess, IOType } from 'node:child_process';
|
|
2
|
-
import { Stream } from 'node:stream';
|
|
3
|
-
import {
|
|
4
|
-
import { MCPTransport } from '../mcp-transport';
|
|
2
|
+
import type { Stream } from 'node:stream';
|
|
3
|
+
import { parseJSONRPCMessage, type JSONRPCMessage } from '../json-rpc-message';
|
|
4
|
+
import type { MCPTransport } from '../mcp-transport';
|
|
5
5
|
import { MCPClientError } from '../../error/mcp-client-error';
|
|
6
6
|
import { createChildProcess } from './create-child-process';
|
|
7
7
|
|
|
@@ -68,7 +68,7 @@ export class StdioMCPTransport implements MCPTransport {
|
|
|
68
68
|
|
|
69
69
|
this.process.stdout?.on('data', chunk => {
|
|
70
70
|
this.readBuffer.append(chunk);
|
|
71
|
-
this.processReadBuffer();
|
|
71
|
+
void this.processReadBuffer();
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
this.process.stdout?.on('error', error => {
|
|
@@ -81,14 +81,15 @@ export class StdioMCPTransport implements MCPTransport {
|
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
private processReadBuffer() {
|
|
84
|
+
private async processReadBuffer() {
|
|
85
85
|
while (true) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
86
|
+
const line = this.readBuffer.readLine();
|
|
87
|
+
if (line === null) {
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
91
90
|
|
|
91
|
+
try {
|
|
92
|
+
const message = await deserializeMessage(line);
|
|
92
93
|
this.onmessage?.(message);
|
|
93
94
|
} catch (error) {
|
|
94
95
|
this.onerror?.(error as Error);
|
|
@@ -127,7 +128,7 @@ class ReadBuffer {
|
|
|
127
128
|
this.buffer = this.buffer ? Buffer.concat([this.buffer, chunk]) : chunk;
|
|
128
129
|
}
|
|
129
130
|
|
|
130
|
-
|
|
131
|
+
readLine(): string | null {
|
|
131
132
|
if (!this.buffer) return null;
|
|
132
133
|
|
|
133
134
|
const index = this.buffer.indexOf('\n');
|
|
@@ -137,7 +138,7 @@ class ReadBuffer {
|
|
|
137
138
|
|
|
138
139
|
const line = this.buffer.toString('utf8', 0, index);
|
|
139
140
|
this.buffer = this.buffer.subarray(index + 1);
|
|
140
|
-
return
|
|
141
|
+
return line;
|
|
141
142
|
}
|
|
142
143
|
|
|
143
144
|
clear(): void {
|
|
@@ -149,6 +150,8 @@ function serializeMessage(message: JSONRPCMessage): string {
|
|
|
149
150
|
return JSON.stringify(message) + '\n';
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
export function deserializeMessage(
|
|
153
|
-
|
|
153
|
+
export async function deserializeMessage(
|
|
154
|
+
line: string,
|
|
155
|
+
): Promise<JSONRPCMessage> {
|
|
156
|
+
return parseJSONRPCMessage(line);
|
|
154
157
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { FetchFunction } from '@ai-sdk/provider-utils';
|
|
1
2
|
import { MCPClientError } from '../error/mcp-client-error';
|
|
2
|
-
import { JSONRPCMessage } from './json-rpc-message';
|
|
3
|
+
import type { JSONRPCMessage } from './json-rpc-message';
|
|
3
4
|
import { SseMCPTransport } from './mcp-sse-transport';
|
|
4
5
|
import { HttpMCPTransport } from './mcp-http-transport';
|
|
5
|
-
import { OAuthClientProvider } from './oauth';
|
|
6
|
+
import type { OAuthClientProvider } from './oauth';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Transport interface for MCP (Model Context Protocol) communication.
|
|
@@ -66,6 +67,13 @@ export type MCPTransportConfig = {
|
|
|
66
67
|
* @default 'error'
|
|
67
68
|
*/
|
|
68
69
|
redirect?: 'follow' | 'error';
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Optional custom fetch implementation to use for HTTP requests.
|
|
73
|
+
* Useful for runtimes that need a request-local fetch.
|
|
74
|
+
* @default globalThis.fetch
|
|
75
|
+
*/
|
|
76
|
+
fetch?: FetchFunction;
|
|
69
77
|
};
|
|
70
78
|
|
|
71
79
|
export function createMcpTransport(config: MCPTransportConfig): MCPTransport {
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { delay } from '@ai-sdk/provider-utils';
|
|
2
|
-
import { JSONRPCMessage } from './json-rpc-message';
|
|
3
|
-
import { MCPTransport } from './mcp-transport';
|
|
2
|
+
import type { JSONRPCMessage } from './json-rpc-message';
|
|
3
|
+
import type { MCPTransport } from './mcp-transport';
|
|
4
4
|
import {
|
|
5
|
-
MCPTool,
|
|
6
|
-
MCPResource,
|
|
7
|
-
MCPPrompt,
|
|
8
|
-
GetPromptResult,
|
|
9
|
-
CallToolResult,
|
|
10
5
|
LATEST_PROTOCOL_VERSION,
|
|
6
|
+
type MCPTool,
|
|
7
|
+
type MCPResource,
|
|
8
|
+
type MCPPrompt,
|
|
9
|
+
type CallToolResult,
|
|
11
10
|
} from './types';
|
|
12
|
-
|
|
13
11
|
const DEFAULT_TOOLS: MCPTool[] = [
|
|
14
12
|
{
|
|
15
13
|
name: 'mock-tool',
|