metadata
Inspect and change a list’s front matter from scripts, with the same set, get, list, and unset subcommands as config.
Every list type takes description, the prose the built site prints above the cards. Decks add tags, format, labels, sourceId, and sourceUrl. Collections add labels, their default card labels. A wanted list carries the description alone.
Every list type also carries a cover image, but that key is out of scope here. It is a mapping rather than a scalar, so set-list-image writes it. set, unset, and get all point you there; only list reports it.
Writes use the same engine as the admin List Metadata route and the MCP set_list_metadata tool, so validation is identical on every surface. Card lines (&N ids, label overrides, notes) survive byte for byte. Only the front-matter block is rewritten; comments and quoting style there are not preserved, though every key and value is.
ritual metadata set [listName] <property> <value...> [--add | --remove]ritual metadata get [listName] <property>ritual metadata list [listName]ritual metadata unset [listName] <property>[listName] names a list of any type; see List Names. Pass --deck, --collection, or --wanted to fix the type or disambiguate. When the name is omitted, an interactive picker offers every list (prompts permitting).
Options
Section titled “Options”Every subcommand takes:
| Flag | Description |
|---|---|
--deck |
Resolve the name as a deck |
--collection |
Resolve the name as a collection |
--wanted |
Resolve the name as a wanted list |
--output <format> |
Output format: text (default), json, or ndjson |
--quiet |
Suppress non-essential text output (get still prints its value) |
set also takes --add / --remove (mutually exclusive) to merge values into an array property (tags, labels) instead of replacing it.
Once a list name is given, no subcommand prompts, so they are safe under --no-input.
Properties
Section titled “Properties”| List type | Property | Value |
|---|---|---|
| any | description |
Free text; values are joined with spaces, so quoting is optional. Stored trimmed; an empty value clears it. |
| deck | tags |
One tag per argument (commas inside an argument split too). --add appends, --remove drops; otherwise values replace. Removing the last tag clears the key. |
| deck | format |
A deck format key (e.g. commander, modern); exactly one value. |
| deck | labels |
Default card labels for the deck. proxy is the only accepted value. Every line inherits it unless its own token takes it away ([no-proxy]). |
| deck | sourceId |
The deck’s id on its sync source; exactly one value. Must agree with sourceUrl (see below). |
| deck | sourceUrl |
The deck’s http(s) URL on its sync source; exactly one value. |
| collection | labels |
Default card labels: sale/trade (combinable) or keep/proxy (each exclusive), as separate values or comma-joined, case-insensitively. |
| any | image |
Not settable here. list reports the stored cover image mapping; get, set, and unset refuse it (exit 2) and name set-list-image. |
Notes on labels (deck and collection):
--add/--removemerge with the current set. Removing the last label clears the key.- A misspelled label errors, even under
--remove, rather than silently removing nothing. - A default is positive only. A
no-…negation is a card-line token (it takes an inherited label away from one card), solabels no-saleis refused here. - An empty
setis refused; clearing isunset’s job.
Setting sourceId plus an archidekt.com sourceUrl is what makes a deck sync-linked. The two must name the same Archidekt deck; a write that would leave them disagreeing is refused. For the interactive linking flow, prefer deck-sync link.
A deck’s lastSynced field is not settable here (a usage error says deck sync stamps it). Any other property is refused with the accepted-fields listing. A list’s name is not front matter at all: it is the file’s # Title heading, changed with rename.
Examples
Section titled “Examples”ritual metadata set trade-binder labels sale,trade # collection default labelsritual metadata set trade-binder labels keep # keep is exclusiveritual metadata unset trade-binder labels # back to no default
ritual metadata set my-deck labels proxy # every deck line counts as a proxyritual metadata unset my-deck labels # back to no default
ritual metadata set my-deck description "A budget mono-red burn list"ritual metadata set trade-binder description "Everything I will trade away"ritual metadata set wants description "Cards I still need" --wantedritual metadata unset wants descriptionritual metadata set my-deck tags aggro budgetritual metadata set my-deck tags spicy --addritual metadata set my-deck format modern
ritual metadata get my-deck tags # ["aggro","budget","spicy"]ritual metadata list my-deckOutput
Section titled “Output”setreports the property’s new stored value (Set labels = ["sale","trade"] on collection 'trade-binder'). A value that cleared the key reportsCleared.getprints the raw value, arrays as JSON, and exits3with anot_founderror when the property is unset.listprints every property for the list’s type,(unset)included, and the non-settableimageamong them. With--output jsonthe payload is{ type, list, frontMatter }, wherefrontMatteris the full mapping, including non-settable keys (lastSynced,sourceUpdatedAt) and hand-authored unknown keys. This is the same shape the admin route returns.
Under --output json/ndjson, errors are emitted on stderr as { "error": { "code", "messageKey", "messageParams", "message" } } per the scripting conventions.
Behavior
Section titled “Behavior”- Only the front-matter block is touched. Prose, fenced blocks, and card lines survive byte for byte. No changelog entry is recorded; the changelog is card-level, and metadata is not a card change.
- Unknown keys round-trip. A hand-authored key the vocabulary does not know is preserved through every write. One deck-side exception: a known field stored with the wrong type (say, a
tags:holding a string) is dropped by the write, exactly as a full deck save would drop it. A file whose front matter cannot be read as YAML refuses every subcommand with a runtime error, since merging over keys that cannot be seen would destroy them. Fix the block by hand first. - An empty array reads as unset.
labels: []means “no default” andtags: []says nothing, sogetexits3for both, and removing an array’s last value deletes the key rather than writing[]. - The
.sha256content hash is refreshed only when it matched the file before the write. A hand-edited file keeps its stale hash, sodetect-changesstill records the edit. - Validation matches the other surfaces: the label vocabulary, the
keep/proxyexclusivity rule, which labels the list’s type carries, deck format keys,http(s)source URLs, and the Archidekt id/URL agreement rule are enforced exactly as the admin route enforces them.
Exit Codes
Section titled “Exit Codes”| Code | Meaning |
|---|---|
0 |
Success |
1 |
Runtime error (unreadable existing front matter, file I/O failure) |
2 |
Usage error (unknown property for the list’s type, invalid value, conflicting type flags, ambiguous list name) |
3 |
Not found (get on an unset property, or no list matches the given name) |
See Also
Section titled “See Also”edit— the session editor’s🏷️ Edit List Labels/🔖 Edit Deck Tags/🏷️ Change Formatactions edit the same fields interactively, deferred to the session’s saveconfig— the same subcommand shape for the ritual configuration fileset-list-image— the coverimage:key, on all three list types- List cover images — what the key means and how the site resolves it
- Admin API — List Metadata and the MCP
set_list_metadatatool — the same engine over HTTP (that route does writeimage)
Ritual is unofficial Fan Content permitted under the Fan Content Policy. Not approved or endorsed by Wizards of the Coast. Portions of the materials used are property of Wizards of the Coast. ©Wizards of the Coast LLC.
Card data, images and symbols come from Scryfall, which is not affiliated with this project. Ritual is free software under the AGPL-3.0. License and attribution.