Observability
mastra-effect can stitch Mastra's active tool span into Effect's Tracer.ParentSpan at the Promise FFI boundary (Toolkit.toMastraTools / Agent.of). Effect child spans created with Effect.withSpan nest under Mastra's execute_tool span — one trace tree, no second exporter.
Opt in with MastraObservability.layer()
Span stitching is opt in. Provide MastraObservability.layer() in your Effect context to enable it. Without the layer, tool FFI is a plain no-op.
import { Agent, MastraTool, Toolkit } from "mastra-effect"
import { MastraObservability } from "mastra-effect/Observability"
import { Context, Effect, Layer } from "effect"
const toolkit = Toolkit.make(MyTool)
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: {
my_tool: Effect.fn(function* (params) {
yield* Effect.withSpan("handler-work")(Effect.succeed(params))
return "done"
}),
},
})
}),
}) {
static readonly layer = Layer.effect(DocsAgent, DocsAgent.make)
}
const appLayer = DocsAgent.layer.pipe(Layer.provide(MastraObservability.layer()))When Mastra calls a tool's execute Promise and MastraObservability is in context:
currentSpanFromToolContextreadstracingContext.currentSpan(or legacytracing.currentSpan).- The bridge enters Mastra async-local context and provides
Tracer.ParentSpanto the handler Effect. - Handler
Effect.withSpanchildren inherit the Mastra parent trace/span ids.
Omit the layer to skip stitching
If you do not provide MastraObservability.layer(), handlers run without Mastra ALS or Effect ParentSpan stitching at the Promise boundary. This is the default — no per-agent config needed.
Configure Mastra once
Set up Mastra observability in your app (exporters, Observability, optional OtelBridge). See Mastra tracing docs.
import { Mastra } from "@mastra/core"
import { Observability } from "@mastra/observability"
const mastra = new Mastra({
observability: new Observability({
// exporters, service name, etc.
}),
})Low-level FFI helpers
import {
currentSpanFromToolContext,
enterMastraSpanContext,
runEffectInMastraSpan,
} from "mastra-effect/Observability"Use these when you own the Promise boundary outside Agent.of.
What we do not do
- Stand up a second OpenTelemetry exporter or duplicate Mastra's agent-loop tracing.
- Fork or replace Mastra's agent loop — we only bridge at the tool FFI used by
Agent.of.
Optional peers
@mastra/observability and @opentelemetry/api are optional peer dependencies. The package imports span types from @mastra/core/observability; install the peers when you configure exporters in your app.