Tools
A tool is an agent-callable Mastra primitive. In mastra-effect, tools are defined with Effect Schema and registered on a toolkit with Effect-ful server handlers.
MastraTool.make
import { Schema } from "effect"
import { MastraTool } from "mastra-effect"
const SearchDocs = MastraTool.make("search_docs", {
description: "Search docs",
inputSchema: Schema.Struct({ query: Schema.String }),
outputSchema: Schema.String,
})Each tool exposes tool.execute(params, context) as an Effect. Wire tools into an agent via Agent.of (preferred) or toolkit.toMastraTools() for loose composition.
For client-fulfilled human tools, attach a Tool UI schema via .annotate(ToolUI.Schema, schema) where schema.tag === tool.id:
const PickAsset = MastraTool.make("pick_asset", {
description: "Pick an asset in the browser",
inputSchema: Schema.Struct({ prompt: Schema.String }),
outputSchema: Schema.String,
}).annotate(ToolUI.Schema, PickAssetSchema)Toolkit
import { Toolkit } from "mastra-effect"
const toolkit = Toolkit.make(SearchDocs, OtherTool)| Method | Description |
|---|---|
Toolkit.make(...tools) | Build a typed toolkit |
toolkit.toMastraTools() | Build Mastra ToolsInput for the agent loop |
Toolkit.merge(...toolkits) | Merge toolkits |
toolkit.schemas holds Tool UI schemas from tool .annotate(ToolUI.Schema, …) calls.
Server handlers
Pass a handler map to Agent.of:
handlers: {
search_docs: Effect.fn(function* ({ query }) {
const api = yield* ApiKey
return `${api.token}:${query}`
}),
}Handler deps from inner yield* are required on the stream/generate Effect — provide them at the run site with Effect.provide.
TypeScript rejects annotated (client) tool keys in handlers — those are fulfilled on the client.
Annotated (client) tools
| Annotation | Execute path |
|---|---|
.annotate(ToolUI.Schema, schema) with schema.tag === tool.id | Fulfilled on the client via ClientToolkit.of + toAssistantUiTools. Server execute throws ClientToolRequiredError. |
| none | Server handler via Agent.of handlers map. |
Details — abort and tool interruption
Mastra forwards abortSignal on the tool execute context. mastra-effect propagates it into Effect:
| Path | Behavior |
|---|---|
toolkit.toMastraTools() | Effect.runPromise(handler, { signal: context.abortSignal }) at the Mastra boundary |
tool.execute / executeEffectTool | Interrupt the running Effect fiber (or pass { signal } to Effect.runPromise at the boundary) |
agent.stream → fromMastraOutput | Interrupting the Effect Stream cancels the underlying ReadableStream |
Write interruptible handlers — use Effect.sleep, Effect.tryPromise with the Effect-provided signal argument, and other interruptible Effect APIs.