Skip to main content
This example demonstrates building agents with multiple tools and shows PromptSmith’s seamless compatibility between the Vercel AI SDK and Mastra frameworks.

What You’ll Build

Multi-tool agents that:
  • Use multiple specialized tools together
  • Work with both AI SDK and Mastra
  • Mix tools from different sources
  • Automatically handle tool format conversion

Prerequisites

npm install promptsmith-ts ai @ai-sdk/openai @mastra/core zod
Set up your API keys:
export OPENAI_API_KEY="your-openai-key"
export ANTHROPIC_API_KEY="your-anthropic-key"  # For Mastra example

Example 1: AI SDK with Multiple Tools

import { createPromptBuilder } from "promptsmith-ts/builder";
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";

// Weather tool
async function getWeather(location: string, units: "celsius" | "fahrenheit") {
  return {
    location,
    temperature: units === "celsius" ? 22 : 72,
    conditions: "Partly cloudy",
    humidity: "65%",
    windSpeed: units === "celsius" ? "15 km/h" : "9 mph",
  };
}

// News tool
async function getNews(category: string, limit: number) {
  return {
    articles: [
      {
        title: "Tech Innovation Reaches New Heights",
        source: "Tech News Daily",
        category,
      },
      {
        title: "Market Analysis: Q1 Report",
        source: "Business Wire",
        category,
      },
    ].slice(0, limit),
    timestamp: new Date().toISOString(),
  };
}

// Stock price tool
async function getStockPrice(symbol: string) {
  return {
    symbol: symbol.toUpperCase(),
    price: 142.50,
    change: +2.35,
    changePercent: +1.68,
    lastUpdated: new Date().toISOString(),
  };
}

async function runAiSdkExample() {
  // Build agent with multiple tools
  const agent = createPromptBuilder()
    .withIdentity(
      "You are a helpful assistant providing real-time information"
    )
    .withCapabilities([
      "Provide current weather conditions",
      "Fetch latest news articles",
      "Look up stock prices",
      "Answer questions using available tools",
    ])
    .withTool({
      name: "get_weather",
      description: "Get current weather conditions for a location",
      schema: z.object({
        location: z.string().describe("City name or location"),
        units: z
          .enum(["celsius", "fahrenheit"])
          .default("celsius")
          .describe("Temperature units"),
      }),
      execute: async ({ location, units }) => {
        return await getWeather(location, units);
      },
    })
    .withTool({
      name: "get_news",
      description: "Fetch latest news articles by category",
      schema: z.object({
        category: z
          .enum(["technology", "business", "sports", "entertainment"])
          .describe("News category"),
        limit: z
          .number()
          .min(1)
          .max(10)
          .default(5)
          .describe("Number of articles to return"),
      }),
      execute: async ({ category, limit }) => {
        return await getNews(category, limit);
      },
    })
    .withTool({
      name: "get_stock_price",
      description: "Get current stock price for a symbol",
      schema: z.object({
        symbol: z
          .string()
          .min(1)
          .max(5)
          .describe("Stock ticker symbol (e.g., AAPL, GOOGL)"),
      }),
      execute: async ({ symbol }) => {
        return await getStockPrice(symbol);
      },
    })
    .withConstraint("must", [
      "Always use the appropriate tool for the requested information",
      "Provide context and explanation with the data",
      "Format numbers and dates in a readable way",
    ])
    .withConstraint("must_not", [
      "Make up data without using the tools",
      "Provide financial advice based on stock prices",
    ])
    .withGuardrails()
    .withTone("Informative and helpful");

  // Use with AI SDK
  const { text } = await generateText({
    model: openai("gpt-4"),
    ...agent.toAiSdk(),
    prompt:
      "What's the weather in Tokyo, any tech news, and what's Apple's stock price?",
  });

  console.log("=== AI SDK Multi-Tool Response ===\n");
  console.log(text);
}

Example 2: Mastra with Mixed Tool Formats

import { Agent } from "@mastra/core/agent";
import { createTool } from "@mastra/core/tools";
import { createPromptBuilder } from "promptsmith-ts/builder";
import { z } from "zod";

// Create a Mastra-native tool
const searchTool = createTool({
  id: "search_knowledge_base",
  description: "Search the company knowledge base",
  inputSchema: z.object({
    query: z.string().describe("Search query"),
    limit: z.number().default(5).describe("Max results"),
  }),
  execute: async ({ context }) => {
    // Mock search results
    return {
      results: [
        {
          id: "doc-1",
          title: "Getting Started Guide",
          relevance: 0.95,
          snippet: "Learn how to use our platform...",
        },
        {
          id: "doc-2",
          title: "API Reference",
          relevance: 0.87,
          snippet: "Complete API documentation...",
        },
      ].slice(0, context.limit),
      query: context.query,
    };
  },
});

