browser-tool.tswas 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-onlymessagepayloads when the frontend can visualize the result. - Browser-family tools should keep
data-tool-progressevents intact while exposing UI-ready output schemas.
Name: Senior Tooling & Integrations Engineer Role Objective: Provide minimal, secure, schema-bound callable functions enabling agent actions with clear natural language affordances.
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.
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.
- Do not import
node:pathornode:fs/node:fs/promisesin tool files. - If a tool needs workspace file access, import
mainFilesystemfrom../workspacesand 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, andmainFilesystem.resolveAbsolutePathover Node filesystem calls. - Keep paths workspace-relative unless the tool explicitly documents another contract.
- Use
mainFilesystem.getInfo()andmainFilesystem.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/Uint8Arrayform and avoid creatingArrayBuffer | SharedArrayBuffervalues that later breakBlobtyping.
executereturns the full application-facing result.toModelOutputmust be defined inline in the tool object; do not extract it into a separate formatter helper just to move model shaping logic elsewhere.- Use
toModelOutputto 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
toModelOutputoutput JSON-safe and omit optional keys instead of returningundefinedvalues. - 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.
// 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,
}
},
})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'sexecutefunction.outputSchema?:Zod schema- A Zod schema defining the expected output structure of the tool'sexecutefunction.suspendSchema?:Zod schema- A Zod schema defining the structure of the payload passed tosuspend(). This payload is returned to the client when the tool suspends execution.resumeSchema?:Zod schema- A Zod schema defining the expected structure ofresumeDatawhen the tool is resumed. Used by the agent to extract data from user messages whenautoResumeSuspendedToolsis enabled.requireApproval?:boolean- When true, the tool requires explicit approval before execution. The agent will emit atool-call-approvalchunk 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) containingrequestContext,tracingContext,abortSignal, and other execution metadata.input:z.infer<TInput>- The validated input data based on inputSchemacontext?:ToolExecutionContext- Optional execution context containing metadata
onInputStart?:function- Optional callback invoked when the tool call input streaming begins. ReceivestoolCallId,messages, andabortSignal.onInputDelta?:function- Optional callback invoked for each incremental chunk of input text as it streams in. ReceivesinputTextDelta,toolCallId,messages, andabortSignal.onInputAvailable?:function- Optional callback invoked when the complete tool input is available and parsed. Receives the validatedinputobject,toolCallId,messages, andabortSignal.onOutput?:function- Optional callback invoked after the tool has successfully executed and returned output. Receives the tool'soutput,toolCallId,messages, andabortSignal.
The second execute argument is the tool execution context.
- Read
context?.requestContextthrough a local interface that extendsRequestContext. - Use
context?.tracingContextfor child spans and structured tracing. - Use
context?.writer?.custom(...)fordata-tool-progressupdates. - Treat
context?.abortSignalas authoritative cancellation state and exit early when it is already aborted. - Use
context?.workspacewhen the tool needs a workspace handle. - Use
context?.mcponly when the tool is executing under MCP and needs protocol-specific request data. - Avoid writing
undefinedinto output payloads; prefer omitting the field entirely.
The createTool() function returns a Tool object.
Tool:object- An object representing the defined tool, ready to be added to an agent.
All tools should implement proper tracing using Mastra's observability system. The following pattern ensures consistent, structured tracing across all tools.
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.
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 executionimport 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'export interface ToolNameContext extends RequestContext {
userId?: string
workspaceId?: string
// Add tool-specific context properties
}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
}
}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?.requestContexttype: SpanType.TOOL_CALL
- Use
tool.idanduser.id/workspace.idin span metadata where applicable. - Emit
data-tool-progressevents at start and completion (stage must match the tool id). - Respect
abortSignalearly (fail fast) and record cancellations in spans.
- Any tool that uses
agent.stream(...)should importChunkTypefrom@mastra/core/streamand typeonChunkhandlers withChunkType<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-deltachunks and keep the callback narrow. - Do not cast streamed chunks to
anyor 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
TracingContexttyping and use it inexecutewhen a tool creates spans - Add
requestContext: context?.requestContexttocreateChildSpancalls - Use
SpanType.TOOL_CALLfor tool spans - Add/verify
data-tool-progressmessages withstatusandstage - Add unit tests that mock
tracingContext.currentSpan.createChildSpan()to assert spans are created/updated/ended
- Keep tool configuration fields grouped predictably:
id,description,inputSchema,outputSchema, hook callbacks (onInputStart,onInputDelta,onInputAvailable,onOutput), thenexecute. - 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; preferz.unknown()or a narrowed union/object schema. - Default nullable booleans/strings/numbers explicitly inside
executeeven when schema defaults exist if strict analysis still sees nullable inputs. - When a tool returns structured output and hooks inspect that output, define an
outputSchemaso hook payloads are strongly typed.
These rules help ensure consistent, filterable traces across Mastra and better integration with exporters and OTEL bridges.
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 insidesrc/mastra/toolswith thetracingContextpattern. - Ensure each tool emits
data-tool-progressevents at start and completion (or stage updates). - Add
abortSignalchecks before long-running operations. - Use
SpanType.TOOL_CALLfor tool-level spans and includetool.idin span metadata. - Add a small unit test that mocks
tracingContext.currentSpan.createChildSpan()to verify spans are created, updated, and closed.
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,
})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',
})See weather-tool.ts for a complete implementation example with proper tracing, error handling, and lifecycle hooks.
| Name | Description | Status | Exports | Dependencies | Links |
|---|---|---|---|---|---|
| Alpha Vantage | Stock, crypto, forex data & indicators | alphaVantageStockTool, alphaVantageCryptoTool, alphaVantageForexTool |
axios |
alpha-vantage.tool.ts | |
| Finnhub | Real-time quotes, company profiles, financials, analysis | finnhubQuotesTool, finnhubCompanyTool, finnhubFinancialsTool, finnhubAnalysisTool |
axios |
finnhub-tools.ts | |
| Polygon | Stock/crypto quotes, aggregates, fundamentals | polygonStockQuotesTool, polygonCryptoQuotesTool, polygonStockAggregatesTool |
axios |
polygon-tools.ts |
| Name | Description | Status | Exports | Dependencies | Links |
|---|---|---|---|---|---|
| Document Chunking | Split docs into chunks with metadata (10+ strategies) | mdocumentChunker |
@langchain/text-splitter |
document-chunking.tool.ts | |
| Data File Manager | Versioned data file management | dataFileManager |
fs-extra |
data-file-manager.ts | |
| PG SQL | PostgreSQL query execution | pgSqlTool |
@mastra/pg |
pg-sql-tool.ts |
| Name | Description | Status | Exports | Dependencies | Links |
|---|---|---|---|---|---|
| Web Scraper | Fetch/parse web content (single/batch) | webScraperTool, batchWebScraperTool |
playwright, cheerio |
web-scraper-tool.ts | |
| Browser Tool | Browser automation | browserTool |
playwright |
browser-tool.ts | |
| SerpAPI Search | General web search | serpapiSearchTool |
serpapi |
serpapi-search.tool.ts | |
| SerpAPI News Trends | News & trends search | serpapiNewsTrendsTool |
serpapi |
serpapi-news-trends.tool.ts | |
| SerpAPI Shopping | Shopping results | serpapiShoppingTool |
serpapi |
serpapi-shopping.tool.ts | |
| SerpAPI Academic Local | Academic & local business search | serpapiAcademicLocalTool |
serpapi |
serpapi-academic-local.tool.ts | |
| Arxiv | Academic paper search | arxivTool |
arxiv-api |
arxiv.tool.ts |
| Name | Description | Status | Exports | Dependencies | Links |
|---|---|---|---|---|---|
| CSV to JSON | Convert CSV → JSON | csvToJsonTool |
csv-parse |
csv-to-json.tool.ts | |
| JSON to CSV | Convert JSON → CSV | jsonToCsvTool |
json2csv |
json-to-csv.tool.ts | |
| JWT Auth | JWT token validation/generation | jwtAuthTool |
jose |
jwt-auth.tool.ts | |
| Weather | Weather data fetch | weatherTool |
axios |
weather-tool.ts | |
| Evaluate Result | Result evaluation | evaluateResultTool |
- | evaluateResultTool.ts | |
| Extract Learnings | Insight extraction | extractLearningsTool |
- | extractLearningsTool.ts |
| Name | Description | Status | Exports | Dependencies | Links |
|---|---|---|---|---|---|
| Copywriter Agent Tool | Content generation | copywriterAgentTool |
- | copywriter-agent-tool.ts | |
| Editor Agent Tool | Text editing | editorAgentTool |
- | editor-agent-tool.ts | |
| Execa Tool | Shell command execution | execaTool |
execa |
execa-tool.ts | |
| Pnpm Tool | PNPM package management | pnpmTool |
execa |
pnpm-tool.ts | |
| Github | GitHub API interactions | listRepositories, listPullRequests, listCommits, listIssues, createIssue, createRelease, getRepositoryInfo, searchCode, getFileContent, getRepoFileTree, createPullRequest, mergePullRequest, addIssueComment, getPullRequest, getIssue |
octokit |
github.ts | |
| FS | Filesystem operations | fsTool |
fs-extra |
fs.ts | |
| Write Note | Note writing utility | writeNoteTool |
fs |
write-note.ts | |
| Calendar Tool | Calendar management | calendarTool |
- | calendar-tool.ts | |
| PDF utilities | pdfTool |
unpdf |
pdf.ts | ||
| SerpAPI Config | SerpAPI configuration | Config exports | serpapi |
serpapi-config.ts | |
| Find Symbol | Find symbol definitions (functions, classes, variables) across the codebase using semantic analysis. | findSymbolTool |
ts-morph, zod, fast-glob |
find-symbol.tool.ts |
| Name | Description | Status | Exports | Dependencies | Links |
|---|---|---|---|---|---|
| Chart Supervisor Tool | Orchestrates chart creation pipeline | chartSupervisorTool |
recharts, financial tools |
financial-chart-tools.ts | |
| Chart Generator Tool | Generates Recharts React components | chartGeneratorTool |
recharts |
financial-chart-tools.ts | |
| Chart Data Processor Tool | Transforms financial data for charts | chartDataProcessorTool |
Polygon, Finnhub, AlphaVantage | financial-chart-tools.ts | |
| Chart Type Advisor Tool | Recommends optimal chart types | 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.
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.
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.
onInputStart- Called when tool call input streaming beginsonInputDelta- Called for each incremental chunk of input text as it streams inonInputAvailable- Called when complete tool input is available and parsedonOutput- Called after tool execution completes successfully
For a typical streaming tool call, the hooks are invoked in this order:
onInputStart→ Input streaming beginsonInputDelta→ Called multiple times as chunks arriveonInputAvailable→ Complete input is parsed and validated- Tool's
executefunction runs 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.
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',
})
},
})All hooks receive a parameter object with:
toolCallId(string): Unique identifier for this specific tool callabortSignal(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 callonInputDelta:inputTextDelta(string): The incremental text chunkonInputAvailable:input: The validated input data (typed according toinputSchema)onOutput:output: The tool's return value (typed according tooutputSchema) andtoolName(string)
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.
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.
- 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
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.
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.
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
}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' }
},
})- 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
userId(string): Current user identifier for user-specific data filteringworkspaceId(string): Workspace/organization ID for multi-tenant data isolationtemperatureUnit('celsius' | 'fahrenheit'): Temperature display preference for weather toolsmaxRows(number): Maximum result limit for data processing toolslocale(string): User locale/language for internationalizationpermissions(string[]): User permission array for authorization checks
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
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 # ReportFrom package.json: @mastra/core, zod, serpapi, playwright, cheerio, unpdf, axios, @mastra/pg, financial SDKs.
Last updated: 2025-12-15
| 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 |