Skip to content

Latest commit

 

History

History
832 lines (660 loc) · 50.1 KB

AGENTS.md

File metadata and controls

832 lines (660 loc) · 50.1 KB

Recent Update (2026-03-17)

  • browser-tool.ts was upgraded to emit structured browser-family outputs for richer chat rendering.
  • Prefer returning preview-friendly metadata (url, finalUrl, previewUrl, title, html, text, typed result arrays) instead of text-only message payloads when the frontend can visualize the result.
  • Browser-family tools should keep data-tool-progress events intact while exposing UI-ready output schemas.

🔧 Tools Directory (/src/mastra/tools)

[stable] [30+ Tools]

Persona

Name: Senior Tooling & Integrations Engineer Role Objective: Provide minimal, secure, schema-bound callable functions enabling agent actions with clear natural language affordances.

Purpose

Encapsulate 30+ atomic operational capabilities (security checks, vector queries, content fetch, data processing) in auditable, schema-validated units invoked by agents. Supports financial intelligence, RAG pipelines, web scraping, document processing, and more with Zod schemas, full test coverage, and production-grade error handling.

createTool()

The createTool() function is used to define custom tools that your Mastra agents can execute. Tools extend an agent's capabilities by allowing it to interact with external systems, perform calculations, or access specific data.

Workspace filesystem policy

  • Do not import node:path or node:fs / node:fs/promises in tool files.
  • If a tool needs workspace file access, import mainFilesystem from ../workspaces and use its filesystem APIs.
  • Prefer mainFilesystem.stat, mainFilesystem.readFile, mainFilesystem.writeFile, mainFilesystem.appendFile, mainFilesystem.deleteFile, mainFilesystem.copyFile, mainFilesystem.moveFile, mainFilesystem.mkdir, mainFilesystem.rmdir, mainFilesystem.readdir, mainFilesystem.exists, mainFilesystem.realpath, and mainFilesystem.resolveAbsolutePath over Node filesystem calls.
  • Keep paths workspace-relative unless the tool explicitly documents another contract.
  • Use mainFilesystem.getInfo() and mainFilesystem.getInstructions() when a tool needs filesystem metadata or workspace guidance.
  • Use mainFilesystem.setAllowedPaths() only in workspace/bootstrap code, not in normal tool execution.
  • When a file operation needs binary data, keep it in Buffer / Uint8Array form and avoid creating ArrayBuffer | SharedArrayBuffer values that later break Blob typing.

Tool output and toModelOutput

  • execute returns the full application-facing result.
  • toModelOutput must be defined inline in the tool object; do not extract it into a separate formatter helper just to move model shaping logic elsewhere.
  • Use toModelOutput to transform rich tool data into the model-facing shape.
  • type: 'json' for structured results and URL-rich payloads that the model can inspect directly.
  • type: 'text' for a concise human-readable summary.
  • type: 'content' for multimodal or multi-part responses (text, image-url, file-url, etc.).
  • Keep toModelOutput output JSON-safe and omit optional keys instead of returning undefined values.
  • If a tool returns URLs, preserve them in the app-facing output schema and expose the important ones in the model-facing summary or content parts.

Usage Example

// src/mastra/tools/reverse-tool.ts
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'

export const tool = createTool({
    id: 'test-tool',
    description: 'Reverse the input string',
    inputSchema: z.object({
        input: z.string(),
    }),
    outputSchema: z.object({
        output: z.string(),
    }),
    execute: async (inputData) => {
        const reversed = inputData.input.split('').reverse().join('')

        return {
            output: reversed,
        }
    },
})

