Configuration
Mastra Code is configured through project-level files, global settings, and environment variables. Configuration files live in .mastracode/ directories at the project and user levels.
Authentication
Mastra Code supports two authentication methods: API keys (environment variables) and OAuth (provider subscriptions). You can use either or both.
Use the /setup command to interactively configure authentication and models.
API keys
The simplest way to get started is to set API key environment variables for the providers you want to use. Mastra Code auto-detects these on startup:
export ANTHROPIC_API_KEY=sk-ant-...
export OPENAI_API_KEY=sk-...
export GOOGLE_API_KEY=...
export DEEPSEEK_API_KEY=...
export CEREBRAS_API_KEY=...
Mastra Code uses Mastra's model router, which routes requests to the correct provider based on the model ID and available API keys. No OAuth login is required when using API keys.
Interactive API key prompt
When you select a model from the model selector (/models) that doesn't have an API key configured, Mastra Code prompts you to enter one. The prompt displays the required environment variable name (for example, ANTHROPIC_API_KEY) and lets you paste your key directly.
- Enter a key: The key is stored in
auth.jsonand set in the current process environment. The model becomes available immediately. - Cancel the prompt: Press Escape to dismiss. The model selection continues without storing a key, and the model remains marked as unavailable.
Stored API keys persist across sessions. On the next startup, Mastra Code loads them into the environment automatically. Environment variables always take priority — if you later set a key via export, it overrides the stored value.
OAuth login (optional)
If you have an Anthropic Claude Max or OpenAI ChatGPT Plus subscription, you can authenticate via OAuth to use your subscription instead of API keys. Run /login in the TUI to start the flow.
| Provider | OAuth flow | What you get |
|---|---|---|
| Anthropic | Claude Max subscription | Access to Claude models via your subscription |
| OpenAI | ChatGPT Plus / Codex | Access to OpenAI models via your subscription |
OAuth credentials are stored in auth.json alongside the database in the app data directory. During onboarding, you can skip the OAuth step if you already have API keys configured.
When you select ChatGPT Plus/Pro (Codex Subscription) in /login or during onboarding, Mastra Code asks how you want to sign in:
- Browser (local callback): opens the browser and waits for the OAuth callback on localhost. This is the default.
- Device code (headless): shows a code to enter at
https://auth.openai.com/codex/device— useful for SSH sessions, remote shells, or environments without a browser.
To preselect the device-code flow (for example in scripts or remote shells where the prompt is inconvenient), set MASTRACODE_OPENAI_CODEX_AUTH_MODE=device before launching Mastra Code:
export MASTRACODE_OPENAI_CODEX_AUTH_MODE=device
Authentication priority for Anthropic
When resolving Anthropic models, Mastra Code checks for credentials in this order:
- Claude Max OAuth (if logged in via
/login) ANTHROPIC_API_KEYenvironment variable or stored API key credential- OAuth login prompt (if nothing else is configured)
To switch from OAuth to API key usage, log out with /logout and set your ANTHROPIC_API_KEY environment variable.
Anthropic OAuth warning
Authenticating with a Claude Max subscription through OAuth is a grey area. Anthropic has reportedly banned users for using Claude max credentials outside of Claude Code, so it may violate Anthropic Terms of Service.
Direct shell passthrough
In the interactive TUI, input that starts with ! runs as a direct shell passthrough command:
!pwd
!git status --short
This direct passthrough is separate from the agent's execute_command tool. Tool permissions and agent command execution are not changed by this setting.
By default, Mastra Code keeps the compatibility behavior from earlier versions:
- macOS and Linux:
execa(command, { shell: true }), which behaves like/bin/sh -c. - Windows:
execa(command, { shell: true }), which behaves likecmd.exethrough the platform shell.
Mastra Code does not use $SHELL automatically and does not switch Windows users to PowerShell automatically. Configure a shell only when you want direct ! commands to use a specific command language.
Shell passthrough settings
Set the shell in global settings.json with the shellPassthrough block:
{
"shellPassthrough": {
"mode": "path",
"executable": "/bin/zsh",
"family": "posix"
}
}The mode field accepts:
| Mode | Behavior |
|---|---|
default | Keep the built-in shell: true behavior. |
path | Run the configured executable without loading login or profile files. |
login | Run the configured executable and allow shell startup or profile files when supported. |
If executable is set and mode is omitted, Mastra Code treats the shell as path mode.
The family field accepts:
| Family | Behavior |
|---|---|
posix | Run the executable with -c. In login mode, run it with -l -c. |
cmd | Run cmd.exe with /d /s /c. |
powershell | Run PowerShell or PowerShell Core with an encoded command. |
For common executables, Mastra Code infers family from the executable name. pwsh, pwsh.exe, powershell, and powershell.exe use powershell. cmd and cmd.exe use cmd. Other explicit shells use posix on macOS and Linux. On Windows, set family when the executable is not cmd.exe, powershell.exe, or pwsh.exe.
Environment overrides
Use environment variables to override the persisted shell for the current process:
export MASTRACODE_SHELL=/bin/zsh
export MASTRACODE_SHELL_MODE=path
MASTRACODE_SHELL sets the executable. MASTRACODE_SHELL_MODE accepts default, path, or login. Empty values are ignored.
macOS and Linux examples
Run direct ! commands through zsh without loading startup files:
export MASTRACODE_SHELL=/bin/zsh
export MASTRACODE_SHELL_MODE=path
Opt into a login shell when you need shell startup files:
export MASTRACODE_SHELL=/bin/zsh
export MASTRACODE_SHELL_MODE=login
Login mode can be slower or noisy because startup files can print output, prompt for input, change directories, or run long setup commands.
Windows examples
Use cmd.exe explicitly:
$env:MASTRACODE_SHELL = "cmd.exe"
$env:MASTRACODE_SHELL_MODE = "path"
Use PowerShell Core without loading profiles:
$env:MASTRACODE_SHELL = "pwsh"
$env:MASTRACODE_SHELL_MODE = "path"
Use Windows PowerShell and load profiles:
$env:MASTRACODE_SHELL = "powershell.exe"
$env:MASTRACODE_SHELL_MODE = "login"
PowerShell passthrough uses -EncodedCommand and preserves native command exit codes where practical. It treats cmdlet errors as failures so direct shell commands return a non-zero exit code. In path mode, it also uses -NoProfile. In login mode, it omits -NoProfile so PowerShell profiles can load.
MCP servers
Mastra Code can connect to external MCP servers and make their tools available to the agent. Configure servers in JSON files:
| Priority | Path | Scope |
|---|---|---|
| Highest | .mastracode/mcp.json | Project |
~/.mastracode/mcp.json | Global | |
| Lowest | .claude/settings.local.json | Project (Claude Code compatible) |
Project config overrides global config by server name. Claude Code settings are the lowest priority fallback.
MCP config format
Mastra Code supports two transport types for MCP servers: stdio (local process) and HTTP (remote server via Streamable HTTP or SSE).
Stdio servers
Launch a local process that communicates via stdin/stdout. Requires a command field. The args and env fields are optional.
{
"mcpServers": {
"my-server": {
"command": "npx",
"args": ["-y", "@my-org/mcp-server"],
"env": {
"API_KEY": "your-key"
}
}
}
}HTTP servers
Connect to a remote MCP server via URL. Requires a url field. The optional headers field lets you pass static headers such as authentication tokens.
{
"mcpServers": {
"remote-api": {
"url": "https://mcp.example.com/sse",
"headers": {
"Authorization": "Bearer your-token"
}
}
}
}HTTP OAuth
HTTP servers can also use OAuth when the server supports MCP authorization. Add an oauth object with a redirectUrl. Mastra Code stores OAuth client data and tokens in the app data directory.
Mastra Code passes this configuration to @mastra/mcp when connecting. The redirect URL must use HTTPS unless it is a loopback HTTP URL, and it must be handled by a callback endpoint that can complete the OAuth flow for that server.
{
"mcpServers": {
"remote-api": {
"url": "https://mcp.example.com/mcp",
"oauth": {
"redirectUrl": "http://localhost:3000/oauth/callback",
"clientName": "Remote MCP",
"scopes": ["mcp:read", "mcp:write"]
}
}
}
}Use clientId and clientSecret when the MCP server requires a pre-registered OAuth client:
{
"mcpServers": {
"remote-api": {
"url": "https://mcp.example.com/mcp",
"oauth": {
"redirectUrl": "http://localhost:3000/oauth/callback",
"clientId": "your-client-id",
"clientSecret": "your-client-secret"
}
}
}
}Do not commit real clientSecret values to a repository. Prefer a project-local or global config file that is not shared.
Startup behavior
On startup, Mastra Code connects to all configured servers and reports the status:
MCP: 2 server(s) connected, 15 tool(s)
Invalid or ambiguous entries (for example, specifying both command and url, or providing an invalid URL) are skipped with a reason:
MCP: Skipped "bad-server": Missing required field: "command" (stdio) or "url" (http)
Tools from MCP servers are namespaced as serverName_toolName and appear in the agent's tool list alongside built-in tools. Use /mcp to see the status of all servers, including their transport type and any skipped entries.
Hooks
Hooks are user-configured shell commands that run at specific lifecycle events. Use hooks for custom validation, logging, notifications, or integration with external systems.
Hook config format
{
"PreToolUse": [
{
"type": "command",
"command": "node scripts/validate-tool.js",
"matcher": {
"tool_name": "execute_command"
},
"timeout": 5000,
"description": "Validate shell commands before execution"
}
],
"PostToolUse": [
{
"type": "command",
"command": "node scripts/log-tool.js",
"description": "Log tool usage"
}
]
}Hook events
| Event | When it fires | Can block? |
|---|---|---|
PreToolUse | Before the agent tool hook wrapper allows a tool call to execute | Yes |
PostToolUse | After the agent tool hook wrapper observes a completed tool call | No |
Stop | When an agent response ends (complete, aborted, or error) | Yes |
UserPromptSubmit | When the user sends a message | Yes |
SessionStart | When a session begins | No |
SessionEnd | When a session ends | No |
Notification | When the TUI fires a notification | No |
AgentStart | When an agent run begins | No |
AgentEnd | When an agent run ends (complete, aborted, error, or suspended) | No |
PermissionRequest | When a tool, sandbox access request, or plan approval requires user permission | No |
PermissionResult | When a user approves, declines, or dismisses a permission gate | No |
Interrupt | When a running run is aborted by the user, goal judge, or a process signal | No |
SubagentStart | When a subagent begins a delegated task | No |
SubagentEnd | When a subagent finishes its delegated task | No |
UserPromptSubmit runs before a non-command prompt is sent to the agent. If a hook blocks, the prompt is not sent.
PreToolUse and PostToolUse are emitted by the agent tool hook wrapper around tool execution. Run, permission, interrupt, and subagent events are emitted by the TUI lifecycle dispatcher.
Only PreToolUse, Stop, and UserPromptSubmit can block agent behavior. PostToolUse and the lifecycle dispatcher events are non-blocking signals for logging and integration.
Hook I/O protocol
Hook processes receive a JSON payload on stdin with context about the event:
{
"session_id": "thread-abc123",
"cwd": "/path/to/project",
"hook_event_name": "PreToolUse",
"tool_name": "execute_command",
"tool_input": { "command": "npm test" }
}
During an active agent run, all hook payloads include a run_id that identifies the current run. The same run_id appears on AgentStart, tool hooks, PermissionRequest, PermissionResult, Interrupt, SubagentStart, SubagentEnd, AgentEnd, and Stop for that run, so external integrations can correlate events.
A PermissionRequest payload includes the permission kind and call context. permission_kind can be tool_approval, sandbox_access, or plan_approval. tool_name and tool_input describe the gated tool call when the request comes from a tool approval, sandbox access request, or plan approval.
{
"session_id": "thread-abc123",
"cwd": "/path/to/project",
"hook_event_name": "PermissionRequest",
"run_id": "run-uuid",
"permission_kind": "tool_approval",
"tool_call_id": "call-1",
"tool_name": "execute_command",
"tool_input": { "command": "rm -rf /" }
}
For blocking events (PreToolUse, Stop, UserPromptSubmit), the hook can respond on stdout with a JSON object:
{
"decision": "block",
"reason": "This command is not allowed"
}
Hook config locations
| Priority | Path | Scope |
|---|---|---|
| Higher | .mastracode/hooks.json | Project (appended after global) |
| Lower | ~/.mastracode/hooks.json | Global (runs first) |
Global hooks run before project hooks. For the same event, all hooks execute in order.
Custom slash commands
Define reusable prompt templates as markdown files. Mastra Code scans these directories for .md files:
| Priority | Path | Scope |
|---|---|---|
| Highest | .mastracode/commands/ | Project |
.claude/commands/ | Project (Claude Code compatible) | |
.opencode/command/ | Project (Opencode compatible) | |
~/.mastracode/commands/ | Global | |
~/.claude/commands/ | Global (Claude Code compatible) | |
| Lowest | ~/.opencode/command/ | Global (Opencode compatible) |
Each command file can include YAML frontmatter with name and description fields:
---
name: review
description: Review the current diff for issues
---
Review my current git diff. Look for bugs, security issues,
and violations of the project's coding standards.The filename (or directory structure) determines the command name. For example, git/commit.md becomes /git:commit.
Commands support variables like $ARGUMENTS for positional args, @filename for file content injection, and !command for shell command output.
Add goal: true to expose the command as a goal entry point under /goal/<command-name>:
---
description: Triage every open PR
goal: true
---
Review every open PR and continue until each PR has a concise TL;DR.Skills
Skills are structured instruction files that the agent loads automatically based on trigger conditions. They provide domain-specific guidance without manually pasting instructions.
Mastra Code scans these directories for skills:
| Priority | Path | Scope |
|---|---|---|
| Highest | .mastracode/skills/ | Project |
.claude/skills/ | Project (Claude Code compatible) | |
.agents/skills/ | Project (Agent Skills spec compatible) | |
~/.mastracode/skills/ | Global | |
~/.claude/skills/ | Global (Claude Code compatible) | |
| Lowest | ~/.agents/skills/ | Global (Agent Skills spec compatible) |
Each skill is a directory containing a SKILL.md file with instructions and trigger descriptions. Skills installed via symlinks (e.g., from npx skills add) are automatically resolved.
Use /skill/<name> to activate a specific skill in the current conversation:
/skill/release-check
Mastra Code loads the skill instructions and sends them to the agent before the next response.
Set user-invocable: false in SKILL.md frontmatter to hide a skill from /skill/<name> autocomplete, the /skills listing, and direct activation. The agent can still load that skill automatically when relevant.
Add metadata.goal: true to expose a skill as a goal entry point under /goal/<skill-name>:
---
name: release-check
description: Check whether a release is ready
metadata:
goal: true
---
Run the release checks and continue until the release is ready or a human decision is required.Agent instructions
Mastra Code loads project-specific instructions from AGENTS.md or CLAUDE.md files and injects them into the agent's system prompt. This is how you can customize the agent's behavior for your project.
Lookup order
For project instructions (first match wins):
AGENTS.mdorCLAUDE.mdin the project root.claude/AGENTS.mdor.claude/CLAUDE.md.mastracode/AGENTS.mdor.mastracode/CLAUDE.md
For global instructions (first match wins):
~/.claude/AGENTS.mdor~/.claude/CLAUDE.md~/.mastracode/AGENTS.mdor~/.mastracode/CLAUDE.md~/.config/claude/AGENTS.mdor~/.config/claude/CLAUDE.md~/.config/mastracode/AGENTS.mdor~/.config/mastracode/CLAUDE.md
AGENTS.md takes priority over CLAUDE.md when both exist at the same location.
Storage
Mastra Code stores threads, messages, state, and observational memory in a database. It uses LibSQL by default with a local file — no setup needed.
To switch to a remote LibSQL (Turso) or PostgreSQL backend, run /settings and select a storage backend. You'll be prompted for a connection URL.
If a PostgreSQL connection fails on startup, Mastra Code falls back to LibSQL and shows a warning so you can fix the connection via /settings.
Observational memory
Observational memory (OM) uses background agents to maintain a dense log of observations and reflections about the conversation, providing long-term context that persists across sessions.
OM scope
Control whether observations are scoped to individual threads or shared across all threads for a project:
| Scope | Behavior |
|---|---|
thread (default) | Observations are private to each conversation thread |
resource | Observations are shared across all threads for the same project |
Configure the scope through:
MASTRA_OM_SCOPEenvironment variable ("thread"or"resource").mastracode/database.json→omScopefield~/.mastracode/database.json→omScopefield
OM model settings
The observer and reflector models default to google/gemini-2.5-flash. Set them through the /om panel or in the controller state.
OM thresholds
| Threshold | Default | Description |
|---|---|---|
| Observation threshold | 30,000 tokens | Token count that triggers an observation pass |
| Reflection threshold | 40,000 tokens | Token count that triggers a reflection pass |
Color theme
Mastra Code detects your terminal's color scheme and applies a matching dark or light theme. You can override the detected theme with the /theme slash command or the MASTRA_THEME environment variable.
Detection order
MASTRA_THEMEenvironment variable: explicitdarkorlight- Persisted setting from
/themecommand (stored insettings.json) - OSC 11 query: asks your terminal for its actual background color
COLORFGBGenvironment variable (set by some terminals like iTerm2 and Konsole)- Falls back to dark theme
Switching themes
Use the /theme command to change the theme at runtime:
/theme light
/theme dark
/theme auto
Running /theme with no arguments shows the current theme. The choice is persisted across sessions.
Environment variables
| Variable | Description |
|---|---|
MASTRA_DB_URL | LibSQL database URL (e.g., libsql://... or file:./data.db) |
MASTRA_DB_AUTH_TOKEN | Auth token for remote LibSQL database |
MASTRA_DB_PATH | Override the local database file path |
MASTRA_USER_ID | Override the auto-detected user identity |
MASTRA_RESOURCE_ID | Override the auto-detected project resource ID |
MASTRA_OM_SCOPE | Observational memory scope (thread or resource) |
DEFAULT_OM_MODEL_ID | Default model for OM observer and reflector |
MASTRA_PLANS_DIR | Override the directory where approved plans are saved (default: app data directory) |
MASTRA_THEME | Color theme override (dark or light) |
MASTRACODE_SHELL | Direct TUI ! shell executable override |
MASTRACODE_SHELL_MODE | Direct TUI ! shell mode (default, path, or login) |
TAVILY_API_KEY | Enable Tavily-powered web search and extract tools |
Resource ID override
Resource IDs determine how threads are grouped. By default, Mastra Code generates one from the Git remote URL or filesystem path. Override it to share threads across repositories or isolate threads within a monorepo:
{
"resourceId": "my-custom-project-id"
}You can also set MASTRA_RESOURCE_ID as an environment variable. Two users who set the same resource ID share threads and observations for that resource.
Telemetry
By default, Mastra Code collects anonymous product analytics about how the TUI is used. Each event includes your OS, Mastra Code version, Node.js version, CPU architecture, and a hostname-derived distinct ID. Tracked events cover session start, prompt submission, thread and model changes, slash command usage, and interactive prompts. Prompt text, message content, file paths, and tool inputs are not collected. You can read the source code to check what's collected.
You can opt out of analytics by setting an environment variable:
MASTRA_TELEMETRY_DISABLED=1
You can also set this when launching Mastra Code:
- npm
- pnpm
- Yarn
- Bun
MASTRA_TELEMETRY_DISABLED=1 npx mastracodeMASTRA_TELEMETRY_DISABLED=1 pnpm dlx mastracodeMASTRA_TELEMETRY_DISABLED=1 yarn dlx mastracodeMASTRA_TELEMETRY_DISABLED=1 bun x mastracode