> ## Documentation Index
> Fetch the complete documentation index at: https://docs.uselemma.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Mastra

> Instrument a Mastra agent so each run is one nested trace in Lemma

[Mastra](https://mastra.ai) is a TypeScript agent framework built on the Vercel AI SDK. It has native OpenTelemetry telemetry, so once you enable it, point Langfuse at Lemma, and wrap each run in one root span, you get a single nested trace per agent execution. Use the [Langfuse Mastra integration](https://langfuse.com/integrations/frameworks/mastra) as the reference for enabling telemetry.

<Note>
  **One agent execution = one trace.** Wrap the run in a single root span so every model and tool call nests under it. See the [trace contract](/reference/trace-contract).
</Note>

<Note>
  Mastra traces **render fully** in Lemma today. Automated [issue detection](/reference/good-vs-bad-traces) is being expanded to this shape — see [Good trace vs bad trace](/reference/good-vs-bad-traces) for current status.
</Note>

## Recipe

<Steps>
  <Step title="Install">
    ```bash theme={null}
    npm install @mastra/core @langfuse/tracing @langfuse/otel @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-proto
    ```
  </Step>

  <Step title="Register the Langfuse → Lemma exporter">
    Register the exporter once, before any agent runs. This matches [Setup](/tracing/instrumentation/setup).

    ```typescript theme={null}
    // instrumentation.ts — imported first, before your app code
    import { LangfuseSpanProcessor } from "@langfuse/otel";
    import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
    import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";

    export const lemmaProcessor = new LangfuseSpanProcessor({
      exporter: new OTLPTraceExporter({
        url: process.env.LEMMA_BASE_URL,
        headers: {
          Authorization: `Bearer ${process.env.LEMMA_API_KEY}`,
          "X-Lemma-Project-ID": process.env.LEMMA_PROJECT_ID,
        },
      }),
    });

    new NodeTracerProvider({ spanProcessors: [lemmaProcessor] }).register();
    ```

    Set the environment variables. Lemma-only export needs no `LANGFUSE_*` credentials.

    ```bash theme={null}
    export LEMMA_BASE_URL="https://api.uselemma.ai/otel/v1/traces"
    export LEMMA_API_KEY="lma_..."
    export LEMMA_PROJECT_ID="proj_..."
    ```
  </Step>

  <Step title="Enable Mastra telemetry">
    Turn on Mastra's OpenTelemetry telemetry so model and tool activity is emitted as spans into the provider you registered. See the [Langfuse Mastra guide](https://langfuse.com/integrations/frameworks/mastra) for configuration details.

    ```typescript theme={null}
    import { Mastra } from "@mastra/core";

    export const mastra = new Mastra({
      agents: { supportAgent },
      telemetry: {
        serviceName: "support-agent",
        enabled: true,
      },
    });
    ```
  </Step>

  <Step title="Wrap the whole run in one root span">
    Wrap `agent.generate` in a single root span so every model and tool call nests under one trace. Record the input and final output on the root, and set a stable agent name.

    ```typescript theme={null}
    import { propagateAttributes, startActiveObservation } from "@langfuse/tracing";

    export async function runSupportAgent(userMessage: string, threadId: string) {
      const agent = mastra.getAgent("supportAgent");

      return await startActiveObservation("support-agent", async (root) => {
        root.update({ input: userMessage });

        return await propagateAttributes(
          {
            traceName: "support-agent",
            sessionId: threadId,
            metadata: { "gen_ai.agent.name": "support-agent" },
          },
          async () => {
            const result = await agent.generate(userMessage);
            root.update({ output: result.text });
            return result.text;
          },
        );
      });
    }
    ```

    The spans Mastra emits inside the callback become children of the root, producing one nested trace:

    ```text theme={null}
    support-agent              ← trace root (input, output)
    ├─ generateText            ← generation (model, tokens)
    ├─ search_docs             ← tool call (args, result)
    └─ generateText            ← generation (final answer)
    ```
  </Step>

  <Step title="Flush before the process exits">
    In serverless or other short-lived runtimes, flush so the whole trace ships in one batch.

    ```typescript theme={null}
    import { lemmaProcessor } from "./instrumentation";

    // at the end of a request / serverless handler
    await lemmaProcessor.forceFlush();
    ```
  </Step>
</Steps>

<Warning>
  If model or tool calls show up as their own separate traces, the work ran outside the root's active context. Keep `agent.generate` inside the `startActiveObservation` callback. See [Troubleshooting](/tracing/troubleshooting/common-problems#every-span-appears-as-a-separate-trace).
</Warning>

## Verify in Lemma

Open the [Lemma dashboard](https://platform.uselemma.ai) → **Traces** and confirm:

* **One trace per run** — a full agent run is one trace, not one per model call.
* **Root has input and output** — the root span shows the user message and the final response.
* **Generations are nested** — each model call appears as a child generation with model and token usage.
* **Tools are nested** — each tool invocation appears as a child tool span with arguments and result.

## Next steps

<CardGroup cols={2}>
  <Card title="Trace contract" icon="file-check" href="/reference/trace-contract">
    The exact shape Lemma reads.
  </Card>

  <Card title="Setup" icon="plug" href="/tracing/instrumentation/setup">
    Wire the Langfuse → Lemma exporter.
  </Card>

  <Card title="Threads and sessions" icon="users" href="/tracing/instrumentation/context">
    Group multi-turn conversations with a thread id.
  </Card>

  <Card title="Good vs bad traces" icon="circle-check" href="/reference/good-vs-bad-traces">
    What issue detection looks for, per shape.
  </Card>
</CardGroup>
