Files
excalidraw-mcp-sentinel/skills/excalidraw-skill/SKILL.md
T
newblaccandClaude Opus 4.6 9fb8ce34ec chore: rename project to excalidraw-mcp-sentinel
- Package name: @sanjibdevnath/mcp-excalidraw-local → excalidraw-mcp-sentinel
- GitHub repo: celstnblacc/mcp-excalidraw-local → celstnblacc/excalidraw-mcp-sentinel
- Docker images, CLI binary, CI workflows, docs all updated
- Version reset to 1.0.0 for independent release track
- Added "Why this fork?" section to README
- Removed superseded planning docs (PLAN.md, PLAN_v2.md, REVIEW.md, HANDOFF.md)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 20:00:27 +02:00

23 KiB
Raw Blame History

name, description
name description
excalidraw-skill MANDATORY prerequisite for ALL Excalidraw MCP tool usage. Read this skill BEFORE calling any Excalidraw tool (batch_create_elements, create_element, create_from_mermaid, update_element, etc.) — without this skill's sizing formulas, two-batch ordering (shapes first, arrows second), and write-check-review verification cycle, diagrams will have invisible arrows, truncated text, and overlapping elements. Use whenever the user asks to draw, create, visualize, sketch, or diagram anything — flowcharts, architecture diagrams, system designs, org charts, sequence flows, decision trees, network topologies, ER diagrams, mind maps, or any visual on Excalidraw canvas. Also covers diagram refinement, PNG/SVG export, project/workspace management, and all canvas interactions.

Excalidraw Skill

Step 0: Detect Connection Mode

Run these checks in order:

  1. MCP Server (best): If tools like batch_create_elements are available → use MCP mode.
  2. REST API (fallback): curl -s http://localhost:3000/health returns {"status":"ok"} → use REST API mode.
  3. Nothing works: Guide user to install (clone celstnblacc/excalidraw-mcp-sentinel, build, configure MCP).

See references/cheatsheet.md for the full MCP-vs-REST mapping and REST API gotchas.

Step 1: Load User Preferences

Before creating any elements, load the user's diagram preferences. These control default font, roughness, stroke width, etc.

Preference Resolution Order (most specific wins)

Priority Scope Location Persists
1 (highest) Session In-memory (set via prompt during this conversation) No — current session only
2 Folder .claude/excalidraw-preferences.json in the current project root Yes — per-project
3 Global ~/.claude/skills/excalidraw-skill/preferences.json Yes — all projects
4 (lowest) Hardcoded Server defaults (fontFamily: 5, roughness: 0, fontSize: 20, strokeWidth: 2)

How to Load

  1. Check folder-level first: Read .claude/excalidraw-preferences.json from the current working directory (or project root). If it exists and has defaults, use those values.
  2. Fall back to global: Read ~/.claude/skills/excalidraw-skill/preferences.json. If it exists and has defaults, use those values.
  3. If neither exists → run the First-Time Setup prompt below.
  4. Merge: Folder preferences override global; global overrides hardcoded. Only override fields that are explicitly set.

First-Time Setup (Interactive)

If no preferences file exists at either location, prompt the user before drawing anything:

Excalidraw Preferences Setup

I don't have any saved diagram preferences yet. Let me set up your defaults so every diagram looks the way you want.

Ask these questions (use AskUserQuestion tool if available, otherwise ask inline):

  1. Font family — Which font for all text? (IDs from src/font-families.json)

    • Excalifont (hand-drawn) = 5
    • Helvetica (sans-serif) = 2
    • Cascadia (monospace) = 3
    • Comic Shanns = 8
    • Nunito = 6
    • Lilita One = 7
  2. Roughness — Diagram style?

    • Clean/professional (roughness: 0) — recommended
    • Hand-drawn sketch (roughness: 1)
    • Very rough (roughness: 2)
  3. Scope — Where to save?

    • This session only — don't save to disk, just use for this conversation
    • This project — save to .claude/excalidraw-preferences.json in project root
    • Global (all projects) — save to ~/.claude/skills/excalidraw-skill/preferences.json

Then save the preferences JSON to the chosen location:

{
  "defaults": {
    "fontFamily": <user_choice>,
    "fontSize": 20,
    "roughness": <user_choice>,
    "strokeWidth": 2
  }
}

For session-only scope, just hold the values in memory and apply them to every element in this conversation.

Applying Preferences

Once loaded, apply defaults to every element that supports the property:

  • fontFamily → all text-containing elements (text, rectangles with labels, diamonds, ellipses, arrows with labels)
  • fontSize → text elements and labels (unless the element explicitly overrides it)
  • roughness → all elements
  • strokeWidth → arrows and lines