Parameters

  • id: string - A unique identifier for the tool.
  • description: string - A description of what the tool does. This is used by the agent to decide when to use the tool.
  • inputSchema?: Zod schema - A Zod schema defining the expected input parameters for the tool's execute function.
  • outputSchema?: Zod schema - A Zod schema defining the expected output structure of the tool's execute function.
  • suspendSchema?: Zod schema - A Zod schema defining the structure of the payload passed to suspend(). This payload is returned to the client when the tool suspends execution.
  • resumeSchema?: Zod schema - A Zod schema defining the expected structure of resumeData when the tool is resumed. Used by the agent to extract data from user messages when autoResumeSuspendedTools is enabled.
  • requireApproval?: boolean - When true, the tool requires explicit approval before execution. The agent will emit a tool-call-approval chunk and pause until approved or declined.
  • execute: function - The function that contains the tool's logic. It receives two parameters: the validated input data (first parameter) and an optional execution context object (second parameter) containing requestContext, tracingContext, abortSignal, and other execution metadata.
    • input: z.infer<TInput> - The validated input data based on inputSchema
    • context?: ToolExecutionContext - Optional execution context containing metadata
  • onInputStart?: function - Optional callback invoked when the tool call input streaming begins. Receives toolCallId, messages, and abortSignal.
  • onInputDelta?: function - Optional callback invoked for each incremental chunk of input text as it streams in. Receives inputTextDelta, toolCallId, messages, and abortSignal.
  • onInputAvailable?: function - Optional callback invoked when the complete tool input is available and parsed. Receives the validated input object, toolCallId, messages, and abortSignal.
  • onOutput?: function - Optional callback invoked after the tool has successfully executed and returned output. Receives the tool's output, toolCallId, messages, and abortSignal.

ToolExecutionContext

The second execute argument is the tool execution context.

  • Read context?.requestContext through a local interface that extends RequestContext.
  • Use context?.tracingContext for child spans and structured tracing.
  • Use context?.writer?.custom(...) for data-tool-progress updates.
  • Treat context?.abortSignal as authoritative cancellation state and exit early when it is already aborted.
  • Use context?.workspace when the tool needs a workspace handle.
  • Use context?.mcp only when the tool is executing under MCP and needs protocol-specific request data.
  • Avoid writing undefined into output payloads; prefer omitting the field entirely.

Returns

The createTool() function returns a Tool object.

  • Tool: object - An object representing the defined tool, ready to be added to an agent.

Tracing Implementation Patterns

All tools should implement proper tracing using Mastra's observability system. The following pattern ensures consistent, structured tracing across all tools.

Observability ('@mastra/observability', '@mastra/core/observability')

I noticed that the file references '@mastra/core/observability' for SpanType keep this consistent.

import { SpanType } from '@mastra/core/observability'
import type { TracingContext } from '@mastra/core/observability'

All tools should import SpanType from @mastra/core/observability and TracingContext from @mastra/core/observability. This is so we can maintain consistency & compatibility across the codebase.

Required Imports

IMPORTANT: Tools MUST use Mastra's tracing helpers and the execution-provided tracingContext. Do NOT import or use OpenTelemetry APIs (for example, @opentelemetry/api) directly inside tools — instrumentation and exporter configuration belong at the platform / runtime startup layer, not inside individual tools. TracingContext: Context for tracing that flows through workflow and agent execution

(alias) interface TracingContext {
    currentSpan?: {
        isInternal: boolean;
        parent?: AnySpan | undefined;
        observabilityInstance: ObservabilityInstance;
        traceState?: TraceState | undefined;
        end(options?: EndSpanOptions<keyof SpanTypeMap> | undefined): void;
        error(options: ErrorSpanOptions<keyof SpanTypeMap>): void;
        update(options: UpdateSpanOptions<keyof SpanTypeMap>): void;
        createChildSpan(options: ChildSpanOptions<SpanType.MODEL_GENERATION>): AIModelGenerationSpan;
        createChildSpan<TChildType extends SpanType>(options: ChildSpanOptions<...>): Span<...>;
        ... 24 more ...;
        isEvent: boolean;
    } | undefined;
}
import TracingContext
Context for tracing that flows through workflow and agent execution
import type { RequestContext } from '@mastra/core/request-context'
import type { InferUITool } from '@mastra/core/tools'
import { createTool } from '@mastra/core/tools'
import { SpanType } from '@mastra/core/observability'
import type { TracingContext } from '@mastra/core/observability'
import { z } from 'zod'
import { log } from '../config/logger'

Context Interface Definition

export interface ToolNameContext extends RequestContext {
    userId?: string
    workspaceId?: string
    // Add tool-specific context properties
}

Tracing Context Usage (preferred)

