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:
show @payload.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 — @payload is available directly
var @env = @payload.env ? @payload.env : "dev"
var @fast = @payload.fast ? @payload.fast : false
Reserved flags: --mlld-env is reserved for agent environment loading. --env now flows through as @payload.env.
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] |
Enable checkpoint replay for this run; optional target invalidates entries |
--fork <script> |
Seed cache from another script's checkpoints (read-only) |
--mlld-env <env> |
Load agent env file(s) or inline KEY=VALUE overrides |
--<name> <value> |
Payload field |
Checkpoint/resume: llm-labeled exes are checkpointed automatically. Scripts default to manual replay unless they declare resume: auto, so use --resume to opt into hits or targeted invalidation:
mlld run pipeline --resume # use cached hits for this 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 resume modes, named checkpoints, TTL/complete policies, cache inspection, and workspace VFS replay behavior.
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 app/index.mld --deep # Follow imports and templates from an entry file
mlld validate guards.mld --context tools.mld # Validate guards against tool declarations
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 include exe-parameter-shadowing, deprecated-json-transform, hyphenated-identifier-in-template, privileged-wildcard-allow, guard-unreachable-arm, unknown-policy-rule, privileged-guard-without-policy-operation, guard-context-missing-exe, guard-context-missing-op-label, and guard-context-missing-arg.
Policy / guard validation:
mlld validate --format json now surfaces policy declarations, exe labels, and richer guard structure:
{
"executables": [{ "name": "send_email", "params": ["recipients"], "labels": ["tool:w"] }],
"policies": [{ "name": "task", "rules": ["no-send-to-unknown"], "operations": { "destructive": ["tool:w"] }, "locked": false }],
"guards": [{
"name": "authSendEmail",
"timing": "before",
"filter": "op:tool:w",
"privileged": true,
"arms": [
{ "condition": "@mx.args.recipients ~= [\"alice@example.com\"]", "action": "allow" },
{ "condition": "@mx.op.name == \"send_email\"", "action": "deny", "reason": "recipients not authorized" }
]
}]
}
This is useful when validating LLM-generated policies/guards before execution.
Semantic guard/policy warnings:
mlld validate also warns about likely-authoring mistakes that are syntactically valid:
unknown-policy-rulefor typos in built-in rule namesprivileged-wildcard-allowforguard privileged ... when [ * => allow ]guard-unreachable-armwhen an earlier guard arm already covers a later oneprivileged-guard-without-policy-operationwhen a privilegedop:guard does not match any policy operation label
These stay warnings, not errors.
Context-aware guard validation:
Use --context to validate guards against one or more tool modules:
mlld validate guards.mld --context tools/workspace.mld
mlld validate guards.mld --context tools/,shared/tooling.mld --format json
With context, validation warns when:
- a function filter references an exe that does not exist
- an
op:filter does not match any exe label in the context @mx.args.someNamereferences a parameter not declared on the guarded exe
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, policies, needs, warnings, redefinitions, antiPatterns. For templates, includes template with type, variables, and discoveredParams.
Checkpoint & Resume
Checkpointing automatically persists results from llm-labeled executables so you can resume interrupted runs without re-calling LLMs. Cache writes are automatic; cache reads follow the script's resume policy.
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.
resume: auto
>> 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 with resume: auto or mlld run ... --resume: @review("main.ts", "sonnet") returns the cached result instantly.
Resume Modes
Set a top-of-file resume: header to control when cached hits are allowed:
resume: manual >> default if omitted
resume: auto >> serve checkpoint hits by default
resume: never >> skip checkpoint reads/writes unless --resume is used
Quoted and unquoted forms are both accepted: resume: auto and resume: "auto".
| Mode | Behavior |
|---|---|
manual |
Default for scripts. Writes checkpoints, but only serves hits when --resume is passed |
auto |
Writes checkpoints and serves hits automatically |
never |
Skips checkpoint reads and writes for normal runs |
--resume overrides the script-level mode for the current run.
CLI Flags
mlld run pipeline # writes checkpoints; default replay is manual unless script says resume:auto
mlld run pipeline --fresh # clear cache, rebuild from scratch
mlld run pipeline --no-checkpoint # disable caching entirely
mlld run pipeline --resume # use existing hits without invalidating a target
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 |
Enable checkpoint replay for this run without invalidating a specific target |
--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 & Policies
The checkpoint directive registers a named marker for resume targeting and can override resume behavior for the checkpoint scope that follows it:
checkpoint "fetch-api" with {
resume: auto,
ttl: 1h,
complete: @fetchDone
}
>> Later:
>> mlld run pipeline --resume "fetch-api"
| Field | Effect |
|---|---|
| `resume: auto | manual |
ttl: 1h |
Expire automatic hits after the duration |
complete: <expr> |
Record completion state when the checkpoint scope closes; automatic hits are only served after a prior run marked it complete |
Named checkpoints are discovered from source before execution starts, so --resume "name" works even if a prior run failed before that checkpoint was reached.
--resume still overrides resume, ttl, and complete for the current run.
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, and workspace snapshots are restored when available
- On miss: result is cached after post-hooks complete
- Automatic hits obey script/checkpoint resume policy, TTL, and completion gates
- Cache read/write errors are isolated (logged as warnings, never abort execution)
Workspace VFS Side Effects Are Replayed
Checkpoint resume now captures and restores VFS workspace state for checkpointed operations running inside an active box workspace. If an LLM call writes files via tools like Write or Bash, those files are restored on cache hits.
resume: auto
var @ws = box [
let @dummy = @claude("Write results to output.txt", { model: "haiku", tools: ["Write"] })
]
show <@ws/output.txt> >> works on resume — workspace snapshot is restored
Current limits:
- Restore only applies when the matching workspace is active at replay time
- Restoring a snapshot clears any stale
shellSessionso later shell commands see the restored VFS state - Malformed snapshots fail soft with a warning
- External side effects outside the workspace are still not replayed
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]}},
"mcpServers": {"tools": "uv run python3 server.py"},
"allowAbsolutePaths": true
}
Execute params:
{
"filepath": "./agent.mld",
"payload": {"task": "analyze"},
"state": {"conversationId": "abc"},
"dynamicModules": {"@tools": "export @search = ..."},
"mcpServers": {"tools": "uv run python3 server.py --config abc"},
"timeoutMs": 30000,
"mode": "markdown"
}
mcpServers maps logical names to MCP server commands. import tools from mcp "tools" in the script resolves against this map before treating the spec as a shell command. Each request gets its own server lifecycle.
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 and static semantics, including context-aware guard checks:
{
"code": "var @x = \"hello\"",
"mode": "strict",
"context": ["./tools.mld"]
}
Returns the same structured validation shape as mlld validate --format json, including errors, warnings, redefinitions, antiPatterns, and module metadata such as executables, guards, policies, and needs.
mlld_analyze — Full module analysis with exports, executables, imports, guards, policies, and statistics:
{
"code": "exe @greet(name) = cmd { echo \"hello\" }\nexport { @greet }",
"includeAst": false,
"context": ["./tools.mld"]
}
Returns structured data about the module: { exports: [...], executables: [...], imports: [...], guards: [...], policies: [...], variables: [...], stats: {...} }.
For guard/policy authoring, mlld_analyze now includes:
- exe
labels - guard
privileged,filter, andarms - policy
rules,operations,locked, andrefs
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.