BTP Solution Diagram Generator

SkillFiles & storage

Generate SAP BTP (Business Technology Platform) solution architecture diagrams as native draw.io (.drawio) files following the official SAP BTP Solution Diagram guidelines (Fiori Horizon design system) and open them via a configured draw.io MCP server. USE WHEN: user asks to create/draw/design/sketch a BTP diagram, BTP architecture, BTP landscape, BTP solution diagram, BTP reference architecture, SAP Business Technology Platform diagram, or wants to visualize SAP BTP services (CAP, Build, Integration Suite, SAC, AI Core, HANA Cloud, Cloud Foundry, Kyma, Workzone, etc.) and their interdependencies in draw.io / drawio / diagrams.net. DO NOT USE FOR: non-BTP architecture diagrams, generic flowcharts, sequence/UML diagrams, or diagrams that should remain in Mermaid/PlantUML.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the BTP Solution Diagram Generator skill

What this skill tells your AI

The instructions your AI receives, as published by likweitan/abap-skills in skills/btp-diagram-generator/SKILL.md and read by ahel’s review.

Produces a .drawio file in the workspace that conforms to the SAP BTP Solution Diagram guidelines and opens it through whichever draw.io MCP server is configured.

⚡ Quick Path (use this first)

For the vast majority of diagrams, do not hand-write XML. Use the btp_builder Python package — it owns icon lookup, SAP palette, port pinning, label HTML, A4 sizing, SVG upscaling, and validation. A typical L1 diagram is ~20 lines.

# scripts/examples/task_center_arch.py — runnable end-to-end
import sys
from pathlib import Path
# Add the skill's scripts/ dir to sys.path so `btp_builder` imports work
# regardless of where the skill is installed
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from btp_builder import BtpDiagram

d = BtpDiagram(level="L1", title="Task Center Reference Architecture")

btp = d.btp_container(x=260, y=80, w=560, h=440)
sub = d.subaccount(parent=btp, label="Subaccount",
                   x=btp.x + 24, y=btp.y + 110, w=520, h=170)

wz = d.service("work zone",   in_=sub, x=sub.x + 60, y=sub.y + 60)
tc = d.service("task center", right_of=wz)
ci = d.service("cloud identity", below=tc)

eu = d.user("End User", x=60, y=btp.y + 100)
ac = d.app_client("Application Clients\n(Mobile or Desktop)", below=eu)

s4    = d.external("SAP S/4HANA\nOn-Premise Solutions",
                   x=btp.right_edge() + 40, y=btp.y + 70, kind="sap")
third = d.external("3rd Party\nApplications", below=s4, kind="non-sap")
cloud = d.external("SAP Cloud\nApplications", below=third, kind="sap")
idp   = d.idp("3rd-party Identity Provider",
              x=ci.center_x() - 140, y=btp.bottom_edge() + 60)

d.connect(eu, ac, direction="down")
d.connect(ac, wz, direction="right")
d.connect(wz, tc, kind="dblhd")
d.connect(tc, ci, kind="dblhd", direction="down")
d.connect(tc, s4); d.connect(tc, third); d.connect(tc, cloud)
d.connect(idp, ci, kind="dashed", direction="up")

d.save("btp-task-center-architecture.drawio")  # validates → raises on errors

Run via uv run python <script>.py from the repository root. save() validates first and raises ValueError with the full error list if anything is off; warnings are printed.

Quick-Path API surface