execute: async (inputData, context) => {
    const writer = context?.writer
    const abortSignal = context?.abortSignal
    const tracingContext: TracingContext | undefined = context?.tracingContext
    const requestCtx = context?.requestContext as ToolNameContext | undefined

    // Extract context values
    const userId = requestCtx?.userId
    const workspaceId = requestCtx?.workspaceId

    // Respect cancellation early
    if (abortSignal?.aborted ?? false) {
        throw new Error('Tool call cancelled')
    }

    // Create child span for tool execution (Mastra tracingContext)
    const toolSpan = tracingContext?.currentSpan?.createChildSpan({
        type: SpanType.TOOL_CALL,
        name: 'tool-operation-name',
        input: inputData,
        // Pass requestContext so configured ObservabilityInstance can automatically extract metadata
        requestContext: context?.requestContext,
        metadata: {
            'tool.id': 'tool-id',
            'tool.input.param': inputData.param,
            'user.id': userId,
            'workspace.id': workspaceId,
        },
    })

    // Emit a structured progress event (required)
    await writer?.custom({
        type: 'data-tool-progress',
        data: {
            status: 'in-progress',
            message: `Input: param="${inputData.param}" - 📝 Operation started`,
            stage: 'tool-id',
        },
        id: 'tool-id',
    })

    try {
        // Tool implementation
        const result = await performToolLogic(inputData)

        // Update span with successful result and finish progress
        toolSpan?.update({
            output: result,
            metadata: {
                'tool.output.success': true,
                'tool.output.resultSize': Array.isArray(result)
                    ? result.length
                    : typeof result === 'string'
                      ? result.length
                      : undefined,
            },
        })
        toolSpan?.end()

        await writer?.custom({
            type: 'data-tool-progress',
            data: {
                status: 'done',
                message: `Input: param="${inputData.param}" - ✅ Operation complete`,
                stage: 'tool-id',
            },
            id: 'tool-id',
        })

        return result
    } catch (error) {
        // Record error in span and mark progress done
        toolSpan?.error({
            error: error instanceof Error ? error : new Error(String(error)),
            endSpan: true,
        })

        await writer?.custom({
            type: 'data-tool-progress',
            data: {
                status: 'done',
                message: `Input: param="${inputData.param}" - ❌ Operation error: ${error instanceof Error ? error.message : String(error)}`,
                stage: 'tool-id',
            },
            id: 'tool-id',
        })

        throw error
    }
}

Tracing Requirements (ENFORCED)

To ensure high-quality, consistent observability across all tools, the following tracing rules are required for every tool in /src/mastra/tools:

  • Import the tracing type where needed:
    • import type { TracingContext } from '@mastra/core/observability'
  • Use typed tracingContext in the execute function:
    • const tracingContext: TracingContext | undefined = context?.tracingContext
  • Always create a TOOL_CALL child span for the primary operation and pass requestContext for automatic metadata extraction:
    • requestContext: context?.requestContext
    • type: SpanType.TOOL_CALL
  • Use tool.id and user.id / workspace.id in span metadata where applicable.
  • Emit data-tool-progress events at start and completion (stage must match the tool id).
  • Respect abortSignal early (fail fast) and record cancellations in spans.

Streaming and ChunkType

  • Any tool that uses agent.stream(...) should import ChunkType from @mastra/core/stream and type onChunk handlers with ChunkType<YourOutputType>.
  • ChunkType<OUTPUT> is the union of all stream chunk families (agent, workflow, network, and data chunks). Use it when inspecting or forwarding streaming events.
  • When accumulating streamed text, read only the typed text-delta chunks and keep the callback narrow.
  • Do not cast streamed chunks to any or invent wrapper objects just to satisfy TypeScript.

See these docs for details and examples:

  • docs/tracing-interfaces-reference.md (Tracing types)
  • docs/tracing-instances-reference.md (Observability instances & startSpan)
  • docs/creating-child-spans.md (creating and managing child spans)
  • docs/tracing-spans-reference.md (span lifecycle & semantics)
  • docs/opentelemetry-semantic-conventions.md (recommended attributes & naming)

