Iron Proxy Gateway for NanoClaw
nanocoai/nanoclaw
Installs or refreshes Iron Proxy and its Iron Control web console for NanoClaw, with a local Docker setup, database, credentials and a human approval bridge.
Write and run MDL-based microflow tests with mxcli test — annotations, file formats, and the warm local test loop.
$ npx skills add mendixlabs/mxcli --skill test-microflows -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install mendixlabs/mxcli test-microflows --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/mendix/test-microflows .claude/skills/test-microflows && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "test-microflows" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/test-microflows into .claude/skills/test-microflows/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "test-microflows", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/test-microflowsType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add mendixlabs/mxcli --skill test-microflows -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install mendixlabs/mxcli test-microflows --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.claude/skills/mendix/test-microflows .agents/skills/test-microflows && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "test-microflows" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/test-microflows into .agents/skills/test-microflows/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "test-microflows", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add mendixlabs/mxcli --skill test-microflows -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install mendixlabs/mxcli test-microflows --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.claude/skills/mendix/test-microflows .cursor/skills/test-microflows && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "test-microflows" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/test-microflows into .cursor/skills/test-microflows/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "test-microflows", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/mendixlabs/mxcli.git --path .claude/skills/mendix/test-microflows--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add mendixlabs/mxcli --skill test-microflows -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install mendixlabs/mxcli test-microflows --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.claude/skills/mendix/test-microflows .gemini/skills/test-microflows && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "test-microflows" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/test-microflows into .gemini/skills/test-microflows/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "test-microflows", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install mendixlabs/mxcli test-microflowsInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add mendixlabs/mxcli --skill test-microflows -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .github/skills && cp -r skills-src/.claude/skills/mendix/test-microflows .github/skills/test-microflows && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "test-microflows" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/test-microflows into .github/skills/test-microflows/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "test-microflows", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add mendixlabs/mxcli --skill test-microflows -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install mendixlabs/mxcli test-microflows --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.claude/skills/mendix/test-microflows .opencode/skills/test-microflows && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "test-microflows" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/test-microflows into .opencode/skills/test-microflows/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "test-microflows", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
test-microflowsWrite and run MDL-based microflow tests with mxcli test — annotations, file formats, and the warm local test loop.
Test Microflows is an agent skill from mendixlabs/mxcli. Write and run MDL-based microflow tests with mxcli test — annotations, file formats, and the warm local test loop. Use when testing microflow logic rather than the UI: return values, entity changes, control flow.
Its SKILL.md is about 6.4k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files (for example `reference/annotations.md`).
It sits in DevOps & Cloud. It works with Docker. The repository describes itself as: Mendix cli tool, a headless way to work with Mendix projects. Enables Mendix projects for use with 3rd party agentic coding tools like Claude Code and Copilot. Includes a… The licence is Apache-2.0.
7 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit a924d11. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
makeFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
MXCLI_TEST_TOKENFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Test Microflows loads about 6.4k tokens when it runs. Until then it costs about 58 tokens; SKILL.md has 3,275 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from mendixlabs/mxcli at commit a924d11, republished under its Apache-2.0 licence (© mendixlabs). 3,275 words, ~6,416 tokens.
.claude/skills/test-microflows/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.This skill guides you through writing and running MDL-based microflow tests using mxcli test.
reference/annotations.md — every test annotation:
@test, @setup, @teardown, @cleanup, @skip, parameters and expected
values, with what each one does and the exact spelling. Check an annotation
here rather than guessing — an unknown one is a parse error, not a warning.Use this when:
For UI/page testing (widget rendering, form interactions, browser tests), see the test-app skill instead.
--local (no Docker): mxcli boots the runtime itself, the same way
mxcli run --local does. This is the only option in a container without a
Docker daemon, which includes Claude Code web sessions.mxcli docker init -p app.mpr) and the app
buildable (mxcli docker build -p app.mpr).mxcli test tests/ -p app.mpr --local # no daemon needed
mxcli test tests/ -p app.mpr # Docker--local uses its own ports (app 8081, admin 8091) and its own <project>_test
database, so a mxcli run --local dev loop can keep serving the same project
while the tests run — the tests never write into the database you are looking at
in the browser. The database is created on first use.
The deployment directory is shared, and not by choice: mxbuild always writes
it to <app dir>/deployment and has no option to move it. That used to blank the
running app — a headless test boot does not bundle the web client, and the boot's
packaging pass deletes deployment/web/dist, so the app answered HTTP 200 with
a blank page (Mendix's SPA shell over a 404 for /dist/index.js) while the
tests passed and nothing was reported at either end. The bundle is now copied
aside before the boot and put back after, so the dev loop keeps the exact bundle
it built.
The compiled Java is shared too, and that one is not fixable — only reportable.
A test run recompiles the project into deployment/run/bin, the classpath the
running app's JVM is holding open. Measured: every class file is rewritten (new
inode, identical content). A JVM loads classes lazily, so one the app has not
reached yet can afterwards fail with NoClassDefFoundError — and the microflows
behind it then answer HTTP 200 with an empty body rather than an error, so the
app looks half-working. Only Java-backed resources are affected, which is why it
does not look like the test run did it.
mxcli warns when it sees a dev loop serving the same project (run --local
records itself in .mxcli/run-local.json, removed on exit). If something the
app serves stops returning data after a test run, restart that app. That is the
whole remedy.
One thing left that has not been measured: both runtimes share deployment/data/.
No damage observed; if you see something odd, run them one at a time and say so.
A --local run boots the app with the same constant values mxcli run --local
uses: the project configuration's shared overrides, layered over each
constant's default. It prints what it applied before the run:
Applying 1 constant value(s):
MyModule.ApiKey configuration "Default"Pass --configuration <name> to pick one when the project has several and none
is called Default (it refuses to guess rather than run production's values by
accident). --attach takes neither flag: it runs against an app someone else
booted and inherits that app's constants.
To set a value for one run without touching the project, use --constant
(repeatable). It wins over the configuration and is never written to the model:
mxcli test tests/ -p app.mpr --local --constant MyModule.ApiKey=sk-test-123A name the project does not declare is refused, before anything boots — the runtime silently ignores a value for a constant that does not exist, so a typo would otherwise be reported as applied and do nothing.
The value is visible in shell history and in ps. That is fine for a throwaway
test value and wrong for a real secret.
This is worth knowing when a test asserts on something a constant feeds. Before
this was wired up, --local ran with each constant's default while --attach
ran with the configuration's, so the same suite could pass one way and fail the
other with nothing in the output to explain it.
For a secret that has to persist across runs, use the machine store:
mxcli constant set MyModule.ApiKey 'sk-live-...' -p app.mpr
mxcli constant list -p app.mpr # values from the store are masked
mxcli constant unset MyModule.ApiKey -p app.mprIt writes <project>/.mxcli/constants.json (mode 0600), adds .mxcli/ to the
project's .gitignore if missing, and then asks git whether the path is
really ignored — refusing to write the value if it is not. It beats the
configuration and loses to --constant.
By default the new value takes effect at the next boot. Add --apply to push it
into a mxcli run --local that is already up, without restarting it:
mxcli constant set MyModule.ApiKey 'sk-live-...' -p app.mpr --applyThat is two admin calls, not one: update_configuration is staged — the
running app keeps its old values and the call still answers success — and only
the following reload_model applies them. mxcli does both. It cannot confirm
the result, because the admin API has no way to read a constant back, so it says
so and points you at the app itself.
This is mxcli's own store, not Mendix's. Mendix's private configuration values
are encrypted per user account by Studio Pro from 10.9, so nothing headless can
read or write them. See docs/11-proposals/PROPOSAL_constant_values.md.
.test.mdl — Pure MDL TestsTest blocks separated by /, each with a javadoc comment containing test annotations:
/**
* @test String concatenation
* @expect $result = 'John Doe'
*/
$result = call microflow MyModule.ConcatNames(
FirstName = 'John', LastName = 'Doe'
);
/
/**
* @test Arithmetic operation
* @expect $result = 50
*/
$result = call microflow MyModule.Multiply(A = 10, B = 5);
/A file may open with a header comment, in either spelling — a /** … */
block or -- lines — and it does not become part of the first test. The test's
own doc comment is the last one above its statements. A /** … */ header may
carry @setup, which then applies to every test in the file.
One @test per block. The / is what ends a test, so leaving it out puts
two tests in one block; that is refused by name rather than resolved, because
either way of resolving it runs one of the two and silently drops the other.
Both rules are #927: a file-level header used to swallow the first test whole —
it disappeared from the results with no error, and every later test reported
under the number of the one above it.
.test.md — Markdown SpecificationTests embedded in documentation as mdl-test fenced code blocks:
# MyModule Specification
## string Operations
The ConcatNames microflow joins first and last name.
```mdl-test
/** @expect $result = 'John Doe' */
$result = call microflow MyModule.ConcatNames(
FirstName = 'John', LastName = 'Doe'
);
```The markdown format turns your tests into living documentation.
# run tests from a file
mxcli test tests/microflows.test.mdl -p app.mpr
# run all tests in a directory
mxcli test tests/ -p app.mpr
# list tests without executing
mxcli test tests/ -p app.mpr --list
# Output JUnit xml for CI
mxcli test tests/ -p app.mpr --junit results.xml
# Skip build (reuse existing deployment)
mxcli test tests/ -p app.mpr --skip-build
# Verbose output (show all runtime logs)
mxcli test tests/ -p app.mpr --verboseThere are two mechanisms. --local uses the test endpoint; Docker uses the
older after-startup microflow pattern.
--local: the test endpointMxTest
module already existsMxTest.Test_<id> microflow per test, plus a Java action
that registers an HTTP endpoint, and points after-startup at a microflow that
registers it and then chains your own after-startup microflow —
no test runs during startupTwo consequences worth knowing when reading a failing run:
ERROR with the
root-cause message, and the next test still runs. Under the after-startup
mechanism an uncaught error ends the whole flow — and because that flow is
the startup action, it also fails the boot.Each test is a separate microflow with its own variable scope, so $result in
one test never collides with $result in another.
The generated startup flow registers the endpoint and then calls the project's own after-startup microflow, so tests see the app in the state it actually boots into — a loaded cache, seeded reference data, whatever your app does. The run says which happened:
After-startup set to MxTest.RegisterEndpoint (registers the endpoint; runs no tests, then runs your MyModule.ASU_Startup)Pass --skip-app-startup when you want an empty, deterministic baseline
instead — the app seeds demo data and your tests assert on counts, say:
After-startup set to MxTest.RegisterEndpoint (… --skip-app-startup, so MyModule.ASU_Startup will NOT run)This is why a suite behaves the same under --local and --attach. Before it
chained, --local ran with the app's startup logic suppressed, and a suite that
depended on startup state passed under --attach and failed under --local for
reasons unrelated to the code.
One thing rollback does not cover: whatever the startup microflow writes
happens at boot, outside any test's transaction, so @cleanup rollback does not
undo it. Under --local that lands in the scratch <project>_test database;
under --attach your app wrote it at its own boot regardless.
--watch: keep the runtime warmmxcli test tests/ -p app.mpr --local --watchThe first run pays the cold boot; after that the runtime and the build server stay up, and the suite re-runs on every change — to a test file or to the project's model. Measured on an 11.13.0 app:
| First run (cold boot) | ~30s |
| Edit a test → verdict on screen | ~2s |
| Edit a microflow → verdict on screen | ~2s |
| The tests themselves | 20–70ms |
Editing a microflow and seeing straight away whether it still passes is the loop
this exists for. Ctrl-C stops watching and restores the project — the shutdown
prints project restored when it has.
Adding, editing and deleting tests all work mid-session: the suite is re-parsed on every change, and a deleted test's microflow is dropped rather than left behind reporting a stale pass.
--watch requires --local. The Docker and --legacy-runner paths can only
re-run tests by restarting, which is the thing being avoided.
--attach: no boot at allIf you already have the app running, tests can skip the boot entirely. The dev loop has to opt into hosting the endpoint, because the handler is registered by the after-startup microflow and so cannot be added to an app that is already up:
# terminal 1 — the app you are working in
mxcli run --local --test-endpoint -p app.mpr
# terminal 2 — runs in ~2s, no boot, repeatable
mxcli test tests/ -p app.mpr --attach
mxcli test tests/ -p app.mpr --attach --watch # ...and re-run on every changeThe hosting app chains your project's own after-startup microflow rather than
displacing it, so it still boots normally. The endpoint and the handshake file
(.mxcli/test-endpoint.json, mode 0600) are removed when the app stops.
Three things to know before reaching for it:
--local uses a separate
<project>_test database; --attach does not.--attach.| Boot | Database | Owns the runtime | |
|---|---|---|---|
--local | ~30s each run | <project>_test | yes |
--local --watch | ~30s once, then ~2s | <project>_test | yes |
--attach | none | the running app's | no |
It executes microflows under a system context, so it is gated four ways:
| Guard | Behaviour |
|---|---|
No MXCLI_TEST_TOKEN in the runtime's environment | The handler is not registered at all (404) |
Missing or wrong X-MxTest-Token header | 401, compared in constant time |
| Non-loopback caller | 403 |
mf outside MxTest.Test_* | 403 — it is not a general microflow-invocation API |
The token is generated per run and reaches the runtime through its environment,
never written into the project. Combined with fail-closed registration, that means
a project which kept the MxTest module through a failed cleanup exposes nothing
when deployed anywhere else.
MxTest
module already existsMxTest.TestRunner microflow containing every test, and
points after-startup at itMXTEST: log lines for pass/failMxTest module when the runner created it, otherwise just the
TestRunner microflowThe project's Security Level is not modified. The after-startup microflow runs
in an administrative context and is not subject to it, and forcing it off breaks
projects whose published REST/OData services use custom authentication. If a
cleanup step fails the run reports an error, names every generated document
still in the project and prints the DROP that removes it — the project is
modified, so it must not read as a clean pass.
Cleanup removes every generated MxTest.Test_* microflow the project holds,
not only the ones this run created. The names are positional (Test_test_1,
_2, … from the test's index in its file) and every test file reuses them, so
keying cleanup on the current suite left a flow behind whenever a later run had
fewer tests than an earlier one — and a leftover that does not build fails
every subsequent run of every test file, with a message about the project
rather than about any test (mendixlabs/mxcli#1104).
mxcli check suite.test.mdl works, and is much faster than a run. A test block
is a microflow body, and check renders it as the microflow it becomes, on
the file's own lines — so a diagnostic points at the statement you wrote.
That includes the semantic rules, which is where most of the value is: a test
whose body would not compile is reported here instead of failing the injection
with nothing but "the project cannot be deployed". An @expect or @verify that
cannot be evaluated is reported here too, as MDL-TEST01.
One rule to know about, because its symptom is confusing and its shape is common
in tests: retrieve $x … first — and retrieve $x … limit 1 in a script without
the mdl 1; header — binds a single object, not a one-element list, so
head($x) is CE0097 at build time and MDL-RETRIEVE01 at check time. Drop the
range to get a list, or use the variable as the object it is.
mxcli fmt --upgrade -w suite.test.mdl reads the file the same way: it rewrites
deprecated spellings in the blocks and keeps every doc comment, -- comment and
/ separator byte for byte. A test file takes no mdl 1; header yet — check
and the runner read its blocks as mdl 0 — so --header adds none to it and says so.
Each test block should test one thing:
/**
* @test Discount applied for orders over 100
* @expect $result = 90.0
*/
$result = call microflow Sales.CalculateDiscount(OrderTotal = 100.0);
/Use separate blocks for different input values:
/**
* @test Negative value returns 'negative'
* @expect $result = 'negative'
*/
$result = call microflow MyModule.Classify(value = -5);
/
/**
* @test Zero returns 'zero'
* @expect $result = 'zero'
*/
$result = call microflow MyModule.Classify(value = 0);
/
/**
* @test Positive value returns 'positive'
* @expect $result = 'positive'
*/
$result = call microflow MyModule.Classify(value = 42);
/Tests can create, modify, and verify entities:
/**
* @test Create and update product
* @expect $updated = true
*/
$product = call microflow Sales.CreateProduct(
Name = 'Widget', Code = 'W-001'
);
commit $product;
$updated = call microflow Sales.UpdateProduct(
Product = $product, NewName = 'Super Widget'
);
/Use @throws to verify that a microflow raises an error:
/**
* @test Invalid input throws validation error
* @throws 'Validation failed'
*/
call microflow Sales.ValidateOrder(Total = -1);
/The message is matched as a substring of what Mendix raised, so name the part
you can predict — a real message often carries an activity name or an object id.
A failing @throws reports both sides:
FAIL Invalid input throws validation error (6ms, 1 assertion)
expected an error containing 'Validation failed',
actual: Could not find object of type 'Sales.Order'Write @throws on its own when the message is not the thing under test:
/**
* @test Invalid input is rejected
* @throws
*/
call microflow Sales.ValidateOrder(Total = -1);
/Anything else after the tag is an error, not a message: @throws not found is
refused rather than read as an unquoted one.
@throws and @expect cannot be combined. A @throws test compiles to a
different shape — the verdict starts as a failure and only the error handler
clears it — and the @expect checks are not emitted into it at all, so the
assertion was counted and never evaluated. There is nothing to assert on either
way: the body was expected not to produce a result. Assert on the error with
@throws, or on the result with @expect.
Recommended structure:
tests/
├── microflows.test.mdl # business logic tests
├── entities.test.mdl # entity CRUD tests
├── validation.test.mdl # validation tests
└── specs/
└── sales-module.test.md # Markdown specification| Failure | Cause | Fix |
|---|---|---|
Exception during execution | Microflow threw a runtime error | Check BSON structure, entity references, attribute types |
Expected $result = 'X' but got 'Y' | Wrong return value | Fix microflow logic |
Test was not executed | Runtime crashed before reaching it | Check earlier test failures or runtime logs |
after startup microflow should return a boolean | Generated runner has wrong return type | Report as bug in mxcli |
Use --junit to produce JUnit XML for CI systems:
mxcli test tests/ -p app.mpr --junit test-results.xmlThe JUnit XML works with GitHub Actions, Jenkins, Azure DevOps, GitLab CI, etc.
# GitHub actions example
- name: run microflow tests
run: mxcli test tests/ -p app.mpr --junit test-results.xml
- name: publish test results
uses: dorny/test-reporter@v1
with:
name: microflow Tests
path: test-results.xml
reporter: java-junitmxcli test --local [--watch], --attach, run --local --test-endpoint)local test runs go through a token-guarded HTTP endpoint registered by a generated Java custom request handler, instead of compiling the suite into the project's after-startup microflow. Boot registers the endpoint and then chains the project's own after-startup microflow, so tests see the app as it really boots (--skip-app-startup opts out) — without that, a suite depending on startup state passed under --attach and failed under --local. One microflow per test, resolved by name at request time from Core.getMicroflowNames() and invoked with Core.microflowCall(...).execute(...) — so a throwing test fails only itself (not the boot), results are returned rather than scraped from the runtime log, and each test has its own variable scope. Owning the IContext is also what finally makes @cleanup rollback (the annotation's documented default, previously parsed and ignored) real: the handler wraps the call in startTransaction()/rollbackTransaction(), so a test's writes do not survive it — verified against Postgres, with @cleanup none as the in-run control. A rollback that fails is reported per test and summarised, never silent; an unknown strategy is a parse error. The handler survives reload_model (after-startup does not re-run, the JVM is unchanged), which is what makes --watch possible: ~30s first run, then ~2s from an edit — to a test or to the microflow under test — to a verdict. --attach skips even that boot by running against an app already up under run --local --test-endpoint, driving that process's serve + admin APIs over loopback; it uses that app's database, only ever adds/removes its own test microflows, and refuses a change needing a restart. Security: the handler is not registered at all without MXCLI_TEST_TOKEN in the runtime env (so a project that kept the MxTest module through a failed cleanup is inert in production), the token is constant-time compared, non-loopback callers are refused, /list is clamped to the test namespace, and only MxTest.Test_* may be invoked. The token reaches the runtime via its environment and is never written into the project. A .test.mdl file is checkable: each block is a microflow body, so mxcli check (and the LSP, hence VS Code) renders the blocks as the microflows they become, on the file's own lines — before #1103 the top-level grammar was applied instead, and since RETRIEVE is a non-reserved keyword the leftover FROM … started an OQL query, so the reader was told their retrieve needed a SELECT; 9 of this repo's 10 test files reported errors that way, one of them 392. make check-mdl now sweeps them, with .fail.test.mdl for a file whose annotations are deliberately unusable. Two things a failed run must not do, both reported as #1104: a rejected build is reported with MxBuild's own errors — BuildResult.ErrorSummary() was in hand and discarded by fmt.Errorf("build failed: %s", build.Message) on the --attach and rebuild paths, and that sentence is identical for every failing build, so it could not tell "your test does not compile" from "an unrelated document is broken"; and cleanup removes every generated MxTest.Test_* the project holds, not just this suite's. The names are positional and every file reuses them, so keying cleanup on the suite left a flow behind whenever a later run had fewer tests than an earlier one — and one leftover that does not build fails every later run of every test file. What cleanup could not remove is named, with the DROP that removes it. Docker keeps the after-startup runner (--legacy-runner selects it locally). Packages: cmd/mxcli/testrunner/ (endpoint.go, client.go, watch.go, host.go, handshake.go, check_source.go, cleanup_leftovers.go). See docs/15-testing/SPIKE_test_endpoint_request_handler.md
© mendixlabs, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 1 other file in .claude/skills/mendix/test-microflows of mendixlabs/mxcli.
Open the folder on GitHubat commit a924d11
Test Microflows next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Test Microflows this skillmendixlabs/mxcli | 128 | — | ~6.4k | Automated safety check: Pass | Apache-2.0 | |
| Iron Proxy Gateway for NanoClawnanocoai/nanoclaw | 31k | — | ~4.6k | Automated safety check: Notes | MIT | |
| GreptimeDB Dev Docker ImageGreptimeTeam/greptimedb | 6.7k | — | ~4k | Automated safety check: Notes | Apache-2.0 | |
| Senior DevOps Toolkitmaslennikov-ig/claude-code-orchestrator-kit | 259 | 6 repos | ~1.1k | Automated safety check: Notes | Custom licence | |
| LangBot Deployment Guidelangbot-app/LangBot | 18k | — | ~1.2k | Automated safety check: Notes | Apache-2.0 | |
| Build Openshell Mxc WindowsNVIDIA/OpenShell | 15k | — | ~4.9k | Automated safety check: Pass | Apache-2.0 |
nanocoai/nanoclaw
Installs or refreshes Iron Proxy and its Iron Control web console for NanoClaw, with a local Docker setup, database, credentials and a human approval bridge.
GreptimeTeam/greptimedb
Packages a locally built GreptimeDB debug binary into a development-only Docker image for local-cluster testing, with an optional push to a dev registry.
maslennikov-ig/claude-code-orchestrator-kit
Comprehensive DevOps skill for CI/CD, infrastructure automation, containerization, and cloud platforms (AWS, GCP, Azure). Includes pipeline setup…
langbot-app/LangBot
Deploys and configures a LangBot instance with Docker Compose or Kubernetes, covering config.yaml, the Box sandbox runtime, the plugin runtime and the global API key.
NVIDIA/OpenShell
Maintain and validate OpenShell's build-only Windows MSVC lane for x64 and ARM64.
yansongda/pay
A skill your agent uses when local PHP environment is unavailable.
mendixlabs/mxcli
Push OData query options into the SQL of a Mendix resource served by a read microflow, so $filter, $orderby, $top, $skip, $count and the key lookup reach the database instead of being silently…
mendixlabs/mxcli
Chart a Mendix app with Vega-Lite through a pluggable widget that takes the specification and the data as separate properties, so the model emits rows and never assembles a chart payload.
mendixlabs/mxcli
Author Mendix AI agent documents in MDL — Model, Knowledge Base, Consumed MCP Service and Agent, with variables, tools and multi-line prompts.
mendixlabs/mxcli
Run set-based INSERT, UPDATE and DELETE against Mendix entities through OQL statements, which the runtime supports and Studio Pro cannot author.
mendixlabs/mxcli
Stand up an HTTP endpoint you control instead of a live third-party API, and point the Mendix app at it — Prism from an OpenAPI contract, a constant swap, or a forward proxy.
mendixlabs/mxcli
Call external REST APIs from Mendix — the three approaches (inline REST CALL, consumed REST client document, generated from OpenAPI) and how to choose.
Works with
Categories
Write and run MDL-based microflow tests with mxcli test — annotations, file formats, and the warm local test loop. Test Microflows is an agent skill from mendixlabs/mxcli. Write and run MDL-based microflow tests with mxcli test — annotations, file formats, and the warm local test loop.
Test Microflows fits situations like: testing microflow logic rather than the UI: return values.
Run `npx skills add mendixlabs/mxcli --skill test-microflows -a claude-code`. Or copy the skill folder (.claude/skills/mendix/test-microflows in mendixlabs/mxcli) into .claude/skills/test-microflows in your project. Claude Code loads it when a task matches its description.
Run `npx skills add mendixlabs/mxcli --skill test-microflows -a codex`. Or copy the skill folder (.claude/skills/mendix/test-microflows in mendixlabs/mxcli) into .agents/skills/test-microflows in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add mendixlabs/mxcli --skill test-microflows -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/test-microflows, .gemini/skills/test-microflows, .github/skills/test-microflows and .opencode/skills/test-microflows in your project.
Going by SKILL.md and its folder, Test Microflows needs the command-line tools its instructions call (make) and credentials named MXCLI_TEST_TOKEN. Our summary lists: Docker.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Test Microflows is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.4k tokens (SKILL.md is roughly 26k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Test Microflows: Iron Proxy Gateway for NanoClaw (nanocoai/nanoclaw, 31k stars), GreptimeDB Dev Docker Image (GreptimeTeam/greptimedb, 6.7k stars), Senior DevOps Toolkit (maslennikov-ig/claude-code-orchestrator-kit, 259 stars) and LangBot Deployment Guide (langbot-app/LangBot, 18k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
mendixlabs (a GitHub organization) maintains it in mendixlabs/mxcli, which has 128 GitHub stars. The repository holds 75 skills in this directory. The repository was last updated on October 7, 2026.
Source: mendixlabs/mxcli on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.