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.

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

import { dynamicTool } from "ai"

API Signature

Parameters

tool:

Object
The dynamic tool definition.
Object

description?:

string
Information about the purpose of the tool including details on how and when it can be used by the model.

inputSchema:

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.

execute:

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

toolCallId:

string
The ID of the tool call.

messages:

ModelMessage[]
Messages that were sent to the language model.

abortSignal?:

AbortSignal
An optional abort signal.

toModelOutput?:

(output: unknown) => LanguageModelV2ToolResultPart['output']
Optional conversion function that maps the tool result to an output that can be used by the language model.

providerOptions?:

ProviderOptions
Additional provider-specific metadata.

Returns

A Tool<unknown, unknown> 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:

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;
}
}
},
});

Usage with useChat

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
}
});
}