Agents
Author, rename, and extend the assistant - a Mastra agent registered in code, with AI SDK tools, MCP clients, inline skills, and an editable persona.
The assistant is a Mastra agent (packages/api/src/mastra/agents/assistant/config.ts). Chat and automated runs both call it, so anything you change here changes both.
Author with Mastra's conventions, not a wrapper of ours - this boilerplate adds no abstraction over the agent, its tools, memory, or MCP clients. Mastra's docs are the source of truth for shapes and options; this page covers only the boilerplate-specific seams.
Register the agent in code
The agent is constructed and registered in one place - getMastra() (packages/api/src/mastra/index.ts) - via Mastra's agents map. Add or rename one by editing that map and its folder under packages/api/src/mastra/agents/<name>/.
new Mastra({
storage,
agents: { assistant: createAssistantAgent(assistantMemory) }
});Register in code - do not rely on file-based discovery. Mastra's agents/ folder auto-discovery runs only under the Mastra CLI, and this backend imports its Mastra instance directly as a library, so the agents map is the registration. The folder shape mirrors Mastra's convention so it stays recognizable if you later adopt the CLI.
Re-voice the persona
The persona is the agent's inline instructions string (assistantInstructions), not a sibling instructions.md: the production build rewrites module paths and does not copy adjacent markdown, so a readFileSync loader throws at runtime. The capability nudge and the other injected strings are a separate seam - see AI prompts.
Give it tools
The agent's tools are AI SDK tool() objects, re-exported by @repo/ai:
import { tool } from "@repo/ai";
import { z } from "zod";
const getForecast = tool({
description: "Look up the weather forecast for a city.",
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }) => fetchForecast(city)
});Attach it to the agent (new Agent({ ..., tools: { getForecast } })) or hand it in per run as a named toolset - the pattern chat and automations use for the capability set. You rarely start from scratch: the capabilities that operate the app and the integrations users connect are already tools per run. See Mastra's tools docs for dynamic tools, approval, and toolsets.
Give it MCP clients
To call external MCP servers (a user's connected service, an internal tool server), use Mastra's MCPClient from @mastra/mcp and pass its toolset into a run. This is the inbound direction; the outward server (your app as an MCP server to someone else's agent) is a separate, config-gated option.
import { MCPClient } from "@mastra/mcp";
const mcp = new MCPClient({
servers: { docs: { url: new URL("https://example.com/mcp") } }
});
const toolsets = await mcp.listToolsets();
// pass `toolsets` into agent.stream(..., { toolsets }) / agent.generate(..., { toolsets })@mastra/mcp already ships in the API package; if a lean build dropped it, run pnpm --filter @repo/api add @mastra/mcp first. Follow Mastra's MCP docs for auth, lifecycle, and server config.
Give it skills
Mastra skills attach reusable instruction bundles the agent loads on demand through three tools it gains automatically: skill, skill_search, and skill_read. Define them inline with createSkill and pass them at construction - the shipped agent defines none, so this is a recipe.
import { Agent } from "@mastra/core/agent";
import { createSkill } from "@mastra/core/skills";
const refundPolicy = createSkill({
name: "refund-policy",
description: "Use when a user asks about refunds or cancellations.",
instructions: "Refunds are prorated within 30 days. Escalate anything older.",
references: { "policy.md": "# Refund policy\n..." } // served in-memory, no filesystem
});
new Agent({ id: "assistant", model: "openai/gpt-5.5", instructions: "...", skills: [refundPolicy] });Inline createSkill only, never a filesystem-backed skill. A skill referenced by path (skills: ["./skills/refund-policy"]) reads its SKILL.md at runtime, but the production build does not trace a file referenced only by a path string - the SKILL.md never reaches the bundle and skill_read fails there. Inline skills have no filesystem dependency, so they always survive.
Reaching the agent from the Mastra CLI
Mastra's CLI can talk to a running Mastra server (mastra api --url <origin>), but this backend's server.auth is a cookie-session guard - the same Better Auth session the app uses. The CLI's bearer-token access is not wired, so add your own token mechanism to the server auth if you want it.
AI
The AI product the agent powers: chat, automated runs, capabilities, billing.
AI prompts
The injected prompt seams, and how they layer with the agent's persona.
AI integrations
Third-party services that become agent tools when a user connects them.
Mastra docs
Agents, tools, memory, workflows, MCP, and skills - the framework's own reference.
AI
The config.ai product - a streaming chat assistant, cron-automated runs, and an act-on-your-app capability layer on the shared Hono backend, billed from credits or run on your users' own API keys.
AI studio
Mastra Studio, the local dev UI for inspecting the assistant agent, its memory threads, and its traces.