Skip to main content

Agent Tools

Tools extend agent capabilities by allowing them to perform actions, access external systems, and manipulate data. This guide covers creating tools and assigning them to agents.

What are Tools?

Tools are type-safe functions that agents can call during conversations. Each tool has:
  • Unique ID - Identifier for the tool
  • Description - What the tool does (used by LLM for selection)
  • Input Schema - Zod schema for parameter validation
  • Execute Function - The actual logic to run
  • Optional Output Schema - Validate tool results

Creating Tools

Basic Tool

From: packages/core/src/tools/tool.ts
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().describe('City name or zip code'),
    units: z.enum(['celsius', 'fahrenheit']).optional().default('fahrenheit'),
  }),
  execute: async ({ location, units }) => {
    // Call weather API
    const response = await fetch(`https://api.weather.com/${location}`);
    const data = await response.json();
    
    return {
      temperature: data.temp,
      condition: data.condition,
      units,
    };
  },
});

Tool with Output Validation

import { createTool } from '@mastra/core/tools';
import { z } from 'zod';

const stockPriceTool = createTool({
  id: 'get-stock-price',
  description: 'Get current stock price for a ticker symbol',
  inputSchema: z.object({
    symbol: z.string().describe('Stock ticker symbol (e.g., AAPL)'),
  }),
  outputSchema: z.object({
    symbol: z.string(),
    price: z.number(),
    change: z.number(),
    changePercent: z.number(),
  }),
  execute: async ({ symbol }) => {
    const data = await fetchStockData(symbol);
    
    // Output is validated against outputSchema
    return {
      symbol: data.symbol,
      price: data.currentPrice,
      change: data.priceChange,
      changePercent: data.changePercent,
    };
  },
});

Tool with Approval Required

For sensitive operations, require user approval:
const deleteFileTool = createTool({
  id: 'delete-file',
  description: 'Delete a file from the system',
  requireApproval: true, // User must approve
  inputSchema: z.object({
    filepath: z.string().describe('Path to file to delete'),
  }),
  execute: async ({ filepath }) => {
    await fs.unlink(filepath);
    return { deleted: true, filepath };
  },
});

Tool with Mastra Integration

Access Mastra resources in tools:
const saveDataTool = createTool({
  id: 'save-data',
  description: 'Save data to persistent storage',
  inputSchema: z.object({
    key: z.string(),
    value: z.any(),
  }),
  execute: async ({ key, value }, context) => {
    // Access Mastra storage through context
    const storage = context?.mastra?.getStorage();
    
    if (!storage) {
      throw new Error('Storage not available');
    }
    
    await storage.set(key, value);
    return { saved: true, key };
  },
});

Assigning Tools to Agents

Static Tool Assignment

Assign tools when creating the agent:
import { Agent } from '@mastra/core/agent';

const agent = new Agent({
  id: 'weather-agent',
  name: 'Weather Agent',
  instructions: 'Help users with weather information',
  model: 'openai/gpt-4o',
  tools: {
    weatherTool,
    stockPriceTool,
  },
});

Dynamic Tool Assignment

From: examples/agent/src/mastra/agents/dynamic-tools-agent.ts Load tools based on request context:
import { Agent } from '@mastra/core/agent';
import { RequestContext } from '@mastra/core/request-context';

const agent = new Agent({
  id: 'dynamic-agent',
  name: 'Dynamic Agent',
  instructions: 'You are a versatile assistant',
  model: 'openai/gpt-4o',
  // Tools function receives requestContext
  tools: ({ requestContext }) => {
    const userTier = requestContext.get('userTier');
    const permissions = requestContext.get('permissions');
    
    const tools: any = {
      weatherTool,
    };
    
    // Premium users get additional tools
    if (userTier === 'premium') {
      tools.stockPriceTool = stockPriceTool;
      tools.advancedAnalysisTool = advancedAnalysisTool;
    }
    
    // Admin users get admin tools
    if (permissions.includes('admin')) {
      tools.deleteFileTool = deleteFileTool;
    }
    
    return tools;
  },
});

// Use with context
const ctx = new RequestContext();
ctx.set('userTier', 'premium');
ctx.set('permissions', ['user', 'admin']);

const result = await agent.generate('Check the weather', {
  requestContext: ctx,
});

Tool Discovery Pattern

From: examples/agent/src/mastra/agents/dynamic-tools-agent.ts Let agents discover and load tools on demand:
import { Agent } from '@mastra/core/agent';
import { Memory } from '@mastra/memory';
import { ToolSearchProcessor } from '@mastra/core/processors';

const toolSearchProcessor = new ToolSearchProcessor({
  tools: {
    calculator_add: calculatorAdd,
    calculator_multiply: calculatorMultiply,
    calculator_divide: calculatorDivide,
    get_stock_price: getStockPrice,
    translate_text: translateText,
    send_notification: sendNotification,
  },
  search: {
    topK: 5, // Return top 5 matches
  },
});

export const dynamicToolsAgent = new Agent({
  id: 'dynamic-tools-agent',
  name: 'Dynamic Tools Agent',
  description: 'An agent that discovers tools on demand',
  instructions: `You are a helpful assistant with a large library of tools.

  You do NOT have direct access to most tools. Instead, you have:
  1. **search_tools**: Search for tools by keyword
  2. **load_tool**: Load a tool by name

  Workflow:
  1. When you need a capability, use search_tools
  2. Review results and pick the most relevant
  3. Use load_tool to load it
  4. The tool will be available on your next response`,
  model: 'openai/gpt-4o',
  memory: new Memory(),
  inputProcessors: [toolSearchProcessor],
});

