Skip to content
Connect an agent
Workflows

sync_markdown

Turns the diagram fences of a Markdown document into pictures, as data. It is a workflow of the MCP server and the HTTP API, with the title "Sync Markdown".

TL;DR

QuestionAnswer
What is it?A workflow: one call does the work of several tools and returns one combined answer.
What does it do?Turns the diagram fences of a Markdown document into pictures, as data.
What does it replace?convert, validate, render and sync steps for every fence in a document.
How do I call it over HTTP?POST /v1/markdown/sync
Does it change anything?No. It is read-only and idempotent, and it talks to nothing outside itself. It saves no file and returns its answer as data.
Which bounds apply?maxMarkdownLength, maxDiagrams, maxOutputBytes, maxPathLength, maxPrefixLength, maxSyncPageSize. See the bounds below.
Where do I read its schema as data?An MCP client gets the name, description, and JSON Schema of every tool from tools/list.

What it does

Workflow. Turns the diagram fences of a Markdown document into pictures, as data. Send the whole document and its path. Returns the rewritten document (each fence folded into a "Diagram source" section with the picture and a text description, the rest byte for byte unchanged), the light and dark SVG files to write (paths from the repository root), and a report per fence with errors at lines of your document. convertMermaid true first turns each convertible mermaid fence into a diagram fence; one that cannot convert or has errors is left as it was and reported. Then write markdown to documentPath and each file to its path, and review the diff. mode "check" only reports. The SVGs can be large: files "none" returns each file's path, bytes, sha256, and viewUrl instead, and page with pageSize returns the files a page at a time. Writes no file itself. Limits: 10000000 characters, 30 diagrams, 2000000 bytes of output; caps says when one stopped work. Replaces the convert, validate, render and sync steps for every fence in a document.

Steps

OperationRunsWhat it does
convertMermaidFencesonly when neededReplace every convertible mermaid fence of a Markdown document with a diagram fence.
renderPairalwaysDraw one diagram in the light and the dark theme with its text description.

One call performs at most 31 operations.

Example call

The arguments of the call, as JSON:

json
{  "markdown": "# Login\n\n```diagram\nflow \"Login\"\nnode user person \"User\"\nnode api service \"Auth API\"\nuser -> api : \"POST /login\"\n```\n",  "documentPath": "docs/login.md",  "convertMermaid": true}

The markdown argument, written out one line at a time:

Markdown
# Login
```diagramflow "Login"node user person "User"node api service "Auth API"user -> api : "POST /login"```

Prompts that lead to it

  • Replace the Mermaid diagrams in README.md with Plotwork diagrams.
  • Render the diagram fences in docs/login.md and give me the files to write.

Arguments

FieldTypeRequiredDescription
markdownstringyesThe whole Markdown document, exactly as it is on disk, with its line endings. Up to 10000000 characters. at least 1 character; at most 10000000 characters.
documentPathstringyesWhere the document lives, relative to the repository root, with forward slashes, for example docs/guide.md. Picture links and file paths are worked out from it. at least 1 character; at most 300 characters.
assetsDirstringnoThe directory for the SVG files, relative to the repository root. The default is assets/diagrams next to the document. at least 1 character; at most 300 characters.
prefixstringnoText put in front of every picture file name, for documents that share one assets directory. By default a titled diagram is named from its title and an untitled one from the document name. at least 1 character; at most 32 characters.
convertMermaidbooleannoWhen true, every mermaid fence that can be converted is replaced by a diagram fence first, then drawn. Fences that cannot be converted are left exactly as they were and reported. default false.
mode"sync" | "check"nosync (the default) returns the rewritten document and the files to write. check changes nothing: it only reports whether a picture is stale or a fence would convert. default "sync".
files"inline" | "none"noinline (the default) returns each SVG file with its content. none returns each with path, bytes, sha256, and viewUrl instead, so the answer stays small. default "inline".
pageintegernoWhich page of files to return, from 1; pictures are paged in document order, light and dark together. minimum 1.
pageSizeintegernoPictures per page, at most 30. Setting page or pageSize turns paging on. minimum 1; maximum 30.
settingsobjectnoThe repo's plotwork.config.json, if any. Schema: https://docs.plotwork.dev/schemas/plotwork.config.json (opens in a new tab).

Result