Checklist for PRs that modify tools:

  • Add TracingContext typing and use it in execute when a tool creates spans
  • Add requestContext: context?.requestContext to createChildSpan calls
  • Use SpanType.TOOL_CALL for tool spans
  • Add/verify data-tool-progress messages with status and stage
  • Add unit tests that mock tracingContext.currentSpan.createChildSpan() to assert spans are created/updated/ended

Tool Definition Conventions

  • Keep tool configuration fields grouped predictably: id, description, inputSchema, outputSchema, hook callbacks (onInputStart, onInputDelta, onInputAvailable, onOutput), then execute.
  • Prefer explicit schema constants plus z.infer-derived types when hook payloads or execute return values need additional local typing help.
  • Avoid z.any() unless there is a strong boundary reason; prefer z.unknown() or a narrowed union/object schema.
  • Default nullable booleans/strings/numbers explicitly inside execute even when schema defaults exist if strict analysis still sees nullable inputs.
  • When a tool returns structured output and hooks inspect that output, define an outputSchema so hook payloads are strongly typed.

These rules help ensure consistent, filterable traces across Mastra and better integration with exporters and OTEL bridges.

Migration: Replacing OpenTelemetry usage in tools

If a tool currently imports and uses OpenTelemetry APIs directly (for example trace.getTracer(...), span.recordException(...), span.setStatus(...)), migrate it to the runtime tracingContext pattern above. Example migration:

- // Old: do NOT use inside tools
- import { trace, SpanStatusCode } from '@opentelemetry/api'
- const tracer = trace.getTracer('pnpm-tool')
- const span = tracer.startSpan('pnpm-build', { attributes: { name, packagePath } })
- try { /* ... */ } catch (e) { span.recordException(e); span.setStatus({ code: SpanStatusCode.ERROR, message: e.message }); span.end() }

+ // New: preferred pattern inside tools
+ const tracingContext = context?.tracingContext
+ const span = tracingContext?.currentSpan?.createChildSpan({
+   type: SpanType.TOOL_CALL,
+   name: 'pnpm-build',
+   input: { name, packagePath },
+   metadata: { 'tool.id': 'mastra_workspace_execute_command', 'operation': 'workspace-build' },
+ })
+ try { /* ... */ } catch (e) {
+   span?.error({ error: e instanceof Error ? e : new Error(String(e)), endSpan: true })
+ }

