-
Notifications
You must be signed in to change notification settings - Fork 162
Description
Summary
When using the export_to_excalidraw tool from the Excalidraw MCP server, diagrams exported to excalidraw.com render shapes and arrows correctly but all text is missing — both standalone text elements and label properties on shapes/arrows.
Important
The Agent-- claude-opus-4.6-high-- produced this bug description based on what it had to do to resolve the issue. I've reviewed it, and it comports with what I saw, but it might not be the correct fix. YMMV, of course.
Environment
- Client: Cursor IDE (VS Code fork)
- MCP Server:
excalidraw-mcp(remote,https://excalidraw-mcp-app.vercel.app/mcp) - Date: 2026-02-12
Steps to Reproduce
- Use the
create_viewtool to render a diagram with labeled shapes and standalone text elements using the simplified MCP element format:
[
{ "type": "rectangle", "id": "r1", "x": 100, "y": 100, "width": 200, "height": 80,
"backgroundColor": "#a5d8ff", "fillStyle": "solid", "roundness": { "type": 3 },
"strokeColor": "#4a9eed",
"label": { "text": "My Label", "fontSize": 16 } },
{ "type": "text", "id": "t1", "x": 100, "y": 10,
"text": "Title Text", "fontSize": 28, "strokeColor": "#1e1e1e" }
]-
The diagram renders correctly as far as the Agent is concerned via the
create_viewtool — all labels and text appear as expected-- but is simply not visible to the user (Diagram is not visible on Cursor #17). -
Use the
export_to_excalidrawtool with the same element data (retrieved viaread_checkpoint). -
Open the resulting excalidraw.com URL in a browser.
Expected Behavior
The exported diagram on excalidraw.com should match what was rendered inline — all text, labels, and arrow annotations should be visible.
Actual Behavior
The exported diagram on excalidraw.com shows only shapes and arrows. All text is missing:
- Standalone
textelements do not render. labelproperties onrectangle,ellipse,diamond, andarrowelements do not render.- Arrow labels (e.g.,
"label": { "text": "connects" }) do not render.
Screenshot of the exported diagram with no text:
Root Cause
The simplified MCP element format used by create_view is not the same as the native Excalidraw element format expected by excalidraw.com. The export_to_excalidraw tool appears to pass through the simplified JSON without converting it to proper Excalidraw format. Specifically:
1. Standalone text elements are missing required fields
The MCP simplified format allows:
{ "type": "text", "id": "t1", "x": 100, "y": 10,
"text": "Hello", "fontSize": 20 }But excalidraw.com requires all of the following fields for text to render:
{
"id": "t1", "type": "text",
"x": 100, "y": 10,
"width": 110, "height": 25,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"roundness": null,
"seed": 12345,
"version": 1,
"versionNonce": 67890,
"isDeleted": false,
"boundElements": null,
"link": null,
"locked": false,
"text": "Hello",
"rawText": "Hello",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "Hello",
"autoResize": true,
"lineHeight": 1.25
}Missing fields like fontFamily, rawText, originalText, containerId, lineHeight, autoResize, width, and height cause excalidraw.com to silently drop the element.
2. The label shorthand on shapes is not a native Excalidraw property
The MCP format supports a convenient label property on shapes:
{ "type": "rectangle", "id": "r1", ..., "label": { "text": "Hello", "fontSize": 16 } }But native Excalidraw does not have a label property. Instead, labeled shapes require two separate elements:
- The shape element, with a
boundElementsarray referencing the text:
{
"id": "r1", "type": "rectangle", ...,
"boundElements": [{ "id": "r1_text", "type": "text" }]
}- A separate text element bound to the shape via
containerId:
{
"id": "r1_text", "type": "text",
"x": 120, "y": 130,
"containerId": "r1",
"textAlign": "center",
"verticalAlign": "middle",
"text": "Hello",
"rawText": "Hello",
"originalText": "Hello",
"fontSize": 16,
"fontFamily": 1,
"lineHeight": 1.25,
"autoResize": true
}The x and y of the bound text must be explicitly calculated to center within the container:
x = container.x + (container.width - textWidth) / 2
y = container.y + (container.height - textHeight) / 2
3. Arrow labels have the same issue
Arrow label properties in the MCP format also need to be converted to separate bound text elements with containerId pointing to the arrow, positioned at the arrow's midpoint.
Workaround
To get text to display in the exported diagram, I had to:
- Navigate to the exported excalidraw.com URL (which loaded shapes/arrows into localStorage).
- Use JavaScript (
page.evaluate) to:- Parse the existing elements from
localStorage.getItem('excalidraw'). - Add
boundElementsreferences to each shape/arrow that should have a label. - Create new, properly-formatted text elements with
containerIdbindings and calculatedx/ypositions. - Create standalone text elements with all required fields.
- Write the updated elements back to localStorage.
- Parse the existing elements from
- Reload the page.
- Use Excalidraw's native "Share > Export to Link" to generate a new URL.
Suggested Fix
The export_to_excalidraw tool should transform the simplified MCP element format into native Excalidraw format before uploading. This means:
-
For standalone
textelements: populate all required fields (rawText,originalText,fontFamily: 1,lineHeight: 1.25,autoResize: true,containerId: null,width,height,angle: 0,groupIds: [], etc.). -
For
labelon shapes: convert eachlabelinto a separate bound text element with:- A unique
id(e.g.,${shapeId}_label). containerIdset to the shape'sid.textAlign: "center"andverticalAlign: "middle".- Calculated
x/ycentered within the container. - The shape's
boundElementsarray updated to reference the new text element. - All other required text fields populated.
- A unique
-
For
labelon arrows: same as shapes, but position the text at the arrow's midpoint. -
For all elements: ensure common required fields are present (
angle,seed,version,versionNonce,isDeleted,boundElements,groupIds,frameId,link,locked,updated).
The create_view tool already handles this conversion for inline rendering in Cursor — the same transformation logic should be applied in export_to_excalidraw.