Skip to Content
ExamplesWorkflows (vNext)Tool & Agent as a Step

Tool/Agent as a Workflow step

This example demonstrates how to create and integrate a tool or an agent as a workflow step. Mastra provides a createStep helper function which accepts either a step or agent and returns an object which satisfies the Step interface.

Setup

npm install @ai-sdk/openai @mastra/core

Define Weather Reporter Agent

Define a weather reporter agent that leverages an LLM to explain the weather report like a weather reporter.

agents/weather-reporter-agent.ts
import { openai } from '@ai-sdk/openai' import { Agent } from '@mastra/core/agent' // Create an agent that explains weather reports in a conversational style export const weatherReporterAgent = new Agent({ name: 'weatherExplainerAgent', model: openai('gpt-4o'), instructions: ` You are a weather explainer. You have access to input that will help you get weather-specific activities for any city. The tool uses agents to plan the activities, you just need to provide the city. Explain the weather report like a weather reporter. `, })

Define Weather Tool

Define a weather tool that take a location name as input and outputs detailed weather information.

tools/weather-tool.ts
import { createTool } from '@mastra/core/tools' import { z } from 'zod' interface GeocodingResponse { results: { latitude: number longitude: number name: string }[] } interface WeatherResponse { current: { time: string temperature_2m: number apparent_temperature: number relative_humidity_2m: number wind_speed_10m: number wind_gusts_10m: number weather_code: number } } // Create a tool to fetch weather data export const weatherTool = createTool({ id: 'get-weather', description: 'Get current weather for a location', inputSchema: z.object({ location: z.string().describe('City name'), }), outputSchema: z.object({ temperature: z.number(), feelsLike: z.number(), humidity: z.number(), windSpeed: z.number(), windGust: z.number(), conditions: z.string(), location: z.string(), }), execute: async ({ context }) => { return await getWeather(context.location) }, }) // Helper function to fetch weather data from external APIs const getWeather = async (location: string) => { const geocodingUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(location)}&count=1` const geocodingResponse = await fetch(geocodingUrl) const geocodingData = (await geocodingResponse.json()) as GeocodingResponse if (!geocodingData.results?.[0]) { throw new Error(`Location '${location}' not found`) } const { latitude, longitude, name } = geocodingData.results[0] const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_gusts_10m,weather_code` const response = await fetch(weatherUrl) const data = (await response.json()) as WeatherResponse return { temperature: data.current.temperature_2m, feelsLike: data.current.apparent_temperature, humidity: data.current.relative_humidity_2m, windSpeed: data.current.wind_speed_10m, windGust: data.current.wind_gusts_10m, conditions: getWeatherCondition(data.current.weather_code), location: name, } } // Helper function to convert numeric weather codes to human-readable descriptions function getWeatherCondition(code: number): string { const conditions: Record<number, string> = { 0: 'Clear sky', 1: 'Mainly clear', 2: 'Partly cloudy', 3: 'Overcast', 45: 'Foggy', 48: 'Depositing rime fog', 51: 'Light drizzle', 53: 'Moderate drizzle', 55: 'Dense drizzle', 56: 'Light freezing drizzle', 57: 'Dense freezing drizzle', 61: 'Slight rain', 63: 'Moderate rain', 65: 'Heavy rain', 66: 'Light freezing rain', 67: 'Heavy freezing rain', 71: 'Slight snow fall', 73: 'Moderate snow fall', 75: 'Heavy snow fall', 77: 'Snow grains', 80: 'Slight rain showers', 81: 'Moderate rain showers', 82: 'Violent rain showers', 85: 'Slight snow showers', 86: 'Heavy snow showers', 95: 'Thunderstorm', 96: 'Thunderstorm with slight hail', 99: 'Thunderstorm with heavy hail', } return conditions[code] || 'Unknown' }

Define Interop Workflow

Defines a workflow which takes an agent and tool as a step.

workflows/interop-workflow.ts
import { createWorkflow, createStep } from '@mastra/core/workflows/vNext' import { weatherTool } from '../tools/weather-tool' import { weatherReporterAgent } from '../agents/weather-reporter-agent' import { z } from 'zod' // Create workflow steps from existing tool and agent const fetchWeather = createStep(weatherTool) const reportWeather = createStep(weatherReporterAgent) const weatherWorkflow = createWorkflow({ steps: [fetchWeather, reportWeather], id: 'weather-workflow-step1-single-day', inputSchema: z.object({ location: z.string().describe("The city to get the weather for"), }), outputSchema: z.object({ text: z.string(), }), }) .then(fetchWeather) .then( createStep({ id: "report-weather", inputSchema: fetchWeather.outputSchema, outputSchema: z.object({ text: z.string(), }), execute: async ({ inputData, mastra }) => { // Create a prompt with the weather data const prompt = 'Forecast data: ' + JSON.stringify(inputData) const agent = mastra.getAgent('weatherReporterAgent') // Generate a weather report using the agent const result = await agent.generate([ { role: "user", content: prompt, }, ]); return { text: result.text }; }, }), ); weatherWorkflow.commit(); export { weatherWorkflow };

Register Workflow instance with Mastra class

Register the workflow with the mastra instance.

index.ts
import { Mastra } from '@mastra/core/mastra' import { PinoLogger } from '@mastra/loggers' import { weatherWorkflow } from './workflows/interop-workflow' import { weatherReporterAgent } from './agents/weather-reporter-agent' // Create a new Mastra instance with our components const mastra = new Mastra({ vnext_workflows: { weatherWorkflow, }, agents: { weatherReporterAgent, }, logger: new PinoLogger({ name: "Mastra", level: "info", }), }) export { mastra }

Execute the workflow

Here, we’ll get the weather workflow from the mastra instance, then create a run and execute the created run with the required inputData.

exec.ts
import { mastra } from "./"; const workflow = mastra.vnext_getWorkflow('weatherWorkflow') const run = workflow.createRun() // Start the workflow with Lagos as the location const result = await run.start({ inputData: { location: "Lagos" } }) console.dir(result, { depth: null })