@mastra/core
Version:
492 lines (382 loc) • 19.1 kB
Markdown
> Discover all available pages from the documentation index: https://mastra.ai/llms.txt
# createTool()
The `createTool()` function is used to define custom tools that your Mastra agents can execute. Tools extend an agent's capabilities by allowing it to interact with external systems, perform calculations, or access specific data.
## Usage example
```typescript
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'
export const tool = createTool({
id: 'test-tool',
description: 'Reverse the input string',
inputSchema: z.object({
input: z.string(),
}),
outputSchema: z.object({
output: z.string(),
}),
execute: async inputData => {
const reversed = inputData.input.split('').reverse().join('')
return {
output: reversed,
}
},
})
```
## Parameters
**id** (`string`): A unique identifier for the tool.
**description** (`string`): A description of what the tool does. This is used by the agent to decide when to use the tool.
**inputSchema** (`StandardJSONSchemaV1`): A Standard JSON Schema defining the expected input parameters for the tool's execute function.
**outputSchema** (`StandardJSONSchemaV1`): A Standard JSON Schema defining the expected output structure of the tool's execute function.
**strict** (`boolean`): When true, Mastra enables strict tool input generation on model adapters that support it. This helps supported providers return arguments that match the tool schema more closely.
**toModelOutput** (`(output: TSchemaOut) => unknown`): Optional function that transforms the tool's execute output before it is sent back to the model. Use this to return text, json, or content-shaped outputs (including multimodal parts like images/files) to the model while still keeping the full raw output in your application code.
**transform** (`ToolPayloadTransform`): Optional target-aware transform for tool payloads before they leave runtime for display streams or user-visible transcript messages. Configure display and transcript transforms for phases such as input, inputDelta, output, error, approval, suspend, and resume.
**suspendSchema** (`StandardJSONSchemaV1`): A Standard JSON Schema defining the structure of the payload passed to suspend(). This payload is returned to the client when the tool suspends execution.
**resumeSchema** (`StandardJSONSchemaV1`): A Standard JSON Schema defining the expected structure of resumeData when the tool is resumed. Used by the agent to extract data from user messages when autoResumeSuspendedTools is enabled.
**requireApproval** (`boolean`): When true, the tool requires explicit approval before execution. The agent will emit a tool-call-approval chunk and pause until approved or declined.
**mcp** (`MCPToolProperties`): MCP-specific properties for tools exposed via Model Context Protocol. Includes annotations (tool behavior hints like title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint) and \_meta (arbitrary metadata passed through to MCP clients).
**requestContextSchema** (`StandardJSONSchemaV1`): A Standard JSON Schema for validating request context values. When provided, the context is validated before execute() runs, returning an error object if validation fails.
**providerOptions** (`Record<string, Record<string, unknown>>`): Provider-specific options passed to the model when this tool is used. Keys are provider names, such as anthropic or openai, and values are provider-specific configuration objects.
**inputExamples** (`Array<{ input: Record<string, unknown> }>`): Examples of valid tool inputs that supported model providers can use as input examples.
**background** (`ToolBackgroundConfig`): Background task configuration for this tool. When enabled, the tool can execute in the background while the agent conversation continues.
**execute** (`function`): The function that contains the tool's logic. Ordinary custom tools usually provide execute, but the type allows omission for tool definitions that are executed or adapted elsewhere. It receives two parameters: the validated input data based on inputSchema (first parameter) and an execution context object (second parameter) containing requestContext, abortSignal, and other execution metadata.
**execute.input** (`z.infer<TInput>`): The validated input data based on inputSchema
**execute.context** (`ToolExecutionContext`): Optional execution context containing metadata
**execute.context.requestContext** (`RequestContext`): Request Context for accessing shared state and dependencies
**execute.context.abortSignal** (`AbortSignal`): Signal for aborting the tool execution
**execute.context.agent** (`AgentToolExecutionContext`): Agent-specific context, available when the tool is executed by an agent.
**execute.context.workflow** (`WorkflowToolExecutionContext`): Workflow-specific context (state, setState, suspend, etc.)
**execute.context.mcp** (`MCPToolExecutionContext`): MCP-specific context (elicitation, etc.)
**execute.context.observe** (`ToolObserve`): Observability helpers for recording child spans and structured logs from inside a tool's execute function. Always provided — when no tracing context is active, span runs the function directly and log is a no-op.
**onInputStart** (`function`): Optional callback invoked when the tool call input streaming begins. Signature: (options: ToolCallOptions) => void | PromiseLike\<void>.
**onInputDelta** (`function`): Optional callback invoked for each incremental chunk of input text as it streams in. Signature: ({ inputTextDelta, ...options }: { inputTextDelta: string } & ToolCallOptions) => void | PromiseLike\<void>.
**onInputAvailable** (`function`): Optional callback invoked when the complete tool input is available and parsed. Signature: ({ input, ...options }: { input: TSchemaIn } & ToolCallOptions) => void | PromiseLike\<void>.
**onOutput** (`function`): Optional callback invoked after the tool has successfully executed and returned output. Signature: ({ output, toolName, ...options }: { output: TSchemaOut; toolName: string } & Omit\<ToolCallOptions, 'messages'>) => void | PromiseLike\<void>.
Runtime-populated fields such as `mastra` and `mcpMetadata` appear in source types but are set by Mastra or MCP adapters. You do not need to configure them for ordinary `createTool()` usage.
## Returns
The `createTool()` function returns a `Tool` object.
**Tool** (`object`): An object representing the defined tool, ready to be added to an agent.
## Define schemas
You can define the tool's `inputSchema` and `outputSchema` with any library that supports [Standard JSON Schema](https://standardschema.dev/json-schema). This includes libraries like [Zod](https://zod.dev/), [Valibot](https://valibot.dev/), and [ArkType](https://arktype.io/).
**Zod**:
```typescript
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'
export const weatherTool = createTool({
id: 'weather-tool',
description: 'Fetches weather for a location',
inputSchema: z.object({
location: z.string(),
}),
outputSchema: z.object({
weather: z.string(),
}),
execute: async inputData => {
// Fetch weather data
},
})
```
**Valibot**:
```typescript
import { createTool } from '@mastra/core/tools'
import * as v from 'valibot'
import { toStandardJsonSchema } from '@valibot/to-json-schema'
export const weatherTool = createTool({
id: 'weather-tool',
description: 'Fetches weather for a location',
inputSchema: toStandardJsonSchema(
v.object({
location: v.string(),
}),
),
outputSchema: toStandardJsonSchema(
v.object({
weather: v.string(),
}),
),
execute: async inputData => {
// Fetch weather data
},
})
```
**ArkType**:
```typescript
import { createTool } from '@mastra/core/tools'
import { type } from 'arktype'
export const weatherTool = createTool({
id: 'weather-tool',
description: 'Fetches weather for a location',
inputSchema: type({
location: 'string',
}),
outputSchema: type({
weather: 'string',
}),
execute: async inputData => {
// Fetch weather data
},
})
```
## Example with strict tool inputs
Set `strict: true` when you want Mastra to ask supported model providers to generate tool arguments that exactly match the tool schema.
```typescript
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'
export const weatherTool = createTool({
id: 'get-weather',
description: 'Get weather for a city',
strict: true,
inputSchema: z.object({
city: z.string(),
units: z.enum(['metric', 'imperial']),
}),
execute: async ({ city, units }) => {
return {
city,
units,
forecast: 'sunny',
}
},
})
```
Mastra forwards `strict: true` to model adapters that support strict tool calling. On adapters that don't support strict tool calling, Mastra ignores this option.
## Example with `toModelOutput`
Use `toModelOutput` when your tool should return rich internal data to your app, but the model should receive either a simplified value or multimodal content.
```typescript
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'
export const weatherTool = createTool({
id: 'get-weather',
description: 'Get weather for a city',
inputSchema: z.object({
city: z.string(),
}),
outputSchema: z.object({
city: z.string(),
temperature: z.number(),
condition: z.string(),
radarImageUrl: z.string().url(),
}),
execute: async ({ city }) => ({
city,
temperature: 72,
condition: 'sunny',
radarImageUrl: 'https://example.com/radar/seattle.png',
}),
toModelOutput: output => {
return {
type: 'content',
value: [
{ type: 'text', text: `${output.city}: ${output.temperature}F and ${output.condition}` },
{ type: 'image-url', url: output.radarImageUrl },
],
}
},
})
```
The tool still returns the full `execute` result to your application, while the model receives the transformed `toModelOutput` value.
`toModelOutput` can return:
- `type: 'text'`
- `type: 'json'`
- `type: 'content'` with parts like `text`, `image-url`, `image-data`, `file-url`, `file-data`, `file-id`, `image-file-id`, or `custom`
## Example with `transform`
Use `transform` when the tool should keep raw inputs or outputs for runtime behavior, but display streams or transcript messages should receive a smaller or safer shape.
```typescript
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'
export const customerTool = createTool({
id: 'lookup-customer',
description: 'Looks up a customer',
inputSchema: z.object({
customerId: z.string(),
internalPath: z.string(),
}),
outputSchema: z.object({
displayName: z.string(),
apiKey: z.string(),
debugScore: z.number(),
}),
execute: async () => {
return {
displayName: 'Acme',
apiKey: 'secret-value',
debugScore: 0.97,
}
},
transform: {
display: {
input: ({ input }) => ({ customerId: input?.customerId }),
output: ({ output }) => ({ displayName: output?.displayName }),
error: () => ({ message: 'Customer lookup failed' }),
},
transcript: {
input: ({ input }) => ({ customerId: input?.customerId }),
output: ({ output }) => ({ displayName: output?.displayName }),
error: () => ({ message: 'Customer lookup failed' }),
},
},
})
```
The tool still receives the raw `inputSchema` value and returns the raw `execute` result. Mastra applies `display` transforms to streamed UI payloads and `transcript` transforms to user-visible transcript messages.
## Example with MCP annotations
When exposing tools via MCP (Model Context Protocol), you can add annotations to describe tool behavior and customize how clients display the tool. These MCP-specific properties are grouped under the `mcp` property:
```typescript
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'
export const weatherTool = createTool({
id: 'get-weather',
description: 'Get current weather for a location',
inputSchema: z.object({
location: z.string().describe('City name or coordinates'),
}),
// MCP-specific properties
mcp: {
// Annotations for client behavior hints
annotations: {
title: 'Weather Lookup', // Human-readable display name
readOnlyHint: true, // Tool doesn't modify environment
destructiveHint: false, // Tool doesn't perform destructive updates
idempotentHint: true, // Same args = same result
openWorldHint: true, // Interacts with external API
},
// Custom metadata for client-specific functionality
_meta: {
version: '1.0.0',
category: 'weather',
},
},
execute: async inputData => {
return {
weather: `Current weather for ${inputData.location}: sunny`,
}
},
})
```
## Tool lifecycle hooks
Tools support lifecycle hooks that allow you to monitor and react to different stages of tool execution. These hooks are particularly useful for logging, analytics, validation, and real-time updates during streaming.
The following example demonstrates a tool with all lifecycle hooks configured:
```typescript
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'
export const weatherTool = createTool({
id: 'get-weather',
description: 'Get weather for a city',
inputSchema: z.object({
city: z.string(),
}),
outputSchema: z.object({
city: z.string(),
forecast: z.string(),
}),
execute: async ({ city }) => {
return {
city,
forecast: 'sunny',
}
},
onInputStart: ({ toolCallId }) => {
console.log(`Tool call ${toolCallId} input started`)
},
onInputDelta: ({ inputTextDelta, toolCallId }) => {
console.log(`Tool call ${toolCallId} received input chunk: ${inputTextDelta}`)
},
onInputAvailable: ({ input, toolCallId }) => {
console.log(`Tool call ${toolCallId} received city: ${input.city}`)
},
onOutput: ({ output, toolCallId, toolName }) => {
console.log(`Tool ${toolName} call ${toolCallId} returned forecast: ${output.forecast}`)
},
})
```
### Available hooks
#### `onInputStart`
Called when tool call input streaming begins, before any input data is received.
```typescript
export const tool = createTool({
id: 'example-tool',
description: 'Example tool with hooks',
onInputStart: ({ toolCallId, messages, abortSignal }) => {
console.log(`Tool ${toolCallId} input streaming started`)
},
})
```
#### `onInputDelta`
Called for each incremental chunk of input text as it streams in. Useful for showing real-time progress or parsing partial JSON.
```typescript
export const tool = createTool({
id: 'example-tool',
description: 'Example tool with hooks',
onInputDelta: ({ inputTextDelta, toolCallId, messages, abortSignal }) => {
console.log(`Received input chunk: ${inputTextDelta}`)
},
})
```
#### `onInputAvailable`
Called when the complete tool input is available and has been parsed and validated against the `inputSchema`.
```typescript
export const tool = createTool({
id: 'example-tool',
description: 'Example tool with hooks',
inputSchema: z.object({
city: z.string(),
}),
onInputAvailable: ({ input, toolCallId, messages, abortSignal }) => {
console.log(`Tool received complete input:`, input)
// input is fully typed based on inputSchema
},
})
```
#### `onOutput`
Called after the tool has successfully executed and returned output. Useful for logging results, triggering follow-up actions, or analytics.
```typescript
export const tool = createTool({
id: 'example-tool',
description: 'Example tool with hooks',
outputSchema: z.object({
result: z.string(),
}),
execute: async input => {
return { result: 'Success' }
},
onOutput: ({ output, toolCallId, toolName, abortSignal }) => {
console.log(`${toolName} execution completed:`, output)
// output is fully typed based on outputSchema
},
})
```
### Hook execution order
For a typical streaming tool call, the hooks are invoked in this order:
1. **onInputStart**: Input streaming begins
2. **onInputDelta**: Called multiple times as chunks arrive
3. **onInputAvailable**: Complete input is parsed and validated
4. Tool's **execute** function runs
5. **onOutput**: Tool has completed successfully
### Hook parameters
Hook callbacks receive these source-backed parameter shapes:
- `onInputStart`: Receives `ToolCallOptions`, including fields such as `toolCallId`, `messages`, and `abortSignal`.
- `onInputDelta`: Receives `{ inputTextDelta: string } & ToolCallOptions`.
- `onInputAvailable`: Receives `{ input: TSchemaIn } & ToolCallOptions`, where `input` is typed from `inputSchema`.
- `onOutput`: Receives `{ output: TSchemaOut; toolName: string } & Omit<ToolCallOptions, 'messages'>`, where `output` is typed from `outputSchema`. This hook does not receive `messages`.
### Error handling
Hook errors are caught and logged automatically, but don't prevent tool execution from continuing. If a hook throws an error, it will be logged to the console but won't fail the tool call.
## MCP tool annotations
When exposing tools via the Model Context Protocol (MCP), you can provide annotations that describe tool behavior. These annotations help MCP clients like OpenAI Apps SDK understand how to present and handle your tools.
MCP-specific properties are grouped under the `mcp` property, which includes `annotations` and `_meta`:
```typescript
mcp: {
annotations: { /* behavior hints */ },
_meta: { /* custom metadata */ },
}
```
### `ToolAnnotations` properties
**title** (`string`): A human-readable title for the tool. Used for display purposes in UI components.
**readOnlyHint** (`boolean`): If true, the tool does not modify its environment. This hint indicates the tool only reads data and has no side effects. Defaults to false.
**destructiveHint** (`boolean`): If true, the tool may perform destructive updates to its environment. If false, the tool performs only additive updates. This hint helps clients determine if confirmation should be required. Defaults to true.
**idempotentHint** (`boolean`): If true, calling the tool repeatedly with the same arguments will have no additional effect on its environment. This hint indicates idempotent behavior. Defaults to false.
**openWorldHint** (`boolean`): If true, this tool may interact with an 'open world' of external entities (e.g., web search, external APIs). If false, the tool's domain is closed and fully defined. Defaults to true.
These annotations follow the [MCP specification](https://spec.modelcontextprotocol.io/specification/2025-03-26/server/tools/#tool-annotations) and are passed through when tools are listed via MCP.
## Related
- [MCP Overview](https://mastra.ai/docs/mcp/overview)
- [Using Tools with Agents](https://mastra.ai/docs/agents/using-tools)
- [Agent Approval](https://mastra.ai/docs/agents/agent-approval)
- [Tool Streaming](https://mastra.ai/docs/agents/using-tools)
- [Request Context](https://mastra.ai/docs/server/request-context)