// Create a PromptSmith-format tool
const calculateTool = {
  name: "calculate",
  description: "Perform mathematical calculations",
  schema: z.object({
    expression: z.string().describe("Math expression to evaluate"),
  }),
  execute: async ({ expression }: { expression: string }) => {
    // In production, use a safe expression evaluator
    // This is just for demonstration
    return {
      expression,
      result: "42",
      explanation: "Calculated using standard math operations",
    };
  },
};

async function runMastraExample() {
  // Build agent with mixed tool formats
  const agent = createPromptBuilder()
    .withIdentity("You are a helpful AI assistant")
    .withCapabilities([
      "Search the knowledge base for information",
      "Perform calculations",
      "Answer questions using available tools",
    ])
    // ✅ Mastra tool - works directly!
    .withTool(searchTool)
    // ✅ PromptSmith tool - also works!
    .withTool(calculateTool)
    .withConstraint(
      "must",
      "Always search the knowledge base before answering questions"
    )
    .withGuardrails()
    .withTone("Clear and helpful");

  // Export to Mastra format
  const { instructions, tools } = agent.toMastra();

  console.log("\n=== Tool Conversion ===\n");
  console.log(`Exported tools: ${Object.keys(tools).join(", ")}`);
  console.log(`Total tools: ${Object.keys(tools).length}\n`);

  // Create Mastra agent
  const mastraAgent = new Agent({
    name: "multi-tool-assistant",
    instructions,
    model: "anthropic/claude-3-5-sonnet",
    tools, // Both tool formats converted automatically!
  });

  // Use the agent
  const response = await mastraAgent.generate([
    {
      role: "user",
      content:
        "Search for API documentation and calculate 15 * 7",
    },
  ]);

  console.log("=== Mastra Multi-Tool Response ===\n");
  console.log(response.text);
}

Example 3: Complete Multi-Tool Application

import { createPromptBuilder } from "promptsmith-ts/builder";
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";

// Complete example with all tools
async function main() {
  const agent = createPromptBuilder()
    .withIdentity(
      "You are a comprehensive business assistant providing weather, news, stock, and calculation services"
    )
    .withCapabilities([
      "Provide weather information",
      "Fetch news articles",
      "Look up stock prices",
      "Perform calculations",
      "Search knowledge base",
    ])
    // Weather tool
    .withTool({
      name: "get_weather",
      description: "Get current weather",
      schema: z.object({
        location: z.string(),
        units: z.enum(["celsius", "fahrenheit"]).default("celsius"),
      }),
      execute: async ({ location, units }) => ({
        location,
        temperature: units === "celsius" ? 22 : 72,
        conditions: "Partly cloudy",
      }),
    })
    // News tool
    .withTool({
      name: "get_news",
      description: "Fetch latest news",
      schema: z.object({
        category: z.enum(["tech", "business", "sports"]),
        limit: z.number().default(5),
      }),
      execute: async ({ category, limit }) => ({
        articles: [
          { title: "Tech Innovation", category },
          { title: "Market Update", category },
        ].slice(0, limit),
      }),
    })
    // Stock tool
    .withTool({
      name: "get_stock",
      description: "Get stock price",
      schema: z.object({
        symbol: z.string(),
      }),
      execute: async ({ symbol }) => ({
        symbol: symbol.toUpperCase(),
        price: 142.50,
        change: +2.35,
      }),
    })
    // Calculator tool
    .withTool({
      name: "calculate",
      description: "Perform calculations",
      schema: z.object({
        expression: z.string(),
      }),
      execute: async ({ expression }) => ({
        expression,
        result: "42",
      }),
    })
    .withConstraint("must", [
      "Use appropriate tools for each request",
      "Combine information from multiple tools when relevant",
      "Provide clear, formatted responses",
    ])
    .withGuardrails()
    .withTone("Professional and efficient");

  // Validate configuration
  const validation = agent.validate();
  if (!validation.isValid) {
    console.error("Configuration errors:", validation.errors);
    return;
  }

  // Show summary
  const summary = agent.getSummary();
  console.log("=== Agent Configuration ===\n");
  console.log(`Tools: ${summary.toolsCount}`);
  console.log(`Capabilities: ${summary.capabilitiesCount}`);
  console.log(`Constraints: ${summary.constraintsCount}\n`);

  // Use the agent
  const { text } = await generateText({
    model: openai("gpt-4"),
    ...agent.toAiSdk(),
    prompt:
      "I need the weather in London, latest tech news, Apple stock price, and calculate 25% of 1000",
  });

  console.log("=== Multi-Tool Response ===\n");
  console.log(text);
}

