ArtCraft Automation: Control Channel, CLI, and MCP
Disclaimer
This skill is an agent skill for the ArtCraft series software, not an official ArtCraft repository. The copyright and official documentation of ArtCraft / LightCraft / FilmCraft / PhotoCraft belong to storyteller (github.com/storytold); this repository merely organizes the publicly available protocols and CLIs into an operations manual and tool scripts for AI agents, with no affiliation or endorsement from the official source. In case of conflict between upstream documentation and this repository's content, the official version prevails.
Official Repositories: lightcraft · filmcraft · photocraft
What Is This
Each desktop application in the ArtCraft suite (github.com/storytold) implements every menu item, slider, and brush stroke in the UI as commands with stable IDs and JSON parameters, accessible through three equivalent entry points:
| Entry Point | What It Looks Like | Best For |
|---|---|---|
| Control Channel | The app opens a TCP port, speaking JSON lines | Controlling the window the user is looking at |
| CLI | lightcraft-cli / filmcraft-cli / photocraft-cli | Headless batch processing |
| MCP | xxx-cli mcp | Letting clients like Claude command it |
All three dispatch through the same command registry, so scripts and agents can operate like a person, and edits are undoable.
Four Golden Rules (Memorize These Before Anything Else)
- Runtime discovery first. The reference documents in this skill are snapshots from a specific version point, for navigation and fallback only; the running instance is the source of truth:
engine.commands(command registry),methods(method table), CLI--help/commands/controls. Never write command IDs from memory — each version's registry differs. - Authentication varies per app and can change. Run
scripts/craft_env.pyfirst to see theauthfield on your machine; don't copy-paste port commands from any blog. Known states:- LightCraft: channel is unauthenticated (any local process can drive it, close after use)
- FilmCraft: credentials in the top-level
tokenfield of each request (enforced in newer versions; 0.9.0 release does not enforce it) - PhotoCraft: the first frame of every connection must be
auth; file access is restricted by automation read/write root fail-closedscripts/craft_ctl.py --app <light|film|photo>automatically applies the corresponding method; when credentials are missing, it doesn't fail prematurely, but lets the server judge and reads the error verbatim.
- Credentials never enter command lines, logs, or repositories. Use token files (Unix mode 0600) or environment variables; don't pass tokens as plaintext to processes on shared machines.
- State impact before editing. Before modifying a real user's photo library or project, explain what will be changed, whether it's undoable, where exports go; ask for scope if uncertain.
Getting Started: Environment Self-Check First
python3 scripts/craft_env.py --table # Installed versions, CLI locations, channel status, credential locations
python3 scripts/craft_env.py # Same content as JSON
This step replaces "I remember I installed version X". It is read-only.
Entry Point Decision
| User Wants | Choose | One-Liner Reason |
|---|---|---|
| "I have this software open, help me click/look at it" | Control Channel | The only entry that can see the real window state |
| "Batch process these 200 files" | CLI headless | Faster and more stable without window rendering |
| "I want to just tell an AI to do this in the future" | MCP | Wire it once, use it long-term |
| "What commands can I run right now" | Any entry's engine.commands / command_list | The only authoritative listing |
A. Control Channel
Starting
All three applications use --control PORT to open a channel, but startup flags and credential requirements differ; check craft_env.py and references/app-matrix.md first:
# LightCraft: in-memory session, best for testing, doesn't write to disk
nohup "/Applications/LightCraft.app/Contents/MacOS/LightCraft" --control 7980 --memory > /tmp/lc.log 2>&1 & disown
# FilmCraft: credentials must be exported first, otherwise no listening
export FILMCRAFT_CONTROL_TOKEN="$(python3 -c 'import secrets; print(secrets.token_hex(32))')"
nohup "/Applications/FilmCraft.app/Contents/MacOS/FilmCraft" --control 9876 > /tmp/fc.log 2>&1 & disown
# PhotoCraft: token file (auto-created on first app launch, mode 0600)
nohup "/Applications/PhotoCraft.app/Contents/MacOS/PhotoCraft" --control 7878 \
--control-token-file ~/.photocraft/control.token > /tmp/pc.log 2>&1 & disown
Process Keep-Alive: A bare
&-started GUI will be reaped when the shell it was launched from exits, causing all subsequent connections to be rejected. Usenohup ... & disown, or write the entire "start → control → exit" sequence in one command.
Controlling
Use the bundled client instead of writing socket code each time:
# Generic form: --app determines auth method, --port for the port
python3 scripts/craft_ctl.py --app light --port 7980 engine.commands
python3 scripts/craft_ctl.py --app film --port 9876 --method engine.execute --params '{"command":"sequence.inspect"}'
python3 scripts/craft_ctl.py --app photo --port 7878 --token-file ~/.photocraft/control.token '{"method":"ui.inspect"}'
# Conditional discovery: streamlined command registry table (--filter to avoid thousands of lines)
python3 scripts/craft_ctl.py --app light --port 7980 --discover --filter develop
# Script mode: one request per line on stdin, lines starting with '#' are comments
echo '{"method":"engine.commands"}' | python3 scripts/craft_ctl.py --app light --port 7980
Exit codes: 0 all ok / 1 some ok=false / 2 usage or connection error.
B. CLI (Headless Batch Processing)
The CLI is a standalone release asset, not inside the .app; ArtCraft Launcher does not install it. See references/cli-reference.md for download and installation.
lightcraft-cli run --import in.dng develop.set control=light.exposure value=0.7 app.export path=out.jpg longEdge=2048
lightcraft-cli render in.dng -o out.jpg --set light.exposure=0.5
lightcraft-cli commands --json # Command registry (equivalent to channel's engine.commands)
lightcraft-cli controls --json # Develop slider IDs and ranges
photocraft-cli run wave.psd --cmd filter.sharpen.smartSharpen --params '{"amount":80}' --out out.png
photocraft-cli batch --actions grade.json --in ./raw --out ./graded
filmcraft-cli commands razor # Find command IDs by keyword
filmcraft-cli describe timeline.razor # Single command menu/shortcut/params/current enabled state
filmcraft-cli --project p.fcproj --save exec timeline.razor seconds=3.5
filmcraft-cli --bridge 127.0.0.1:9876 exec window.workspace.color # Remote control a running application
General rules: options can be anywhere; output is JSON; exit code 0=success / 1=command failure / 2=usage error; key=value is parsed as JSON if possible, otherwise as a string; dot-notation keys indicate nesting (e.g., color.r=1).
C. MCP (Integration with Claude and Other Clients)
claude mcp add lightcraft -- lightcraft-cli mcp ~/Pictures/shoot
claude mcp add lightcraft-app -- lightcraft-cli mcp --connect 127.0.0.1:7980
claude mcp add filmcraft -- filmcraft-cli mcp --bridge 127.0.0.1:9876
claude mcp add photocraft -- photocraft-cli mcp --bridge 127.0.0.1:7878 --control-token-file /path/token
Two modes: headless (in-process engine, no window) and bridge (drives an already launched application). See references/mcp-servers.md for credential requirements.
Core Workflow: Discover → Execute → Verify → Revert
- Discover:
engine.commands/command_listto fetch the registry, find the target command ID and parameter documentation by label/menu. - Execute:
engine.execute {command, params}. Note the semantic difference —ui.menu.invokeis a "menu click" (opens the command's dialog if no parameters), whileengine.executeruns the command with default parameters immediately, no dialog. - Verify: Read state instead of guessing.
ui.inspectfor general UI state; each app has domain-specific state methods (LightCraft:library.state/photo.inspect/develop.get, FilmCraft:sequence.inspect, PhotoCraft:document.inspect). - Check Results:
ui.screenshot {path}captures the window (only after rendering is complete; use headless screenshot when the window doesn't render frames). - Revert:
edit.undo/edit.redo. Agent edits also go into the undo stack.
Protocol rules (consistent across all three):
- One JSON request per line, one reply per line, in order:
{"id":N,"method":...,"params":{...}}→{"id":N,"ok":true,"result":...}or{"id":N,"ok":false,"error":"..."}. - No event push: the channel only replies to requests. To know what's happening in the GUI, poll inspect methods and compare snapshots.
- Each request is an independent snapshot: after switching objects, re-read the state; don't use cached IDs to infer current state.
- Server reply timeout is 60s; timed-out commands may still be running; check state before retrying. For long-running tasks, use
wait:falseand polljobs.list.
Security Boundaries
- All channels only listen on
127.0.0.1, protocol is not encrypted—do not tunnel or proxy to untrusted hosts. Loopback blocks remote access but not other local processes. - LightCraft channel has no authentication: open as needed, use
LIGHTCRAFT_CONFIG_DIRto isolate configurations for automation, close when not in use. - PhotoCraft file system is fail-closed: automation read/write is restricted to
--automation-read-root/--automation-write-root; unauthorized access is denied;file.open/file.saveare rejected by the channel; useapp.open/app.saveinstead. - FilmCraft's
FILMCRAFT_CONTROL_TOKENgrants full control: keep it confidential; don't put it in shell history or MCP configuration. - By default, do not touch the user's existing data (e.g., LightCraft's
~/Pictures/LightCraft Library), unless explicitly requested.
Troubleshooting: Check Here First
references/known-pitfalls.md covers actual issues encountered (connection refused, no-frame timeout, PhotoCraft missing --help, atomic export save blocked by system policy, layer origin offsets, quad relative coordinates, etc.). Check the table first; don't repeat the same wrong command.
Reference Document Index
| File | Content |
|---|---|
references/app-matrix.md | Quick reference for app differences (startup flags, credentials, sandbox, state methods, CLI/MCP entry points) — read this first |
references/control-protocols.md | Control channel protocol: common mechanisms + per-app method tables + authentication states |
references/cli-reference.md | CLI acquisition, subcommands, parameter semantics, exit codes |
references/mcp-servers.md | MCP server tool list, two modes, setup instructions |
references/known-pitfalls.md | Real-world pitfalls and troubleshooting paths |
references/upstream-sources.md | Upstream document URLs, snapshot dates, how to refresh this reference |
scripts/craft_ctl.py | Control channel client (authentication differences for all three apps built-in) |
scripts/craft_env.py | Environment self-check: apps, versions, CLI, channels, credentials |