Install
$ agentstack add skill-besser-pearl-uml-drawing-skill-uml-drawing ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Embed a correct UML diagram in your docs
Agents are constantly asked to "add a diagram" to documentation — and they routinely get the modeling subtly wrong (invalid multiplicities, arrows reversed, inheritance backwards) or hand back a code block that never renders as a picture. This skill produces a correct class diagram and a real embeddable image.
Scope
- Class diagrams only, for now — classes, attributes, associations,
multiplicities, inheritance. Other UML diagram types (state machines, object, sequence, activity, …) are out of scope for this skill.
- Always through BESSER B-UML. Every diagram is built as a B-UML
DomainModel first, then delivered. Going through the model is what makes the diagram correct and reusable as code.
Where it can start
- A description — the user explains the domain; the agent writes the model.
- Existing code — the agent reads the source and builds the B-UML model
from its structure. (There is no automatic code scanner; the agent does the modeling from what it reads — it is not a one-click importer.)
- A supported into-B-UML path — PlantUML, Draw.io, or a UI mockup.
Two ways to deliver
Model once, then deliver as embedded B-UML code (Deliver A) or a rendered image (Deliver B) — both from the same model, so they never disagree. Pick by context (a design doc that should stay runnable → code; a README that should show a picture → image), or provide both. Details in the Deliver sections below.
Where it lands. If the user names a target document (a README, a design doc), embed into that file. If they name none — or ask for a standalone diagram of a repo/system — default to delivering a new self-contained .md (e.g. docs/.md): the rendered image (Deliver B) and/or the B-UML code (Deliver A), plus a line or two explaining what the diagram shows. That turns a bare "diagram my repo" into a committable document rather than a loose SVG. When you create a new file, say where you put it (and offer the alternative — embed into an existing doc instead — if that may be what they wanted).
Workflow
1. Build the B-UML model — from a description, from existing code the agent
reads, or via a supported into-B-UML path (PlantUML / Draw.io / mockup)
2. model.validate() → confirm it is structurally correct
Then deliver one (or both):
A) Embed the code: drop the DomainModel Python into a fenced block in the .md
B) Embed an image: POST the model to BESSER's headless SVG endpoint (one
HTTP call — no browser), save the returned SVG (e.g. docs/img/data-model.svg),
embed
3. When the model changes, update the model and re-deliver — one source of truth
1. Build the model
A correct diagram starts from a correct model. The minimal shape:
from besser.BUML.metamodel.structural import (
DomainModel, Class, Property, Multiplicity,
BinaryAssociation, Generalization, StringType, IntegerType,
)
publication = Class(name="Publication", attributes={Property(name="title", type=StringType)})
book = Class(name="Book", attributes={Property(name="pages", type=IntegerType)})
author = Class(name="Author", attributes={Property(name="name", type=StringType)})
book_is_a = Generalization(general=publication, specific=book) # Book is a Publication
# A Book is written by 1..* Authors; an Author writes 0..* Books
written_by = Property(name="writtenBy", type=author, multiplicity=Multiplicity(1, "*"))
writes = Property(name="writes", type=book, multiplicity=Multiplicity(0, "*"))
book_author = BinaryAssociation(name="book_author", ends={written_by, writes})
model = DomainModel(
name="Library",
types={publication, book, author},
associations={book_author},
generalizations={book_is_a},
)
assert model.validate()["success"]
Naming: no spaces, no hyphens. For everything beyond this minimal shape — enumerations, all multiplicity/composition patterns, association classes, generalization sets, methods, validation details — read references/class-diagram.md (bundled with this skill). For non-class model types (object, feature, deployment, OCL, …), see the BESSER platform's besser-user skill.
> Importable form — required for both delivery paths. Write every > Class(...), BinaryAssociation(...), and Generalization(...) as an > explicit top-level assignment, exactly as shown above. BESSER's importer > (used by the SVG endpoint and by the editor's Import) reads the file's > structure — it does not execute it — so relationships built inside a > helper function or a loop are invisible: the classes import but the > associations silently vanish. One literal statement per relationship. > > Because the importer never runs the file, model.validate() is not > executed on the render path — you do not need BESSER installed to render via > the endpoint. Keep the validate() call for when you do have BESSER locally > (and before generating code); it is a no-op for the endpoint, not a > requirement.
Where the model comes from:
- From a description — write it as above.
- From existing code — read the source and translate its structure
(classes, fields, relationships, inheritance) into the B-UML model. The agent does the modeling; there is no automatic code scanner.
- From PlantUML / Draw.io / a UI mockup — use the matching into-B-UML
conversion (available via the BESSER platform), then continue.
Now deliver it one of two ways.
Deliver A — embed the B-UML code in the doc
Drop the model straight into a fenced `python block in the .md. It is correct, validate()-checked, re-runnable, and importable into the editor. Best when the document should carry the model itself (a design doc, a spec):
````markdown
Data model
# (the DomainModel from above)
This B-UML model is the source of truth for the domain — validate()-checked, and the input to any BESSER generator.
````
Deliver B — embed a rendered image
The model becomes a real picture through BESSER's renderer, so the image always matches the model. Two ways — pick by whether you want it hands-off or hand-tuned.
B1. Render it automatically (default) — one HTTP call, no browser
BESSER exposes a headless B-UML → SVG endpoint. The agent POSTs the model .py file and gets an SVG back — no editor, no manual steps, nothing for the user to click:
curl -X POST https://editor.besser-pearl.org/besser_api/get-svg \
-F "buml_file=@data-model.py;type=text/x-python" \
-o docs/img/data-model.svg
The endpoint parses the B-UML, auto-lays it out, and returns image/svg+xml. Save it into the repo and embed:
- The model file must be in importable form (see the callout in §1) — the
same form the editor imports. Class diagrams only.
- Layout is automatic. When the placement matters, hand-tune it with B2.
- Save the
.pyas UTF-8 without a BOM — a leading byte-order mark makes
the endpoint reject the file (HTTP 400).
- Omit enum-literal
default_value=...in a model you render here — the
endpoint currently fails on it (HTTP 500). It is fine for code generation; a plain enum used as an attribute type renders fine.
- A non-200 response is a small JSON error, not an SVG (
{ "detail": ... }) —
check the status before saving.
B2. Hand-tuned export from the editor (when layout matters)
Open https://editor.besser-pearl.org, Import the .py (choose the B-UML format), drag boxes and route lines to taste, then Export and pick a format:
- SVG — sharp at any size; best for docs and the web.
- PNG — transparent background.
- PNG (white background) — for surfaces that need an opaque image.
Save into the repo and embed it the same way. (The editor can also export the model as B-UML or JSON — those are the model, not an image.)
Either way, SVG and PNG render everywhere Markdown images do — GitHub, GitLab, docs sites, wikis, slides — with no plugin.
Keep it from drifting
Whichever mode you choose, the model is the source of truth. When requirements change, update the model and re-deliver (re-paste the code, or re-export the image) — never hand-edit an exported SVG/PNG, it is generated.
The payoff beyond a picture
Because the diagram is a real B-UML model, the same artifact can generate working code — Python classes, SQL, a FastAPI backend, Django, React, and more. A diagram you drew to explain the system can become the system. To generate code from it, defer to the besser-generators skill.
When to reach for related skills
- Building models in depth (enums, inheritance, methods, OCL, object /
feature / deployment / NN / quantum models) → the besser-user skill.
- Generating code from the model → the besser-generators skill.
- Errors (import/validation/editor issues) → the besser-troubleshooting skill.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: BESSER-PEARL
- Source: BESSER-PEARL/uml-drawing-skill
- License: Apache-2.0
- Homepage: https://editor.besser-pearl.org
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.