Skip to content

Agents & stream requirements ​

Context.Service + Agent.of ​

Wire toolkit and handlers in a service make effect. Handler R is inferred on stream / generate:

ts
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 like ApiKey.
  • agent.stream / agent.generate — require handler R from yield* 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):

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

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

ExportDescription
Agent.of(id, { …, toolkit, handlers })Typed ops — handler R on stream / generate (preferred)
Context.Service + Layer.effectService class + explicit Live layer
yield* ServiceCall agent methods; method Effects carry handler R
Service.useCallback form of yield* Service (advanced)

Memory & stream utilities ​

See API overview for MastraMemory.* and stream helpers (fromMastraOutput, etc.).