Command Line
Two command-line programs ship alongside the application. Both use the same core, the same node library and the same export path as the app, so a headless run reproduces what the Export dialog would have written.
surface_lab_headless
Section titled “surface_lab_headless”Opens a project, evaluates it, writes its textures and exits. No window, no Flutter engine.
surface_lab_headless <project.surfacelabs> --out <dir> [--native-lib <path to surface_gpu>] [--format png|tga|tiff|bmp|exr] [--prefix <name>] [--material <index>|all] [--verbose]| Flag | Meaning |
|---|---|
--out, -o | Output directory. Required. |
--native-lib | Path to the surface_gpu library. Auto-detected from a source checkout; SURFACE_GPU_LIB also works. |
--format, -f | Output format: png, tga, tiff (tif also accepted), bmp or exr. Anything else is an error rather than a fallback. Defaults to the project’s saved export preset. |
--prefix, -p | File-name prefix. Defaults to the saved preset, otherwise a slug of the project name. |
--material, -m | A material index, or all. Defaults to the active material. all exports every material, suffixing files with the material name. |
--verbose, -v | Print core diagnostics to stderr. |
--help, -h | Usage. |
It prints what it opened, each file it wrote, and a summary. Warnings, meaning load warnings and skipped nodes, go to stderr.
Exit codes: 0 wrote at least one file, 1 usage or project error, 2 no
render core, 3 nothing to export.
Trim Sheets export from here like any other output node: the strip compositor is an ordinary fragment shader, so the whole sheet resolves on the native core with no app involved. Draw nodes, Text nodes and Custom Shaders the native path declined are still skipped, each named on stderr.
This is the piece you want in CI. A build step can re-export a committed
.surfacelabs on every change with no GUI, no display and no human.
surface_lab_mcp
Section titled “surface_lab_mcp”The same nine tools as the in-app server, driving projects on disk instead of the
open one. project create and project close both work here. See
MCP Server for what the tools do; the flags below are what
differs.
surface_lab_mcp [--http [port]] [--token <secret>] [--native-lib <path>] [--out <dir>] [--root <dir>] [--read-only] [--log <file>] [--verbose]| Flag | Meaning |
|---|---|
| (none) | JSON-RPC over standard input and output, the transport most MCP clients launch a server with. This is the default. |
--http [port] | Serve the same protocol on a loopback port instead, default 4319, like the in-app server. |
--token <secret> | Bearer token required on HTTP requests. Without it, --http accepts any local caller. |
--native-lib <path> | surface_gpu library path. Auto-detected in a source checkout; SURFACE_GPU_LIB also works. |
--out <dir>, -o | Default directory for renders and exports when a tool names none. Defaults to <temp>/surface_lab_mcp. |
--root <dir> | Confine every file argument to this directory, rejecting .. traversal out of it. Repeatable. Without it the server can read and write anywhere you can. |
--read-only | Refuse mutating tools. See the caveat below. |
--log <file> | Append diagnostics to a file instead of stderr. |
--verbose, -v | Include core diagnostics. |
--help, -h | Usage, on stderr. |
A stdio client config:
{ "mcpServers": { "surface-labs": { "command": "dart", "args": ["run", "surface_lab_mcp", "--out", "C:/textures/out"], "cwd": "C:/…/surface_lab/packages/surface_lab_mcp" } }}On stdio, standard output is the protocol. Every diagnostic goes to stderr or the
log file so nothing corrupts the stream, and --http prints its endpoint to
stderr for the same reason.
On stdio the server exits when its client closes the pipe. Under --http it
serves until interrupted, and exits 4 if the port will not bind.
Batch processing
Section titled “Batch processing”Today the CLI treats the project as a closed box. Every input and every parameter
is whatever was saved, and the only knobs are output format, file-name prefix and
which material. Batching means running the CLI once per project in a shell loop,
or exporting --material all.
Before you script it
Section titled “Before you script it”graph validate is cheap and needs no GPU, so run it before a long export. See
MCP Server for which node types the headless renderer skips
and what happens when the native core is missing.