<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Mastra on RockB</title><link>https://baeseokjae.github.io/tags/mastra/</link><description>Recent content in Mastra on RockB</description><image><title>RockB</title><url>https://baeseokjae.github.io/images/og-default.png</url><link>https://baeseokjae.github.io/images/og-default.png</link></image><generator>Hugo</generator><language>en-us</language><lastBuildDate>Tue, 21 Apr 2026 23:21:28 +0000</lastBuildDate><atom:link href="https://baeseokjae.github.io/tags/mastra/index.xml" rel="self" type="application/rss+xml"/><item><title>Mastra AI Guide 2026: Build TypeScript AI Agents with the Framework That Hit 300K Weekly Downloads</title><link>https://baeseokjae.github.io/posts/mastra-ai-guide-2026/</link><pubDate>Tue, 21 Apr 2026 23:21:28 +0000</pubDate><guid>https://baeseokjae.github.io/posts/mastra-ai-guide-2026/</guid><description>Complete guide to Mastra AI TypeScript framework: install, build agents, add memory, create workflows, and deploy to production.</description><content:encoded><![CDATA[<p>Mastra is an open-source TypeScript framework for building production AI agents, giving you agents, tools, memory, workflows, RAG, evals, and observability in a single cohesive package. Install it with <code>npm create mastra@latest</code>, define an agent in under 20 lines of TypeScript, and have a working REST API in minutes — no Python environment, no multi-library stitching.</p>
<h2 id="why-mastra-is-the-typescript-ai-framework-to-watch-in-2026">Why Mastra Is the TypeScript AI Framework to Watch in 2026</h2>
<p>Mastra is the TypeScript-first AI agent framework built by the team behind Gatsby — the same engineers who made static-site generation mainstream for JavaScript developers. With 23.2k GitHub stars, $35M in total funding (including a $22M Series A led by Spark Capital announced in April 2026), and enterprise deployments at Brex, Docker, Elastic, MongoDB, Salesforce, Replit, and SoftBank, Mastra has moved from interesting experiment to production infrastructure. The Marsh McLennan enterprise search agent built on Mastra is used by 100,000+ employees every day. Brex&rsquo;s Mastra-powered agents contributed directly to their $5.1B Capital One acquisition. These aren&rsquo;t toy demos — they are mission-critical workloads. For JavaScript and TypeScript developers who&rsquo;ve been watching the Python AI ecosystem from the sidelines, Mastra is the on-ramp. The CEO Sam Bhagwat has cited data that 60–70% of YC X25 agent startups are building in TypeScript, signaling a clear ecosystem shift.</p>
<h3 id="the-shift-from-python-to-typescript-for-ai-agents">The Shift From Python to TypeScript for AI Agents</h3>
<p>For years, AI frameworks defaulted to Python: LangChain, LangGraph, CrewAI, and AutoGen all began Python-first. The assumption was that data scientists and ML researchers drove adoption. But in 2025–2026, the builders shipping production agents are full-stack web developers — and they live in TypeScript. Mastra is built for that reality. Its API design mirrors familiar Node.js patterns, its type safety catches agent configuration errors at compile time, and its integration with Next.js, Express, Hono, and SvelteKit means your AI layer fits naturally into existing web apps without a Python sidecar service.</p>
<h3 id="what-is-mastra-origin-story-built-by-the-gatsby-team">What Is Mastra? (Origin Story: Built by the Gatsby Team)</h3>
<p>Mastra was created by Sam Bhagwat and the former Gatsby core team, who applied their experience building developer-facing infrastructure to the AI agent problem. They spent time at Netlify scaling a platform that served millions of developers, then looked at the emerging AI agent landscape and saw the same fragmentation that existed in static site generation before Gatsby unified it. The result is a framework that prioritizes developer experience, strong TypeScript types, and a batteries-included philosophy — you get memory, workflow orchestration, RAG, observability, and a local dev UI (Mastra Studio) out of the box, not as separate packages you wire together yourself.</p>
<hr>
<h2 id="getting-started-setting-up-your-first-mastra-project">Getting Started: Setting Up Your First Mastra Project</h2>
<p>Setting up Mastra takes under five minutes on any machine with Node.js 18+ installed. The framework ships a scaffold CLI (<code>npm create mastra@latest</code>) that generates a complete project with sensible defaults, including TypeScript configuration, a starter agent, and Mastra Studio for local development. The project structure is opinionated but not restrictive — you can drop Mastra into an existing Next.js or Express app, or start fresh with the scaffold. The free tier of Mastra Platform gives you Studio and the server runtime at no cost, making it practical to experiment before committing to infrastructure spend. Apache 2.0 licensing means you can use it commercially without restriction, unlike some frameworks with commercial-use clauses. Mastra requires Node.js 18 or later and works with npm, pnpm, or yarn. The scaffold wizard takes roughly 60 seconds: it asks for your LLM provider, writes <code>.env</code> key stubs, and generates a working weather agent you can query immediately. By the time the install finishes, you have a typed, tested, locally runnable AI agent.</p>
<h3 id="prerequisites-and-installation">Prerequisites and Installation</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Requires Node.js 18+</span>
</span></span><span style="display:flex;"><span>npm create mastra@latest
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Or add to an existing project</span>
</span></span><span style="display:flex;"><span>npm install @mastra/core
</span></span></code></pre></div><p>The scaffold wizard asks for your preferred LLM provider (OpenAI, Anthropic, Google Gemini, Mistral, or Groq), sets up <code>.env</code> with the right API key variable names, and generates a working agent you can test immediately.</p>
<h3 id="project-structure-overview">Project Structure Overview</h3>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 408 169"
      >
      <g transform='translate(8,16)'>
<path d='M 136,64 L 144,48' fill='none' stroke='currentColor'></path>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='0' y='36' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='52' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='68' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='84' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='100' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='116' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='132' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='0' y='148' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='8' y='132' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='8' y='148' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='16' y='132' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='16' y='148' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='32' y='36' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='32' y='52' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='32' y='68' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='32' y='84' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='32' y='100' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='32' y='116' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='32' y='132' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='32' y='148' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='40' y='36' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='40' y='116' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='40' y='132' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='40' y='148' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='48' y='36' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='48' y='116' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='48' y='132' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='48' y='148' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='56' y='132' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='56' y='148' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='64' y='36' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='64' y='52' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='64' y='68' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='64' y='84' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='64' y='100' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='64' y='116' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='64' y='148' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='72' y='36' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='72' y='52' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='72' y='68' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='72' y='84' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='72' y='100' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='72' y='116' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='72' y='148' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='80' y='36' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='80' y='52' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='80' y='68' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='80' y='84' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='80' y='100' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='80' y='116' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='80' y='148' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='88' y='36' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='88' y='116' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='88' y='148' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='96' y='36' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='96' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='96' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='96' y='84' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='96' y='100' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='96' y='116' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='96' y='148' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='104' y='36' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='104' y='52' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='104' y='68' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='104' y='84' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='104' y='100' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='104' y='116' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='104' y='148' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='112' y='36' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='112' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='112' y='68' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='112' y='84' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='112' y='100' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='112' y='116' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='112' y='148' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='120' y='52' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='120' y='68' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='120' y='84' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='120' y='100' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='120' y='116' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='120' y='148' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='128' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='128' y='68' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='128' y='84' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='128' y='100' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='136' y='52' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='136' y='84' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='136' y='100' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='144' y='84' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='144' y='100' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='152' y='84' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='152' y='100' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='160' y='84' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='168' y='84' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='216' y='52' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='216' y='68' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='216' y='84' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='216' y='100' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='216' y='116' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='232' y='52' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='232' y='68' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='232' y='84' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='232' y='100' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='232' y='116' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='240' y='52' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='240' y='68' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='240' y='84' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='240' y='100' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='240' y='116' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='248' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='248' y='68' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='248' y='84' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='248' y='100' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='248' y='116' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='256' y='52' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='256' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='256' y='84' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='256' y='100' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='264' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='264' y='68' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='264' y='84' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='264' y='100' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='264' y='116' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='272' y='68' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='272' y='84' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='272' y='100' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='272' y='116' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='280' y='52' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='280' y='84' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='280' y='116' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='288' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='288' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='288' y='84' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='288' y='100' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='288' y='116' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='296' y='52' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='296' y='68' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='296' y='84' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='296' y='100' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='296' y='116' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='304' y='52' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='304' y='68' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='304' y='84' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='304' y='100' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='312' y='52' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='312' y='68' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='312' y='100' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='312' y='116' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='320' y='52' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='320' y='84' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='320' y='100' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='320' y='116' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='328' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='328' y='68' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='328' y='84' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='328' y='100' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='328' y='116' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='336' y='52' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='336' y='68' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='336' y='84' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='336' y='100' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='336' y='116' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='344' y='52' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='344' y='68' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='344' y='84' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='344' y='100' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='344' y='116' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='352' y='52' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='352' y='68' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='352' y='84' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='360' y='52' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='360' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='360' y='84' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='368' y='68' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='368' y='84' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='376' y='68' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='376' y='84' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='384' y='68' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='384' y='84' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='392' y='68' fill='currentColor' style='font-size:1em'>s</text>
</g>

    </svg>
  
