Skip to main content

MCP tools reference

Loom MCP exposes 12 tools. Validation, compile, and run operations call shared Loom libraries in-process; the read tools operate on workspace artifacts. All tools return typed JSON.

All tools accept an optional workspace_root compatibility field. When present, it must exactly match the server's canonical startup directory. Requests cannot add roots or use relative or symlink aliases. Every content path is repo-relative and resolved beneath that root before open; workflow/log path rejections use WF_TRUSTED_PATH at the offending field. Declared artifact tools use the inspection errors documented below. See MCP overview for startup and process-isolation guidance.

Quick reference​

ToolPurposeKey inputs
loom_versionPrint CLI version—
loom_validateValidate a workflowworkflow_path, source_format, variable_context, gitlab_context
loom_compileCompile or plan a workflowworkflow_path, source_format, variable_context, gitlab_context
loom_run_localExecute a workflow locallyworkflow_path, source_format, variable_context, gitlab_context, mount_mode, parallel
loom_start_runStart a server-owned local runSame run inputs
loom_run_statusObserve owned or recorded outcomerun_id
loom_cancel_runCancel this server’s owned runrun_id
loom_list_artifactsList declared membersrun_id, job_id, max_members
loom_read_artifactRead original member bytesrun_id, job_id, member_path, encoding, max_bytes
loom_list_runsDiscover saved runs—
loom_read_runRead recorded staterun_id
loom_read_eventsRead recorded eventsrun_id, max_lines, tail

Workflow inputs​

Validate, compile, and run accept source_format: loom (the default) or gitlab. Existing requests keep their native workflow behavior. Native input defaults to .loom/workflow.yml; GitLab input defaults to .gitlab-ci.yml. Set workflow_path to select another repo-relative file. Selection and local includes stay beneath the startup root, including when the server starts in a subdirectory of a Git repository.

The validate, compile, run_local and start_run tools accept optional configuration_inputs, a plain typed JSON object bound per document before composition. Explicit top-level null, duplicate keys at any depth, malformed values and unknown input names fail. An empty object is valid. Null members use the declaration's default/required behavior; numbers retain their original precision through registered protocol validation. This field is separate from variable_context and gitlab_context. See the configuration input contract.

All three tools accept optional requested_jobs and requested_instances, each a nonempty string array. Jobs resolve source aliases first, then exact final IDs; instances resolve only exact final IDs. Both include ordinary prerequisite closure. Omit both for full-workflow behavior. Empty arrays, JSON null, empty or whitespace-padded IDs, and nonstring values are rejected. Commas are literal; duplicates form one sorted set. The shared planner includes all ordering and artifact prerequisites without bypassing rules. Unknown or unavailable selectors fail before snapshot materialization or provider work.

Selected responses expose optional job_selection with requested_jobs, optional requested_instances, effective_jobs and a stable scope identity. Native Graph IR and GitLab plans also retain that scope. Validation receipts persist it. Runs retain one protected job selection event from the final snapshot, or a terminal preflight when no snapshot is created. A selected result verifies this scope, not the entire workflow. Native validation compiles a graph for explicit selectors or integer fanout; other unscoped sources retain schema-only validation.

Native requests may supply variable_context to validate, compile, run locally or start a server-owned run. Omitted/null means no context; an empty variables map is supplied. Mixed contexts and wrong-format contexts fail before execution.

GitLab requests may supply gitlab_context, using the versioned pipeline context: version, variables, optional changes, and optional runner_tags. Without it, planning uses an empty variable map and no runner tags. Context input requires source_format: "gitlab".

{
"tool": "loom_compile",
"source_format": "gitlab",
"gitlab_context": {
"version": "loom.gitlab-pipeline-context.v1",
"variables": { "CI_COMMIT_BRANCH": "main" },
"changes": { "base": "main", "head": "HEAD" },
"runner_tags": ["local"]
}
}

For changes, omitting paths resolves tracked Git changes from the given comparison; paths: [] explicitly means no changes. Git path whitespace is preserved. Invalid format or context input fails before execution.


loom_version​

Print the Loom CLI version string. Use to verify the toolchain is available.

Equivalent Loom action: loom version

Parameters​

NameTypeRequiredDescription
workspace_rootstringnoOptional exact canonical startup directory; other values are rejected.

Output​

FieldTypeDescription
versionstringCLI version, e.g. loom v0.0.0-alpha.1

Example​

// Request
{ "tool": "loom_version" }

// Response
{ "version": "loom v0.0.0-alpha.1" }

loom_validate​

Validate a native workflow or plan a GitLab workflow without executing it. Returns a receipt with validation results.

Equivalent Loom action: loom check

Parameters​

