bizagi-modeler
# bizagi-mcp
**Turn a description of a process into a BPMN 2.0 diagram that opens cleanly in Bizagi Modeler.**
An [MCP](https://modelcontextprotocol.io) server that generates, reads, audits and previews
BPMN 2.0 diagrams — and drives the Bizagi Modeler desktop app on Windows.

*Generated from a 60-line JSON spec. Every coordinate above was computed, not placed by hand.*
---
## Why this exists
Bizagi Modeler has no scripting API. The one integration path it does support is the open
**BPMN 2.0 XML** format, through its Export / Import tab.
But there is a catch that makes naive generation useless: **Bizagi imports the coordinates
written in the file verbatim.** It does not lay out a diagram for you. Emit a structurally
perfect BPMN file without geometry and it opens as a pile of boxes stacked on the origin.
So the hard part of this server is not the XML. It is the layout.
---
## What it does
| Tool | What it does |
| --- | --- |
| `get_spec_reference` | The spec format: every node type, field and rule |
| `create_process` | Description → a `.bpmn` file ready to import, coordinates computed |
| `update_process` | Edit an existing `.bpmn` (add/change/remove nodes and flows), re-laid out |
| `read_process` | Parse a `.bpmn` → structured JSON, a readable walk-through, or an editable spec |
| `list_processes` | Scan a folder and summarise each BPMN file |
| `validate_process` | Audit against BPMN 2.0 rules and modelling conventions, with a fix for each finding |
| `render_preview` | Render to SVG — check the result without opening Bizagi |
| `export_documentation` | Process documentation as Markdown (outline + audit) |
| `bizagi_status` | Whether Bizagi Modeler can be driven from here |
| `bizagi_open` | Launch Bizagi Modeler, optionally with a file |
| `bizagi_import_bpmn` | Drive Export / Import ▸ BPMN, and verify that it landed |
| `bizagi_export_bpmn` | Drive Export ▸ BPMN for the open diagram |
The first eight are pure Python and run on any OS, with or without Bizagi installed.
Only the four `bizagi_*` tools need Windows.
---
## The layout engine
A lane-aware layered layout, in the order it runs:
1. **Break cycles** so the graph can be layered at all
2. **Longest-path layering** → each node's horizontal column
3. **Barycenter ordering** per (column, lane) → fewer crossing lines
4. **Adaptive lane heights**, sized to the tallest cell each band holds
5. **Reserved strips** — a bypass band along the top of any lane carrying a
column-skipping branch, and a channel strip at the bottom for loop-backs
6. **Orthogonal routing** that goes *around* obstacles rather than through them
7. **Label separation** as a final pass
### What it guarantees
These are not aspirations. Each one is a test that fails when the rule is removed:
- No two shapes overlap
- No edge is drawn through a shape that is not its own endpoint
- Every element sits inside its pool
- Message flows run in the empty corridor **between** pools, never horizontally through one
- Each message flow gets its own line in that corridor, and the corridor is sized
from how many flows cross it — so their labels do not stack
- Loop-backs each get their own channel in a strip reserved while lanes are sized
- A branch that skips columns detours inside its own lane, over the activities it skips
- A gateway's branches leave from visibly different points, so a two-way split
does not read as a single arrow
- Boundary-event flows leave downwards, never back up through the host activity
- Annotations and data stores sit beside what they describe — or, when they have no
association, inside the pool they declare rather than off the canvas
- No label is written over another label or over a shape
### Design notes
A few decisions that are easy to get wrong:
- **A label is as wide as its text.** Reserving a flat box for every label makes
collisions between the long ones invisible to anything that measures the reserved box.
- **Reserved space must be held out of centring.** Grow a lane to make room for a
channel and then centre the shapes in it, and half the new space is handed back as
padding above — the channel ends up too thin to use.
- **A detour belongs in the gaps between shapes, not around all of them.** Routing over
or under everything lands the line outside the pool, and the verticals that reach it
then cross every lane on the way.
- **A data store can be associated with many activities but sits beside one.** Placing
it once per association leaves holes in the lanes where the earlier placements were.
---
## Install
```bash
pip install -e .
```
For the Windows desktop tools:
```bash
pip install -e ".[desktop]"
```
Python ≥ 3.10.
### Register with Claude
`claude_desktop_config.json` (Windows: `%APPDATA%\Claude\claude_desktop_config.json`):
```json
{
"mcpServers": {
"bizagi-modeler": {
"command": "bizagi-mcp",
"env": {
"BIZAGI_MCP_ROOT": "C:\\Users\\you\\Documents\\Bizagi"
}
}
}
}
```
If `bizagi-mcp` is not on PATH:
```json
{
"mcpServers": {
"bizagi-modeler": {
"command": "python",
"args": ["-m", "bizagi_mcp.server"],
"env": { "BIZAGI_MCP_ROOT": "C:\\Users\\you\\Documents\\Bizagi" }
}
}
}
```
For Claude Code: `claude mcp add bizagi-modeler -- bizagi-mcp`
### Environment variables
| Variable | What it does |
| --- | --- |
| `BIZAGI_MCP_ROOT` | Confine every file read and write to this folder. **Strongly recommended.** |
| `BIZAGI_MODELER_PATH` | Full path to `BizagiModeler.exe` or `BizAgiMC.exe` when it is not found automatically |
---
## Usage
### Generate a diagram
> "Model a leave request: the employee submits it, the manager approves or rejects it,
> HR records the outcome. Save it to `D:\Processes\leave.bpmn`."
Then in Bizagi Modeler: **Export / Import ▸ Import ▸ BPMN**.
### Analyse an existing model
Export from Bizagi first (**Export / Import ▸ Export ▸ BPMN**), then:
> "Read `D:\Processes\purchasing.bpmn`, walk me through it, and tell me what is wrong with it."
### Example
See `examples/purchase_request.json` (the spec), `.bpmn` (generated) and `.svg` (preview).
---
## Validation rules
**Structure** (`BPMN001`–`BPMN020`, severity `error` / `warning`)
Missing start or end events · unreachable elements · dead ends · sequence flows crossing
pools · message flows inside one pool · gateways branching without conditions ·
event-based gateway targets · implicit split and merge · boundary events on
non-activities · duplicate ids · a default flow that also carries a condition ·
one-in-one-out gateways.
**Conventions** (`BP001`–`BP017`, severity `warning` / `info`)
Activity naming (verb + object) · gateways not phrased as questions · unlabelled
branches · documentation coverage · pools without lanes · empty lanes · diagram size ·
duplicate names · pools that never exchange messages.
Every finding names the offending element and the concrete step to fix it.
---
## Driving the desktop app
`bizagi_open` is the dependable path: Modeler takes a file as a command line argument,
so no menu has to be driven.
`bizagi_import_bpmn` drives the ribbon, and is honest about it:
- It claims the foreground and **verifies** it got there. Windows refuses
`SetForegroundWindow` to a process that does not own the foreground, and `set_focus()`
returns as if it worked — clicking on regardless sends a real mouse click into whatever
the user is working in.
- It counts diagram tabs before and after, and reports `imported: true` / `false` from
that evidence rather than from hope.
- Both counts are taken with the window raised, because a window that is behind can hand
back an incomplete accessibility tree.
### There is no background mode
Import cannot run while the machine is used for something else. Three routes were tested
against Modeler 4.3.0.008 and all three are closed:
| Route | Result |
| --- | --- |
| UI Automation `Invoke` pattern | Ribbon tabs expose no patterns at all |
| `PostMessage` mouse messages | Ignored, across every candidate window handle |
| `BizAgiMC.exe file.bpmn` | Exits 0 without importing anything |
The ribbon only responds to real mouse input on a focused window. For unattended runs,
give Bizagi its own Windows session or VM. If you want that recorded so nobody retries
it: this table is the record.
---
## Security
- Paths are fully resolved (`~`, `..`, symlinks) **before** being checked, then confined
to `BIZAGI_MCP_ROOT` when it is set
- XML parsing goes through `defusedxml` when available (XXE, billion laughs)
- Files are never overwritten without `overwrite=true`
- Bizagi is launched with an argument list and no shell, so a filename can never become
a command
- Every error comes back as data (`{"ok": false, ...}`), never a traceback
---
## Tests
```bash
pip install -e ".[dev]"
pytest -q
```
72 tests: spec normalisation, XSD element ordering, BPMNDI completeness, every layout
guarantee listed above, label collisions, round-trips, each validation rule, path
traversal, ribbon button selection, foreground verification, and the error contract of
every tool.
---
## Known limitations
- **`.bpm` is not read.** It is Bizagi's proprietary format; export to BPMN first.
`list_processes` still lists `.bpm` files and flags them.
- **Desktop control is Windows-only** and needs `pywinauto`.
- **Diagrams are generated one level deep.** A sub-process appears as a collapsed shape;
its contents are not generated.
- **Layout tidiness is guaranteed for the geometry written to the file.** Bizagi places
node names by its own rules, which the diagram interchange section does not control.
## License
MIT — see [LICENSE](LICENSE).
TDQS
Scored across 12 tools
Each tool targets a distinct action: file discovery, spec reference, create, update, read, validate, preview, documentation, and Bizagi desktop automation. The only mild overlap is preview capability between create_process and render_preview, but their outputs and intended use are clearly differentiated.
Most tools follow a clear verb_noun pattern, and the desktop automation tools consistently share the bizagi_ prefix. The minor deviation is bizagi_status, which is a noun phrase rather than verb-first, and get_spec_reference uses get while similar operations use read; still, the overall pattern is predictable.
Twelve tools is well-scoped for a BPMN modeling server. Each tool covers a meaningful part of the workflow without unnecessary redundancy, and the count is comfortably within the typical 3-15 range.
The toolset covers the full modeling lifecycle: discovery, creation, editing, reading, validation, preview, documentation, and desktop import/export. The only noted limitation—reading proprietary .bpm files—is documented with a practical workaround rather than an untooled dead end.