AIPUBLISHED UPDATED 8 MIN READ

Claude Code + Excalidraw: my prompt was wrong

Claude Code and Excalidraw connect through an MCP server, and once they do, the agent can draw on a live canvas, look at what it drew, and fix it. Setup is two Docker commands and takes about five minutes, most of which is spent finding out that a VS Code-only install has no global claude binary. The harder part comes after: my first diagram had eleven modules, twenty arrows crossing the whole canvas, and not one path you could trace from end to end. The tool was fine. My prompt described what to include and said nothing about how it should look, so the agent solved the only problem I gave it. This post has the full setup, the checks that tell you it really connected, and the prompt I now paste every time.

TL;DR

  • Two Docker commands connect Claude Code to a live Excalidraw canvas. No Excalidraw+ account, no API key. The install trap is that a VS Code-only setup has no global claude binary.
  • claude mcp list saying connected is not proof. Check the canvas is serving on port 3000 and that your running session can see the tools, because the tool list loads at session startup.
  • My first diagram was correct and unreadable. Three layout rules fixed it, and the full prompt containing them is in this post.

Two things are called an Excalidraw MCP. Pick the right one.

This trips people up before they install anything, because the two projects share a name and solve different problems.

The official Excalidraw+ MCP is a chat widget. You prompt, a diagram streams into the conversation, the model gets a couple of tools. It needs an API key from an Excalidraw+ account. Fine for a one-off picture in a chat.

The community server, yctimlin/mcp_excalidraw, is a workbench. A persistent local canvas, 26 tools for creating, reading, updating and deleting elements one at a time, and a screenshot tool so the agent can look at its own output. MIT licensed, runs on your machine through Docker or Node, no account, no key.

For a coding agent only the second one is useful, and the screenshot tool is the reason. An agent that cannot see its own canvas is drawing blind and handing you whatever comes out. An agent that can look will catch its own overlaps before you ever see them. Everything in this post uses the community server.

Connecting Claude Code to Excalidraw

Two containers do two different jobs, and mixing them up is the most common setup mistake. The canvas server is the whiteboard itself: a web UI, a REST API and websocket sync, running persistently. The MCP server is the bridge Claude Code talks to, and it is disposable - it starts per session and pushes every change to the canvas.

Start the canvas first:

docker run -d -p 3000:3000 --name excalidraw-canvas \
  ghcr.io/yctimlin/mcp_excalidraw-canvas:latest

Open http://localhost:3000 and you should see an empty Excalidraw board. Leave that tab open - it matters later.

Then register the bridge with Claude Code:

claude mcp add excalidraw -- docker run -i --rm \
  -e EXPRESS_SERVER_URL=http://host.docker.internal:3000 \
  -e ENABLE_CANVAS_SYNC=true \
  ghcr.io/yctimlin/mcp_excalidraw:latest

ENABLE_CANVAS_SYNC=true is what makes the agent's work show up on your board instead of living in memory and disappearing.

The trap that cost me twenty minutes. If you installed Claude Code as the VS Code extension and never installed the CLI separately, that second command fails in every shell:

/usr/bin/bash: line 1: claude: command not found
claude : The term 'claude' is not recognized as the name of a cmdlet...

There is no global binary. The CLI ships inside the extension, at a path like ...\extensions\anthropic.claude-code-2.1.226-win32-x64\resources\native-binary\claude.exe. Calling claude mcp add through that full path works immediately.

Two things to know before you build on it. That path contains a version number, so the next extension update breaks anything you scripted against it - if this is going into a repo, install the CLI properly instead. And claude mcp add writes to local scope by default: the config lands in ~/.claude.json for that project only, not in the .mcp.json your teammates would get from --scope project.

How do you know the MCP server actually connected?

"Added successfully" is not verification. There are three separate things that can be true or false independently, and I have had two of them green while the third quietly was not.

Check

Command

What it proves

The host registered it

claude mcp list

Claude Code knows the server exists and can start it

The canvas is alive

open localhost:3000

The board the server syncs to is actually serving

Your session can use it

/mcp inside the session

The tools are callable right now, in this conversation

The third one is where I lost time. The CLI said connected, the canvas was serving, and /mcp in my running session still listed three servers instead of four. Nothing was broken. A session loads its tool list at startup, so a server added afterwards stays invisible until you restart. If you are mid-conversation and do not want to lose the context, finish what you are doing and restart before you start drawing.

One more failure worth recognising, because it looks like a server problem and is not. Partway through a session my screenshots started timing out after 30 seconds and the element count stopped updating. The server was fine and the data was all there - the browser tab had silently dropped its websocket connection. Bound-label rendering and image export both happen in the frontend, so when that tab goes quiet, both stall.

