CellScript Wiki
Tutorial 13: Agentic Loops and cellc-mcp
Tutorial 13: Agentic Loops and cellc-mcp
CellScript is a small, narrow language with a deterministic compiler and
machine-readable diagnostics. Those properties make it a good fit for an
agentic loop: a programme writes .cell source, asks the compiler what is
wrong, and corrects itself from structured feedback rather than from guessed
prose.
This chapter describes that loop. It explains why the cellc surface is shaped
for automated callers, which commands give stable machine-readable output, and
where a reference wrapper (cellc-mcp) exposes those commands as agent tools.
It keeps the same boundary as the rest of the wiki: a loop that ends at "the
compiler accepted this" has produced compiler evidence, not CKB chain
acceptance.
What You Will Learn
- why
cellcsuits a write -> check -> explain -> fix loop; - which commands emit stable, machine-readable output an agent can act on;
- how stable diagnostic codes and
cellc explainclose the correction loop; - what a reference MCP wrapper (
cellc-mcp) exposes, and how an agent uses it; - the read-vs-write rule that keeps an automated loop safe;
- where the loop's evidence stops, and what still needs builder and CKB evidence.
Why The Compiler Fits A Loop
Hand-written CKB scripts are hard for an automated caller to get right: it must track inputs, CellDeps, and outputs by index, encode typed state into byte arrays, and preserve linear-asset semantics by convention. Those are exactly the details an automated writer gets subtly wrong, and the failure often only appears at transaction time.
CellScript moves those invariants into the language, so the feedback an automated caller needs arrives at compile time instead. The compiler is also:
- narrow: a small set of keywords and a fixed set of Cell effects such as
consume,create,destroy, andtransition, so the space of valid output is small enough to hold in context; - deterministic: the same source produces the same diagnostics, so a loop can rely on the answer instead of sampling it;
- machine-readable: diagnostics carry stable codes and JSON shape, so a caller acts on a code rather than parsing English.
The result is a tight loop: the writer proposes source, the compiler returns a structured verdict, and the writer corrects from it. The compiler is the oracle; the writer never has to guess whether its contract is well formed.
The Machine-Readable Surface
The loop is built from commands that already emit JSON. Run them from a package
directory that contains Cell.toml; the . argument refers to the current
package when a command expects an explicit path.
cellc check is the core of the loop. It type-checks and lowers the package and
emits a JSON summary:
cellc check --target-profile ckb --jsonOn success the summary reports "status": "ok". On failure it reports
"status": "failed" together with counts and a diagnostics array. Each
diagnostic carries the fields an automated caller needs to locate and classify
the problem:
{
"status": "failed",
"error_count": 1,
"warning_count": 0,
"diagnostics": [
{
"message": "a proposed output failed its declared field transition check",
"severity": "error",
"code": "E0014",
"span": { "line": 21, "column": 9, "start": 360, "end": 372 }
}
]
}The code is the important field. Diagnostic codes are the stable correction
signal: a caller can branch on E0014 rather than parse the rendered message.
Two more read commands give the writer context without leaving the loop:
cellc explain E0014
cellc constraints . --target-profile ckb --jsoncellc explain turns a code into a description and a fix hint. cellc constraints lets the writer inspect what the compiler believes the contract
reads, writes, creates, consumes, and is obliged to verify.
cellc metadata is also useful, but it is commonly run with an output path:
cellc metadata . --target riscv64-elf --target-profile ckb -o /tmp/metadata.jsonTreat that as an explicitly confirmed inspection step rather than as part of an unattended read-only loop, because it writes a file even though it still produces compiler evidence.
The Loop
With those commands, the loop is small:
- The writer proposes
.cellsource for the package. - Run
cellc check --target-profile ckb --json. - If
statusisok, stop. - Otherwise, for each diagnostic, optionally run
cellc explain <code>, revise the source, and return to step 2.
A bounded iteration count keeps a non-terminating writer from looping forever. In practice a capable writer converges in a few rounds: the first check finds a syntax or transition mistake, the explain output names the fix, and the next check passes.
The value of this loop over an unchecked writer is that every claim of success
is backed by the compiler. A writer that says "this token contract is valid" has
either passed cellc check or it has not, and the loop knows which.
A Worked Correction
Suppose a writer proposes a mint action whose output does not satisfy its
declared transition. cellc check --json returns status: failed with code
E0014 (mutate-transition-mismatch). The writer calls cellc explain E0014,
learns that a proposed output failed its declared field transition check, and
revises the action so the transition and the constructed output agree. The
next cellc check returns status: ok. No human needs to read the intermediate
error; the stable code and the explain text carried the correction.
The bundled examples are a good source of grounding for a writer that has not
seen CellScript before. Pointing the writer at examples/token.cell and the
other bundled contracts gives it correct patterns to imitate, which shortens the
first few rounds of the loop. See
Bundled Example Contracts.
A Reference Wrapper: cellc-mcp
A loop needs the compiler available as callable tools. cellc-mcp is a
reference wrapper, a small Model Context Protocol server that exposes the
read-oriented commands above as agent tools, so an MCP-capable model can call
them directly. It is published at
github.com/toastmanAu/cellc-mcp.
The wrapper is deliberately thin. It owns only the boundary work an agent caller
needs: locating the binary, running each command, and presenting the JSON in a
form sized for a model's context. Compilation still belongs to cellc. Because
the wrapper invokes the same cellc binary, the diagnostics a model sees are
the compiler's, not a re-implementation.
Any MCP client can drive it; the wrapper does not assume a particular model. A local model that has little or no CellScript in its training data benefits most, because the narrow language surface fits in context and the deterministic compiler supplies the correctness signal the model lacks on its own.
Read Automatically, Write On Confirmation
An automated loop should run read-only commands freely and gate anything that
writes. The core commands in this chapter, check, explain, and
constraints, only read source and print reports, so a loop can call them
without supervision. That is what makes the write -> check -> fix cycle safe to
automate.
Anything that produces an artifact, writes a report, or touches state is a
different class. Writing a checked contract to disk, producing metadata with
-o, building an ELF, or preparing a transaction should pass through an
explicit confirmation step rather than run inside the automatic loop. Keeping
that line, read freely and confirm before writing, lets the compiler-in-the-loop
stay fast while a human keeps the gate on side effects.
Where The Loop's Evidence Stops
This is the same boundary the rest of the wiki keeps. A loop that ends at
cellc check passing has produced compiler evidence: the source is well
formed, the effects and transitions are consistent, and the target profile
accepted it. It has not produced CKB chain evidence.
A passing check does not prove that a builder can spend the right input Cells, serialise the right witness, satisfy capacity, pass dry-run, and commit. As Metadata, Verification, and Production Gates explains, that distinction is what prevents overclaiming. An agentic loop makes the compiler-evidence half fast and autonomous; it does not move the chain- evidence half. A contract a model wrote and checked is a draft for review, not a deployment.
For the chain-facing half, the loop hands off to the same release-facing evidence the rest of the wiki describes: builder generation, builder tests, and the CKB acceptance gate, none of which an automatic loop should run unattended:
cellc build --target riscv64-elf --target-profile ckb --json
cellc verify-artifact build/main.elf --expect-target-profile ckb
./scripts/cellscript_gate.sh releaseNext
You have now seen the full local picture: the language, the package and profile workflow, metadata and production gates, editor tooling, bundled examples, the registry flow, and the agentic loop that ties the read-oriented compiler surface together. For the chain-facing evidence an agent loop deliberately stops short of, return to Metadata, Verification, and Production Gates.