mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-01 05:58:19 -06:00
Two queue items that resolve to one output (the same comp added twice, a duplicated item) replaced
each other's file inside one job. Nothing is on disk when the job starts, so no check said anything.
- A default output name (renderQueue.add, render.preRender, comp.saveFrameAs {queue},
file.createProxy) that another item of the queue writes, in any output module or segment, is
numbered like a footage collision: Main.mp4, Main_1.mp4, Main_2.mp4. Sequences are compared by
the frames they write, so two stills of one comp at different times keep their names. Names a
caller gave and names already stored in items are never changed.
- Outputs of one job that still name the same file (explicit names, renderQueue.duplicate, output
modules, segments) refuse the whole render before anything is written, naming both items.
Sequences clash only on frames both write; a later item with Skip Existing Files leaves the
earlier item's frames alone; items refused for footage take no part.
Refusing the whole render, not just the later item: which of two items should keep the file is the
user's to say, and failing only the later one would leave a half-rendered queue found after they
left. renderQueue.duplicate stays a verbatim copy so the clash is reported, not hidden.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDvxCS8xcR28VArjzn1x7U
97 lines
6.1 KiB
Markdown
97 lines
6.1 KiB
Markdown
# MCP conventions
|
|
|
|
Run `effectcraft-cli mcp` for a headless session, or `effectcraft-cli mcp --bridge 9877`
|
|
to use the desktop app's control channel. Existing ports are unchanged.
|
|
|
|
## Confining file access
|
|
|
|
`--automation-read-root DIR` and `--automation-write-root DIR` (each repeatable, as in
|
|
PhotoCraft) confine what the agent can reach: files are read only under a read or write root and
|
|
written only under a write root; with no write root nothing is written. Every engine command
|
|
(`execute_command`, `command_run`, each `batch` step) is checked before it runs: its file
|
|
parameters must be absolute paths inside the roots (symbolic links resolved, no `..`), and so must
|
|
any other absolute path in its parameters. Save, Increment and Save and rendering the Render
|
|
Queue are checked against the files they write (the open project's, the queued outputs);
|
|
`renderQueue.add`, `render.preRender` and `file.createProxy` without an output file need a saved
|
|
project, since an unsaved one's default output would go to the home folder (#2479). Scripts
|
|
(`run_script`, `file.runScript`…), Execute File, Reveal in Finder, `$N` references to file paths
|
|
in a batch, and the bridge's UI-driving tools (`ui_click`, `ui_key`, `ui_type`, `ui_drag`,
|
|
`ui_set`, `script_ui`, `control`) are refused while confined. Without the flags nothing changes.
|
|
|
|
The server provides the same core conventions as FilmCraft #28:
|
|
|
|
| Tool | Arguments | Result |
|
|
| --- | --- | --- |
|
|
| `command_list` | `filter?`, `enabled_only?`, `schemas?`, `compact?` | Command catalog |
|
|
| `command_run` | `id`, `params?` | Engine command result |
|
|
| `command_batch` | `steps: [{id, params?}]`, `stop_on_error?` | `completed`, `failed`, per-step `results` |
|
|
| `doc_inspect` | none | Project overview and active composition |
|
|
| `render_preview` | `comp?`, `time?`, `max_side?`, `transparent?` | PNG and frame information |
|
|
|
|
`command_batch` stops at the first error by default; false continues. Each edit has its
|
|
own undo step. The existing `batch` tool retains its atomic undo grouping and result references.
|
|
Existing tools, including `list_commands`, `execute_command`, and `batch`, remain listed
|
|
because existing workflows use them; there are no hidden aliases. See [agents.md](agents.md)
|
|
for the full catalog. Each tool has a title and read-only, destructive, idempotent and
|
|
open-world hints. Tools with an optional output path are conservatively annotated as file writers.
|
|
|
|
Unknown top-level tool argument keys return JSON-RPC `-32602` naming the key and accepted
|
|
arguments. EffectCraft already rejects unknown engine command parameters; that behaviour is
|
|
preserved. Tool failures (including escaped panics) return `isError: true`, and so does any JSON
|
|
result with a `failed` count above zero (a `command_batch` / `batch` with a failed step, a
|
|
render whose queue items did not all finish), even though the other steps or items succeeded:
|
|
read `results` / `items` for the details. Malformed JSON
|
|
returns `-32700` with a null id and the session keeps serving. The MCP backend owns its session
|
|
without a mutex; the engine's render-job mutexes already recover poisoned locks.
|
|
|
|
Resources `effectcraft://document` and `effectcraft://commands` return JSON matching
|
|
`doc_inspect` and `command_list`. Clients declaring MCP 2026-07-28 in per-request `_meta`
|
|
receive `resultType: complete` and list/read cache hints; document reads are never cached.
|
|
|
|
```json
|
|
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"command_run","arguments":{"id":"comp.new","params":{"width":640,"height":360}}}}
|
|
{"jsonrpc":"2.0","id":2,"method":"resources/read","params":{"uri":"effectcraft://document"}}
|
|
```
|
|
|
|
## Two outputs on one file
|
|
|
|
Two outputs of one job that write the same file (two items with one Output To, a duplicated item,
|
|
output modules or segments that meet) refuse the whole `renderQueue.render` before anything is
|
|
written: the reply is `isError: true`, naming both items, and the queue is left as it was.
|
|
Sequences clash only on the frames both write, and an item with Skip Existing Files leaves the
|
|
earlier one's frames alone. A new item's default output name is numbered (`Main.mp4`,
|
|
`Main_1.mp4`) when another item of the queue writes it.
|
|
|
|
## Progress and cancellation
|
|
|
|
Headless `command_run` / `execute_command` calls to `renderQueue.render` (unless `wait: false`)
|
|
run the existing background job while the MCP server keeps reading stdin. A
|
|
`params._meta.progressToken` string or number opts into `notifications/progress`, at most ten
|
|
per second, strictly increasing with a queue-item total. No token means no notifications.
|
|
`ping`, inspection and other requests are served while rendering. Closing stdin lets the
|
|
pending render finish and reply.
|
|
|
|
### Requests during a render
|
|
|
|
A render job owns a snapshot of the project taken when it started. While it runs:
|
|
|
|
- Every way of starting a second render is refused with `isError: true`: a blocking
|
|
`renderQueue.render`, one with `wait: false`, or one inside a batch.
|
|
- Other commands, including edits, run normally and change the project. They do not affect the
|
|
running job, which keeps rendering its snapshot; to render an edit, render again after the
|
|
job ends. Queue items are matched by id when the job reports their status, so an item removed
|
|
mid-render is left out of the reply and counted in `failed`.
|
|
|
|
Send `notifications/cancelled` with `params.requestId` to stop the matching request at the next
|
|
frame batch. No response is sent for that cancelled request. Unknown and completed ids are
|
|
ignored. The exporter records the exact files it opens, including storage overflow paths, and
|
|
removes those files on cancellation; it never scans for similarly named files. Finished earlier
|
|
queue items and skipped existing frames remain. Custom exporters implement the same cleanup
|
|
contract. A failed queue item gives `isError: true`.
|
|
|
|
Bridge calls, `wait: false`, batch tools and single-frame tools retain synchronous/polling
|
|
behaviour and do not report MCP progress or cancellation. `renderQueue.render {"wait": false}`
|
|
returns the ids being rendered at once; poll `renderQueue.list` for status and stop it with
|
|
`renderQueue.stop`. Use a direct blocking command call
|
|
for an MCP-cancellable render.
|