Symptom

What it looks like

What it is

Screenshot times out at 30s

The MCP server is hanging

The browser tab lost its connection

Element count stops updating

Writes are failing

Same tab, same problem

The canvas API still returns data

Nothing makes sense

Server is healthy, frontend is not

Reload localhost:3000 and both clear instantly. Check which side is broken before you debug either. I ran into the same shape of problem wiring Claude Code to Obsidian - a connector reporting healthy is where the check starts, not where it ends.

What I asked for, and what I got

I had backend-architecture.md in the repo already. Eleven modules with their dependencies: auth, multi-currency, e-commerce, refunds and amendments, security, payments, inventory, notifications, reporting, admin. The planning doc most backend teams have and nobody rereads.

So I asked for the obvious thing:

Read backend-architecture.md and draw an architecture diagram of this backend,
showing the modules and how they connect.

Look at what came back. Every module is there. Every dependency is correct. Auth gates access, Refunds reads from E-commerce, the reporting lines run one way only. As a description of my system it is accurate.

Now try to answer a question with it. What happens when a refund comes in? Your eye starts at 4. Refunds & Amendments, crosses to 3. E-commerce on a horizontal arrow, then hunts for 2. Multi-currency somewhere below, then 7. Payments back on the right, while three orange dashed lines cut through that path and the grey reporting lines cross everything on their way to the corner. authorized and approves sit so close they read as one phrase.

Nothing is wrong. Nobody can read it.

Everything on this diagram is correct. Try tracing what happens when a refund comes in.

Why do AI-generated architecture diagrams come out messy?

Because the prompt described content and said nothing about form, so the agent optimised for the only thing it was asked about: including everything and connecting it correctly. It did that perfectly. Readability was never in the brief.

I spent a while fixing symptoms - move this label, shorten that one, nudge a box. It got marginally better and stayed unreadable, which is the tell that you are working on the wrong problem.

What changed it was showing instead of describing. I handed over two reference diagrams as images: a Netflix architecture map, and a domain-oriented backend map with a gateway and grouped domain containers. Within one pass the layout was right, and the principle behind both references was not better arrow routing:

Clean diagrams do not route long arrows well. They have almost no long arrows.

Count the arrows on any architecture diagram you find readable. The reference backend map used about eight arrows for twenty-five boxes. Mine used twenty for eleven. That ratio is the whole difference, and it comes out of three rules.

Containment replaces arrows. A box inside a labelled commerce domain container already says it belongs to commerce. Drawing an arrow to say the same thing costs a line across the canvas and buys nothing.

Every connector is a right angle and stays short. No diagonals. A diagonal crosses more of the page than an elbow does, so it collides with more things.

One flow per lane. Give each end-to-end path its own horizontal band. Flows in separate bands cannot collide, structurally, however many you add. This is the rule that scales - the other two make today's diagram cleaner, this one keeps it clean when you add the twelfth module.

The prompt you can copy

Point your agent at your architecture doc and paste this. It encodes the three rules so you do not have to discover them the way I did.

Keep it in English even if you work in another language - the agent handles it better, and your module names are probably English anyway.

Read <YOUR-ARCHITECTURE-DOC.md> and draw an architecture diagram of this system
on the Excalidraw canvas.

LAYOUT RULES - these matter more than completeness:

1. Group before you connect. Put related modules inside labelled containers
   (for example: commerce domain, money domain, platform domain). Nesting a box
   inside a container already expresses that it belongs there, so do not also
   draw an arrow saying the same thing. Cross-cutting concerns like security
   wrap everything as an outer dashed boundary rather than connecting to each
   module individually.

2. Every connector is orthogonal and short. Right angles only, no diagonals.
   If two things need a long connector, they are probably in the wrong place -
   move them closer instead of drawing a longer line.

3. One flow per lane. After the system overview, add a separate horizontal lane
   for each end-to-end path (checkout, refund, sign-in, admin action, webhook,
   reporting). Each lane reads left to right as one story. Flows in separate
   lanes cannot collide with each other.

4. Annotate every step. Under each module box in a flow lane, add a short note
   saying what that module does IN THAT FLOW - not what the module is. Write
   "locks the exchange rate at purchase", not "handles currency".

5. Nothing overlaps. No two boxes, no label on top of another label, no
   connector passing through a box, no panel straddling the edge of a container.

BEFORE YOU START: call read_diagram_guide and follow its conventions.

WHEN YOU ARE DONE: take a screenshot of the canvas and look at it. Check for
overlapping labels, connectors crossing each other, and any path a reader
cannot follow from start to finish. Fix what you find, then show me the result.

