Agent mode

Every minc tool can answer in records instead of terminal text. minc agent serves those records over the Model Context Protocol, giving a coding agent six tools to compile, run, test, debug, profile and query a project.

The server keeps the source index warm between calls. When the agent asks about the code, the parser answers, not a text search. When a program crashes, the agent gets a backtrace, not a status code to chase.

Setup

With Claude Code:

claude mcp add minc -- minc agent

With any other client that can launch a stdio server:

{
  "mcpServers": {
    "minc": { "command": "minc", "args": ["agent"] }
  }
}

One registration covers every project. If the client offers the MCP roots capability, the server works in the client's first root and follows it when it changes. Otherwise it works in the directory it started in. Pass --root <dir> to pin it.

The install puts minc, minc-lsp and minc-dbg side by side. The server itself runs inside minc-lsp, and the debug and profile tools call minc-dbg.

The tools

toolargumentsresult
querywhat, name, targetdefinitions, references, callers, matching symbols, defining files, import closure
compilefile, output, flags, hashdiagnostics with codes and fixes, then a summary or output record
runfile, args, timeout, memory, flagsthe program's output, then exit status, milliseconds, peak memory
debugfile, argsthe fault and a symbolized backtrace
profilefile, args, secondsthe functions by share of samples
testscope, flagsone record per test, then the results

A tool sets isError when its command exits nonzero. For compile that means diagnostics. For run it means the program failed or crashed. compile, run, debug and profile work on the file you name. test runs the whole project suite, through build.mc if the project has one.

Diagnostics carry the repair

When the compiler knows the mechanical repair, it ships the fix with the diagnostic, and the agent can apply it without spending a reasoning turn.

{"kind":"error","file":"app.mc","line":5,"col":9,"code":"type-mismatch",
 "message":"initializer type mismatch",
 "fix":{"kind":"wrap","line":5,"col":13,"end_col":16,"before":"cast(i32, ","after":")"}}
codefixeffect
type-mismatch, sign-mismatchwrapput before and after around the expression at line:col
must-useinsertput text at line:col, here ignore 
unused-variable, unreachabledelete_lineremove the line
syntaxreported with position and message

What comes back

Each tool returns one JSON object per line, and streams them as the run produces them.

recordfromfields
error, warning, noteevery toolfile, line, col, code, message, fix
summary, outputcompileerrors; or file, bytes, lines
hashcompilethe output's SHA-256; the same source and flags give the same digest
runrun, debugexit, ms, peak_kb, and signal, timeout or status on a failure
crashdebugsignal, pc, frames of fn, file, line
profileprofilesamples, ms, exit, functions of fn, samples, pct
test, resultstestname, status, ms, exit, a failure's output or diagnostics; then passed, failed, unchanged
leak--track-alloc buildslive, total allocations at exit
def, ref, caller, filequeryfile, line, col, symbol, node; in and sites where they apply

A crash record names the faulting instruction and the path that reached it. Inlined functions appear under their caller, each with its own line:

{"kind":"crash","signal":"access_violation","pc":"0x401042",
 "frames":[{"fn":"depth","file":"rec.mc","line":2},{"fn":"depth","file":"rec.mc","line":3},
           {"fn":"main","file":"rec.mc","line":7}]}
{"kind":"run","exit":-1073741819,"status":"0xc0000005"}

The profiler samples the instruction pointer about a thousand times a second and ranks the functions by share:

{"kind":"profile","samples":1095,"ms":1674,"exit":0,
 "functions":[{"fn":"quick_sort","samples":867,"pct":79.1},
              {"fn":"insertion_sort","samples":201,"pct":18.3},
              {"fn":"main","samples":27,"pct":2.4}]}

The source index

The index only counts identifiers the parser resolved. So query reports the sites that actually reach a declaration.

whatanswers
defwhere a name is defined; Struct.field names a field
refsevery reference site, with the declaration holding it
callersfunctions whose bodies reference it, with site counts
symbolsdefinitions whose name contains the text
definesthe files that define it
closureevery file a compile of the named file reads
$ minc query callers random_quat
sample_browser/sample_collision.mc:133:6   caller  query_spawn_once      1
sample_browser/sample_collision.mc:321:6   caller  create_wave_pile      1
sample_browser/sample_compound.mc:113:6    caller  build_compound_hulls  1
sample_browser/sample_joint.mc:1826:6      caller  gl_create_debris      1

The index covers top-level declarations across the tree, plus the modules it imports from outside, standard library included. It indexes for one target at a time, and target picks which when arms it keeps. It skips whatever the root's .gitignore lists, the build directory, and any directory starting with a dot. The minc compiler's own source, 149,000 lines across 50 files, indexes from cold in about 0.3 seconds. Later calls only re-index the files whose modification time or size changed.

On the command line

The MCP tools can be used directly as verbs on the minc compiler. A harness that runs shell commands instead of MCP gets the same records.

switchshape
--agentone line per diagnostic, one summary line per run, sorted output, no prompts
--agent=jsonthe same as JSON records, one JSON object per line
MINC_AGENT=1, MINC_AGENT=jsonagent mode flag for the process
$ minc app.mc --agent
app.mc:5:9: error: initializer type mismatch [fix: wrap 5:13-16 in cast(i32, ...)]
1 error(s) found

Color and source excerpts switch off by themselves when stderr is a pipe or a file. NO_COLOR switches off the color alone. The --agent flag also sets MINC_AGENT for every process the compiler starts. Compiles from a build.mc, the test runner's processes and minc run inherits the setting.

The same tools by hand

commanddoes
minc app.mccompile; --hash prints the output's SHA-256
minc run app.mcbuild and run, with --timeout N and --memory N limits
minc debug --batch app.mcrun to the end and report a crash with its backtrace
minc profile app.mcsample the run and list the functions by share
minc testcompile and run test/*.mc; --filter, --changed, --timeout
minc query refs nameask the index
minc agentserve all of it over MCP

The profiler prints the program's own output first, then the sample count and the functions by share. Example output from headless box3d benchmark:

$ minc profile live-demo/box3d_bench.mc
samples: 118
 35.5%      42  b3SolveContacts_Convex
 13.5%      16  b3CollideTask
  6.7%       8  b3IntegrateVelocitiesTask
  5.9%       7  b3PrepareContacts_Convex
  5.0%       6  b3FinalizeBodiesTask
  5.0%       6  <outside .text>
  4.2%       5  b3WarmStartContacts_Convex
  2.5%       3  b3ScatterBodies

The --track-alloc flag builds the program with allocation counters and reports what is still live when main returns. That lets run tell you whether a program leaked, without touching the program's source. Note: current version only tracks if leaks occured, not where they happened.

$ minc run --track-alloc list.mc
sum 6
leak: 4 live of 4 allocations

$ minc run --track-alloc --agent=json list.mc
sum 6
{"kind":"leak","live":4,"total":4}
{"kind":"run","exit":0,"ms":36,"peak_kb":3644}

Before an agent writes minc, point it at AGENTS.md. Everything else is in the language reference.

Questions, bugs? Open an issue.