User-specified values in individual element calls always override preferences.

Core Principles (Read Before Any Diagram)

These principles were learned through extensive iterative use. Violating them produces bad diagrams.

1. Never Trust Blind Output — Use the Write-Check-Review Cycle

Every diagram iteration follows this mandatory loop:

WRITE (create/update elements)
  → CHECK (screenshot to see actual rendering)
    → REVIEW (critically evaluate against Quality Checklist)
      → FIX (if issues found, fix and re-screenshot)
        → only proceed when ALL checks pass

Screenshot strategy: get_canvas_screenshot may return empty images. When it fails, use Chrome DevTools MCP (take_screenshot after navigate_page to canvas URL) as a reliable fallback.

2. Use batch_create_elements, Not Mermaid

The create_from_mermaid tool produces low-quality output: overlapping text, poor spacing, unreadable labels. It is a quick preview tool, not a production tool.

For quality diagrams, always use batch_create_elements with precise coordinates, explicit sizing, and color coding. The extra planning time pays for itself in fewer fix iterations.

3. Shapes First, Arrows Second — Two Separate Batches

Create shapes in one batch, then arrows in a separate batch. Arrow binding (startElementId/endElementId) requires shapes to already exist in the scene. Mixing both in one call can work but often produces binding errors.

4. Multiple Diagrams on One Canvas

Never clear the canvas between diagrams. Place them side-by-side or in a grid:

Diagram 1: x=0 to ~1100
Diagram 2: x=1400 onward  (300px gap)
— or —
Row 1: y=0 to ~800
Row 2: y=1100 onward  (300px gap)

Use a title text element above each diagram to label it.

5. Set roughness: 0 for Clean Diagrams

Excalidraw defaults to hand-drawn style (roughness > 0). For professional, readable diagrams, always set "roughness": 0 on every element. Also use "strokeWidth": 2 for arrows to ensure visibility.

Sizing Rules (Critical — Prevents Truncation)

Excalidraw's Virgil font is ~30% wider than standard fonts. These rules account for that.

Rectangles

width:  max(200, characterCount * 11)
height: 70 (1 line), 80 (2 lines), 100 (3 lines)
fontSize: 16-20

Diamonds (Decision Nodes)

Diamond usable text area is ~50% of the bounding box. Double your width estimate.

width:  max(400, longestLineChars * 18)
height: max(160, lineCount * 50)
fontSize: 16

A diamond with text "Behavioral guideline\nor project standard?" (20 chars) needs at least 400x160.

Ellipses

Ellipse text area is ~60% of bounding box. Size generously.

width:  max(280, characterCount * 14)
height: max(65, lineCount * 35)
fontSize: 16-18

Text Elements (Standalone Titles)

fontSize: 24-28 for diagram titles
fontSize: 16-20 for annotations

Arrow Visibility Rules (Critical — Prevents Invisible Arrows)

When arrows are bound to shapes via startElementId/endElementId, the actual rendered arrow length equals the gap between shape edges minus binding padding (8px each side). If shapes are too close, arrows shrink to 0px and become invisible.

Minimum Gap Between Connected Shapes

Connection Direction Minimum Gap Recommended Gap
Vertical (top-down flow) 80px 120px
Horizontal (left-right) 100px 140px

Calculating Vertical Gap for Flowcharts

gap = nextShapeY - (currentShapeY + currentShapeHeight)

Example (diamonds h=160, gap needed ≥ 120):
  Q1: y=260, h=160 → bottom edge = 420
  Q2: y=540         → gap = 540 - 420 = 120px ✓

If the gap is < 80px, arrows will be too short to see — especially with labels like "YES"/"NO".

Workflow: Multi-Diagram Canvas (Spatial Organization)

Excalidraw's infinite canvas supports multiple diagrams coexisting side by side. Never clear the canvas to make room — place new diagrams spatially offset from existing ones.

Before Drawing Anything

Always call describe_scene first. It reports:

  • Diagram Zones: grouped elements with bounding boxes, labels, and element counts
  • Canvas bounding box: the overall occupied area
  • Suggested placement: a recommended (x, y) for the next diagram (300px right of existing content)

Creating a New Diagram Alongside Existing Ones

  1. describe_scene — read the suggested placement coordinates
  2. Offset your new diagram to the suggested area (or 300px+ from the rightmost edge)
  3. Add a title text element above your diagram (fontSize 24-28) as an identifier
  4. Create shapes (Batch 1) with coordinates offset to the new area
  5. Create arrows (Batch 2)
  6. group_elements — group ALL elements of your new diagram together
  7. set_viewport with scrollToContent: true to see everything
  8. Screenshot to verify