main().catch(console.error);

Step-by-Step Walkthrough

1

Define Multiple Tools

Create separate tools for different functionalities:
const weatherTool = { name: "get_weather", ... };
const newsTool = { name: "get_news", ... };
const stockTool = { name: "get_stock", ... };
Each tool has a focused, single responsibility.
2

Add All Tools to Builder

Chain multiple withTool() calls:
const agent = createPromptBuilder()
  .withTool(weatherTool)
  .withTool(newsTool)
  .withTool(stockTool)
  .withTool(calculatorTool);
PromptSmith automatically handles tool deduplication and validates that tool names are unique.
3

Mix Tool Formats (Mastra Only)

Combine Mastra and PromptSmith tool formats:
// Mastra format
const mastraTool = createTool({ id: "search", ... });

// PromptSmith format
const promptSmithTool = { name: "calculate", ... };

createPromptBuilder()
  .withTool(mastraTool)      // Works!
  .withTool(promptSmithTool) // Also works!
Tools are automatically converted to the correct format.
4

Set Tool Usage Constraints

Guide how tools should be used together:
.withConstraint("must", [
  "Use appropriate tools for each request",
  "Combine information from multiple tools when relevant",
])
This ensures the agent uses tools effectively.
5

Export to Your Framework

Convert to AI SDK or Mastra format:
// For AI SDK
const aiSdkConfig = agent.toAiSdk();

// For Mastra
const { instructions, tools } = agent.toMastra();
All tools are automatically converted to the target format.
6

Validate Before Use

Check configuration before deployment:
const validation = agent.validate();
if (!validation.isValid) {
  console.error(validation.errors);
}
Validation catches duplicate tool names and other issues.

Expected Output

Running the complete example produces:
=== Agent Configuration ===

Tools: 4
Capabilities: 5
Constraints: 3

=== Multi-Tool Response ===

Here's the information you requested:

Weather in London:
- Temperature: 22°C
- Conditions: Partly cloudy
- Humidity: 65%

Latest Tech News:
1. "Tech Innovation Reaches New Heights" - Tech News Daily
2. "Market Analysis: Q1 Report" - Business Wire

Apple Stock (AAPL):
- Current Price: $142.50
- Change: +$2.35 (+1.68%)
- Last Updated: 2026-03-03

Calculation:
25% of 1000 = 250

Is there anything else you'd like to know?

Key Concepts

Tool Composition - Multiple specialized tools are better than one complex tool. Each tool should have a single, well-defined responsibility.
Framework Compatibility - PromptSmith automatically converts tools between formats. Define tools once, use everywhere.
Mastra Tool Support - Tools created with Mastra’s createTool() work seamlessly with PromptSmith. No manual conversion needed.
Validation - Always validate agents with multiple tools to catch duplicate names and configuration issues before deployment.

Best Practices

Tool Naming

Use clear, descriptive names:
// Good
.withTool({ name: "get_weather", ... })
.withTool({ name: "search_knowledge_base", ... })

// Avoid
.withTool({ name: "tool1", ... })
.withTool({ name: "fetch", ... })

Tool Organization

Group related tools:
// Weather tools
.withTool(getCurrentWeather)
.withTool(getWeatherForecast)

// Data tools
.withTool(queryDatabase)
.withTool(exportData)

Error Handling

Handle errors in tool execution:
execute: async ({ location }) => {
  try {
    return await fetchWeather(location);
  } catch (error) {
    return {
      error: "Failed to fetch weather",
      details: error.message,
    };
  }
}

Advanced Use Cases

Dynamic Tool Loading

const tools = [
  weatherTool,
  newsTool,
  stockTool,
];

let builder = createPromptBuilder().withIdentity("Assistant");

for (const tool of tools) {
  builder = builder.withTool(tool);
}

Conditional Tool Sets

const agent = createPromptBuilder()
  .withToolIf(hasWeatherAccess, weatherTool)
  .withToolIf(hasNewsAccess, newsTool)
  .withToolIf(isAdmin, adminTools);

Next Steps

Tools Guide

Learn advanced tool patterns and best practices

AI SDK Integration

Deep dive into Vercel AI SDK integration

Build docs developers (and LLMs) love