mastra
Use this skill when working with Mastra - the TypeScript AI framework for building agents, workflows, tools, and AI-powered applications. Triggers on creating agents, defining workflows, configuring memory, RAG pipelines, MCP client/server setup, voice integration, evals/scorers, deployment, and Mastra CLI commands. Also triggers on "mastra dev", "mastra build", "mastra init", Mastra Studio, or any Mastra package imports.
ai-ml ai-agentstypescriptworkflowsragmcpllmWhat is mastra?
Use this skill when working with Mastra - the TypeScript AI framework for building agents, workflows, tools, and AI-powered applications. Triggers on creating agents, defining workflows, configuring memory, RAG pipelines, MCP client/server setup, voice integration, evals/scorers, deployment, and Mastra CLI commands. Also triggers on "mastra dev", "mastra build", "mastra init", Mastra Studio, or any Mastra package imports.
mastra
mastra is a production-ready AI agent skill for claude-code, gemini-cli, openai-codex, and 1 more. Working with Mastra - the TypeScript AI framework for building agents, workflows, tools, and AI-powered applications.
Quick Facts
| Field | Value |
|---|---|
| Category | ai-ml |
| Version | 0.1.0 |
| Platforms | claude-code, gemini-cli, openai-codex, mcp |
| License | MIT |
How to Install
- Make sure you have Node.js installed on your machine.
- Run the following command in your terminal:
npx skills add AbsolutelySkilled/AbsolutelySkilled --skill mastra- The mastra skill is now available in your AI coding agent (Claude Code, Gemini CLI, OpenAI Codex, etc.).
Overview
Mastra is a TypeScript framework for building AI-powered applications. It provides
a unified Mastra() constructor that wires together agents, workflows, tools,
memory, RAG, MCP, voice, evals, and observability. Projects scaffold via
npm create mastra@latest and run with mastra dev (dev server + Studio UI at
localhost:4111). Built on Hono, deployable to Node.js 22+, Bun, Deno, Cloudflare,
Vercel, Netlify, AWS, and Azure.
Tags
ai-agents typescript workflows rag mcp llm
Platforms
- claude-code
- gemini-cli
- openai-codex
- mcp
Related Skills
Pair mastra with these complementary skills:
Frequently Asked Questions
What is mastra?
Use this skill when working with Mastra - the TypeScript AI framework for building agents, workflows, tools, and AI-powered applications. Triggers on creating agents, defining workflows, configuring memory, RAG pipelines, MCP client/server setup, voice integration, evals/scorers, deployment, and Mastra CLI commands. Also triggers on "mastra dev", "mastra build", "mastra init", Mastra Studio, or any Mastra package imports.
How do I install mastra?
Run npx skills add AbsolutelySkilled/AbsolutelySkilled --skill mastra in your terminal. The skill will be immediately available in your AI coding agent.
What AI agents support mastra?
This skill works with claude-code, gemini-cli, openai-codex, mcp. Install it once and use it across any supported AI coding agent.
Maintainers
Generated from AbsolutelySkilled
SKILL.md
Mastra
Mastra is a TypeScript framework for building AI-powered applications. It provides
a unified Mastra() constructor that wires together agents, workflows, tools,
memory, RAG, MCP, voice, evals, and observability. Projects scaffold via
npm create mastra@latest and run with mastra dev (dev server + Studio UI at
localhost:4111). Built on Hono, deployable to Node.js 22+, Bun, Deno, Cloudflare,
Vercel, Netlify, AWS, and Azure.
When to use this skill
Trigger this skill when the user:
- Creates or configures a Mastra agent with tools, memory, or structured output
- Defines workflows with steps, branching, loops, or parallel execution
- Creates custom tools with
createTooland Zod schemas - Sets up memory (message history, working memory, semantic recall)
- Builds RAG pipelines (chunking, embeddings, vector stores)
- Configures MCP clients to connect to external tool servers
- Exposes Mastra agents/tools as an MCP server
- Runs Mastra CLI commands (
mastra dev,mastra build,mastra init) - Deploys a Mastra application to any cloud provider
Do NOT trigger this skill for:
- General TypeScript/Node.js questions unrelated to Mastra
- Other AI frameworks (LangChain, CrewAI, AutoGen) unless comparing to Mastra
Setup & authentication
Environment variables
# Required - at least one LLM provider
OPENAI_API_KEY=sk-...
# Or: ANTHROPIC_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, OPENROUTER_API_KEY
# Optional
POSTGRES_CONNECTION_STRING=postgresql://... # for pgvector RAG/memory
PINECONE_API_KEY=... # for Pinecone vector storeInstallation
# New project
npm create mastra@latest
# Existing project
npx mastra init --components agents,tools,workflows --llm openaiBasic initialization
import { Mastra } from '@mastra/core'
import { Agent } from '@mastra/core/agent'
import { createTool } from '@mastra/core/tool'
import { z } from 'zod'
const myAgent = new Agent({
id: 'my-agent',
instructions: 'You are a helpful assistant.',
model: 'openai/gpt-4.1',
tools: {},
})
export const mastra = new Mastra({
agents: { myAgent },
})Always access agents via
mastra.getAgent('myAgent')- not direct imports. Direct imports bypass logger, telemetry, and registered resources.
Core concepts
Mastra instance - the central registry. Pass agents, workflows, tools, memory,
MCP servers, and config to the new Mastra({}) constructor. Everything registered
here gets wired together (logging, telemetry, resource access).
Agents - LLM-powered entities created with new Agent({}). They take
instructions, a model string (e.g. 'openai/gpt-4.1'), and optional tools.
Call agent.generate() for complete responses or agent.stream() for streaming.
Both accept maxSteps (default 5) to cap tool-use loops.
Workflows - typed multi-step pipelines built with createWorkflow() and
createStep(). Steps have Zod inputSchema/outputSchema. Chain with .then(),
branch with .branch(), loop with .dountil()/.dowhile(), parallelize with
.parallel(), iterate with .foreach(). Always call .commit() at the end.
Tools - typed functions via createTool({ id, description, inputSchema, outputSchema, execute }). The description field guides the LLM's tool selection.
Memory - four types: message history (recent messages), working memory
(persistent user profile), observational memory (background summarization), and
semantic recall (RAG over past conversations). Configure via new Memory({}).
MCP - MCPClient connects to external tool servers; MCPServer exposes
Mastra tools/agents as an MCP endpoint. Use listTools() for static single-user
setups, listToolsets() for dynamic multi-user scenarios.
Common tasks
Create an agent with tools
import { Agent } from '@mastra/core/agent'
import { createTool } from '@mastra/core/tool'
import { z } from 'zod'
const weatherTool = createTool({
id: 'get-weather',
description: 'Fetches current weather for a city',
inputSchema: z.object({ city: z.string() }),
outputSchema: z.object({ temp: z.number(), condition: z.string() }),
execute: async ({ city }) => {
const res = await fetch(`https://wttr.in/${city}?format=j1`)
const data = await res.json()
return { temp: Number(data.current_condition[0].temp_F), condition: data.current_condition[0].weatherDesc[0].value }
},
})
const agent = new Agent({
id: 'weather-agent',
instructions: 'Help users check weather. Use the get-weather tool.',
model: 'openai/gpt-4.1',
tools: { [weatherTool.id]: weatherTool },
})Stream agent responses
const stream = await agent.stream('What is the weather in Tokyo?')
for await (const chunk of stream.textStream) {
process.stdout.write(chunk)
}Define a workflow with steps
import { createWorkflow, createStep } from '@mastra/core/workflow'
import { z } from 'zod'
const summarize = createStep({
id: 'summarize',
inputSchema: z.object({ text: z.string() }),
outputSchema: z.object({ summary: z.string() }),
execute: async ({ inputData, mastra }) => {
const agent = mastra.getAgent('summarizer')
const res = await agent.generate(`Summarize: ${inputData.text}`)
return { summary: res.text }
},
})
const workflow = createWorkflow({
id: 'summarize-workflow',
inputSchema: z.object({ text: z.string() }),
outputSchema: z.object({ summary: z.string() }),
}).then(summarize).commit() // .commit() is required!
const run = workflow.createRun()
const result = await run.start({ inputData: { text: 'Long article...' } })
if (result.status === 'success') console.log(result.result)Always check
result.statusbefore accessingresult.resultorresult.error. Possible statuses:success,failed,suspended,tripwire,paused.
Configure agent memory
import { Memory } from '@mastra/memory'
import { LibSQLStore, LibSQLVector } from '@mastra/libsql'
const memory = new Memory({
storage: new LibSQLStore({ id: 'mem', url: 'file:./local.db' }),
vector: new LibSQLVector({ id: 'vec', url: 'file:./local.db' }),
options: {
lastMessages: 20,
semanticRecall: { topK: 3, messageRange: 2 },
workingMemory: { enabled: true, template: '# User\n- Name:\n- Preferences:' },
},
})
const agent = new Agent({ id: 'mem-agent', model: 'openai/gpt-4.1', memory })
// Use with thread context
await agent.generate('Remember my name is Alice', {
memory: { thread: { id: 'thread-1' }, resource: 'user-123' },
})Connect to MCP servers
import { MCPClient } from '@mastra/mcp'
const mcp = new MCPClient({
id: 'my-mcp',
servers: {
github: { command: 'npx', args: ['-y', '@modelcontextprotocol/server-github'] },
custom: { url: new URL('https://my-mcp-server.com/sse') },
},
})
const agent = new Agent({
id: 'mcp-agent',
model: 'openai/gpt-4.1',
tools: await mcp.listTools(), // static - fixed at init
})
// For multi-user (dynamic credentials per request):
const res = await agent.generate(prompt, {
toolsets: await mcp.listToolsets(),
})
await mcp.disconnect()Run CLI commands
mastra dev # Dev server + Studio at localhost:4111
mastra build # Bundle to .mastra/output/
mastra build --studio # Include Studio UI in build
mastra start # Serve production build
mastra lint # Validate project structure
mastra migrate # Run DB migrationsError handling
| Error | Cause | Resolution |
|---|---|---|
| Schema mismatch between steps | Step outputSchema doesn't match next step's inputSchema | Use .map() between steps to transform data |
| Workflow not committed | Forgot .commit() after chaining steps |
Add .commit() as the final call on the workflow chain |
maxSteps exceeded |
Agent loops through tools beyond limit (default 5) | Increase maxSteps or improve tool descriptions to reduce loops |
| Memory scope mismatch | Using resource-scoped memory but not passing resource in generate |
Always pass memory: { thread, resource } when using resource-scoped memory |
| MCP resource leak | Dynamic listToolsets() without disconnect() |
Always call mcp.disconnect() after multi-user requests |
Gotchas
Forgetting
.commit()causes a silent no-op workflow - A workflow chain that is missing.commit()at the end will not throw an error when defined, but callingworkflow.createRun()will either fail or produce unexpected behavior. Always end every workflow chain with.commit()as the final call.Accessing agents directly (not via
mastra.getAgent()) bypasses telemetry and logging - Importing and calling an agent instance directly skips the Mastra registry's wiring, meaning no trace data, no logger output, and no resource access via the registered Mastra instance. Always resolve agents throughmastra.getAgent('id')in step execute functions.mcp.listTools()caches tools at initialization time - If the MCP server's available tools change afterMCPClientinitializes, the agent will not see the new tools until the process restarts. For dynamic multi-user scenarios where credentials or available tools differ per request, usemcp.listToolsets()per request instead of the staticlistTools()pattern.Memory
resourcescope isolation can cause cross-user data leakage if resource IDs are not unique - If two users share the sameresourceID (e.g., a static string like"default"), their working memory and semantic recall overlap. Always derive the resource ID from a unique identifier (user ID, session token) before passing it toagent.generate().Workflow step schema mismatches produce cryptic runtime errors - When a step's
outputSchemadoes not match the next step'sinputSchema, Mastra throws a Zod parse error at runtime, not at workflow definition time. Use.map()between steps to transform data shapes, and verify schema compatibility during development by running the workflow with a test payload before deploying.
References
For detailed content on specific Mastra sub-domains, read the relevant file
from the references/ folder:
references/workflows-advanced.md- branching, loops, parallel, foreach, suspend/resume, state managementreferences/memory-and-rag.md- full memory config, working memory schemas, RAG pipeline, vector stores, semantic recallreferences/mcp-and-voice.md- MCP client/server patterns, voice providers, CompositeVoice, realtime audioreferences/deployment-and-server.md- server config, middleware, auth, CLI reference, deployment targets, evals/observability
Only load a references file if the current task requires it - they are long and will consume context.
References
deployment-and-server.md
Deployment, Server, Evals, and Observability
CLI reference
| Command | Description |
|---|---|
mastra dev |
Dev server + Studio at localhost:4111 |
mastra dev --https |
Dev with local HTTPS (auto-generates cert) |
mastra dev --inspect |
Dev with Node.js debugger |
mastra build |
Bundle to .mastra/output/ |
mastra build --studio |
Include Studio UI in build |
mastra start |
Serve production build (OTEL tracing enabled) |
mastra start --dir <path> |
Custom build output directory |
mastra init |
Initialize Mastra in existing project |
mastra lint |
Validate project structure |
mastra migrate |
Run DB migrations |
mastra studio |
Standalone Studio server on port 3000 |
mastra scorers add <name> |
Add a scorer template |
mastra scorers list |
List available scorer templates |
Common flags
--dir- Mastra folder path (default:src/mastra)--debug- Verbose logging--env- Custom environment file path--root- Root folder path--tools- Tool paths (default:src/mastra/tools)
Server configuration
The full server key inside new Mastra({}):
export const mastra = new Mastra({
server: {
port: 4111,
host: 'localhost',
timeout: 180000, // ms
bodySizeLimit: 4718592, // bytes (~4.5MB)
cors: { origin: '*' }, // or false to disable
https: { key: Buffer, cert: Buffer },
studioBase: '/',
// Auth
auth: jwtAuthProvider | clerkProvider | supabaseProvider,
// Middleware (Hono-compatible)
middleware: [
{
path: '/api/*',
handler: async (c, next) => {
// return Response to halt, call next() to continue
await next()
},
},
],
// Custom routes
apiRoutes: [
{
path: '/api/custom',
method: 'GET',
handler: async (c) => c.json({ ok: true }),
},
],
// Error handlers
onError: (err, c) => c.json({ error: err.message }, 500),
onValidationError: (error, context) => ({
status: 400,
body: { errors: error.issues },
}),
// Build options
build: {
swaggerUI: false,
apiReqLogs: false,
openAPIDocs: false,
},
},
})Built-in endpoints
| Endpoint | Description |
|---|---|
GET /health |
Health check |
GET /api/openapi.json |
OpenAPI spec |
GET /swagger-ui |
Interactive API docs (requires build.swaggerUI: true) |
Auth providers
JWT, Clerk, Supabase, Firebase, Auth0, WorkOS.
JWT requires MASTRA_JWT_SECRET env var.
Security
Stream data redaction is enabled by default - strips system prompts, tool definitions, and API keys from streamed responses.
Deployment targets
| Target | Notes |
|---|---|
| Mastra Server | mastra build && mastra start - standalone Hono server |
| Vercel | Deploy via Vercel CLI or git integration |
| Netlify | Standard Netlify deployment |
| Cloudflare Workers | Edge runtime support |
| AWS EC2 | Standard Node.js deployment |
| AWS Lambda | Serverless functions |
| Azure App Services | Azure deployment |
| DigitalOcean | App Platform or Droplets |
| Monorepo | Deploy alongside other services |
| Mastra Cloud | Managed hosting (beta) |
| Next.js / Astro | Embed in web framework |
| Inngest | Workflow runner with step memoization, retries, monitoring |
Runtime requirements
- Node.js v22.13.0+
- Bun, Deno, and Cloudflare Workers also supported
Evals and scorers
Package
npm install @mastra/evals@latestBuilt-in scorers
import {
createAnswerRelevancyScorer,
createToxicityScorer,
} from '@mastra/evals/scorers/prebuilt'Categories: Textual, Classification, Prompt Engineering.
Agent-level scorers
const agent = new Agent({
id: 'eval-agent',
model: 'openai/gpt-4.1',
scorers: {
relevancy: {
scorer: createAnswerRelevancyScorer({ model: 'openai/gpt-4.1-nano' }),
sampling: { type: 'ratio', rate: 0.5 },
},
safety: {
scorer: createToxicityScorer({ model: 'openai/gpt-4.1-nano' }),
sampling: { type: 'ratio', rate: 1 },
},
},
})Mastra-level scorers (trace evaluation)
const mastra = new Mastra({
scorers: {
answerRelevancy: myScorer,
responseQuality: myOtherScorer,
},
})Behavior
- Execution is asynchronous - does not block agent responses
sampling.rate: 0 to 1 (1.0 = every response, 0.5 = 50%)- Results auto-persist to
mastra_scorersdatabase table - Results visible in Studio UI
Observability
Packages
npm install @mastra/observability
npm install @mastra/loggers # includes PinoLoggerConfiguration
import { PinoLogger } from '@mastra/loggers'
import {
Observability,
DefaultExporter,
CloudExporter,
SensitiveDataFilter,
} from '@mastra/observability'
export const mastra = new Mastra({
logger: new PinoLogger(),
observability: new Observability({
configs: {
default: {
serviceName: 'my-app',
exporters: [
new DefaultExporter(), // persists to storage
new CloudExporter(), // sends to Mastra Cloud
],
spanOutputProcessors: [
new SensitiveDataFilter(), // redacts passwords, tokens, keys
],
},
},
}),
})What is traced
- Model calls (tokens, latency, prompts, completions)
- Agent execution paths, tool calls, memory operations
- Workflow steps, branching, parallel execution
External integrations
MLflow, Langfuse, Braintrust, and any OpenTelemetry-compatible platform (Datadog, New Relic, SigNoz).
Required env vars
MASTRA_CLOUD_ACCESS_TOKEN- for CloudExporter
Environment variables reference
| Variable | Purpose |
|---|---|
OPENAI_API_KEY |
OpenAI models and voice |
ANTHROPIC_API_KEY |
Anthropic models |
GOOGLE_GENERATIVE_AI_API_KEY |
Google/Gemini models |
OPENROUTER_API_KEY |
OpenRouter models |
POSTGRES_CONNECTION_STRING |
PostgreSQL / pgvector |
MASTRA_CLOUD_ACCESS_TOKEN |
Mastra Cloud / CloudExporter |
MASTRA_JWT_SECRET |
JWT authentication |
PORT |
Override server port (default 4111) |
MASTRA_SKIP_DOTENV |
Skip .env loading |
MASTRA_DEV_NO_CACHE=1 |
Force full rebuild |
MASTRA_CONCURRENCY=N |
Limit parallel operations |
MASTRA_TELEMETRY_DISABLED=1 |
Opt out of analytics |
MASTRA_SKIP_PEERDEP_CHECK=1 |
Skip peer dep checks |
TypeScript requirements
{
"compilerOptions": {
"module": "ES2022",
"moduleResolution": "bundler",
"target": "ES2022"
}
}CommonJS and legacy node module resolution are NOT supported.
mcp-and-voice.md
MCP and Voice
MCPClient - connecting to external tool servers
import { MCPClient } from '@mastra/mcp'
const mcp = new MCPClient({
id: 'my-mcp-client',
servers: {
// Local stdio transport
github: {
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-github'],
},
// Remote HTTP/SSE transport
custom: {
url: new URL('https://my-mcp-server.com/sse'),
requestInit: {
headers: { Authorization: 'Bearer token' },
},
},
},
})Static tools (single-user, CLI tools)
Tools are resolved once at agent initialization and don't change.
const agent = new Agent({
id: 'github-agent',
model: 'openai/gpt-4.1',
tools: await mcp.listTools(),
})Dynamic toolsets (multi-user SaaS)
Tools are resolved per-request with user-specific credentials.
const response = await agent.generate(userPrompt, {
toolsets: await userMcp.listToolsets(),
})
await userMcp.disconnect() // always disconnect after dynamic useStatic tools (
listTools()) are fixed at init - credential changes don't propagate. UselistToolsets()for multi-user scenarios.
MCPServer - exposing Mastra as MCP
import { MCPServer } from '@mastra/mcp'
const mcpServer = new MCPServer({
id: 'my-mcp-server',
name: 'My Tools Server',
version: '1.0.0',
agents: { myAgent },
tools: { myTool },
workflows: { myWorkflow },
})
// Register with Mastra
const mastra = new Mastra({
mcpServers: { mcpServer },
})MCP registry integrations
| Registry | Transport | Notes |
|---|---|---|
| Smithery.ai | stdio | CLI via @smithery/cli@latest |
| Klavis AI | Hosted | Enterprise-authenticated, uses instance IDs |
| mcp.run | SSE | Pre-authenticated; SSE URLs are credentials - store in env vars |
| Composio.dev | SSE | Single-user tied; not for multi-tenant |
| Ampersand | SSE or stdio | 150+ SaaS integrations |
mcp.run SSE URLs are sensitive - never hardcode them, always use env vars. Composio URLs are single-user and unsuitable for multi-tenant systems.
Voice providers
Package installation
npm install @mastra/voice-openai # OpenAI TTS/STT
npm install @mastra/voice-elevenlabs # ElevenLabs synthesis
npm install @mastra/voice-google # Google Cloud Speech
npm install @mastra/voice-deepgram # Deepgram transcription
npm install @mastra/voice-azure # Microsoft Azure Speech
npm install @mastra/voice-playai # PlayAI synthesis
npm install @mastra/voice-cloudflare # Cloudflare Workers AI
npm install @mastra/voice-speechify # Speechify TTS
npm install @mastra/voice-sarvam # Sarvam AI (multilingual)
npm install @mastra/voice-murf # Murf Studio
# Realtime (speech-to-speech)
npm install @mastra/voice-openai-realtime
npm install @mastra/voice-google-gemini-live
# Audio utilities
npm install @mastra/node-audioEnvironment variables
| Provider | Required env var |
|---|---|
| OpenAI | OPENAI_API_KEY |
| Azure | AZURE_SPEECH_KEY, AZURE_SPEECH_REGION |
| ElevenLabs | ELEVENLABS_API_KEY |
GOOGLE_API_KEY |
|
| Deepgram | DEEPGRAM_API_KEY |
| PlayAI | PLAYAI_API_KEY |
| Cloudflare | CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN |
Voice API
Text-to-speech (TTS)
const audio = await agent.voice.speak('Hello, world!', { speaker: 'alloy' })Speech-to-text (STT)
const transcript = await agent.voice.listen(audioStream)Speech-to-speech (realtime)
await agent.voice.connect()
agent.voice.send(microphoneStream)
agent.voice.on('speaker', ({ audio }) => playAudio(audio))
agent.voice.on('writing', ({ text, role }) => console.log(text))Agent with voice
import { OpenAIVoice } from '@mastra/voice-openai'
const agent = new Agent({
id: 'voice-agent',
model: 'openai/gpt-4.1',
instructions: 'You are a voice assistant.',
voice: new OpenAIVoice({
speechModel: { name: 'tts-1' },
listeningModel: { name: 'whisper-1' },
speaker: 'alloy',
}),
})CompositeVoice - mix providers
Use one provider for STT and another for TTS.
import { CompositeVoice } from '@mastra/core'
import { OpenAIVoice } from '@mastra/voice-openai'
import { ElevenLabsVoice } from '@mastra/voice-elevenlabs'
const openai = new OpenAIVoice()
const elevenlabs = new ElevenLabsVoice()
const voice = new CompositeVoice({
input: openai.transcription('whisper-1'), // STT via OpenAI
output: elevenlabs.speech('eleven_turbo_v2'), // TTS via ElevenLabs
})Audio utilities
import { playAudio, getMicrophoneStream } from '@mastra/node-audio'
const mic = await getMicrophoneStream()
const audio = await agent.voice.speak('Hello')
await playAudio(audio)Semantic recall (vector queries) runs before each LLM call. For real-time voice applications, this adds noticeable latency. Consider disabling semantic recall for voice-first agents or using message history only.
memory-and-rag.md
Memory and RAG
Memory types
| Type | Purpose | Persistence |
|---|---|---|
| Message history | Recent N messages in context window | Per thread |
| Working memory | Structured user profile (Markdown or JSON schema) | Per resource or thread |
| Observational | Background Observer/Reflector agents summarizing conversations | Per thread |
| Semantic recall | RAG over past messages via vector similarity search | Cross-thread (resource scope) or per-thread |
Full Memory configuration
import { Memory } from '@mastra/memory'
import { LibSQLStore, LibSQLVector } from '@mastra/libsql'
import { ModelRouterEmbeddingModel } from '@mastra/core/llm'
const memory = new Memory({
storage: new LibSQLStore({ id: 'storage', url: 'file:./local.db' }),
vector: new LibSQLVector({ id: 'vector', url: 'file:./local.db' }),
embedder: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
options: {
lastMessages: 20,
semanticRecall: {
topK: 5,
messageRange: 2,
scope: 'resource', // 'resource' = cross-thread, or thread-specific
},
workingMemory: {
enabled: true,
scope: 'resource', // 'resource' (default) or 'thread'
template: `# User Profile\n- **Name**:\n- **Location**:\n- **Preferences**:`,
},
},
})Attach memory to agent
const agent = new Agent({
id: 'memory-agent',
model: 'openai/gpt-4.1',
instructions: 'You remember user preferences across conversations.',
memory,
})Call agent with thread context
await agent.generate('My name is Alice and I prefer dark mode', {
memory: {
thread: { id: 'thread-abc', title: 'Onboarding' },
resource: 'user-456',
options: { readOnly: false },
},
})Forgetting the
resourceparameter when using resource-scoped memory will break cross-thread recall silently.
Working memory - schema-based
For structured data, use a Zod schema instead of a Markdown template. Schema and template are mutually exclusive.
const memory = new Memory({
options: {
workingMemory: {
enabled: true,
schema: z.object({
name: z.string().optional(),
timezone: z.string().optional(),
preferences: z.object({
theme: z.string().optional(),
language: z.string().optional(),
}).optional(),
}),
},
},
})Schema arrays replace entirely on update (no element-level merge). Set a field to
nullto delete it.
Thread management
// Create thread with initial working memory
const thread = await memory.createThread({
threadId: 'thread-123',
resourceId: 'user-456',
title: 'Support Chat',
metadata: { workingMemory: '# User\n- Name: Alice' },
})
// Update working memory directly
await memory.updateWorkingMemory({
threadId: 'thread-123',
resourceId: 'user-456',
workingMemory: '# User\n- Name: Alice\n- Plan: Enterprise',
})Semantic recall query
const mem = await agent.getMemory()
const { messages } = await mem.recall({
threadId: 'thread-123',
vectorSearchString: 'What did we discuss about the deadline?',
threadConfig: { semanticRecall: true },
perPage: 50,
})Storage backends
| Package | Backend | Notes |
|---|---|---|
@mastra/libsql |
LibSQL / Turso | Good for dev, file-based option |
@mastra/pg |
PostgreSQL | Production recommended |
@mastra/upstash |
Upstash Redis | Serverless-friendly |
@mastra/mongodb |
MongoDB | Document store |
File-based storage (
file:./mastra.db) is incompatible with serverless. Use PostgreSQL or ClickHouse for production high-traffic workloads.
Vector stores
Supported for both memory semantic recall and RAG:
Astra, Chroma, Cloudflare Vectorize, Convex, Couchbase, DuckDB, Elasticsearch, LanceDB, LibSQL, MongoDB, OpenSearch, Pinecone, PostgreSQL (pgvector), Qdrant, S3 Vectors, Turbopuffer, Upstash.
PostgreSQL HNSW index optimization
import { PgVector } from '@mastra/pg'
const memory = new Memory({
vector: new PgVector({ id: 'vec', connectionString: process.env.DATABASE_URL }),
options: {
semanticRecall: {
topK: 5,
messageRange: 2,
indexConfig: {
type: 'hnsw',
metric: 'dotproduct', // recommended for OpenAI embeddings
m: 16,
efConstruction: 64,
},
},
},
})RAG pipeline
Process documents into searchable embeddings.
1. Chunk documents
import { MDocument } from '@mastra/core/document'
const doc = MDocument.fromText(longText)
const chunks = await doc.chunk({
strategy: 'recursive', // or 'sliding window'
size: 512,
overlap: 50,
})2. Generate embeddings
import { ModelRouterEmbeddingModel } from '@mastra/core/llm'
const embedder = new ModelRouterEmbeddingModel('openai/text-embedding-3-small')
const embeddings = await embedder.embedMany(chunks.map(c => c.text))3. Store in vector database
import { PgVector } from '@mastra/pg'
const vectorStore = new PgVector({
id: 'rag-store',
connectionString: process.env.POSTGRES_CONNECTION_STRING,
})
await vectorStore.upsert({
indexName: 'my-docs',
vectors: embeddings.map((vec, i) => ({
id: `chunk-${i}`,
values: vec,
metadata: { text: chunks[i].text, source: 'docs' },
})),
})4. Query at runtime
const queryEmbedding = await embedder.embed('How do I configure auth?')
const results = await vectorStore.query({
indexName: 'my-docs',
queryVector: queryEmbedding,
topK: 5,
})Security note
The memory system has NO built-in access control. Always verify user authorization
in your application logic before querying any resourceId. Never expose raw
memory APIs to untrusted clients.
workflows-advanced.md
Workflows - Advanced Patterns
Control flow methods
All control flow methods are chained on the workflow builder before .commit().
Sequential
workflow.then(step1).then(step2).then(step3).commit()Parallel
Runs steps concurrently. Waits for all to complete. Output is keyed by step ID.
workflow
.then(fetchData)
.parallel([analyzeText, analyzeImages, analyzeMeta])
.then(combineResults)
.commit()If any step in a parallel block throws, the entire block fails. Use try/catch inside step
executefunctions and return typed success/failure indicators.
Branch
Only the first matching branch executes. All branches must share the same input and output schema.
workflow
.then(classify)
.branch([
[({ inputData }) => inputData.type === 'email', handleEmail],
[({ inputData }) => inputData.type === 'sms', handleSms],
[() => true, handleDefault], // fallback
])
.then(notify)
.commit()Do-until loop
Repeats a step until the condition returns true.
workflow
.dountil(pollStatus, ({ inputData }) => inputData.status === 'complete')
.then(processResult)
.commit()Use
iterationCountlimit to prevent infinite loops in production.
Do-while loop
Repeats while condition is true.
workflow
.dowhile(refineOutput, ({ inputData }) => inputData.score < 0.9)
.commit()Foreach
Iterates over an array, running a step per element. Default concurrency is 1.
workflow
.then(getItems)
.foreach(processItem, { concurrency: 5 })
.then(aggregateResults)
.commit()Chained
.foreach().foreach()creates nested arrays. Use.map()with.flat()or nested workflows to flatten.
Map (data transformation)
Transform data between steps when schemas don't align.
workflow
.then(fetchUser)
.map(({ inputData }) => ({ name: inputData.firstName + ' ' + inputData.lastName }))
.then(greetUser)
.commit()State management
Shared state persists across all steps and survives suspend/resume cycles.
const step = createStep({
id: 'counter',
inputSchema: z.object({ value: z.number() }),
outputSchema: z.object({ value: z.number() }),
stateSchema: z.object({ count: z.number() }),
execute: async ({ inputData, state, setState }) => {
const newCount = (state.count || 0) + 1
setState({ ...state, count: newCount })
return { value: inputData.value * newCount }
},
})State is defined per-step via stateSchema but shared across all steps in the
workflow. Each step sees the same state object.
Suspend and resume
Workflows can suspend mid-execution (e.g. waiting for human approval) and resume later with new input.
const approvalStep = createStep({
id: 'wait-for-approval',
inputSchema: z.object({ proposal: z.string() }),
outputSchema: z.object({ approved: z.boolean() }),
execute: async ({ inputData, suspend }) => {
// Suspend and wait for external input
const resumeData = await suspend({ proposal: inputData.proposal })
return { approved: resumeData.approved }
},
})
// Start workflow - will suspend at approval step
const run = workflow.createRun()
const result = await run.start({ inputData: { proposal: 'Buy 100 widgets' } })
if (result.status === 'suspended') {
// Later, resume with approval decision
const resumed = await run.resume({
step: 'wait-for-approval',
data: { approved: true },
})
}RequestContext in steps
Access per-request context (e.g. user tier, auth tokens) inside steps.
const step = createStep({
id: 'tiered-step',
inputSchema: z.object({ query: z.string() }),
outputSchema: z.object({ results: z.array(z.string()) }),
execute: async ({ inputData, requestContext }) => {
const tier = requestContext.get('user-tier')
const limit = tier === 'enterprise' ? 1000 : 50
return { results: await search(inputData.query, limit) }
},
})Nested and cloned workflows
Nested workflows
Use a workflow as a step inside another workflow.
const parentWorkflow = createWorkflow({ id: 'parent', ... })
.then(step1)
.then(childWorkflow) // child runs as a step
.then(step2)
.commit()Cloning
Create a copy of a workflow with a new ID.
import { cloneWorkflow } from '@mastra/core/workflow'
const clone = cloneWorkflow(parentWorkflow, { id: 'cloned-parent' })Active run management
// List all active (in-progress or suspended) runs
const activeRuns = await workflow.listActiveWorkflowRuns()
// Restart a single run from its last active step
await run.restart()
// Restart all active runs
await workflow.restartAllActiveWorkflowRuns()Result status discrimination
Always check status before accessing result properties.
const result = await run.start({ inputData })
switch (result.status) {
case 'success':
console.log(result.result) // typed output
break
case 'failed':
console.error(result.error) // Error object
break
case 'suspended':
console.log(result.suspendPayload, result.suspended)
break
case 'tripwire':
console.log(result.tripwire) // limit exceeded info
break
case 'paused':
// common properties only
break
}Streaming workflow execution
const run = workflow.createRun()
const stream = run.stream({ inputData: { text: 'Hello' } })
for await (const chunk of stream.fullStream) {
console.log(chunk) // step-level progress events
}
const finalResult = await stream.resultStep-level scorers
Attach evals to individual workflow steps.
const step = createStep({
id: 'scored-step',
inputSchema: z.object({ query: z.string() }),
outputSchema: z.object({ answer: z.string() }),
execute: async ({ inputData }) => ({ answer: 'result' }),
scorers: {
relevancy: {
scorer: createAnswerRelevancyScorer({ model: 'openai/gpt-4.1-nano' }),
sampling: { type: 'ratio', rate: 1 },
},
},
})Scorer execution is asynchronous and does not block step output.
Frequently Asked Questions
What is mastra?
Use this skill when working with Mastra - the TypeScript AI framework for building agents, workflows, tools, and AI-powered applications. Triggers on creating agents, defining workflows, configuring memory, RAG pipelines, MCP client/server setup, voice integration, evals/scorers, deployment, and Mastra CLI commands. Also triggers on "mastra dev", "mastra build", "mastra init", Mastra Studio, or any Mastra package imports.
How do I install mastra?
Run npx skills add AbsolutelySkilled/AbsolutelySkilled --skill mastra in your terminal. The skill will be immediately available in your AI coding agent.
What AI agents support mastra?
mastra works with claude-code, gemini-cli, openai-codex, mcp. Install it once and use it across any supported AI coding agent.