excalidraw-skill by yctimlin/mcp_excalidraw
npx skills add https://github.com/yctimlin/mcp_excalidraw --skill excalidraw-skill有两种模式可用。优先尝试 MCP 模式——它功能更强大。
MCP 模式(首选):如果你的工具列表中出现了 excalidraw/batch_create_elements 和其他 excalidraw/* 工具,请直接使用它们。MCP 工具会自动处理标签和箭头绑定的格式。
REST API 模式(备选):如果 MCP 工具不可用,请使用 http://localhost:3000 上的 HTTP 端点。请查阅速查表了解 REST 负载。注意下表中的格式差异——REST 和 MCP 接受的字段名略有不同。
两种都不行? 请告知用户:
Excalidraw 画布服务器未运行。请按以下步骤设置:
git clone https://github.com/yctimlin/mcp_excalidraw && cd mcp_excalidrawnpm ci && npm run buildPORT=3000 npm run canvas
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
http://localhost:3000claude mcp add excalidraw -s user -e EXPRESS_SERVER_URL=http://localhost:3000 -- node /path/to/mcp_excalidraw/dist/index.js| 操作 | MCP 工具 | REST API 等效操作 |
|---|---|---|
| 创建元素 | batch_create_elements | POST /api/elements/batch |
| 获取所有元素 | query_elements | GET /api/elements |
| 获取单个元素 | get_element | GET /api/elements/:id |
| 更新元素 | update_element | PUT /api/elements/:id |
| 删除元素 | delete_element | DELETE /api/elements/:id |
| 清空画布 | clear_canvas | DELETE /api/elements/clear |
| 描述场景 | describe_scene | GET /api/elements(需手动解析) |
| 导出场景 | export_scene | GET /api/elements(保存到文件) |
| 导入场景 | import_scene | POST /api/elements/sync |
| 快照 | snapshot_scene | POST /api/snapshots |
| 恢复快照 | restore_snapshot | GET /api/snapshots/:name 然后 POST /api/elements/sync |
| 截图 | get_canvas_screenshot | POST /api/export/image(需要浏览器) |
| 视口 | set_viewport | POST /api/viewport(需要浏览器) |
| 导出图片 | export_to_image | POST /api/export/image(需要浏览器) |
| 导出 URL | export_to_excalidraw_url | 仅通过 MCP |
"text": "My Label"(自动转换)。REST 要求 "label": {"text": "My Label"}。startElementId/endElementId。REST 要求 "start": {"id": "..."} / "end": {"id": "..."}。"1")或完全省略。切勿传递数字。"label",以确保更新后能正确渲染。画布使用二维坐标网格:(0, 0) 是原点,x 向右增加,y 向下增加。在编写任何 JSON 之前,请先规划好布局。
通用间距指南:
max(160, labelCharCount * 9) 以防止文本截断这些是导致图表难以阅读的最常见错误。请避免所有这些问题。
label.text(或 text)当你在背景矩形上放置标签时,Excalidraw 会在该形状的中间创建一个绑定的文本元素——正好是你放置服务框的位置。该文本会与区域内的所有内容重叠,并且无法重新定位。
错误示例:
{"id": "vpc-zone", "type": "rectangle", "x": 50, "y": 50, "width": 800, "height": 400, "text": "VPC (10.0.0.0/16)"}
正确做法——使用锚定在区域顶部的独立文本元素:
{"id": "vpc-zone", "type": "rectangle", "x": 50, "y": 50, "width": 800, "height": 400, "backgroundColor": "#e3f2fd"},
{"id": "vpc-label", "type": "text", "x": 70, "y": 60, "width": 300, "height": 30, "text": "VPC (10.0.0.0/16)", "fontSize": 18, "fontWeight": "bold"}
独立的文本元素位于区域的顶角,不会干扰放置在内部的元素。
从一个布局区域中的元素指向远处区域中元素的箭头会绘制一条长长的对角线,穿过中间的所有内容。在多区域的基础设施图中,这会产生难以阅读的混乱线条。
设计规则: 将箭头保持在同一个区域或层级内。要显示跨区域的关系,请使用注释文本或将区域分开,使它们的边缘相邻(中间没有元素),并沿着边缘路由箭头。
如果必须跨区域连接,请使用沿着周边行进的弯折箭头——切勿穿过另一个区域的中间。
箭头标签放置在箭头的中间点。在短箭头上,它们会与两端的形状重叠。在拥挤的图表中,它们会与附近的元素碰撞。
Excalidraw 图表是视觉沟通。如果文本被截断、元素重叠或箭头穿过不相关的形状,图表就会变得混乱且不专业——这完全违背了绘制它的目的。因此,在每批元素创建之后,添加更多元素之前,请进行验证。
每次 batch_create_elements / POST /api/elements/batch 之后,截图并检查:
width 和/或 height。text/label.text,区域标签将位于区域的中心,与内部的所有内容重叠。修复方法:删除绑定的文本元素,并在区域顶部添加一个独立的文本元素(见上文布局反模式)。如果发现任何问题:停止,修复它,重新截图,然后继续。 说“我发现 [问题],正在修复它”,而不是掩盖问题。只有在所有检查都通过后才能继续。
使用 create_from_mermaid 当:用户已经有 Mermaid 图表,或者结构可以清晰地映射到具有标准 Mermaid 语法的流程图/序列图/ER 图时。它速度快且能自动处理转换,尽管你对精确布局的控制较少。
直接使用 batch_create_elements 当:你需要精确的布局控制,图表类型不能很好地映射到 Mermaid(例如,自定义架构、带注释的云图),或者你希望元素定位在特定的坐标网格中时。
read_diagram_guide 获取设计最佳实践(颜色、字体、反模式)。clear_canvas 以重新开始。batch_create_elements——在一次调用中创建形状和箭头。自定义 id 字段(例如 "id": "auth-svc")便于后续更新。max(160, labelLength * 9) 设置形状宽度。使用 text 字段作为标签。startElementId / endElementId 绑定箭头——它们会自动路由到元素边缘。scrollToContent: true 调用 set_viewport 以自动适应。get_canvas_screenshot → 运行质量检查清单 → 在下一次迭代前修复问题。MCP 元素 + 箭头示例:
{"elements": [
{"id": "lb", "type": "rectangle", "x": 300, "y": 50, "width": 180, "height": 60, "text": "Load Balancer"},
{"id": "svc-a", "type": "rectangle", "x": 100, "y": 200, "width": 160, "height": 60, "text": "Web Server 1"},
{"id": "svc-b", "type": "rectangle", "x": 450, "y": 200, "width": 160, "height": 60, "text": "Web Server 2"},
{"id": "db", "type": "rectangle", "x": 275, "y": 350, "width": 210, "height": 60, "text": "PostgreSQL"},
{"type": "arrow", "x": 0, "y": 0, "startElementId": "lb", "endElementId": "svc-a"},
{"type": "arrow", "x": 0, "y": 0, "startElementId": "lb", "endElementId": "svc-b"},
{"type": "arrow", "x": 0, "y": 0, "startElementId": "svc-a", "endElementId": "db"},
{"type": "arrow", "x": 0, "y": 0, "startElementId": "svc-b", "endElementId": "db"}
]}
curl -X DELETE http://localhost:3000/api/elements/clearPOST /api/elements/batch 创建元素。使用 "label": {"text": "..."} 作为标签。"start": {"id": "..."} / "end": {"id": "..."} 绑定箭头。POST /api/export/image 验证 → 保存 PNG → 运行质量检查清单。REST API 元素 + 箭头示例:
curl -X POST http://localhost:3000/api/elements/batch \
-H "Content-Type: application/json" \
-d '{
"elements": [
{"id": "svc-a", "type": "rectangle", "x": 100, "y": 100, "width": 160, "height": 60, "label": {"text": "Service A"}},
{"id": "svc-b", "type": "rectangle", "x": 400, "y": 100, "width": 160, "height": 60, "label": {"text": "Service B"}},
{"type": "arrow", "x": 0, "y": 0, "start": {"id": "svc-a"}, "end": {"id": "svc-b"}, "label": {"text": "calls"}}
]
}'
在复杂图表中,直线箭头可能会穿过元素。需要时请使用曲线箭头或弯折箭头:
曲线箭头(平滑弧线越过障碍物):
{
"type": "arrow", "x": 100, "y": 100,
"points": [[0, 0], [50, -40], [200, 0]],
"roundness": {"type": 2}
}
中间路径点 [50, -40] 将箭头向上抬起。roundness: {type: 2} 使其平滑。
弯折箭头(直角 / L 形路由):
{
"type": "arrow", "x": 100, "y": 100,
"points": [[0, 0], [0, -50], [200, -50], [200, 0]],
"elbowed": true
}
何时使用哪种:
规则: 如果箭头会穿过一个不相关的形状,请添加一个路径点以绕过它。
路径点格式:[[x, y], ...] 元组和 [{"x": ..., "y": ...}] 对象都接受;两者都会自动规范化。
结合使用 describe_scene 和 get_canvas_screenshot 是这个技能强大的原因。
describe_scene → 返回结构化文本:元素 ID、类型、位置、标签、连接。当你需要在以编程方式更新之前(查找 ID、理解边界框)了解画布上有什么时使用此功能。get_canvas_screenshot → 返回实际渲染画布的 PNG 图像。用于视觉质量验证——它向你展示用户看到的确切内容,包括截断、重叠和箭头路由。反馈循环(MCP):
batch_create_elements
→ get_canvas_screenshot → "auth-svc 上的文本被截断"
→ update_element(增加宽度) → get_canvas_screenshot → "auth-svc 和 rate-limiter 之间重叠"
→ update_element(重新定位) → get_canvas_screenshot → "所有检查通过"
→ 继续
反馈循环(REST):
POST /api/elements/batch
→ POST /api/export/image → 保存 PNG → 评估
→ PUT /api/elements/:id(修复问题) → 重新截图 → 评估
→ 继续
describe_scene 以了解当前状态——注意元素 ID 和位置。id 或标签文本识别元素(不要通过 x/y 坐标——它们会变化)。update_element 以调整大小/重新着色/移动;delete_element 以删除。get_canvas_screenshot 以确认更改看起来正确。get_element 检查 ID 是否存在;使用 unlock_elements 检查它是否未被锁定。用于将现有的 Mermaid 图表转换为 Excalidraw:
MCP 模式:
create_from_mermaid(mermaidDiagram: "graph TD\n A --> B\n B --> C")
转换后,使用 scrollToContent: true 调用 set_viewport,并使用 get_canvas_screenshot 验证布局。如果自动布局不佳(节点拥挤、边交叉),请使用 describe_scene 识别问题元素,并使用 update_element 重新定位。
REST 模式:
curl -X POST http://localhost:3000/api/elements/from-mermaid \
-H "Content-Type: application/json" \
-d '{"mermaid": "graph TD\n A --> B\n B --> C"}'
.excalidraw:export_scene,可选 filePath.excalidraw 导入:import_scene,使用 mode: "replace" 或 "merge"export_to_image,使用 format: "png" 或 "svg"(需要浏览器打开)export_to_excalidraw_url——加密场景,返回可分享的 excalidraw.com URLnode scripts/export-elements.cjs --out diagram.elements.jsonnode scripts/import-elements.cjs --in diagram.elements.json --mode batch|syncsnapshot_scene。describe_scene / get_canvas_screenshot 评估。restore_snapshot。使用 duplicate_elements,指定 elementIds 和可选的 offsetX/offsetY(默认:20, 20)。对于重复模式或复制布局很有用。
describe_scene——它们可能被创建在屏幕外。使用 scrollToContent: true 调用 set_viewport。get_element 验证元素 ID。确保 startElementId/endElementId(MCP)或 start.id/end.id(REST)与现有的元素 ID 匹配。snapshot_scene,然后 clear_canvas 并重建。或者调用 restore_snapshot 回退。unlock_elements。describe_scene 检查实际位置,然后批量更新位置。label.text 的形状生成一个绑定的文本元素。如果你清空并重新发送元素,Excalidraw 可能会重新注入其缓存的绑定文本,导致重复。要清理:(1) 使用 query_elements / GET /api/elements 查找 type: "text" 且带有 containerId 的元素;(2) 使用 delete_element 删除不需要的元素;(3) 在导出前等待几秒钟让自动同步稳定下来。最安全的方法是永远不要在背景区域矩形上放置标签——改用独立的文本元素。references/cheatsheet.md:完整的 MCP 工具列表(26 个工具)+ REST API 端点 + 负载形状。每周安装量
101
代码仓库
GitHub 星标数
1.5K
首次出现
2026年2月12日
安全审计
安装于
opencode94
codex93
gemini-cli92
amp92
kimi-cli92
github-copilot92
Two modes are available. Try MCP first — it has more capabilities.
MCP mode (preferred): If excalidraw/batch_create_elements and other excalidraw/* tools appear in your tool list, use them directly. MCP tools handle label and arrow binding format automatically.
REST API mode (fallback): If MCP tools aren't available, use HTTP endpoints at http://localhost:3000. See the cheatsheet for REST payloads. Note the format differences in the table below — REST and MCP accept slightly different field names.
Neither works? Tell the user:
The Excalidraw canvas server is not running. To set up:
git clone https://github.com/yctimlin/mcp_excalidraw && cd mcp_excalidrawnpm ci && npm run buildPORT=3000 npm run canvas- Open
http://localhost:3000in a browser- (Recommended) Install the MCP server:
claude mcp add excalidraw -s user -e EXPRESS_SERVER_URL=http://localhost:3000 -- node /path/to/mcp_excalidraw/dist/index.js
| Operation | MCP Tool | REST API Equivalent |
|---|---|---|
| Create elements | batch_create_elements | POST /api/elements/batch |
| Get all elements | query_elements | GET /api/elements |
| Get one element | get_element | GET /api/elements/:id |
| Update element |
"text": "My Label" on shapes (auto-converts). REST requires "label": {"text": "My Label"}.startElementId/endElementId. REST requires "start": {"id": "..."} / "end": {"id": "..."}."1") or omit entirely. Never pass a number."label" in the PUT body to ensure it renders correctly after updates.The canvas uses a 2D coordinate grid: (0, 0) is the origin , x increases rightward , y increases downward. Plan your layout before writing any JSON.
General spacing guidelines:
max(160, labelCharCount * 9) to prevent text truncationThese are the most common mistakes that produce unreadable diagrams. Avoid all of them.
label.text (or text) on large background zone rectanglesWhen you put a label on a background rectangle, Excalidraw creates a bound text element centered in the middle of that shape — right where your service boxes will be placed. The text overlaps everything inside the zone and cannot be repositioned.
Wrong:
{"id": "vpc-zone", "type": "rectangle", "x": 50, "y": 50, "width": 800, "height": 400, "text": "VPC (10.0.0.0/16)"}
Right — use a free-standing text element anchored at the top of the zone:
{"id": "vpc-zone", "type": "rectangle", "x": 50, "y": 50, "width": 800, "height": 400, "backgroundColor": "#e3f2fd"},
{"id": "vpc-label", "type": "text", "x": 70, "y": 60, "width": 300, "height": 30, "text": "VPC (10.0.0.0/16)", "fontSize": 18, "fontWeight": "bold"}
The free-standing text element sits at the top corner of the zone and doesn't interfere with elements placed inside.
An arrow from an element in one layout zone to an element in a distant zone will draw a long diagonal line crossing through everything in between. In a multi-zone infra diagram this produces an unreadable tangle of spaghetti.
Design rule: Keep arrows within the same zone or tier. To show cross-zone relationships, use annotation text or separate the zones so their edges are adjacent (no elements between them), and route the arrow along the edge.
If you must connect across zones, use an elbowed arrow that travels along the perimeter — never through the middle of another zone.
Arrow labels are placed at the midpoint of the arrow. On short arrows, they overlap the shapes at both ends. On crowded diagrams, they collide with nearby elements.
Excalidraw diagrams are visual communication. If text is cut off, elements overlap, or arrows cross through unrelated shapes, the diagram becomes confusing and unprofessional — it defeats the whole purpose of drawing it. So after every batch of elements, verify before adding more.
After each batch_create_elements / POST /api/elements/batch, take a screenshot and check:
width and/or height.text/label.text on a background zone rectangle, the zone label will be centered in the middle of the zone, overlapping everything inside. Fix: delete the bound text element and add a free-standing text element at the top of the zone instead (see Layout Anti-Patterns above).If you find any issue: stop, fix it, re-screenshot, then continue. Say "I see [issue], fixing it" rather than glossing over problems. Only proceed once all checks pass.
Usecreate_from_mermaid when: the user already has a Mermaid diagram, or the structure maps cleanly to a flowchart/sequence/ER diagram with standard Mermaid syntax. It's fast and handles conversion automatically, though you get less control over exact layout.
Usebatch_create_elements directly when: you need precise layout control, the diagram type doesn't map to Mermaid well (e.g., custom architecture, annotated cloud diagrams), or you want elements positioned in a specific coordinate grid.
read_diagram_guide for design best practices (colors, fonts, anti-patterns).clear_canvas to start fresh.batch_create_elements — create shapes and arrows in one call. Custom id fields (e.g. "id": "auth-svc") make later updates easy.max(160, labelLength * 9). Use text field for labels.startElementId / endElementId — they auto-route to element edges.MCP element + arrow example:
{"elements": [
{"id": "lb", "type": "rectangle", "x": 300, "y": 50, "width": 180, "height": 60, "text": "Load Balancer"},
{"id": "svc-a", "type": "rectangle", "x": 100, "y": 200, "width": 160, "height": 60, "text": "Web Server 1"},
{"id": "svc-b", "type": "rectangle", "x": 450, "y": 200, "width": 160, "height": 60, "text": "Web Server 2"},
{"id": "db", "type": "rectangle", "x": 275, "y": 350, "width": 210, "height": 60, "text": "PostgreSQL"},
{"type": "arrow", "x": 0, "y": 0, "startElementId": "lb", "endElementId": "svc-a"},
{"type": "arrow", "x": 0, "y": 0, "startElementId": "lb", "endElementId": "svc-b"},
{"type": "arrow", "x": 0, "y": 0, "startElementId": "svc-a", "endElementId": "db"},
{"type": "arrow", "x": 0, "y": 0, "startElementId": "svc-b", "endElementId": "db"}
]}
curl -X DELETE http://localhost:3000/api/elements/clearPOST /api/elements/batch. Use "label": {"text": "..."} for labels."start": {"id": "..."} / "end": {"id": "..."}.POST /api/export/image → save PNG → run Quality Checklist.REST API element + arrow example:
curl -X POST http://localhost:3000/api/elements/batch \
-H "Content-Type: application/json" \
-d '{
"elements": [
{"id": "svc-a", "type": "rectangle", "x": 100, "y": 100, "width": 160, "height": 60, "label": {"text": "Service A"}},
{"id": "svc-b", "type": "rectangle", "x": 400, "y": 100, "width": 160, "height": 60, "label": {"text": "Service B"}},
{"type": "arrow", "x": 0, "y": 0, "start": {"id": "svc-a"}, "end": {"id": "svc-b"}, "label": {"text": "calls"}}
]
}'
Straight arrows can cross through elements in complex diagrams. Use curved or elbowed arrows when needed:
Curved arrows (smooth arc over obstacles):
{
"type": "arrow", "x": 100, "y": 100,
"points": [[0, 0], [50, -40], [200, 0]],
"roundness": {"type": 2}
}
The intermediate waypoint [50, -40] lifts the arrow upward. roundness: {type: 2} makes it smooth.
Elbowed arrows (right-angle / L-shaped routing):
{
"type": "arrow", "x": 100, "y": 100,
"points": [[0, 0], [0, -50], [200, -50], [200, 0]],
"elbowed": true
}
When to use which:
Rule: If an arrow would pass through an unrelated shape, add a waypoint to route around it.
Points format : Both [[x, y], ...] tuples and [{"x": ..., "y": ...}] objects are accepted; both are normalized automatically.
Using describe_scene and get_canvas_screenshot together is what makes this skill powerful.
describe_scene → returns structured text: element IDs, types, positions, labels, connections. Use this when you need to know what's on the canvas before making programmatic updates (find IDs, understand bounding boxes).get_canvas_screenshot → returns a PNG image of the actual rendered canvas. Use this for visual quality verification — it shows you exactly what the user sees, including truncation, overlap, and arrow routing.Feedback loop (MCP):
batch_create_elements
→ get_canvas_screenshot → "text truncated on auth-svc"
→ update_element (increase width) → get_canvas_screenshot → "overlap between auth-svc and rate-limiter"
→ update_element (reposition) → get_canvas_screenshot → "all checks pass"
→ proceed
Feedback loop (REST):
POST /api/elements/batch
→ POST /api/export/image → save PNG → evaluate
→ PUT /api/elements/:id (fix issues) → re-screenshot → evaluate
→ proceed
describe_scene to understand current state — note element IDs and positions.id or label text (not by x/y coordinates — they change).update_element to resize/recolor/move; delete_element to remove.get_canvas_screenshot to confirm the change looks right.get_element; check it's not locked with unlock_elements.For converting existing Mermaid diagrams to Excalidraw:
MCP mode:
create_from_mermaid(mermaidDiagram: "graph TD\n A --> B\n B --> C")
After conversion, call set_viewport with scrollToContent: true and get_canvas_screenshot to verify layout. If the auto-layout is poor (nodes crowded, edges crossing), identify problem elements with describe_scene and reposition with update_element.
REST mode:
curl -X POST http://localhost:3000/api/elements/from-mermaid \
-H "Content-Type: application/json" \
-d '{"mermaid": "graph TD\n A --> B\n B --> C"}'
.excalidraw: export_scene with optional filePath.excalidraw: import_scene with mode: "replace" or "merge"export_to_image with format: "png" or "svg" (requires browser open)export_to_excalidraw_url — encrypts scene, returns shareable excalidraw.com URLsnapshot_scene with a name before risky changes.describe_scene / get_canvas_screenshot.restore_snapshot to roll back if needed.duplicate_elements with elementIds and optional offsetX/offsetY (default: 20, 20). Useful for repeated patterns or copying layouts.
describe_scene — they may have been created off-screen. Use set_viewport with scrollToContent: true.get_element. Make sure startElementId/endElementId (MCP) or start.id/end.id (REST) match existing element IDs.snapshot_scene first, then clear_canvas and rebuild. Or to go back.references/cheatsheet.md: Complete MCP tool list (26 tools) + REST API endpoints + payload shapes.Weekly Installs
101
Repository
GitHub Stars
1.5K
First Seen
Feb 12, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode94
codex93
gemini-cli92
amp92
kimi-cli92
github-copilot92
agent-browser 浏览器自动化工具 - Vercel Labs 命令行网页操作与测试
157,400 周安装
update_elementPUT /api/elements/:id |
| Delete element | delete_element | DELETE /api/elements/:id |
| Clear canvas | clear_canvas | DELETE /api/elements/clear |
| Describe scene | describe_scene | GET /api/elements (parse manually) |
| Export scene | export_scene | GET /api/elements (save to file) |
| Import scene | import_scene | POST /api/elements/sync |
| Snapshot | snapshot_scene | POST /api/snapshots |
| Restore snapshot | restore_snapshot | GET /api/snapshots/:name then POST /api/elements/sync |
| Screenshot | get_canvas_screenshot | POST /api/export/image (needs browser) |
| Viewport | set_viewport | POST /api/viewport (needs browser) |
| Export image | export_to_image | POST /api/export/image (needs browser) |
| Export URL | export_to_excalidraw_url | Only via MCP |
set_viewport with scrollToContent: true to auto-fit.get_canvas_screenshot → run Quality Checklist → fix issues before next iteration.node scripts/export-elements.cjs --out diagram.elements.jsonnode scripts/import-elements.cjs --in diagram.elements.json --mode batch|syncrestore_snapshotunlock_elements first.describe_scene to inspect actual positions, then batch-update positions.label.text. If you clear and re-send elements, Excalidraw may re-inject its cached bound texts, causing duplicates. To clean up: (1) use query_elements / GET /api/elements to find elements of type: "text" with a containerId; (2) delete the unwanted ones with delete_element; (3) wait a few seconds for auto-sync to settle before exporting. The safest approach is to never put labels on background zone rectangles — use free-standing text elements instead.