@ai-sdk/mcp 1.0.42 → 1.0.44
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 +21 -0
- package/README.md +134 -0
- package/dist/index.js +40 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +40 -15
- package/dist/index.mjs.map +1 -1
- package/dist/mcp-stdio/index.js +7 -1
- package/dist/mcp-stdio/index.js.map +1 -1
- package/dist/mcp-stdio/index.mjs +7 -1
- package/dist/mcp-stdio/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/error/mcp-client-error.ts +40 -0
- package/src/tool/mcp-http-transport.ts +34 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @ai-sdk/mcp
|
|
2
2
|
|
|
3
|
+
## 1.0.44
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 77775a4: feat(mcp): expose `statusCode`, `url`, and `responseBody` on `MCPClientError` for HTTP transport failures
|
|
8
|
+
|
|
9
|
+
`MCPClientError` now carries structured HTTP context when it originates from the
|
|
10
|
+
streamable HTTP transport. This lets downstream consumers (e.g. agent frameworks
|
|
11
|
+
that need to decide whether to fall back from streamable HTTP to legacy SSE
|
|
12
|
+
transport per the MCP spec) branch on the actual response status without parsing
|
|
13
|
+
the error message string.
|
|
14
|
+
|
|
15
|
+
Fields are optional — they remain `undefined` for stdio transport errors and for
|
|
16
|
+
non-response failures (network errors, aborts).
|
|
17
|
+
|
|
18
|
+
## 1.0.43
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- e2b923f: fix(mcp): deduplicate auth refresh on http transport
|
|
23
|
+
|
|
3
24
|
## 1.0.42
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# AI SDK - Model Context Protocol Client
|
|
2
|
+
|
|
3
|
+
The **Model Context Protocol (MCP) client** for the
|
|
4
|
+
[AI SDK](https://ai-sdk.dev/docs) lets you connect to MCP servers and use their
|
|
5
|
+
tools with AI SDK functions like `generateText` and `streamText`.
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
The MCP client is available in the `@ai-sdk/mcp` module. You can install it with
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i @ai-sdk/mcp ai zod
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Skill for Coding Agents
|
|
16
|
+
|
|
17
|
+
If you use coding agents such as Claude Code or Cursor, we highly recommend
|
|
18
|
+
adding the AI SDK skill to your repository:
|
|
19
|
+
|
|
20
|
+
```shell
|
|
21
|
+
npx skills add vercel/ai
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
Create an MCP client with `createMCPClient()`, fetch the server tools with
|
|
27
|
+
`mcpClient.tools()`, and pass them to an AI SDK call:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { createMCPClient } from '@ai-sdk/mcp';
|
|
31
|
+
import { generateText, isStepCount } from 'ai';
|
|
32
|
+
|
|
33
|
+
const mcpClient = await createMCPClient({
|
|
34
|
+
transport: {
|
|
35
|
+
type: 'http',
|
|
36
|
+
url: 'https://your-server.com/mcp',
|
|
37
|
+
headers: {
|
|
38
|
+
Authorization: `Bearer ${process.env.MCP_API_KEY}`,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const tools = await mcpClient.tools();
|
|
45
|
+
|
|
46
|
+
const { text } = await generateText({
|
|
47
|
+
model: 'openai/gpt-5.4',
|
|
48
|
+
tools,
|
|
49
|
+
stopWhen: isStepCount(10),
|
|
50
|
+
prompt: 'Use the available tools to answer the user question.',
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
console.log(text);
|
|
54
|
+
} finally {
|
|
55
|
+
await mcpClient.close();
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The client converts MCP tool definitions into AI SDK tools, so model calls can
|
|
60
|
+
use them through the standard `tools` option.
|
|
61
|
+
|
|
62
|
+
For streaming responses, close the MCP client when the stream finishes:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import { createMCPClient } from '@ai-sdk/mcp';
|
|
66
|
+
import { streamText } from 'ai';
|
|
67
|
+
|
|
68
|
+
const mcpClient = await createMCPClient({
|
|
69
|
+
transport: {
|
|
70
|
+
type: 'http',
|
|
71
|
+
url: 'https://your-server.com/mcp',
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const result = streamText({
|
|
76
|
+
model: 'openai/gpt-5.4',
|
|
77
|
+
tools: await mcpClient.tools(),
|
|
78
|
+
prompt: 'Use the available tools to answer the user question.',
|
|
79
|
+
onFinish: async () => {
|
|
80
|
+
await mcpClient.close();
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
for await (const textPart of result.textStream) {
|
|
85
|
+
process.stdout.write(textPart);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Transports
|
|
90
|
+
|
|
91
|
+
HTTP is recommended for production deployments:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { createMCPClient } from '@ai-sdk/mcp';
|
|
95
|
+
|
|
96
|
+
const mcpClient = await createMCPClient({
|
|
97
|
+
transport: {
|
|
98
|
+
type: 'http',
|
|
99
|
+
url: 'https://your-server.com/mcp',
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
SSE is also supported for MCP servers that use Server-Sent Events:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
const mcpClient = await createMCPClient({
|
|
108
|
+
transport: {
|
|
109
|
+
type: 'sse',
|
|
110
|
+
url: 'https://your-server.com/sse',
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
For local MCP servers, you can use stdio transport from the `@ai-sdk/mcp/mcp-stdio`
|
|
116
|
+
subpath:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { createMCPClient } from '@ai-sdk/mcp';
|
|
120
|
+
import { Experimental_StdioMCPTransport } from '@ai-sdk/mcp/mcp-stdio';
|
|
121
|
+
|
|
122
|
+
const mcpClient = await createMCPClient({
|
|
123
|
+
transport: new Experimental_StdioMCPTransport({
|
|
124
|
+
command: 'node',
|
|
125
|
+
args: ['server.js'],
|
|
126
|
+
}),
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Documentation
|
|
131
|
+
|
|
132
|
+
Please check out the
|
|
133
|
+
[AI SDK MCP documentation](https://ai-sdk.dev/docs/ai-sdk-core/mcp-tools) for
|
|
134
|
+
more information.
|
package/dist/index.js
CHANGED
|
@@ -54,12 +54,18 @@ var MCPClientError = class extends (_b = import_provider.AISDKError, _a = symbol
|
|
|
54
54
|
message,
|
|
55
55
|
cause,
|
|
56
56
|
data,
|
|
57
|
-
code
|
|
57
|
+
code,
|
|
58
|
+
statusCode,
|
|
59
|
+
url,
|
|
60
|
+
responseBody
|
|
58
61
|
}) {
|
|
59
62
|
super({ name: name3, message, cause });
|
|
60
63
|
this[_a] = true;
|
|
61
64
|
this.data = data;
|
|
62
65
|
this.code = code;
|
|
66
|
+
this.statusCode = statusCode;
|
|
67
|
+
this.url = url;
|
|
68
|
+
this.responseBody = responseBody;
|
|
63
69
|
}
|
|
64
70
|
static isInstance(error) {
|
|
65
71
|
return import_provider.AISDKError.hasMarker(error, marker);
|
|
@@ -1351,6 +1357,24 @@ var HttpMCPTransport = class {
|
|
|
1351
1357
|
(0, import_provider_utils4.getRuntimeEnvironmentUserAgent)()
|
|
1352
1358
|
);
|
|
1353
1359
|
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Runs a single OAuth recovery flow for concurrent 401 responses.
|
|
1362
|
+
*/
|
|
1363
|
+
authorizeOnce(resourceMetadataUrl) {
|
|
1364
|
+
if (!this.authProvider) {
|
|
1365
|
+
return Promise.resolve("REDIRECT");
|
|
1366
|
+
}
|
|
1367
|
+
if (!this.authPromise) {
|
|
1368
|
+
this.authPromise = auth(this.authProvider, {
|
|
1369
|
+
serverUrl: this.url,
|
|
1370
|
+
resourceMetadataUrl,
|
|
1371
|
+
fetchFn: this.fetchFn
|
|
1372
|
+
}).finally(() => {
|
|
1373
|
+
this.authPromise = void 0;
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
return this.authPromise;
|
|
1377
|
+
}
|
|
1354
1378
|
async start() {
|
|
1355
1379
|
if (this.abortController) {
|
|
1356
1380
|
throw new MCPClientError({
|
|
@@ -1401,11 +1425,7 @@ var HttpMCPTransport = class {
|
|
|
1401
1425
|
if (response.status === 401 && this.authProvider && !triedAuth) {
|
|
1402
1426
|
this.resourceMetadataUrl = extractResourceMetadataUrl(response);
|
|
1403
1427
|
try {
|
|
1404
|
-
const result = await
|
|
1405
|
-
serverUrl: this.url,
|
|
1406
|
-
resourceMetadataUrl: this.resourceMetadataUrl,
|
|
1407
|
-
fetchFn: this.fetchFn
|
|
1408
|
-
});
|
|
1428
|
+
const result = await this.authorizeOnce(this.resourceMetadataUrl);
|
|
1409
1429
|
if (result !== "AUTHORIZED") {
|
|
1410
1430
|
const error2 = new UnauthorizedError();
|
|
1411
1431
|
throw error2;
|
|
@@ -1429,7 +1449,10 @@ var HttpMCPTransport = class {
|
|
|
1429
1449
|
errorMessage += ". This server does not support HTTP transport. Try using `sse` transport instead";
|
|
1430
1450
|
}
|
|
1431
1451
|
const error2 = new MCPClientError({
|
|
1432
|
-
message: errorMessage
|
|
1452
|
+
message: errorMessage,
|
|
1453
|
+
statusCode: response.status,
|
|
1454
|
+
url: this.url.href,
|
|
1455
|
+
responseBody: text != null ? text : void 0
|
|
1433
1456
|
});
|
|
1434
1457
|
(_c = this.onerror) == null ? void 0 : _c.call(this, error2);
|
|
1435
1458
|
throw error2;
|
|
@@ -1448,7 +1471,9 @@ var HttpMCPTransport = class {
|
|
|
1448
1471
|
if (contentType.includes("text/event-stream")) {
|
|
1449
1472
|
if (!response.body) {
|
|
1450
1473
|
const error2 = new MCPClientError({
|
|
1451
|
-
message: "MCP HTTP Transport Error: text/event-stream response without body"
|
|
1474
|
+
message: "MCP HTTP Transport Error: text/event-stream response without body",
|
|
1475
|
+
statusCode: response.status,
|
|
1476
|
+
url: this.url.href
|
|
1452
1477
|
});
|
|
1453
1478
|
(_e = this.onerror) == null ? void 0 : _e.call(this, error2);
|
|
1454
1479
|
throw error2;
|
|
@@ -1486,7 +1511,9 @@ var HttpMCPTransport = class {
|
|
|
1486
1511
|
return;
|
|
1487
1512
|
}
|
|
1488
1513
|
const error = new MCPClientError({
|
|
1489
|
-
message: `MCP HTTP Transport Error: Unexpected content type: ${contentType}
|
|
1514
|
+
message: `MCP HTTP Transport Error: Unexpected content type: ${contentType}`,
|
|
1515
|
+
statusCode: response.status,
|
|
1516
|
+
url: this.url.href
|
|
1490
1517
|
});
|
|
1491
1518
|
(_f = this.onerror) == null ? void 0 : _f.call(this, error);
|
|
1492
1519
|
throw error;
|
|
@@ -1551,11 +1578,7 @@ var HttpMCPTransport = class {
|
|
|
1551
1578
|
if (response.status === 401 && this.authProvider && !triedAuth) {
|
|
1552
1579
|
this.resourceMetadataUrl = extractResourceMetadataUrl(response);
|
|
1553
1580
|
try {
|
|
1554
|
-
const result = await
|
|
1555
|
-
serverUrl: this.url,
|
|
1556
|
-
resourceMetadataUrl: this.resourceMetadataUrl,
|
|
1557
|
-
fetchFn: this.fetchFn
|
|
1558
|
-
});
|
|
1581
|
+
const result = await this.authorizeOnce(this.resourceMetadataUrl);
|
|
1559
1582
|
if (result !== "AUTHORIZED") {
|
|
1560
1583
|
const error = new UnauthorizedError();
|
|
1561
1584
|
(_b3 = this.onerror) == null ? void 0 : _b3.call(this, error);
|
|
@@ -1572,7 +1595,9 @@ var HttpMCPTransport = class {
|
|
|
1572
1595
|
}
|
|
1573
1596
|
if (!response.ok || !response.body) {
|
|
1574
1597
|
const error = new MCPClientError({
|
|
1575
|
-
message: `MCP HTTP Transport Error: GET SSE failed: ${response.status} ${response.statusText}
|
|
1598
|
+
message: `MCP HTTP Transport Error: GET SSE failed: ${response.status} ${response.statusText}`,
|
|
1599
|
+
statusCode: response.status,
|
|
1600
|
+
url: this.url.href
|
|
1576
1601
|
});
|
|
1577
1602
|
(_d = this.onerror) == null ? void 0 : _d.call(this, error);
|
|
1578
1603
|
return;
|