Skip to content

Agent model ​

deckforge is built so that an AI agent can edit a presentation safely and you can check what it did. This page describes that contract: the tools the agent can call, the scope rules, the undo semantics, what the agent can never do, how it is grounded on your brief, and the security boundaries around it. It applies to the Copilot assistant in the editor and to agents that use deckforge mcp.

The loop ​

  1. Structured source. deck.yaml is the only source of truth. Each slide picks a template and fills its typed slots (text, richtext, list, cards, image…), so a change is a readable diff. See deck.yaml.
  2. Typed tools only. The agent changes the deck by calling deck tools such as update_slide, set_template and add_overlay. Each call is validated against the templates and themes before it is applied.
  3. Reversible turns. One request to Copilot is one undo step. The rail highlights the slides it changed and the drawer lists the slides and slots it touched.
  4. Audit. Download the agent log, a JSON file with every tool call of every turn, and review it next to the deck.yaml diff.

A whole-deck request: Copilot updates three slides, the rail highlights them, the drawer lists the slots it touched, and one click on Undo these changes restores them

Tool catalogue ​

The tables below are generated from the tool definitions in src/server/deck-tools.js (npm run docs:tools), and a test fails when they drift. Read tools work in every scope. Tools that change the deck are listed with the scope that allows them.

ToolChanges the deckAllowed in This slide scopeEditor chatdeckforge mcp
get_authoring_guide–––✓
get_deck–✓✓✓
list_templates–✓✓✓
list_themes–✓✓✓
update_slide✓✓✓✓
add_slide✓–✓✓
remove_slide✓–✓✓
move_slide✓–✓✓
set_hidden✓✓✓✓
set_template✓✓✓✓
set_theme✓–✓✓
list_assets–✓✓✓
set_image✓✓✓✓
add_overlay✓✓✓✓
update_overlay✓✓✓✓
remove_overlay✓✓✓✓
update_meta✓–✓✓

get_authoring_guide ​

Return the deckforge authoring guide for this deck: how to use these tools, design rules, the deck brief and the template catalogue (slots). Call it once before editing.

No parameters.

get_deck ​

Return the full deck: meta (title, lang, theme, footer, brief) and slides (id, template, hidden, title, data, notes) in order.

No parameters.

list_templates ​

List available slide templates with their description and slot schema (type, max, fields).

No parameters.

list_themes ​

List available themes (name, label, description).

No parameters.

update_slide ​

Update one slide. data merges top-level slot values (null deletes a slot). set assigns values by dotted path, e.g. {"cards.0.title": "New"}. Also updates notes, nav title or footer override.

ParameterTypeRequiredDescription
idstring✓Slide id
dataobject–Slot values to merge
setobject–Dotted slot path → value
notesstring–Speaker notes (replaces existing)
titlestring–Short title used in navigation (optional)
footerstring–Footer override for this slide (richtext); empty string to clear

add_slide ​

Insert a new slide using a template. Slots you do not provide show the template's sample text as editor-only placeholders (listed in the slide's placeholders, never published), so provide real data for every slot that should appear.

ParameterTypeRequiredDescription
templatestring✓
dataobject–
notesstring–
titlestring–Short navigation title
afterstring–Insert after this slide id (default: end of deck)
idstring–Optional explicit id (letters, digits, dashes)

remove_slide ​

Delete a slide.

ParameterTypeRequiredDescription
idstring✓Slide id

move_slide ​

Move a slide to a new 0-based index, or after another slide (after: null moves it first).

ParameterTypeRequiredDescription
idstring✓Slide id
indexinteger ≥ 0–
afterstring or null–

set_hidden ​

Hide or show a slide (hidden slides are skipped in the presentation).

ParameterTypeRequiredDescription
idstring✓Slide id
hiddenboolean✓

set_template ​

Change a slide's template. Compatible slot values are kept; values the new template does not use are kept in the slide's stash and restored if it switches back. Provide data for new slots, otherwise they become editor-only sample placeholders.

ParameterTypeRequiredDescription
idstring✓Slide id
templatestring✓
dataobject–

set_theme ​

Switch the deck theme.

ParameterTypeRequiredDescription
themestring✓

list_assets ​

List the image files already in the deck's assets/ folder (path, type, pixel size, slides using them). Only these files can be used; you cannot download images.

No parameters.

set_image ​

Put an image from assets/ into a slide's image slot (default: the template's first image slot). Always write a concise, descriptive alt text; when you cannot know what the image shows, derive it from the file name/slide context and tell the user it is a suggestion to check.