</div>
<p>The <code>Mastra</code> instance in <code>index.ts</code> is your registry — you import agents, tools, and workflows and pass them to the constructor. This central registry pattern means Mastra knows about all your components for observability and Studio integration.</p>
<h3 id="mastra-studio-the-interactive-dev-ui">Mastra Studio: The Interactive Dev UI</h3>
<p>Run <code>npx mastra dev</code> and Mastra Studio opens at <code>http://localhost:4111</code>. Studio gives you a chat interface to test agents, a visual workflow runner, memory inspection, trace viewer, and eval results — all without writing test code. This dramatically shortens the feedback loop during development. You can send messages to agents, inspect the exact tool calls they made, see token counts per step, and replay failed runs from any point in the trace.</p>
<hr>
<h2 id="building-your-first-ai-agent-with-mastra">Building Your First AI Agent with Mastra</h2>
<p>An AI agent in Mastra is defined using the <code>Agent</code> class from <code>@mastra/core</code>. You provide a name, system instructions, an LLM model reference, and optionally a set of tools and a memory configuration. In fewer than 30 lines of TypeScript you have an agent that can call external APIs, remember conversation history, and respond with context awareness. Mastra&rsquo;s type system ensures your tool input/output schemas are validated at compile time, catching the class of bugs that show up as cryptic runtime errors in weakly-typed frameworks. The agent automatically handles prompt construction, tool call parsing, and multi-turn conversation state — you focus on the business logic. Mastra agents are model-agnostic by design: swap from GPT-4o to Claude Sonnet to Gemini Flash by changing one import, with no changes to tool definitions, memory setup, or downstream code. This is especially valuable for production systems where you may want to switch providers for cost, latency, or capability reasons without a costly refactor. Each agent instance is also independently observable — all calls are traced through OpenTelemetry automatically.</p>
<h3 id="defining-an-agent-with-system-prompts-and-tools">Defining an Agent with System Prompts and Tools</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Agent</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/core/agent&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">openai</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@ai-sdk/openai&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">weatherTool</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;../tools/weather&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">weatherAgent</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Agent</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Weather Agent&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">instructions</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`You are a helpful weather assistant. When asked about weather,
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    always use the weather tool to get current conditions. Be concise and specific.`</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">openai</span>(<span style="color:#e6db74">&#34;gpt-4o&#34;</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">tools</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">weatherTool</span> },
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><h3 id="adding-memory-working-memory-and-semantic-recall">Adding Memory: Working Memory and Semantic Recall</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Memory</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/memory&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">LibSQLStore</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/memory/storage/libsql&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">memory</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Memory</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">storage</span>: <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">LibSQLStore</span>({ <span style="color:#a6e22e">url</span>: <span style="color:#66d9ef">process.env.DATABASE_URL</span><span style="color:#f92672">!</span> }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">options</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">workingMemory</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">enabled</span>: <span style="color:#66d9ef">true</span> },
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">semanticRecall</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">topK</span>: <span style="color:#66d9ef">5</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">messageRange</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">before</span>: <span style="color:#66d9ef">2</span>, <span style="color:#a6e22e">after</span>: <span style="color:#66d9ef">2</span> },
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">weatherAgent</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Agent</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Weather Agent&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">instructions</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;...&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">openai</span>(<span style="color:#e6db74">&#34;gpt-4o&#34;</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">tools</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">weatherTool</span> },
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">memory</span>,
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>Mastra supports two memory modes. <strong>Working memory</strong> stores structured facts about the user or session that persist across conversations — think name, preferences, previous requests. <strong>Semantic recall</strong> performs vector similarity search over past messages so the agent can surface relevant prior context without loading the entire history into the context window.</p>
<h3 id="connecting-llm-providers">Connecting LLM Providers</h3>
<p>Mastra uses the Vercel AI SDK under the hood for model calls, which means you can swap providers by changing one import:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">anthropic</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@ai-sdk/anthropic&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">google</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@ai-sdk/google&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">groq</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@ai-sdk/groq&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Drop-in swap — same Agent API regardless of provider
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">anthropic</span>(<span style="color:#e6db74">&#34;claude-sonnet-4-5&#34;</span>),
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">google</span>(<span style="color:#e6db74">&#34;gemini-2.0-flash&#34;</span>),
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">groq</span>(<span style="color:#e6db74">&#34;llama-3.3-70b-versatile&#34;</span>),
</span></span></code></pre></div><hr>
<h2 id="tools-and-mcp-connecting-your-agent-to-the-real-world">Tools and MCP: Connecting Your Agent to the Real World</h2>
<p>Tools are the mechanism by which Mastra agents take action — calling APIs, reading databases, sending messages, or triggering webhooks. A Mastra tool is a TypeScript function with a Zod schema for inputs and outputs, a description the LLM uses to decide when to call it, and an async execute function containing the actual logic. The type safety guarantees that the LLM&rsquo;s JSON tool call is validated before it reaches your code, eliminating an entire class of injection and malformed-input bugs. Beyond custom tools, Mastra has first-class support for MCP (Model Context Protocol), letting your agents consume any of the thousands of existing MCP servers — GitHub, Slack, Postgres, Notion, and more — without writing glue code. Docker&rsquo;s engineering team used exactly this pattern to build a PR automation agent that listens to GitHub webhooks and takes action using the GitHub Official MCP server via Mastra&rsquo;s MCP Gateway integration.</p>
<h3 id="defining-a-custom-tool">Defining a Custom Tool</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">createTool</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/core/tools&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">z</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;zod&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">weatherTool</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">createTool</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">id</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;get_weather&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">description</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Get current weather conditions for a city&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">inputSchema</span>: <span style="color:#66d9ef">z.object</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">city</span>: <span style="color:#66d9ef">z.string</span>().<span style="color:#a6e22e">describe</span>(<span style="color:#e6db74">&#34;City name&#34;</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">units</span>: <span style="color:#66d9ef">z.enum</span>([<span style="color:#e6db74">&#34;celsius&#34;</span>, <span style="color:#e6db74">&#34;fahrenheit&#34;</span>]).<span style="color:#66d9ef">default</span>(<span style="color:#e6db74">&#34;celsius&#34;</span>),
</span></span><span style="display:flex;"><span>  }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">outputSchema</span>: <span style="color:#66d9ef">z.object</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">temperature</span>: <span style="color:#66d9ef">z.number</span>(),
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">conditions</span>: <span style="color:#66d9ef">z.string</span>(),
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">humidity</span>: <span style="color:#66d9ef">z.number</span>(),
</span></span><span style="display:flex;"><span>  }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">execute</span>: <span style="color:#66d9ef">async</span> ({ <span style="color:#a6e22e">context</span> }) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">city</span>, <span style="color:#a6e22e">units</span> } <span style="color:#f92672">=</span> <span style="color:#a6e22e">context</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Call your weather API here
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">data</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">fetchWeatherAPI</span>(<span style="color:#a6e22e">city</span>, <span style="color:#a6e22e">units</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> { <span style="color:#a6e22e">temperature</span>: <span style="color:#66d9ef">data.temp</span>, <span style="color:#a6e22e">conditions</span>: <span style="color:#66d9ef">data.desc</span>, <span style="color:#a6e22e">humidity</span>: <span style="color:#66d9ef">data.humidity</span> };
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><h3 id="mcp-integration">MCP Integration</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">MCPClient</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/mcp&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">mcpClient</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MCPClient</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">servers</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">github</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">command</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;npx&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">args</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#34;-y&#34;</span>, <span style="color:#e6db74">&#34;@modelcontextprotocol/server-github&#34;</span>],
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">env</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">GITHUB_TOKEN</span>: <span style="color:#66d9ef">process.env.GITHUB_TOKEN</span><span style="color:#f92672">!</span> },
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Get MCP tools and pass to agent
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">tools</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">mcpClient</span>.<span style="color:#a6e22e">getTools</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">githubAgent</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Agent</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;GitHub Agent&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">instructions</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;You manage GitHub repositories and PRs.&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">anthropic</span>(<span style="color:#e6db74">&#34;claude-sonnet-4-5&#34;</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">tools</span>,
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><h3 id="real-example-dockers-pr-automation-agent">Real Example: Docker&rsquo;s PR Automation Agent</h3>
<p>Docker built a three-agent architecture using Mastra and the GitHub MCP server:</p>
<ol>
<li><strong>Analyze PR agent</strong> — reads the diff and generates a structured review</li>
<li><strong>Generate comment agent</strong> — formats the review as a GitHub comment</li>
<li><strong>Post and close PR agent</strong> — submits the comment and updates PR status</li>
</ol>
<p>Each agent runs as a Mastra workflow step. The orchestration triggers on a GitHub webhook, runs all three agents in sequence, and posts the result back to GitHub — no human in the loop. This is the pattern Mastra was designed for: event-driven, not chat-driven.</p>
<hr>
<h2 id="workflows-orchestrating-complex-agent-tasks">Workflows: Orchestrating Complex Agent Tasks</h2>
<p>Mastra workflows let you define deterministic, multi-step processes where each step can call an agent, run a function, or trigger another workflow. Unlike a single agent trying to plan and execute everything in one context window, workflows give you explicit control over the execution graph — with support for sequential steps, parallel branches, conditional routing, and suspension points for human-in-the-loop approval. This separation of concerns is critical for production systems: agents handle the fuzzy reasoning, workflows handle the reliable orchestration. The Mastra workflow engine is built on a durable execution model, meaning in-progress workflows can be paused, inspected in Studio, and resumed — even after a server restart. Docker&rsquo;s PR automation pipeline (three sequential agents triggered by a GitHub webhook) is a textbook Mastra workflow: deterministic order, explicit data passing between steps, and observable state at every stage. Compared to LangGraph&rsquo;s graph-based model, Mastra workflows use a linear <code>.then()</code> builder API that TypeScript developers find immediately readable. For branching, a <code>.branch()</code> method accepts predicate functions rather than requiring explicit graph node connections.</p>
<h3 id="when-to-use-workflows-vs-agents">When to Use Workflows vs Agents</h3>
<table>
  <thead>
      <tr>
          <th>Scenario</th>
          <th>Use Agent</th>
          <th>Use Workflow</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Open-ended Q&amp;A</td>
          <td>✓</td>
          <td></td>
      </tr>
      <tr>
          <td>Multi-step document processing</td>
          <td></td>
          <td>✓</td>
      </tr>
      <tr>
          <td>Sequential API calls with error recovery</td>
          <td></td>
          <td>✓</td>
      </tr>
      <tr>
          <td>Research and synthesis</td>
          <td>✓</td>
          <td></td>
      </tr>
      <tr>
          <td>Approval-gated actions</td>
          <td></td>
          <td>✓</td>
      </tr>
      <tr>
          <td>Real-time chat</td>
          <td>✓</td>
          <td></td>
      </tr>
      <tr>
          <td>ETL-style data pipelines</td>
          <td></td>
          <td>✓</td>
      </tr>
  </tbody>
