The compiler is an API.
This page is its documentation. If you're building an agent, a coding assistant, or a framework that generates Astra, everything you need is here: the CLI surface, the JSON contract, the error-code catalogue, and the canonical loop.
- JSON
- every diagnostic: a stable code, a line, a column, a suggestion
- 21 codes
- never renumbered, so an agent can branch on them
- 4–5 ms
- per
astra verify: cheaper than one model call
Connect any agent: astra mcp v0.13
astra mcp serves the whole toolchain over the
Model Context Protocol, so any MCP client can call it, whichever model drives it.
| Client | Setup |
|---|---|
| Claude Code | claude mcp add astra -- astra mcp |
| Cursor | .cursor/mcp.json: {"mcpServers": {"astra": {"command": "astra", "args": ["mcp"]}}} |
| VS Code | .vscode/mcp.json: {"servers": {"astra": {"type": "stdio", "command": "astra", "args": ["mcp"]}}} |
| Gemini CLI | .gemini/settings.json: {"mcpServers": {"astra": {"command": "astra", "args": ["mcp"]}}} |
| Codex CLI | ~/.codex/config.toml: [mcp_servers.astra], command = "astra", args = ["mcp"] |
| Any other | a stdio server: command astra, argument mcp |
Its tools are verify, fix, run,
build, format, explain and
test, and each answers with exactly the JSON the matching
command prints. Programs run in a separate process with a time limit and
capped output, and paths outside the folder the server started in are
refused. The model reads the developer guide once, from the resource
astra://guide; astra://errors lists every code.
The commands an agent calls
An agent needs only these commands, each with a stable exit code.
| Command | What it does | Exit code |
|---|---|---|
astra verify --error-format=json <file> |
Parse + typecheck only. No codegen, no execution. ~100 ms. | 0 = verified · non-zero = diagnostics emitted |
astra compile --error-format=json <file> --output <bin> |
Full pipeline to a native executable. | 0 = binary written · non-zero = failed |
astra run --error-format=json <file> v0.13 |
Checks first (a compile error is reported without running), then runs with the interpreter. A runtime error goes to stderr as JSON with an R code; stdout stays the program's. | the program's · 1 = check or runtime error |
astra explain E0002 |
Expands any error code into a full explanation + guidance; astra explain list lists them all. |
0 |
astra fix <file> v0.12 |
Applies every machine-applicable fix, re-verifies, repeats, and writes the file. Accepts --error-format=json. |
0 = verified after fixing · non-zero = errors remain |
Rust callers can skip the CLI entirely:
let report = astra::compiler::verify(source);
let result = astra::compiler::compile_to_result(source);
The diagnostic JSON shape
One object per invocation. Fields are stable; new fields may be added, existing ones are never repurposed.
{
"success": false,
"diagnostics": [
{
"phase": "typecheck", // "parse" | "typecheck" | "codegen"
"kind": "TypeError", // coarse class
"error_code": "E0002", // stable, catalogue below
"message": "Type error: Undefined variable 'total'",
"file": null,
"line": 4, // 1-based; null if unresolvable
"column": 12, // 1-based; null if unresolvable
"severity": "error",
"suggestion": "Declare 'total' with let before use, e.g. let total = ...;"
}
]
}
- Parse errors always carry
lineandcolumnof the offending token. - Typecheck errors: every type error in the
main file carries the line and column of the statement that raised it
(human output ends with
(line N, column M)); a function reports all of its independent errors in one run, not just the first; capability errors (E0014) point at the offending built-in call. Errors in imported modules and in function signatures still have no line (null, never0). suggestionis a human/LLM-readable repair hint, name-aware where possible.- Success shape:
{"success": true, "diagnostics": []}.
Machine-applicable fixes v0.12
When the repair is unambiguous, a diagnostic also carries a
fix: text edits that repair the error without an LLM call.
Each edit replaces the text from line/column up
to, not including, end_line/end_column (1-based)
with text. This is real output from main:
{
"phase": "typecheck",
"error_code": "E0002",
"message": "Type error: Undefined variable 'totl'; did you mean 'total'?",
"line": 4,
"column": 17,
"fix": {
"edits": [
{ "line": 4, "column": 17, "end_line": 4, "end_column": 21, "text": "total" }
]
}
}
- Fixed since v0.12: misspelt variable and function names
(E0002, E0003) and a missing or extra
mutat a call (E0022). - Next: a missing match arm (E0021) and a missing trait method (E0020).
- No
fixfield means no unambiguous repair is known; hand the diagnostic to the model as before.
Canonical integration pattern
The whole integration is one loop: generate, verify, repair from the JSON, then compile.
# pseudocode for any agent framework
source = llm.generate(task)
for attempt in 1..MAX_REPAIRS:
report = run("astra verify --error-format=json", source)
if report.success:
break # verified, safe to compile
source = llm.repair(source, report.diagnostics)
# feed error_code + line +
# column + suggestion back in
binary = run("astra compile --error-format=json", source)
execute(binary) # native, already typechecked
Apply the compiler's own fixes first, and spend a model call only on what remains:
for attempt in 1..MAX_REPAIRS:
report = run("astra verify --error-format=json", source)
if report.success:
break
if any(d.fix for d in report.diagnostics):
run("astra fix", source) # apply edits, re-verify, repeat
continue # no LLM call for these errors
source = llm.repair(source, report.diagnostics)
Why this converges better than a Python retry loop:
- Diagnostics are positional: the model repairs one pinpointed line instead of regenerating the file.
- Codes are categorical: frameworks can route E0002 to the compiler's own fix and reserve the LLM for E0004/E0011.
- Verification takes 4–5 ms: cheaper than any token round-trip, so verify after every single edit.
- Whole error classes cannot escape to runtime, so the expensive failure mode (bad code executing) is structurally removed.
Versioning promise: the JSON field set and the E-code catalogue only grow, existing codes and fields are never renamed or repurposed. Breaking changes to this contract would get a major version bump and a migration note on this page.
Stable error codes
Codes are permanent identifiers, safe to branch on, cache repair
strategies against, or fine-tune with. astra explain E####
returns the long-form description for any of them.
| Code | Meaning | Typical agent repair |
|---|---|---|
E0001 | Syntax error | Re-emit the statement; check delimiters and semicolons |
E0002 | Undefined variable | Declare with let, or fix the identifier spelling (a near-miss name carries a fix) |
E0003 | Undefined function | Define the function or correct the call target (a near-miss name carries a fix) |
E0004 | Type mismatch | Align value type with the annotation / return type |
E0005 | Wrong number of arguments | Match the call to the function signature |
E0006 | Condition must be boolean | Use a comparison, not a bare value, in if/while |
E0007 | Unknown method | Check the receiver type's supported methods |
E0008 | Cannot index non-array | Index arrays only; check the variable's type |
E0009 | Duplicate definition | Rename or remove the redefinition |
E0010 | Struct field error | Use fields declared on the struct; check literal completeness |
E0011 | Generic type error | Make type parameters consistent at the call site |
E0012 | IO error | Check the file path passed to the CLI |
E0013 | Module error | Check the module file exists (next to the importer or on ASTRA_PATH), the item is pub, and there is no import cycle |
E0014 | Capability denied | Remove the call at the end of the reported chain, or allow the effect in the build policy |
E0016 | Assignment to immutable binding | Declare it with let mut, or assign a different binding |
E0017 | Tuple destructuring error | Bind as many names as the tuple has elements |
E0018 | Operator requires integers | Use integers on both sides of bitwise operators and shifts |
E0020 | Trait not implemented | Add the missing method (or fix its signature) named in the message in the impl Trait for Type block; for a bound, pass a type that implements the trait |
E0021 | Non-exhaustive match | Add an arm for each missing variant named in the message, or end with _ => |
E0022 | Value cannot be modified here | Declare the parameter mut, pass it as mut at the call, or copy it and modify the copy (a missing or extra mut at the call carries a fix) |
E0099 | Internal / uncategorized | Report upstream; do not retry blindly |
Runtime error codes v0.13
When a program stops with an error, astra run --error-format=json,
the run tool of astra mcp and the playground all report one of these.
| Code | Meaning |
|---|---|
R0001 | Integer overflow |
R0002 | Division by zero |
R0003 | Invalid shift amount |
R0004 | Index out of bounds |
R0005 | Stack overflow (runaway recursion) |
R0006 | Uncaught throw |
R0007 | Assertion failed |
R0099 | Any other runtime error |