NameTypeRequiredDescription
workspace_rootstringnoOptional exact canonical startup directory; other values are rejected.

Also accepts the workflow inputs: workflow_path, source_format, variable_context, gitlab_context, configuration_inputs, requested_jobs, and requested_instances.

Output​

FieldTypeDescription
statusstring"success", "failed", or GitLab "skipped"
exit_codenumberProcess exit code
receipt_pathstringRepo-relative path to the validation receipt
receipt_jsonobjectParsed receipt contents
job_selectionobjectOptional requested/effective job scope and identity

Example​

// Request
{ "tool": "loom_validate" }

// Response
{
"status": "success",
"exit_code": 0,
"receipt_path": ".loom/.runtime/receipts/loom-check-1772050684042246000.json",
"receipt_json": { "..." }
}

See Receipts contract for the receipt schema. GitLab receipts identify the selected source and include gitlab_plan with its state, selections, diagnostics, requirements, and resolved changes.


loom_compile​

Compile a native workflow into Graph IR or return a GitLab plan without executing it.

Equivalent Loom action: loom compile

Parameters​

NameTypeRequiredDescription
workspace_rootstringnoOptional exact canonical startup directory; other values are rejected.
workflow_pathstringnoRepo-relative workflow YAML path. Defaults to .loom/workflow.yml.
source_formatstringnoloom (default) or gitlab; GitLab defaults to .gitlab-ci.yml.
configuration_inputsobjectnoFile-scoped typed configuration values; omitted means none.
variable_contextobjectnoNative caller scalar context; see its versioned contract.
gitlab_contextobjectnoVersioned GitLab pipeline context and runner tags.
requested_jobsstring[]noNonempty source jobs or exact final IDs plus prerequisite closure; omit for the full workflow.
requested_instancesstring[]noExact final IDs with prerequisite closure, bypassing source aliases.

Output​

FieldTypeDescription
graph_ir_jsonobjectThe compiled Graph IR; absent on compilation failure
warningsobject[]Compiler warnings (each: code, path, message); omitted when empty
diagnosticsobject[]Ordered public compiler findings; optional, omitted when no findings exist
exit_codenumber0 on success; 1 on ordinary compilation failure
gitlab_planobjectComplete GitLab plan; omitted for native workflows.
job_selectionobjectOptional requested/effective job scope and identity

Each diagnostic has code, pointer, and message. Its optional location has format, path, line, and column, preserving the public compiler source location when supplied. A missing location is omitted. Findings do not include raw source snippets or wrapper error text. Errors without public findings return exit_code: 1 without diagnostics.

Trusted-path findings continue to return a transport error, including a trusted-path finding later in a wrapped error's list. They do not return a structured compilation-failure payload. Native selection retains the existing format hint for GitLab-shaped input.

For GitLab input, gitlab_plan preserves the shared loom.gitlab-plan.v1 contract: state, selection_identity, selections, requirements, diagnostics, resolved_changes, and graph when executable. The states source_invalid and non_executable return exit code 1; executable and workflow_skipped return 0. A skipped plan has no graph. An executable plan may have an empty graph when all jobs were excluded.

Example​

// Request
{ "tool": "loom_compile" }

// Response
{
"graph_ir_json": { "..." },
"warnings": [],
"exit_code": 0
}

For an invalid native workflow such as version: v2 with stages: [test], the ordinary failure response includes the public finding:

{
"diagnostics": [
{
"code": "WF_SCHEMA_V1",
"pointer": "/version",
"message": "set version to the exact string \"v1\""
}
],
"exit_code": 1
}

loom_run_local​

Execute the workflow through the shared local-run service.

ParameterTypeDescription
workflow_pathstringRepo-relative workflow YAML; default .loom/workflow.yml.
mount_modestringephemeral_volume (default) or bind_mount.
parallelintegerOptional positive maximum concurrent jobs.
source_formatstringloom (default) or gitlab; GitLab defaults to .gitlab-ci.yml.
configuration_inputsobjectFile-scoped typed configuration values; explicit null is invalid.
variable_contextobjectNative caller scalar context; omitted/null means absent.
gitlab_contextobjectVersioned GitLab pipeline context and runner tags.
privileged_service_aliasesstring[]Explicit GitLab privileged service aliases authorized for this run.
requested_jobsstring[]Nonempty source jobs or exact final IDs plus prerequisite closure; omit for the full workflow.
requested_instancesstring[]Exact final IDs with prerequisite closure, bypassing source aliases.

