Install the "write-microflows" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/write-microflows into .claude/skills/write-microflows/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-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.
Type 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.
skills CLI
$ npx skills add mendixlabs/mxcli --skill write-microflows -a codex
Project install goes to .agents/skills/; add -g for ~/.codex/skills/.
Install the "write-microflows" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/write-microflows into .agents/skills/write-microflows/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-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.
skills CLI
$ npx skills add mendixlabs/mxcli --skill write-microflows -a cursor
Project install goes to .agents/skills/; add -g for ~/.cursor/skills/.
Install the "write-microflows" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/write-microflows into .cursor/skills/write-microflows/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-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.
--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
skills CLI
$ npx skills add mendixlabs/mxcli --skill write-microflows -a gemini-cli
Project install goes to .agents/skills/; add -g for ~/.gemini/skills/.
Install the "write-microflows" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/write-microflows into .gemini/skills/write-microflows/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-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.
Installs 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).
skills CLI
$ npx skills add mendixlabs/mxcli --skill write-microflows -a github-copilot
Project install goes to .agents/skills/; add -g for ~/.copilot/skills/.
Install the "write-microflows" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/write-microflows into .github/skills/write-microflows/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-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.
skills CLI
$ npx skills add mendixlabs/mxcli --skill write-microflows -a opencode
OpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
Install the "write-microflows" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/write-microflows into .opencode/skills/write-microflows/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-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.
Facts
Skill name
write-microflows
GitHub stars
128
Token cost
~7.1k tokens
SKILL.md length
2,534 words
Files
5
Skills in repo
75
Repo updated
First seen
Licence
Apache-2.0
At a glance
Microflow syntax reference in MDL — every activity type, control flow, expressions, and the mistakes that fail mxcli check.
Works in 9 steps: Always use fully qualified names:… → Test incrementally: Create simple… → Check entity definitions: Ensure all… → …
Development work in your project
SKILL.md covers Reference files, When to Use This Skill, Changing an Existing Microflow and When to Use a Microflow vs a…, plus 9 more sections
Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md
What it does
Write Microflows is an agent skill from mendixlabs/mxcli. Microflow syntax reference in MDL — every activity type, control flow, expressions, and the mistakes that fail mxcli check. Use before writing any CREATE MICROFLOW, and when debugging a microflow syntax error.
Its SKILL.md is about 7.1k tokens, which your agent loads only when the skill is triggered. The skill folder holds 5 other files (for example `reference/control-flow.md`, `reference/data-operations.md` and `reference/integration.md`).
It sits in Development. 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.
When your agent uses it
Development work in your project
Example prompts
“/write-microflows”
Workflow steps
9 steps, taken from the first numbered list in SKILL.md.
1Always use fully qualified names: Module.Entity, Module.Association
2Test incrementally: Create simple microflows first, then add complexity
3Check entity definitions: Ensure all attributes exist before referencing
4Use meaningful variable names: $Customer not $c, $ProductList not $list
5Comment complex logic: Use -- for inline comments
6Log important events: Help with debugging and auditing
7Handle empty cases: Check for = empty before using objects
8Use WITHOUT EVENTS appropriately: Only when handlers must be skipped (events are on by default)
9Validate before executing: Use mxcli check script.mdl -p app.mpr --references to catch errors
What it can do on your machine
Read from SKILL.md and the folder at commit a924d11. It shows what the files ask for, not the result of running them.
Tool permissions
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.
Runs code
No scripts in the folder and no shell commands in SKILL.md (its code samples are mdl).
From the folder's file list and the shell code blocks in SKILL.md.
Network
Links to these hosts (documentation or services it may open):
docs.mendix.com
From URLs in SKILL.md, links to its own repository left out.
Credentials
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Context cost
Write Microflows loads about 7.1k tokens when it runs. Until then it costs about 57 tokens; SKILL.md has 2,534 words of instructions outside code blocks.
Always· name and description, kept in context so the agent knows when to use it
~57
When it runs· the whole SKILL.md, loaded when a task matches
~7.1k
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.
Safety
Auto-check passed
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.
Download SKILL.mdSave it as .claude/skills/write-microflows/SKILL.md (or your agent's skills folder). This skill also uses 4 other files; get the full folder from GitHub.
name
write-microflows
description
Microflow syntax reference in MDL — every activity type, control flow, expressions, and the mistakes that fail `mxcli check`. Use before writing any CREATE MICROFLOW, and when debugging a microflow syntax error.
Mendix Microflow Skill
Reference files
SKILL.md covers the shape of a microflow and the decisions. The detail lives beside it:
reference/control-flow.md — every form of if,
case, split type, loop and while, plus error handling (try/custom
handlers) and the @position / @merge / @anchor activity annotations.
reference/data-operations.md — create, change,
commit, delete; list operations and aggregates; retrieve from database,
association and list; XPath navigation.
reference/integration.md — rest call and
send rest request, legacy SOAP, calling Java actions (including the empty
argument and microflow-typed parameters), execute database query, file downloads.
reference/pitfalls.md — read this when
mxcli check rejects something you believe is correct. The anti-patterns, the
CE0111 duplicate-variable trap, the syntax that looks plausible and does not
parse, and the Studio Pro errors each one produces.
When to Use This Skill
Use this skill when:
Writing CREATE MICROFLOW statements
Debugging microflow syntax errors
Converting Studio Pro microflows to MDL
Understanding microflow control flow and structure
If you're not sure whether the logic belongs in a microflow or a nanoflow, read the next section first. The mirror lives in write-nanoflows — keep both copies in sync.
Created by your MDL scripts, and not edited in Studio Pro since: edit the script
(or fresh describe output) and re-run create or modify.
Authored in Studio Pro: prefer alter microflow X { insert/replace/drop … }
(targets from describe microflow X with handles). create or modify of describe
output patches too: unchanged writes nothing; a statement (even one whose handler returns), guard
clause, return value, if condition, header clause, parameter (added/retyped; removed only if unused) or stated @position/@start change is
patched in place (a move keeps the node's flows). A redrawn @anchor/@curve, loop body,
error handler or other return added/taken away rebuilds under mdl 0 (MDL-V1-REBUILD: IDs
renumbered, merges and curves lost) and is refused under mdl 1;. To change a loop body, alter … replace the whole loop — neither mode edits inside one (pitfalls). To rebuild deliberately, drop and create in ONE script — the grants carry only within it (pitfalls).
When to Use a Microflow vs a Nanoflow
Scenario
Use
Querying the database
Microflow
Calling REST services or external actions, or sending email (send email, 11.13+)
Rule of thumb: A nanoflow runs before the server call. A microflow IS the server call.
Key Differences from Nanoflows
Aspect
Microflow
Nanoflow
Execution
Server-side
Client-side (browser/mobile)
Database access
Full
No direct access
Transactions
Supported
Not supported
Java actions
Supported
Not supported
JavaScript actions
Not supported
Supported
SYNCHRONIZE
Not available
Available (offline sync)
File downloads
Supported
Not supported
Error handling
Full ON ERROR blocks; RAISE ERRORinside a handler only (main flow = MDL084 / CE0710)
Per-action ON ERROR supported; RAISE ERROR / ErrorEvent forbidden
Offline
Not available
Available
Binary return type
Supported
Not supported
For nanoflow-specific authoring guidance, see write-nanoflows.
Microflow Structure
CRITICAL: All microflows MUST have JavaDoc-style documentation
mdl
/**
* Microflow description explaining what it does
*
* Detailed explanation of the business logic, use cases,
* and any important implementation notes.
*
* @param $Parameter1 Description of first parameter
* @param $Parameter2 Description of second parameter
* @returns Description of return value
* @since 1.0.0
* @author Team Name
*/
create microflow Module.MicroflowName (
$Parameter1: type,
$Parameter2: type
)
returns ReturnType as $ReturnVariable
[folder 'FolderPath']
begin
-- Microflow logic here
return $ReturnVariable;
end;
exposed as … action — putting a microflow in the toolbox
Studio Pro can show a microflow in its toolbox, so whoever drags it onto a flow
does not need to know it is a microflow rather than a Java action. A microflow
has two such entries — one for the microflow editor, one for the workflow
editor — so the clause names which:
mdl
mdl 1;
create microflow Module.FormatCode ($Raw: String)
returns String
exposed as microflow action 'Format code' in 'Toolbox demo'
icon 'assets/format-64.png'
icon dark 'assets/format-64-dark.png'
image 'assets/format-256.png'
exposed as workflow action 'Format code' in 'Toolbox demo'
begin
return trim($Raw);
end;
The icon should be a 64x64 PNG and the image 256x192; paths resolve
against the directory of the .mdl file, so a script and its artwork travel
together. A different size is written with a warning; a
file that is not a PNG is refused.
An omitted clause preserves what is stored — it does not clear it:
You write
What happens
exposed as microflow action 'X' in 'Y'
Caption and category from MDL; icon and image carried from what was stored
(no clause)
Both entries preserved, bitmaps included
not exposed as workflow action
Removes that one entry; the microflow entry is untouched
… drop icon dark
Clears one bitmap; the other three are untouched
Rewriting a microflow's body must not cost it the icon a designer set in Studio
Pro, and saying nothing about the toolbox is not asking to be taken out of it.
Nothing below Studio Pro sees the difference — mx check reports 0 errors
either way — so the preserve rule is the only thing protecting it.
Nanoflows and rules cannot be exposed. Only Microflows$Microflow stores a
toolbox entry; the clause is refused on the other two with a message naming the
alternative. Java and JavaScript actions have one entry each and use the shorter
exposed as 'Caption' in 'Category' — see the java-actions skill.
@excluded — documents excluded from the project
@excluded before a create microflow marks the document "Exclude from project"
(the same checkbox Studio Pro offers). The document stays in the .mpr, does not
build, and list microflows reports it in the Excluded column.
The sibling document annotation is @applyentityaccess — runs the flow under the current user's entity access rules rather than with full access, with the same absent-preserves rule and an explicit (false) to turn it off (pitfalls).
Two rules follow, and both are enforced rather than documented-and-hoped:
An absent @excluded never un-excludes. It means "the script does not say",
not "make this active" — so re-running a create or modify you wrote before the
document was excluded leaves the exclusion alone. Un-exclude in Studio Pro.
(Before #914 the rewrite cleared it, which is how a valid project ended up
failing CE0122 — see the next rule.)
A name is not unique when a twin is excluded. Mendix allows two documents of
the same name in one module as long as at most one is active — verified on
11.13.0: the excluded pair builds at 0 errors, the same pair both active is
[error] CE0122 "Duplicate document name". create or modify, describe and
the other by-name lookups therefore target the live document; the excluded
twin is neither rewritten nor deleted.
The same applies to every document type that carries the flag — nanoflows, pages,
snippets, enumerations, queues, workflows, Java/JavaScript actions, mappings, JSON
structures, REST/OData services, image collections and the agent documents.
FOLDER Option
Place microflows in folders for organization:
mdl
mdl 1;
create microflow MyModule.ACT_ProcessOrder ($Order: MyModule.Order)
returns boolean as $success
folder 'Orders/Processing'
begin
-- logic
return true;
end;
Key Rules:
Parameters start with $ prefix
Return variable must be declared or used
Every microflow must end with return statement
Every body statement ends with a semicolon ; — required, not optional. This
includes block terminators: end if;, end loop;, end while;, end case;.
A missing one is a parse error (missing ';' at 'return'), not a warning.
You cannot declare an object (entity) variable.declare becomes a
Create Variable activity, which Mendix only allows to hold primitive types
(String, Integer/Long, Decimal, Boolean, DateTime, Enumeration). An object type
is rejected by Studio Pro/mxbuild with CE0053 ("Selected type is not allowed"),
plus CE0038 ("Value required") and CE7247 on any following set — whether or
not you give it an initializer. mxcli check now flags it as MDL043. There
is no "empty object variable" activity. Get objects from one of these:
a microflow parameter: create microflow M.Save ($Product: Test.Product) ...
a retrieve: retrieve $Product from Test.Product where Code = $c first;
a create object: $Product = create Test.Product (Name = $n);
a loop iterator: loop $Product in $Products ...
You cannot declare a list either. Same Create Variable restriction —
Studio Pro rejects a list with CE0053/CE0038, and mxcli check flags it as
MDL040. Get lists from:
a microflow parameter: create microflow M.Process ($Items: list of Test.Product) ...
a retrieve: retrieve $Products from Test.Product where IsActive = true;
a create list: $Products = create list of Test.Product;
Decimal values into an integer/long fail CE0117. Integer division
($a div $b) is always a Decimal even for two integers, and so are random()
and the duration *Between functions (secondsBetween, minutesBetween,
hoursBetween, daysBetween, weeksBetween). Assigning any of them straight
to an integer/long variable fails mx check with CE0117 (mxcli check
now flags it as MDL041). Either declare the target decimal, or round it:
mdl
declare $Avg decimal = $Total div $Count; -- ✅ Decimal target
declare $Whole integer = round($Total div $Count); -- ✅ rounded to Integer
declare $Secs integer = round(secondsBetween($a,$b)); -- ✅ rounded to Integer
declare $Bad integer = $Total div $Count; -- ❌ CE0117 / MDL041
The calendar*Between functions (calendarMonthsBetween, calendarYearsBetween)
return whole units (Integer) and are fine to assign directly.
Changing a variable: always set
set $Counter = $Counter + 1; changes a variable. $x = … without set is an
activity that creates $x — required under mdl 1; (MDL-V1-SET otherwise).
List operations and aggregates are one statement per activity and never nest:
$Open = filter $Orders by Status = M.Status.Open; then $N = count $Open; —
see reference/data-operations.md.
❌ INCORRECT Syntax
mdl
-- WRONG: Declaring an object/entity variable (CE0053/CE0038, MDL043)
declare $Product Test.Product; -- bare object declare is invalid
declare $Product Test.Product = $someObj; -- initialized object declare is also invalid
-- WRONG: Declaring a list variable (CE0053/CE0038, MDL040)
declare $ProductList list of Test.Product = empty; -- use a parameter, retrieve, or create list
-- WRONG: Using AS keyword (not supported in mxcli)
declare $Product as Test.Product; -- ERROR: parse error
-- WRONG: No value (CE0038, MDL061)
declare $X string; -- a Create Variable activity requires a value
-- WRONG: Missing type
declare $Counter = 0; -- Type inference not always supported
-- WRONG: Using 'OF' instead of 'of'
declare $list list of Test.Product; -- Case sensitive
Operators
Arithmetic
mdl
set $Result = $A + $B; -- Addition
set $Result = $A - $B; -- Subtraction
set $Result = $A * $B; -- Multiplication
set $Result = $A div $B; -- Division (use 'div', not '/')
Important: Use div for division, NOT /. In a Mendix expression / is the
member/association separator ($obj/Attr), so $A / $B is not division —
mxcli check rejects it as MDL045 (it would fail the build with CE0117).
Integer/decimal division always yields a Decimal; wrap it in round()/trunc()
for an Integer result (else MDL041).
Comparison
mdl
$A = $B -- Equals
$A != $B -- Not equals
$A > $B -- Greater than
$A >= $B -- Greater than or equal
$A < $B -- Less than
$A <= $B -- Less than or equal
$A = empty -- Check if empty/null
$A != empty -- Check if not empty
Boolean Logic
mdl
set $Result = $A and $B; -- Logical AND
set $Result = $A or $B; -- Logical OR
set $Result = not $A; -- Logical NOT
-- Complex expressions
if $IsActive and $IsValid and $HasStock then
set $CanProcess = true;
end if;
Show full SKILL.md (1,028 more words)Show less
Date construction
dateTime(...) / dateTimeUTC(...) build a date from literal numeric
constants only — a variable or computed argument fails the build with CE0117
(mxcli check flags it as MDL046). To build a date from variables, step off
a literal anchor with addDays() / addMonths() (which do take variables):
mdl
-- WRONG: variable args to dateTime() (CE0117 / MDL046)
set $D = dateTime(2026, $Month, $Day);
-- RIGHT: anchor on a literal, then step with addMonths/addDays
set $D = addDays(addMonths(dateTime(2026, 1, 1), $Month - 1), $Day - 1);
Logging
mdl
-- Log levels
log info 'Information message';
log warning 'Warning message';
log error 'Error message';
-- With node name
log info node 'OrderService' 'Processing order';
log warning node 'ValidationService' 'Invalid data detected';
-- With variables (use concatenation)
log info node 'OrderService' 'Order processed: ' + $OrderNumber;
log error node 'Service' 'Error: ' + $ErrorMessage;
Special Values
mdl
empty -- Null/empty value
[%CurrentDateTime%] -- Current date/time
[%CurrentUser%] -- Current user object
toString($value) -- Convert to string
No randomInt. Mendix has no randomInt function. Use random() (returns a
Decimal in [0,1)) and round to an integer range — e.g. a value in 0..8 is
round(random() * 8). Because random() is a Decimal, assigning it (or div,
secondsBetween(), and the other duration *Between functions) directly to an
integer variable fails the build with CE0117 — wrap it in round()/floor()/ceil().
mxcli check now flags an unknown expression function like randomInt as
MDL044 (with a "did you mean random()?" hint), and a Decimal assigned to an
integer target as MDL041 — before the build does.
MDL044 also blocks mxcli exec, not just check: a call to a name Mendix
has no built-in for is CE0117 at build time, so exec refuses to write the
microflow or nanoflow (log messages included). Not real: currentDeviceType(),
[%CurrentDeviceType%] (a CE0117 check misses) and trunc() (use
round/floor/ceil). If exec rejects a function you believe IS a Mendix
built-in, build it once and — if mxbuild accepts it — add it to funcTable in
mdl/exprcheck/func_checker.go; that table is the rule's only allow-list.
Complete Example
mdl
mdl 1;
/**
* Process order with validation and status update.
*
* @param $OrderNumber The order to process
* @returns true when the order was found and marked processed
*/
create microflow Shop.ProcessOrder (
$OrderNumber: string
)
returns boolean as $success
begin
declare $success boolean = false;
-- Find the order (the retrieve establishes $Order — objects are never declared)
retrieve $Order from Shop.Order
where OrderNumber = $OrderNumber;
-- Validate order exists
if $Order = empty then
log warning node 'OrderService' 'Order not found: ' + $OrderNumber;
return false;
end if;
-- Validate customer association
if $Order/Shop.Order_Customer = empty then
log error node 'OrderService' 'Order has no customer';
return false;
end if;
-- Update order status
change $Order (
status = 'PROCESSING',
ProcessedDate = [%CurrentDateTime%]);
commit $Order;
-- Log success
log info node 'OrderService' 'Order processed: ' + $OrderNumber;
set $success = true;
return $success;
end;
Calling Microflows
✅ CORRECT Syntax
mdl
-- Call with result assignment (no SET keyword)
$Result = call microflow Module.ProcessOrder(Order = $Order);
-- Call without result (void microflow)
call microflow Module.SendNotification(message = $message);
-- Call with error handling
$Result = call microflow Module.ExternalService(data = $data) on error continue;
-- Run the call on a task queue (background execution). The clause goes after
-- the arguments and before any ON ERROR, and works on CALL JAVA ACTION too.
call microflow Module.ACT_Refresh() in queue Module.RefreshQueue;
call java action Module.RefreshData(Url = $Url) in queue Module.RefreshQueue;
Queued calls — the queue must already exist (create task queue Module.RefreshQueue (Parallelism: 2)), and the called flow must return nothing: a queued
microflow with a returns clause fails the build with CE7033 (mxcli check:
MDL088), a queued Java action must returns void or it fails with CE7038. Rewriting a microflow that has a queued call must restate the
in queue clause; a rewrite that omits it is refused rather than silently
dropping the binding. See .claude/skills/mendix/scheduled-events-and-queues.
❌ INCORRECT Syntax
mdl
-- WRONG: Do NOT use SET with CALL MICROFLOW
set $Result = call microflow Module.ProcessOrder(Order = $Order); -- ERROR!
-- CORRECT: Direct variable assignment
$Result = call microflow Module.ProcessOrder(Order = $Order);
Important: The set keyword is for changing existing variable values, NOT for capturing microflow return values. Use direct assignment ($var = call microflow ...).
Parameter Name Matching
CRITICAL: Parameter names in call microflow must exactly match the parameter names declared in the target microflow's signature (without the $ prefix). A mismatch causes a build error (MxBuild) but may fail silently at MDL execution time.
mdl
-- Target microflow declaration:
create microflow Module.SendEmail ($Recipient: string, $Subject: string)
begin ... end;
-- CORRECT: parameter names match the declaration
call microflow Module.SendEmail(Recipient = $Email, Subject = $title);
-- WRONG: parameter name does not match (EmailAddress vs Recipient)
call microflow Module.SendEmail(EmailAddress = $Email, Subject = $title); -- BUILD ERROR!
When calling microflows, always check the target's parameter list. Use describe microflow Module.Name to see the exact parameter names.
Page Navigation
SHOW PAGE
mdl
-- Open page with parameter
show page Module.EditPage(Product = $Product);
Every call site binds an argument as Param = expression, with no $ on the
parameter name: call microflow, show page, and widget actions
(action: show page Module.Page(Param = $value)) alike. $Param = $value and
Param: $value still parse but are deprecated (MDL-DEPR006/007); mxcli fmt --upgrade rewrites them.
CLOSE PAGE
mdl
close page;
SHOW HOME PAGE
mdl
show home page;
Validation Checklist
Before executing a microflow script, verify:
No object (entity) or list is declared — objects come from a parameter, retrieve, create object, or loop iterator (MDL043); lists from a parameter, retrieve, or create list (MDL040)
All primitive variables are declared before SET (declare $var type = value;)
XPath association navigation uses qualified names (Module.AssociationName)
All referenced attributes exist in entity definitions
Every flow path ends with return
No code appears after return statements
Division uses div operator (not /)
All entity/association names are fully qualified
CALL MICROFLOW parameter names exactly match target signature (use describe microflow to verify)
Microflow ends with / separator
Parameters start with $ prefix
Proper closing for control structures (end if, end loop)
Tips for Success
Always use fully qualified names: Module.Entity, Module.Association
Test incrementally: Create simple microflows first, then add complexity
Check entity definitions: Ensure all attributes exist before referencing
Use meaningful variable names: $Customer not $c, $ProductList not $list
Comment complex logic: Use -- for inline comments
Log important events: Help with debugging and auditing
Handle empty cases: Check for = empty before using objects
Use WITHOUT EVENTS appropriately: Only when handlers must be skipped (events are on by default)
Validate before executing: Use mxcli check script.mdl -p app.mpr --references to catch errors
declare $primitive type = value; -- Primitives (String/Integer/Decimal/Boolean/DateTime)
declare $status Enumeration(Module.Enum) = …; -- Enumerations are primitives too
-- Objects: never declare. Use a parameter, retrieve (… first), `$obj = create Module.Entity(...)`, or a loop iterator.
-- Lists: never declare. Use a parameter, retrieve, or `$list = create list of Module.Entity;`
if condition then ... [else ...] end if;
loop $var in $list begin ... end loop;
return $value;
XPath Pattern
mdl
$var/attributename -- Attribute
$var/Module.AssociationName -- Association
$var/Module.AssociationName/attribute -- Chained
Annotation Pattern
mdl
@position(200, 200) -- optional: omit it and mxcli lays the flow out; to re-arrange an existing flow run `mxcli layout flows`
@caption 'Persist order'
@color Green
@annotation 'Note about the next activity'
commit $Order; -- Annotations apply here
Annotations Are Notes, and a Note Can Be Shared
A note is a node with edges in Mendix, not a property of the activity it
documents. So @annotation is repeatable — one activity can carry several,
each its own note — and one note can be attached to several activities:
mdl
@annotation(id: n1, text: 'both of these touch the same record')
commit $Order;
@annotation(id: n1) -- attaches THAT note, does not copy it
commit $Invoice;
id: is scoped to the flow you are writing and is not stored in the model; it
exists only so a second mention can point at the first. Without it, two lines
with identical text are two separate notes — mxcli never merges on text.
A note's own canvas geometry is position: (x, y) and size: (w, h), e.g.
@annotation(text: 'note', position: (175, -40), size: (260, 70)). Omit them
and the note goes 100px above the activity at 200×50, stacking 60px per extra
note; DESCRIBE omits them again whenever they match, so an ordinary note keeps
the short @annotation 'text' form.
Page Navigation Pattern
mdl
show page Module.Page(Param = $value);
close page;
show home page;
Error Handling Pattern
mdl
call microflow ... on error continue; -- Ignore error
call microflow ... on error rollback; -- Rollback on error
call microflow ... on error begin log ...; return ...; end error; -- Custom handler
call microflow ... on error without rollback begin ... end error; -- No rollback
The clause goes on whichever activity may fail, not only on calls:
mdl
declare $Name String = 'default' on error begin return 'could not initialise'; end error;
set $Name = $Other/Name on error begin return 'lookup failed'; end error;
change $Order (Status = Shipped) on error begin log error 'could not ship'; return; end error;
log info node 'App' 'starting' on error begin return; end error;
show message 'saved' on error begin return; end error;
-- BLOCKING halts the client until dismissed; after `objects`, before `on error`.
show message 'Hello {1}' type Warning with ({1} = $Name) blocking;
validation feedback $Order/Total message 'must be positive' on error begin return; end error;
show page Module.Page on error begin return; end error;
close page on error begin return; end error;
Two limits, both reported rather than silently ignored:
on error continue is rejected by Mendix (CE6035) on create, change,
commit, log, show page, close page, show message and
validation feedback — MDL076. A custom { handler } is accepted on all
of them; continue is fine on declare, set, retrieve, delete and
call microflow. Measured on 11.14.0 — note that create-variable and
change-variable accept continue while change-object does not.
List operations and aggregates ($x = head $l;, $n = count $l;)
have no error handling in Mendix at all — MDL077.
In a nanoflow, almost none of them take a clause at all.change, log,
show page, close page, show message and validation feedback are CE6035
there whichever form is written; only declare and set accept one. See
write-nanoflows.
End the handler. A handler body that does not finish with return or throw
merges back into the main flow, so a variable created after the merge point is
out of scope on the error path — CE0108, which Studio Pro reports for the same
model. Ending the handler (as Studio Pro does when you wire it to an end event)
avoids this entirely.
Write 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.
Walks the last step of a branch: confirm tests pass, detect the git environment, ask how to integrate, carry out your choice and clean up the worktree.
Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications.
Reviews code against a five-part checklist covering security, correctness, performance, maintainability and testing, and reports findings in a fixed format.
Iteratively improves a PR (GitHub), MR (GitLab), or shelved changelist (Perforce) until Greptile gives it a 5/5 confidence score with zero unresolved comments.
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…
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.
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.
Call external REST APIs from Mendix — the three approaches (inline REST CALL, consumed REST client document, generated from OpenAPI) and how to choose.
Microflow syntax reference in MDL — every activity type, control flow, expressions, and the mistakes that fail mxcli check. Write Microflows is an agent skill from mendixlabs/mxcli. Microflow syntax reference in MDL — every activity type, control flow, expressions, and the mistakes that fail mxcli check.
When should I use Write Microflows?
Write Microflows fits situations like: development work in your project.
How do I install Write Microflows in Claude Code?
Run `npx skills add mendixlabs/mxcli --skill write-microflows -a claude-code`. Or copy the skill folder (.claude/skills/mendix/write-microflows in mendixlabs/mxcli) into .claude/skills/write-microflows in your project. Claude Code loads it when a task matches its description.
How do I install Write Microflows in Codex?
Run `npx skills add mendixlabs/mxcli --skill write-microflows -a codex`. Or copy the skill folder (.claude/skills/mendix/write-microflows in mendixlabs/mxcli) into .agents/skills/write-microflows in your project. Codex loads it when a task matches its description.
Can I use Write Microflows in Cursor, Gemini CLI or GitHub Copilot?
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add mendixlabs/mxcli --skill write-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/write-microflows, .gemini/skills/write-microflows, .github/skills/write-microflows and .opencode/skills/write-microflows in your project.
What does Write Microflows need to run?
SKILL.md names no scripts, command-line tools or credentials: Write Microflows is instructions for the agent only.
Does Write Microflows access the network?
SKILL.md names 1 domain. As links in the text: docs.mendix.com. This is read from the text; nothing was executed.
Is Write Microflows safe to install?
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.
What licence does Write Microflows use?
Write 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.
How many tokens does Write Microflows use?
About 7.1k tokens (SKILL.md is roughly 28k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
What are the alternatives to Write Microflows?
Skills that share tags, products or a category with Write Microflows: Finishing a Development Branch (obra/superpowers, 296k stars), Typescript Advanced Types (rolling-scopes/rsschool-app, 10k stars), PR Babysitter (openinterpreter/openinterpreter, 69k stars) and Code Review Checklist (shareAI-lab/learn-claude-code, 78k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
Who maintains Write Microflows?
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.