Runtime Tool Assignment

Override agent tools for specific calls:
const agent = new Agent({
  id: 'basic-agent',
  name: 'Basic Agent',
  instructions: 'You are helpful',
  model: 'openai/gpt-4o',
  tools: {
    weatherTool,
  },
});

// Use additional tools for this call only
const result = await agent.generate('Send a notification', {
  clientTools: {
    notificationTool,
  },
});

Tool Execution Control

Tool Choice Strategies

Control when and how tools are called:
// Let model decide (default)
const result = await agent.generate('What is the weather?', {
  toolChoice: 'auto',
});

// Require tool usage
const result = await agent.generate('Get weather for NYC', {
  toolChoice: 'required',
});

// Force specific tool
const result = await agent.generate('Check weather', {
  toolChoice: { type: 'tool', toolName: 'get-weather' },
});

// Disable tools
const result = await agent.generate('Just chat', {
  toolChoice: 'none',
});

Active Tools Filter

Limit which tools are available:
const agent = new Agent({
  id: 'agent',
  name: 'Agent',
  instructions: 'You are helpful',
  model: 'openai/gpt-4o',
  tools: {
    weatherTool,
    stockTool,
    calculatorTool,
    deleteTool,
  },
});

// Only allow specific tools for this call
const result = await agent.generate('Help me', {
  activeTools: ['get-weather', 'get-stock-price'],
});

Tool Approval

Require approval for sensitive operations:
const result = await agent.stream('Delete old logs', {
  requireToolApproval: true, // Approve all tools
});

for await (const chunk of result.fullStream) {
  if (chunk.type === 'tool-call') {
    console.log('Tool call pending approval:', chunk.toolName);
    
    // Approve or reject
    await chunk.approve(); // or chunk.reject('Not allowed')
  }
}

Tool Examples from Source

Cooking Tool

From: examples/agent/src/mastra/agents/model-v2-agent.ts
const cookingTool = createTool({
  id: 'get-recipe',
  description: 'Get a recipe for a given ingredient',
  inputSchema: z.object({
    ingredient: z.string().describe('Main ingredient'),
  }),
  execute: async ({ ingredient }) => {
    // Fetch recipe from API
    return {
      name: `${ingredient} Recipe`,
      ingredients: ['...'],
      steps: ['...'],
    };
  },
});

Calculator Tool

From: examples/agent/src/mastra/agents/dynamic-tools-agent.ts
const calculatorAdd = createTool({
  id: 'calculator-add',
  description: 'Add two numbers together',
  inputSchema: z.object({
    a: z.number().describe('First number'),
    b: z.number().describe('Second number'),
  }),
  execute: async ({ a, b }) => {
    return { result: a + b };
  },
});

Database Search Tool

const searchDatabase = createTool({
  id: 'search-database',
  description: 'Search the database for records',
  inputSchema: z.object({
    query: z.string().describe('Search query'),
    limit: z.number().optional().default(10),
  }),
  outputSchema: z.object({
    results: z.array(z.object({
      id: z.string(),
      title: z.string(),
      content: z.string(),
    })),
    total: z.number(),
  }),
  execute: async ({ query, limit }) => {
    const results = await db.search(query, limit);
    return {
      results,
      total: results.length,
    };
  },
});

Workspace Tools

When you attach a workspace to an agent, file operation tools are automatically added:
import { Workspace, LocalFilesystem } from '@mastra/core/workspace';

const workspace = new Workspace({
  filesystem: new LocalFilesystem({
    basePath: './data',
  }),
});

const agent = new Agent({
  id: 'file-agent',
  name: 'File Agent',
  instructions: 'You can work with files',
  model: 'openai/gpt-4o',
  workspace, // Injects file tools automatically
});

// Agent can now use:
// - write_file
// - read_file
// - list_files
// - delete_file
// - create_directory

Tool Best Practices

Tool descriptions help the LLM decide when to use each tool:
// Good
description: 'Get current weather conditions and forecast for a specific location'

// Avoid
description: 'Weather'
Add .describe() to schema fields:
inputSchema: z.object({
  location: z.string().describe('City name or zip code'),
  units: z.enum(['C', 'F']).describe('Temperature units'),
})
Use schemas to catch errors early:
createTool({
  inputSchema: z.object({ /* ... */ }),
  outputSchema: z.object({ /* ... */ }),
  execute: async (input) => {
    // Output automatically validated
    return result;
  },
})
Return user-friendly error messages:
execute: async ({ query }) => {
  try {
    return await searchAPI(query);
  } catch (error) {
    return {
      error: true,
      message: 'Failed to search. Please try again.',
    };
  }
}
Protect destructive actions:
createTool({
  id: 'delete-data',
  requireApproval: true,
  // ...
})

Next Steps

Streaming

Learn about streaming agent responses

Structured Output

Get typed, validated output from agents

Workflows

Execute multi-step workflows from agents

Tool API Reference

Complete API documentation for tools

Build docs developers (and LLMs) love