All skills
jwynia avatar

/mastra-hono

@99a8797
by J Wyniajwynia/agent-skills160 stars
20

Develop AI agents, tools, and workflows with Mastra v1 Beta and Hono servers. This skill should be used when creating Mastra agents, defining tools with Zod schemas, building workflows with step data flow, setting up Hono API servers with Mastra adapters, or implementing agent networks. Keywords: mastra, hono, agent, tool, workflow, AI, LLM, typescript, API, MCP.

Use this Skill: https://skilld.dev/gh/jwynia/agent-skills/mastra-hono

This session only. Nothing lands on disk.

referencestool-patterns.md

≈3.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Tool Patterns

Complete guide to creating and using tools in Mastra v1 Beta.

Basic Tool Definition

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

export const weatherTool = createTool({
  id: "get-weather",
  description: "Fetches current weather for a city. Use when user asks about " +
               "temperature, conditions, or forecast for a specific location.",
  inputSchema: z.object({
    location: z.string().describe("City name, e.g., 'Seattle' or 'Tokyo, Japan'"),
    units: z.enum(["celsius", "fahrenheit"]).optional().default("celsius")
      .describe("Temperature unit"),
  }),
  outputSchema: z.object({
    temperature: z.number(),
    conditions: z.string(),
    humidity: z.number(),
  }),
  execute: async (inputData, context) => {
    const { location, units } = inputData;
    const { abortSignal } = context;

    if (abortSignal?.aborted) throw new Error("Aborted");

    // Fetch weather data...
    return { temperature: 72, conditions: "sunny", humidity: 45 };
  },
});

v1 Beta Execute Signature (CRITICAL)

Correct v1 Beta Signature

// v1 Beta: execute(inputData, context)
execute: async (inputData, context) => {
  // First parameter: parsed input matching inputSchema
  const { location, units } = inputData;

  // Second parameter: context object
  const {
    mastra,         // Access to Mastra instance (agents, workflows)
    runtimeContext, // Request-specific values
    abortSignal,    // Abort controller signal
  } = context;

  return { result: "..." };
}

Wrong (Stable 0.24.x Signature)

// WRONG for v1 Beta - this is the stable signature
execute: async ({ context, mastra, runtimeContext }) => {
  const { location } = context;  // context contains input in stable
  // ...
}

Comparison Table

Version Signature Input Access Context Access
v1 Beta (inputData, context) inputData.field context.mastra
Stable 0.24.x ({ context, mastra }) context.field mastra

Zod Schema Best Practices

Input Schema

inputSchema: z.object({
  // Always add .describe() to help LLM understand usage
  query: z.string().describe("Search query, 1-100 characters"),

  // Use .optional() with .default() for optional params
  limit: z.number().optional().default(10).describe("Max results (1-100)"),

  // Use enums for constrained choices
  format: z.enum(["json", "text", "markdown"]).describe("Output format"),

  // Complex nested objects
  filters: z.object({
    startDate: z.string().optional().describe("ISO date string"),
    endDate: z.string().optional().describe("ISO date string"),
    categories: z.array(z.string()).optional().describe("Filter categories"),
  }).optional().describe("Optional filters for the search"),
})

Output Schema

// Always define outputSchema - prevents validation issues
outputSchema: z.object({
  results: z.array(z.object({
    title: z.string(),
    url: z.string(),
    snippet: z.string(),
  })),
  totalCount: z.number(),
  hasMore: z.boolean(),
})

Description Guidelines

// Good: Specific, actionable descriptions
description: "Fetches current weather conditions including temperature, " +
             "humidity, and conditions for a specific city. Use when the " +
             "user asks about weather, temperature, or climate in a location."

// Bad: Vague, unhelpful
description: "Gets weather data"

// Good: Field descriptions guide LLM input
location: z.string().describe("City name with optional country, e.g., 'Paris' or 'Paris, France'")

// Bad: No guidance
location: z.string()

Abort Signal Handling

Basic Pattern

execute: async (inputData, context) => {
  const { abortSignal } = context;

  // Check at start
  if (abortSignal?.aborted) throw new Error("Aborted");

  const result = await someOperation();

  // Check after long operations
  if (abortSignal?.aborted) throw new Error("Aborted");

  return result;
}