The response contains status, exit_code, optional message and workflow_rule, plus run_id, run_directory, state_path and events_path when the run directory was created. Paths are relative to the startup root. Selected runs also return optional job_selection identifying their requested and effective scope with a stable identity. A failed workflow still returns its recorded paths and failed status. The shared service checks rooted source before snapshot preparation. Invalid, blocked, skipped and unsupported GitLab cache-diff outcomes retain their run identity and planning evidence without materializing a snapshot. Accepted source is compiled again from the isolated snapshot before execution. GitLab runs also return gitlab_plan from the actual run, including its selections, diagnostics, requirements, resolved changes, and executable graph. Service authorization applies only to the named aliases in this invocation; validation and compile requests grant no privileged service consent.

{ "tool": "loom_run_local", "parallel": 2 }

loom_start_run​

Explicitly start a server-owned local run with the same inputs and normalization as loom_run_local. The start returns exact run_id, run_directory, state_path and events_path as soon as recording begins, before workspace preparation and execution finish. Concurrent starts are independent attempts even with identical inputs. Only this explicit start detaches from its request after handoff; loom_run_local remains synchronous and bound to request cancellation. Cancellation before handoff cancels and joins pending work.

The response has state (active, terminal or unknown), owned and cancel_requested. Active responses omit status and exit_code; zero is never used to imply unfinished success. Terminal responses include status, exit_code (including zero), optional protected message, workflow_rule and known job_selection. A semantic outcome before recording returns terminal information without inventing a run ID; an operational startup failure is a tool error. Paths are relative to the startup root.

{ "tool": "loom_start_run", "workflow_path": ".loom/workflow.yml" }

loom_run_status​

Supply required exact run_id and optional exact workspace_root. For an attempt owned by this server, the response uses the same fields as loom_start_run and reports its active or compact terminal outcome. Ownership is limited to this server process and startup root; another server cannot establish its liveness.

For an unowned ID, the tool reads only that run's existing state.jsonl through the retained startup root. A recorded terminal workflow state returns state: terminal, owned: false, status and its recorded exit_code. Historical pending/running state returns state: unknown, owned: false and recorded_status, without a terminal exit code. A valid missing ID is unknown. Malformed complete records, identity mismatches and unsafe paths fail the call; an incomplete final append follows the existing state-reader rules. This does not create a durable daemon, new receipt or persisted liveness signal.

{ "tool": "loom_run_status", "run_id": "<run_id>" }

loom_cancel_run​

Supply required exact run_id and optional exact workspace_root. Cancellation is authorized only for an attempt owned by this server. For an active attempt, the response acknowledges cancel_requested: true; poll loom_run_status for the eventual terminal outcome and inspect its saved states/events. Repeated cancellation is safe, including after completion. Foreign or historical IDs are tool errors and cannot authorize cancellation.

The server cancels and joins all pending/active owned work during shutdown before closing its retained startup-root reader. Owned terminal views last only for the server lifetime; recorded evidence remains readable after restart.

{ "tool": "loom_cancel_run", "run_id": "<run_id>" }

loom_list_runs​

Discover saved run IDs without executing a workflow. The only input is the optional exact workspace_root binding described above. This reads state files through the retained startup root and returns { "runs": [] } when none exist.

Each summary contains run_id and, when recorded, workflow_id, status, exit_code, first_workflow_timestamp and last_workflow_timestamp. Exit zero is retained; unavailable values are omitted. First/last timestamps refer to complete workflow records in append order. Rows sort by first recorded timestamp descending, run ID ascending for ties, with unavailable starts last. Empty or no-workflow state files still list the ID. Pending/running is last recorded state, not process liveness.

A malformed complete record, missing/unreadable/nonregular candidate state file or escaping path fails the call without partial structured output. Incomplete final appends follow the normal state reader rules. Non-directory entries, symlink aliases and invalid run-ID directories are ignored. No index is written; there is no automatic latest selection or listing filter/limit.

{ "tool": "loom_list_runs" }

Choose a returned ID explicitly, call loom_read_run to find its failed command, then call loom_read_events with that job/step/command scope. The same saved files remain readable after a server restart. See loom runs for terminal discovery.

loom_read_run​

Read all state records from .loom/.runtime/runs/<run_id>/state.jsonl. The required run_id is a directory name discovered with loom_list_runs or returned by loom_run_local. The response contains run_id, state_path and ordered states. Each state records a workflow, job, step or command transition, its context and status. Optional exact job_id, step_id and command_id filters select one execution scope. All supplied filters must match; omitted fields select every state.

{ "tool": "loom_read_run", "run_id": "<run_id>" }

loom_read_events​

Read event records from .loom/.runtime/runs/<run_id>/events.jsonl.

ParameterTypeDescription
run_idstringRequired run directory name.
allbooleanReturn every matching record; excludes tail and nonzero max_lines.
max_linesintegerMaximum returned records; default 200.
tailbooleanReturn the last matching records, preserving file order.
job_idstringOptional exact job ID filter.
step_idstringOptional exact step ID filter.
command_idstringOptional exact command ID filter.
levelstringOptional exact log level filter.
streamstringOptional exact stream filter, such as stderr.

