Skip to main content
Mastra tools can be called directly, used by agents, or executed in workflows. This guide covers all tool calling patterns.

Using Tools with Agents

Agents can automatically call tools based on their descriptions and schemas:
import { Agent } from '@mastra/core/agent';
import { createTool } from '@mastra/core/tools';
import { z } from 'zod';

const weatherTool = createTool({
  id: 'get-weather',
  description: 'Get current weather for a location',
  inputSchema: z.object({
    location: z.string(),
  }),
  execute: async (input) => {
    return await fetchWeather(input.location);
  },
});

const agent = new Agent({
  id: 'weather-assistant',
  name: 'Weather Assistant',
  instructions: 'You help users check the weather',
  model: 'openai/gpt-4',
  tools: {
    weatherTool,
  },
});

const response = await agent.generate('What is the weather in San Francisco?');

Using Tools in Workflows

Tools can be called as workflow steps:
import { Workflow } from '@mastra/core/workflows';
import { z } from 'zod';

const workflow = new Workflow({
  id: 'weather-report',
  description: 'Generate a weather report',
  inputSchema: z.object({
    cities: z.array(z.string()),
  }),
});

workflow
  .step(weatherTool, {
    variables: { location: { step: 'trigger', path: 'cities.0' } },
  })
  .then(weatherTool, {
    variables: { location: { step: 'trigger', path: 'cities.1' } },
  })
  .commit();

const run = await workflow.createRun();
const result = await run.start({
  inputData: { cities: ['New York', 'London'] },
});

Direct Tool Execution

Call tools directly for testing or programmatic use:
const tool = createTool({
  id: 'calculate',
  description: 'Perform calculations',
  inputSchema: z.object({
    operation: z.enum(['add', 'subtract']),
    a: z.number(),
    b: z.number(),
  }),
  execute: async (input) => {
    if (input.operation === 'add') {
      return { result: input.a + input.b };
    }
    return { result: input.a - input.b };
  },
});

// Direct execution
const result = await tool.execute?.({
  operation: 'add',
  a: 5,
  b: 3,
});

console.log(result); // { result: 8 }

Tool Execution with Context

Pass context for access to Mastra resources:
import { RequestContext } from '@mastra/core/request-context';

const authenticatedTool = createTool({
  id: 'get-user-data',
  description: 'Get user-specific data',
  execute: async (input, context) => {
    const userId = context?.requestContext?.get('userId');
    const apiClient = context?.requestContext?.get('apiClient');
    
    return await apiClient.getUserData(userId);
  },
});

// Execute with context
const requestContext = new RequestContext();
requestContext.set('userId', '123');
requestContext.set('apiClient', myApiClient);

const result = await authenticatedTool.execute?.({}, {
  requestContext,
  mastra,
});

Handling Tool Approval

For tools marked with requireApproval: true, implement approval handling:
const agent = new Agent({
  id: 'admin-agent',
  name: 'Admin Agent',
  instructions: 'You help with administrative tasks',
  model: 'openai/gpt-4',
  tools: {
    deleteFileTool, // requireApproval: true
  },
});

// Implement approval handler
const response = await agent.generate('Delete the log file', {
  onToolCall: async ({ toolCall, approve, reject }) => {
    if (toolCall.requiresApproval) {
      const userConsent = await promptUser(
        `Allow deletion of ${toolCall.args.filepath}?`
      );
      
      if (userConsent) {
        await approve();
      } else {
        await reject('User denied file deletion');
      }
    }
  },
});

Streaming Tool Output

Stream tool output in real-time:
const streamingTool = createTool({
  id: 'process-data',
  description: 'Process data with progress updates',
  execute: async (input, context) => {
    const writer = context?.writer;
    
    for (let i = 0; i < 10; i++) {
      await processChunk(i);
      
      // Stream progress updates
      await writer?.write({
        type: 'progress',
        value: { progress: (i + 1) * 10, step: i + 1 },
      });
    }
    
    return { completed: true };
  },
});

const agent = new Agent({
  id: 'processor',
  tools: { streamingTool },
  model: 'openai/gpt-4',
});

const stream = await agent.stream('Process the data');

for await (const chunk of stream) {
  if (chunk.type === 'tool-output') {
    console.log('Tool output:', chunk.value);
  }
}

Error Handling

Handle tool execution errors gracefully:
const resilientTool = createTool({
  id: 'api-call',
  description: 'Call external API',
  execute: async (input) => {
    try {
      const response = await fetch(input.url);
      
      if (!response.ok) {
        throw new Error(`API returned ${response.status}`);
      }
      
      return await response.json();
    } catch (error) {
      // Return error information instead of throwing
      return {
        error: true,
        message: error instanceof Error ? error.message : 'Unknown error',
      };
    }
  },
});

Validation Errors

Validation errors are automatically returned when input doesn’t match the schema:
const tool = createTool({
  id: 'validate',
  inputSchema: z.object({
    email: z.string().email(),
    age: z.number().min(0).max(120),
  }),
  execute: async (input) => {
    return { valid: true };
  },
});

// Invalid input
const result = await tool.execute?.({
  email: 'not-an-email',
  age: 150,
});

// Result will be a ValidationError with details
if ('error' in result) {
  console.log(result.error.errors);
  // [
  //   { path: ['email'], message: 'Invalid email' },
  //   { path: ['age'], message: 'Number must be less than or equal to 120' }
  // ]
}

Dynamic Tool Selection

Agents automatically select appropriate tools based on the conversation:
const agent = new Agent({
  id: 'multi-tool-agent',
  instructions: 'You can check weather, search the web, and send emails',
  model: 'openai/gpt-4',
  tools: {
    weatherTool,
    searchTool,
    emailTool,
  },
});

// Agent will automatically choose the right tool
const response = await agent.generate(
  'What is the weather in Tokyo and email it to [email protected]'
);
// Agent will call: weatherTool -> emailTool

Next Steps

Creating Tools

Learn how to create custom tools

MCP Overview

Connect tools via Model Context Protocol

Build docs developers (and LLMs) love