edit_diagram
Changes diagram source with typed operations, on every kind, and checks the result. It is a workflow of the MCP server and the HTTP API, with the title "Edit diagram".
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? | Changes diagram source with typed operations, on every kind, and checks the result. |
| What does it replace? | rewriting the source by hand and the draw_diagram check. |
| How do I call it over HTTP? | POST /v1/diagrams/edit |
| 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? | maxEditOperations, maxEditTextLength. 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. Changes diagram source with typed operations, on every kind, and checks the result. Each op and its fields: rename (target, to: an id everywhere it is used, or a label), set (target, or from and to for an edge, or target "header"; option, value, null removes it), add_node (id, label, type, options; into a block or after a statement; statement for a whole line), remove (target, with its block and edges), add_edge (from, to, label, arrow, options), remove_edge (from, to, label), move (target, into: a block, or null for the top level), replace_text (find, replace, all). Untouched lines keep their comments and layout. Each op reports applied, or why not with a hint, and a failed one is skipped. svg "never" (the default) lays nothing out; auto or always draws as draw_diagram does, with viewUrl and saveCommand. Replaces rewriting the source by hand and the draw_diagram check.
Steps
| Operation | Runs | What it does |
|---|---|---|
parse | only when needed | Parse the source and return its errors, without laying it out or linting it. |
outline | only when needed | Describe a valid diagram as plain text. |
validate | only when needed | Parse the source and lint it: every error, and the quality, icon, and group-type warnings. |
render | only when needed | Lay out and paint a valid diagram in one color scheme. |
One call performs at most 4 operations.
Example call
The arguments of the call, as JSON:
{ "source": "flow \"Login\" direction=right\n# the sign-in path\nnode user person \"User\"\nnode api service \"Auth API\"\nnode db database \"Users DB\"\nuser -> api : \"POST /login\"\napi -> db : \"look up user\"", "operations": [ { "op": "rename", "target": "api", "to": "gateway" }, { "op": "set", "target": "db", "option": "icon", "value": "simple:postgresql" }, { "op": "add_node", "id": "cache", "type": "database", "label": "Session cache" }, { "op": "add_edge", "from": "gateway", "to": "cache", "label": "store session" } ]}The source argument, written out one line at a time:
flow "Login" direction=right# the sign-in pathnode user person "User"node api service "Auth API"node db database "Users DB"user -> api : "POST /login"api -> db : "look up user"Prompts that lead to it
- Rename the Auth API node to gateway and add a session cache it writes to.
- Move the database into the data group and remove the old queue.
Arguments
| Field | Type | Required | Description |
|---|---|---|---|
source | string | yes | Diagram source; the first line is the header, such as flow "Title" (draw_diagram lists every kind). at least 1 character; at most 50000 characters. |
operations | object[] | yes | Applied in order. |
operations[].op | "rename" | "set" | "add_node" | "remove" | "add_edge" | "remove_edge" | "move" | "replace_text" | yes | |
operations[].target | string | no | Id, label, table.column, or "header". at most 2000 characters. |
operations[].to | string | no | rename: the new name. Edges: the end. at most 2000 characters. |
operations[].from | string | no | Edges: the start. at most 2000 characters. |
operations[].option | string | no | at most 40 characters; pattern ^[a-z][A-Za-z0-9_-]*$. |
operations[].value | string | null | no | set: null removes the option. |
operations[].id | string | no | at most 2000 characters. |
operations[].label | string | no | add_edge on sankey: the amount. at most 2000 characters. |
operations[].type | string | no | add_node: node type, or statement word (c4 person, tree dir). at most 2000 characters. |
operations[].options | object | no | |
operations[].statement | string | no | add_node: the whole line, for a kind with no plain node statement. at most 2000 characters. |
operations[].into | string | null | no | A block; null is the top level. |
operations[].after | string | no | at most 2000 characters. |
operations[].arrow | "<-->" | "-->" | "<->" | "..>" | "->" | "--" | no | |
operations[].find | string | no | at most 2000 characters. |
operations[].replace | string | no | at most 2000 characters. |
operations[].all | boolean | no | |
svg | "auto" | "always" | "never" | no | never (the default) draws nothing; auto or always draws like draw_diagram. default "never". |
themes | "light" | "dark" | "both" | no | light, dark, or both (the default): the light picture and the dark one a dark page needs. default "both". |
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 |
|---|---|---|---|
source | string | yes | The edited source. Lines no operation touched are byte for byte as they were, comments included. |
changed | boolean | yes | |
results | object[] | yes | One entry for each operation, in order. |
results[].op | "rename" | "set" | "add_node" | "remove" | "add_edge" | "remove_edge" | "move" | "replace_text" | yes | |
results[].applied | boolean | yes | |
results[].lines | integer[] | yes | Lines of the new source this operation wrote, or the lines it removed from. |
results[].message | string | yes | |
results[].hint | string | null | yes | |
valid | boolean | yes | |
kind | string | null | yes | |
errors | Diagnostic[] | yes | |
errors[].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. |
errors[].severity | "error" | "warning" | yes | Errors stop rendering; warnings never do. |
errors[].message | string | yes | What is wrong, in one sentence. |
errors[].line | integer | yes | 1-based line in the submitted source. minimum 1. |
errors[].column | integer | yes | 1-based column in that line. minimum 1. |
errors[].length | integer | yes | Number of characters the problem covers. minimum 1. |
errors[].hint | string | null | yes | A concrete suggestion for fixing it, or null. |
warnings | Diagnostic[] | yes | Quality warnings; only when the result is drawn. |
warnings[].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. |
warnings[].severity | "error" | "warning" | yes | Errors stop rendering; warnings never do. |
warnings[].message | string | yes | What is wrong, in one sentence. |
warnings[].line | integer | yes | 1-based line in the submitted source. minimum 1. |
warnings[].column | integer | yes | 1-based column in that line. minimum 1. |
warnings[].length | integer | yes | Number of characters the problem covers. minimum 1. |
warnings[].hint | string | null | yes | A concrete suggestion for fixing it, or null. |
summary | string | null | yes | |
outline | string[] | yes | |
outlineTruncated | boolean | yes | |
size | Size | null | yes | |
renders | object[] | yes | |
renders[].theme | "light" | "dark" | yes | |
renders[].svg | string | yes | |
renders[].size | Size | yes | Natural size of a rendered diagram in px. |
renders[].size.width | integer | yes | Width in px. minimum 1. |
renders[].size.height | integer | yes | Height in px. minimum 1. |
picturesOmitted | object | no | Present only when pictures were left out because the answer would have been too big for an AI client to read in one piece. |
picturesOmitted.themes | ("light" | "dark")[] | yes | The color schemes whose pictures were left out. |
picturesOmitted.characters | integer | yes | How many characters the pictures left out came to. |
picturesOmitted.limit | integer | yes | The most characters one answer may hold before the pictures are left out. |
picturesOmitted.why | string | yes | Why they were left out, and what to do. |
viewUrl | string | no | A link that shows a valid diagram in a browser; give it to the person who wants to look. The source is in the part after #, never sent to a server, but anyone with the link can read it. Absent when too long; next says so. |
svgUrl | string | no | The viewUrl with &download=svg: a person opens it to download the SVG. Not a file address for curl; use saveCommand. Absent when too long. |
saveCommand | string | no | Run in a shell to save the SVG into the repository: the CLI decodes the link locally, fetches nothing, and embeds the source. Set --out to the path; add --theme dark for dark. Absent when too long. |
next | string | yes |
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 |
|---|---|---|
maxEditOperations | 50 | Operations in one call. |
maxEditTextLength | 2000 | Characters in one field of an operation, such as a statement, a find, or a replacement. |
See also
| Doc | Why |
|---|---|
| Tool reference | Every tool, with the workflows first |
POST /v1/diagrams/edit | The same operation over HTTP |
| Language reference | What the diagram source may contain |
| Writing diagrams with an agent | The loop in practice |