examples/mastra-agent
in the repo: the unmodified create-mastra
weather-agent starter, fronted with Next.js per Mastra’s own guide
(@mastra/ai-sdk → useChat + AI SDK UI), plus four touches (~60 lines).
Every Vendo line in the example sits inside --- vendo: / --- /vendo
comment fences.
Read the overview first for the tool pack and envelope
contract. The frontend here is identical to the
AI SDK example: the embed contract is
framework-agnostic.
Touch 1: the Vendo composition
OnecreateVendo call with the cautious policy and two host actions:
get_weather (risk read, so generated apps can query live data) and the
deliberately risky send_trip_report (risk write, which exercises
approvals).
src/lib/vendo-actions.ts (the same
open-meteo lookup as the starter’s weatherTool, plus the send action), and
their descriptors (name, schema, risk) in .vendo/tools.json, exactly where
vendo init extracts them in a real app. The shipped example wraps this
call in a small composeVendo(overrides) helper so its fixture e2e can
inject a scripted model; the config is what you see above.
Touch 2: the wire route
The stock catch-all route, whatvendo init scaffolds. It serves apps and
approvals to the embeds.
Touch 3: the tools spread
The starter’s agent definition gains the pack next to its ownweatherTool.
vendoMastraTools returns a Promise (the pack enumerates the live
registry), which is why the agent uses Mastra’s tools-as-function form:
vendo_* call resolves the caller’s
principal lazily from Mastra’s request context under the key
vendo-principal (exported as VENDO_PRINCIPAL_KEY). A call without one
fails closed. The optional vendo-session-id key (VENDO_SESSION_KEY)
carries your host session id into Vendo’s audit trail.
The starter scaffolds
openai/gpt-5-mini, but multi-turn tool use with
GPT-5 reasoning models currently fails on history replay
(mastra-ai/mastra#9005).
The example pins openai/gpt-4.1-mini, the workaround those issues
document.Touch 4: the principal and the embeds
The chat route is Mastra’s own Next.js guide route with two additions: the caller’s principal set server-side on theRequestContext (never trust the
client for identity), and a store warm-up so guarded tools can execute
before any wire request has run.
useChat chat wrapped once in VendoProvider, handing
each finished tool part to <VendoToolResult>. Mastra streams tool calls as
dynamic-tool or tool-* parts, so the example matches both:
<VendoToolResult> renders the app embed for vendo/app-ref@1, the
approval card for vendo/approval-ref@1, and nothing for plain data, like
the starter’s own weatherTool output.
Run it
createVendo({ model }) wins, otherwise Vendo walks the
dev-mode credential ladder. Two models, deliberately:
your agent keeps its model; Vendo’s model seam powers generation and the
delegate.
The demo script
- “What’s the weather in Paris?” The starter’s own
weatherTool, untouched. - “Make me a dashboard comparing weather in Paris, Tokyo and NYC.” The
agent calls
vendo_create_app; the app builds live inline in the chat. - “Email the report to ops@example.com.”
vendo_send_trip_reportparks on the cautious policy; an approval card renders inline; approving executes the parked call in place.

Step 2 in the Mastra chat: the generated weather dashboard, live inside VendoAppEmbed, querying the registered host action for real data.
Tests
The example ships a hermetic fixture e2e (examples/mastra-agent/e2e/vendo-seam.e2e.test.ts): a real Mastra agent
turn through the pack, the approval park → wire approve → execute round
trip, the app-ref envelope from a real generation, delegation, and the
fail-closed principal check. Scripted models and a temp store, so no keys
are needed. Run it with pnpm test in the example.