# Excalidraw

> MANDATORY prerequisite for ALL Excalidraw MCP tool usage. Read BEFORE calling any Excalidraw tool (batch_create_elements, create_element, update_element, etc.). Without the sizing formulas, two-batch ordering (shapes-then-arrows), compact legends, domain styling presets, and write-check-review cycle in this skill, diagrams have invisible arrows, truncated text, and inconsistent colors. Use whenev…

- **Type:** Skill
- **Install:** `agentstack add skill-vanducng-skills-excalidraw`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [vanducng](https://agentstack.voostack.com/s/vanducng)
- **Installs:** 0
- **Category:** [Cloud & Infrastructure](https://agentstack.voostack.com/c/cloud-infrastructure)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [vanducng](https://github.com/vanducng)
- **Source:** https://github.com/vanducng/skills/tree/main/skills/excalidraw
- **Website:** https://skills.vanducng.dev

## Install

```sh
agentstack add skill-vanducng-skills-excalidraw
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Excalidraw — Technical Diagram Skill

Build professional, consistent Excalidraw diagrams via MCP. Skill covers: tool mechanics, sizing formulas, write-check-review verification cycle, and **domain-specific styling presets** for software engineering, system architecture, and data solutions.

## Step 0 — Detect Connection

Check **in order**:

1. **MCP server**: tools prefixed `mcp__excalidraw-mcp__*` (e.g. `batch_create_elements`, `describe_scene`) available → use MCP mode. This is the default for this user.
2. **REST fallback**: only if MCP missing — `curl -s $EXPRESS_SERVER_URL/health` returns `{"status":"ok"}`.
3. **Nothing** → auto-bootstrap the MCP config (see below), then tell user to reconnect the MCP so it registers. Do not fake output.

### Auto-bootstrap `.mcp.json`

When neither the MCP tools nor the REST fallback are available:

1. Resolve **project root**: `git rev-parse --show-toplevel` (fallback to CWD if not a git repo). Inside a git worktree this correctly returns the worktree itself — `.mcp.json` must live at the working root the session runs in, so the MCP registers for that session.
2. Derive **project name** = `basename` of the project root.
3. If `/.mcp.json` does **not** exist, create it with this exact template (substitute ``):

   ```json
   {
     "mcpServers": {
       "excalidraw-mcp": {
         "type": "http",
         "url": "https://mcp.dataplanelabs.com/excalidraw/mcp",
         "headers": {
           "Authorization": "Bearer ${EXCALIDRAW_MCP_TOKEN}",
           "X-Tenant-Id": ""
         }
       }
     }
   }
   ```

4. If `.mcp.json` already exists, **merge** — add the `excalidraw-mcp` entry under `mcpServers` without clobbering other servers. Skip if `excalidraw-mcp` already present.
5. Tell the user: file written, ensure `EXCALIDRAW_MCP_TOKEN` is exported in shell env, then reconnect before re-running the skill — **Claude Code:** restart it (or run `/mcp`). **Codex:** the `.mcp.json` above is Claude Code-specific; register the same server with `codex mcp add excalidraw-mcp` or add `[mcp_servers.excalidraw-mcp]` (url + headers) to `~/.codex/config.toml`, then restart Codex.

Never write the bootstrap file outside the resolved project root, and never echo the token value.

The remote canvas (when this user's MCP is used) is at `https://draw.dataplanelabs.com`. For visual verification beyond `get_canvas_screenshot`, use Chrome DevTools MCP to `take_screenshot` of the canvas URL — `get_canvas_screenshot` sometimes returns blank PNGs.

## Step 1 — Tenant & Project Setup

The remote Excalidraw MCP is multi-tenant. Tenant is selected via the `X-Tenant-Id` header (configured in `.mcp.json`) or via tools.

Before any drawing:

1. `list_tenants` — confirm active tenant
2. `list_projects` — confirm active project; `switch_project` with `createName` if a fresh canvas is wanted
3. `describe_scene` — read existing diagram zones + suggested next placement coordinates

**Never** call `clear_canvas` unless the user explicitly says wipe. Place new diagrams spatially offset (≥300px) from existing ones.

## Core Principles (Read Before Any Diagram)

### 1. Write → Check → Review → Fix (Mandatory Loop)

```
WRITE batch → set_viewport(scrollToContent: true) → screenshot
  → REVIEW against Quality Checklist → FIX issues → re-screenshot
    → only proceed when all checks pass
```

### 2. Use `batch_create_elements` — Not `create_from_mermaid`

`create_from_mermaid` produces overlapping text and broken layouts. Use it only as a quick preview, never for final output. For quality, always plan coordinates and call `batch_create_elements`.

### 3. Two Batches: Shapes First, Arrows Second

Arrow binding (`startElementId`/`endElementId`) requires shapes to exist already. Mixing in one batch causes binding errors.

### 4. `roughness: 0` for Technical Diagrams

Excalidraw defaults to hand-drawn (roughness > 0). For professional system / data / architecture diagrams, set `roughness: 0` on every element. Use `strokeWidth: 2` for arrows.

### 5. Multi-Diagram Canvas

Never clear canvas between diagrams. Place side-by-side or stacked with ≥300px gap. Add a title text element (fontSize 24-28) above each. Group with `group_elements` so `describe_scene` reports it as a named zone.

### 6. Add a Compact Legend When It Clarifies

Include a small legend whenever colors, shapes, stroke styles, or arrow colors encode non-obvious meaning. Keep it minimal:

- Trigger: 3+ semantic node colors, 2+ arrow styles/colors, or mixed ownership/status meanings (internal/external, allow/deny, batch/stream).
- Size: 3-5 entries total; include only semantics actually used in the diagram.
- Placement: top-right or bottom-right whitespace, outside primary flow paths; fontSize 13-14.
- Content: one swatch/mini-line + a short label (`Stream`, `Batch`, `External`, `Denied`).
- Skip it when labels already make the encoding obvious and the diagram only uses 1-2 semantic styles.

Do not build a full catalog. A legend should explain the visual language, not duplicate every node or edge label.

## Sizing Rules (Prevents Truncation)

Excalidraw's font is ~30% wider than typical sans-serif. Use these formulas:

| Shape | Width | Height | fontSize |
|-------|-------|--------|----------|
| Rectangle | `max(200, chars * 11)` | 70 (1 line) / 80 (2) / 100 (3) | 16-20 |
| Diamond (text uses ~50% of bbox — **double**) | `max(400, longestLine * 18)` | `max(160, lines * 50)` | 16 |
| Ellipse (text ~60% of bbox) | `max(280, chars * 14)` | `max(65, lines * 35)` | 16-18 |
| Title text | — | — | 24-28 |

## Arrow Visibility (Prevents Invisible Arrows)

Bound arrows shrink to `gap_between_shapes - 16` (8px binding padding each side). Below 80px, arrows vanish.

| Direction | Min gap | Recommended |
|-----------|---------|-------------|
| Vertical | 80px | **120px** |
| Horizontal | 100px | **140px** |

```
gap = nextShape.y - (currentShape.y + currentShape.height)
```

## Domain Styling Presets

Pick the preset matching the diagram type. Apply fill + stroke + shape per row. Don't mix palettes within one diagram unless intentional.

### Active Color Budget

Use at most **5 active semantic colors** in any one diagram. Similar components share one color family: all internal services together, all data stores together, all compute/jobs together, all external systems together, all security/blocked paths together.

Treat long preset tables as menus, not a requirement to use every color. If the diagram needs more than five meanings, keep the color and vary shape, stroke style, arrow width, grouping boundary, or label. Neutral gray boundaries/backgrounds and black/white text do not count against the 5-color budget.

### Minimal Legend Defaults

Use these entries only when the matching semantics appear in the diagram:

| Meaning | Legend mark |
|---------|-------------|
| Sync/API call | blue solid arrow `#1976d2`, width 2 |
| Batch/data load | gray solid arrow `#757575`, width 2 |
| Stream/event | orange solid arrow `#f57c00`, width 3 |
| Async/queue | orange dashed arrow `#f57c00`, width 2 |
| Lineage/dependency | purple dotted arrow `#9c27b0`, width 1 |
| Denied/security block | red solid arrow `#d32f2f`, width 3 |

For node legends, show only the shape/color roles that are not already obvious from labels, such as `Internal service`, `External system`, `Database`, `Queue`, or `Security boundary`.

### Software Architecture (C4 / Microservices)

| Role | Shape | Fill | Stroke | Label format |
|------|-------|------|--------|--------------|
| Person / Actor | hexagon or ellipse | `#fff3e0` | `#f57c00` | `User\n[Person]` |
| Software System (Context) | rectangle | `#e3f2fd` | `#1976d2` | `My System\n[Software System]` |
| Container | rounded rectangle | `#a5d8ff` | `#0d6efd` | `API Gateway\n[Container: Node.js]` |
| Component | rectangle | `#b9e0fb` | `#0c8599` | `OrderHandler\n[Component]` |
| Database | cylinder approx (rect + ellipse top) | `#f3e5f5` | `#7b1fa2` | `Orders DB\n[PostgreSQL]` |
| External system | rectangle, **dashed** stroke | `#fce4ec` | `#c2185b` | `Stripe\n[External]` |
| Async / Message Queue | hexagon | `#f0f4c3` | `#827717` | `Order Events\n[Kafka topic]` |
| Cache | rounded rectangle | `#b2dfdb` | `#00695c` | `Session Cache\n[Redis]` |

**Rule:** all elements in one C4 view share the same abstraction level. Don't mix Container with Component shapes in one diagram — split into two.

### Cloud Architecture (AWS / GCP / Azure)

Color by **service category**, label by service name + bracket type:

| AWS Category | Fill | Stroke | Examples |
|--------------|------|--------|----------|
| Compute | `#ffe0b2` | `#f57c00` | EC2, Lambda, ECS, Fargate |
| Storage | `#c8e6c9` | `#388e3c` | S3, EBS, EFS, Glacier |
| Database | `#ffccbc` | `#d84315` | RDS, DynamoDB, Aurora |
| Network | `#e1bee7` | `#7b1fa2` | VPC, ALB, Route 53, CloudFront |
| Security | `#ffcdd2` | `#d32f2f` | IAM, KMS, Secrets Manager, WAF |
| Analytics | `#ede7f6` | `#3f51b5` | Athena, Redshift, EMR, QuickSight |
| Messaging | `#fff9c4` | `#fbc02d` | SQS, SNS, Kinesis, EventBridge |
| Monitoring | `#bbdefb` | `#1976d2` | CloudWatch, X-Ray |

Bound zones (VPC, subnet, account) with a translucent rectangle: `backgroundColor: "#e9ecef"`, `opacity: 30`, label as title above.

**No icons?** Use `[Compute]`, `[Storage]`, etc. in the label and the category color does the visual work.

### Data Pipeline / Lakehouse / ETL

Encode **batch vs stream** via stroke width and color, **lineage** via dotted, **async** via dashed:

| Component | Shape | Fill | Stroke | Label |
|-----------|-------|------|--------|-------|
| Source (DB / API / files) | cylinder | `#b3e5fc` | `#0097a7` | `PostgreSQL\n[Source]` |
| Stream broker | hexagon | `#f0f4c3` | `#827717` | `Kafka\n[Topic: events]` |
| Batch job (Airflow / dbt) | rounded rectangle | `#fff9c4` | `#fbc02d` | `dbt run\n[Daily 02:00]` |
| Stream processor (Spark / Flink) | hexagon | `#ffecb3` | `#f57f17` | `Spark Stream\n[Processor]` |
| Sink (warehouse / lake) | cylinder | `#c8e6c9` | `#388e3c` | `Snowflake\n[Warehouse]` |
| ML model / feature store | rectangle | `#d1c4e9` | `#3f51b5` | `Recommender\n[Model]` |
| BI / dashboard | rounded rectangle | `#e1bee7` | `#7b1fa2` | `Looker\n[Dashboard]` |

| Edge type | Style | Color | Width | Label |
|-----------|-------|-------|-------|-------|
| Batch | solid | `#757575` | 2 | `daily 02:00` |
| Stream | solid | `#f57c00` | **3** | `topic: orders` |
| Async / queue | dashed | `#f57c00` | 2 | `queue: tasks` |
| Lineage (dbt parent→child) | dotted | `#9c27b0` | 1 | `derived from` |
| Sync API | solid | `#1976d2` | 2 | `POST /v1/...` |

**Layout:** sources top → processing middle → sinks bottom; warehouse layers (raw → staging → marts) flow top-down with consistent column alignment.

### UML — Sequence / ER / State / Class

#### Sequence
- Actors: hexagon `#fff3e0` / `#f57c00`
- Lifeline: vertical dashed line `#999`, strokeWidth 1
- Service: rectangle on top of lifeline `#e3f2fd` / `#1976d2`
- Sync message: solid arrow `#1976d2`, label `method()`
- Async message: dashed arrow `#f57c00`, label `event`
- Return: dashed arrow `#b0bec5`, label `result`

#### ER
- Entity: rectangle `#e8f5e9` / `#388e3c`, label = TableName
- Attribute: ellipse `#f3e5f5` / `#7b1fa2`, label = `column\ntype`
- Primary key: ellipse, **underline label**
- Relationship: diamond `#fff3e0` / `#f57c00`, label = verb (`has`, `owns`)
- Cardinality: text on the connection line (`1:N`, `M:N`)

#### State
- State: rounded rectangle `#bbdefb` / `#1976d2`
- Initial: small filled ellipse `#212121`
- Final: ellipse with inner dot
- Transition: solid arrow with `event [guard] / action` label

#### Class
- Class: rectangle, three sections separated by horizontal lines (name / fields / methods)
- Inheritance: solid arrow with empty triangle head, color `#1976d2`
- Composition: arrow with filled diamond, color `#d32f2f`
- Aggregation: arrow with empty diamond, color `#757575`

### Deployment — Kubernetes / Docker

K8s blue is `#326ce5`. Use it as the cluster boundary.

| Component | Shape | Fill | Stroke |
|-----------|-------|------|--------|
| Cluster (bounding box) | rectangle, opacity 30 | `#e3f2fd` | `#326ce5` |
| Node | rectangle | `#bbdefb` | `#1976d2` |
| Pod | rounded rectangle | `#a5d8ff` | `#0d6efd` |
| Service | hexagon | `#b2dfdb` | `#00695c` |
| Ingress | hexagon | `#80cbc4` | `#00897b` |
| PVC / Storage | cylinder | `#f3e5f5` | `#7b1fa2` |
| ConfigMap / Secret | small rounded rectangle | `#fffde7` | `#f57f17` |
| NetworkPolicy | rectangle, dashed stroke | `#ffcdd2` | `#d32f2f` |

Arrows: API call solid `#1976d2`, mount dotted `#7b1fa2`, replication dashed `#f57c00`, denied policy thick red.

## Quality Checklist

After every batch, verify ALL:

| Check | Look For | Fix |
|-------|----------|-----|
| Truncation | Labels cut off, especially in diamonds | Increase width using formulas above |
| Invisible arrows | Connections you cannot trace | Increase gap to ≥120px vertical |
| Arrow label collision | YES/NO labels overlap shapes | Shorten label or widen gap |
| Element overlap | Shapes share space | Reposition with proper spacing |
| Readability | Text legible at 50–70% zoom | Bump fontSize to ≥16 |
| Color consistency | Colors match a single domain preset | Re-pick from one table above |
| Color budget | More than 5 active semantic colors in one diagram | Merge similar components; use shape/stroke/labels for extra meaning |
| Stroke + fill contrast | Light fill + dark stroke (or inverse) | Use the pairs in tables — never light fill + light stroke |
| Legend clarity | Needed semantics absent, or legend lists everything | Add 3-5 used meanings only, or remove if redundant |

If any check fails: STOP. Use `update_element` or delete + recreate. Re-screenshot. Only proceed when all checks pass.

## Standard Workflow

```
1. describe_scene                              # see existing
2. Plan: list elements, layout direction, IDs, coordinates
3. Pick a domain preset (architecture/cloud/data/UML/deployment)
4. batch_create_elements: shapes (Batch 1)
5. batch_create_elements: arrows (Batch 2)
6. Add compact legend if trigger conditions apply
7. group_elements                              # group new diagram
8. set_viewport(scrollToContent: true)
9. Screenshot → Quality Checklist → fix → repeat
```

## Anti-Patterns (Avoid)

| Mistake | Why it fails | Do this |
|---------|--------------|---------|
| `create_from_mermaid` for production | overlapping text, bad layout | `batch_create_elements` with coordinates |
| Mixing C4 levels in one view | suggests false relationships | one diagram per level (Context, Container, Component) |
| Color chaos (6+ semantic colors) | viewer loses semantic mapping | cap at 5 active colors; merge similar roles |
| Unlabeled arrows | ambiguous: sync? async? what data? | label every arrow with what + how |
| Labeling shape by tech only ("Lambda") | diagram describes infra, not domain | name first: `CheckoutHandler [Lambda]` |
| Light fill + light stroke | invisible boundary | always pair light fill with dark stroke |
| Default `roughness > 0` | unprofessional for technical diagrams | always set `roughness: 0` |
| Shapes too close | arrows shrink to 0px | ≥120px vertical, ≥140px horizontal gap |
| Single-batch shapes + arrows | binding errors | two separate `batch_create_elements` calls |
| Trusting a single screenshot | MCP screenshot may be blank | fall back to Chrome DevTools `take_screenshot` of canvas URL |
| Master diagram showing all levels | illegible | split by concern (data flow / deployment / security) |
| Oversized legend | legend becomes visual clutter | 3-5 used meanings, tucked into whitespace |

## MCP Tool Quick Reference

| Category | Tools |
|----------|-------|
| Element CRUD | `create_element`, `get_element`, `update_element`, `delete_element`, `query_elements`, `batch_create_elements`, `duplicate_elements`, `search_elements`, `element_history` |
| Layout | `align_elements`, `distribu

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [vanducng](https://github.com/vanducng)
- **Source:** [vanducng/skills](https://github.com/vanducng/skills)
- **License:** MIT
- **Homepage:** https://skills.vanducng.dev

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-vanducng-skills-excalidraw
- Seller: https://agentstack.voostack.com/s/vanducng
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