Example Layout

  "Architecture" (group A)          "User Flow" (group B)
  x=0 to x=1100                     x=1400 to x=2500
  ┌─────────────────────┐           ┌─────────────────────┐
  │                     │   300px   │                     │
  │   Diagram A         │   gap    │   Diagram B         │
  │                     │           │                     │
  └─────────────────────┘           └─────────────────────┘

Rules

  • Never call clear_canvas unless the user explicitly asks to wipe everything
  • Always describe_scene before drawing to see existing content
  • Group every diagram so describe_scene reports it as a named zone
  • Add a title text element as the first element of each diagram — this becomes the zone label
  • Use search_elements to find specific diagrams by their title text
  • Use set_viewport with scrollToElementId to navigate to a specific diagram

Workflow: Draw A Diagram

Phase 1: Plan

Before writing any JSON, plan on paper:

  1. List all elements: shapes, labels, connections
  2. Choose layout direction: top-down (flowcharts), left-right (timelines), grid (architecture)
  3. Assign coordinates: use the sizing rules above to compute widths/heights, then lay out with proper gaps
  4. Assign IDs: every shape needs a custom id so arrows can reference it

Phase 2: Create Shapes (Batch 1)

{"elements": [
  {"id": "title", "type": "text", "x": 100, "y": 0,
   "text": "MY DIAGRAM", "fontSize": 28, "strokeColor": "#1e1e1e"},
  {"id": "box-a", "type": "rectangle", "x": 0, "y": 80,
   "width": 200, "height": 70, "text": "Service A",
   "backgroundColor": "#a5d8ff", "strokeColor": "#1971c2",
   "roughness": 0, "fontSize": 18},
  {"id": "box-b", "type": "rectangle", "x": 0, "y": 280,
   "width": 200, "height": 70, "text": "Service B",
   "backgroundColor": "#b2f2bb", "strokeColor": "#2f9e44",
   "roughness": 0, "fontSize": 18}
]}

Phase 3: Create Arrows (Batch 2)

{"elements": [
  {"type": "arrow", "x": 100, "y": 150,
   "startElementId": "box-a", "endElementId": "box-b",
   "width": 0, "height": 130, "text": "calls",
   "strokeColor": "#1e1e1e", "roughness": 0, "strokeWidth": 2,
   "endArrowhead": "arrow"}
]}

Phase 4: Check (MANDATORY)

  1. set_viewport with scrollToContent: true
  2. Wait 1-2 seconds for render
  3. Take screenshot (MCP get_canvas_screenshot or Chrome DevTools take_screenshot)
  4. Critically evaluate against the Quality Checklist below
  5. Fix any issues, re-screenshot, repeat until clean

Quality Checklist

After EVERY batch of elements, verify ALL of these:

Check What to Look For Fix
Text truncation Any label cut off or hidden? Increase shape width/height
Invisible arrows Can you see arrows between all connected shapes? Increase gap between shapes to ≥ 120px
Arrow labels Do YES/NO/labels overlap with shapes? Shorten labels or increase gap
Overlap Do any elements share space? Reposition with more spacing
Readability Can all text be read at 50-70% zoom? Increase fontSize to ≥ 16
Spacing At least 40px gap between unconnected elements? Spread elements apart

If ANY Check Fails

STOP. Do not add more elements. Fix the issue first:

  1. Use update_element to resize/reposition
  2. Or delete_element + recreate with better coordinates
  3. Re-screenshot to verify the fix
  4. Only proceed when ALL checks pass

How to Honestly Evaluate a Screenshot

  • Zoom into different regions — don't just glance at the overview
  • Check every label individually for truncation
  • Trace every arrow path for visibility
  • If you see ANY issue, say "I see [issue], fixing it" — never say "looks great" unless it truly is

Color Palette

Use consistent colors from this palette:

Role Fill Stroke Use For
Primary #a5d8ff #1971c2 Main flow, services
Success #b2f2bb #2f9e44 Approved, healthy, YES paths
Warning #ffd8a8 #e8590c Attention, agents
Error #ffc9c9 #e03131 Critical, NO paths, failures
Purple #eebefa #9c36b5 Rules, governance
Cyan #99e9f2 #0c8599 Data stores, MCP
Neutral #e9ecef #868e96 Secondary, annotations
Default #ffffff #1e1e1e Decisions, generic

Flowchart Template (Tested & Verified)

This template produces clean, readable decision flowcharts:

