Skip to content

Commit 3e3b9df

Browse files
fix (ai/mcp): improve handling of zero-argument MCP tools (#5670) (#5683)
Co-authored-by: Grace Yun <74513600+iteratetograceness@users.noreply.github.com>
1 parent 8dea1b4 commit 3e3b9df

File tree

7 files changed

+71
-17
lines changed

7 files changed

+71
-17
lines changed

‎.changeset/pretty-bikes-appear.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+
fix (ai/mcp): better support for zero-argument MCP tools

‎content/docs/03-ai-sdk-core/15-tools-and-tool-calling.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,10 @@ const tools = await mcpClient.tools({
768768
format: z.enum(['json', 'text']).optional(),
769769
}),
770770
},
771+
// For tools with zero arguments, you should use an empty object:
772+
'tool-with-no-args': {
773+
parameters: z.object({}),
774+
},
771775
},
772776
});
773777
```

‎examples/mcp/src/sse/client.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { openai } from '@ai-sdk/openai';
22
import { experimental_createMCPClient, generateText } from 'ai';
33
import 'dotenv/config';
4-
import { z } from 'zod';
54

65
async function main() {
76
const mcpClient = await experimental_createMCPClient({
@@ -14,21 +13,17 @@ async function main() {
1413
},
1514
});
1615

16+
const tools = await mcpClient.tools();
17+
1718
const { text: answer } = await generateText({
1819
model: openai('gpt-4o-mini', { structuredOutputs: true }),
19-
tools: await mcpClient.tools({
20-
schemas: {
21-
'find-product': {
22-
parameters: z.object({}),
23-
},
24-
},
25-
}),
20+
tools,
2621
maxSteps: 10,
2722
onStepFinish: async ({ toolResults }) => {
2823
console.log(`STEP RESULTS: ${JSON.stringify(toolResults, null, 2)}`);
2924
},
3025
system: 'You are a helpful chatbot',
31-
prompt: 'Can you find a product called The Product?',
26+
prompt: 'List all products, then find availability for Product 1.',
3227
});
3328

3429
await mcpClient.close();

‎examples/mcp/src/sse/server.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,38 @@
1-
import 'dotenv/config';
2-
import express from 'express';
31
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
42
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
3+
import 'dotenv/config';
4+
import express from 'express';
5+
import { z } from 'zod';
56

67
const mcpServer = new McpServer({
78
name: 'example-server',
89
version: '1.0.0',
910
});
1011

11-
mcpServer.tool('find-product', 'Find a product', {}, async () => {
12+
// Tool with arguments:
13+
mcpServer.tool(
14+
'check-product-stock',
15+
'Check if a product is available',
16+
{
17+
productName: z.string(),
18+
},
19+
async ({ productName }) => {
20+
return {
21+
content: [
22+
{
23+
type: 'text',
24+
text: `The product ${productName} is available in stock`,
25+
},
26+
],
27+
};
28+
},
29+
);
30+
31+
// Tool with zero arguments:
32+
mcpServer.tool('list-products', 'List all products', async () => {
1233
return {
1334
content: [
14-
{
15-
type: 'text',
16-
text: 'The Product is available in stock',
17-
},
35+
{ type: 'text', text: 'Products: Product 1, Product 2, Product 3' },
1836
],
1937
};
2038
});

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,4 +276,25 @@ describe('MCPClient', () => {
276276
createMCPClient({ transport: invalidTransport }),
277277
).rejects.toThrow();
278278
});
279+
280+
it('should support zero-argument tools', async () => {
281+
client = await createMCPClient({
282+
transport: { type: 'sse', url: 'https://example.com/sse' },
283+
});
284+
const tools = await client.tools();
285+
const tool = tools['mock-tool-no-args'];
286+
expect(tool).toHaveProperty('parameters');
287+
expect(tool.parameters).toMatchObject({
288+
jsonSchema: {
289+
type: 'object',
290+
properties: {},
291+
additionalProperties: false,
292+
},
293+
});
294+
295+
const result = await tool.execute({}, { messages: [], toolCallId: '1' });
296+
expect(result).toEqual({
297+
content: [{ type: 'text', text: 'Mock tool call result' }],
298+
});
299+
});
279300
});

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,11 @@ class MCPClient {
309309

310310
const parameters =
311311
schemas === 'automatic'
312-
? jsonSchema(inputSchema as JSONSchema7)
312+
? jsonSchema({
313+
...inputSchema,
314+
properties: inputSchema.properties ?? {},
315+
additionalProperties: false,
316+
} as JSONSchema7)
313317
: schemas[name].parameters;
314318

315319
const self = this;

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ const DEFAULT_TOOLS: MCPTool[] = [
1414
},
1515
},
1616
},
17+
{
18+
name: 'mock-tool-no-args',
19+
description: 'A mock tool for testing',
20+
inputSchema: {
21+
type: 'object',
22+
},
23+
},
1724
];
1825

1926
export class MockMCPTransport implements MCPTransport {

0 commit comments

Comments
 (0)