Reference · MCP server 0.2.0

ohmyartifact reference

Everything a model needs to read and write ohmyartifact artifacts: the markdown formats, the overlay JSON schema, and each MCP tool with its parameters, result and error messages. Style rules (what makes a good slide) are in the server's authoring guide, oma://guide, and are not repeated here.

endpoint http://localhost:8080/mcptransport Streamable HTTP, statelessauth none

Overview

ohmyartifact is a local slide and document editor. Every artifact is one markdown string, stored as the source of truth. A person edits it on a canvas in the web app; a model edits it over MCP. Both write the same string, so edits from either side appear on the other within a few seconds.

  • Two kinds of artifact: document (one GFM markdown file) and slideshow (Marp-style: slides separated by ---, each with an optional ```overlays JSON block).
  • The MCP server runs inside the backend on the same port as the app (make dev in app/). It is always on, stateless, and unauthenticated; it sends the authoring guide as its instructions on connect.
  • Every write is optimistically concurrent: it carries the version you last read and is rejected as stale version if anyone saved in between. Every read and write returns the current version.
  • Every write is validated as a whole: an invalid overlay or an unsupported mermaid line rejects the entire write with one message per problem, and nothing is saved.

Connect

# Claude Code
claude mcp add --transport http --scope user ohmyartifact http://localhost:8080/mcp

# Any MCP client: .mcp.json / mcpServers
{ "mcpServers": { "ohmyartifact": { "type": "http", "url": "http://localhost:8080/mcp" } } }

The loop

  1. list_artifacts (optionally with a query) to find an id, then read_artifact. Keep the version; for a deck, the outline gives you slide numbers.
  2. Pick the narrowest write: edit_artifact for a sentence, replace_slide for one slide, insert_slide for a new one, update_artifact only to rewrite most of it.
  3. Send the version you read. On stale version, read again, reapply your change to the fresh text, and write once more. Do not loop.
  4. On invalid content, fix the fields named in the message and retry. Never drop an element to make an error go away.

The artifact

FieldTypeMeaning
idstringStable identifier, assigned on create. Used by every other tool and by the oma://artifacts/{id} resource.
kind"document" | "slideshow"Fixed at creation. Slide tools reject documents.
titlestringShown in the gallery. Independent of the markdown; change it with update_artifact.
versionintegerIncremented on every write. Sent with every write; stale values are rejected.
updatedAtstring (RFC 3339)Time of the last write.
markdownstringThe whole content. For decks this is the serialized deck (see Slide decks).

read_artifact returns the artifact both as structured JSON (the fields above, plus slides for decks) and as text in this shape:

[artifact] id=a1b2c3 kind=slideshow title="Platform reliability review" version=7 updatedAt=2026-08-25T14:02:11Z
[slides] 1: Platform reliability review · 2: Latency fell for the third straight quarter · 3: Three numbers that matter

### Q3 2026

# Platform reliability review
…

The first line is metadata; the [slides] line lists 1-based slide numbers with each slide's title (its first # heading, else its first heading of any level, else Slide N); the markdown starts after the blank line.

Documents

A document is ordinary GitHub-flavored markdown: headings, paragraphs, lists, tables, links, images, code fences, blockquotes. On write, \r\n is normalized to \n and any ```mermaid block is converted to a ```drawing block (see Diagrams). Nothing else is rewritten.

MarkdownRenders as
### Label directly above the first # TitleSmall uppercase accent eyebrow. Anywhere else, ### is a normal heading.
# Title, ## SectionSerif headline and section headings.
> textAccent callout. Intended for the thesis or recommendation, not quotations.
pipe tableCard table with a header row.
<!-- width: text; density: compact --> on the line above a tableThe editor's layout setting for that table (width: full | text | content). Leave it in place; do not add one yourself.
```mermaid flowchartConverted on save to an editable ```drawing diagram.
```drawing JSONA hand-editable diagram. Do not edit its JSON; add a new diagram with mermaid instead.

Slide decks

A deck is one markdown string. Slides are separated by a line containing only ---. Each slide is markdown, optionally followed by exactly one fenced ```overlays block holding a JSON array of positioned elements on a 1280×720 canvas.

### Summary

# Three numbers that matter

Availability beat the SLO for the fourth straight quarter while request volume grew 2.4×.

```overlays
[
  {"type":"stat","slot":"bottom","value":"99.97%","caption":"Availability · SLO 99.9%","color":"terracotta"},
  {"type":"stat","slot":"bottom","value":"184 ms","caption":"p95 latency · −31% QoQ"},
  {"type":"stat","slot":"bottom","value":"0","caption":"Sev-1 incidents · 2 last quarter"}
]
```

---

### Incidents

# One root cause behind both Sev-2s
…

Parsing rules

  • A leading YAML frontmatter block (------ at the very top) is stripped and ignored.
  • A --- line inside any fenced code block is content, not a separator. Empty chunks between separators are dropped. A deck always has at least one slide.
  • The overlays block is ```overlays on its own line, the JSON array, then ```. Only one per slide is parsed; whatever precedes and follows it is the slide body.
  • A slide's body starts with an optional <!-- background: URL --> line for a full-bleed background image (URL or data: URL). Removing the line removes the image.

Stored form

Every write re-serializes the deck: each slide is its trimmed body, then (if present) a blank line and the overlays fence with every element given an id; slides are joined with \n\n---\n\n and the deck ends with a newline. read_artifact returns this form, so old_text for edit_artifact must be copied from it.

How slide markdown renders

MarkdownRenders as
### Label directly above # TitleSmall uppercase accent eyebrow.
# TitleSerif headline; one per slide.
paragraph, bulletsBody copy in the left column at 22px. The body narrows to the left column when anything sits in a right-side slot.
> quoteAccent callout.
pipe tableCard table.
```mermaidA diagram, converted to ```drawing on save.
<!-- background: URL -->Full-bleed background image behind the slide.

Slide tools (replace_slide, insert_slide) take the markdown of one slide, without --- separators, and validate it the same way.

Overlays schema

The ```overlays block is a JSON array. Each element is one of five types, selected by type. The full JSON Schema (draft 2020-12) is served as the resource oma://schema/overlays; the server validates every write against it, choosing the variant by type so errors name the field that is wrong. Elements without an id get one on save. Keep the id of any element you were given so the app can tell an edit from a replacement.

Placement (all types)

FieldTypeDescription
idstringStable id; generated when omitted. Preserve it on edits.
slotenumWhere to place the element. Use this instead of coordinates. Several elements in bottom sit side by side; several in right stack.
xnumber 0–1280Left edge in px on the 1280×720 canvas. Only for exact placement; an element with x/y instead of slot was pinned by a person — leave it alone.
ynumber 0–720Top edge in px.
widthnumber > 0Optional explicit width.
heightnumber > 0Optional explicit height.
rotationnumber = 0Degrees clockwise.

Slots

righttop-rightbottombottom-leftbottom-right

text

FieldTypeDescription
typerequired"text"
textrequiredstring, non-emptyThe text.
fontSizenumber 12–120 = 32A label such as INTERNAL or DRAFT is a text in top-right at 16.
colortone = nonenone is plain text; terracotta an accent callout (at most one per slide); ivory/sand a neutral card; ink a dark card; blue/green/amber/red a tag or status.
{"type":"text","slot":"top-right","text":"INTERNAL","fontSize":16,"color":"blue"}

stat

FieldTypeDescription
typerequired"stat"
valuerequiredstring, non-emptyThe big number, e.g. "99.97%".
captionstringSmall label under the value; carries the unit and the delta.
colortone = ivoryCard tone.
{"type":"stat","slot":"bottom","value":"184 ms","caption":"p95 latency · −31% QoQ"}

panel

FieldTypeDescription
typerequired"panel"
bodyrequiredstring, non-emptyMarkdown: paragraphs, **bold**, *italic*, `code`, ~~struck~~, - bullets, 1. steps. An indented line under a list item is that item's caption.
eyebrowstringSmall uppercase label above the title, e.g. COST OF CHANGE.
metastringSmall mono text right-aligned on the eyebrow row, e.g. 3 files.
titlestringHeadline; one line, states the point.
footerstringMono line under a divider at the bottom, e.g. events › actions › providers.
colortone = terracottaAccent of the edge, eyebrow and code. ink makes the whole panel dark.
edge"top" | "left" = topWhich side carries the accent line.
{"type":"panel","slot":"right","eyebrow":"OPTION B","meta":"3 files","title":"Read-through cache",
 "body":"- Adds one hop on a miss\n  ~4 ms at p95\n- Invalidation stays in the writer",
 "footer":"edge › cache › api","color":"blue","edge":"left"}

image

FieldTypeDescription
typerequired"image"
srcrequiredstring (uri)Image URL or data: URL.
altstringAlternative text.
{"type":"image","slot":"right","src":"https://example.com/architecture.png","alt":"Request path after the cache"}

card

FieldTypeDescription
typerequired"card"
itemsrequiredarray of text | stat | panelIndependent items laid out side by side. Items carry their own type-specific fields (and an optional id) but no placement: no slot, x, y, width, height or rotation.
tonetone = noneBackground box around all items; none means no box, the items float together.
{"type":"card","slot":"bottom","tone":"sand","items":[
  {"type":"stat","value":"2.4×","caption":"Request volume YoY"},
  {"type":"stat","value":"+0.8%","caption":"Infra spend QoQ","color":"green"},
  {"type":"text","text":"Reserved capacity absorbed the growth.","fontSize":20}
]}

Tones

The one enum behind color on text/stat/panel and tone on card:

noneplain, no box
ivoryneutral card
sandneutral card, warmer
inkdark card, once per deck
terracottathe accent, once per slide
bluetag / status
greentag / status
ambertag / status
redtag / status

Validation

  • The block must be a JSON array of objects. Each object's type selects its schema; an unknown type is rejected.
  • Required fields, enums and numeric ranges are enforced exactly as listed above. Fields outside the schema are not validated.
  • Failures are collected per element and reported together as invalid content, prefixed with slide N: in deck-wide writes. Nothing is saved.

Diagrams

Write a fenced ```mermaid flowchart in a document or a slide body. On save the server converts it to a ```drawing block that a person can then rearrange by hand. Unsupported syntax rejects the write and names the block and line.

```mermaid
flowchart LR
  edge["EDGE<br/>Rate limit<br/>per-key tokens"] --> cache["NEW<br/>Read-through cache"]
  cache -->|hit| api["SERVICE<br/>API"]
  cache -->|miss| db(["STORE<br/>Postgres"])
  style cache fill:#ffec99,stroke:#f08c00
```
SupportedSyntax
Directionflowchart LR · flowchart TD
Nodesid[text] · id([text]) · id{text} · id[(text)]
Edges--> · --- · <--> · -->|label|
Stylestyle id fill:#hex,stroke:#hex
Node text<br/> splits a node into label / text / footer: a short uppercase label, the name, a detail.
Not supportedSubgraphs, other diagram types, anything else. The write is rejected with mermaid block N, line L: ….

Existing ```drawing blocks are hand-drawn diagrams; do not edit their JSON.

MCP tools

Eleven tools. Every tool returns a human-readable text line and a structured object. Read tools are annotated read-only; delete_artifact and delete_slide are annotated destructive. Parameters shared by all writes:

ParameterTypeDescription
idrequiredstringArtifact id.
versionrequiredintegerThe version you last read (from read_artifact, list_artifacts or a previous write). Rejected if stale.

Every successful write returns { id, kind, title, version } where version is the new version to pass to your next write.

list_artifacts

(query?)read-only

List all artifacts, newest first, or search them by words in the title or content.

ParameterTypeDescription
querystringOptional search words; prefix match on title and content.

Returns

- "Platform reliability review" — slideshow · id=a1b2c3 · version=7 · updated 2026-08-25T14:02:11Z
- "Q4 roadmap" — document · id=d4e5f6 · version=2 · updated 2026-08-24T09:41:00Z

Structured: { artifacts: [{ id, kind, title, version, updatedAt }] }. With no artifacts the text is No artifacts yet.; with a query that matches nothing, No artifacts match "…".

read_artifact

(id)read-only

Read one artifact as markdown. The first line is metadata; for decks a [slides] outline follows; the markdown starts after the blank line. Keep the version for your next write.

ParameterTypeDescription
idrequiredstringArtifact id.

Returns

Text as shown in The artifact. Structured: { id, kind, title, version, updatedAt, markdown, slides?: [{ index, title }] } (slides only for decks, index is 1-based).

Errors

  • no artifact with id "…"; use list_artifacts to find ids

create_document

(title, markdown)write

Create a new markdown document.

ParameterTypeDescription
titlerequiredstringTitle shown in the gallery. Must not be blank.
markdownrequiredstringFull markdown (see Documents).

Returns

Created document "Q4 roadmap" (id=d4e5f6) → version 1

Errors

  • title is required
  • invalid content: … (mermaid)

create_slideshow

(title, markdown)write

Create a new slide deck from Marp-style markdown: slides separated by a line containing only ---; each slide may end with one ```overlays JSON block.

ParameterTypeDescription
titlerequiredstringTitle shown in the gallery. Must not be blank.
markdownrequiredstringThe whole deck (see Slide decks).

Returns

Created slideshow "Platform reliability review" (id=a1b2c3) → version 1

Errors

  • title is required
  • invalid content:
    - slide 3: overlays[1].value: expected string, got number
    - slide 5: mermaid block 1, line 4: unsupported syntax …

edit_artifact

(id, version, old_text, new_text, replace_all?)write

Replace an exact snippet of the markdown with new text. Works for documents and decks. This is the tool for a sentence, a number, a caption.

ParameterTypeDescription
idrequiredstringArtifact id.
versionrequiredintegerVersion you last read.
old_textrequiredstringExact text to replace. Copy it from read_artifact, whitespace included. Must occur exactly once unless replace_all.
new_textrequiredstringReplacement text; may be empty to delete.
replace_allboolean = falseReplace every occurrence instead of requiring a unique match.

Returns

Edited slideshow "Platform reliability review" (id=a1b2c3) → version 8 (1 replacement)

Structured: { id, kind, title, version, replacements }. The result is validated like any write, so an edit that breaks an overlays block is rejected.

Errors

  • old_text is required
  • old_text was not found; read the artifact and copy the text exactly (whitespace included)
  • old_text occurs 3 times; include more surrounding text to make it unique, or set replace_all
  • stale version: "…" is now at version 9; read it again and retry with the current version

update_artifact

(id, version, markdown?, title?)write

Replace the whole markdown and/or rename an artifact. For small changes prefer edit_artifact or the slide tools.

ParameterTypeDescription
idrequiredstringArtifact id.
versionrequiredintegerVersion you last read.
markdownstringNew full markdown (document body or whole deck).
titlestringNew title.

At least one of markdown or title is required. Passing only title renames without touching the content (the version still increments).

Returns

Updated document "Q4 roadmap" (id=d4e5f6) → version 3

Errors

  • nothing to update: pass markdown and/or title
  • stale version: …
  • invalid content: …

replace_slide

(id, version, slide, markdown)write · decks

Replace one slide of a deck with new slide markdown (body plus optional ```overlays block). Other slides are untouched.

ParameterTypeDescription
idrequiredstringDeck id.
versionrequiredintegerVersion you last read.
sliderequiredinteger1-based slide number.
markdownrequiredstringMarkdown for this one slide; no --- separators.

Returns

Replaced slide 3 of slideshow "Platform reliability review" (id=a1b2c3) → version 5

Errors

  • "…" is a document, not a slide deck
  • slide 9 does not exist; the deck has 6 slides
  • invalid content: …
  • stale version: …

insert_slide

(id, version, markdown, after?)write · decks

Insert a new slide into a deck. By default it is appended; pass after to place it after a given slide.

ParameterTypeDescription
idrequiredstringDeck id.
versionrequiredintegerVersion you last read.
markdownrequiredstringMarkdown for the new slide; no --- separators.
afterinteger1-based slide number to insert after; 0 inserts first; omit to append.

Returns

Inserted slide 4 into slideshow "Platform reliability review" (id=a1b2c3) → version 6

Structured: { id, kind, title, version, index } where index is the 1-based position of the new slide.

Errors

  • after=9 is out of range; the deck has 6 slides (use 0 to insert first)
  • "…" is a document, not a slide deck
  • invalid content: …
  • stale version: …

move_slide

(id, version, from, to)write · decks

Move a slide to another position. The slide is removed from from and inserted so that it ends up at position to.

ParameterTypeDescription
idrequiredstringDeck id.
versionrequiredintegerVersion you last read.
fromrequiredinteger1-based slide number to move.
torequiredinteger1-based destination position.

Returns

Moved slide 4 → 2 in slideshow "Platform reliability review" (id=a1b2c3) → version 2

Errors

  • slide 9 does not exist; the deck has 6 slides
  • "…" is a document, not a slide deck
  • stale version: …

delete_slide

(id, version, slide)destructive · decks

Remove one slide from a deck. A deck keeps at least one slide.

ParameterTypeDescription
idrequiredstringDeck id.
versionrequiredintegerVersion you last read.
sliderequiredinteger1-based slide number.

Returns

Deleted slide 6 of slideshow "Platform reliability review" (id=a1b2c3) → version 9

Errors

  • a deck must keep at least one slide; delete the artifact instead
  • slide 9 does not exist; the deck has 6 slides
  • stale version: …

delete_artifact

(id)destructive

Permanently delete an artifact. No version check and no undo; only do this when the user asks for it by name.

ParameterTypeDescription
idrequiredstringArtifact id.

Returns

Deleted "Q4 roadmap" (id=d4e5f6)

Structured: { id, title }.

Errors

  • no artifact with id "…"; use list_artifacts to find ids

Resources

URIMIMEContents
oma://guidetext/markdownThe authoring guide: formats, what earns its place on a slide, overlay usage, diagrams, copy rules. Also sent as the server's instructions on connect. Read it once per session before your first write.
oma://schema/overlaysapplication/jsonThe JSON Schema (draft 2020-12) of the ```overlays array; the exact document every write is validated against.
oma://artifacts/{id}text/markdownThe raw markdown of one artifact (no metadata line). Same content as read_artifact's markdown.

Errors

Tool errors are plain messages written to be acted on. The complete set:

MessageCauseWhat to do
stale version: "T" is now at version N; read it again and retry with the current versionSomeone saved since you read.Read again, reapply your change to the fresh text, write once with version N.
invalid content:
- slide 3: …
- …
An overlay or mermaid block failed validation. One line per problem; nothing was saved.Fix the named fields and retry. Do not remove elements to silence the error.
no artifact with id "…"; use list_artifacts to find idsUnknown id.list_artifacts, then retry with a real id.
"T" is a document, not a slide deckA slide tool was called on a document.Use edit_artifact or update_artifact.
slide N does not exist; the deck has M slidesOut-of-range slide number.Use the outline from read_artifact.
after=N is out of range; the deck has M slides (use 0 to insert first)insert_slide with a bad after.Pass 0…M.
a deck must keep at least one slide; delete the artifact insteaddelete_slide on a one-slide deck.Replace the slide, or delete_artifact if the user asked.
old_text was not found; read the artifact and copy the text exactly (whitespace included)edit_artifact snippet does not occur.Copy the snippet from the stored form returned by read_artifact.
old_text occurs N times; include more surrounding text to make it unique, or set replace_allAmbiguous snippet.Widen the snippet, or set replace_all when every occurrence should change.
old_text is required · title is required · nothing to update: pass markdown and/or titleMissing argument.Pass it.
mermaid block N, line L: …Unsupported mermaid syntax (reported inside invalid content).Restrict the diagram to the supported subset.
internal error: …Storage failure.Report it to the user; do not retry in a loop.