Two lines in there do more work than the rest, and both are easy to leave out.

read_diagram_guide is a tool the server ships that hands back its own house style: palette with hex values, minimum shape sizes, arrow-binding rules, a list of anti-patterns. It is free quality and the agent will not call it unless you ask.

The screenshot instruction matters because the agent can see its own canvas. Make it look. Mine caught two overlaps I would have missed and fixed them before showing me anything.

If the first result is still too dense, the follow-up that works is asking for less on one canvas rather than better routing on the same one: split this into a system overview plus separate flow diagrams.

What came back the second time

Same markdown file, same modules, same tool. Two diagrams instead of one.

The system overview stopped being a web. A dashed Security boundary wraps the backend, because security is a cross-cutting concern and drawing eleven arrows out of it was always the wrong picture. Auth sits at the entry column. Three containers - commerce domain, money domain, platform domain - hold the modules that belong to each, so membership costs nothing. External payment providers sit outside the security boundary, exactly where they belong, and reach in with one right-angled arrow.

Then six flow lanes, one per end-to-end path: browse and checkout, refund and amendment, sign-in with MFA, staff admin action, payment provider webhook, reporting. Each reads left to right as one story.

The annotations turned out to matter more than I expected. A box labelled 2. Multi-currency tells you nothing you did not already know. The same box in the refund lane, with reuses the rate snapshot taken at purchase under it, is a design decision made visible. Anyone reading that lane now knows we refund at the original rate rather than today's, and that is the kind of thing that normally lives in one engineer's head until the week they are on holiday.

That is the difference between a picture of your system and a document about it.

Same eleven modules. Security wraps the backend, domains group what belongs together, and the arrows are short.
The refund path, readable in one pass. This is the question the first diagram could not answer.

What is this duo actually good for?

Three things, and only one of them is what I expected.

Onboarding. A new backend engineer traces the refund path in a minute instead of assembling it from files scattered across a repo. Obvious, real, and not the biggest.

Design review. This is the one that surprised me. With the refund lane laid out end to end, the annotations made a coupling obvious that I had never articulated: refunds depend on a rate snapshot taken at purchase, which means that snapshot has to survive as long as the refund window stays open. That was always true of the system. I had just never seen it written next to the thing that depends on it. A diagram that makes you argue with your own design is worth more than one that documents it.

Talking to people who are not backend engineers. Frontend, product, whoever signs off. The hand-drawn style does real work here: a polished corporate diagram invites nitpicking about whether every box is exactly right, a sketch invites a conversation about the design. Same information, completely different meeting.

The honest cost is that the first attempt will be wrong and you should budget an afternoon rather than ten minutes. But the .excalidraw file lives next to the code and the agent can update it, which is more than I can say for any diagram I ever drew by hand. Those were accurate on the day I drew them and started rotting immediately.

Next thing I want to try: regenerating the diagram in CI on every merge to main and diffing it against the committed version, so the architecture doc cannot quietly drift away from the architecture.

I described what I wanted and got a diagram nobody could read. I showed two examples and got the layout in one pass.

Ogtay Iskandarov

Designer and full-stack developer running klauzzdcode, a one-person studio in Baku. Freelance since 2023, I ship products from Figma to deploy and write down what survives contact with production.

STORY →

FAQ

How do I connect Claude Code to Excalidraw?

Run the canvas server on port 3000 in Docker, then register the bridge with claude mcp add excalidraw, passing ENABLE_CANVAS_SYNC=true so the agent's work lands on your board. Use yctimlin/mcp_excalidraw, which needs no account. If claude is not found, the CLI sits inside the VS Code extension.

Do I need an Excalidraw+ subscription for this?

No. The community server yctimlin/mcp_excalidraw is MIT licensed and runs entirely on your machine via Docker or Node, with no account and no API key. Excalidraw's official endpoint does need a key, but it is a chat widget rather than a workbench.

Why does Claude Code not see my MCP server after adding it?

Because a session loads its tool list at startup, so a server added mid-session stays invisible until you restart. Run /mcp inside the session to see what it can actually call. claude mcp list proves the host registered it, not that your conversation can use it.

How do I prompt an AI agent for a better architecture diagram?

Give it layout rules, not just content. Group modules inside labelled containers instead of connecting them with arrows, keep every connector at a right angle, give each flow its own horizontal lane, and annotate each step. Then tell it to screenshot the canvas and fix what it finds.

Will this work on a real codebase, not just a planning doc?

Yes, and pointing the agent at the code rather than a markdown file usually gives a more honest diagram, because it draws what is there instead of what someone intended. A real repo has far more detail, so ask for the overview first and generate flow lanes separately.