With Fetch Requests

execute: async (inputData, context) => {
  const { abortSignal } = context;

  // Pass signal to fetch
  const response = await fetch(url, {
    signal: abortSignal,
  });

  return await response.json();
}

With Iterative Operations

execute: async (inputData, context) => {
  const { abortSignal } = context;
  const results = [];

  for (const item of items) {
    // Check each iteration
    if (abortSignal?.aborted) {
      throw new Error("Aborted during processing");
    }

    results.push(await processItem(item));
  }

  return { results };
}

Tool Wrappers

Wrapping an Agent as a Tool

export const copywriterTool = createTool({
  id: "copywriter-tool",
  description: "Writes blog post copy about a given topic using AI",
  inputSchema: z.object({
    topic: z.string().describe("Topic to write about"),
    tone: z.enum(["formal", "casual", "technical"]).optional().default("casual"),
  }),
  outputSchema: z.object({
    copy: z.string(),
    wordCount: z.number(),
  }),
  execute: async (inputData, context) => {
    const { topic, tone } = inputData;
    const { mastra } = context;

    // Get agent from Mastra (NOT direct import)
    const agent = mastra?.getAgent("copywriter-agent");
    if (!agent) throw new Error("Copywriter agent not found");

    const prompt = `Write a ${tone} blog post about: ${topic}`;
    const result = await agent.generate(prompt);

    return {
      copy: result.text,
      wordCount: result.text.split(/\s+/).length,
    };
  },
});

Wrapping a Workflow as a Tool

export const dataProcessorTool = createTool({
  id: "data-processor",
  description: "Processes data through the data pipeline workflow",
  inputSchema: z.object({
    data: z.string().describe("JSON data to process"),
  }),
  outputSchema: z.object({
    result: z.string(),
    success: z.boolean(),
    duration: z.number(),
  }),
  execute: async (inputData, context) => {
    const { data } = inputData;
    const { mastra, runtimeContext } = context;

    const startTime = Date.now();

    const workflow = mastra?.getWorkflow("data-pipeline");
    if (!workflow) throw new Error("Workflow not found");

    const run = workflow.createRun();
    const result = await run.start({
      inputData: { data: JSON.parse(data) },
      runtimeContext,  // Propagate context
    });

    return {
      result: JSON.stringify(result.steps?.["final-step"]?.output),
      success: result.status === "success",
      duration: Date.now() - startTime,
    };
  },
});

Context Propagation

Propagating Runtime Context

execute: async (inputData, context) => {
  const { mastra, runtimeContext } = context;

  // Propagate to nested agent
  const agent = mastra?.getAgent("nested-agent");
  await agent?.generate("Query", { runtimeContext });

  // Propagate to nested workflow
  const workflow = mastra?.getWorkflow("nested-workflow");
  await workflow?.createRun().start({
    inputData: { value: inputData.value },
    runtimeContext,
  });
}

Accessing Runtime Context Values

execute: async (inputData, context) => {
  const { runtimeContext } = context;

  const userId = runtimeContext.get("user-id");
  const tier = runtimeContext.get("user-tier");
  const requestId = runtimeContext.get("request-id");

  // Use context values for authorization, logging, etc.
  if (tier !== "premium") {
    throw new Error("Premium subscription required");
  }

  return { result: "..." };
}

Error Handling

Structured Error Returns

outputSchema: z.object({
  success: z.boolean(),
  data: z.any().optional(),
  error: z.string().optional(),
}),
execute: async (inputData, context) => {
  try {
    const result = await riskyOperation(inputData);
    return { success: true, data: result };
  } catch (error) {
    return { success: false, error: error.message };
  }
}

Throwing vs Returning Errors

// Throw for unrecoverable errors (stops agent execution)
if (!context.mastra) {
  throw new Error("Mastra instance not available");
}

// Return for recoverable/expected errors (agent can continue)
if (!data) {
  return { success: false, error: "No data found" };
}

Tool Categories

API Integration Tools

