Skip to main content

Headless mode

Mastra Code can run non-interactively — both from your shell and programmatically from Node or CI. Both paths share one core runner, runMC. The CLI is a thin adapter over it.

  • CLI: mastracode --prompt "..." parses flags, bootstraps Mastra Code, runs the task, prints output, and exits with a status code.
  • Programmatic: you bootstrap Mastra Code once with createMastraCode(), then call runMC() to run a task as an async-iterable that also resolves to a typed result.

CLI usage

Pass --prompt (or -p) to run a single task and exit:

mastracode --prompt "Fix the bug in auth.ts"

Prompts can also be piped via stdin by passing - as the prompt:

echo "Summarize the repo" | mastracode --prompt -

Flags

FlagDescription
--prompt, -p <text>The task to execute (required, or pipe via stdin)
--continue, -cResume the most recent thread instead of creating a new one
--thread, -t <id>Resume a specific thread by ID
--title <title>Set or rename the thread title
--clone-threadClone the current thread before running (work on a copy)
--resource-id <id>Set the resource ID for thread scoping
--timeout <seconds>Exit with code 2 if not complete within the timeout
--max-turns <n>Abort after N agentic turns (exit code 1)
--permission-mode <mode>How tool approvals/suspensions resolve: auto (default) approves everything; deny refuses approvals and aborts on suspension
--output, -o <mode>Output mode: human (default), json, or jsonl
--model, -m <id>Model override (e.g. a provider/model id)
--mode <build|plan|fast>Execution mode — defaults to build if omitted
--thinking-level <level>Thinking level: off, low, medium, high, xhigh
--settings <path>Path to a settings.json file (default: global settings)
--help, -hShow usage

--continue and --thread cannot be used together.

Output modes

ModeBehavior
humanStreaming assistant text to stdout; tool activity and errors to stderr
jsonA single final JSON object (text, usage, tool calls/results, threadId, status)
jsonlNewline-delimited JSON: one line per event, then a final { "type": "result", ... } line

Exit codes

CodeMeaning
0Agent completed successfully
1Error, aborted, or max turns reached
2Timeout

Examples

# JSON output with a timeout
mastracode --prompt "Add tests" --timeout 300 --output json

# Event stream
mastracode --prompt "Refactor" --output jsonl

# Gate a CI run: no unattended tool execution, bounded turns
mastracode --prompt "Review this PR" --permission-mode deny --max-turns 10

# Use a CI-specific settings file
mastracode --settings ./settings-ci.json --prompt "Run tests"

# Resume the most recent thread
mastracode -c --prompt "Continue where you left off"

Programmatic usage

Bootstrap Mastra Code with createMastraCode(), then pass the returned controller and session to runMC(). createMastraCode() is async; runMC() is synchronous and returns a run handle immediately.

TypeScriptsrc/run.ts
import { createMastraCode, runMC } from 'mastracode'

const { controller, session } = await createMastraCode()

const run = runMC({
  controller,
  session,
  prompt: 'Fix the failing test in auth.test.ts',
})

// Optional: stream live events as they happen.
for await (const event of run) {
  console.log(event.type)
}

// Resolves once the run completes, times out, errors, or is aborted.
const result = await run.result
console.log(result.status, result.text)

runMC never calls process.exit and never writes to global streams, so it is safe to embed in CI scripts and servers.

The run handle

runMC returns an MCRun, which is both an async-iterable over controller events and a handle that resolves to a final result:

const run = runMC({ controller, session, prompt })

run.result // Promise<RunMCResult>
run.abort() // abort the in-flight run
for await (const event of run) {
  /* ... */
}

Both for await (const event of run) and await run.result work on the same run. Awaiting result without iterating still drains events internally.

runMC options

OptionTypeDescription
controllerAgentControllerController from createMastraCode() (required)
sessionSessionSession from createMastraCode() (required)
promptstringThe task to run (required)
modelstringExplicit model id override; takes precedence over mode
mode'build' | 'plan' | 'fast'Execution mode; resolves a model from modeDefaults when model is absent
modeDefaultsRecord<string, string>Per-mode default model ids
thinkingLevel'off' | 'low' | 'medium' | 'high' | 'xhigh'Thinking-effort level
thread{ id?: string; continueLatest?: boolean; clone?: boolean }Thread selection / mutation
resourceIdstringResource id for thread scoping
titlestringSet or rename the thread title before running
timeoutMsnumberAbort with status: 'timeout' (exit code 2) if not complete in time
maxTurnsnumberAbort with status: 'max_turns' (exit code 1) after N assistant turns
policyResolutionPolicyHow approvals/suspensions resolve (defaults to autoApprovePolicy)
signalAbortSignalExternal abort signal; aborting it aborts the run

RunMCResult

FieldTypeDescription
status'completed' | 'error' | 'aborted' | 'timeout' | 'max_turns'How the run ended
textstringAggregated assistant text across the run
finishReasonstringUnderlying finish reason when the run finished normally
usage{ inputTokens?; outputTokens?; totalTokens? }Token usage
toolCallsArray<{ id; name; args }>Tool calls made during the run
toolResultsArray<{ id; name; result; isError }>Tool results
threadIdstringThe thread the run executed in
error{ name; message; stack? }Present when status is error
exitCodenumber0 success, 1 error/aborted/max_turns, 2 timeout

Resolution policies

A resolution policy decides how the run resumes tool_approval_required and tool_suspended events when there is no human in the loop. The default is autoApprovePolicy, which reproduces the historical headless behavior: approve every tool, auto-resolve sandbox and submit_plan suspensions, and answer any other suspension with a "use your best judgment" instruction.

Two built-in policies and a resolver are exported:

ExportBehavior
autoApprovePolicyApprove every tool; auto-resolve suspensions (default)
denyPolicyRefuse every tool approval; abort on any suspension
permissionModeToPolicy(mode)Resolve a PermissionMode ('auto' or 'deny') to a built-in policy

The CLI --permission-mode flag maps directly onto these via permissionModeToPolicy.

Custom policies

Implement the ResolutionPolicy interface to make per-event decisions:

TypeScriptsrc/strict-policy.ts
import { createMastraCode, runMC, type ResolutionPolicy } from 'mastracode'

const readOnlyPolicy: ResolutionPolicy = {
  // Only approve read_file; deny everything else.
  onToolApproval(event) {
    return event.toolName === 'read_file' ? 'approve' : 'deny'
  },
  // Abort rather than answer suspensions unattended.
  onSuspension() {
    return { abort: true }
  },
}

const { controller, session } = await createMastraCode()

const run = runMC({
  controller,
  session,
  prompt: 'Audit the codebase for secrets',
  policy: readOnlyPolicy,
})

const result = await run.result

onToolApproval returns 'approve' or 'deny'. onSuspension returns either { resumeData } to resume the suspended tool or { abort: true } to abort the run.

Next steps

  • Customization — extend Mastra Code with custom modes, tools, and subagents.
  • API reference — full createMastraCode() options and return values.