Agents & stream requirements
Context.Service + Agent.of
Wire toolkit and handlers in a service make effect. Handler R is inferred on stream / generate:
class DocsAgent extends Context.Service<DocsAgent>()("app/DocsAgent", {
make: Effect.gen(function* () {
return yield* Agent.of("docs-agent", {
name: "Docs Agent",
instructions: "Use search_docs when needed.",
model: "openai/gpt-4o",
toolkit,
handlers: {
search_docs: Effect.fn(function* ({ query }) {
const api = yield* ApiKey
return `(${api.token}) results for ${query}`
}),
},
})
}),
}) {
static readonly layer = Layer.effect(DocsAgent, DocsAgent.make)
}
Effect.gen(function* () {
const agent = yield* DocsAgent
return yield* agent.stream("Find Effect Layer docs").pipe(
Effect.provide(Layer.succeed(ApiKey, { token: process.env.API_KEY! })),
)
}).pipe(Effect.provide(DocsAgent.layer), Effect.runPromise)DocsAgent.layer— agent service only. Does not include handler deps likeApiKey.agent.stream/agent.generate— require handlerRfromyield*in server tool handlers.
Annotated (client) tools omit server handlers — handlers is HandlersFrom<Tools> (server tools only).
Advanced: Service.use
DocsAgent.use((agent) => agent.stream("…")) is equivalent to Effect.gen + yield* DocsAgent when you prefer the callback form.
Processors
Mastra's inputProcessors, outputProcessors, and errorProcessors pass through on Agent.of config and per-call options. No parallel Effect processor API — use Mastra's native processor types.
Agent-level (every stream / generate call):
import type { InputProcessor } from "@mastra/core/processors"
const trimInput: InputProcessor = {
id: "trim-input",
processInput: async ({ messages }) => ({
messages: messages.map((m) => ({ ...m, content: m.content.trim() })),
}),
}
class DocsAgent extends Context.Service<DocsAgent>()("app/DocsAgent", {
make: Effect.gen(function* () {
return yield* Agent.of("docs-agent", {
name: "Docs Agent",
instructions: "Be helpful.",
model: "openai/gpt-4o",
toolkit,
handlers,
inputProcessors: [trimInput],
outputProcessors: [],
errorProcessors: [],
})
}),
}) {}Per-call override (merged with agent defaults by Mastra):
Effect.gen(function* () {
const agent = yield* DocsAgent
return yield* agent.stream("hello", {
inputProcessors: [trimInput],
})
})Client tools
Client tools do not add a server handler layer. They register Mastra stubs and throw ClientToolRequiredError if the server execute path is hit. Fulfillment happens in the browser via Assistant UI.
Agent exports
| Export | Description |
|---|---|
Agent.of(id, { …, toolkit, handlers }) | Typed ops — handler R on stream / generate (preferred) |
Context.Service + Layer.effect | Service class + explicit Live layer |
yield* Service | Call agent methods; method Effects carry handler R |
Service.use | Callback form of yield* Service (advanced) |
Memory & stream utilities
See API overview for MastraMemory.* and stream helpers (fromMastraOutput, etc.).