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
| Question | Answer |
|---|---|
| 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
| Operation | Runs | What it does |
|---|---|---|
convertMermaidFences | only when needed | Replace every convertible mermaid fence of a Markdown document with a diagram fence. |
renderPair | always | Draw 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:
{ "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:
# 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
| Field | Type | Required | Description |
|---|---|---|---|
markdown | string | yes | The 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. |
documentPath | string | yes | Where 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. |
assetsDir | string | no | The 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. |
prefix | string | no | Text 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. |
convertMermaid | boolean | no | When 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" | no | sync (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" | no | inline (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". |
page | integer | no | Which page of files to return, from 1; pictures are paged in document order, light and dark together. minimum 1. |
pageSize | integer | no | Pictures per page, at most 30. Setting page or pageSize turns paging on. minimum 1; maximum 30. |
settings | object | no | The repo's plotwork.config.json, if any. Schema: https://docs.plotwork.dev/schemas/plotwork.config.json (opens in a new tab). |
Result
| Field | Type | Required | Description |
|---|---|---|---|
mode | "sync" | "check" | yes | The mode that ran. |
markdown | string | yes | The 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. |
changed | boolean | yes | True 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. |
files | object[] | yes | The 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[].path | string | yes | Where to write the file, relative to the repository root. |
files[].content | string | no | The file contents; absent when files is none. |
files[].encoding | "utf8" | yes | How content is encoded. Always utf8: the files are SVG text. |
files[].bytes | integer | no | Only when files is none: the size of the file in bytes. |
files[].sha256 | string | no | Only when files is none: the SHA-256 of the content, in hex, to check a file fetched later. |
files[].viewUrl | string | no | Only when files is none: a link that shows this diagram in a browser. |
report | object[] | yes | One entry per fence handled, in document order. |
report[].id | string | null | yes | The 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" | yes | diagram for a fence that was already Plotwork, mermaid for one that came from Mermaid. |
report[].kind | string | null | yes | The diagram kind, or null when it could not be read. |
report[].line | integer | yes | The 1-based line of the opening fence in the document that was sent. minimum 1. |
report[].status | "synced" | "converted" | "unchanged" | "skipped" | "failed" | yes | synced: 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[].diagnostics | Diagnostic[] | yes | Errors 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" | yes | Stable code to branch on; E-codes are errors, W-codes are warnings. |
report[].diagnostics[].severity | "error" | "warning" | yes | Errors stop rendering; warnings never do. |
report[].diagnostics[].message | string | yes | What is wrong, in one sentence. |
report[].diagnostics[].line | integer | yes | 1-based line in the submitted source. minimum 1. |
report[].diagnostics[].column | integer | yes | 1-based column in that line. minimum 1. |
report[].diagnostics[].length | integer | yes | Number of characters the problem covers. minimum 1. |
report[].diagnostics[].hint | string | null | yes | A concrete suggestion for fixing it, or null. |
report[].message | string | null | yes | A plain sentence when something needs saying, such as why a fence was skipped; otherwise null. |
summary | object | yes | |
summary.diagrams | integer | yes | Fences handled: synced, converted, unchanged, skipped, and failed together. |
summary.synced | integer | yes | |
summary.converted | integer | yes | |
summary.unchanged | integer | yes | |
summary.skipped | integer | yes | |
summary.failed | integer | yes | |
summary.mermaidIgnored | integer | yes | Mermaid fences left alone because convertMermaid was false. |
summary.files | integer | yes | Files to write. |
summary.outputBytes | integer | yes | Bytes in the returned document and files together. |
caps | object | yes | |
caps.hit | boolean | yes | True when a cap stopped some of the work. Nothing is cut silently: see messages. |
caps.messages | string[] | yes | One plain sentence per cap that was hit, saying what was skipped and what to do. |
caps.maxDiagrams | integer | yes | Most fences handled in one call. |
caps.maxOutputBytes | integer | yes | Most bytes in the returned document and files together. |
pages | object | no | Present when page or pageSize was sent: which pictures files holds, and the next page. |
pages.page | integer | yes | |
pages.pageSize | integer | yes | |
pages.pages | integer | yes | Pages in all. |
pages.pictures | integer | yes | Pictures in all, each a light and a dark file. |
pages.nextPage | integer | null | yes | The page to ask for next, or null on the last one. |
next | string | yes | The next step in one sentence. |
Behavior hints
| Hint | Value |
|---|---|
readOnlyHint | true |
destructiveHint | false |
idempotentHint | true |
openWorldHint | false |
Bounds
A bound that is reached is reported in the result; nothing is cut silently.
| Bound | Value | What it limits |
|---|---|---|
maxMarkdownLength | 10000000 | Characters in the document. |
maxDiagrams | 30 | Diagram fences, plus mermaid fences when converting, handled in one call; the rest are left alone and reported. |
maxOutputBytes | 2000000 | Bytes in the returned document and files together; fences that would pass it are not drawn and are reported. |
maxPathLength | 300 | Characters in the document path or the assets directory. |
maxPrefixLength | 32 | Characters in the file name prefix. |
maxSyncPageSize | 30 | Pictures in one page of files, with page and pageSize. |
See also
| Doc | Why |
|---|---|
| Tool reference | Every tool, with the workflows first |
POST /v1/markdown/sync | The same operation over HTTP |
| Language reference | What the diagram source may contain |
| Writing diagrams with an agent | The loop in practice |