File: dynamic-tool.md | Updated: 11/15/2025
Menu
v5 (Latest)
AI SDK 5.x
Model Context Protocol (MCP) Tools
Experimental_StdioMCPTransport
Copy markdown
==========================================================================================
The dynamicTool function creates tools where the input and output types are not known at compile time. This is useful for scenarios such as:
Unlike the regular tool function, dynamicTool accepts and returns unknown types, allowing you to work with tools that have runtime-determined schemas.
import { dynamicTool } from 'ai';import { z } from 'zod';
export const customTool = dynamicTool({ description: 'Execute a custom user-defined function', inputSchema: z.object({}), // input is typed as 'unknown' execute: async input => { const { action, parameters } = input as any;
// Execute your dynamic logic return { result: `Executed ${action} with ${JSON.stringify(parameters)}`, }; },});
import { dynamicTool } from "ai"
Object
The dynamic tool definition.
Object
string
Information about the purpose of the tool including details on how and when it can be used by the model.
FlexibleSchema<unknown>
The schema of the input that the tool expects. While the type is unknown, a schema is still required for validation. You can use Zod schemas with z.unknown() or z.any() for fully dynamic inputs.
ToolExecuteFunction<unknown, unknown>
An async function that is called with the arguments from the tool call. The input is typed as unknown and must be validated/cast at runtime.
ToolCallOptions
string
The ID of the tool call.
ModelMessage[]
Messages that were sent to the language model.
AbortSignal
An optional abort signal.
(output: unknown) => LanguageModelV2ToolResultPart['output']
Optional conversion function that maps the tool result to an output that can be used by the language model.
ProviderOptions
Additional provider-specific metadata.
A Tool<unknown, unknown> with type: 'dynamic' that can be used with generateText, streamText, and other AI SDK functions.
When using dynamic tools alongside static tools, you need to check the dynamic flag for proper type narrowing:
const result = await generateText({ model: openai('gpt-4'), tools: { // Static tool with known types weather: weatherTool, // Dynamic tool with unknown types custom: dynamicTool({ /* ... */ }), }, onStepFinish: ({ toolCalls, toolResults }) => { for (const toolCall of toolCalls) { if (toolCall.dynamic) { // Dynamic tool: input/output are 'unknown' console.log('Dynamic tool:', toolCall.toolName); console.log('Input:', toolCall.input); continue; }
// Static tools have full type inference switch (toolCall.toolName) { case 'weather': // TypeScript knows the exact types console.log(toolCall.input.location); // string break; } } },});
When used with useChat (UIMessage format), dynamic tools appear as dynamic-tool parts:
{ message.parts.map(part => { switch (part.type) { case 'dynamic-tool': return ( <div> <h4>Tool: {part.toolName}</h4> <pre>{JSON.stringify(part.input, null, 2)}</pre> </div> ); // ... handle other part types } });}
On this page
Deploy and Scale AI Apps with Vercel.
Vercel delivers the infrastructure and developer experience you need to ship reliable AI-powered applications at scale.
Trusted by industry leaders: