Bootstrap prompt (empty repo → running Mendix app)
The primary way to start a Mendix + mxcli project from the web or an iPad — no local CLI, no GitHub template to pick from a (short) mobile list. Open an empty repo in Claude Code Web and paste the prompt below; the agent asks you what the app is, then provisions everything and commits the result so future sessions self-bootstrap.
The prompt itself is deliberately tiny — install mxcli, unpack its skills, hand over to
the bootstrap-app skill. Everything with detail in it lives in that skill, which
ships inside the binary, so the paste stays phone-sized and the procedure is versioned
with mxcli instead of with whatever text someone copied months ago.
Why a prompt instead of a GitHub template repo: the mobile “New repository” template dropdown shows only a small subset of templates, and a template repo needs per-Mendix- version upkeep. A prompt starts from a truly empty repo, runs current mxcli, and can seed the model from a design prototype in the same session — nothing to maintain.
The prompt
This is an empty repo. Provision it as a Mendix app developed with mxcli.
1. Make sure `mxcli` is available. The environment may already have it; if not,
download the prebuilt binary for your OS/arch and put it at `./mxcli`:
```bash
curl -fsSL -o ./mxcli \
https://github.com/mendixlabs/mxcli/releases/download/nightly/mxcli-linux-amd64
chmod +x ./mxcli
```
2. Unpack the skills that ship inside it — this needs no project, so it works in an
empty repo:
```bash
./mxcli init --sync-skills
```
3. Read `.ai-context/skills/bootstrap-app/SKILL.md` and follow it end to end. It begins by
interviewing me about the app, so ask me those questions and wait for my answers
before running anything else.
If I gave you a design to work from, use it as the source of truth for the model and
the pages: <paste or link a design here — otherwise ignore this line>.
That is the whole prompt. The procedure it used to spell out — the interview, the
provisioning steps, the multi-app deltas, the model proposal — now lives in the
bootstrap-app skill, which is embedded in the mxcli binary and unpacked by
step 2. Two things follow: the prompt is short enough to paste from a phone, and the
procedure is fixed by shipping a new mxcli rather than by asking everyone to re-paste
a longer prompt.
What the skill does once it takes over
- Interviews you — one app or a solution, app name, what the app is for, what it
keeps track of, who logs in, theme, Mendix version, and whether you have
requirements to work from. The app name comes first because it becomes the
.mprfile name, the Studio Pro app name and the path baked into the SessionStart hook. - Provisions —
mxcli newinto a subfolder and moves it to the repo root (the root is where.claude/and./mxclimust live),mxcli init --tool claude, thenrun --local --setup --ensure-dbto cache MxBuild + runtime and create the database. - Writes the brief —
README.md(what is being built, in your words) andFINDINGS.md(anything surprising or broken, appended as work proceeds). These are what an idle-reaped session reads to know what it is working on. - Records the plan — the requirements, grouped into deliverable slices, in
docs/brain/plan/. On by default; say so at the interview if you would rather skip it. This exists because a specification in a Word document, a Figma file or a chat window leaves no trace in git — not an issue, not a commit message — so hours of work can end up with nothing recording what it was for. Requirements anchor at what will implement them, somxcli brain planreports progress derived from the model: building the thing moves the number, and there is no status column to maintain. - Commits, then boots and verifies — HTTP 200 at
http://localhost:8080/, plus an optionalrun --hubpreview URL. - Takes the quality baseline —
mxcli lintand the scoredmxcli reporton the blank app, recorded inFINDINGS.md. This is the only moment those numbers mean “what the template ships with”; afterwards every figure is yours plus the template’s, with nothing to subtract. See mxcli report for how to read the six category scores. - Proposes the model in MDL and waits — module, entities, roles, pages — before
building anything. Every script it writes, from the first, starts with
mdl 1;, so the project never holds a headerless (mdl 0) script to upgrade. Two of those choices it makes deliberately rather than by default: a process with steps someone has to act on becomes a workflow (state machine, user-task inbox, timers, a definition the business can read) rather than a status attribute and some microflows, and a total or count across records becomes a view entity — OQL the database executes — rather than a microflow that retrieves every row to produce one number. Both are cheap to choose at the proposal and expensive to retrofit, because the pages, security rules and tests bind to whichever was picked. The same two rules are in the project’s generatedCLAUDE.md, so later sessions apply them without being asked.
For a solution repo it also covers the parts that bite: per-app ports, a hostname per
app so the two apps do not share one cookie jar, the root SessionStart hook that
mxcli init will not write for you, and wiring OData in dependency order.
Which mxcli version gets installed
Prebuilt binaries are the working install path. CI publishes them on every vX.Y.Z
tag (latest is v0.16.0) and as a rolling nightly pre-release, with assets named
mxcli-<os>-<arch>.
nightly— recommended while mxcli is fast-moving alpha. New features (the whole warm-loop surface:run --local,--watch,--ensure-db,--setup, screenshots) land innightlybefore they reach a tagged release, so the bootstrap flow above needs it. Download.../releases/download/nightly/mxcli-<os>-<arch>, or once mxcli is present,mxcli setup mxcli --tag nightly.vX.Y.Z— pin for reproducibility / stability. The CI marks nightly a pre-release (“use tagged releases for production”). Download.../releases/download/vX.Y.Z/mxcli-<os>-<arch>ormxcli setup mxcli --tag vX.Y.Z. With no--tag,mxcli setup mxclimatches the mxcli already running it (nightly →nightly,vX.Y.Z→ that release) — mainly useful for replicating a version onto another OS/arch (e.g. the Linux binary in a Dev Container), not the first install.- Environment pre-install (the robust path) installs whatever the Claude Code Web image bakes in — the way to pin a known-good version fleet-wide.
go install …@latestdoes not work yet. The module is public (tags v0.1.0– v0.16.0), but the generated ANTLR parser (mdl/grammar/parser/) is gitignored and not committed, so ago installfrom the tagged source fails on the missing package. Building from source works only viamake build/make release(which runmake grammarfirst). Enablinggo installwould require committing the generated parser (or generating it during module build) — a maintainer decision.
Which Mendix version to ask for
The skill prefers whatever the session environment already provides, and otherwise the newest version with a published MxBuild.
The environment’s contribution is a cached MxBuild, not a variable — a Claude Code session image may bake one in, and reusing it turns a multi-hundred-MB download into no download at all. The cache directory is the signal:
ls ~/.mxcli/mxbuild/ 2>/dev/null | sort -V | tail -1 # e.g. 11.13.0, or empty
With no cached version, the skill takes the newest on the CDN — everything mxcli does starts with downloading MxBuild, so “supported” means “on the CDN”. At the time of writing that is 11.14.0; treat the number as perishable and run the check rather than quoting it:
V=11.14.0
curl -sI -o /dev/null -w '%{http_code}\n' https://cdn.mendix.com/runtime/mxbuild-$V.tar.gz # 200
curl -sI -o /dev/null -w '%{http_code}\n' https://cdn.mendix.com/runtime/mendix-$V.tar.gz # 200 (runtime)
Both have to answer 200 — run --local needs the runtime tarball as well as
MxBuild. This is also the check to run before bumping the version named in the skill.
In a solution, give every app the same version: they share the ~/.mxcli/mxbuild
cache, and a mismatch means a second multi-hundred-MB download and two runtimes to
keep straight.
Two rules that make this robust
- Committing the config is mandatory (the skill’s provisioning step 7). The prompt
is a one-time seed. Its output —
.mpr+.devcontainer/+.claude/with the SessionStart hook andbootstrap-mxcli.sh— must be committed so the steady state is file-driven and deterministic. After that, every new session runs the hook automatically; you never re-paste the prompt. Miss the script and the hook has nothing to run after a reap. - mxcli delivery is an environment concern, not the prompt’s. The download in
step 1 is the fragile part in a gated web session (a GitHub release
curlmay be blocked), and it is the one thing that cannot move into the skill — the skill is inside the binary. The robust fix is for the Claude Code Web environment image / setup script to pre-install mxcli (and pre-cache MxBuild + runtime);go installviaproxy.golang.orgis the fallback and needs mxcli published as a public Go module.
After bootstrap — the inner loop
./mxcli run --local -p <AppName>.mpr --watch --screenshot # warm dev loop + screenshots
./mxcli exec change.mdl -p <AppName>.mpr # edit the model; the loop hot-applies
The gates
mxcli init writes this list into the project’s CLAUDE.md, so an agent has it in
context in every session without being asked — it is the definition of done, not a
menu. The same list is in the bootstrap-app skill, and the three are held together
by a test, because a gate that is named in two of the three places is a gate that only
runs when someone remembers to ask for it.
They run once per change, not per edit: a change is a coherent unit of work, not a
single statement and not a file write. Iterate with exec, then run the gates once
over the result. That distinction is held by a test too — without it, “definition of
done” reads as the whole list after every edit, which is ~55s and five tool calls each
time, and was the dominant cost in a measured agent session.
./mxcli check change.mdl -p <AppName>.mpr --references # syntax + references (~2s)
./mxcli exec change.mdl -p <AppName>.mpr # apply
./mxcli lint -p <AppName>.mpr # rules (~3s)
./mxcli report -p <AppName>.mpr # scored quality report
./mxcli docker check -p <AppName>.mpr # mxbuild, the slow one (~25s)
./mxcli test tests/ -p <AppName>.mpr --local # microflow tests (~30s cold, ~2s warm)
./mxcli run --local --watch --detach -p <AppName>.mpr # the app, in the background; then `run wait`
Each buys something the one above it cannot: lint and
report score the model that check only proved was well-formed,
test proves behaviour that a clean build does not, and the running app
is the only thing that proves how a page renders. report’s six category scores are
comparable against the baseline taken at bootstrap — a score that fell is a finding.
Writing the first test with the first microflow, rather than later, is what keeps
test from being permanently skipped: after the code is written the expected values
have stopped being obvious. --local needs no Docker daemon, so it works in a web
session.
In a solution, run one loop per app from its own folder, with the second app on the alternate ports, and start the producer first so the consumer’s external entities resolve:
(cd backend && ./mxcli run --local -p Backend.mpr --watch)
(cd frontend && ./mxcli run --local -p Frontend.mpr --watch \
--app-port 8180 --admin-port 8190 --serve-port 6643)
With 127.0.0.1 backend.local frontend.local in /etc/hosts, browse them at
http://backend.local:8080/ and http://frontend.local:8180/ so each app gets its
own cookie jar.
See mxcli run –local for the warm loop, --watch, --ensure-db, and
the screenshot flags.