</table>
<h3 id="building-a-workflow">Building a Workflow</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">createWorkflow</span>, <span style="color:#a6e22e">createStep</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/core/workflows&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">z</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;zod&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">fetchDataStep</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">createStep</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">id</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;fetch-data&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">inputSchema</span>: <span style="color:#66d9ef">z.object</span>({ <span style="color:#a6e22e">url</span>: <span style="color:#66d9ef">z.string</span>() }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">outputSchema</span>: <span style="color:#66d9ef">z.object</span>({ <span style="color:#a6e22e">content</span>: <span style="color:#66d9ef">z.string</span>() }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">execute</span>: <span style="color:#66d9ef">async</span> ({ <span style="color:#a6e22e">inputData</span> }) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">response</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">fetch</span>(<span style="color:#a6e22e">inputData</span>.<span style="color:#a6e22e">url</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">content</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">response</span>.<span style="color:#a6e22e">text</span>();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> { <span style="color:#a6e22e">content</span> };
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">analyzeStep</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">createStep</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">id</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;analyze&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">inputSchema</span>: <span style="color:#66d9ef">z.object</span>({ <span style="color:#a6e22e">content</span>: <span style="color:#66d9ef">z.string</span>() }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">outputSchema</span>: <span style="color:#66d9ef">z.object</span>({ <span style="color:#a6e22e">summary</span>: <span style="color:#66d9ef">z.string</span>(), <span style="color:#a6e22e">sentiment</span>: <span style="color:#66d9ef">z.string</span>() }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">execute</span>: <span style="color:#66d9ef">async</span> ({ <span style="color:#a6e22e">inputData</span>, <span style="color:#a6e22e">mastra</span> }) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">agent</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">mastra</span><span style="color:#f92672">?</span>.<span style="color:#a6e22e">getAgent</span>(<span style="color:#e6db74">&#34;analyzerAgent&#34;</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">agent</span><span style="color:#f92672">?</span>.<span style="color:#a6e22e">generate</span>(
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">`Analyze this content: </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">inputData</span>.<span style="color:#a6e22e">content</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> { <span style="color:#a6e22e">summary</span>: <span style="color:#66d9ef">result?.text</span> <span style="color:#f92672">??</span> <span style="color:#e6db74">&#34;&#34;</span>, <span style="color:#a6e22e">sentiment</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;positive&#34;</span> };
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">contentWorkflow</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">createWorkflow</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">id</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;content-analysis&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">inputSchema</span>: <span style="color:#66d9ef">z.object</span>({ <span style="color:#a6e22e">url</span>: <span style="color:#66d9ef">z.string</span>() }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">outputSchema</span>: <span style="color:#66d9ef">z.object</span>({ <span style="color:#a6e22e">summary</span>: <span style="color:#66d9ef">z.string</span>(), <span style="color:#a6e22e">sentiment</span>: <span style="color:#66d9ef">z.string</span>() }),
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">then</span>(<span style="color:#a6e22e">fetchDataStep</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">then</span>(<span style="color:#a6e22e">analyzeStep</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">commit</span>();
</span></span></code></pre></div><h3 id="parallel-execution-and-conditional-branching">Parallel Execution and Conditional Branching</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// Run steps in parallel
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">workflow</span>.<span style="color:#a6e22e">parallel</span>([<span style="color:#a6e22e">step1</span>, <span style="color:#a6e22e">step2</span>, <span style="color:#a6e22e">step3</span>]).<span style="color:#a6e22e">then</span>(<span style="color:#a6e22e">mergeStep</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Conditional routing
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">workflow</span>.<span style="color:#a6e22e">branch</span>([
</span></span><span style="display:flex;"><span>  [<span style="color:#66d9ef">async</span> ({ <span style="color:#a6e22e">inputData</span> }) <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">inputData</span>.<span style="color:#66d9ef">type</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">&#34;urgent&#34;</span>, <span style="color:#a6e22e">urgentStep</span>],
</span></span><span style="display:flex;"><span>  [<span style="color:#66d9ef">async</span> ({ <span style="color:#a6e22e">inputData</span> }) <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">inputData</span>.<span style="color:#66d9ef">type</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">&#34;normal&#34;</span>, <span style="color:#a6e22e">normalStep</span>],
</span></span><span style="display:flex;"><span>]);
</span></span></code></pre></div><hr>
<h2 id="rag-with-mastra-giving-your-agent-knowledge">RAG with Mastra: Giving Your Agent Knowledge</h2>
<p>RAG (Retrieval-Augmented Generation) in Mastra is handled through the <code>@mastra/rag</code> package, which provides document chunking, embedding, vector storage, and retrieval as first-class primitives. Rather than building a separate vector pipeline with LangChain, Pinecone, and custom glue code, Mastra gives you a unified API that works with PostgreSQL (pgvector), Pinecone, Qdrant, Weaviate, Chroma, or any vector store implementing the Mastra <code>VectorStore</code> interface. Elastic&rsquo;s engineering blog documented building a full agentic RAG assistant using Mastra and Elasticsearch, noting that Mastra&rsquo;s model-agnostic design and clean TypeScript API were key advantages over Python-based alternatives. The result was a production chatbot with semantic document retrieval, citation tracking, and conversational memory — built in a few hundred lines of TypeScript. The <code>@mastra/rag</code> package ships chunking strategies (fixed-size, recursive, sentence, and markdown-aware), multiple embedding providers (OpenAI <code>text-embedding-3-small</code>, Cohere, and others), and query-time reranking. You can implement the complete ingest-and-retrieve loop in under 50 lines, with the vector store swap requiring only a single configuration change rather than a full rewrite.</p>
<h3 id="setting-up-rag">Setting Up RAG</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">MastraVector</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/vector-pg&#34;</span>; <span style="color:#75715e">// PostgreSQL + pgvector
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">embed</span>, <span style="color:#a6e22e">chunk</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/rag&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">openai</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@ai-sdk/openai&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Chunk and embed documents
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">documents</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">chunk</span>(<span style="color:#a6e22e">rawText</span>, {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">strategy</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;recursive&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">size</span>: <span style="color:#66d9ef">512</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">overlap</span>: <span style="color:#66d9ef">50</span>,
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">embeddings</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">embed</span>(<span style="color:#a6e22e">documents</span>, {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">provider</span>: <span style="color:#66d9ef">openai.embedding</span>(<span style="color:#e6db74">&#34;text-embedding-3-small&#34;</span>),
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Store in vector DB
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">vectorStore</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MastraVector</span>({ <span style="color:#a6e22e">connectionString</span>: <span style="color:#66d9ef">process.env.DATABASE_URL</span><span style="color:#f92672">!</span> });
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">await</span> <span style="color:#a6e22e">vectorStore</span>.<span style="color:#a6e22e">upsert</span>({ <span style="color:#a6e22e">indexName</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;docs&#34;</span>, <span style="color:#a6e22e">vectors</span>: <span style="color:#66d9ef">embeddings</span> });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Query at retrieval time
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">results</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">vectorStore</span>.<span style="color:#a6e22e">query</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">indexName</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;docs&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">queryVector</span>: <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">embed</span>(<span style="color:#a6e22e">userQuery</span>, { <span style="color:#a6e22e">provider</span>: <span style="color:#66d9ef">openai.embedding</span>(<span style="color:#e6db74">&#34;text-embedding-3-small&#34;</span>) }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">topK</span>: <span style="color:#66d9ef">5</span>,
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><h3 id="adding-rag-to-an-agent">Adding RAG to an Agent</h3>
<p>The retrieved chunks are injected into the agent&rsquo;s context via a tool, giving the LLM access to relevant documents without blowing out the context window with entire corpora:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ragTool</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">createTool</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">id</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;search_docs&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">description</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Search internal documentation for relevant information&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">inputSchema</span>: <span style="color:#66d9ef">z.object</span>({ <span style="color:#a6e22e">query</span>: <span style="color:#66d9ef">z.string</span>() }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">execute</span>: <span style="color:#66d9ef">async</span> ({ <span style="color:#a6e22e">context</span> }) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">results</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">vectorStore</span>.<span style="color:#a6e22e">query</span>({ ... });
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> { <span style="color:#a6e22e">documents</span>: <span style="color:#66d9ef">results.map</span>(<span style="color:#a6e22e">r</span> <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">metadata</span>.<span style="color:#a6e22e">text</span>) };
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><hr>
<h2 id="productionizing-evals-observability-and-guardrails">Productionizing: Evals, Observability, and Guardrails</h2>
<p>Production AI agents fail in ways that unit tests cannot catch — hallucinations, prompt injection, unexpected tool chaining, model degradation across versions, and context window violations. Mastra addresses this with a built-in evals system, distributed tracing via OpenTelemetry, and configurable guardrails. The evals system supports both rule-based checks (response contains X, JSON schema validation, keyword presence) and model-graded evaluations using a judge LLM to score qualities like helpfulness, factuality, and tone. You define evals as code, version them with your agent definitions, and see results in Mastra Studio alongside the traces that explain why each response was generated. This closes the loop between development and production: the same evals you run locally can be scheduled against production traffic samples to catch regressions before users report them.</p>
<h3 id="running-evals">Running Evals</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">evaluate</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/evals&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">ToneConsistencyMetric</span>, <span style="color:#a6e22e">AnswerRelevancyMetric</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/evals/metrics&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">evaluate</span>(<span style="color:#a6e22e">weatherAgent</span>, <span style="color:#e6db74">&#34;What&#39;s the weather in Tokyo?&#34;</span>, {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">metrics</span><span style="color:#f92672">:</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">ToneConsistencyMetric</span>({ <span style="color:#a6e22e">tone</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;professional&#34;</span> }),
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">AnswerRelevancyMetric</span>({ <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">openai</span>(<span style="color:#e6db74">&#34;gpt-4o-mini&#34;</span>) }),
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#a6e22e">result</span>.<span style="color:#a6e22e">scores</span>); <span style="color:#75715e">// { toneConsistency: 0.92, answerRelevancy: 0.87 }
</span></span></span></code></pre></div><h3 id="observability-with-opentelemetry">Observability with OpenTelemetry</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Mastra</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/core&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">OpenTelemetry</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/core/telemetry/otel&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">mastra</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Mastra</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">agents</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">weatherAgent</span> },
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">telemetry</span>: <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">OpenTelemetry</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">serviceName</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;my-mastra-app&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">export</span><span style="color:#f92672">:</span> { <span style="color:#66d9ef">type</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;otlp&#34;</span>, <span style="color:#a6e22e">endpoint</span>: <span style="color:#66d9ef">process.env.OTEL_EXPORTER_ENDPOINT</span><span style="color:#f92672">!</span> },
</span></span><span style="display:flex;"><span>  }),
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>Every agent call, tool invocation, and LLM request is automatically traced. You see the full span tree in Studio or any OTLP-compatible backend (Jaeger, Honeycomb, Grafana Tempo).</p>
<h3 id="guardrails">Guardrails</h3>
<p>Mastra supports input and output guardrails as middleware functions on the agent:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">safeAgent</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Agent</span>({
</span></span><span style="display:flex;"><span>  ...<span style="color:#a6e22e">baseConfig</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">beforeGenerate</span>: <span style="color:#66d9ef">async</span> (<span style="color:#a6e22e">messages</span>) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Reject messages containing PII patterns
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">hasPII</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">/\b\d{3}-\d{2}-\d{4}\b/</span>.<span style="color:#a6e22e">test</span>(<span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">at</span>(<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)<span style="color:#f92672">?</span>.<span style="color:#a6e22e">content</span> <span style="color:#f92672">??</span> <span style="color:#e6db74">&#34;&#34;</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">hasPII</span>) <span style="color:#66d9ef">throw</span> <span style="color:#66d9ef">new</span> Error(<span style="color:#e6db74">&#34;PII detected in input&#34;</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">messages</span>;
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><hr>
<h2 id="deployment-from-dev-to-production">Deployment: From Dev to Production</h2>
<p>Mastra agents deploy as standard HTTP servers — no special runtime required. The <code>mastra build</code> command compiles your TypeScript agent definitions into a Node.js server exposing REST endpoints for each agent and workflow. You can deploy this anywhere: a VPS, Kubernetes pod, AWS Lambda, Vercel Edge Function, or Cloudflare Worker. The Mastra Platform (their managed offering) adds Studio, a Memory Gateway for cross-deployment memory persistence, and a server runtime with automatic scaling — useful if you don&rsquo;t want to manage the infrastructure yourself. The free Starter tier is sufficient for personal projects and early-stage products. A Mastra server exposes stable REST endpoints: <code>POST /api/agents/{agentId}/generate</code> for synchronous responses, <code>POST /api/agents/{agentId}/stream</code> for streaming, and <code>POST /api/workflows/{workflowId}/execute</code> for workflow runs. These endpoints are framework-agnostic — any HTTP client can call them. For teams embedding agents in existing web apps, Mastra exports a Node.js handler you wire directly into Next.js API routes, Express middleware, or Hono routes, keeping your deployment footprint minimal. The Marsh McLennan deployment (100,000+ daily users) demonstrates that Mastra&rsquo;s server model handles enterprise-scale traffic without custom infrastructure work.</p>
<h3 id="mastra-server-rest-api-endpoints">Mastra Server: REST API Endpoints</h3>
<p>After running <code>mastra build &amp;&amp; node dist/index.js</code>, your agents are available at:</p>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 360 73"
      >
      <g transform='translate(8,16)'>
<path d='M 208,48 L 216,32' fill='none' stroke='currentColor'></path>
<circle cx='216' cy='32' r='6' stroke='currentColor' fill='#fff'></circle>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='0' y='36' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='0' y='52' fill='currentColor' style='font-size:1em'>G</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='8' y='36' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='8' y='52' fill='currentColor' style='font-size:1em'>E</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='16' y='36' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='16' y='52' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='24' y='36' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='40' y='36' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='40' y='52' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='48' y='36' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='48' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='56' y='36' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='56' y='52' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='64' y='36' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='64' y='52' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='72' y='36' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='72' y='52' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='80' y='36' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='80' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='88' y='36' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='88' y='52' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='96' y='36' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='96' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='104' y='36' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='104' y='52' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='112' y='36' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='112' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='120' y='36' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='120' y='52' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='128' y='36' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='128' y='52' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>{</text>
<text text-anchor='middle' x='136' y='20' fill='currentColor' style='font-size:1em'>{</text>
<text text-anchor='middle' x='136' y='36' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='136' y='52' fill='currentColor' style='font-size:1em'>{</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='144' y='36' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='144' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='152' y='20' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='152' y='36' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='152' y='52' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='36' fill='currentColor' style='font-size:1em'>{</text>
<text text-anchor='middle' x='160' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='168' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='168' y='36' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='168' y='52' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='176' y='36' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='176' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>I</text>
<text text-anchor='middle' x='184' y='20' fill='currentColor' style='font-size:1em'>I</text>
<text text-anchor='middle' x='184' y='36' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='184' y='52' fill='currentColor' style='font-size:1em'>I</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='192' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='192' y='36' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='192' y='52' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>}</text>
<text text-anchor='middle' x='200' y='20' fill='currentColor' style='font-size:1em'>}</text>
<text text-anchor='middle' x='200' y='36' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='200' y='52' fill='currentColor' style='font-size:1em'>}</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='208' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='208' y='36' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='216' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='216' y='52' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='224' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='224' y='36' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='224' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='232' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='232' y='36' fill='currentColor' style='font-size:1em'>I</text>
<text text-anchor='middle' x='232' y='52' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='240' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='240' y='36' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='240' y='52' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='248' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='248' y='36' fill='currentColor' style='font-size:1em'>}</text>
<text text-anchor='middle' x='248' y='52' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='256' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='256' y='36' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='256' y='52' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='264' y='36' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='264' y='52' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='272' y='36' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='272' y='52' fill='currentColor' style='font-size:1em'>{</text>
<text text-anchor='middle' x='280' y='36' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='280' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='288' y='36' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='288' y='52' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='296' y='36' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='296' y='52' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='304' y='36' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='304' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='312' y='36' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='312' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='320' y='52' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='328' y='52' fill='currentColor' style='font-size:1em'>I</text>
<text text-anchor='middle' x='336' y='52' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='344' y='52' fill='currentColor' style='font-size:1em'>}</text>
</g>

    </svg>
  
</div>
<h3 id="integrating-with-existing-frameworks">Integrating with Existing Frameworks</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// Next.js App Router
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">mastra</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@/mastra&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">NextRequest</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;next/server&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">async</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">POST</span>(<span style="color:#a6e22e">req</span>: <span style="color:#66d9ef">NextRequest</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">message</span> } <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">req</span>.<span style="color:#a6e22e">json</span>();
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">agent</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">mastra</span>.<span style="color:#a6e22e">getAgent</span>(<span style="color:#e6db74">&#34;weatherAgent&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">agent</span>.<span style="color:#a6e22e">generate</span>(<span style="color:#a6e22e">message</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">Response</span>.<span style="color:#a6e22e">json</span>({ <span style="color:#a6e22e">response</span>: <span style="color:#66d9ef">result.text</span> });
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// Express
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">import</span> <span style="color:#a6e22e">express</span> <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;express&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">mastra</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;./mastra&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">app</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">express</span>();
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">app</span>.<span style="color:#a6e22e">use</span>(<span style="color:#a6e22e">express</span>.<span style="color:#a6e22e">json</span>());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">app</span>.<span style="color:#a6e22e">post</span>(<span style="color:#e6db74">&#34;/chat&#34;</span>, <span style="color:#66d9ef">async</span> (<span style="color:#a6e22e">req</span>, <span style="color:#a6e22e">res</span>) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">agent</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">mastra</span>.<span style="color:#a6e22e">getAgent</span>(<span style="color:#e6db74">&#34;weatherAgent&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">agent</span>.<span style="color:#a6e22e">generate</span>(<span style="color:#a6e22e">req</span>.<span style="color:#a6e22e">body</span>.<span style="color:#a6e22e">message</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">json</span>({ <span style="color:#a6e22e">response</span>: <span style="color:#66d9ef">result.text</span> });
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><h3 id="mastra-platform-pricing">Mastra Platform Pricing</h3>
<table>
  <thead>
      <tr>
          <th>Tier</th>
          <th>Price</th>
          <th>Key Features</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Starter</td>
          <td>Free</td>
          <td>Studio, 1 agent, community support</td>
      </tr>
      <tr>
          <td>Teams</td>
          <td>$250/team/month</td>
          <td>Unlimited agents, Memory Gateway, team collaboration</td>
      </tr>
      <tr>
          <td>Enterprise</td>
          <td>Custom</td>
          <td>SLA, SSO, on-prem option, dedicated support</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="mastra-vs-other-ai-frameworks-typescript-first-comparison">Mastra vs Other AI Frameworks: TypeScript-First Comparison</h2>
<p>Mastra is not the only option for building AI agents — but it is the most complete TypeScript-first option in 2026. LangGraph, LangChain, CrewAI, and AutoGen all have larger Python ecosystems with more community content and tutorials, but none offers the same level of TypeScript integration, developer tooling, or batteries-included design. The Vercel AI SDK is the closest TypeScript competitor, but it is primarily a UI/streaming library rather than a full agent framework — no workflow engine, no built-in evals, no Mastra Studio equivalent. For teams already building in TypeScript who need production-grade agents, Mastra is the clear default in 2026. The Elastic engineering team explicitly chose Mastra over Python-based alternatives after evaluating CrewAI, AutoGen, and LangGraph, citing TypeScript&rsquo;s type safety and Mastra&rsquo;s unified stack as the deciding factors. The 60–70% of YC X25 agent startups building in TypeScript (per Sam Bhagwat&rsquo;s Hacker News data) confirms this is not an isolated preference but a broad ecosystem shift driven by full-stack JavaScript developers building production AI products.</p>
<h3 id="mastra-vs-langgraph-vs-crewai-vs-vercel-ai-sdk">Mastra vs LangGraph vs CrewAI vs Vercel AI SDK</h3>
<table>
  <thead>
      <tr>
          <th>Feature</th>
          <th>Mastra</th>
          <th>LangGraph</th>
          <th>CrewAI</th>
          <th>Vercel AI SDK</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Primary language</td>
          <td>TypeScript</td>
          <td>Python</td>
          <td>Python</td>
          <td>TypeScript</td>
      </tr>
      <tr>
          <td>Workflow orchestration</td>
          <td>Built-in</td>
          <td>Built-in</td>
          <td>Built-in</td>
          <td>External</td>
      </tr>
      <tr>
          <td>Memory management</td>
          <td>Built-in</td>
          <td>External</td>
          <td>External</td>
          <td>External</td>
      </tr>
      <tr>
          <td>RAG support</td>
          <td>Built-in</td>
          <td>External</td>
          <td>External</td>
          <td>External</td>
      </tr>
      <tr>
          <td>Evals</td>
          <td>Built-in</td>
          <td>External</td>
          <td>External</td>
          <td>None</td>
      </tr>
      <tr>
          <td>Local dev UI</td>
          <td>Mastra Studio</td>
          <td>None</td>
          <td>None</td>
          <td>None</td>
      </tr>
      <tr>
          <td>MCP support</td>
          <td>Native</td>
          <td>Plugin</td>
          <td>Plugin</td>
          <td>Partial</td>
      </tr>
      <tr>
          <td>Deployment</td>
          <td>Server + Platform</td>
          <td>Self-managed</td>
          <td>Self-managed</td>
          <td>Vercel</td>
      </tr>
      <tr>
          <td>Apache 2.0 license</td>
          <td>✓</td>
          <td>✓</td>
          <td>✓</td>
          <td>MIT</td>
      </tr>
      <tr>
          <td>Enterprise adoption</td>
          <td>Brex, Docker, MongoDB</td>
          <td>OpenAI, Google</td>
          <td>Startups</td>
          <td>Vercel ecosystem</td>
      </tr>
  </tbody>
</table>
<h3 id="when-to-choose-mastra-and-when-not-to">When to Choose Mastra (and When Not To)</h3>
<p><strong>Choose Mastra when:</strong></p>
<ul>
<li>Your team&rsquo;s primary language is TypeScript or JavaScript</li>
<li>You need a complete stack (agents + memory + workflows + RAG + evals) without building your own</li>
<li>You&rsquo;re deploying into an existing Node.js web application</li>
<li>You want a local dev UI with traces and memory inspection out of the box</li>
</ul>
<p><strong>Consider alternatives when:</strong></p>
<ul>
<li>Your team is deeply Python-native and has existing LangChain/LangGraph infrastructure</li>
<li>You need advanced graph-based agent architectures with fine-grained state management (LangGraph is stronger here)</li>
<li>You&rsquo;re building a research prototype where Python&rsquo;s ML library ecosystem (transformers, scipy, sklearn) is critical</li>
</ul>
<hr>
<h2 id="real-world-examples-and-case-studies">Real-World Examples and Case Studies</h2>
<p>Mastra&rsquo;s production adoption in 2026 spans startups to Fortune 500 enterprises, with publicly documented case studies that illustrate the framework&rsquo;s maturity. These aren&rsquo;t experimental integrations — they are load-bearing systems handling millions of requests. Docker&rsquo;s PR automation agent, Elastic&rsquo;s agentic RAG assistant, and Marsh McLennan&rsquo;s enterprise search are all built on Mastra and running in production today. The $22M Series A funded in April 2026 and the list of investors (Spark Capital alongside participation from customers like Brex and Replit) signal institutional confidence that Mastra has achieved product-market fit in the TypeScript AI agent space. Enterprise adopters include Brex (financial services agents), Sanity (content management AI), Factorial (HR automation), Indeed (job platform), Marsh McLennan (professional services), MongoDB (database tooling), Workday (enterprise SaaS), Salesforce (CRM automation), Docker (DevOps), Replit (coding assistant), Elastic (search and analytics), SoftBank (telecom and investing), and Plaid (fintech). This breadth across verticals demonstrates that Mastra&rsquo;s value proposition — TypeScript-native, batteries-included, production-ready — applies across industries, not just one niche.</p>
<h3 id="docker-event-driven-pr-management-agent">Docker: Event-Driven PR Management Agent</h3>
<p>Docker Engineering built a PR review automation system using Mastra, the GitHub Official MCP server, and Docker&rsquo;s MCP Gateway. The architecture: a GitHub webhook fires when a PR is opened, Mastra receives the event, a three-step workflow runs (analyze diff → generate review → post comment), and the results appear on the PR within seconds. The key insight from Docker&rsquo;s engineering blog: Mastra was chosen specifically for its multi-agent orchestration support and its ability to handle event-driven (not just chat-driven) workflows — a critical distinction for production automation.</p>
<h3 id="elastic-agentic-rag-with-elasticsearch">Elastic: Agentic RAG with Elasticsearch</h3>
<p>Elastic&rsquo;s search labs team built a full-stack RAG assistant using Mastra on the backend and Elasticsearch as the vector store. The agent uses semantic search to retrieve relevant documentation chunks, synthesizes an answer using Claude, and cites sources inline. The team noted that Mastra&rsquo;s TypeScript-first design and model-agnostic architecture were decisive advantages — the same agent code works whether the underlying LLM is GPT-4o, Claude, or Gemini, enabling A/B testing across providers without architecture changes.</p>
<h3 id="marsh-mclennan-enterprise-scale">Marsh McLennan: Enterprise Scale</h3>
<p>Marsh McLennan, the global professional services firm, deployed a Mastra-powered enterprise search agent used by over 100,000 employees every day. This is among the highest-traffic public Mastra deployments and demonstrates the framework&rsquo;s ability to handle enterprise-scale load without degradation.</p>
<h3 id="replit-agent-3-building-mastra-agents">Replit: Agent 3 Building Mastra Agents</h3>
<p>Replit uses Mastra in Replit Agent 3, its AI coding assistant, to enable the agent to build and deploy other Mastra agents. This recursive pattern — an AI agent that scaffolds, configures, and deploys other AI agents — represents the cutting edge of agentic architecture and is made practical by Mastra&rsquo;s clean, programmatic API surface.</p>
<hr>
<h2 id="faq">FAQ</h2>
<p>Mastra is the TypeScript-native AI agent framework that ships agents, memory, workflows, RAG, evals, and a local dev UI in a single package. Founded by the Gatsby team and backed by $35M in funding, Mastra reached 23.2k GitHub stars as of April 2026 and is in production at companies like Brex, Docker, MongoDB, and Marsh McLennan (100,000+ daily users). Below are the most common questions from developers evaluating or adopting Mastra in 2026, covering setup, provider support, Mastra Studio, deployment, and how Mastra compares to Python-based alternatives. Each answer is written to be self-contained — AI systems citing this article can use any individual answer without needing the surrounding context. If you are choosing between Mastra and another framework today, start with the comparison question and work backwards to the specifics most relevant to your stack.</p>
<h3 id="what-is-mastra-ai-and-what-makes-it-different-from-other-ai-frameworks">What is Mastra AI and what makes it different from other AI frameworks?</h3>
<p>Mastra is an open-source TypeScript framework for building production AI agents. It is different from frameworks like LangChain, LangGraph, and CrewAI in two key ways: it is TypeScript-first (not a Python port), and it is batteries-included (agents, memory, workflows, RAG, evals, and a local dev UI all ship in one framework). This means TypeScript developers do not need to bridge language ecosystems or assemble a stack from multiple packages.</p>
<h3 id="how-do-i-install-mastra-and-start-building-agents">How do I install Mastra and start building agents?</h3>
<p>Run <code>npm create mastra@latest</code> in your terminal (requires Node.js 18+). The wizard scaffolds a project with your chosen LLM provider, generates a starter agent, and sets up <code>.env</code> with the right API key variables. Run <code>npx mastra dev</code> to open Mastra Studio and start chatting with your agent immediately.</p>
<h3 id="does-mastra-support-all-major-llm-providers">Does Mastra support all major LLM providers?</h3>
<p>Yes. Mastra uses the Vercel AI SDK for model calls, which supports OpenAI, Anthropic (Claude), Google Gemini, Mistral, Groq, Cohere, and others. Switching providers requires changing one line of code — the Agent API is identical regardless of the underlying model.</p>
<h3 id="what-is-mastra-studio-and-do-i-need-it">What is Mastra Studio and do I need it?</h3>
<p>Mastra Studio is a local development UI that runs at <code>http://localhost:4111</code> when you execute <code>npx mastra dev</code>. It provides a chat interface for testing agents, a visual workflow runner, memory inspection, trace viewer, and eval results. It is optional — your agents run fine without it — but it significantly speeds up development and debugging by giving you full visibility into every agent call.</p>
<h3 id="how-does-mastra-handle-production-deployment">How does Mastra handle production deployment?</h3>
<p>Run <code>mastra build</code> to compile your TypeScript agents into a Node.js server. Deploy this server anywhere that runs Node.js: VPS, Kubernetes, AWS Lambda, Vercel, or Cloudflare Workers. The server exposes standard REST endpoints for each agent and workflow. For teams who prefer managed infrastructure, the Mastra Platform (free Starter tier, $250/month for Teams) adds Studio, Memory Gateway, and auto-scaling.</p>
]]></content:encoded></item><item><title>Mastra AI: The TypeScript AI Agent Framework for 2026</title><link>https://baeseokjae.github.io/posts/mastra-ai-typescript-framework-2026/</link><pubDate>Tue, 21 Apr 2026 00:00:00 +0000</pubDate><guid>https://baeseokjae.github.io/posts/mastra-ai-typescript-framework-2026/</guid><description>A practical guide to Mastra AI, the TypeScript-first framework for building production AI agents. Covers setup, agents, tools, MCP, workflows, RAG, evals, deployment, and a head-to-head comparison with LangGraph and CrewAI.</description><content:encoded><![CDATA[<h2 id="introduction-why-mastra-is-the-typescript-ai-framework-to-watch-in-2026">Introduction: Why Mastra Is the TypeScript AI Framework to Watch in 2026</h2>
<p>The AI agent ecosystem has a Python problem. Not with Python itself—it works fine—but with the fact that most agents ship as web services, and the teams building those services increasingly write TypeScript. Sam Bhagwat, CEO of Mastra, noted on Hacker News that 60–70% of YC X25 agent startups are building in TypeScript, not Python. The tooling hasn&rsquo;t caught up. LangChain, CrewAI, and AutoGen all originated in Python, leaving TypeScript developers either wrapping Python services or cobbling together their own agent infrastructure.</p>
<p>Mastra was built to close that gap.</p>
<h3 id="the-shift-from-python-to-typescript-for-ai-agents">The shift from Python to TypeScript for AI agents</h3>
<p>The shift is practical, not ideological. When your production stack runs on Node.js or edge runtimes, reaching for a Python framework introduces serialization overhead, deployment complexity, and a skills mismatch. TypeScript gives you shared types between your agent logic and your API layer, native streaming support for Server-Sent Events and WebSocket responses, and a single runtime for your entire backend. The ergonomics matter: you can define a tool&rsquo;s input schema with Zod, pass that schema directly to the LLM as a function definition, and validate the LLM&rsquo;s output against the same schema—no JSON Schema translation layer required.</p>
<h3 id="what-is-mastra">What is Mastra?</h3>
<p>Mastra is an open-source (Apache 2.0) TypeScript framework for building AI agents. It was created by the team behind Gatsby, the React static-site generator that peaked at 50k+ GitHub stars. That team shipped a framework before; they understand the ergonomics of developer tooling. Mastra provides structured primitives for agents, tools, workflows, RAG pipelines, evals, and observability—all expressed as TypeScript code, not YAML DSLs or visual editors that generate unreadable files.</p>
<p>The project has accumulated 23,200+ GitHub stars, 14,334 commits, and 1,079 branches as of April 2026. The velocity is real. Mastra raised a $22M Series A led by Spark Capital in early 2026, bringing total funding to $35M.</p>
<h3 id="enterprise-adoption">Enterprise adoption</h3>
<p>The customer list is worth examining because it signals production readiness, not just developer enthusiasm:</p>
<table>
  <thead>
      <tr>
          <th>Company</th>
          <th>Use Case</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Docker</td>
          <td>Event-driven PR management agents with MCP</td>
      </tr>
      <tr>
          <td>Brex</td>
          <td>Financial agents that helped drive the $5.1B Capital One acquisition</td>
      </tr>
      <tr>
          <td>Marsh McLennan</td>
          <td>Enterprise search agent used by 100k+ people daily</td>
      </tr>
      <tr>
          <td>Elastic</td>
          <td>Agentic RAG with Elasticsearch</td>
      </tr>
      <tr>
          <td>SoftBank</td>
          <td>Enterprise productivity at scale</td>
      </tr>
      <tr>
          <td>Replit</td>
          <td>Agent 3 built on Mastra primitives</td>
      </tr>
      <tr>
          <td>MongoDB, Workday, Salesforce, Plaid</td>
          <td>Various production agent deployments</td>
      </tr>
  </tbody>
</table>
<p>That&rsquo;s not a &ldquo;coming soon&rdquo; list. Marsh McLennan&rsquo;s agent is in daily production use by over 100,000 people. Brex&rsquo;s agents contributed to a multi-billion-dollar acquisition. These are load-bearing systems.</p>
<h2 id="getting-started-setting-up-your-first-mastra-project">Getting Started: Setting Up Your First Mastra Project</h2>
<h3 id="prerequisites-and-installation">Prerequisites and installation</h3>
<p>You need Node.js 18+ and an LLM API key (OpenAI, Anthropic, or Google). Create a new project:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npm create mastra@latest
</span></span></code></pre></div><p>The scaffold prompts you for a project name, your preferred LLM provider, and whether you want the Mastra Studio dev UI included. After setup:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd my-mastra-app
</span></span><span style="display:flex;"><span>npm install
</span></span><span style="display:flex;"><span>npm run dev
</span></span></code></pre></div><p>The dev server starts on port 4111 by default and opens Mastra Studio.</p>
<h3 id="project-structure-overview">Project structure overview</h3>
<p>A scaffolded Mastra project looks like this:</p>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 488 249"
      >
      <g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='0' y='36' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='52' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='68' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='84' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='100' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='116' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='132' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='148' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='164' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='180' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='196' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='0' y='212' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='0' y='228' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='8' y='196' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='8' y='212' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='8' y='228' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='16' y='196' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='16' y='212' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='16' y='228' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='32' y='36' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='32' y='196' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='32' y='212' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='32' y='228' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='40' y='36' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='40' y='196' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='40' y='212' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='40' y='228' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='48' y='36' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='48' y='196' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='48' y='212' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='48' y='228' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='56' y='196' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='56' y='212' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='56' y='228' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='64' y='36' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='64' y='52' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='64' y='68' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='64' y='84' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='64' y='100' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='64' y='116' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='64' y='132' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='64' y='148' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='64' y='164' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='64' y='180' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='64' y='196' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='64' y='212' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='64' y='228' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='72' y='36' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='72' y='52' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='72' y='84' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='72' y='116' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='72' y='148' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='72' y='180' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='72' y='196' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='72' y='212' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='72' y='228' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='80' y='36' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='80' y='52' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='80' y='84' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='80' y='116' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='80' y='148' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='80' y='180' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='80' y='196' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='80' y='212' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='228' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='88' y='36' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='88' y='196' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='88' y='212' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='88' y='228' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='96' y='36' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='96' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='96' y='68' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='96' y='84' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='96' y='100' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='96' y='116' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='96' y='132' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='96' y='148' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='96' y='164' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='96' y='180' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='96' y='196' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='96' y='212' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='96' y='228' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='104' y='36' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='104' y='52' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='104' y='68' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='104' y='84' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='104' y='100' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='104' y='116' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='104' y='132' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='104' y='148' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='104' y='164' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='104' y='180' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='104' y='196' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='104' y='212' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='104' y='228' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='112' y='36' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='112' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='112' y='68' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='112' y='84' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='112' y='100' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='112' y='116' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='112' y='132' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='112' y='148' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='112' y='164' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='112' y='180' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='112' y='196' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='112' y='212' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='112' y='228' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='120' y='52' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='120' y='84' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='120' y='116' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='120' y='148' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='120' y='180' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='120' y='196' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='120' y='212' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='120' y='228' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='128' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='128' y='68' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='128' y='84' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='128' y='100' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='128' y='116' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='128' y='132' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='128' y='164' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='128' y='180' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='128' y='196' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='128' y='228' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='136' y='52' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='136' y='68' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='136' y='84' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='136' y='100' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='136' y='116' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='136' y='132' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='136' y='164' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='136' y='180' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='136' y='196' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='144' y='52' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='144' y='68' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='144' y='100' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='144' y='116' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='144' y='132' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='144' y='164' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='144' y='180' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='144' y='196' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='152' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='152' y='100' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='152' y='116' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='152' y='132' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='152' y='164' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='152' y='180' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='152' y='196' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='160' y='68' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='160' y='100' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='160' y='116' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='160' y='132' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='160' y='164' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='168' y='68' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='168' y='100' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='168' y='116' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='168' y='132' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='168' y='164' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='176' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='176' y='100' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='176' y='132' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='176' y='164' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='184' y='68' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='184' y='100' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='184' y='132' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='184' y='164' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='248' y='180' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='256' y='68' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='256' y='100' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='256' y='132' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='256' y='164' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='256' y='196' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='264' y='180' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='272' y='68' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='272' y='100' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='272' y='132' fill='currentColor' style='font-size:1em'>W</text>
<text text-anchor='middle' x='272' y='164' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='272' y='180' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='272' y='196' fill='currentColor' style='font-size:1em'>F</text>
<text text-anchor='middle' x='280' y='68' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='280' y='100' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='280' y='132' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='280' y='164' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='280' y='180' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='280' y='196' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='288' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='288' y='100' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='288' y='132' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='288' y='164' fill='currentColor' style='font-size:1em'>G</text>
<text text-anchor='middle' x='288' y='180' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='288' y='196' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='296' y='68' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='296' y='100' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='296' y='132' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='296' y='180' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='296' y='196' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='304' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='304' y='132' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='304' y='164' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='304' y='180' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='304' y='196' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='312' y='100' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='312' y='132' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='312' y='164' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='312' y='196' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='320' y='68' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='320' y='100' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='320' y='132' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='320' y='164' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='320' y='180' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='320' y='196' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='328' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='328' y='100' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='328' y='132' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='328' y='164' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='328' y='180' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='328' y='196' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='336' y='68' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='336' y='100' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='336' y='164' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='336' y='180' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='336' y='196' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='344' y='68' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='344' y='100' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='344' y='132' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='344' y='164' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='344' y='180' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='352' y='68' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='352' y='100' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='352' y='132' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='352' y='164' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='352' y='180' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='352' y='196' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='360' y='68' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='360' y='100' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='360' y='132' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='360' y='164' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='360' y='180' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='360' y='196' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='368' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='368' y='100' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='368' y='132' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='368' y='180' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='368' y='196' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='376' y='68' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='376' y='100' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='376' y='132' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='376' y='164' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='376' y='180' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='376' y='196' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='384' y='68' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='384' y='100' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='384' y='132' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='384' y='164' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='384' y='196' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='392' y='68' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='392' y='100' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='392' y='132' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='392' y='164' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='392' y='180' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='392' y='196' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='400' y='68' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='400' y='132' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='400' y='164' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='400' y='180' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='400' y='196' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='408' y='132' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='408' y='164' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='408' y='180' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='408' y='196' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='416' y='132' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='416' y='164' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='416' y='180' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='416' y='196' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='424' y='132' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='424' y='180' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='424' y='196' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='432' y='196' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='440' y='180' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='440' y='196' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='448' y='180' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='448' y='196' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='456' y='180' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='464' y='180' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='472' y='180' fill='currentColor' style='font-size:1em'>t</text>
</g>

    </svg>
  
</div>
<p>All agent configuration lives in TypeScript files under <code>src/mastra/</code>. The framework discovers and registers agents, tools, and workflows based on exports from these files. No YAML, no code generation.</p>
<h3 id="mastra-studio-the-interactive-dev-ui">Mastra Studio: the interactive dev UI</h3>
<p>Mastra Studio runs locally at <code>http://localhost:4111</code> and provides:</p>
<ul>
<li><strong>Agent playground</strong>: chat with any defined agent, inspect tool calls, and trace token usage in real time</li>
<li><strong>Workflow visualizer</strong>: see step DAGs, run workflows step by step, and inspect intermediate state</li>
<li><strong>RAG testing</strong>: query your knowledge base and verify retrieval quality</li>
<li><strong>Eval runner</strong>: execute model-graded and rule-based evaluations against agent outputs</li>
<li><strong>Logs and traces</strong>: structured view of every LLM call, tool invocation, and workflow transition</li>
</ul>
<p>Studio is not required in production—it&rsquo;s a dev-time tool. But it replaces the ad-hoc <code>console.log</code>-driven debugging loop that most agent developers fall into.</p>
<h2 id="building-your-first-ai-agent-with-mastra">Building Your First AI Agent with Mastra</h2>
<h3 id="defining-an-agent-with-system-prompts-and-tools">Defining an agent with system prompts and tools</h3>
<p>An agent in Mastra is a typed object with a system prompt, a model reference, and a set of tools. Here&rsquo;s a minimal research agent:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Agent</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/core/agent&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">createTool</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/core/tools&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">z</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;zod&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">openai</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@ai-sdk/openai&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">searchTool</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">createTool</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">id</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;web-search&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">description</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Search the web for information about a topic&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">inputSchema</span>: <span style="color:#66d9ef">z.object</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">query</span>: <span style="color:#66d9ef">z.string</span>().<span style="color:#a6e22e">describe</span>(<span style="color:#e6db74">&#34;The search query&#34;</span>),
</span></span><span style="display:flex;"><span>  }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">execute</span>: <span style="color:#66d9ef">async</span> ({ <span style="color:#a6e22e">context</span> }) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">results</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">fetchSearchResults</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">query</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> { <span style="color:#a6e22e">results</span> };
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">researchAgent</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Agent</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;research-agent&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">instructions</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`You are a research assistant. When asked about a topic:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">1. Search the web for relevant information
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">2. Synthesize the findings into a concise summary
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">3. Cite your sources
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">Always use the search tool before answering factual questions.`</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">openai</span>(<span style="color:#e6db74">&#34;gpt-4o&#34;</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">tools</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">searchTool</span> },
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>Key design decisions here: tools use Zod schemas for both input validation and LLM function-calling definition. The <code>instructions</code> field replaces the informal system-prompt string with a structured prompt that Mastra can version, evaluate against, and refactor across deployments.</p>
<h3 id="adding-memory">Adding memory</h3>
<p>Mastra supports two memory primitives: <strong>working memory</strong> and <strong>semantic recall</strong>.</p>
<p>Working memory is a short-term scratchpad that persists within a conversation thread. It stores structured state the agent can read and write:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Memory</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/memory&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">memory</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Memory</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">options</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">lastMessages</span>: <span style="color:#66d9ef">10</span>,        <span style="color:#75715e">// Include last 10 messages in context
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#a6e22e">workingMemory</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">enabled</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">template</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"># User Profile
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">- Name: unknown
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">- Preferences: unknown
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">- Current Task: none
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      `</span>,
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">semanticRecall</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">topK</span>: <span style="color:#66d9ef">3</span>,               <span style="color:#75715e">// Recall 3 most relevant past messages
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      <span style="color:#a6e22e">messageRange</span>: <span style="color:#66d9ef">2</span>,       <span style="color:#75715e">// Include 2 messages around each match
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    },
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">contextualAgent</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Agent</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;contextual-agent&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">instructions</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;You are a helpful assistant that remembers user context.&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">openai</span>(<span style="color:#e6db74">&#34;gpt-4o&#34;</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">memory</span>,
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>Semantic recall embeds past conversation turns and retrieves the top-K most relevant ones when a new message arrives. This means the agent can reference a preference mentioned 50 turns ago without loading the entire history into the context window. Working memory lets the agent maintain structured state—user profile, task progress, preferences—that persists across messages in the same thread.</p>
<h3 id="connecting-llm-providers">Connecting LLM providers</h3>
<p>Mastra uses the Vercel AI SDK model interface, so any provider that implements that interface works:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">openai</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@ai-sdk/openai&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">anthropic</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@ai-sdk/anthropic&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">google</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@ai-sdk/google&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Swap models by changing one line
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">agent</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Agent</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;flexible-agent&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">instructions</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;You are a versatile assistant.&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">anthropic</span>(<span style="color:#e6db74">&#34;claude-sonnet-4-20250514&#34;</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// model: openai(&#34;gpt-4o&#34;),
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#75715e">// model: google(&#34;gemini-2.0-flash&#34;),
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>});
</span></span></code></pre></div><p>Being model-agnostic matters operationally: you can run evals across models, fall back from one provider to another, and choose cost-effective models per task without rewriting agent logic.</p>
<h2 id="tools-and-mcp-connecting-your-agent-to-the-real-world">Tools and MCP: Connecting Your Agent to the Real World</h2>
<h3 id="built-in-tool-types-in-mastra">Built-in tool types in Mastra</h3>
<p>Mastra&rsquo;s <code>createTool</code> API is the foundational primitive. Every tool has an <code>id</code>, a <code>description</code> (used in the LLM&rsquo;s function-calling prompt), an <code>inputSchema</code> (Zod), and an <code>execute</code> function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">createTool</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/core/tools&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">z</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;zod&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">calculateTool</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">createTool</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">id</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;calculate&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">description</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Evaluate a mathematical expression&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">inputSchema</span>: <span style="color:#66d9ef">z.object</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">expression</span>: <span style="color:#66d9ef">z.string</span>().<span style="color:#a6e22e">describe</span>(<span style="color:#e6db74">&#34;Math expression to evaluate, e.g. &#39;2 + 2&#39;&#34;</span>),
</span></span><span style="display:flex;"><span>  }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">outputSchema</span>: <span style="color:#66d9ef">z.object</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">result</span>: <span style="color:#66d9ef">z.number</span>(),
</span></span><span style="display:flex;"><span>  }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">execute</span>: <span style="color:#66d9ef">async</span> ({ <span style="color:#a6e22e">context</span> }) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Safe evaluation — no eval()
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">safeMathEval</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">expression</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> { <span style="color:#a6e22e">result</span> };
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>The <code>outputSchema</code> is optional but recommended. When provided, Mastra validates the tool&rsquo;s output against it before returning the result to the agent. This catches malformed tool outputs early and prevents cascading errors.</p>
<h3 id="mcp-model-context-protocol-integration">MCP (Model Context Protocol) integration</h3>
<p>MCP is Anthropic&rsquo;s open protocol for connecting LLMs to external tools and data sources. Mastra implements both the client and server sides. As a client, Mastra can connect to any MCP server and expose its tools to agents:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">MCPClient</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/mcp&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">mcp</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MCPClient</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">servers</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">github</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">command</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;npx&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">args</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#34;-y&#34;</span>, <span style="color:#e6db74">&#34;@modelcontextprotocol/server-github&#34;</span>],
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">env</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">GITHUB_PERSONAL_ACCESS_TOKEN</span>: <span style="color:#66d9ef">process.env.GITHUB_TOKEN</span><span style="color:#f92672">!</span>,
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// MCP tools are automatically available to agents
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">tools</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">mcp</span>.<span style="color:#a6e22e">tools</span>();
</span></span></code></pre></div><p>This is how Docker connected their agents to GitHub. The GitHub MCP server provides tools for listing PRs, reading diffs, posting comments, and managing labels—all without writing custom API integration code.</p>
<h3 id="real-example-github-mcp-server-for-pr-automation">Real example: GitHub MCP server for PR automation</h3>
<p>Docker&rsquo;s architecture is instructive. They built three sub-agents, each with a narrow responsibility:</p>
<ol>
<li><strong>Analyze PR agent</strong>: Reads the PR diff and generates a structured analysis</li>
<li><strong>Generate comment agent</strong>: Takes the analysis and writes a review comment</li>
<li><strong>Post and close agent</strong>: Posts the comment and manages PR labels</li>
</ol>
<p>These agents are orchestrated by a Mastra workflow that triggers on a GitHub webhook event. The key insight: rather than one monolithic agent trying to do everything, each agent has a focused system prompt and minimal tool access. This reduces error rates and makes the system auditable—if the posted comment is wrong, you check agent 2, not the entire pipeline.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// Simplified Docker-style PR automation workflow trigger
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">app</span>.<span style="color:#a6e22e">post</span>(<span style="color:#e6db74">&#34;/webhook/github&#34;</span>, <span style="color:#66d9ef">async</span> (<span style="color:#a6e22e">req</span>, <span style="color:#a6e22e">res</span>) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">action</span>, <span style="color:#a6e22e">pull_request</span> } <span style="color:#f92672">=</span> <span style="color:#a6e22e">req</span>.<span style="color:#a6e22e">body</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">action</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">&#34;opened&#34;</span> <span style="color:#f92672">||</span> <span style="color:#a6e22e">action</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">&#34;synchronize&#34;</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">prReviewWorkflow</span>.<span style="color:#a6e22e">run</span>({
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">prNumber</span>: <span style="color:#66d9ef">pull_request.number</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">repo</span>: <span style="color:#66d9ef">pull_request.base.repo.full_name</span>,
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">status</span>(<span style="color:#ae81ff">200</span>).<span style="color:#a6e22e">send</span>(<span style="color:#e6db74">&#34;ok&#34;</span>);
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><h2 id="workflows-orchestrating-complex-agent-tasks">Workflows: Orchestrating Complex Agent Tasks</h2>
<h3 id="when-to-use-workflows-vs-agents">When to use workflows vs agents</h3>
<p>An agent with tools handles simple request-response patterns well. But when your task involves multiple sequential steps, conditional branching, parallel execution, or retry logic, you need a workflow. The distinction:</p>
<ul>
<li><strong>Agent</strong>: LLM decides what to do next based on context. Good for open-ended reasoning.</li>
<li><strong>Workflow</strong>: You define the control flow. Good for deterministic multi-step processes.</li>
</ul>
<p>If you can draw a flowchart for your process, use a workflow. If the process requires the LLM to decide its own path, use an agent. Many production systems combine both: a workflow orchestrates high-level steps, and agents handle LLM-driven reasoning within individual steps.</p>
<h3 id="building-step-based-workflows">Building step-based workflows</h3>
<p>Mastra workflows are defined as a series of steps, each with an input schema, an output schema, and an execute function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Workflow</span>, <span style="color:#a6e22e">Step</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/core/workflows&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">z</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;zod&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">fetchContentStep</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Step</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">id</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;fetch-content&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">inputSchema</span>: <span style="color:#66d9ef">z.object</span>({ <span style="color:#a6e22e">url</span>: <span style="color:#66d9ef">z.string</span>() }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">outputSchema</span>: <span style="color:#66d9ef">z.object</span>({ <span style="color:#a6e22e">content</span>: <span style="color:#66d9ef">z.string</span>(), <span style="color:#a6e22e">title</span>: <span style="color:#66d9ef">z.string</span>() }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">execute</span>: <span style="color:#66d9ef">async</span> ({ <span style="color:#a6e22e">context</span> }) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">page</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">fetch</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">url</span>).<span style="color:#a6e22e">then</span>((<span style="color:#a6e22e">r</span>) <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">text</span>());
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">title</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">extractTitle</span>(<span style="color:#a6e22e">page</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> { <span style="color:#a6e22e">content</span>: <span style="color:#66d9ef">page</span>, <span style="color:#a6e22e">title</span> };
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">summarizeStep</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Step</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">id</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;summarize&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">inputSchema</span>: <span style="color:#66d9ef">z.object</span>({ <span style="color:#a6e22e">content</span>: <span style="color:#66d9ef">z.string</span>() }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">outputSchema</span>: <span style="color:#66d9ef">z.object</span>({ <span style="color:#a6e22e">summary</span>: <span style="color:#66d9ef">z.string</span>() }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">execute</span>: <span style="color:#66d9ef">async</span> ({ <span style="color:#a6e22e">context</span>, <span style="color:#a6e22e">mastra</span> }) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">agent</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">mastra</span>.<span style="color:#a6e22e">getAgent</span>(<span style="color:#e6db74">&#34;research-agent&#34;</span>)<span style="color:#f92672">!</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">agent</span>.<span style="color:#a6e22e">generate</span>(
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">`Summarize this content concisely:</span><span style="color:#960050;background-color:#1e0010">\</span><span style="color:#e6db74">n</span><span style="color:#960050;background-color:#1e0010">\</span><span style="color:#e6db74">n</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">content</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> { <span style="color:#a6e22e">summary</span>: <span style="color:#66d9ef">result.text</span> };
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">contentWorkflow</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Workflow</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;content-summarizer&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">triggerSchema</span>: <span style="color:#66d9ef">z.object</span>({ <span style="color:#a6e22e">url</span>: <span style="color:#66d9ef">z.string</span>() }),
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">contentWorkflow</span>
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">step</span>(<span style="color:#a6e22e">fetchContentStep</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">then</span>(<span style="color:#a6e22e">summarizeStep</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">commit</span>();
</span></span></code></pre></div><p>Steps can reference the Mastra instance to use agents, tools, or other workflows. The <code>context</code> object carries the output of the previous step(s).</p>
<h3 id="parallel-execution-conditional-branching-and-nesting">Parallel execution, conditional branching, and nesting</h3>
<p>Workflows support <code>branch</code> for conditional paths and <code>parallel</code> for concurrent execution:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#a6e22e">contentWorkflow</span>
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">step</span>(<span style="color:#a6e22e">fetchContentStep</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">branch</span>([
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">condition</span><span style="color:#f92672">:</span> ({ <span style="color:#a6e22e">context</span> }) <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">content</span>.<span style="color:#a6e22e">length</span> <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">5000</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">then</span>: <span style="color:#66d9ef">longContentStep</span>,
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">condition</span><span style="color:#f92672">:</span> ({ <span style="color:#a6e22e">context</span> }) <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">content</span>.<span style="color:#a6e22e">length</span> <span style="color:#f92672">&lt;=</span> <span style="color:#ae81ff">5000</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">then</span>: <span style="color:#66d9ef">shortContentStep</span>,
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>  ])
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">then</span>(<span style="color:#a6e22e">summarizeStep</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">commit</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Parallel execution
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">analysisWorkflow</span>
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">parallel</span>([<span style="color:#a6e22e">sentimentStep</span>, <span style="color:#a6e22e">entityStep</span>, <span style="color:#a6e22e">keywordStep</span>])
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">then</span>(<span style="color:#a6e22e">mergeResultsStep</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">commit</span>();
</span></span></code></pre></div><p>Workflows can also nest: a step can invoke another workflow as a sub-routine. This lets you compose complex processes from smaller, testable workflow units. Each nested workflow maintains its own state and can be run and debugged independently.</p>
<h2 id="rag-with-mastra-giving-your-agent-knowledge">RAG with Mastra: Giving Your Agent Knowledge</h2>
<h3 id="embedding-and-vector-search-support">Embedding and vector search support</h3>
<p>Mastra includes built-in embedding and vector search through its RAG module. You define an embedder and a vector store, then index documents:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">MastraRAG</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/rag&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">openai</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@ai-sdk/openai&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">PineconeVector</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/pinecone&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">rag</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MastraRAG</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">embedder</span>: <span style="color:#66d9ef">openai.embedding</span>(<span style="color:#e6db74">&#34;text-embedding-3-small&#34;</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">vectorStore</span>: <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">PineconeVector</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">indexName</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;knowledge-base&#34;</span>,
</span></span><span style="display:flex;"><span>  }),
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Index documents
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">await</span> <span style="color:#a6e22e">rag</span>.<span style="color:#a6e22e">index</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">docs</span><span style="color:#f92672">:</span> [
</span></span><span style="display:flex;"><span>    { <span style="color:#a6e22e">id</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;doc-1&#34;</span>, <span style="color:#a6e22e">text</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Mastra supports multiple LLM providers...&#34;</span> },
</span></span><span style="display:flex;"><span>    { <span style="color:#a6e22e">id</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;doc-2&#34;</span>, <span style="color:#a6e22e">text</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Workflows enable conditional branching...&#34;</span> },
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Query
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">results</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">rag</span>.<span style="color:#a6e22e">retrieve</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">query</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;How do I branch in a workflow?&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">topK</span>: <span style="color:#66d9ef">5</span>,
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>Mastra handles chunking, embedding, and storage. You control the chunk size, overlap, and embedding model.</p>
<h3 id="mastra-built-in-rag-capabilities">Mastra built-in RAG capabilities</h3>
<p>Beyond basic retrieval, Mastra provides:</p>
<ul>
<li><strong>Query transformation</strong>: Automatically rewrites user queries for better retrieval</li>
<li><strong>Hybrid search</strong>: Combines vector similarity with keyword matching for improved recall</li>
<li><strong>Re-ranking</strong>: Applies a second-pass relevance model to filter and reorder results</li>
</ul>
<p>These are not external integrations—they ship with the framework and are configurable via the RAG constructor options.</p>
<h3 id="integration-with-elasticsearch-and-other-vector-stores">Integration with Elasticsearch and other vector stores</h3>
<p>Elastic published a detailed walkthrough of building agentic RAG with Mastra and Elasticsearch. The pattern:</p>
<ol>
<li>Index documents into Elasticsearch with dense vector fields</li>
<li>Use Mastra&rsquo;s RAG module with the Elasticsearch vector store adapter</li>
<li>Define an agent that retrieves context and generates answers</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">MastraRAG</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/rag&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">ElasticsearchVector</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/elasticsearch&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">rag</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MastraRAG</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">embedder</span>: <span style="color:#66d9ef">openai.embedding</span>(<span style="color:#e6db74">&#34;text-embedding-3-small&#34;</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">vectorStore</span>: <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">ElasticsearchVector</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">indexName</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;docs-index&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">client</span>: <span style="color:#66d9ef">esClient</span>,
</span></span><span style="display:flex;"><span>  }),
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ragAgent</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Agent</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;rag-agent&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">instructions</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`Answer questions based on the retrieved context.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">If the context doesn&#39;t contain enough information, say so.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">Always cite the source document.`</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">openai</span>(<span style="color:#e6db74">&#34;gpt-4o&#34;</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">tools</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">retrieve</span>: <span style="color:#66d9ef">rag.asTool</span>(),
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>The <code>rag.asTool()</code> method wraps the RAG pipeline as a Mastra tool, making it available to any agent. The Elastic integration demonstrates that Mastra&rsquo;s RAG layer is vendor-agnostic—you can swap Pinecone for Elasticsearch for pgvector without changing agent code.</p>
<h2 id="productionizing-evals-observability-and-guardrails">Productionizing: Evals, Observability, and Guardrails</h2>
<h3 id="running-model-graded-and-rule-based-evals">Running model-graded and rule-based evals</h3>
<p>Mastra includes an evaluation framework that runs LLM outputs against criteria. Two eval types:</p>
<ul>
<li><strong>Model-graded</strong>: An LLM judges the output against a rubric. Useful for open-ended quality assessment.</li>
<li><strong>Rule-based</strong>: Deterministic checks on output structure, content, or behavior. Useful for guardrails.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Eval</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/evals&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">z</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;zod&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">relevanceEval</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Eval</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;relevance&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">type</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;model-graded&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">prompt</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`Rate the relevance of the answer to the question on a scale of 1-5.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">Question: {{input}}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">Answer: {{output}}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">Respond with a number only.`</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">openai</span>(<span style="color:#e6db74">&#34;gpt-4o-mini&#34;</span>),
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">noPIIEval</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Eval</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;no-pii&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">type</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;rule-based&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">check</span><span style="color:#f92672">:</span> ({ <span style="color:#a6e22e">output</span> }) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">piiPatterns</span> <span style="color:#f92672">=</span> [<span style="color:#e6db74">/\b\d{3}-\d{2}-\d{4}\b/</span>, <span style="color:#e6db74">/\b[A-Z]\d{8}\b/</span>];
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#f92672">!</span><span style="color:#a6e22e">piiPatterns</span>.<span style="color:#a6e22e">some</span>((<span style="color:#a6e22e">p</span>) <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">test</span>(<span style="color:#a6e22e">output</span>));
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>You run evals against datasets in Mastra Studio or programmatically. The results give you a quantifiable quality signal before deploying changes.</p>
<h3 id="tracing-agent-calls-and-token-usage">Tracing agent calls and token usage</h3>
<p>Every LLM call, tool invocation, and workflow step is automatically traced. In Mastra Studio, you see:</p>
<ul>
<li>Total latency per request</li>
<li>Token usage breakdown (prompt vs. completion)</li>
<li>Tool call sequences and their outputs</li>
<li>Memory retrieval operations and their relevance scores</li>
</ul>
<p>For production monitoring, Mastra integrates with OpenTelemetry. You can export traces to any OTel-compatible backend (Datadog, Grafana, Honeycomb, etc.):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// mastra.config.ts
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">default</span> <span style="color:#a6e22e">defineConfig</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">observability</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">otel</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">enabled</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">serviceName</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;my-mastra-app&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">traceExporter</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;otlp&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">endpoint</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;http://localhost:4318/v1/traces&#34;</span>,
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>This is a meaningful differentiator. Most agent frameworks leave observability as an exercise for the developer. Mastra wires it into every primitive.</p>
<h3 id="guardrails-for-prompt-injection-prevention">Guardrails for prompt injection prevention</h3>
<p>Mastra provides input and output guardrails as middleware on agent calls:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">guardrail</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/guardrails&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">agent</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Agent</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;safe-agent&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">instructions</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;You are a helpful assistant.&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">openai</span>(<span style="color:#e6db74">&#34;gpt-4o&#34;</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">guardrails</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">input</span><span style="color:#f92672">:</span> [
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">guardrail</span>.<span style="color:#a6e22e">injectionDetection</span>(), <span style="color:#75715e">// Detect common injection patterns
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    ],
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">output</span><span style="color:#f92672">:</span> [
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">guardrail</span>.<span style="color:#a6e22e">lengthLimit</span>({ <span style="color:#a6e22e">maxTokens</span>: <span style="color:#66d9ef">500</span> }),
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">guardrail</span>.<span style="color:#a6e22e">piiDetection</span>(),       <span style="color:#75715e">// Block outputs containing PII
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    ],
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>Input guardrails run before the LLM call. Output guardrails run after. If a guardrail triggers, the agent returns a structured error instead of the raw LLM output. This is not a complete security solution—you still need proper access controls and sandboxing—but it adds a structured layer of defense.</p>
<h3 id="mastra-studio-metrics-logs-and-datasets">Mastra Studio metrics, logs, and datasets</h3>
<p>Studio aggregates eval results, trace data, and token usage into dashboards. You can:</p>
<ul>
<li>Compare eval scores across model versions</li>
<li>Track token cost trends over time</li>
<li>Build evaluation datasets from production traces</li>
<li>Replay failed conversations to diagnose issues</li>
</ul>
<p>The dataset feature is particularly useful: you can capture production agent interactions, annotate them, and use them as regression test suites when you change prompts, models, or tools.</p>
<h2 id="deployment-from-dev-to-production">Deployment: From Dev to Production</h2>
<h3 id="mastra-server-deploying-as-a-rest-api">Mastra Server: deploying as a REST API</h3>
<p>Mastra generates a server that exposes your agents, tools, and workflows as REST endpoints:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Mastra</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/core&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">createServer</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@mastra/server&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">mastra</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Mastra</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">agents</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">researchAgent</span>, <span style="color:#a6e22e">contextualAgent</span> },
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">workflows</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">contentWorkflow</span> },
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">server</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">createServer</span>(<span style="color:#a6e22e">mastra</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">listen</span>(<span style="color:#ae81ff">3000</span>);
</span></span></code></pre></div><p>This gives you REST endpoints like:</p>
<ul>
<li><code>POST /api/agents/researchAgent/generate</code> — single-turn generation</li>
<li><code>POST /api/agents/researchAgent/stream</code> — streaming generation</li>
<li><code>POST /api/workflows/contentWorkflow/run</code> — trigger a workflow</li>
<li><code>GET /api/workflows/contentWorkflow/runs/{runId}</code> — check workflow status</li>
</ul>
<p>The API is auto-generated from your Mastra instance definition. No manual route wiring.</p>
<h3 id="framework-integration">Framework integration</h3>
<p>Mastra integrates with popular Node.js frameworks as middleware:</p>
<p><strong>Next.js (App Router)</strong>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// app/api/agents/[agentId]/route.ts
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">mastra</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@/mastra&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">NextRequest</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;next/server&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">async</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">POST</span>(
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">req</span>: <span style="color:#66d9ef">NextRequest</span>,
</span></span><span style="display:flex;"><span>  { <span style="color:#a6e22e">params</span> }<span style="color:#f92672">:</span> { <span style="color:#a6e22e">params</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">agentId</span>: <span style="color:#66d9ef">string</span> } }
</span></span><span style="display:flex;"><span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">agent</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">mastra</span>.<span style="color:#a6e22e">getAgent</span>(<span style="color:#a6e22e">params</span>.<span style="color:#a6e22e">agentId</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">message</span> } <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">req</span>.<span style="color:#a6e22e">json</span>();
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">agent</span>.<span style="color:#a6e22e">generate</span>(<span style="color:#a6e22e">message</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">Response</span>.<span style="color:#a6e22e">json</span>({ <span style="color:#a6e22e">text</span>: <span style="color:#66d9ef">result.text</span> });
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><strong>Express</strong>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#a6e22e">express</span> <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;express&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">mastra</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;./mastra&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">app</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">express</span>();
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">app</span>.<span style="color:#a6e22e">use</span>(<span style="color:#a6e22e">express</span>.<span style="color:#a6e22e">json</span>());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">app</span>.<span style="color:#a6e22e">post</span>(<span style="color:#e6db74">&#34;/api/agents/:agentId/generate&#34;</span>, <span style="color:#66d9ef">async</span> (<span style="color:#a6e22e">req</span>, <span style="color:#a6e22e">res</span>) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">agent</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">mastra</span>.<span style="color:#a6e22e">getAgent</span>(<span style="color:#a6e22e">req</span>.<span style="color:#a6e22e">params</span>.<span style="color:#a6e22e">agentId</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">agent</span>.<span style="color:#a6e22e">generate</span>(<span style="color:#a6e22e">req</span>.<span style="color:#a6e22e">body</span>.<span style="color:#a6e22e">message</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">json</span>({ <span style="color:#a6e22e">text</span>: <span style="color:#66d9ef">result.text</span> });
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">app</span>.<span style="color:#a6e22e">listen</span>(<span style="color:#ae81ff">3000</span>);
</span></span></code></pre></div><p><strong>Hono and SvelteKit</strong> are similarly supported with adapter packages. The pattern is the same: import your Mastra instance, call <code>getAgent</code> or <code>getWorkflow</code>, and handle the request.</p>
<h3 id="mastra-platform-studio--server--memory-gateway">Mastra Platform: Studio + Server + Memory Gateway</h3>
<p>For teams that don&rsquo;t want to manage their own infrastructure, Mastra offers a hosted platform:</p>
<ul>
<li><strong>Mastra Studio (cloud)</strong>: Same dev UI, hosted and shared across your team</li>
<li><strong>Mastra Server (cloud)</strong>: Managed deployment of your agents and workflows</li>
<li><strong>Memory Gateway</strong>: Hosted memory service with persistent storage, semantic recall, and cross-session state</li>
</ul>
<h3 id="pricing-and-tiers">Pricing and tiers</h3>
<table>
  <thead>
      <tr>
          <th>Tier</th>
          <th>Price</th>
          <th>Key Limits</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Starter</td>
          <td>Free</td>
          <td>3 agents, 10k memory records, community support</td>
      </tr>
      <tr>
          <td>Teams</td>
          <td>$250/team/month</td>
          <td>Unlimited agents, 1M memory records, priority support</td>
      </tr>
      <tr>
          <td>Enterprise</td>
          <td>Custom</td>
          <td>Dedicated infra, SLA, SSO, custom integrations</td>
      </tr>
  </tbody>
</table>
<p>The free tier is genuinely usable for prototyping and personal projects. The Teams tier is where production deployments land.</p>
<h2 id="mastra-vs-other-ai-frameworks-typescript-first-comparison">Mastra vs Other AI Frameworks: TypeScript-First Comparison</h2>
<h3 id="mastra-vs-langgraph-vs-crewai">Mastra vs LangGraph vs CrewAI</h3>
<table>
  <thead>
      <tr>
          <th>Feature</th>
          <th>Mastra</th>
          <th>LangGraph</th>
          <th>CrewAI</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Language</td>
          <td>TypeScript</td>
          <td>Python</td>
          <td>Python</td>
      </tr>
      <tr>
          <td>Agent abstraction</td>
          <td><code>Agent</code> class with tools + memory</td>
          <td><code>StateGraph</code> with nodes/edges</td>
          <td><code>Crew</code> with <code>Agent</code> roles</td>
      </tr>
      <tr>
          <td>Workflow model</td>
          <td>Step-based with branch/parallel</td>
          <td>State graph with conditional edges</td>
          <td>Sequential/hierarchical process</td>
      </tr>
      <tr>
          <td>Memory</td>
          <td>Built-in (working + semantic recall)</td>
          <td>Manual (checkpointer interface)</td>
          <td>Short-term + long-term memory</td>
      </tr>
      <tr>
          <td>Observability</td>
          <td>Built-in OTel + Studio</td>
          <td>LangSmith (separate product)</td>
          <td>Manual or LangSmith</td>
      </tr>
      <tr>
          <td>Eval framework</td>
          <td>Built-in</td>
          <td>LangSmith (separate product)</td>
          <td>Not included</td>
      </tr>
      <tr>
          <td>MCP support</td>
          <td>Client + server</td>
          <td>Client (via langchain-mcp)</td>
          <td>Not native</td>
      </tr>
      <tr>
          <td>RAG</td>
          <td>Built-in module</td>
          <td>Manual (LangChain retrieval)</td>
          <td>Manual</td>
      </tr>
      <tr>
          <td>Deployment</td>
          <td>Built-in server + Platform</td>
          <td>Manual or LangServe</td>
          <td>Manual</td>
      </tr>
      <tr>
          <td>License</td>
          <td>Apache 2.0</td>
          <td>MIT</td>
          <td>MIT</td>
      </tr>
  </tbody>
</table>
<p>The core distinction: LangGraph and CrewAI are Python frameworks that require the Python ecosystem for production deployment. If your stack is TypeScript, you&rsquo;ll write a Python service, wrap it in a Docker container, and communicate via HTTP. That works, but it adds operational overhead and prevents you from sharing types, tests, and utilities across your codebase.</p>
<h3 id="mastra-vs-vercel-ai-sdk">Mastra vs Vercel AI SDK</h3>
<p>The Vercel AI SDK focuses on LLM integration at the transport layer: streaming responses, managing tool calls, and providing React hooks for chat UIs. Mastra operates at a higher level:</p>
<ul>
<li><strong>Vercel AI SDK</strong>: &ldquo;How do I call an LLM and stream the response to a React component?&rdquo;</li>
<li><strong>Mastra</strong>: &ldquo;How do I build an agent with memory, tools, and guardrails, evaluate it, and deploy it as an API?&rdquo;</li>
</ul>
<p>They&rsquo;re complementary. Mastra uses the Vercel AI SDK&rsquo;s model interface under the hood for LLM calls. You can use the Vercel AI SDK for your frontend chat UI and Mastra for your backend agent logic.</p>
<h3 id="when-to-choose-mastra-and-when-not-to">When to choose Mastra (and when not to)</h3>
<p>Choose Mastra when:</p>
<ul>
<li>Your backend is TypeScript/Node.js</li>
<li>You need structured agents with memory, tools, and guardrails</li>
<li>You want built-in evals and observability without extra tooling</li>
<li>You&rsquo;re building multi-step workflows with conditional logic</li>
</ul>
<p>Skip Mastra when:</p>
<ul>
<li>Your team is Python-first and you&rsquo;re happy with LangGraph or CrewAI</li>
<li>You only need raw LLM streaming (use Vercel AI SDK directly)</li>
<li>You need capabilities Mastra doesn&rsquo;t support yet (e.g., specialized multimodal agent patterns)</li>
</ul>
<h2 id="real-world-examples-and-case-studies">Real-World Examples and Case Studies</h2>
<h3 id="docker-event-driven-pr-management-agent">Docker: Event-driven PR management agent</h3>
<p>Docker built an event-driven agent system that responds to GitHub webhooks. When a PR is opened, their Mastra workflow:</p>
<ol>
<li>Triggers on the webhook payload</li>
<li>Routes through the Docker MCP Gateway to the GitHub MCP server</li>
<li>Runs the analyze PR agent on the diff</li>
<li>Passes the analysis to the generate comment agent</li>
<li>Posts the comment via the post and close agent</li>
</ol>
<p>This is not a chatbot. There&rsquo;s no human in the loop. The entire pipeline runs in response to an event, with no user interaction. That&rsquo;s a different deployment model from most demo agents, and it&rsquo;s where Mastra&rsquo;s workflow primitives matter—the pipeline is deterministic, observable, and can fail gracefully at any step.</p>
<h3 id="elastic-agentic-rag-with-elasticsearch">Elastic: Agentic RAG with Elasticsearch</h3>
<p>Elastic built a RAG assistant combining a Vite + React frontend, a Mastra backend, and Elasticsearch as the vector store. Their writeup highlights the developer experience of staying in a single language stack: the same TypeScript types that define the search index schema also define the agent&rsquo;s tool interface and the frontend&rsquo;s API contract.</p>
<h3 id="softbank-enterprise-productivity-at-scale">SoftBank: Enterprise productivity at scale</h3>
<p>SoftBank deployed Mastra-based agents internally for productivity tools. Scale is the notable aspect—this isn&rsquo;t a prototype serving 10 users. Mastra&rsquo;s memory gateway and observability infrastructure handle the traffic.</p>
<h3 id="replit-agent-3-building-mastra-agents">Replit: Agent 3 building Mastra agents</h3>
<p>Replit&rsquo;s Agent 3 can scaffold and deploy Mastra agents. This meta-pattern—an AI agent building other AI agents—validates Mastra&rsquo;s code-first API design. If an LLM can generate valid Mastra code from a description, the abstractions are well-defined enough to be machine-writable.</p>
<h2 id="conclusion-and-next-steps">Conclusion and Next Steps</h2>
<p>Mastra addresses a real gap in the AI agent tooling ecosystem: a production-grade, TypeScript-native framework with first-class support for the primitives that matter—agents, tools, workflows, RAG, evals, and observability. The enterprise adoption numbers (100k+ daily users at Marsh McLennan, Brex&rsquo;s $5.1B acquisition involvement) confirm that it&rsquo;s not just developer-friendly but production-ready.</p>
<h3 id="key-takeaways">Key takeaways</h3>
<ol>
<li><strong>TypeScript-first is now a viable choice for AI agents.</strong> With 60–70% of YC X25 agent startups choosing TypeScript, the ecosystem demand is clear. Mastra provides the framework primitives that Python alternatives have had for years.</li>
<li><strong>MCP integration is a differentiator.</strong> Mastra&rsquo;s ability to connect to any MCP server as a tool source gives agents access to external systems without custom integration code. Docker&rsquo;s PR automation demonstrates this in production.</li>
<li><strong>Built-in evals and observability are not optional extras.</strong> If you can&rsquo;t measure agent quality, you can&rsquo;t improve it. Mastra&rsquo;s eval framework and OpenTelemetry integration give you measurement from day one.</li>
<li><strong>Workflows complement agents.</strong> Not every problem needs an LLM deciding what to do next. Mastra&rsquo;s workflow engine handles the structured part of your pipeline while agents handle the reasoning part.</li>
<li><strong>The Gatsby team&rsquo;s framework experience shows.</strong> The DX decisions—Zod schemas, code-first configuration, Studio as a dev tool—reflect experience shipping a framework used by tens of thousands of developers.</li>
</ol>
<h3 id="resources">Resources</h3>
<ul>
<li><strong>Mastra docs</strong>: <a href="https://mastra.ai/docs">mastra.ai/docs</a></li>
<li><strong>Mastra GitHub</strong>: <a href="https://github.com/mastra-ai/mastra">github.com/mastra-ai/mastra</a></li>
<li><strong>Mastra templates</strong>: <a href="https://mastra.ai/templates">mastra.ai/templates</a></li>
<li><strong>Agent Book</strong>: <a href="https://mastra.ai/agentbook">mastra.ai/agentbook</a> — community-contributed agent examples</li>
<li><strong>Community Discord</strong>: <a href="https://mastra.ai/community">mastra.ai/community</a></li>
</ul>
<h3 id="the-future-of-typescript-ai-development">The future of TypeScript AI development</h3>
<p>The trajectory is clear. As agent deployments move from demos to production, the operational requirements—evals, observability, guardrails, memory management, workflow orchestration—become the differentiating factors. Mastra builds these into the framework rather than leaving them as integration exercises. For TypeScript teams building AI agents in 2026, Mastra is the framework that matches the language, runtime, and operational demands of the job.</p>
]]></content:encoded></item></channel></rss>