export const githubTool = createTool({
  id: "github-api",
  description: "Interacts with GitHub API for repository operations",
  inputSchema: z.object({
    action: z.enum(["list-repos", "get-issues", "create-issue"]),
    owner: z.string(),
    repo: z.string().optional(),
    data: z.any().optional(),
  }),
  outputSchema: z.object({
    result: z.any(),
    status: z.number(),
  }),
  execute: async (inputData, context) => {
    const { action, owner, repo, data } = inputData;
    const { runtimeContext, abortSignal } = context;

    const token = runtimeContext.get("github-token");
    if (!token) throw new Error("GitHub token not configured");

    const response = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {
      headers: { Authorization: `token ${token}` },
      signal: abortSignal,
    });

    return {
      result: await response.json(),
      status: response.status,
    };
  },
});

Computation Tools

export const calculatorTool = createTool({
  id: "calculator",
  description: "Performs mathematical calculations. Supports +, -, *, /, ^, sqrt",
  inputSchema: z.object({
    expression: z.string().describe("Math expression, e.g., '(5 + 3) * 2'"),
  }),
  outputSchema: z.object({
    result: z.number(),
    expression: z.string(),
  }),
  execute: async (inputData) => {
    const { expression } = inputData;

    // Safe evaluation (use a proper math parser in production)
    const result = evaluateExpression(expression);

    return { result, expression };
  },
});

Data Retrieval Tools

export const databaseTool = createTool({
  id: "database-query",
  description: "Executes read-only database queries",
  inputSchema: z.object({
    table: z.enum(["users", "orders", "products"]),
    filters: z.record(z.string()).optional(),
    limit: z.number().optional().default(10),
  }),
  outputSchema: z.object({
    rows: z.array(z.any()),
    count: z.number(),
  }),
  execute: async (inputData, context) => {
    const { table, filters, limit } = inputData;
    const { runtimeContext } = context;

    // Get database connection from context
    const db = runtimeContext.get("database");

    const rows = await db.query(table, filters, limit);

    return { rows, count: rows.length };
  },
});

Registering Tools

In Agents

const agent = new Agent({
  name: "multi-tool-agent",
  tools: {
    weatherTool,
    calculatorTool,
    searchTool,
  },
});

In Mastra (for workflow access)

const mastra = new Mastra({
  agents: { myAgent },
  tools: {
    weatherTool,
    calculatorTool,
  },
});

// Access in workflows
const tool = mastra.getTool("weather-tool");

Testing Tools

Unit Test Pattern

import { describe, it, expect } from "vitest";

describe("Weather Tool", () => {
  it("should return weather data", async () => {
    const result = await weatherTool.execute(
      { location: "Seattle", units: "celsius" },
      {
        mastra: undefined,
        runtimeContext: new RuntimeContext(),
        abortSignal: new AbortController().signal,
      }
    );

    expect(result.temperature).toBeTypeOf("number");
    expect(result.conditions).toBeTypeOf("string");
  });
});

With Mocked Dependencies

describe("Copywriter Tool", () => {
  it("should use agent to generate copy", async () => {
    const mockMastra = {
      getAgent: vi.fn().mockReturnValue({
        generate: vi.fn().mockResolvedValue({ text: "Generated copy" }),
      }),
    };

    const result = await copywriterTool.execute(
      { topic: "AI trends" },
      {
        mastra: mockMastra as any,
        runtimeContext: new RuntimeContext(),
        abortSignal: new AbortController().signal,
      }
    );

    expect(result.copy).toBe("Generated copy");
    expect(mockMastra.getAgent).toHaveBeenCalledWith("copywriter-agent");
  });
});

Source: SKILL.md on GitHub

2 warnings16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    This skill provides a comprehensive developer toolkit for building AI agents and workflows with Mastra v1 Beta and Hono servers. It includes scaffolding scripts, code templates, and detailed documentation on best practices for agent orchestration and data flow. All external dependencies are from well-known technology organizations and the code patterns align with standard software development practices.

  • Socket16d

    1 alert: gptAnomaly

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer7mo

    22/22 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 99a8797. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Dormantupdated 8 months ago
compatibility
Node.js 22.13.0+ required for v1 Beta
Other metadata
metadata
{
  "author": "agent-skills",
  "version": "1.0",
  "type": "utility",
  "mode": "assistive",
  "domain": "development"
}

README badge

README badge for jwynia/agent-skills/mastra-hono