ParameterTypeRequiredDescription
idstring✓Slide id
pathstring✓Asset path from list_assets, e.g. assets/1a2b3c4d5e6f.png
altstring✓Alternative text (what the image shows, for screen readers)
fitcover, contain–cover crops to fill the frame, contain shows the whole image
focusstring–Focal point kept visible when cropping, "x% y%" (default "50% 50%")
slotstring–Image slot name (optional)

add_overlay ​

Add a freely positioned element above a slide's template (image, text, callout, arrow, shape). Geometry is in % of the 1280×720 slide. Keep overlays inside the slide and off the template's text. Prefer template slots; use overlays for annotations (arrows, callouts) and extra pictures.

ParameterTypeRequiredDescription
idstring✓Slide id
kindimage, text, callout, arrow, shape✓
xnumber✓Left edge, % of the slide width (0–100)
ynumber✓Top edge, % of the slide height (0–100)
wnumber✓Width, % of the slide width
hnumber✓Height, % of the slide height
zinteger–Stacking order among overlays (higher is in front)
rotatenumber–Rotation in degrees (0 to clear)
orderinteger or null–Reveal step (0 = with the first template element); omit to reveal after the template, null to clear
dataobject–Kind-specific fields, theme tokens only. text: {text (richtext), style: body|heading|title|caption|label, align: left|center|right, color: ink|muted|primary|accent}. callout: {text, tone: primary|accent|neutral, align}. arrow: {color: primary|accent|ink|muted, head: end|start|both|none, weight: thin|regular|bold, line: solid|dashed} (points right; use rotate). shape: {shape: rounded|rect|ellipse|pill, fill: primary|accent|neutral|paper|none, stroke: primary|accent|line|dashed|none}. image: {src: "assets/…" from list_assets, alt (required), fit: cover|contain, focus: "x% y%"}.

update_overlay ​

Move, resize or edit an overlay. Only the given properties change; data fields are merged.

ParameterTypeRequiredDescription
idstring✓Slide id
overlayIdstring✓
xnumber–Left edge, % of the slide width (0–100)
ynumber–Top edge, % of the slide height (0–100)
wnumber–Width, % of the slide width
hnumber–Height, % of the slide height
zinteger–Stacking order among overlays (higher is in front)
rotatenumber–Rotation in degrees (0 to clear)
orderinteger or null–Reveal step (0 = with the first template element); omit to reveal after the template, null to clear
dataobject–Kind-specific fields, theme tokens only. text: {text (richtext), style: body|heading|title|caption|label, align: left|center|right, color: ink|muted|primary|accent}. callout: {text, tone: primary|accent|neutral, align}. arrow: {color: primary|accent|ink|muted, head: end|start|both|none, weight: thin|regular|bold, line: solid|dashed} (points right; use rotate). shape: {shape: rounded|rect|ellipse|pill, fill: primary|accent|neutral|paper|none, stroke: primary|accent|line|dashed|none}. image: {src: "assets/…" from list_assets, alt (required), fit: cover|contain, focus: "x% y%"}.

remove_overlay ​

Delete an overlay from a slide.

ParameterTypeRequiredDescription
idstring✓Slide id
overlayIdstring✓

update_meta ​

Update deck metadata: title, subtitle, author, date, footer, description, lang, and brief {topic, audience, goal, sources[], duration}.

ParameterTypeRequiredDescription
metaobject✓

Scope rules ​

The drawer has two scopes. The editor adds the scope to each message, and the server enforces it on every tool call:

ScopeAllowed changesRefused
This slideupdate_slide, set_template, set_hidden, set_image, add_overlay, update_overlay and remove_overlay, only with the selected slide's idAny other slide (Out of scope), and add_slide, remove_slide, move_slide, set_theme and update_meta (not allowed)
Whole deckEvery toolNothing

A refused call changes nothing. The agent gets an error message and the drawer shows it as a red tool chip. Read tools (get_deck, list_templates, list_themes, list_assets) are always allowed. In slide scope, get_deck also returns scope: "this slide" and the slide's id.

Agents that use deckforge mcp have no scope: they work on the whole deck, like the Whole deck scope.

Undo semantics ​

  • One turn, one undo step. The editor opens an undo group when Copilot starts a turn and closes it when the turn ends, even on an error, a timeout (5 minutes) or Stop. The step is named Agent: <your request>.
  • Undo these changes, under the turn summary, or ⌘/Ctrl Z reverts the whole turn. Redo brings it back.
  • No mixed steps. While a turn runs, your own edits, uploads, undo and redo are refused, so a turn's undo step only contains Copilot's changes. Changes from deckforge mcp are refused too.
  • No empty steps. A turn that changes nothing adds no undo step. A tool call that writes the current value is ignored.
  • Outside the editor. Changes from deckforge mcp while the editor is open are named Copilot (outside the editor): <tool>. Calls less than 30 seconds apart share one undo step.
  • The editor keeps the last 200 undo steps.