FieldTypeRequiredDescription
mode"sync" | "check"yesThe mode that ran.
markdownstringyesThe document to write back: every diagram fence wrapped in its collapsed source section, with the picture block and text description under it, and everything else unchanged. In check mode this is the document that was sent.
changedbooleanyesTrue when the document or a file differs from what was sent. In check mode this says the pictures are stale or a fence would convert.
filesobject[]yesThe SVG files to write, light and dark for each picture that was refreshed, with paths relative to the repository root. Empty in check mode. A picture that was already current has no files.
files[].pathstringyesWhere to write the file, relative to the repository root.
files[].contentstringnoThe file contents; absent when files is none.
files[].encoding"utf8"yesHow content is encoded. Always utf8: the files are SVG text.
files[].bytesintegernoOnly when files is none: the size of the file in bytes.
files[].sha256stringnoOnly when files is none: the SHA-256 of the content, in hex, to check a file fetched later.
files[].viewUrlstringnoOnly when files is none: a link that shows this diagram in a browser.
reportobject[]yesOne entry per fence handled, in document order.
report[].idstring | nullyesThe picture id, which names the SVG files; null for a fence that has none (a mermaid fence that was not converted).
report[].source"diagram" | "mermaid"yesdiagram for a fence that was already Plotwork, mermaid for one that came from Mermaid.
report[].kindstring | nullyesThe diagram kind, or null when it could not be read.
report[].lineintegeryesThe 1-based line of the opening fence in the document that was sent. minimum 1.
report[].status"synced" | "converted" | "unchanged" | "skipped" | "failed"yessynced: the picture was written or refreshed. converted: a mermaid fence became a diagram fence and was drawn. unchanged: the picture was already current. skipped: a cap stopped it, see message. failed: it has errors, the fence is left as it was.
report[].diagnosticsDiagnostic[]yesErrors and warnings, with line numbers counted in the document that was sent. For a converted fence they point at the Mermaid lines.
report[].diagnostics[].code"E100" | "E101" | "E110" | "E111" | "E120" | "E121" | "E122" | "E123" | "E124" | "E125" | "E130" | "E131" | "E132" | "E133" | "E134" | "E135" | "E136" | "E137" | "E138" | "E139" | "E140" | "W200" | "W201" | "W202" | "W203" | "W204" | "W205" | "W206" | "W207" | "W500" | "M100" | "M101" | "M102" | "M103" | "M104" | "M110" | "M200" | "M201" | "M202" | "M203" | "M204" | "M205" | "M206" | "M207" | "M208" | "M209" | "M210" | "M211" | "M212" | "M213" | "M214" | "M215" | "M216" | "M217" | "M218" | "E300" | "E301" | "E302" | "E303" | "E304" | "W300" | "W301" | "W302" | "W303" | "M120" | "M220" | "M221" | "M222" | "E340" | "E341" | "E342" | "E343" | "E344" | "E345" | "W320" | "W321" | "W322" | "W323" | "M240" | "M241" | "M242" | "E360" | "E361" | "E362" | "E363" | "E364" | "E365" | "E366" | "E367" | "W330" | "W331" | "W332" | "W333" | "W334" | "M250" | "M251" | "E400" | "E401" | "E402" | "E403" | "W350" | "W351" | "W352" | "W353" | "M270" | "M271" | "E420" | "E421" | "E422" | "E423" | "E424" | "W360" | "W361" | "W362" | "W363" | "W364" | "M180" | "M280" | "M281" | "M282" | "E320" | "E321" | "E322" | "E323" | "E324" | "E325" | "E326" | "E327" | "E328" | "E329" | "E330" | "W310" | "W311" | "W312" | "W313" | "W314" | "M130" | "M131" | "M132" | "M230" | "M231" | "M232" | "M233" | "M234" | "M235" | "M236" | "M237" | "M238" | "E380" | "E381" | "E382" | "E383" | "W340" | "W341" | "W342" | "W343" | "M260" | "I100" | "I101" | "I102" | "I103" | "I104" | "I105" | "I200" | "I201" | "I202" | "I203" | "I204" | "I206" | "R100" | "R101" | "R200" | "R201" | "R202" | "R203" | "R204" | "R205" | "R206" | "R207" | "S100" | "S200" | "S101" | "S102" | "S103" | "S104" | "S201" | "S202" | "S203"yesStable code to branch on; E-codes are errors, W-codes are warnings.
report[].diagnostics[].severity"error" | "warning"yesErrors stop rendering; warnings never do.
report[].diagnostics[].messagestringyesWhat is wrong, in one sentence.
report[].diagnostics[].lineintegeryes1-based line in the submitted source. minimum 1.
report[].diagnostics[].columnintegeryes1-based column in that line. minimum 1.
report[].diagnostics[].lengthintegeryesNumber of characters the problem covers. minimum 1.
report[].diagnostics[].hintstring | nullyesA concrete suggestion for fixing it, or null.
report[].messagestring | nullyesA plain sentence when something needs saying, such as why a fence was skipped; otherwise null.
summaryobjectyes
summary.diagramsintegeryesFences handled: synced, converted, unchanged, skipped, and failed together.
summary.syncedintegeryes
summary.convertedintegeryes
summary.unchangedintegeryes
summary.skippedintegeryes
summary.failedintegeryes
summary.mermaidIgnoredintegeryesMermaid fences left alone because convertMermaid was false.
summary.filesintegeryesFiles to write.
summary.outputBytesintegeryesBytes in the returned document and files together.
capsobjectyes
caps.hitbooleanyesTrue when a cap stopped some of the work. Nothing is cut silently: see messages.
caps.messagesstring[]yesOne plain sentence per cap that was hit, saying what was skipped and what to do.
caps.maxDiagramsintegeryesMost fences handled in one call.
caps.maxOutputBytesintegeryesMost bytes in the returned document and files together.
pagesobjectnoPresent when page or pageSize was sent: which pictures files holds, and the next page.
pages.pageintegeryes
pages.pageSizeintegeryes
pages.pagesintegeryesPages in all.
pages.picturesintegeryesPictures in all, each a light and a dark file.
pages.nextPageinteger | nullyesThe page to ask for next, or null on the last one.
nextstringyesThe next step in one sentence.

Behavior hints

HintValue
readOnlyHinttrue
destructiveHintfalse
idempotentHinttrue
openWorldHintfalse

Bounds

A bound that is reached is reported in the result; nothing is cut silently.

BoundValueWhat it limits
maxMarkdownLength10000000Characters in the document.
maxDiagrams30Diagram fences, plus mermaid fences when converting, handled in one call; the rest are left alone and reported.
maxOutputBytes2000000Bytes in the returned document and files together; fences that would pass it are not drawn and are reported.
maxPathLength300Characters in the document path or the assets directory.
maxPrefixLength32Characters in the file name prefix.
maxSyncPageSize30Pictures in one page of files, with page and pageSize.

See also

DocWhy
Tool referenceEvery tool, with the workflows first
POST /v1/markdown/syncThe same operation over HTTP
Language referenceWhat the diagram source may contain
Writing diagrams with an agentThe loop in practice