← WebMCP Directory

Tool tree

origami.gratis

Productivity & BusinessTools verified
Website
Origami Folio
Explore capabilities
    origami_guideAction

    START HERE. The Origami contract: what a Fold is, the read-edit-write chunk protocol, every kind schema, the inert/active rules, the capability model and the tool catalog. Pass topic:"quickstart" FIRST if you are building a deck — under 3 KB, the five calls that do it, with a complete add_fold example. topic: quickstart | contract | kinds | recipes | starters | issues | tools | blocks. Pass kind for one kind's schema.

    View tool JSON
    {
      "name": "origami_guide",
      "kind": "act",
      "impl": "imperative",
      "description": "START HERE. The Origami contract: what a Fold is, the read-edit-write chunk protocol, every kind schema, the inert/active rules, the capability model and the tool catalog. Pass topic:\"quickstart\" FIRST if you are building a deck — under 3 KB, the five calls that do it, with a complete add_fold example. topic: quickstart | contract | kinds | recipes | starters | issues | tools | blocks. Pass kind for one kind's schema.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "kind": {
            "type": "string",
            "description": "Optional: one kind to detail (else the whole contract)"
          },
          "topic": {
            "type": "string",
            "enum": [
              "quickstart",
              "contract",
              "kinds",
              "recipes",
              "starters",
              "issues",
              "tools",
              "blocks"
            ],
            "description": "Optional: one section only — contract | kinds | recipes | starters | issues | tools | blocks"
          }
        }
      },
      "page": "/folio/"
    }
    get_kind_schemaAnswer

    The markup contract for one slide/block kind: what structure and attributes are valid. Changes nothing.

    View tool JSON
    {
      "name": "get_kind_schema",
      "kind": "answer",
      "impl": "imperative",
      "description": "The markup contract for one slide/block kind: what structure and attributes are valid. Changes nothing.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "kind": {
            "type": "string",
            "description": "Kind key, e.g. \"free\", \"gantt\", \"chart\""
          }
        },
        "required": [
          "kind"
        ]
      },
      "page": "/folio/"
    }
    create_deckAction

    Create a NEW Fold and OPEN IT IN THIS TAB. It renders immediately. Call this FIRST when building from nothing, then add folds with add_fold and finish with save_deck. The single fold it mints is a real COVER carrying your title (plus subtitle/eyebrow if given) — there is no placeholder text to overwrite or delete, so do NOT add a cover fold of your own. foldType: "deck" (default) | "scroll" (long-form document) | "ledger". If a Fold with UNSAVED changes is already open this refuses; pass discard:true to replace it anyway.

    View tool JSON
    {
      "name": "create_deck",
      "kind": "act",
      "impl": "imperative",
      "description": "Create a NEW Fold and OPEN IT IN THIS TAB. It renders immediately. Call this FIRST when building from nothing, then add folds with add_fold and finish with save_deck. The single fold it mints is a real COVER carrying your title (plus subtitle/eyebrow if given) — there is no placeholder text to overwrite or delete, so do NOT add a cover fold of your own. foldType: \"deck\" (default) | \"scroll\" (long-form document) | \"ledger\". If a Fold with UNSAVED changes is already open this refuses; pass discard:true to replace it anyway.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 200,
            "description": "Deck title (default \"Untitled deck\"); the cover h1, and it seeds the suggested filename"
          },
          "subtitle": {
            "type": "string",
            "maxLength": 300,
            "description": "A supporting line under the title on the cover (.lede). Omitted entirely when absent"
          },
          "eyebrow": {
            "type": "string",
            "maxLength": 80,
            "description": "A small label above the title on the cover, e.g. \"Q3 review\". Omitted entirely when absent"
          },
          "foldType": {
            "type": "string",
            "enum": [
              "deck",
              "scroll",
              "ledger"
            ],
            "description": "deck (default card-stage) | scroll (long-form document) | ledger"
          },
          "discard": {
            "type": "boolean",
            "description": "Replace an open Fold that has unsaved changes, losing them. Default false (refuse instead)"
          }
        }
      },
      "page": "/folio/"
    }
    list_chunksAnswer

    Table of contents of the open Fold: every editable chunk with id, kind, label and hidden flag, in order, plus the deck title, theme, foldType and capabilities.

    View tool JSON
    {
      "name": "list_chunks",
      "kind": "answer",
      "impl": "imperative",
      "description": "Table of contents of the open Fold: every editable chunk with id, kind, label and hidden flag, in order, plus the deck title, theme, foldType and capabilities.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {}
      },
      "page": "/folio/"
    }
    read_chunkAnswer

    Read one chunk for editing: the deck context, the kind schema and the slide <template>. Edit it and send the whole element back via write_chunk. Prefer get_block when you only want a data block's JSON.

    View tool JSON
    {
      "name": "read_chunk",
      "kind": "answer",
      "impl": "imperative",
      "description": "Read one chunk for editing: the deck context, the kind schema and the slide <template>. Edit it and send the whole element back via write_chunk. Prefer get_block when you only want a data block's JSON.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "chunkId": {
            "type": "string",
            "description": "Chunk id from list_chunks"
          }
        },
        "required": [
          "chunkId"
        ]
      },
      "page": "/folio/"
    }
    write_chunkAction

    Apply an edited chunk to the open Fold — this CHANGES THE DECK the human is looking at and re-renders it. Send the whole <template data-origami-slide=...> element from read_chunk, edited; the slide id and kind are immutable and drift is rejected. Scripts, styles, iframes and remote URLs are ALLOWED but mark the deck "active" (recipients open it locked). Refused only when the content would break the single-file structure, or a data block fails its kind's schema. dryRun:true runs the WHOLE gate and applies nothing — same verdict, deck byte-identical.

    View tool JSON
    {
      "name": "write_chunk",
      "kind": "act",
      "impl": "imperative",
      "description": "Apply an edited chunk to the open Fold — this CHANGES THE DECK the human is looking at and re-renders it. Send the whole <template data-origami-slide=...> element from read_chunk, edited; the slide id and kind are immutable and drift is rejected. Scripts, styles, iframes and remote URLs are ALLOWED but mark the deck \"active\" (recipients open it locked). Refused only when the content would break the single-file structure, or a data block fails its kind's schema. dryRun:true runs the WHOLE gate and applies nothing — same verdict, deck byte-identical.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "chunkId": {
            "type": "string",
            "description": "The chunk the edit was for"
          },
          "html": {
            "type": "string",
            "description": "The edited <template> element (a full chunk reply is fine too)"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Validate only: same verdict/error, nothing applied, deck byte-identical. Default false"
          }
        },
        "required": [
          "chunkId",
          "html"
        ]
      },
      "page": "/folio/"
    }
    add_chunkAction

    Add a new slide to the open Fold — this CHANGES THE DECK the human is looking at and re-renders it. Prefer add_fold when building a card from data; use this for raw markup, a starter, or a composite block. Defaults to a "free" slide with starter content at the end. Supply html for a built-in kind (get_kind_schema first), block + fields for a COMPOSITE defined in this Fold, or starter for a ready-made seeded fold (list_starters). dryRun:true builds and validates WITHOUT adding — same verdict, deck byte-identical.

    View tool JSON
    {
      "name": "add_chunk",
      "kind": "act",
      "impl": "imperative",
      "description": "Add a new slide to the open Fold — this CHANGES THE DECK the human is looking at and re-renders it. Prefer add_fold when building a card from data; use this for raw markup, a starter, or a composite block. Defaults to a \"free\" slide with starter content at the end. Supply html for a built-in kind (get_kind_schema first), block + fields for a COMPOSITE defined in this Fold, or starter for a ready-made seeded fold (list_starters). dryRun:true builds and validates WITHOUT adding — same verdict, deck byte-identical.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "kind": {
            "type": "string",
            "description": "Slide kind (default \"free\")"
          },
          "position": {
            "type": "integer",
            "minimum": 0,
            "description": "0-based insert index (default: end)"
          },
          "label": {
            "type": "string",
            "description": "Sidebar label (default: kind/def name)"
          },
          "html": {
            "type": "string",
            "description": "Slide inner markup; required for kinds other than \"free\""
          },
          "block": {
            "type": "string",
            "description": "A composite block kind (x.<name>) already defined in this Fold"
          },
          "fields": {
            "type": "object",
            "description": "Field values for the composite block (block instance values)"
          },
          "starter": {
            "type": "string",
            "description": "A ready-made fold from list_starters (roadmap | flowchart | node-graph | drawing | venn | ledger). Not combinable with html or block"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Validate only: same verdict/error, nothing added, deck byte-identical. Default false"
          }
        }
      },
      "page": "/folio/"
    }
    add_custom_foldAction

    Add a whole CUSTOM FOLD (a full page) as one fold — this CHANGES THE OPEN FOLD and re-renders it. Pass `html`, the fold's inner. Prefer add_fold when building a card from data; use this to paste a report verbatim, or for markup add_fold cannot express. For a page a human EDITS by clicking, use inline-editable blocks in a <div class="slide-inner">: h2/h3, p, p.lede, p.eyebrow, ul>li, .card-grid>.stat-card. Active content is ALLOWED but flags the deck active so recipients open it under the padlock; a stray <template>, an unbalanced <script>, or a data block that fails its schema is rejected.

    View tool JSON
    {
      "name": "add_custom_fold",
      "kind": "act",
      "impl": "imperative",
      "description": "Add a whole CUSTOM FOLD (a full page) as one fold — this CHANGES THE OPEN FOLD and re-renders it. Pass `html`, the fold's inner. Prefer add_fold when building a card from data; use this to paste a report verbatim, or for markup add_fold cannot express. For a page a human EDITS by clicking, use inline-editable blocks in a <div class=\"slide-inner\">: h2/h3, p, p.lede, p.eyebrow, ul>li, .card-grid>.stat-card. Active content is ALLOWED but flags the deck active so recipients open it under the padlock; a stray <template>, an unbalanced <script>, or a data block that fails its schema is rejected.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "html": {
            "type": "string",
            "description": "The fold's inner HTML — a whole page (editable Origami blocks in a .slide-inner, or a raw report)"
          },
          "label": {
            "type": "string",
            "description": "Sidebar label (default: \"Custom fold\")"
          },
          "position": {
            "type": "integer",
            "minimum": 0,
            "description": "0-based insert index (default: end)"
          }
        },
        "required": [
          "html"
        ]
      },
      "page": "/folio/"
    }
    move_chunkAction

    Move one chunk to a different place in the open Fold — this CHANGES THE DECK the human is looking at and re-renders it. `position` is the 0-based index the chunk ENDS UP at, counting hidden folds. Order only: no content, label or kind is touched. A position outside the deck is REFUSED rather than clamped, so a wrong index never silently means "last". One undo step.

    View tool JSON
    {
      "name": "move_chunk",
      "kind": "act",
      "impl": "imperative",
      "description": "Move one chunk to a different place in the open Fold — this CHANGES THE DECK the human is looking at and re-renders it. `position` is the 0-based index the chunk ENDS UP at, counting hidden folds. Order only: no content, label or kind is touched. A position outside the deck is REFUSED rather than clamped, so a wrong index never silently means \"last\". One undo step.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "chunkId": {
            "type": "string",
            "description": "Chunk id from list_chunks"
          },
          "position": {
            "type": "integer",
            "minimum": 0,
            "description": "0-based index to move it to (0 = first)"
          }
        },
        "required": [
          "chunkId",
          "position"
        ]
      },
      "page": "/folio/"
    }
    set_chunk_metaAction

    Set one chunk's label, speaker notes or hidden flag — this CHANGES THE DECK the human is looking at. `label` is the sidebar and tab name; `notes` is presenter text that never renders; hidden:true takes a fold out of the show without deleting it, and set_chunk_meta({chunkId, hidden:false}) is the ONLY way back for a fold delete_chunk hid. Fields you do not pass are left alone ("" clears). Content and kind are untouched. One undo step.

    View tool JSON
    {
      "name": "set_chunk_meta",
      "kind": "act",
      "impl": "imperative",
      "description": "Set one chunk's label, speaker notes or hidden flag — this CHANGES THE DECK the human is looking at. `label` is the sidebar and tab name; `notes` is presenter text that never renders; hidden:true takes a fold out of the show without deleting it, and set_chunk_meta({chunkId, hidden:false}) is the ONLY way back for a fold delete_chunk hid. Fields you do not pass are left alone (\"\" clears). Content and kind are untouched. One undo step.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "chunkId": {
            "type": "string",
            "description": "Chunk id from list_chunks"
          },
          "label": {
            "type": "string",
            "maxLength": 200,
            "description": "Sidebar/tab label (\"\" clears it)"
          },
          "hidden": {
            "type": "boolean",
            "description": "true takes the fold out of the show; false puts it back"
          },
          "notes": {
            "type": "string",
            "description": "Speaker notes — never rendered on the fold (\"\" clears them)"
          }
        },
        "required": [
          "chunkId"
        ]
      },
      "page": "/folio/"
    }
    delete_chunkSensitive Action

    Hide or DELETE a slide in the open Fold — this CHANGES THE DECK the human is looking at. Default mode "hide" keeps the slide in the file but out of the show (recoverable — prefer it); mode "delete" removes the slide template entirely. A hidden fold comes back with set_chunk_meta({chunkId, hidden:false}); a deleted one only comes back through undo. Use propose_delete when the human should approve first.

    View tool JSON
    {
      "name": "delete_chunk",
      "kind": "transact",
      "impl": "imperative",
      "description": "Hide or DELETE a slide in the open Fold — this CHANGES THE DECK the human is looking at. Default mode \"hide\" keeps the slide in the file but out of the show (recoverable — prefer it); mode \"delete\" removes the slide template entirely. A hidden fold comes back with set_chunk_meta({chunkId, hidden:false}); a deleted one only comes back through undo. Use propose_delete when the human should approve first.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "chunkId": {
            "type": "string",
            "description": "Chunk id from list_chunks"
          },
          "mode": {
            "type": "string",
            "enum": [
              "hide",
              "delete"
            ],
            "description": "Default \"hide\""
          }
        },
        "required": [
          "chunkId"
        ]
      },
      "page": "/folio/"
    }
    define_blockAction

    Register (or update) a COMPOSITE BLOCK definition in the deck — a reusable typed component a human can still edit field by field: a template of inert primitives plus a field manifest. Author instances with add_chunk({block, fields}). The template MUST render inert — no <script>, <style>, <iframe>, on* handlers or remote URLs — and an active one is rejected. This CHANGES THE OPEN FOLD.

    View tool JSON
    {
      "name": "define_block",
      "kind": "act",
      "impl": "imperative",
      "description": "Register (or update) a COMPOSITE BLOCK definition in the deck — a reusable typed component a human can still edit field by field: a template of inert primitives plus a field manifest. Author instances with add_chunk({block, fields}). The template MUST render inert — no <script>, <style>, <iframe>, on* handlers or remote URLs — and an active one is rejected. This CHANGES THE OPEN FOLD.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "def": {
            "type": "object",
            "description": "the CompositeBlockDef",
            "properties": {
              "kind": {
                "type": "string",
                "description": "x.<name> — lowercase letters/digits/hyphens; never collides with built-ins"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "integer",
                "minimum": 1
              },
              "fields": {
                "type": "array",
                "description": "the human-edit contract — the Studio auto-generates a control per field",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "identifier, referenced in the template as {{name}}"
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "text",
                        "number",
                        "select",
                        "color"
                      ]
                    },
                    "label": {
                      "type": "string"
                    },
                    "options": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "required for type \"select\""
                    },
                    "default": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "name",
                    "type"
                  ]
                }
              },
              "template": {
                "type": "string",
                "description": "inert HTML using {{field}} placeholders (HTML-escaped at render)"
              },
              "schemaComment": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "required": [
              "kind",
              "name",
              "version",
              "fields",
              "template"
            ]
          }
        },
        "required": [
          "def"
        ]
      },
      "page": "/folio/"
    }
    list_block_defsAnswer

    List the composite block definitions registered in this deck (kind, name, version, fields). Use a kind with add_chunk({block, fields}).

    View tool JSON
    {
      "name": "list_block_defs",
      "kind": "answer",
      "impl": "imperative",
      "description": "List the composite block definitions registered in this deck (kind, name, version, fields). Use a kind with add_chunk({block, fields}).",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {}
      },
      "page": "/folio/"
    }
    list_startersAnswer

    The ready-made FOLDS you can add in one call: roadmap, flowchart, node-graph, drawing, venn, ledger. Each is a free card already holding one seeded data block, copied from the Studio's own palette. Add one with add_chunk({starter:"<key>"}). Use a starter when a seeded example is a fine base you will edit; use add_fold when you already have the data.

    View tool JSON
    {
      "name": "list_starters",
      "kind": "answer",
      "impl": "imperative",
      "description": "The ready-made FOLDS you can add in one call: roadmap, flowchart, node-graph, drawing, venn, ledger. Each is a free card already holding one seeded data block, copied from the Studio's own palette. Add one with add_chunk({starter:\"<key>\"}). Use a starter when a seeded example is a fine base you will edit; use add_fold when you already have the data.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {}
      },
      "page": "/folio/"
    }
    delete_blockSensitive Action

    Delete a composite block definition. Non-destructive to content: every placed instance keeps its baked output but loses its data-script, becoming plain inert content, so nothing dangles and the deck stays valid. This CHANGES THE OPEN FOLD.

    View tool JSON
    {
      "name": "delete_block",
      "kind": "transact",
      "impl": "imperative",
      "description": "Delete a composite block definition. Non-destructive to content: every placed instance keeps its baked output but loses its data-script, becoming plain inert content, so nothing dangles and the deck stays valid. This CHANGES THE OPEN FOLD.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "kind": {
            "type": "string",
            "description": "Block kind x.<name> from list_block_defs"
          }
        },
        "required": [
          "kind"
        ]
      },
      "page": "/folio/"
    }
    set_headerAction

    Set the deck-level masthead in the header bar: a subtitle line under the title and metadata chips (e.g. ["5 plants","Q3 2026"]). This CHANGES THE OPEN FOLD. The bar's COLOURS and thickness are theme tokens, not set here. Pass "" or [] to clear.

    View tool JSON
    {
      "name": "set_header",
      "kind": "act",
      "impl": "imperative",
      "description": "Set the deck-level masthead in the header bar: a subtitle line under the title and metadata chips (e.g. [\"5 plants\",\"Q3 2026\"]). This CHANGES THE OPEN FOLD. The bar's COLOURS and thickness are theme tokens, not set here. Pass \"\" or [] to clear.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "subtitle": {
            "type": "string",
            "maxLength": 200,
            "description": "A line under the deck title"
          },
          "chips": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 60
            },
            "maxItems": 8,
            "description": "Metadata chips"
          }
        }
      },
      "page": "/folio/"
    }
    set_deck_metaAction

    Set the deck TITLE and/or theme — this CHANGES THE DECK the human is looking at and re-renders it. `title` is the manifest and header-bar name; it does NOT rename the file. `themeName` renames the theme; ON ITS OWN IT CHANGES THE LABEL AND NOTHING ELSE — use apply_theme for colours. `themeTokens` patches CSS custom properties onto the ones in force (the rest survive); only the 17 the stylesheet reads are honoured, and braces, semicolons, angle brackets, @ and url() are rejected with nothing applied. One undo step.

    View tool JSON
    {
      "name": "set_deck_meta",
      "kind": "act",
      "impl": "imperative",
      "description": "Set the deck TITLE and/or theme — this CHANGES THE DECK the human is looking at and re-renders it. `title` is the manifest and header-bar name; it does NOT rename the file. `themeName` renames the theme; ON ITS OWN IT CHANGES THE LABEL AND NOTHING ELSE — use apply_theme for colours. `themeTokens` patches CSS custom properties onto the ones in force (the rest survive); only the 17 the stylesheet reads are honoured, and braces, semicolons, angle brackets, @ and url() are rejected with nothing applied. One undo step.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 200,
            "description": "Deck title (manifest + header bar); does not rename the file"
          },
          "themeName": {
            "type": "string",
            "maxLength": 60,
            "description": "Theme name, e.g. \"origami-default\" — a label, not a restyle"
          },
          "themeTokens": {
            "type": "object",
            "description": "CSS custom properties to patch, e.g. {\"accent\":\"#3F7268\"} — merged onto the theme in force"
          }
        }
      },
      "page": "/folio/"
    }
    set_fold_typeAction

    Set the deck's reading experience — this CHANGES THE OPEN FOLD. "deck" (default) is the card-stage: one fold at a time with tabs and pips. "scroll" is a continuous document: every fold stacked top to bottom (pair with document-kind folds). "ledger" is reserved. "deck" writes no key, so the file stays byte-stable.

    View tool JSON
    {
      "name": "set_fold_type",
      "kind": "act",
      "impl": "imperative",
      "description": "Set the deck's reading experience — this CHANGES THE OPEN FOLD. \"deck\" (default) is the card-stage: one fold at a time with tabs and pips. \"scroll\" is a continuous document: every fold stacked top to bottom (pair with document-kind folds). \"ledger\" is reserved. \"deck\" writes no key, so the file stays byte-stable.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "foldType": {
            "type": "string",
            "enum": [
              "deck",
              "scroll",
              "ledger"
            ],
            "description": "deck | scroll | ledger"
          }
        },
        "required": [
          "foldType"
        ]
      },
      "page": "/folio/"
    }
    inspect_renderAction

    SEE THE DECK YOU CANNOT SEE. Lays the open Fold out in a real browser, off-screen, reports each fold's geometry and names four defects it can PROVE: OVERFLOW, masthead CLIP, EMPTY fold, SVG label COLLISION. Stated viewport, 1280x720 default. Read `outcome` first: clean | defects | unknown — unknown = something was NOT measured; never ship on unknown. `clean` is true only for a clean WHOLE deck. Too big for the 15s budget? It answers with the folds it reached plus `remeasure`: pass that back as `foldIds` (or use `maxFolds`) to measure a subset directly.

    View tool JSON
    {
      "name": "inspect_render",
      "kind": "act",
      "impl": "imperative",
      "description": "SEE THE DECK YOU CANNOT SEE. Lays the open Fold out in a real browser, off-screen, reports each fold's geometry and names four defects it can PROVE: OVERFLOW, masthead CLIP, EMPTY fold, SVG label COLLISION. Stated viewport, 1280x720 default. Read `outcome` first: clean | defects | unknown — unknown = something was NOT measured; never ship on unknown. `clean` is true only for a clean WHOLE deck. Too big for the 15s budget? It answers with the folds it reached plus `remeasure`: pass that back as `foldIds` (or use `maxFolds`) to measure a subset directly.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "viewport": {
            "type": "object",
            "description": "Screen to measure against (default 1280x720). Width 320-3840, height 240-2160.",
            "properties": {
              "width": {
                "type": "integer",
                "description": "CSS px, 320-3840"
              },
              "height": {
                "type": "integer",
                "description": "CSS px, 240-2160"
              }
            }
          },
          "foldIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Measure ONLY these chunk ids (deck order is kept). An unknown id is refused. Folds not listed come back skipped:true and the deck-level `clean` stays false."
          },
          "maxFolds": {
            "type": "integer",
            "minimum": 1,
            "description": "Measure at most this many folds, from the top of the deck (after foldIds, if both are given)."
          }
        }
      },
      "page": "/folio/"
    }
    undoAction

    Reverse the LAST change to the open Fold and re-render it. One tool call is one undo step, so a run_batch of six is six steps. It covers every writer (origami_guide({topic:"tools"}) lists them). It does NOT cross create_deck or a Fold the human opened — both reset the stack — does not touch bytes already on disk, and does not cover a staged proposal (reject_proposal) or a saved theme (delete_theme). 50 steps deep, no redo. revert_to_saved drops a whole run_batch in one call.

    View tool JSON
    {
      "name": "undo",
      "kind": "act",
      "impl": "imperative",
      "description": "Reverse the LAST change to the open Fold and re-render it. One tool call is one undo step, so a run_batch of six is six steps. It covers every writer (origami_guide({topic:\"tools\"}) lists them). It does NOT cross create_deck or a Fold the human opened — both reset the stack — does not touch bytes already on disk, and does not cover a staged proposal (reject_proposal) or a saved theme (delete_theme). 50 steps deep, no redo. revert_to_saved drops a whole run_batch in one call.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {}
      },
      "page": "/folio/"
    }
    revert_to_savedAction

    Drop EVERY unsaved change on the open Fold in ONE call — NOT undo (one step at a time). Jumps to the last save_deck, or to how the Fold was created/opened if never saved, clearing the undo stack in the same move — the safe pivot after a bad run_batch. Cannot itself be undone. Touches no disk. Refuses when nothing is open or nothing is unsaved.

    View tool JSON
    {
      "name": "revert_to_saved",
      "kind": "act",
      "impl": "imperative",
      "description": "Drop EVERY unsaved change on the open Fold in ONE call — NOT undo (one step at a time). Jumps to the last save_deck, or to how the Fold was created/opened if never saved, clearing the undo stack in the same move — the safe pivot after a bad run_batch. Cannot itself be undone. Touches no disk. Refuses when nothing is open or nothing is unsaved.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {}
      },
      "page": "/folio/"
    }
    list_activityAnswer

    What has been DONE in this tab, newest first — one entry per tool call, whoever made it: seq, at, source (agent | human | console | replay), tool, ok plus the error, the target, ms and a one-line summary. The summary NEVER carries slide html, so reading the feed cannot cost what reading the deck costs — use read_chunk or export_deck for content. It is not the undo stack and not part of the Fold: nothing here is saved, a reload starts empty, and only the 500 newest are held.

    View tool JSON
    {
      "name": "list_activity",
      "kind": "answer",
      "impl": "imperative",
      "description": "What has been DONE in this tab, newest first — one entry per tool call, whoever made it: seq, at, source (agent | human | console | replay), tool, ok plus the error, the target, ms and a one-line summary. The summary NEVER carries slide html, so reading the feed cannot cost what reading the deck costs — use read_chunk or export_deck for content. It is not the undo stack and not part of the Fold: nothing here is saved, a reload starts empty, and only the 500 newest are held.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "limit": {
            "type": "integer",
            "minimum": 1,
            "description": "How many of the newest entries to return (default 50, capped at the 500 held)"
          }
        }
      },
      "page": "/folio/"
    }
    export_deckAnswer

    Hand YOURSELF the complete .origami.html text of the open Fold, as a string in the result — to hash it, diff it, or quote a fragment. It writes NOTHING, saves NOTHING and changes NOTHING, so calling this INSTEAD of save_deck ends the job with the work stranded in your context. save_deck stamps a fresh manifest.modified and this does not. A Fold over 4 MB is refused with its size — use save_deck.

    View tool JSON
    {
      "name": "export_deck",
      "kind": "answer",
      "impl": "imperative",
      "description": "Hand YOURSELF the complete .origami.html text of the open Fold, as a string in the result — to hash it, diff it, or quote a fragment. It writes NOTHING, saves NOTHING and changes NOTHING, so calling this INSTEAD of save_deck ends the job with the work stranded in your context. save_deck stamps a fresh manifest.modified and this does not. A Fold over 4 MB is refused with its size — use save_deck.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {}
      },
      "page": "/folio/"
    }
    save_deckAction

    FINISH THE JOB: re-validate the Fold and put it somewhere durable. READ THE RESULT — only one of three outcomes is a save. saved:true means bytes were written to a real file AND read back. opfs.written means it is safe in this browser's private storage — invisible outside the page and evictable; the human retrieves it with "Download last save". downloadStarted means a download was fired but the page cannot see where it landed, so it is NEVER reported as saved. When saved is false the human must still press Save — say so rather than reporting success. It never throws and never opens a picker, so always end on it. Safe to call repeatedly; it changes no content.

    View tool JSON
    {
      "name": "save_deck",
      "kind": "act",
      "impl": "imperative",
      "description": "FINISH THE JOB: re-validate the Fold and put it somewhere durable. READ THE RESULT — only one of three outcomes is a save. saved:true means bytes were written to a real file AND read back. opfs.written means it is safe in this browser's private storage — invisible outside the page and evictable; the human retrieves it with \"Download last save\". downloadStarted means a download was fired but the page cannot see where it landed, so it is NEVER reported as saved. When saved is false the human must still press Save — say so rather than reporting success. It never throws and never opens a picker, so always end on it. Safe to call repeatedly; it changes no content.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {}
      },
      "page": "/folio/"
    }
    propose_chunkAction

    Propose an edit to a chunk WITHOUT applying it — STAGED for a human (or another agent) to review (a "document PR"). It appears as a review card in the page; if nobody is watching, resolve it yourself with accept_proposal. Same edit contract as write_chunk, validated NOW so a broken proposal never reaches review. accept_proposal refuses with a 3-way view if the chunk changed since — never a silent overwrite. Returns a proposalId.

    View tool JSON
    {
      "name": "propose_chunk",
      "kind": "act",
      "impl": "imperative",
      "description": "Propose an edit to a chunk WITHOUT applying it — STAGED for a human (or another agent) to review (a \"document PR\"). It appears as a review card in the page; if nobody is watching, resolve it yourself with accept_proposal. Same edit contract as write_chunk, validated NOW so a broken proposal never reaches review. accept_proposal refuses with a 3-way view if the chunk changed since — never a silent overwrite. Returns a proposalId.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "chunkId": {
            "type": "string",
            "description": "The chunk to edit"
          },
          "html": {
            "type": "string",
            "description": "The edited <template> element (a full chunk reply is fine)"
          },
          "title": {
            "type": "string",
            "description": "Short summary of the change (the PR title)"
          },
          "prompt": {
            "type": "string",
            "description": "What you were asked to do (optional provenance)"
          },
          "author": {
            "type": "string",
            "description": "Who is proposing (default \"agent\")"
          }
        },
        "required": [
          "chunkId",
          "html"
        ]
      },
      "page": "/folio/"
    }
    propose_addAction

    Propose a NEW slide WITHOUT adding it — staged for review (the add equivalent of propose_chunk). Same content args as add_chunk; the content is rendered, baked and validated now, then a slide.insert is staged. It appears as a review card for a watching human; resolve it yourself with accept_proposal if nobody is.

    View tool JSON
    {
      "name": "propose_add",
      "kind": "act",
      "impl": "imperative",
      "description": "Propose a NEW slide WITHOUT adding it — staged for review (the add equivalent of propose_chunk). Same content args as add_chunk; the content is rendered, baked and validated now, then a slide.insert is staged. It appears as a review card for a watching human; resolve it yourself with accept_proposal if nobody is.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "kind": {
            "type": "string",
            "description": "Slide kind (default \"free\")"
          },
          "position": {
            "type": "integer",
            "minimum": 0,
            "description": "0-based insert index (default: end)"
          },
          "label": {
            "type": "string",
            "description": "Sidebar label"
          },
          "html": {
            "type": "string",
            "description": "Slide inner markup; required for kinds other than \"free\""
          },
          "block": {
            "type": "string",
            "description": "A composite block kind (x.<name>) already defined in this Fold"
          },
          "fields": {
            "type": "object",
            "description": "Field values for the composite block"
          },
          "starter": {
            "type": "string",
            "description": "A ready-made fold from list_starters. Not combinable with html or block"
          },
          "title": {
            "type": "string",
            "description": "Short summary (the PR title)"
          },
          "prompt": {
            "type": "string",
            "description": "What you were asked to do (optional provenance)"
          },
          "author": {
            "type": "string",
            "description": "Who is proposing (default \"agent\")"
          }
        }
      },
      "page": "/folio/"
    }
    propose_deleteSensitive Action

    Propose hiding or deleting a slide WITHOUT doing it — staged for review, as a card in the page and a queue entry you can resolve yourself. mode "hide" (default, recoverable) or "delete". accept_proposal refuses if the chunk is already gone.

    View tool JSON
    {
      "name": "propose_delete",
      "kind": "transact",
      "impl": "imperative",
      "description": "Propose hiding or deleting a slide WITHOUT doing it — staged for review, as a card in the page and a queue entry you can resolve yourself. mode \"hide\" (default, recoverable) or \"delete\". accept_proposal refuses if the chunk is already gone.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "chunkId": {
            "type": "string",
            "description": "Chunk id from list_chunks"
          },
          "mode": {
            "type": "string",
            "enum": [
              "hide",
              "delete"
            ],
            "description": "Default \"hide\""
          },
          "title": {
            "type": "string",
            "description": "Short summary (the PR title)"
          },
          "prompt": {
            "type": "string",
            "description": "Why (optional provenance)"
          },
          "author": {
            "type": "string",
            "description": "Who is proposing (default \"agent\")"
          }
        },
        "required": [
          "chunkId"
        ]
      },
      "page": "/folio/"
    }
    list_proposalsAnswer

    The review queue: every staged proposal with author, title, target chunk, the before/after content and a conflict flag (true if that chunk changed since). Empty until a propose_* call stages something. The human accepts or rejects by clicking the cards.

    View tool JSON
    {
      "name": "list_proposals",
      "kind": "answer",
      "impl": "imperative",
      "description": "The review queue: every staged proposal with author, title, target chunk, the before/after content and a conflict flag (true if that chunk changed since). Empty until a propose_* call stages something. The human accepts or rejects by clicking the cards.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {}
      },
      "page": "/folio/"
    }
    accept_proposalAction

    Accept a staged proposal — apply its edit to the open Fold immediately. Refuses if the target chunk changed since, returning the proposed and current content so you can re-propose against the new base (never a silent overwrite), and re-checks the data blocks against the CURRENT deck. Same action the human takes by clicking Accept, so use it when running unattended.

    View tool JSON
    {
      "name": "accept_proposal",
      "kind": "act",
      "impl": "imperative",
      "description": "Accept a staged proposal — apply its edit to the open Fold immediately. Refuses if the target chunk changed since, returning the proposed and current content so you can re-propose against the new base (never a silent overwrite), and re-checks the data blocks against the CURRENT deck. Same action the human takes by clicking Accept, so use it when running unattended.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "proposalId": {
            "type": "string",
            "description": "Proposal id from list_proposals"
          }
        },
        "required": [
          "proposalId"
        ]
      },
      "page": "/folio/"
    }
    reject_proposalAction

    Drop a staged proposal without applying it. The same action the human takes by clicking Reject on the card.

    View tool JSON
    {
      "name": "reject_proposal",
      "kind": "act",
      "impl": "imperative",
      "description": "Drop a staged proposal without applying it. The same action the human takes by clicking Reject on the card.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "proposalId": {
            "type": "string",
            "description": "Proposal id from list_proposals"
          }
        },
        "required": [
          "proposalId"
        ]
      },
      "page": "/folio/"
    }
    get_blockAnswer

    READ A DATA BLOCK BEFORE YOU REPLACE IT. Returns one data block's JSON exactly as stored, plus its caption and kind schema. Address it by chunkId + kind (chart | venn | flow | graph | gantt | draw | table) and nth when the fold holds more than one of that kind (0 = first, the default). Leave kind out to get EVERY data block on the fold — one call instead of one per block. set_block REPLACES a block rather than patching it, so read here, edit what you read, send the whole thing back. Changes nothing.

    View tool JSON
    {
      "name": "get_block",
      "kind": "answer",
      "impl": "imperative",
      "description": "READ A DATA BLOCK BEFORE YOU REPLACE IT. Returns one data block's JSON exactly as stored, plus its caption and kind schema. Address it by chunkId + kind (chart | venn | flow | graph | gantt | draw | table) and nth when the fold holds more than one of that kind (0 = first, the default). Leave kind out to get EVERY data block on the fold — one call instead of one per block. set_block REPLACES a block rather than patching it, so read here, edit what you read, send the whole thing back. Changes nothing.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "chunkId": {
            "type": "string",
            "description": "Chunk id from list_chunks"
          },
          "kind": {
            "type": "string",
            "enum": [
              "chart",
              "venn",
              "flow",
              "graph",
              "gantt",
              "draw",
              "table"
            ],
            "description": "One block kind: chart | venn | flow | graph | gantt | draw | table. Omit for every block on the fold"
          },
          "nth": {
            "type": "integer",
            "minimum": 0,
            "description": "Which block of that kind, 0-based (default 0)"
          }
        },
        "required": [
          "chunkId"
        ]
      },
      "page": "/folio/"
    }
    set_blockAction

    Replace the WHOLE JSON of one data block on one fold — this CHANGES THE DECK the human is looking at and re-renders it. Address it by chunkId + kind (chart | venn | flow | graph | gantt | draw | table) + nth (0 = first, the default) and pass the COMPLETE data: it REPLACES what is there, so call get_block first to keep part of it. The kind's validator runs before anything is applied and a bad shape is refused with the violation named; a table's formulas are baked; a flow/graph node with no `tone` and an edge with no `label` get "" (their legal blank, no meaning changes). A fold with no block of that kind is refused with the kinds it has — this never CREATES one, use add_fold. One undo step.

    View tool JSON
    {
      "name": "set_block",
      "kind": "act",
      "impl": "imperative",
      "description": "Replace the WHOLE JSON of one data block on one fold — this CHANGES THE DECK the human is looking at and re-renders it. Address it by chunkId + kind (chart | venn | flow | graph | gantt | draw | table) + nth (0 = first, the default) and pass the COMPLETE data: it REPLACES what is there, so call get_block first to keep part of it. The kind's validator runs before anything is applied and a bad shape is refused with the violation named; a table's formulas are baked; a flow/graph node with no `tone` and an edge with no `label` get \"\" (their legal blank, no meaning changes). A fold with no block of that kind is refused with the kinds it has — this never CREATES one, use add_fold. One undo step.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "chunkId": {
            "type": "string",
            "description": "Chunk id from list_chunks"
          },
          "kind": {
            "type": "string",
            "enum": [
              "chart",
              "venn",
              "flow",
              "graph",
              "gantt",
              "draw",
              "table"
            ],
            "description": "The block kind to replace: chart | venn | flow | graph | gantt | draw | table"
          },
          "data": {
            "type": "object",
            "description": "The block's COMPLETE JSON — call get_kind_schema(kind) for the shape"
          },
          "caption": {
            "type": "string",
            "maxLength": 200,
            "description": "Optional: the caption under the figure (\"\" clears it; left alone when absent)"
          },
          "nth": {
            "type": "integer",
            "minimum": 0,
            "description": "Which block of that kind, 0-based (default 0)"
          }
        },
        "required": [
          "chunkId",
          "kind",
          "data"
        ]
      },
      "page": "/folio/"
    }
    add_foldAction

    BUILD A WHOLE FOLD IN ONE CALL — this CHANGES THE DECK the human is looking at and re-renders it. Give the card as DATA: an eyebrow, a heading, then `blocks` in order. Each entry names EXACTLY ONE of chart | venn | flow | graph | gantt | draw | table (that kind's own JSON plus an optional caption — get_kind_schema for the shape) or text (an HTML string: p, p.lede, h3, ul/li), bullets (strings), stats (up to 4 {value,label}), quote ({text,by}). Every data block is validated before anything is added and a bad one is refused naming the block index and the violation; tables are baked; flow/graph node `tone` and edge `label` get "" when absent (their legal blank, no meaning changes). columns:2 lays the blocks in two tracks. A chart or graph that names no size is SIZED TO FIT the card. One call is ONE fold and ONE undo step. Returns the chunkId and the (kind, nth) address of every data block, which is what set_block takes.

    View tool JSON
    {
      "name": "add_fold",
      "kind": "act",
      "impl": "imperative",
      "description": "BUILD A WHOLE FOLD IN ONE CALL — this CHANGES THE DECK the human is looking at and re-renders it. Give the card as DATA: an eyebrow, a heading, then `blocks` in order. Each entry names EXACTLY ONE of chart | venn | flow | graph | gantt | draw | table (that kind's own JSON plus an optional caption — get_kind_schema for the shape) or text (an HTML string: p, p.lede, h3, ul/li), bullets (strings), stats (up to 4 {value,label}), quote ({text,by}). Every data block is validated before anything is added and a bad one is refused naming the block index and the violation; tables are baked; flow/graph node `tone` and edge `label` get \"\" when absent (their legal blank, no meaning changes). columns:2 lays the blocks in two tracks. A chart or graph that names no size is SIZED TO FIT the card. One call is ONE fold and ONE undo step. Returns the chunkId and the (kind, nth) address of every data block, which is what set_block takes.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 200,
            "description": "The fold's heading (rendered as the h2) and the default sidebar label"
          },
          "eyebrow": {
            "type": "string",
            "maxLength": 80,
            "description": "The small label line above the heading, e.g. \"Q3 review\" (omit for none)"
          },
          "label": {
            "type": "string",
            "maxLength": 200,
            "description": "Sidebar/tab label (default: the title, trimmed to ~28 characters)"
          },
          "position": {
            "type": "integer",
            "minimum": 0,
            "description": "0-based insert index (default: the end)"
          },
          "columns": {
            "type": "integer",
            "minimum": 1,
            "description": "1 (default) or 2 — lay the blocks out in two tracks"
          },
          "blocks": {
            "type": "array",
            "description": "The card's content, in order. Each entry names exactly ONE of: chart | venn | flow | graph | gantt | draw | table | text | bullets | stats | quote — e.g. { \"chart\": { \"type\": \"bar\", \"labels\": [\"Q1\",\"Q2\"], \"series\": [{ \"name\": \"Revenue\", \"color\": \"#4A8CC4\", \"values\": [12,19] }], \"yMax\": null }, \"caption\": \"Revenue by quarter\" }, or { \"stats\": [{ \"value\": \"48\", \"label\": \"Decks shipped\" }] }, or { \"text\": \"<p class=\\\"lede\\\">A line of copy.</p>\" }. SIZE: a venn | flow | graph | gantt | table entry may add \"width\" and/or \"height\", whole CSS px (width 160-2600, height 120-2160) — leave them out and the card picks a height that fits 1280x720 (a default graph overflows it). A chart takes \"width\" only (its height is plotHeight in its own JSON, fitted for you); a draw block takes neither (wpct in its JSON). A size the runtime would ignore is REFUSED, not written",
            "items": {
              "type": "object"
            }
          }
        },
        "required": [
          "title",
          "blocks"
        ]
      },
      "page": "/folio/"
    }
    add_ledgerAction

    ADD A LEDGER FOLD IN ONE CALL — a titled card holding one live spreadsheet block. This CHANGES THE DECK the human is looking at and re-renders it. `formulas` maps an A1 cell to a formula and the calc engine RUNS them on the way in, so the file carries numbers, not an engine: {"D1":"=B1-C1","B5":"=SUM(B1:B4)"}. `totals` is a footer row: {"on":true,"fns":{"1":"SUM","2":"SUM"}} — the keys are COLUMN INDICES as strings, 0-based. `kpis` pins cards above the table: [{"name":"planTotal","ref":"B5"}] — `name` is an identifier, `ref` an A1 cell. A column `format` is an OBJECT — {"kind":"currency"|"number"|"percent"|"date"|"text"|"general"} — never a string. `currency` sets the PREFIX every currency column prints; without it the format library prints "$". Validated before anything is applied; a bad shape is refused with the violation named. One undo step.

    View tool JSON
    {
      "name": "add_ledger",
      "kind": "act",
      "impl": "imperative",
      "description": "ADD A LEDGER FOLD IN ONE CALL — a titled card holding one live spreadsheet block. This CHANGES THE DECK the human is looking at and re-renders it. `formulas` maps an A1 cell to a formula and the calc engine RUNS them on the way in, so the file carries numbers, not an engine: {\"D1\":\"=B1-C1\",\"B5\":\"=SUM(B1:B4)\"}. `totals` is a footer row: {\"on\":true,\"fns\":{\"1\":\"SUM\",\"2\":\"SUM\"}} — the keys are COLUMN INDICES as strings, 0-based. `kpis` pins cards above the table: [{\"name\":\"planTotal\",\"ref\":\"B5\"}] — `name` is an identifier, `ref` an A1 cell. A column `format` is an OBJECT — {\"kind\":\"currency\"|\"number\"|\"percent\"|\"date\"|\"text\"|\"general\"} — never a string. `currency` sets the PREFIX every currency column prints; without it the format library prints \"$\". Validated before anything is applied; a bad shape is refused with the violation named. One undo step.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 200,
            "description": "The fold's heading (rendered as the h2) and the default sidebar label"
          },
          "eyebrow": {
            "type": "string",
            "maxLength": 80,
            "description": "The small label line above the heading, e.g. \"Q3 review\" (omit for none)"
          },
          "label": {
            "type": "string",
            "maxLength": 200,
            "description": "Sidebar/tab label (default: the title, trimmed to ~28 characters)"
          },
          "position": {
            "type": "integer",
            "minimum": 0,
            "description": "0-based insert index (default: the end)"
          },
          "columns": {
            "type": "array",
            "description": "Column definitions: [{ label, align?: \"left\"|\"right\"|\"center\", format?: { kind: \"general\"|\"number\"|\"currency\"|\"percent\"|\"date\"|\"text\", decimals?: int } }]",
            "items": {
              "type": "object"
            }
          },
          "rows": {
            "type": "array",
            "description": "Cells, row by row, as strings: [[\"Rent\",\"1200\"], …]. Leave a formula cell \"\" — the calc engine fills it",
            "items": {
              "type": "array"
            }
          },
          "currency": {
            "type": "string",
            "maxLength": 8,
            "description": "The PREFIX every currency-kind column prints, e.g. \"€\" or \"EUR\" or \"£\". It is printed LITERALLY, not resolved from an ISO code: \"EUR\" gives \"EUR1,234.50\" and \"€\" gives \"€1,234.50\". Default: the format library's own \"$\""
          },
          "formulas": {
            "type": "object",
            "description": "A1 cell -> formula, e.g. { \"D1\": \"=B1-C1\", \"B5\": \"=SUM(B1:B4)\" }"
          },
          "named": {
            "type": "object",
            "description": "Named outputs other blocks can reference, e.g. { \"grandTotal\": \"=B3\" }"
          },
          "totals": {
            "type": "object",
            "description": "A footer row: { \"on\": true, \"fns\": { \"1\": \"SUM\", \"2\": \"SUM\" } } — keys are COLUMN INDICES as strings, 0-based"
          },
          "kpis": {
            "type": "array",
            "description": "Cards pinned above the table: [{ \"name\": \"planTotal\", \"ref\": \"B5\" }] — name is an identifier, ref an A1 cell or a cellName",
            "items": {
              "type": "object"
            }
          },
          "cellFormats": {
            "type": "object",
            "description": "Per-cell format overrides by A1 reference, e.g. { \"B3\": { \"kind\": \"currency\" } }"
          },
          "caption": {
            "type": "string",
            "maxLength": 200,
            "description": "The line under the table (default: none)"
          }
        },
        "required": [
          "title",
          "columns",
          "rows"
        ]
      },
      "page": "/folio/"
    }
    list_themesAnswer

    Every palette apply_theme can use: the four presets the runtime ships (origami-default, boardroom, meadow, dusk) plus anything save_theme kept in this browser, each with its complete token map and whether it is a preset or saved. Call this before apply_theme rather than guessing a name. Changes nothing.

    View tool JSON
    {
      "name": "list_themes",
      "kind": "answer",
      "impl": "imperative",
      "description": "Every palette apply_theme can use: the four presets the runtime ships (origami-default, boardroom, meadow, dusk) plus anything save_theme kept in this browser, each with its complete token map and whether it is a preset or saved. Call this before apply_theme rather than guessing a name. Changes nothing.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {}
      },
      "page": "/folio/"
    }
    apply_themeAction

    Put a whole named palette on the open Fold — this CHANGES THE COLOURS the human is looking at and re-renders it. `name` is a preset or saved theme from list_themes; an unknown name is refused with the ones that exist. Its tokens merge onto the ones in force, so tokens it does not name survive. This is the tool that restyles a deck: set_deck_meta({themeName}) only renames the label. One undo step.

    View tool JSON
    {
      "name": "apply_theme",
      "kind": "act",
      "impl": "imperative",
      "description": "Put a whole named palette on the open Fold — this CHANGES THE COLOURS the human is looking at and re-renders it. `name` is a preset or saved theme from list_themes; an unknown name is refused with the ones that exist. Its tokens merge onto the ones in force, so tokens it does not name survive. This is the tool that restyles a deck: set_deck_meta({themeName}) only renames the label. One undo step.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 60,
            "description": "A theme name from list_themes (preset or saved)"
          }
        },
        "required": [
          "name"
        ]
      },
      "page": "/folio/"
    }
    save_themeAction

    Keep a palette of your own so apply_theme can use it — stored IN THIS BROWSER, surviving a reload; it does NOT change the open Fold. `basedOn` takes a preset or saved theme as the base and merges your tokens on top, so a one-colour variant is one token. ONLY the 17 custom properties the deck stylesheet reads are accepted (list_themes reports them); anything else — primary, background, textColor — is REFUSED with that list, never stored and silently ignored. Returns a WCAG contrast report for ink/bg, ink/paper, accent/paper and chrome-ink/chrome, warning under 4.5:1. A preset name is refused; one of your own is replaced.

    View tool JSON
    {
      "name": "save_theme",
      "kind": "act",
      "impl": "imperative",
      "description": "Keep a palette of your own so apply_theme can use it — stored IN THIS BROWSER, surviving a reload; it does NOT change the open Fold. `basedOn` takes a preset or saved theme as the base and merges your tokens on top, so a one-colour variant is one token. ONLY the 17 custom properties the deck stylesheet reads are accepted (list_themes reports them); anything else — primary, background, textColor — is REFUSED with that list, never stored and silently ignored. Returns a WCAG contrast report for ink/bg, ink/paper, accent/paper and chrome-ink/chrome, warning under 4.5:1. A preset name is refused; one of your own is replaced.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 40,
            "description": "Theme key: lowercase letters, digits and hyphens, e.g. \"house-navy\""
          },
          "label": {
            "type": "string",
            "maxLength": 60,
            "description": "Human-facing name (default: the key)"
          },
          "tokens": {
            "type": "object",
            "description": "CSS custom properties, e.g. {\"accent\":\"#38628F\",\"ink\":\"#19222C\"} — merged onto basedOn when one is given"
          },
          "basedOn": {
            "type": "string",
            "maxLength": 60,
            "description": "A preset or saved theme whose tokens are the base (default: none — tokens stand alone)"
          }
        },
        "required": [
          "name",
          "tokens"
        ]
      },
      "page": "/folio/"
    }
    delete_themeSensitive Action

    Forget a theme you saved — it is GONE from this browser and apply_theme can no longer name it. undo does not cover this: a theme is not part of the Fold. A deck already wearing those colours KEEPS them, because a theme is applied by value. Presets cannot be deleted.

    View tool JSON
    {
      "name": "delete_theme",
      "kind": "transact",
      "impl": "imperative",
      "description": "Forget a theme you saved — it is GONE from this browser and apply_theme can no longer name it. undo does not cover this: a theme is not part of the Fold. A deck already wearing those colours KEEPS them, because a theme is applied by value. Presets cannot be deleted.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 40,
            "description": "A saved theme name from list_themes"
          }
        },
        "required": [
          "name"
        ]
      },
      "page": "/folio/"
    }
    run_batchAction

    Run several tool calls in ONE turn — this CHANGES THE DECK exactly as the same calls made one at a time would. `calls` is [{tool, args}] run IN ORDER, stopping at the FIRST failure; you get every result up to and including it, so a batch that half-lands says where it stopped and what landed. Each call takes the normal route, so the feed records every step and undo reverses them ONE AT A TIME. create_deck then five add_fold calls then apply_theme is one turn instead of seven. Nesting is refused, the cap is 25, and a read-only answer only arrives when the batch returns — put inspect_render last.

    View tool JSON
    {
      "name": "run_batch",
      "kind": "act",
      "impl": "imperative",
      "description": "Run several tool calls in ONE turn — this CHANGES THE DECK exactly as the same calls made one at a time would. `calls` is [{tool, args}] run IN ORDER, stopping at the FIRST failure; you get every result up to and including it, so a batch that half-lands says where it stopped and what landed. Each call takes the normal route, so the feed records every step and undo reverses them ONE AT A TIME. create_deck then five add_fold calls then apply_theme is one turn instead of seven. Nesting is refused, the cap is 25, and a read-only answer only arrives when the batch returns — put inspect_render last.",
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "calls": {
            "type": "array",
            "maxItems": 25,
            "description": "The calls, in order: [{ \"tool\": \"add_fold\", \"args\": { … } }, …]. Max 25. Stops at the first failure.",
            "items": {
              "type": "object",
              "properties": {
                "tool": {
                  "type": "string",
                  "description": "A tool name from this page"
                },
                "args": {
                  "type": "object",
                  "description": "That tool's arguments (omit for a tool that takes none)"
                }
              },
              "required": [
                "tool"
              ]
            }
          }
        },
        "required": [
          "calls"
        ]
      },
      "page": "/folio/"
    }