Blog

MCP | 10 min read | 2026-04-25 | Updated 2026-04-26 | By Variant Team

Build Agent-Editable Presentation Decks with MCP

Design an MCP presentation workflow agents can safely edit: stable slide IDs, readable HTML/CSS source, previews, scoped edits, exports, and rollback.

Author: Variant Team. Variant is built by a small team working on HTML-native presentation tools, MCP workflows, and agent-editable decks.

If you want an AI agent to maintain a slide deck the way it maintains a codebase, the deck needs source, identity, preview, edit, version, and export. Without those pieces, the agent is guessing. Sometimes the guess looks fine. Sometimes it quietly ruins slide 12.

The short version: agent-editable decks need a tight MCP loop of create, read, preview, edit, version, and export. HTML and CSS are a good source format because agents can write them and humans can fix them without specialized tooling.

This post is the architecture pass: the tool surface, why slide previews matter, how scoped edits should work, and where humans stay in the loop. Here's a companion artifact you can inspect: download the agent-editable MCP deck.

#Quick answer

  • Agent-editable decks need tools for create, read, preview, edit, version, and export.
  • slide.preview is essential because slides are visual output, not just text.
  • Scoped edit tools keep one typo fix from becoming a full-slide rewrite.
  • HTML/CSS is a good source format because agents can write it and humans can fix it.
  • MCP turns those operations into typed tools an agent can call safely.

#Why "agent-editable" is different from "AI-generated"

Most AI slide tools are generators. You give a prompt, you get a deck. If something's wrong, you regenerate. That's fine for throwaway content. It falls apart the moment the deck matters.

The thing you actually want is closer to how an agent edits source files. Read the file. Understand the structure. Make a small change. Run the tests. Repeat. For decks, the equivalent loop is: list slides, fetch one, look at a rendered preview, apply a targeted edit, look again, move on. No regenerate-the-whole-thing-because-the-headline-was-wrong. No black-box "improve this slide" button. Just normal edit-and-verify, the way agents already work in code.

Agent applying a scoped edit to a slide on the Variant canvas
Agent applying a scoped edit to a slide on the Variant canvas

This requires three properties from the deck format and the editor:

  1. Slides are real, identifiable objects. Each slide has a stable ID. You can fetch it, replace it, move it, duplicate it.
  2. The source is readable to a model. HTML and CSS work. Proprietary slide languages don't.
  3. There's a way to see what just happened. A screenshot of the rendered slide, fetched as part of the same MCP call.

If any of those are missing, the agent will guess, and guessing is how you end up with a deck that looks fine in JSON and broken in person.

#The minimum tool surface

You don't need a hundred MCP tools. You need a handful that compose. Here's the set I keep coming back to, with rough purpose.

ToolPurpose
decks.listFind existing decks the user owns
deck.createStart a new deck from a brief or template
deck.getRead deck metadata and slide list
deck.listSlidesIterate slides without pulling full content
deck.updateChange deck-level settings (theme, title, dimensions)
deck.replaceAllSlidesBulk rewrite when the structure really did change
deck.exportRender to HTML, PDF, PPTX, or JSON
slide.getRead one slide's HTML/CSS
slide.replaceOverwrite one slide's content
slide.editApply a scoped edit (a string replace, attribute change, etc.)
slide.duplicateClone a slide as a starting point
slide.moveReorder
slide.previewRender the slide and return an image
slides.batchUpdateApply many edits atomically
selection.getSee what the human has selected in the canvas
asset.uploadGet an image into the deck
deck.versions.listSee the snapshot history
deck.version.restoreRoll back

That's the Variant set, and it's roughly the shape any MCP presentation editor will converge on. The interesting design choices are not which tools exist. They're how slide.edit is scoped, how slide.preview is shaped, and how versions work.

#Why slide.preview is the most important tool

Agents are confident. They will tell you the slide looks great. The slide does not look great. Without a rendered image in the loop, the agent is editing blind, and you are reviewing blind by proxy.

slide.preview closes that gap. The contract is simple: give me a slide ID, hand me back a PNG (or a base64 image content block, depending on your transport). The agent now has the same information a designer would have when glancing at the canvas.

Claude Code slide.preview call returning a rendered slide image
Claude Code slide.preview call returning a rendered slide image

