The mlld CLI runs scripts, validates syntax, manages checkpoints for LLM call caching, and serves MCP tool endpoints. mlld run executes named scripts from the script directory. mlld <file> runs any file directly with payload injection.
Direct File Invocation
Run any mlld file directly with mlld <file>. Supports the same payload injection as mlld run, plus timeout and metrics flags.
mlld script.mld # Run a file
mlld script.mld --topic foo --count 5 # Pass payload fields
mlld script.mld --timeout 5m # Limit execution time
mlld script.mld --metrics # Show timing on stderr
Payload injection: Unknown flags become @payload fields. @payload is always available as {} even with no flags, so scripts can safely import from it:
import { topic } from @payload
show @topic
mlld script.mld --topic hello # @payload = {"topic":"hello"}
mlld script.mld # @payload = {}
Options:
| Flag | Description |
|---|---|
--timeout <duration> |
Overall execution timeout (e.g., 5m, 1h, 30s) |
--metrics |
Show execution timing on stderr |
--<name> <value> |
Payload field (unknown flags become @payload fields) |
Difference from mlld run: Direct invocation takes a file path. mlld run discovers scripts from the configured script directory (default: llm/run/) and supports AST caching across runs.
mlld run Command
Run mlld scripts from a configured directory with automatic payload injection.
mlld run # List available scripts
mlld run hello # Run llm/run/hello.mld
mlld run qa --topic vars # Pass payload: @payload.topic = "vars"
Payload injection: Unknown flags become @payload fields:
mlld run build --env prod --fast true
>> In build.mld
import "@payload" as @payload
var @env = @payload.env ? @payload.env : "dev"
var @fast = @payload.fast ? @payload.fast : false
Script directory: Configured in mlld-config.json, defaults to llm/run/.
Options:
| Flag | Description |
|---|---|
--timeout <duration> |
Script timeout (e.g., 5m, 1h, 30s) - default: unlimited |
--debug |
Show execution metrics |
--fresh / --new |
Clear checkpoint cache before running |
--no-checkpoint |
Disable checkpoint reads/writes for this run |
--resume [target] |
Invalidate checkpoint entries and re-run (see below) |
--fork <script> |
Seed cache from another script's checkpoints (read-only) |
--<name> <value> |
Payload field |
Checkpoint/resume: llm-labeled exes are automatically cached. Use --resume to selectively re-run:
mlld run pipeline --resume @review # invalidate all @review entries
mlld run pipeline --resume @review:2 # invalidate 3rd invocation (0-based)
mlld run pipeline --resume @review("src/") # invalidate by arg prefix
See mlld howto checkpoint for cache inspection and named checkpoints.
Validate Features
The mlld validate command detects common mistakes before runtime. Supports .mld, .mld.md, .att, and .mtt files. Pass a directory to validate all files recursively.
mlld validate ./my-project/ # Validate all files recursively (recommended)
mlld validate ./my-project/ --verbose # Full details for all files
mlld validate module.mld # Validate a single module
mlld validate template.att # Validate a template
mlld validate module.mld --error-on-warnings # Fail on warnings
mlld validate module.mld --format json # JSON output
Undefined variable detection:
var @name = "alice"
show @nmae
>> Warning: @nmae undefined
Warns about typos like @mx.now (should be @now).
Optional parameter pass-through warnings:
exe @inner(x, timeout) = sh { echo @timeout }
exe @outer(x, timeout) = [
let @result = @inner(@x, @timeout)
=> @result
]
var @r = @outer("hello")
>> Warning: @timeout may be omitted by callsites and fail when passed to @inner
This catches wrapper patterns where trailing exe parameters are omitted by callers and then forwarded into nested function calls.
Variable redefinition detection:
var @count = 0
when @condition [
var @count = 1
>> Error: cannot redefine @count in nested scope
]
mlld variables are immutable. Use let for block-scoped values or += for accumulation.
Reserved name conflicts:
var @now = "custom"
>> Error: cannot redefine reserved @now
Reserved names: @now, @base, @mx, @p, @env, @payload, @state.
Builtin shadowing:
exe @transform() = [
let @parse = "not-a-transformer"
>> Info: @parse shadows built-in transform in this scope
]
Shadowable builtins: @parse, @exists, @upper, @lower, etc. Reported as info, not errors.
Exe parameter shadowing:
exe @process(result) = [
=> @result
]
>> Warning: @result can shadow caller variables
>> Suggestion: use @status instead
Generic parameter names (result, output, data, value, response) warn about collision risk.
Suppressing warnings:
Add mlld-config.json to suppress intentional patterns:
{
"validate": {
"suppressWarnings": ["exe-parameter-shadowing"]
}
}
Suppressible codes: exe-parameter-shadowing, deprecated-json-transform, hyphenated-identifier-in-template.
Template validation (.att / .mtt):
mlld validate prompts/welcome.att
Reports all @variable and @function() references found in the template. When validating a directory, parameters are resolved from exe declarations across the entire project tree. For single-file validation, sibling .mld files are scanned. Discovered parameters are shown and undefined references are flagged:
Valid template (.att)
variables @name, @role, @unknownVar
params name, role (from exe declarations)
Warnings (1):
@unknownVar (line 5:1) - undefined variable
hint: @unknownVar is not a known parameter. Known: name, role
Template @for anti-pattern:
.att templates using strict-mode @for syntax are flagged — use /for ... /end instead:
⚠ prompts/report.att
"@for" is not valid in templates. Use "/for ... /end" instead. (line 8)
hint: In .att templates, use /for @var in @collection ... /end (slash prefix, no brackets).
Directory validation:
mlld validate ./my-project/
mlld validate ./my-project/ --verbose
Recursively validates all .mld, .mld.md, .att, and .mtt files. Default output is concise — clean files get a green checkmark, only files with issues show details:
✓ lib/utils.mld
✓ lib/helpers.mld
⚠ prompts/welcome.att
@unknownVar (line 5) - undefined variable
✗ broken.mld
Expected directive (line 3:1)
4 files: 3 passed, 1 failed, 1 with warnings
Use --verbose for full per-file details. Exit code 1 if any file fails.
Exit codes:
- Exit 0: valid syntax, no errors
- Exit 1: parse errors or hard validation errors
- Exit 1 with
--error-on-warnings: any warnings present
JSON output:
mlld validate module.mld --format json
Returns structured data: executables, exports, imports, guards, needs, warnings, redefinitions, antiPatterns. For templates, includes template with type, variables, and discoveredParams.
Checkpoint & Resume
Checkpointing automatically caches results from llm-labeled executables so you can resume interrupted runs without re-calling LLMs.
Auto-detection: exe and run operations with the llm label that make external calls (cmd, sh, prose, node) are checkpoint-eligible. No opt-in flag needed.
>> This call is automatically checkpointed
exe llm @review(file, model) = cmd {claude -p "Review @file" --model @model}
var @result = @review("main.ts", "sonnet")
Second run: @review("main.ts", "sonnet") returns the cached result instantly.
CLI Flags
mlld run pipeline # auto-caches llm calls
mlld run pipeline --fresh # clear cache, rebuild from scratch
mlld run pipeline --no-checkpoint # disable caching entirely
mlld run pipeline --resume @review # invalidate @review's cache entries, re-run
mlld run pipeline --fork other-script # seed cache from another script's results
| Flag | Effect |
|---|---|
--fresh / --new |
Clear this script's cache before running |
--no-checkpoint |
Disable all checkpoint reads and writes |
--resume @fn |
Invalidate all entries for @fn, re-execute |
--resume @fn:2 |
Invalidate only the 3rd invocation (0-based) |
--resume @fn("src/") |
Invalidate entries where first arg starts with "src/" |
--fork <script> |
Read hits from another script's cache (read-only); misses write to current cache |
Inspecting the Cache
mlld checkpoint list <script> # list cached entries
mlld checkpoint inspect <script> # full JSON dump (manifest + records)
mlld checkpoint clean <script> # delete cache for a script
Cache lives in .mlld/checkpoints/<script-name>/.
Named Checkpoints
The checkpoint directive registers a named marker for resume targeting:
checkpoint "data-loaded"
>> Later, resume from this point:
>> mlld run pipeline --resume "data-loaded"
Named checkpoints are discovered from source before execution starts, so --resume "name" works even if a prior run failed before that checkpoint was reached.
Context Variables
| Variable | Description |
|---|---|
@mx.checkpoint.hit |
true when current operation was served from cache |
@mx.checkpoint.key |
Cache key for the current operation |
Use in hooks for observability:
hook @cacheLog after @review = [
append `@mx.op.name hit=@mx.checkpoint.hit` to "cache.log"
]
How It Works
- Cache key = SHA-256 hash of function name + normalized args
- On hit: execution short-circuits, guards are skipped, post-hooks still run
- On miss: result is cached after post-hooks complete
- Cache read/write errors are isolated (logged as warnings, never abort execution)
Live STDIO Transport
Persistent NDJSON RPC transport for long-running SDK operations.
mlld live --stdio
Protocol format:
>> Request
{"method":"process","id":1,"params":{"script":"show 'hello'"}}
>> Event stream (during execution)
{"event":{"id":1,"type":"stream:chunk","content":"hello"}}
{"event":{"id":1,"type":"state:write","path":"key","value":"val"}}
>> Result
{"result":{"id":1,"output":"hello","exports":[]}}
Methods:
process— Execute script text viaparams.scriptexecute— Run file viaparams.filepathwith optional payload/state/dynamicModulesanalyze— Static analysis viaparams.filepathstate:update— Update in-flight@stateforparams.requestId(path + value)cancel— Abort active request by id
Process params:
{
"script": "show 'hello'",
"filePath": "/example/context.mld",
"mode": "strict",
"payload": {"key": "value"},
"state": {"sessionId": "123"},
"dynamicModules": {"@custom": {"data": [1,2,3]}},
"allowAbsolutePaths": true
}
Execute params:
{
"filepath": "./agent.mld",
"payload": {"task": "analyze"},
"state": {"conversationId": "abc"},
"dynamicModules": {"@tools": "export @search = ..."},
"timeoutMs": 30000,
"mode": "markdown"
}
State updates (during execution):
>> Request to update in-flight state
{"method":"state:update","id":2,"params":{
"requestId": 1,
"path": "loopControl.stop",
"value": true
}}
>> Response
{"result":{"id":2,"requestId":1,"path":"loopControl.stop"}}
Lifecycle:
- Server runs until stdin EOF, SIGINT, or SIGTERM
- Each request uses fresh interpreter environment
- AST caching persists across requests (mtime-based invalidation)
- Active requests abort on shutdown or explicit
cancel
SDK integration:
Go, Python, Rust, and Ruby SDKs maintain persistent mlld live --stdio subprocesses. Each provides async handle APIs:
handle = client.execute_async("./script.mld", payload)
handle.update_state("flag", True) # In-flight state mutation
result = handle.wait()
Error codes:
INVALID_JSON— Malformed requestINVALID_REQUEST— Missing required fieldsREQUEST_IN_PROGRESS— Duplicate request idREQUEST_NOT_FOUND— Cancel/state:update for unknown idSTATE_UNAVAILABLE— No dynamic@stateto updateMETHOD_NOT_FOUND— Unknown methodRUNTIME_ERROR— Execution failureABORTED— Canceled requestTIMEOUT— Execution exceeded limit
mlld mcp-dev Command
Start an MCP server that provides language introspection tools for development. Use with Claude Code or other MCP clients to validate syntax, analyze modules, and inspect ASTs during development.
mlld mcp-dev
Configure in claude_desktop_config.json:
{
"mcpServers": {
"mlld-dev": {
"command": "mlld",
"args": ["mcp-dev"]
}
}
}
Tools provided:
mlld_validate — Validate syntax, return errors and warnings:
{
"code": "var @x = \"hello\"",
"mode": "strict"
}
Returns { valid: true/false, errors: [...], warnings: [...] }.
mlld_analyze — Full module analysis with exports, executables, imports, guards, and statistics:
{
"code": "exe @greet(name) = cmd { echo \"hello\" }\nexport { @greet }",
"includeAst": false
}
Returns structured data about the module: { exports: [...], executables: [...], imports: [...], guards: [...], variables: [...], stats: {...} }.
mlld_ast — Get the raw parsed AST:
{
"code": "var @x = \"hello\""
}
Returns { success: true, ast: [...] } or parse error details.
Input modes:
All tools accept either file (path to .mld file) or code (inline string):
{ "file": "./script.mld" }
{ "code": "var @x = 1" }
Mode inference: .mld files use strict mode, .mld.md files use markdown mode. Override with "mode": "strict" or "mode": "markdown".
Separate from user tools:
mlld mcp-dev serves built-in language introspection tools. mlld mcp serves user-defined functions as MCP tools. The two commands run independent servers with different purposes.
Claude Code Plugin
Install the mlld Claude Code plugin for orchestrator authoring skills, the /mlld:scaffold command, language server integration, and MCP dev tools.
mlld plugin install # Install for current user
mlld plugin install --scope project # Install for current project only
mlld plugin status # Check if installed
mlld plugin uninstall # Remove the plugin
Requires the claude CLI to be installed. The command adds the mlld marketplace (mlld-lang/mlld) and installs the plugin through Claude Code's plugin system.
What's included:
| Component | Description |
|---|---|
| Orchestrator skill | Patterns for audit, research, and development pipelines |
| Agent skill | Tool agents, event-driven agents, workflow agents |
/mlld:scaffold |
Generate starter orchestrator projects |
| Language server | .mld syntax highlighting and diagnostics |
| MCP dev tools | mlld mcp-dev for development |
Restart Claude Code after installing to activate the plugin.