Checklist for safe migration:

  • Replace @opentelemetry/* imports inside src/mastra/tools with the tracingContext pattern.
  • Ensure each tool emits data-tool-progress events at start and completion (or stage updates).
  • Add abortSignal checks before long-running operations.
  • Use SpanType.TOOL_CALL for tool-level spans and include tool.id in span metadata.
  • Add a small unit test that mocks tracingContext.currentSpan.createChildSpan() to verify spans are created, updated, and closed.

Span Lifecycle Management

Creating Child Spans:

const childSpan = tracingContext?.currentSpan?.createChildSpan({
    type: SpanType.TOOL_CALL, // Use SpanType enum
    name: 'operation-name',
    input: inputData,
    metadata: {
        'tool.id': 'tool-id',
        'operation.type': 'specific-operation',
    },
})

Updating Spans:

span?.update({
    output: result,
    metadata: {
        'operation.status': 'success',
        'result.size': result.length,
    },
})

Ending Spans:

span?.end()

Error Handling:

span?.error({
    error: error instanceof Error ? error : new Error(String(error)),
    endSpan: true,
})

Progress Events

Emit progress events for long-running operations:

await writer?.custom({
    type: 'data-tool-progress',
    data: {
        status: 'in-progress', // "in-progress" | "done"
        message: `Input: param="${inputData.param}" - 📝 Operation in progress`,
        stage: 'tool-id',
    },
    id: 'tool-id',
})

Complete Example

See weather-tool.ts for a complete implementation example with proper tracing, error handling, and lifecycle hooks.

Categories

1. 💰 Financial Data APIs

financial

Name Description Status Exports Dependencies Links
Alpha Vantage Stock, crypto, forex data & indicators stable alphaVantageStockTool, alphaVantageCryptoTool, alphaVantageForexTool axios alpha-vantage.tool.ts
Finnhub Real-time quotes, company profiles, financials, analysis stable finnhubQuotesTool, finnhubCompanyTool, finnhubFinancialsTool, finnhubAnalysisTool axios finnhub-tools.ts
Polygon Stock/crypto quotes, aggregates, fundamentals stable polygonStockQuotesTool, polygonCryptoQuotesTool, polygonStockAggregatesTool axios polygon-tools.ts

2. 🔍 RAG & Document Processing

rag

Name Description Status Exports Dependencies Links
Document Chunking Split docs into chunks with metadata (10+ strategies) stable mdocumentChunker @langchain/text-splitter document-chunking.tool.ts
Data File Manager Versioned data file management experimental dataFileManager fs-extra data-file-manager.ts
PG SQL PostgreSQL query execution alpha pgSqlTool @mastra/pg pg-sql-tool.ts

3. 🌐 Web Scraping & Search

web

Name Description Status Exports Dependencies Links
Web Scraper Fetch/parse web content (single/batch) stable webScraperTool, batchWebScraperTool playwright, cheerio web-scraper-tool.ts
Browser Tool Browser automation alpha browserTool playwright browser-tool.ts
SerpAPI Search General web search stable serpapiSearchTool serpapi serpapi-search.tool.ts
SerpAPI News Trends News & trends search stable serpapiNewsTrendsTool serpapi serpapi-news-trends.tool.ts
SerpAPI Shopping Shopping results stable serpapiShoppingTool serpapi serpapi-shopping.tool.ts
SerpAPI Academic Local Academic & local business search stable serpapiAcademicLocalTool serpapi serpapi-academic-local.tool.ts
Arxiv Academic paper search alpha arxivTool arxiv-api arxiv.tool.ts

4. 📊 Data Conversion & Utilities

document

Name Description Status Exports Dependencies Links
CSV to JSON Convert CSV → JSON stable csvToJsonTool csv-parse csv-to-json.tool.ts
JSON to CSV Convert JSON → CSV stable jsonToCsvTool json2csv json-to-csv.tool.ts
JWT Auth JWT token validation/generation stable jwtAuthTool jose jwt-auth.tool.ts
Weather Weather data fetch alpha weatherTool axios weather-tool.ts
Evaluate Result Result evaluation experimental evaluateResultTool - evaluateResultTool.ts
Extract Learnings Insight extraction experimental extractLearningsTool - extractLearningsTool.ts

5. 🛠️ System & Editor Tools

tool

Name Description Status Exports Dependencies Links
Copywriter Agent Tool Content generation alpha copywriterAgentTool - copywriter-agent-tool.ts
Editor Agent Tool Text editing alpha editorAgentTool - editor-agent-tool.ts
Execa Tool Shell command execution experimental execaTool execa execa-tool.ts
Pnpm Tool PNPM package management alpha pnpmTool execa pnpm-tool.ts
Github GitHub API interactions alpha listRepositories, listPullRequests, listCommits, listIssues, createIssue, createRelease, getRepositoryInfo, searchCode, getFileContent, getRepoFileTree, createPullRequest, mergePullRequest, addIssueComment, getPullRequest, getIssue octokit github.ts
FS Filesystem operations stable fsTool fs-extra fs.ts
Write Note Note writing utility experimental writeNoteTool fs write-note.ts
Calendar Tool Calendar management alpha calendarTool - calendar-tool.ts
PDF PDF utilities alpha pdfTool unpdf pdf.ts
SerpAPI Config SerpAPI configuration stable Config exports serpapi serpapi-config.ts
Find Symbol Find symbol definitions (functions, classes, variables) across the codebase using semantic analysis. stable findSymbolTool ts-morph, zod, fast-glob find-symbol.tool.ts

6. 📈 Financial Chart Tools

charts

Name Description Status Exports Dependencies Links
Chart Supervisor Tool Orchestrates chart creation pipeline alpha chartSupervisorTool recharts, financial tools financial-chart-tools.ts
Chart Generator Tool Generates Recharts React components alpha chartGeneratorTool recharts financial-chart-tools.ts
Chart Data Processor Tool Transforms financial data for charts alpha chartDataProcessorTool Polygon, Finnhub, AlphaVantage financial-chart-tools.ts
Chart Type Advisor Tool Recommends optimal chart types alpha chartTypeAdvisorTool recharts financial-chart-tools.ts

Status based on test coverage & maturity: stable (tested, production-ready), alpha (basic tests), experimental (untested/prototype). 18/30+ tools tested.

Data Tool Progress Events

All tools must emit progress events with this exact format:

// ✅ CORRECT: Progress event format
await context?.writer?.custom({
    type: 'data-tool-progress',
    data: {
        status: 'in-progress', // "in-progress" | "done"
        message: `Input: key="${inputData.key}" - 📝 Descriptive progress message`,
        stage: 'tool-name', // Tool identifier (same as id)
    },
    id: 'tool-name', // Tool identifier outside data object
})

// ❌ WRONG: Don't put id inside data object
await context?.writer?.custom({
    type: 'data-tool-progress',
    data: {
        id: 'tool-name', // ❌ Wrong - id belongs outside
        message: '...',
    },
})

Example from Alpha Vantage Tool:

await context?.writer?.custom({
    type: 'data-tool-progress',
    data: {
        status: 'in-progress',
        message: `📈 Fetching Alpha Vantage crypto data for ${inputData.symbol}/${inputData.market}`,
        stage: 'alpha-vantage-crypto',
    },
    id: 'alpha-vantage-crypto',
})

Note: These conventions are tracked in the memory bank task TASK001-update-2025-12-14. Recent commits synced tool implementations and memory-bank progress notes to follow this standard.

Tool Lifecycle Hooks

Mastra tools support lifecycle hooks that allow monitoring and reacting to different stages of tool execution. These hooks provide enhanced observability and can be used for logging, analytics, validation, and real-time updates.

Available Hooks

  • onInputStart - Called when tool call input streaming begins
  • onInputDelta - Called for each incremental chunk of input text as it streams in
  • onInputAvailable - Called when complete tool input is available and parsed
  • onOutput - Called after tool execution completes successfully

Hook Execution Order

For a typical streaming tool call, the hooks are invoked in this order:

  1. onInputStart → Input streaming begins
  2. onInputDelta → Called multiple times as chunks arrive
  3. onInputAvailable → Complete input is parsed and validated
  4. Tool's execute function runs
  5. onOutput → Tool has completed successfully

Implementation requirement: Lifecycle hooks should reflect the logical execution order to improve readability. onInputStart, onInputDelta, and onInputAvailable must be declared before the tool's execute property, while onOutput should be declared after execute.

Implementation Pattern

All lifecycle hooks use structured logging with the Mastra logger for consistent, searchable logs:

import { log } from '../config/logger'

export const exampleTool = createTool({
    id: 'example-tool',
    description: 'Example tool with lifecycle hooks',
    inputSchema: z.object({
        param: z.string().describe('Input parameter'),
    }),
    outputSchema: z.object({
        result: z.string(),
    }),
    onInputStart: ({ toolCallId, messages, abortSignal }) => {
        log.info('Tool input streaming started', {
            toolCallId,
            messageCount: messages.length,
            abortSignal: abortSignal?.aborted,
            hook: 'onInputStart',
        })
    },
    onInputDelta: ({ inputTextDelta, toolCallId, messages, abortSignal }) => {
        log.info('Tool received input chunk', {
            toolCallId,
            inputTextDelta,
            messageCount: messages.length,
            abortSignal: abortSignal?.aborted,
            hook: 'onInputDelta',
        })
    },
    onInputAvailable: ({ input, toolCallId, messages, abortSignal }) => {
        log.info('Tool received input', {
            toolCallId,
            messageCount: messages.length,
            inputData: { param: input.param },
            abortSignal: abortSignal?.aborted,
            hook: 'onInputAvailable',
        })
    },
    execute: async (inputData, context) => {
        // Tool implementation
        return { result: 'success' }
    },
    onOutput: ({ output, toolCallId, toolName, abortSignal }) => {
        log.info('Tool completed', {
            toolCallId,
            toolName,
            outputData: { result: output.result },
            abortSignal: abortSignal?.aborted,
            hook: 'onOutput',
        })
    },
})

Hook Parameters

All hooks receive a parameter object with:

  • toolCallId (string): Unique identifier for this specific tool call
  • abortSignal (AbortSignal): Signal for detecting if the operation should be cancelled

Additional parameters vary by hook:

  • onInputStart, onInputDelta, onInputAvailable: messages (array): The conversation messages at the time of the tool call
  • onInputDelta: inputTextDelta (string): The incremental text chunk
  • onInputAvailable: input: The validated input data (typed according to inputSchema)
  • onOutput: output: The tool's return value (typed according to outputSchema) and toolName (string)

Error Handling

Hook errors are caught and logged automatically, but do not prevent tool execution from continuing. If a hook throws an error, it will be logged to the console but won't fail the tool call.

Current Implementation Status

Lifecycle hooks have been implemented in ALL tools with complete onInputStart, onInputDelta, onInputAvailable, and onOutput hooks:

Tool Hooks Implemented Purpose
All 40+ Tools ✅ onInputStart, onInputDelta, onInputAvailable, onOutput Complete lifecycle monitoring

Complete Implementation: All tools in /src/mastra/tools/ now have full lifecycle hook coverage for enhanced observability, debugging, and analytics.

Benefits

  • Enhanced Monitoring: Track tool execution stages in real-time
  • Better Debugging: Detailed logging for input processing and outputs
  • Analytics: Collect usage patterns and performance metrics
  • Security: Monitor authentication and authorization attempts
  • Observability: Improved visibility into tool behavior

Hook Generator Utility

A utility script is available to help generate standardized lifecycle hooks for new tools:

# Generate hooks for a new tool
node generate-hooks.js my-new-tool.ts myNewTool inputField1 inputField2

# Example output:
🔧 Generating lifecycle hooks for myNewTool

📋 Copy these hooks into your createTool definition:

onInputStart: ({ toolCallId, messages, abortSignal }) => {
  log.info('myNewTool tool input streaming started', {
    toolCallId,
    messageCount: messages.length,
    abortSignal: abortSignal?.aborted,
    hook: 'onInputStart'
  });
},
onInputDelta: ({ inputTextDelta, toolCallId, messages, abortSignal }) => {
  log.info('myNewTool received input chunk', {
    toolCallId,
    inputTextDelta,
    messageCount: messages.length,
    abortSignal: abortSignal?.aborted,
    hook: 'onInputDelta'
  });
},
onInputAvailable: ({ input, toolCallId, messages, abortSignal }) => {
  log.info('myNewTool received input', {
    toolCallId,
    messageCount: messages.length,
    inputData: {
      inputField1: input.inputField1,
      inputField2: input.inputField2
    },
    abortSignal: abortSignal?.aborted,
    hook: 'onInputAvailable',
  });
},
onOutput: ({ output, toolCallId, toolName, abortSignal }) => {
  log.info('myNewTool completed', {
    toolCallId,
    toolName,
    outputData: {
      // Add output fields here
    },
    abortSignal: abortSignal?.aborted,
    hook: 'onOutput'
  });
},

This ensures consistency across all tools and reduces boilerplate code.

RequestContext Usage Patterns

All tools should define and use typed RequestContext interfaces to ensure type safety and clear documentation of expected context values. This pattern provides better IntelliSense, prevents runtime errors, and makes the tool's context requirements explicit.

Interface Definition Pattern

import type { RequestContext } from '@mastra/core/request-context'

export interface ToolNameContext extends RequestContext {
    userId?: string
    workspaceId?: string
    temperatureUnit?: 'celsius' | 'fahrenheit'
    maxRows?: number
    // Add other context properties as needed
}

Usage in Tools

export const exampleTool = createTool({
    id: 'example-tool',
    description: 'Example tool with typed RequestContext',
    inputSchema: z.object({
        param: z.string(),
    }),
    outputSchema: z.object({
        result: z.string(),
    }),
    execute: async (inputData, context) => {
        // Extract typed context
        const requestContext = context?.requestContext as ToolNameContext

        // Access context values with type safety
        const userId = requestContext?.userId
        const workspaceId = requestContext?.workspaceId
        const temperatureUnit = requestContext?.temperatureUnit ?? 'celsius'
        const maxRows = requestContext?.maxRows ?? 1000

        // Use context values in tool logic
        log.info('Tool executed with context', {
            userId,
            workspaceId,
            temperatureUnit,
            maxRows,
        })

        return { result: 'success' }
    },
})

Benefits

  • Type Safety: Prevents typos and ensures correct property access
  • Documentation: Interface serves as living documentation of context requirements
  • IntelliSense: Better IDE support and autocomplete
  • Runtime Safety: Catches context usage errors at compile time
  • Consistency: Standardized pattern across all tools

Common Context Properties

  • userId (string): Current user identifier for user-specific data filtering
  • workspaceId (string): Workspace/organization ID for multi-tenant data isolation
  • temperatureUnit ('celsius' | 'fahrenheit'): Temperature display preference for weather tools
  • maxRows (number): Maximum result limit for data processing tools
  • locale (string): User locale/language for internationalization
  • permissions (string[]): User permission array for authorization checks

Implementation Checklist

When adding RequestContext to a tool:

  • Define interface extending RequestContext
  • Include all context properties the tool uses
  • Use optional properties (?:) for non-required context
  • Cast context in execute function: as ToolNameContext
  • Provide sensible defaults for optional properties
  • Document context requirements in tool description if needed

Testing

97% coverage via Vitest. Run:

npm test src/mastra/tools/tests  # All
npm test src/mastra/tools/tests/your-tool.test.ts  # Specific
npm run coverage  # Report

Dependencies

From package.json: @mastra/core, zod, serpapi, playwright, cheerio, unpdf, axios, @mastra/pg, financial SDKs.


Last updated: 2025-12-15

Related

Changelog

Version Date Changes
2.16.0 2025-12-15 TEST COVERAGE: Fixed 3 test files (json-to-csv, data-validator, csv-to-json), added weather-tool.test.ts with 5 tests. All 29 tests now passing. Tracing verified across all 67 tools using SpanType.TOOL_CALL pattern.
  • 2026-01-11: Added unit tests for Finnhub tools (finnhub-quotes & finnhub-company) and improved existing financial tool tests (alpha-vantage) to mock httpFetch and make assertions more robust (error handling, missing-key behavior, lifecycle events).

| 2.15.0 | 2025-12-15 | COMPLETE: All 40+ tools now have full lifecycle hooks (onInputStart, onInputDelta, onInputAvailable, onOutput) with abortSignal logging | | 2.14.0 | 2025-12-15 | Extended Tool Lifecycle Hooks to 15 tools with onInputDelta + abortSignal: added arxiv, browser, serpapi-academic-local | | 2.11.0 | 2025-12-15 | Extended Tool Lifecycle Hooks to 6 tools with onInputDelta + abortSignal: github.ts, serpapi-search, csv-to-json, json-to-csv | | 2.10.0 | 2025-12-15 | Extended Tool Lifecycle Hooks to 17 tools + added hook generator utility script | | 2.9.0 | 2025-12-15 | Fixed writer progress messages and lifecycle hooks to properly display Input data in UI messages | | 2.8.0 | 2025-12-15 | Extended Tool Lifecycle Hooks to 14 tools: added polygon-tools, arxiv, browser-tool + fixed writer event formats | | 2.7.0 | 2025-12-15 | Extended Tool Lifecycle Hooks to 11 tools with structured logging: added json-to-csv, serpapi-search, find-symbol | | 2.6.0 | 2025-12-15 | Extended Tool Lifecycle Hooks to 8 tools: added alpha-vantage.tool.ts and fs.ts | | 2.5.0 | 2025-12-15 | Added Tool Lifecycle Hooks documentation and implementation examples | | 2.4.0 | 2025-12-14 | Custom Agent nest events | | 2.3.0 | 2025-11-28 | Added Financial Chart Tools: chartSupervisorTool, chartGeneratorTool, chartDataProcessorTool, chartTypeAdvisorTool | | 2.2.0 | 2025-11-27 | Full 30+ tools catalogued w/ badges, categories (financial/RAG/web/document), tests status, relative links | | 2.1.0 | 2025-11-26 | Meta update, 30+ claim | | 2.0.0 | 2025-11-16 | Reorg by categories |