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-rule for typos in built-in rule names
  • privileged-wildcard-allow for guard privileged ... when [ * => allow ]
  • guard-unreachable-arm when an earlier guard arm already covers a later one
  • privileged-guard-without-policy-operation when a privileged op: 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.someName references 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 shellSession so 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 via params.script
  • execute — Run file via params.filepath with optional payload/state/dynamicModules
  • analyze — Static analysis via params.filepath
  • state:update — Update in-flight @state for params.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 request
  • INVALID_REQUEST — Missing required fields
  • REQUEST_IN_PROGRESS — Duplicate request id
  • REQUEST_NOT_FOUND — Cancel/state:update for unknown id
  • STATE_UNAVAILABLE — No dynamic @state to update
  • METHOD_NOT_FOUND — Unknown method
  • RUNTIME_ERROR — Execution failure
  • ABORTED — Canceled request
  • TIMEOUT — 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, and arms
  • policy rules, operations, locked, and refs

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.