Layout:
  Diamonds: w=400, h=160, fontSize=16, gap=120px vertical
  Answer boxes: w=300, h=80, fontSize=20, offset 130px right of diamonds
  Start ellipse: w=340, h=70, fontSize=18
  Title: fontSize=28
  Arrows: strokeWidth=2, roughness=0
  YES arrows: strokeColor=#2f9e44 (green), horizontal right
  NO arrows: strokeColor=#e03131 (red), vertical down
  All elements: roughness=0

Architecture Diagram Template

Layout:
  Zones: large rectangles, backgroundColor=#e9ecef, opacity=30
  Services: w=200, h=70, fontSize=18, spaced 60px apart
  Data stores: w=180, h=60, fontSize=16, strokeColor=#0c8599
  Arrows: solid for sync, dashed (strokeStyle="dashed") for async
  Title: fontSize=24 above each zone

Geometric Thinking (Critical — For All Diagram Types)

Excalidraw only offers basic primitives: rectangles, diamonds, ellipses, lines, arrows, text. Building anything beyond simple box-and-arrow diagrams requires geometric composition — combining many small primitives using coordinate math to form complex shapes, textures, and layouts.

Principle 1: Compose Complex Shapes from Many Small Primitives

Never use one large primitive where many small ones create a better result.

A single large diamond looks like a flat rhombus. But 45 small diamonds arranged in a triangular grid with tessellating offset rows looks like a tiled roof. The technique:

  1. Identify the target shape (triangle, circle, arc, wave, etc.)
  2. Choose a small primitive that can tile/fill that shape (diamond for tiles, rectangle for bricks, ellipse for clouds)
  3. Compute a grid of positions that fills the target shape's boundary
  4. Apply row-by-row reduction for tapered shapes (triangles, cones)
  5. Add interleaving offset rows to eliminate gaps (tessellation)
Example — Triangular tiled roof (building width=380, center_x=1090):

Tile size: 52w × 36h
Row spacing: 28px vertical (= tile height)
Interleave offset: 26px horizontal (= tile width / 2)
Interleave rows at: midpoint y between main rows

Main rows (reduce by 2 tiles per row):
  Row 1: 9 tiles at y=130  |  ◇◇◇◇◇◇◇◇◇
  Row 2: 7 tiles at y=102  |   ◇◇◇◇◇◇◇
  Row 3: 5 tiles at y=74   |    ◇◇◇◇◇
  Row 4: 3 tiles at y=46   |     ◇◇◇
  Row 5: 1 tile  at y=18   |      ◇

Interleaving rows (offset by half tile width, fill gaps):
  Inter 1: 8 tiles at y=116 |  ◇◇◇◇◇◇◇◇
  Inter 2: 6 tiles at y=88  |   ◇◇◇◇◇◇
  Inter 3: 4 tiles at y=60  |    ◇◇◇◇
  Inter 4: 2 tiles at y=32  |     ◇◇

Total: 45 tiles → seamless triangular roof

Principle 2: Tessellation — Eliminate Gaps with Offset Rows

When same-row primitives leave triangular/pointed gaps, add interleaving rows offset by half the primitive width:

Gap pattern (diamonds side-by-side):     Filled with interleaving row:
  /\  /\  /\  /\                           /\  /\  /\  /\
 /  \/  \/  \/  \                         /  \/  \/  \/  \
 \  /\  /\  /\  /                         \ /\/\/\/\/\/\ /
  \/  \/  \/  \/                           ◇◇◇◇◇◇◇◇◇  ← offset row
                                           /\  /\  /\  /\

Formula for interleaving:

main_row_y[n] = base_y - n * row_spacing
inter_row_y[n] = (main_row_y[n] + main_row_y[n+1]) / 2
inter_row_x_offset = tile_width / 2
inter_row_count = main_row_count[n] - 1

Principle 3: Parametric Positioning — Use Formulas, Not Guessing

Compute element positions mathematically. Common formulas:

Centering N items in a container:

item_x[i] = container_x + (container_width - N * item_width) / (N + 1) * (i + 1) + i * item_width

Circular arrangement (N items around center):

angle[i] = (2π / N) * i + rotation_offset
x[i] = center_x + radius * cos(angle[i]) - item_width / 2
y[i] = center_y + radius * sin(angle[i]) - item_height / 2

Triangular reduction (pyramid/roof):

count[row] = base_count - 2 * row
x_start[row] = center_x - (count[row] * tile_width) / 2
y[row] = base_y - row * row_spacing

Isometric projection (2.5D diagrams):

screen_x = (grid_x - grid_y) * tile_width / 2
screen_y = (grid_x + grid_y) * tile_height / 2

Principle 4: Scale Primitives to Context

When applying a technique to different-sized containers, scale the primitive size proportionally:

Hut (body width 290px)  → tiles 40w × 28h, 9 per base row
Building (body width 380px) → tiles 52w × 36h, 9 per base row

Maintain the same count-per-row for visual consistency; adjust individual tile dimensions.

Technique Catalogs

For detailed recipes and formulas for specific diagram types, see geometric-thinking.md:

  • Illustrative diagrams: 11 visual element recipes (roofs, walls, clouds, trees, fences, water, smoke, windows, stairs)
  • Flow diagrams: Sugiyama-inspired 4-step hierarchical layout (layer assignment → ordering → coordinates → edge routing)
  • Architecture diagrams: Zone-grid layout with service grid-packing and dependency layering
  • Isometric/2.5D diagrams: Screen projection formulas for depth illusion
  • Repeating patterns: Generic repeat and brick-pattern offset formulas
  • Anti-patterns: 6 common geometric composition mistakes and fixes

Workflow: Iterative Refinement

create shapes (batch 1)
  → create arrows (batch 2)
    → set_viewport(scrollToContent: true)
      → wait 1-2s
        → screenshot
          → evaluate quality checklist
            → issues? fix → re-screenshot → re-evaluate
              → clean? proceed to next diagram section

For multi-diagram canvases, offset each new diagram by 300px+ from the previous one's bounding box.

Multi-tenant: each Cursor workspace auto-gets its own tenant (SHA-256 hash of workspace path). Globally-configured MCPs detect per-window workspace automatically.

Task Tool Notes
List workspaces list_tenants Returns id, name, workspace_path
Switch workspace switch_tenant with tenantId Canvas reloads tenant's elements
List projects list_projects Projects group diagrams within a tenant
Switch/create project switch_project Pass projectId or createName
Full-text search search_elements with query Searches labels and text content
Version history element_history Pass elementId or omit for project-wide

Workflow: Refine An Existing Diagram

  1. describe_scene to understand current state
  2. Identify targets by id or label text (or use search_elements for text search)
  3. update_element to move/resize/recolor
  4. Screenshot to verify
  5. If updates fail: check element id exists (get_element), element isn't locked
  6. Use element_history to see what changed if something looks wrong

Workflow: File I/O

  • Export: export_scene (optional filePath)
  • Import: import_scene with mode: "replace" or "merge"
  • Image export: export_to_image with format: "png" or "svg" (requires browser)

Workflow: Snapshots

  1. snapshot_scene with a name before risky changes
  2. Make changes, screenshot to evaluate
  3. restore_snapshot to rollback if needed

Note: Snapshot restore may not always reload elements into the active view. If the canvas appears empty after restore, re-fetch elements or recreate.

Workflow: Viewport Control

  • scrollToContent: true — auto-fit all elements
  • scrollToElementId: "my-element" — center on specific element
  • zoom: 0.7, offsetX: 100, offsetY: 50 — manual camera for close-up review

Anti-Patterns (Common Mistakes)

Mistake Why It Fails Do This Instead
Using create_from_mermaid for final diagrams Overlapping text, poor layout Use batch_create_elements with coordinates
Shapes too small for text Truncation, especially in diamonds Use sizing formulas above
No gap between connected shapes Arrows become invisible (0px length) Maintain 120px+ vertical gap
Clearing canvas between diagrams Loses previous work, requires confirm: true Place diagrams side-by-side using spatial offset; call describe_scene first
Skipping screenshot verification Invisible defects compound Screenshot after EVERY batch
Shapes + arrows in one batch Binding errors Shapes first, arrows second
Default roughness (hand-drawn look) Unprofessional for technical diagrams Set roughness: 0 on all elements
Trusting MCP screenshot alone May return empty image Use Chrome DevTools as fallback

MCP Tool Quick Reference (32 Tools)

Category Tools
Element CRUD (9) create_element, get_element, update_element, delete_element, query_elements, batch_create_elements, duplicate_elements, search_elements, element_history
Layout (6) align_elements, distribute_elements, group_elements, ungroup_elements, lock_elements, unlock_elements
Scene (4) describe_scene, get_canvas_screenshot, get_resource, read_diagram_guide
File I/O (4) export_scene, import_scene, export_to_image, export_to_excalidraw_url
State (3) clear_canvas, snapshot_scene, restore_snapshot
Viewport (1) set_viewport
Tenants (2) list_tenants, switch_tenant
Projects (2) list_projects, switch_project
Conversion (1) create_from_mermaid (⚠ low quality — use batch_create_elements instead)

References

  • references/cheatsheet.md: Complete MCP tool list (32 tools) + REST API endpoints + payload shapes + env vars