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}¤t=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 })