CallReturnsNotes
BtpDiagram(level, title)builderlevel ∈ L0/L1/L2. Drives icon size + label weight.
.btp_container(x,y,w,h, sub_label, env_label, with_logo)NodeRefLight-blue outer frame + SAP corner logo + Subaccount/Multi-Cloud labels.
.subaccount(parent, label, ...)NodeRefWhite card inside the BTP container.
.inner_card(parent, label, ...)NodeRefGeneric white sub-card (e.g. CIS service group).
.service(name, in_=, right_of=, left_of=, below=, above=)NodeRefname is fuzzy-matched via reference/icon-aliases.json (e.g. "task center", "cpi", "hana cloud").
.user(label, kind="sap")NodeRefkind ∈ sap / non-sap / highlight.
.app_client(label, ...)NodeRefGeneric mobile/desktop tile.
.external(label, kind="sap"/"non-sap", ...)NodeRefRight-side external system tile.
.idp(label, ...)NodeRef3rd-party Identity Provider tile.
.connect(src, tgt, kind, direction)edge idkindstd/dblhd/dashed/optional/auth/scim/trust/neutral. direction auto-pins ports — override with "right"/"left"/"up"/"down".
.save(path, validate=True)PathWrites XML. Validates inline (raises on errors).

Positional kwargs (right_of, left_of, below, above, in_) auto-place nodes — only set explicit x,y for the first anchor in each row/column.

Icon name discovery

from btp_builder import list_aliases, list_icons, lookup_icon
print(list(list_aliases())[:30])     # short names → canonical keys
print(lookup_icon("integration suite", "L1")["key"])

If lookup_icon raises IconNotFound, fall back to a styled tile (external(label, kind="sap")) and call it out in the final response.

Open the diagram

uv run python skills/btp-diagram-generator/scripts/open_diagram.py btp-task-center-architecture.drawio

Falls back to printing a https://app.diagrams.net/?…#create=... URL if no system opener is found. If a draw.io MCP tool is available in the runtime, prefer that — only call MCP tools that actually appear in the tool list.


Manual XML path (advanced / niche cases only)

Use this only when the Quick Path doesn't cover what you need (e.g. exotic legend variants, custom flow-protocol pills, novel layouts not yet supported by the builder). Everything below documents the underlying XML primitives the builder generates for you.

When to use

Trigger on requests like:

  • "Draw a BTP architecture for …"
  • "Generate a BTP solution diagram showing CAP + HANA Cloud + Build Workzone"
  • "Create a draw.io of our SAP BTP integration landscape"
  • "L0 / L1 / L2 BTP diagram for "

If the request is a generic flowchart, sequence diagram, or non-SAP architecture, do not use this skill — generate Mermaid or use a plain draw.io workflow instead.

Inputs to gather (ask once, concisely)

Before generating, confirm what is missing. Default to L1 if unspecified.

InputDefaultNotes
Audience levelL1L0 = business overview (no legend, neutral connectors); L1 = technical (services + main flows); L2 = detailed (data flows, protocols, components)
BTP services / SaaS apps(must ask)e.g. CAP, Build Code, Integration Suite (CPI/Event Mesh/API Mgmt), HANA Cloud, SAC, AI Core, Joule, Build Workzone, Identity Authentication, Destination, Connectivity
Non-BTP systems(optional)e.g. S/4HANA Cloud, SuccessFactors, Ariba, third-party SaaS, on-prem systems, end users
Environment / runtimeCloud FoundryCloud Foundry, Kyma, ABAP Environment
Region / multi-region?singleAffects grouping containers
Primary flows(must ask)What connects to what, direction, purpose
Output format.drawio + PNG.drawio always; optionally export PNG (-s 2), SVG, or PDF via draw.io CLI
Output filenamebtp-diagram.drawioSaved to workspace root unless user specifies

If the user gives a one-line prompt with enough services and a clear flow, proceed without asking — surface assumptions in the final response.

Component allowlist discipline

Before layout, create an explicit component allowlist from the request:

  1. List requested actors, clients, services, targets, and any architecturally required supporting services (e.g. CIS for authentication flows).
  2. Do not add familiar products as decoration. SAP Build Work Zone, SuccessFactors, or extra posting targets appear only when explicitly requested or required by a described flow.
  3. Preserve the requested sequence as an ordered edge list. E.g. Outlook → Document AI → Integration Suite → S/4HANA Cloud means exactly those adjacent edges — do not insert intermediary steps or additional targets.

Workflow

1. Map requirements to BTP icons

