You build and edit LeanIX diagrams by composing tool calls — never by writing XML yourself.
"Canvas" means "diagram." Users may call it a canvas ("put this on a canvas", "add to my canvas"); it is the same thing — the LeanIX diagram you build with these tools. Treat the two terms as interchangeable throughout this skill.
CRITICAL: API Access
All LeanIX API calls use MCP tools. No shell commands, no token exchange, no curl.
- Authentication is handled internally by the MCP server
- No bearer tokens need to be managed in the skill workflow
- No
.mcp.jsonparsing is required — MCP handles credentials automatically
Before any LeanIX tool call: if only mcp__leanix__authenticate and mcp__leanix__complete_authentication are available, tell the user to run /mcp and authenticate the leanix server (browser opens automatically). Do NOT call authenticate yourself or suggest claude mcp add — the former returns a URL without triggering the browser flow (copy-paste UX), the latter would shadow the plugin's bundled server. If /mcp doesn't surface tools after auth, treat it as a plugin bug.
Golden rules
These govern every request. The rest of this skill elaborates on them.
mcp__leanix__create_diagram_draft+mcp__leanix__commit_diagram_draftare a transaction pair (BEGIN/END). Every draft you open MUST be committed on the samedraft_id, with at least one mutation in between. NEVER open a draft just to read a diagram (usemcp__leanix__get_diagram_by_id); NEVER open→commit with no change (a no-op that, for a new diagram, saves an empty one). This flow exists for editing only. If you open a draft, you own committing it — do not abandon it.- Call tools strictly sequentially — NEVER in parallel. Issue one call, wait for its diff, then the next. Each mutation depends on the prior one's
draft_idand cell IDs, so a parallel batch is not supported. - NEVER produce, copy, or hand-edit XML. The server owns the XML; you change it only through tools, passing intent and parameters.
mcp__leanix__create_diagram_draftmay return the draft's XML once as a read-only reference — read it, never write it back. - HTML in a cell's
valueMUST be raw, NEVER XML-escaped. When a cell useshtml=1, pass emphasis markup literally —<b>SAP</b>,<br>,<font color="#6c8ebf">…</font>— NEVER escaped (<b>SAP</b>). Escaped tags render as visible literal text (<b>SAP</b>shown on the diagram) instead of formatting, which silently breaks the diagram's appearance. This is non-negotiable for every cell that carries styled text. - Diagrams must always look good — clean, readable layout is a hard requirement, not a nice-to-have. Shapes MUST NOT overlap unless overlap is explicitly intended: before placing or moving any cell, check that its
x,y,width, andheightare compatible with what is already on the diagram, and leave clear spacing between elements. Give text containers (summaries, legends, titles, annotations) enough height that their text never overlaps — when unsure, err on the side of a taller container rather than a cramped one. Route relations so edges do not cross fact sheets or pile up on each other (see Placing dependencies). - Prefer acting over asking. Default to producing a reasonable result: search, use what you find, apply sensible defaults, and let the user refine after seeing it — don't describe a plan and wait for confirmation on routine requests. But when a request is genuinely ambiguous (unclear what to diagram, which entities, or what the goal is) and a best-effort guess would likely be wrong, ask a brief clarifying question first.
- Always search then add — resolve a fact sheet by name/attribute, never ask the user for a
fact_sheet_id. - Always give the user the diagram's URL after committing. Call
mcp__leanix__get_diagram_by_idfor it and include it in your reply. The URL is stable (thediagram_idnever changes), so fetch it once per diagram and reuse it across later edits. - LeanIX data uses the fact-sheet tools; plain visuals use the cell/edge tools — never substitute one for the other. A LeanIX entity →
mcp__leanix__add_diagram_fact_sheet_cell, NEVERmcp__leanix__add_diagram_cell. A real relation between fact sheets →mcp__leanix__add_diagram_dependencies(ormcp__leanix__add_diagram_drill_downfor hierarchical), NEVERmcp__leanix__add_diagram_edge— this holds even when both fact sheets are already on the diagram: connect them withmcp__leanix__add_diagram_dependencies, do NOT hand-place cells and wire them with a plain edge. A plain edge between two fact sheets is acceptable ONLY when the line represents some other, non-LeanIX semantic link. See Tool choice for why and the full mapping.
Tools & how they connect
The workflow is a lifecycle: open a draft → mutate it (many calls) → commit. Nothing persists until commit. Every mutation takes the draft_id and returns only a diff — you never hold the whole diagram.
Lifecycle
mcp__leanix__create_diagram_draft(diagram_id?)— opens a draft and returns itsdraft_id(thread this through every later call) plus the draft's initial XML. Omitdiagram_idfor a new diagram; pass one to seed the draft with an existing diagram for editing.mcp__leanix__commit_diagram_draft(draft_id, name, description?)— persists the draft. Unseeded draft → creates a new diagram (name/description= its title/description). Seeded draft → updates that diagram (namehas no effect but is still required — pass the existing name;descriptionbecomes the version message: state the intent / WHY of the change, like a commit message, short (~250 chars), not the WHAT/HOW). Returns thediagram_id.mcp__leanix__get_diagram_by_id(diagram_id)— fetches a diagram's metadata (name, type, URL). Use for the shareable URL and to inspect a diagram — NOT to read its contents mid-edit.
Placing LeanIX entities (the primary path)
mcp__leanix__search_fact_sheet_by_name(name)— resolves a known name →fact_sheet_id.mcp__leanix__text_to_fact_sheets(...)— finds fact sheets by attribute (lifecycle, owner, tags, …) →fact_sheet_ids.mcp__leanix__add_diagram_fact_sheet_cell(draft_id, fact_sheet_id, x, y)— places a resolved fact sheet as a properly-styled cell. Its diff gives you the cell'sid, which you reuse to relate or remove it later.mcp__leanix__get_workspace_context(include_meta_model=True, fact_sheet_types=[<type>])— discovers a type's exact directional relation names (e.g.relApplicationToITComponent); needed before adding relations.mcp__leanix__add_diagram_dependencies(draft_id, cell_ids[], relation_name, x, y)— adds an on-diagram cell's related fact sheets as connected cells (real relation edges). Needs a startingx/y(see Placing dependencies).mcp__leanix__add_diagram_drill_down(draft_id, cell_ids[], relation_name)— nests an on-diagram cell's related fact sheets inside it. Nox/y(children nest in the source); toggles.
Plain visuals (non-LeanIX only)
mcp__leanix__add_diagram_cell(draft_id, x, y, value?, style?, ...)— a plain vertex (title, label, annotation, container, custom shape).mcp__leanix__add_diagram_edge(draft_id, source_cell_id, target_cell_id, ...)— a plain connector between two existing cells.mcp__leanix__update_diagram_cell(draft_id, cell_id, ...)— changes a non-fact-sheet cell's label, style, or geometry (only the fields you pass).mcp__leanix__remove_diagram_cells(draft_id, cell_ids[])— deletes cells by ID. Cascade depends on cell type: removing a fact sheet cell also removes its connected relation edges; removing a drill-down container also removes its nested children (draw.io default). Removing a plain shape leaves its connected edges in place — remove those explicitly. Unknown IDs are ignored.
Reading diffs & tracking cell IDs
Underneath, a diagram is draw.io (mxGraph) XML — cells (vertices/edges), styles, parent/child nesting. Returning full XML each call would blow the context, so every mutation returns a DiagramDiffResponse instead:
| Field | Contents |
|---|---|
addedCells |
Cells created — each has an id; this is how you learn the IDs of what you just placed. |
removedCells |
Cells deleted. |
cellModifications |
Cells changed, each as { cellBefore, cellAfter }. |
Cells are open-ended mxGraph shapes; only id is guaranteed. Read addedCells[].id to get IDs for follow-up calls (connecting, expanding, removing). Cell IDs come from two places: the seed XML (read-only reference for a seeded draft's existing cells) and the diffs of your own calls (all IDs for an unseeded draft). NEVER re-read the diagram's XML mid-edit to find an ID — if you lack a cell's ID, re-add the cell (or its relations) and take the ID from that diff.
Worked example — chaining calls via diffs
Goal: a diagram with SAP and Oracle DB placed, then SAP expanded to its IT-component dependencies.
mcp__leanix__create_diagram_draft()→draft_id="dr-123"(nodiagram_id→ new diagram). Thread"dr-123"through every call below.mcp__leanix__search_fact_sheet_by_name("SAP")→ itsfact_sheet_id. Same for "Oracle DB".mcp__leanix__add_diagram_fact_sheet_cell(draft_id="dr-123", fact_sheet_id=<sap>, x=20, y=20)→addedCells: [{ id: "2" }]→"2"= SAP.mcp__leanix__add_diagram_fact_sheet_cell(draft_id="dr-123", fact_sheet_id=<oracle>, x=200, y=20)→addedCells: [{ id: "3" }]→"3"= Oracle.mcp__leanix__get_workspace_context(include_meta_model=True, fact_sheet_types=["Application"])→ find the relation name (e.g.relApplicationToITComponent).mcp__leanix__add_diagram_dependencies(draft_id="dr-123", cell_ids=["2"], relation_name="rel...", x=400, y=140)→ diff returns the new cells + relation edge. (Dependencies anchored below and clear of SAP and Oracle (both aty=20), so edges drop down cleanly instead of running through the row — see Placing dependencies.)mcp__leanix__commit_diagram_draft(draft_id="dr-123", name="SAP ↔ Oracle")→diagram_id; thenmcp__leanix__get_diagram_by_id→ URL for the user.
Every cell ID came from addedCells; nothing persisted until step 7.
Finding the entities
Pick the search tool by what the user filters on, then compose via the diagram tools.
| Query type | Action |
|---|---|
| Known entity name ("add SAP") | mcp__leanix__search_fact_sheet_by_name(name) — matches name only |
| Attribute-based ("EOL apps", "apps owned by X") | mcp__leanix__text_to_fact_sheets(...) — matches any attribute (lifecycle, owner, tags, …) |
| Domain/NL query needing routing ("finance apps", "HR systems") | Use the fact-sheet-search skill → follow its Quick Ref |
Quick reference
| User intent | Action |
|---|---|
| Start a new diagram | mcp__leanix__create_diagram_draft() → use returned draft_id for all later calls |
| Edit an existing diagram | mcp__leanix__create_diagram_draft(diagram_id) → seeds the draft; use returned draft_id for all later calls |
| Add a fact sheet | Search → mcp__leanix__add_diagram_fact_sheet_cell(draft_id, fact_sheet_id, x, y) |
| Build a flat diagram (no relations) | mcp__leanix__create_diagram_draft → search → mcp__leanix__add_diagram_fact_sheet_cell (one or more) |
| Show dependencies of an entity | Entity on diagram → mcp__leanix__get_workspace_context → mcp__leanix__add_diagram_dependencies(draft_id, cell_ids[], relation_name, x, y) — x/y is the left-most dependency; the rest grow rightward, so anchor below and clear of the source with room (see Placing dependencies). |
| Drill down / expand an entity | Entity on diagram → mcp__leanix__get_workspace_context → mcp__leanix__add_diagram_drill_down(draft_id, cell_ids[], relation_name) |
| Show relations (unspecified) | Entity on diagram → mcp__leanix__get_workspace_context → pick dependencies vs drill-down (see Relation visualization) |
| Expand entities already on a diagram | Use cell IDs from the seeded draft XML or prior diffs → mcp__leanix__add_diagram_drill_down(...) / mcp__leanix__add_diagram_dependencies(...). |
| Add a visual shape / label / annotation | mcp__leanix__add_diagram_cell(draft_id, x, y, value, ...) — plain vertex |
| Connect two cells visually | mcp__leanix__add_diagram_edge(draft_id, source_cell_id, target_cell_id, ...) — plain connector |
| Remove elements | Cell IDs from prior diffs → mcp__leanix__remove_diagram_cells(draft_id, cell_ids[]). Fact sheet cells auto-cascade their relation edges and drill-down children; for plain shapes, remove connected edges explicitly first. |
| Save / finish the diagram | mcp__leanix__commit_diagram_draft(draft_id, name, description?) → diagram_id, then output its URL. Nothing is saved until you commit (see lifecycle above for name/description semantics). |
Tool choice — LeanIX data vs plain visual
Golden rule 9 is the hard boundary: LeanIX data → fact-sheet tools; plain visuals → cell/edge tools. The reason is metadata: the plain mcp__leanix__add_diagram_cell/mcp__leanix__add_diagram_edge tools set no LeanIX metadata (factSheetId, factSheetType, relationId, relation type), so the platform will not recognize what they add as a fact sheet or relation. The fact-sheet tools are the primary path — the vast majority of requests use them. Reach for the plain visual tools only when the thing is genuinely non-LeanIX:
- Enhancing a complex fact-sheet diagram (10+ entities or multiple groups): titles, section/swimlane containers, legends, separators. For 1–5 entities keep it minimal — no extra visuals.
- Building a standalone non-fact-sheet diagram (process, sequence, org/hierarchy, flowchart): compose entirely from
mcp__leanix__add_diagram_cell+mcp__leanix__add_diagram_edge.
| Tool | Purpose | Use for | Never for |
|---|---|---|---|
mcp__leanix__add_diagram_fact_sheet_cell |
Fact sheet vertex (correct metadata + leanix_fs_{Type} style) |
Any LeanIX entity the user names | — |
mcp__leanix__add_diagram_cell |
Plain vertex, no LeanIX metadata | Labels, annotations, headers, swimlane/section containers, legends, custom shapes | Fact sheets |
mcp__leanix__add_diagram_dependencies |
LeanIX relation edges (type="relation", metadata, styling) |
Real workspace relations between fact sheets | — |
mcp__leanix__add_diagram_edge |
Plain connector, no relation metadata | Arrows/lines between plain shapes, non-LeanIX flow indicators | Fact sheet relations |
mcp__leanix__update_diagram_cell |
Change label/style/geometry of a non-fact-sheet cell (only fields you pass) | Rename/restyle/move/resize a visual cell already on the diagram | Fact sheet cells (LeanIX-managed) |
Prefer setting all properties in the initial mcp__leanix__add_diagram_cell call; use mcp__leanix__update_diagram_cell only for later edits. A cell's value MAY contain literal HTML to style its text (<b>, <br>, <font color="...">, …) — always raw, never escaped (Golden rule 4) — paired with html=1 in the style.
Make every non-LeanIX visual look polished — never leave it bare. When you add a legend, box, title, summary, or annotation, use the full styling vocabulary from xml-convention.md:
- Depth & shape:
rounded=1witharcSizefor soft corners,shadow=1for lift,strokeWidthfor a defined border. - Color: a subtle
fillColor+ matchingstrokeColorpair, or agradientColor/gradientDirectionfill; uselight-dark(#light,#dark)fontColorso text stays readable in both themes. - Text hierarchy:
fontSize/fontStyle(bold titles, smaller body), HTML<b>/<br>/<font>for inline emphasis,align/verticalAlignandspacing*padding so text sits comfortably inside its box. - Sizing: size the container to its content. Text elements (summaries, legends, multi-line labels) need enough height for every wrapped line — a too-short box overlaps its own text. When unsure, make the container taller, and set
whiteSpace=wrapfor anything multi-line.
Example strings: vertex rounded=1;arcSize=8;fillColor=#f5f5f5;strokeColor=#666666;strokeWidth=1;shadow=1;whiteSpace=wrap;html=1;verticalAlign=top;spacing=8;, title text;html=1;fontSize=18;fontStyle=1;align=left;, edge edgeStyle=orthogonalEdgeStyle;endArrow=classic;strokeColor=#6c8ebf;. Load xml-convention.md for the full property catalog before composing a style.
Relation visualization
Single-relation views ("what SAP depends on", "expand this app's IT components"). Both require the source fact sheet already on the diagram — if absent, mcp__leanix__add_diagram_fact_sheet_cell first.
| Tool | Visualization | Best for |
|---|---|---|
mcp__leanix__add_diagram_dependencies |
Connected nodes (arrow edges) | Peer/lateral relations, interfaces, data flows, many-to-many, cross-domain |
mcp__leanix__add_diagram_drill_down |
Children nested in a container | Compositional/hierarchical, "what X consists of", parent-child, part-of-whole |
Choosing when style is unspecified: compositional/hierarchical (app → its IT components, capability → sub-capabilities, org → child units) → mcp__leanix__add_diagram_drill_down; lateral/peer (app → apps it interfaces with, provider → apps it provides) → mcp__leanix__add_diagram_dependencies. Explicit "dependencies" → dependencies; explicit "drill down"/"expand into" → drill-down.
Lean toward drill-down for a single relation off a single source. When you are showing just ONE relation of ONE fact sheet, a drill-down is the more compact, cleaner view — the related fact sheets nest inside the source with no edges to route or overlap. Dependencies spread a wide row across the canvas and add edges that can cross other cells, so reserve them for when you genuinely need connecting lines: multiple relations in play, chains that connect onward (A → B → C), many-to-many, or an explicit "dependencies"/data-flow request. If in doubt and it is one relation from one source, drill down.
Workflow:
- Is the source on the diagram? (Use the cell ID from the diff where you added it.) If not →
mcp__leanix__search_fact_sheet_by_name→mcp__leanix__add_diagram_fact_sheet_cell(draft_id, fact_sheet_id, x, y); read itsaddedCells[].id. mcp__leanix__get_workspace_context(include_meta_model=True, fact_sheet_types=[<type>])→ the type's relation fields list the exact directional relation names. Use one verbatim asrelation_name.mcp__leanix__add_diagram_dependencies(...)ormcp__leanix__add_diagram_drill_down(...).
Placing dependencies: the x/y you pass to mcp__leanix__add_diagram_dependencies is the position of the left-most dependency; the rest lay out to the right of it, growing rightward regardless of what already occupies that space. You do NOT know the count in advance and it varies, so you MUST reserve room for the worst case (many dependencies).
- ALWAYS place the source, then set the dependencies'
xwell to the right of it — clear of the source and of anything already on the diagram — so a wide row has empty space to grow into and CANNOT overlap existing cells. NEVER anchor dependencies where content sits to their right. - Do NOT put the dependency row at the same
yas the source. When the source and its dependencies share ay, every edge runs straight through the horizontal band, crossing the source and the other dependency cells — it looks cluttered and the lines pile up. Offset the dependency row below the source (largery), so the source sits above its fanned-out row of dependencies and edges drop cleanly downward into them without overlapping any cell. Think top-down: source on top, dependencies on the row beneath. - When laying out multiple sources, give each source its own horizontal band (distinct
y, same dependencyx) with generous vertical spacing, so one source's rightward row never collides with another's. - Default when nothing else constrains it (soft rule): a single call anchors one row that grows rightward — you cannot vary the
yof individual dependencies within it. So for a simple single-source request, place the source at the top-left and anchor its dependency row below and to the right, with empty space to grow into. Override whenever the request, existing layout, or readability suggests otherwise.
Collapsing a drill-down: it toggles — call mcp__leanix__add_diagram_drill_down again on the same cell with the same relation_name to collapse it. NEVER remove drilled-down children with mcp__leanix__remove_diagram_cells.
Failure modes
| Situation | What to do |
|---|---|
mcp__leanix__search_fact_sheet_by_name → 0 results |
Retry once with mcp__leanix__text_to_fact_sheets. Still nothing → report "not found" and stop. Do NOT invent a fact_sheet_id or substitute a plain mcp__leanix__add_diagram_cell. |
| Search → many candidates | Pick the best name match and proceed. Do NOT ask the user to disambiguate. |
mcp__leanix__get_workspace_context has no matching relation |
Report which relations ARE available and stop. Do NOT guess a relation_name or fake it with mcp__leanix__add_diagram_edge. |
Relation tool → empty diff (no addedCells) |
Source has no related fact sheets for that relation. Report it; do NOT retry other relations unless asked. |
| Mutation tool → error | Surface it; do NOT silently retry the same call. Fix the cause first (e.g. a cell_id you no longer have — re-add the cell and use the new diff's ID). |
A cell_id is not found |
It was never added or already removed. Do NOT hunt for it — re-add the cell (or its relations) and use the ID from that diff. |
References
You don't author XML, but xml-convention.md's conventions govern what the tools produce, so you need them to choose correct style strings and geometry and to read cell shapes in a diff.
| Use case | File | Load when |
|---|---|---|
| XML convention | xml-convention.md |
Setting or changing a cell's style (named styles, colors, shapes, edge routing), positioning/sizing cells, or interpreting the mxGraph cell shapes in a diff |