Skip to content

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 ​

ts
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:

ts
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 ​

ts
import { Toolkit } from "mastra-effect"

const toolkit = Toolkit.make(SearchDocs, OtherTool)
MethodDescription
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:

ts
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 ​

AnnotationExecute path
.annotate(ToolUI.Schema, schema) with schema.tag === tool.idFulfilled on the client via ClientToolkit.of + toAssistantUiTools. Server execute throws ClientToolRequiredError.
noneServer handler via Agent.of handlers map.

See Client tools & Tool UI.

Details — abort and tool interruption ​

Mastra forwards abortSignal on the tool execute context. mastra-effect propagates it into Effect:

PathBehavior
toolkit.toMastraTools()Effect.runPromise(handler, { signal: context.abortSignal }) at the Mastra boundary
tool.execute / executeEffectToolInterrupt the running Effect fiber (or pass { signal } to Effect.runPromise at the boundary)
agent.stream → fromMastraOutputInterrupting 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.