A few things make slide.preview actually useful instead of theatre:

  • It returns a real render, not a CSS snapshot. Web fonts loaded, images fetched, scripts run if you allow them.
  • The viewport matches the export viewport. A preview at the wrong aspect ratio is worse than no preview, because it teaches the agent the wrong thing.
  • It's cheap enough to call after every edit. If preview takes 8 seconds, the agent will skip it. Aim for under a second on a warm path.
  • It returns as an image content block, so the model can actually look at it. Returning a URL is a cop-out. The model has to fetch it, which it often won't.

In Variant, slide.preview returns an image content block directly. Claude Code sees the slide. That single design choice changes the loop from "trust the JSON diff" to "trust your eyes."

#Scoped edits, the part everyone gets wrong

The biggest mistake in this category is treating every edit as a regeneration. User says "change the headline to 'Q3 results.'" Tool dutifully rewrites the whole slide. Layout shifts. The footer becomes a different colour. The chart turns into bullet points.

Scoped edits avoid this by being honest about what changed. There are roughly three flavours, and a good editor exposes all three:

  1. String or attribute edits. "Replace the text content of the <h1> with 'Q3 results.'" The agent sends a structural edit, not a new slide.
  2. Region replacements. "Replace the contents of <section data-region='hero'>." Useful when the agent is updating a logical chunk.
  3. Full slide replacements. When the structure really has changed. This is slide.replace, and it should be the rarest call, not the default.

In practice, you want slide.edit for the first two and slide.replace for the third. The agent should reach for the smallest tool that does the job, the same way it reaches for Edit over Write in normal coding.

A worked example. A user says: "Update slide 3 to use 'Q3' instead of 'Q2' everywhere, and swap the chart image."

// Bad: regenerate the whole slide.
{
  "tool": "slide.replace",
  "input": { "slideId": "sl_3", "html": "<section>...entire slide rewritten...</section>" }
}
// Good: scoped edit, two operations, atomic.
{
  "tool": "slides.batchUpdate",
  "input": {
    "ops": [
      { "kind": "text.replace", "slideId": "sl_3", "find": "Q2", "replace": "Q3", "all": true },
      { "kind": "asset.swap", "slideId": "sl_3", "selector": "img[data-role='chart']", "assetId": "ast_q3_chart" }
    ]
  }
}

The second version is auditable. You can see exactly what changed in version history. The user can revert one op without losing the other. The layout is guaranteed not to drift, because the layout was never touched.

#Versions are the safety net

Agents will, at some point, do something you didn't ask for. Maybe they misunderstand the brief. Maybe a tool call gets cut off. Maybe a model update changes the way it interprets "make it tighter." Doesn't matter. You need an undo that survives across sessions.

A reasonable shape:

  • Auto-snapshot before each agent-initiated edit. Cheap if you're storing diffs.
  • deck.versions.list returns timestamps, authors (human vs agent), and a short label.
  • deck.version.restore is one call. No "restore individual slide from version X" gymnastics on day one. Whole-deck restore covers 95% of cases.
  • Snapshots are not the same as exports. An export is a frozen artifact. A snapshot is a point you can restore to in place.

Once this exists, the social contract with the agent changes. You can let it try things. If it ruins slide 4, you restore. The cost of a mistake is one tool call, not an hour of cleanup.

Variant version history showing agent and human edits
Variant version history showing agent and human edits

#Connecting an agent: the config

Here's how you'd wire Variant into Claude Code with OAuth. This is the path I'd recommend for a single user on their own machine.

{
  "mcpServers": {
    "variant": {
      "transport": "http",
      "url": "https://app.variant.art/mcp",
      "auth": {
        "type": "oauth",
        "authorization_server": "https://app.variant.art/oauth"
      }
    }
  }
}

For headless use, like a CI job that regenerates a deck nightly, a scoped bearer token is cleaner.

{
  "mcpServers": {
    "variant": {
      "transport": "http",
      "url": "https://app.variant.art/mcp",
      "headers": {
        "Authorization": "Bearer ss_..."
      }
    }
  }
}

Drop either of those into your MCP config, restart the agent, and the tool list above shows up. From the agent's side, the deck is now a thing it can read, edit, and render.

