Skip to content

Commit f7f5af0

Browse files
iteratetogracenesslgrammel
authored andcommitted
chore (ai/mcp): add assertCapability method to experimental MCP client (#6047)
<!-- Welcome to contributing to AI SDK! We're excited to see your changes. We suggest you read the following contributing guide we've created before submitting: https://github.com/vercel/ai/blob/main/CONTRIBUTING.md --> ## Background Splits up #5972 into package changes and updates to examples. ## Summary Moves capabilities validation downstream to the request level instead of the method level. This PR adds a new private `assertCapability` method called at the request level. This means our custom, lightweight client defaults strict mode to true for tool listing and tool calling. ## Verification <!-- For features & bugfixes. Please explain how you *manually* verified that the change works end-to-end as expected (independent of automated tests). Remove the section if it's not needed (e.g. for docs). --> ## Tasks <!-- This task list is intended to help you keep track of what you need to do. Feel free to add tasks and remove unnecessary tasks as needed. Please check if the PR fulfills the following requirements: --> - [x] Tests have been added / updated (for bug fixes / features) - [ ] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] Formatting issues have been fixed (run `pnpm prettier-fix` in the project root) ## Future Work <!-- Feel free to mention things not covered by this PR that can be done in future PRs. Remove the section if it's not needed. --> ## Related Issues <!-- List related issues here, e.g. "Fixes #1234". Remove the section if it's not needed. -->
1 parent 28ad69e commit f7f5af0

File tree

3 files changed

+42
-13
lines changed

3 files changed

+42
-13
lines changed

.changeset/sour-mails-cheer.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'ai': patch
3+
---
4+
5+
chore (ai/mcp): add `assertCapability` method to experimental MCP client

packages/ai/core/tool/mcp/mcp-client.ts

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,13 @@ export async function createMCPClient(
6161
* Tool parameters are automatically inferred from the server's JSON schema
6262
* if not explicitly provided in the tools configuration
6363
*
64+
* This client is meant to be used to communicate with a single server. To communicate and fetch tools across multiple servers, it's recommended to create a new client instance per server.
65+
*
6466
* Not supported:
6567
* - Client options (e.g. sampling, roots) as they are not needed for tool conversion
6668
* - Accepting notifications
69+
* - Session management (when passing a sessionId to an instance of the Streamable HTTP transport)
70+
* - Resumable SSE streams
6771
*/
6872
class MCPClient {
6973
private transport: MCPTransport;
@@ -163,6 +167,25 @@ class MCPClient {
163167
this.onClose();
164168
}
165169

170+
private assertCapability(method: string): void {
171+
switch (method) {
172+
case 'initialize':
173+
break;
174+
case 'tools/list':
175+
case 'tools/call':
176+
if (!this.serverCapabilities.tools) {
177+
throw new MCPClientError({
178+
message: `Server does not support tools`,
179+
});
180+
}
181+
break;
182+
default:
183+
throw new MCPClientError({
184+
message: `Unsupported method: ${method}`,
185+
});
186+
}
187+
}
188+
166189
private async request<T extends ZodType<object>>({
167190
request,
168191
resultSchema,
@@ -181,6 +204,8 @@ class MCPClient {
181204
);
182205
}
183206

207+
this.assertCapability(request.method);
208+
184209
const signal = options?.signal;
185210
signal?.throwIfAborted();
186211

@@ -214,7 +239,7 @@ class MCPClient {
214239
resolve(result);
215240
} catch (error) {
216241
const parseError = new MCPClientError({
217-
message: 'Failed to parse server initialization result',
242+
message: 'Failed to parse server response',
218243
cause: error,
219244
});
220245
reject(parseError);
@@ -235,12 +260,6 @@ class MCPClient {
235260
params?: PaginatedRequest['params'];
236261
options?: RequestOptions;
237262
} = {}): Promise<ListToolsResult> {
238-
if (!this.serverCapabilities.tools) {
239-
throw new MCPClientError({
240-
message: `Server does not support tools`,
241-
});
242-
}
243-
244263
try {
245264
return this.request({
246265
request: { method: 'tools/list', params },
@@ -261,12 +280,6 @@ class MCPClient {
261280
args: Record<string, unknown>;
262281
options?: ToolExecutionOptions;
263282
}): Promise<CallToolResult> {
264-
if (!this.serverCapabilities.tools) {
265-
throw new MCPClientError({
266-
message: `Server does not support tools`,
267-
});
268-
}
269-
270283
try {
271284
return this.request({
272285
request: { method: 'tools/call', params: { name, arguments: args } },

packages/ai/core/tool/mcp/mock-mcp-transport.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,17 @@ export class MockMCPTransport implements MCPTransport {
8282

8383
if (message.method === 'tools/list') {
8484
await delay(10);
85+
if (this.tools.length === 0) {
86+
this.onmessage?.({
87+
jsonrpc: '2.0',
88+
id: message.id,
89+
error: {
90+
code: -32000,
91+
message: 'Method not supported',
92+
},
93+
});
94+
return;
95+
}
8596
this.onmessage?.({
8697
jsonrpc: '2.0',
8798
id: message.id,

0 commit comments

Comments
 (0)