Page actions

How Mastra Connects

This page records the AG-UI wire behavior measured for Mastra on 2026-08-31. It describes what the runtime emitted, not what the protocol permits in general. The captured Server-Sent Events are committed at libs/ag-ui/fixtures/runtime-transcripts/ and replayed by the adapter test suite on every run.

Transport is Threadplane's, not upstream's

@ag-ui/mastra ships an in-process MastraAgent bridge and a mount for its own chat frontend runtime, and no plain AG-UI HTTP endpoint. The service in deployments/ag-ui-mastra supplies the missing endpoint.

RouteMethodAuthNotes
/okGETNoneHealth check.
/agent/mastraPOSTX-Internal-TokenRunAgentInput JSON in, Server-Sent Events out.

The service refuses to boot without AG_UI_INTERNAL_TOKEN, rejects any non-health route without a matching header, and maps an error from the underlying observable to a RUN_ERROR frame rather than dropping the socket. Any route other than /ok that arrives without a matching header returns 401 {"detail":"unauthorized"}; with a valid token, an unknown path or method returns 404 {"detail":"not found"} and an unregistered topic returns 404 {"detail":"no such topic: <topic>"}.

In your own application this is still an ordinary provideAgent({ url: '/agent', interruptTransport: 'mastra-command' }); the example in this repository passes a factory only because it resolves its endpoint at runtime. The token is injected by the dev proxy in front of the app, never by the browser.

Interrupts arrive by both conventions

Mastra is the only measured runtime that signals an interrupt twice: it emits a CUSTOM event named on_interrupt and finishes the run with the protocol-standard interrupt outcome.

AWS Strands and Microsoft Agent Framework emit only the outcome. The LangGraph bridge emits only on_interrupt. The adapter retains both forms in one session. The native batch drives the display projection, while interruptSession().legacy?.value preserves the Mastra suspend details used by the approval card.

Resume uses interruptEvent

Mastra reads resume data from forwardedProps.command.interruptEvent, carrying a tool-call id and a run id:

{
  "forwardedProps": {
    "command": {
      "interruptEvent": { "toolCallId": "...", "runId": "..." }
    }
  }
}

That is a third distinct shape. Strands and Microsoft Agent Framework read a top-level resume array; the LangGraph bridge reads forwardedProps.command.resume. Application code passes one neutral submit({ resume }). The Mastra provider selects interruptTransport: 'mastra-command' explicitly because this backend emits both compatibility and native interrupts, and auto otherwise selects the native wire shape.

Suspend and resume require persistent storage

reserve_campsite calls Mastra's suspend() on its first invocation and reads resumeData on the second. Mastra writes the suspended-run snapshot to LibSQL file storage, and resume loads it back.

Because those two invocations are separate HTTP requests, an in-memory store cannot round-trip them. On a deployment with an ephemeral filesystem, every redeploy orphans pending interrupts. This is a property of the runtime, not of the adapter, and it is the single most consequential operational difference between Mastra and the two Python runtimes.

State is working memory, with real deltas

Shared state is a Mastra working-memory object under a Zod schema. The bridge emits STATE_SNAPSHOT followed by real JSON-Patch STATE_DELTA events as the model revises it, so the adapter applies ordinary deltas with no reassembly.

That places Mastra alongside Microsoft Agent Framework and apart from AWS Strands, whose bridge emits snapshots only.

Subagents stream through a tee ahead of the bridge

Mastra forwards every chunk of a delegated child on the parent stream as a public tool-output chunk, but @ag-ui/mastra drops those chunks and withholds the delegation tool call — Mastra names it agent-<childKey> — until the child resolves. The hosting service wraps the agent so that each chunk is observed before the bridge processes it, and a per-run injector emits the delegation tool call eagerly, SUBAGENT_STARTED, one TEXT_MESSAGE_CONTENT per child delta attributed to the subagent, and SUBAGENT_FINISHED on the result, while dropping the bridge's later copy of the same tool call. The card therefore mounts as soon as the delegation begins and its text grows while the child runs, which is what makes this cell Supported.

Next steps

Looking for something specific?