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 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]}},
  "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 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, 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.