Change summary and agent log ​

After each turn, the drawer shows a summary:

  • the number of slides changed and of tool calls made, with the number of failed calls;
  • each changed slide, with the slots and fields the agent wrote (for example eyebrow, cards.0.title, notes), and the deck settings it changed (theme, meta.brief…);
  • Undo these changes and Agent log.

Agent log downloads <deck>.agent-log.json, with one entry per turn since the editor started:

json
{
  "deck": { "title": "Shipping with confidence", "file": "deck.yaml" },
  "exportedAt": "2026-10-02T18:30:00.000Z",
  "turns": [
    {
      "turn": 1,
      "startedAt": "2026-10-02T18:29:41.120Z",
      "endedAt": "2026-10-02T18:29:52.418Z",
      "sessionId": "…",
      "prompt": "Tighten the copy on this slide",
      "scope": "slide",
      "slideId": "zoom",
      "calls": [
        { "tool": "get_deck", "args": {}, "ok": true, "slideId": null, "fields": [] },
        { "tool": "update_slide", "args": { "id": "zoom", "set": { "eyebrow": "04 / Zoom in" } }, "ok": true, "slideId": "zoom", "fields": ["eyebrow"] }
      ],
      "changed": ["zoom"],
      "summary": { "toolCalls": 2, "failed": 0, "slides": [{ "id": "zoom", "fields": ["eyebrow"] }], "deck": [] },
      "reply": "I shortened the eyebrow…",
      "error": null
    }
  ]
}

Each call has an ISO at timestamp too. Failed and refused calls have "ok": false and an error. The log stays in memory: it is never written to disk unless you download it, and it is cleared when the editor stops. It covers the editor's Copilot turns. Use git diff deck.yaml to review changes made through deckforge mcp.

What the agent can never do ​

  • Run anything else. The Copilot session only has the deck tools. It has no shell, file, web or code tools, and every other permission request is refused.
  • Change files directly. It cannot write deck.yaml, deck.html, templates, themes or any other file. Tools change the deck in memory, and the editor validates and saves it.
  • Fetch content. It cannot download, search or generate images. It can only place files that are already in the deck's assets/ folder: any other image path is refused.
  • Act outside a request. Tool calls are refused when no turn is running.
  • Leave its scope. In This slide scope it can only change the selected slide.
  • Bypass validation. Every change goes through the same operations as your own edits. Unknown templates, themes, overlay properties and deck settings are refused. After each change the deck is rebuilt and checked against its templates: unknown slots and text over a slot's maximum are reported as issues, and rich text is sanitized when it is rendered.

The ✨ Improve buttons use a separate, short-lived session with no tools at all. It returns text, and the editor applies it.

Grounding on the brief ​

The agent's instructions include:

  • the design rules (one idea per slide, short headlines, slot limits, no invented facts, metrics, dates or quotes);
  • the template catalogue, with each slot's type, maximum length and description;
  • the deck's meta.brief (topic, audience, goal, sources, duration), with the title, language and theme.

The instructions are built when the conversation starts. If you change the brief, start a New conversation, or ask Copilot to read the deck again: get_deck always returns the current meta.brief. Agents that use deckforge mcp get the same rules, catalogue and brief from get_authoring_guide. See Write a deck for the brief fields.

Security boundaries ​

  • The editor binds 127.0.0.1 on a random port. Every request needs the per-run token, sent as a cookie by the browser and as a header by deckforge mcp.
  • The server checks the Host header against DNS rebinding and the Origin header on every write. Writes need a JSON body.
  • The agent runs on your GitHub Copilot sign-in, through the Copilot SDK. deckforge stores no API key and sends no telemetry.
  • deckforge mcp reaches a running editor through a private record (~/.config/deckforge/editors/<id>.json, mode 0600) and is refused while the editor's own Copilot turn runs.

The security model has the full list.

External agents ​

deckforge mcp gives any MCP client (Copilot CLI, the Copilot app, or another agent) the same typed tools, plus get_authoring_guide. With the editor open, each change appears live and can be undone. Without it, the tools edit deck.yaml and rebuild deck.html. See Continue in Copilot CLI or the Copilot app.

See it ​

  • The animation at the top of this page: a whole-deck request, the highlighted slides and the turn summary, then one undo.
  • Agent-native decks: this page as an 8-slide deck, written from a brief by the Copilot skill and checked by its validation report.

Released under the MIT License.