There is no mxgraph.sap.* stencil family. SAP BTP icons in draw.io are SVGs embedded as base64 inside shape=image;image=data:image/svg+xml,<base64>;… style strings, distributed via the SAP draw.io shape library XML files. Generating shape=mxgraph.sap.foo produces an empty square in the canvas — you have seen this fail.

For every requested service, obtain its real style string by looking it up in reference/icon-index.json:

  1. Preferred: Load reference/icon-index.json (~660 KB, 100 icons). It maps each icon title (e.g. 31068-sap-build-work-zone_sd) to its style string and library cell width/height. Match by substring against the requested service name.
  2. Source XML libraries (if you need a non-default size or a metadata field the index doesn't carry) live in reference/libraries/ — one size-M mxlibrary per icon set (foundational, integration suite, app-dev, AI, data-analytics, BTP-SaaS, all-in-one).
  3. Style donors: for compound patterns (subaccount cards, NETWORK boundaries, pill labels, legend cards), consult the curated reference/examples/ — 11 official editable diagrams (Task Center L0/L1/L2, Build Work Zone L2, Process Automation L2, Cloud Identity Services L1/L2, Private Link L2, SAP Start L2). Open any of them and copy the exact style string.
  4. If a service is genuinely missing from the library, use the styled fallback tile (see §3 below) and list it in the final response so the user can replace it.

Default icon geometry by audience level (matches the official examples — see reference/example-patterns.md §5):

SVG intrinsic size warning: Every icon in the SAP shape library has width="16" height="16" on its root <svg> element, even in the size-M set. draw.io rasterizes at that intrinsic size then upscales, producing a blurry icon. After extracting a base64 SVG, patch the root <svg width> and <svg height> to match the target cell size (e.g. 48 for L1) before re-encoding. Keep viewBox unchanged. See reference/example-patterns.md §11 for the Python helper.

LevelIcon sizeLabel
L050×50Arial 14 bold
L148×48Arial 14 bold
L232×32Arial 12 regular

The label goes in the cell's value= attribute and renders below the icon (the library style already sets verticalLabelPosition=bottom). Always keep the points=[[0,0,0,0,0],…] 12-anchor array from the library style so connectors snap cleanly.

2. Apply the SAP Fiori Horizon palette

Always use these colors only (never pick arbitrary fills). Source: SAP BTP Solution Diagram guideline — Foundation (Atoms) and Areas:

TokenHexUse
SAP/BTP border (Primary)#0070F2BTP container stroke, accent fills, sub-card stroke
SAP/BTP fill#EBF8FFBTP container background
Non-SAP border#475E75Non-SAP / external area strokes, generic data-flow connectors
Non-SAP fill / Subtle bg#F5F6F7Non-SAP areas, page background, generic pill fill
Title text#1D2D3EHeadings, primary labels
Secondary text#556B82Sub-labels, descriptions, footnotes
Positive (Auth, green)#188918 / bg #F5FAE5Authentication flows (SAML, OIDC) — per guideline
Critical (Warning, orange)#C35500 / bg #FFF8D6Warnings
Negative (Error, red)#D20A0A / bg #FFEAF4Errors
Teal accent#07838F / bg #DAFDF5Highlight areas
Indigo (Authorization)#5D36FF / bg #F1ECFFAuthorization / provisioning (SCIM) flows — per guideline
Pink (Trust)#CC00DC / bg #FFF0FATrust flows (mutual trust, federation) — per guideline

Font: Arial (or Arial Black for headings), size 12 for body labels, 14 for service labels, 16 for group titles.

3. Apply the atomic structure

Per the SAP atomic design system (Atoms → Molecules → Organisms). The exact style strings, sizes and HTML label patterns to copy live in reference/example-patterns.md — match those rather than inventing variants.

  • Document title (every diagram): a floating text cell above the BTP container, blue #0070F2 bold Arial 16, format "{Scenario} - SAP BTP Solution Diagram".
  • Outer container (Subaccount / Multi-Cloud): rounded=1;strokeColor=#0070F2;fillColor=#EBF8FF;arcSize=32;absoluteArcSize=1;strokeWidth=1.5;. Carries the SAP-logo image tile in the top-left and two stacked labels: bold Subaccount (Arial 16) + smaller Multi-Cloud (Arial 12).
  • Sub-containers (white cards inside the BTP boundary — e.g. Cloud Identity Services group): same #0070F2 stroke, #FFFFFF fill, arcSize=16. Always blue stroke, never slate, when the card sits inside the BTP container. Per the guideline Areas / Nesting: alternate fill vs. no-fill between parent and child to keep visual contrast (BTP container has fill #EBF8FF, so inner cards use white).
  • Service nodes: BTP icons (§1) wrapped in a style="group" connectable="0" cell whenever they need a separate text label or are co-positioned with another shape. Children use coordinates relative to the group origin.
  • External-system tiles (e.g. SAP S/4HANA On-Premise): a group of (white card arcSize=14 + ~28×28 icon top-left + bold font-size:16px label on the right). Sit outside the BTP container.
  • Users / actors: a group containing the user SVG image and a centered End User text label below.
  • Connectors — copy the right variant from the example patterns reference. Per the guideline Connectors section, line style encodes flow nature and line color encodes flow semantic:
    • Line style: solid = direct synchronous request/response · dashed=1 = indirect / asynchronous · dashed=1;dashPattern=1 4; (dotted) = optional · strokeWidth=3 = firewalls / network barriers only.
    • Semantic color: Authentication = green #188918 · Authorization / Provisioning (SCIM) = indigo #5D36FF · Mutual trust / federation = pink #CC00DC · Generic data = slate #475E75.
    • Standard data flow: endArrow=blockThin;strokeColor=#475E75;endFill=1;endSize=4;startSize=4;strokeWidth=1.5;
    • Orthogonal: prefix with edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;
    • Authentication (SAML/OIDC): same shape, strokeColor=#188918
    • Authorization / Provisioning (SCIM): same shape, strokeColor=#5D36FF
    • Mutual trust: add startArrow=blockThin;startFill=1 and strokeColor=#CC00DC
    • Async / indirect: use edgeStyle=entityRelationEdgeStyle;rounded=0;html=1;strokeColor=#475E75;strokeWidth=1.5;endArrow=blockThin;endFill=1;endSize=4;startArrow=none;startFill=0;startSize=4;jumpStyle=none;jumpSize=0;targetPerimeterSpacing=15;dashed=1; (the official SAP indirect connector style — entityRelationEdgeStyle + targetPerimeterSpacing=15)
    • Optional: add dashed=1;dashPattern=1 4; (dotted) to the async style above
    • Network / firewall boundary line: thick grey vertical separator strokeColor=#475E75;strokeWidth=3;jumpStyle=gap; with a small uppercase NETWORK label (#475E75) beside it. Reserve strokeWidth=3 for firewalls/network barriers only — do not use it for normal data flows.
    • Always pin exit/entry ports to avoid diagonal auto-routing: add exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0; (adjust X/Y for the direction). For vertical connectors use exitY=1 / entryY=0. Remove manual <mxPoint> waypoints unless a deliberate detour is needed — leave <Array as="points"/> empty. See reference/example-patterns.md §8 for the full port-pinning reference.
  • Edge labels are separate vertex pills, not inline edge text — small rounded rectangles arcSize=50, ~16 px tall, color-matched to the connector (generic #475E75/#F5F6F7, auth #188918/#F5FAE5, authz/SCIM #5D36FF/#F1ECFF, trust #CC00DC/#FFF0FA).
  • L0: no legend, no protocol pills, neutral endArrow=block or endArrow=none connectors.
  • L1: directional endArrow=blockThin connectors, optionally a few colored auth/provisioning flows and pill labels.
  • L2: add a description block + Diagram Level: L2 under the title, pill tags on every meaningful edge, and a legend card in the top-right (white card, strokeColor=#eaecee, with colored 16×16 ellipse swatches for each flow type and a sample arrow per arrow style).

4. Generate the draw.io XML

Follow the draw.io AI generation rules:

  • Use uncompressed XML, full <mxfile> wrapper (so file-level vars are usable).
  • Always include <mxCell id="0"/> and <mxCell id="1" parent="0"/>.
  • Vertices: vertex="1". Edges: edge="1" with source/target. Mutually exclusive.
  • Unique IDs across the diagram.
  • Coordinates: top-left (0,0), x→right, y→down. Children inside a group use coordinates relative to the group.
  • Match perimeter to shape (e.g. perimeter=ellipsePerimeter for ellipses).
  • XML-escape labels (&amp;, &lt;, &gt;, &quot;). HTML markup inside value= is allowed and is the standard way to bold/size text — see the official examples.
  • HTML label escaping: Build the full HTML string first (tags + text, with plain </>/"), then apply one complete escape pass (&&amp;, <&lt;, >&gt;, "&quot;) before inserting into the value="…" attribute. When using xml.etree.ElementTree, pass the unescaped value and let the serializer escape it. Parse the written file with a real XML parser and reject any visible label containing <font, &lt;font, or <div.
  • Use container=0; on area shapes (BTP boundary, subaccount cards) — not draw.io container behavior — so connectors route correctly through them.
  • Keep grid spacing on multiples of 10 px (gridSize="10"); leave ≥20 px gaps between siblings; sub-containers padded by ~18–24 px.
  • It is normal and expected for diagrams to be authored in negative coordinate space (e.g. x="-2200"); draw.io centers on content.
  • Every edge mxCell MUST contain <mxGeometry relative="1" as="geometry" /> — self-closing edge cells are invalid and will not render.

Page size: always A4 landscapepageWidth="1169" pageHeight="827", even for dense L2. Larger virtual canvas comes from spreading groups across negative coordinates, not from changing the page size. L2 additionally sets background="none" on <mxGraphModel>.

5. Write the file

Save to the path the user requested (default: workspace root, btp-diagram.drawio). Do not overwrite an existing file without confirming.

6. Open via the MCP server

Detect which draw.io MCP integration is available, in this order — use the first that is configured:

  1. MCP Tool Server (@drawio/mcp / npx @drawio/mcp): call its "open diagram" tool with the generated XML to launch the editor in the browser.
  2. MCP App Server (mcp.draw.io/mcp remote): call its render tool to embed the interactive viewer inline in chat.
  3. Neither configured: skip silently and instead print a https://app.diagrams.net/?pv=0&grid=0#create=... URL built per the FAQ (URI-encode JSON {"type":"xml","compressed":true,"data":"<base64-deflate-raw>"}). If you cannot compress in-context, print the file path and instruct the user to open it in their installed draw.io.

Never invent an MCP tool name — only call tools that actually appear in the available tool list.

7. Validate before delivering

Run the bundled validator script first — it codifies most of the checklist:

python3 skills/btp-diagram-generator/scripts/validate_diagram.py <file.drawio>

If it warns about blurry icons (SVG intrinsic size smaller than cell), fix in place:

python3 skills/btp-diagram-generator/scripts/upscale_svg_icons.py <file.drawio> --size 48 --in-place

Then verify these remaining items by eye:

  • All labels XML-escaped (no raw <font> visible).
  • No overlapping shapes (≥20px gap).
  • BTP container visually encloses all BTP services; external systems sit outside it.
  • L0 diagrams have no legend and neutral connectors; L2 includes a legend card top-right and a description block under the title.
  • Edge labels are pill vertex cells, not inline value= text on the edge="1" cell.
  • Connectors do not cross through unrelated containers.
  • No unrequested components present (check against the allowlist).
  • Service sequence matches the user's requested order.
  • CIS is outside the Subaccount but inside BTP.
  • Legend (if present) does not overlap any connector.

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
60
Forks
16
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
btp-diagram-generator
Source
github.com/likweitan/abap-skills