Step class
The Step class defines individual units of work within a workflow, encapsulating execution logic, data validation, and input/output handling. It can take a tool, agent, or configured classifier as a parameter to automatically create a step from it.
Usage exampleDirect link to Usage example
import { createWorkflow, createStep } from '@mastra/core/workflows'
import { z } from 'zod'
const step1 = createStep({
id: 'step-1',
description: 'passes value from input to output',
inputSchema: z.object({
value: z.number(),
}),
outputSchema: z.object({
value: z.number(),
}),
execute: async ({ inputData }) => {
const { value } = inputData
return {
value,
}
},
})
Define schemasDirect link to Define schemas
You can define the step's inputSchema and outputSchema with any library that supports Standard JSON Schema. This includes libraries like Zod, Valibot, and ArkType.
- Zod
- Valibot
- ArkType
import { createStep } from '@mastra/core/workflows'
import { z } from 'zod'
const step1 = createStep({
id: 'step-1',
inputSchema: z.object({
message: z.string(),
}),
outputSchema: z.object({
formatted: z.string(),
}),
execute: async ({ inputData }) => {
const { message } = inputData
return {
formatted: message.toUpperCase(),
}
},
})
import { createStep } from '@mastra/core/workflows'
import * as v from 'valibot'
import { toStandardJsonSchema } from '@valibot/to-json-schema'
const step1 = createStep({
id: 'step-1',
inputSchema: toStandardJsonSchema(
v.object({
message: v.string(),
}),
),
outputSchema: toStandardJsonSchema(
v.object({
formatted: v.string(),
}),
),
execute: async ({ inputData }) => {
const { message } = inputData
return {
formatted: message.toUpperCase(),
}
},
})
import { createStep } from '@mastra/core/workflows'
import { type } from 'arktype'
const step1 = createStep({
id: 'step-1',
inputSchema: type({
message: 'string',
}),
outputSchema: type({
formatted: 'string',
}),
execute: async ({ inputData }) => {
const { message } = inputData
return {
formatted: message.toUpperCase(),
}
},
})
Creating steps from agentsDirect link to Creating steps from agents
You can create a step directly from an agent. The step will use the agent's name as its ID.
Basic agent stepDirect link to Basic agent step
import { testAgent } from '../agents/test-agent'
const agentStep = createStep(testAgent)
// inputSchema: { prompt: string }
// outputSchema: { text: string }
Agent step with structured outputDirect link to Agent step with structured output
Pass structuredOutput to have the agent return typed structured data:
const articleSchema = z.object({
title: z.string(),
summary: z.string(),
tags: z.array(z.string()),
})
const agentStep = createStep(testAgent, {
structuredOutput: { schema: articleSchema },
})
// inputSchema: { prompt: string }
// outputSchema: { title: string, summary: string, tags: string[] }
Agent step optionsDirect link to Agent step options
structuredOutput:
onFinish:
Creating steps from classifiersDirect link to Creating steps from classifiers
Pass a Classifier with constructor-configured questions to createStep(). By default, the classifier evaluates the complete inputData. Use state to select a JSON value from the step context.
import { Classifier } from '@mastra/core/classifier'
import { createStep } from '@mastra/core/workflows'
const router = new Classifier({
id: 'ticket-router',
model,
questions: {
route: {
type: 'choice',
criteria: {
billing: 'Billing, invoices, and payments',
support: 'Account access and product help',
other: 'Anything else',
},
},
urgent: { type: 'boolean' },
},
})
const classifyTicket = createStep(router, {
maxRetries: 2,
retries: 1,
})
The typed step output is JSON-safe:
answers.route.choiceis the selected choice literal.answers.urgent.probabilityis the probability that the boolean answer istrue, not a thresholded boolean.- Choice and score answers may include distributions.
usagecontains normalized token usage.
maxRetries controls classifier model-call retries. retries controls workflow step retries. Classifier warnings, response metadata, and raw provider data aren't included in workflow output.
A classifier without constructor-configured questions can't be used as a workflow step. The step evaluates its complete input. Add a preceding .map() call when the workflow needs to select or reshape that input.
Constructor parametersDirect link to Constructor parameters
id:
description:
inputSchema:
outputSchema:
resumeSchema:
suspendSchema:
stateSchema:
requestContextSchema:
execute:
inputData:
resumeData:
suspendData:
mastra:
getStepResult:
getInitData:
suspend:
state:
setState:
runId:
requestContext?:
retryCount?:
scorers:
{ [name]: { scorer, sampling? } }, or a function that returns one. Scoring runs asynchronously and doesn't block the workflow. See Scoring step output.retries:
execute function if it throws.metadata:
Scoring step outputDirect link to Scoring step output
Attach scorers to a step to evaluate that step's output automatically, at the point it runs, instead of only scoring the workflow's final answer. This is useful for multi-step and RAG workflows, where you want to see which step degraded quality, for example whether a retrieval step returned relevant chunks before later steps reason over them.
Each scorer receives the step's own input and output. Scoring runs asynchronously after the step succeeds, and the result is stored against the step's trace. Use sampling to control how often a scorer runs.
The following example attaches a scorer to a retrieval step so every execution is scored:
import { createStep } from '@mastra/core/workflows'
import { z } from 'zod'
import { retrievalRelevanceScorer } from '../scorers/retrieval-relevance'
const retrievalStep = createStep({
id: 'retrieval',
inputSchema: z.object({ query: z.string() }),
outputSchema: z.object({ query: z.string(), chunks: z.array(z.string()) }),
scorers: {
retrievalRelevance: {
scorer: retrievalRelevanceScorer(),
sampling: { type: 'ratio', rate: 1 },
},
},
execute: async ({ inputData }) => {
const chunks = await retrieve(inputData.query)
return { query: inputData.query, chunks }
},
})
Attach a scorer to each step you want to measure to build per-step scores across a multi-step workflow. Because scoring is scoped to a single step, you don't need a dedicated cross-step metric to see where quality changes.
Agent and tool steps added with Workflow.agent() and Workflow.tool() accept the same scorers option in their step options.
Visit the Scorers overview to learn how live evaluations run and where results are stored, and Custom scorers to build your own.