The response contains run_id, events_path, ordered events, total_events (the number matching the filters before limiting), returned_events, and complete. Use all: true to read every matching record in one request. Omit tail and max_lines for that request. Explicit positive max_lines values remain uncapped; negative values are rejected.

complete means every complete matching record available during this read was returned. A live run can append more records afterward. Messages preserve their string contents, including blank lines, carriage returns, and unterminated output. Filters select records rather than splitting messages. All supplied filters must match and apply before the record limit or tail.

{ "tool": "loom_read_events", "run_id": "<run_id>", "all": true }
{
"tool": "loom_read_events",
"run_id": "<run_id>",
"tail": true,
"max_lines": 30,
"job_id": "build",
"stream": "stderr"
}

Both readers ignore an incomplete final JSON record left by an interrupted append. A malformed complete line fails the read. Run names cannot contain path separators; readers reject escaping symlinks and nonregular files. The JSONL files are the recorded source; no manifests or derived views select other files.

See Runtime logs for the record fields and loom logs for terminal inspection.

loom_list_artifacts​

List a published job archive without extraction. Required run_id and job_id are exact identities; the job ID must already be canonical. Optional max_members defaults to 200 (maximum 1,000). workspace_root has the same exact startup-root contract as every tool.

The typed result contains run_id, job_id, status (published or empty), archive (path, format, size_bytes), sorted members (path, size_bytes), total_members, returned_members and complete. empty means a validated empty archive. Missing publication cannot prove skipped, empty or undeclared outputs. Missing run/job errors describe only the artifact namespace.

loom_read_artifact​

Required inputs are exact run_id, canonical job_id, canonical member_path from the listing, and explicit encoding: text or base64. Optional max_bytes defaults to 65,536 (maximum 1,048,576). The typed result contains run_id, job_id, archive, member, encoding, body, original returned_bytes, total_bytes and truncated. Text validates the entire selected member as UTF-8 and preserves an original prefix ending at a rune boundary. Base64 returns an original byte prefix. Artifacts are not redacted.

Both artifact tools validate the entire archive before publishing any result, using the shared reader behind loom artifacts. They only read .loom/.runtime/artifacts/<run_id>/jobs/<job-id>/artifacts.tar.gz, never a log-derived or arbitrary path. Linux/macOS descriptor-relative no-follow opens and substitution rechecks enforce containment; they do not authenticate bytes, continuously freeze the namespace or guarantee hostile-mount safety. Other platforms return unsupported_platform.

Per-call limits are 256 MiB compressed, 1 GiB decoded including metadata, 10,000 regular members, 4,096 UTF-8 bytes per member path, and a private 2 MiB budget per internal tar header traversal. Unsafe paths, symlinks, special/link members, duplicate/conflicting names, sparse metadata and malformed/truncated archives fail without partial results. InspectionError codes are invalid_request, path_rejected, run_not_found, job_not_found, publication_absent, publication_empty, member_not_found, invalid_archive, resource_limit, invalid_encoding and unsupported_platform; messages include the affected field. Cancellation remains a context error.

Native selection example​

loom_compile and loom_validate apply the same compile selection as the CLI:

{
"workflow_path": ".loom/conformance/compile-selection/native-host.yml",
"source_format": "loom",
"variable_context": {
"version": "loom.variable-context.v1",
"variables": { "MODE": "blue", "EMPTY": "" }
}
}

The compiled graph selects blue, common, raw-phase, scalar-logic and consumer, with safe compile_selection decisions. loom_run_local accepts the same source/context and executes retained nodes subject to their runtime rules. For GitLab use source_format: "gitlab" and gitlab_context; its if lookup preserves raw strings independently of execution variable expansion.

Public remote source effects​

Compile, validate and run tools resolve public HTTP(S) include sources through the shared compiler. Requests can perform network reads; cache: true or a valid per-include duration also writes original source bytes under .loom/.runtime/source-cache/v1/. Omitted/cleared cache controls bypass disk reuse. There is no request field granting internal network access or ambient credentials. Request/run cancellation reaches retrieval and retry delays.

Graph IR, GitLab plans and check receipts optionally expose sources: an aggregate identity and selected requested/final address hashes, query-free origins, raw content digests and byte counts. This evidence contains no bodies or URL queries. Configuration input identities remain separate. Runs retain a protected source closure event in their existing events file, with the same bytes pinned from preflight through authoritative snapshot compilation. See the include contract for literal URLs, integrity, cache forms and anonymous nesting.