#A workflow that actually holds up

Here's the loop I use when drafting a deck with Claude Code and Variant. Nothing exotic, but the order matters.

  1. Brief in plain English. "Six-slide deck pitching the Q3 roadmap to the leadership team. Keep it tight, no wall-of-text slides." Agent calls deck.create.
  2. Skim the structure. Agent calls deck.listSlides and reports the outline back. I either nod or rewrite the outline before any styling work happens. Cheap to fix at this stage.
  3. Style pass on slide 1. Agent generates slide 1, calls slide.preview, looks at the image, iterates. Once slide 1 looks right, the rest of the deck inherits the visual language.
  4. Generate the rest. Agent fills in slides 2–6 using slide 1 as the template. Each gets a slide.preview after generation.
  5. Human edit. I open the canvas, fix three things by hand that are faster to fix by hand than to describe.
  6. Targeted agent edits. "Make the chart on slide 4 use the brand teal." Agent uses slide.edit, not slide.replace. Previews after.
  7. Export. deck.export to PPTX for the leadership tool, single-file HTML for me.

Total time, maybe twenty minutes for a deck I'd previously spend an afternoon on. The win is not "AI made the deck." The win is "I never had to fight the deck to fix it."

#Failure modes, honestly

A few things that will bite you:

  • The agent forgets to preview. Add it to your system prompt. Or wrap edits so the tool returns the preview as part of the response. Don't trust voluntary preview calls.
  • Token sprawl on slide.get. Slides with embedded base64 images blow up context. Return image references, not data URIs, when the agent reads a slide.
  • Concurrent edits. Two agents, or an agent and a human, editing the same slide simultaneously will clobber each other unless you have either optimistic concurrency (versions on slides) or single-writer locks. Pick one before you ship.
  • "Looks fine" pixel diffs. A 1px shift in a footer is the kind of thing the agent will not flag and you will only see in PDF export. Always preview at export resolution at least once before exporting.
  • Brand drift. Without a constrained design system, the agent will quietly invent new shades of grey. A theme block in the deck schema, plus deck.update to set it, plus a system prompt that says "use only theme colours" gets you most of the way there.
  • Single-user MCP auth. OAuth is great until you want a team. Plan for scoped tokens and per-deck permissions earlier than you think.

#Where this is going

Decks are early. The same pattern applies to anything that's currently a generate-or-regenerate experience: documents, spreadsheets, design files, dashboards. The MCP shape converges on the same handful of verbs every time. List, get, edit, preview, export, version. If you're building in this space, get those right and resist the urge to add a hundred specialty tools.

#Try the loop

Variant is the MCP presentation editor I've been describing throughout. If you want to actually feel the loop, the fastest path is: connect Variant to Claude Code with the OAuth config above, ask it to draft a short deck, and edit the result both visually and through the agent. The interesting moment is the first time you watch it call slide.preview and self-correct.

#FAQ

Can the agent edit a deck I created in the canvas by hand? Yes. Decks are decks. The agent reads the same HTML you authored, edits it, and previews. There's no "AI deck" vs "human deck" split. That's a big part of the point.

What happens if the agent breaks a slide? You restore from deck.versions.list. Agent edits are snapshotted before they apply. If you catch it during the session, you can also just tell the agent to undo, and it'll patch back to the prior content using the version it has in context.

Why HTML and CSS instead of a structured slide schema? Because models write HTML and CSS very well, and humans can read and fix it. Structured schemas are easier to validate and harder to author. For a tool where the agent and the human both need to edit, the lingua franca wins.

Do I have to use Claude Code? No. Any MCP-capable client works: Codex, Cursor, custom agents, the works. Claude Code is the one I use most because the editing patterns map cleanly, but the MCP surface is client-agnostic.

How does this compare to "AI presentation generators" that don't expose tools? Different category. Generators are one-shot. Agent-editable decks are a loop. If you only ever use a deck once and never revise it, a generator is fine. The moment you need to update last quarter's deck for this quarter, you want the loop.

What about exports for stakeholders who don't use Variant? Export to PPTX or PDF and send it. The single-file HTML export is also nice for sharing a link without inflicting another tool on someone. The deck stops being editable once it's exported, which is usually what you want for a "final" version.