The MCP App server renders draw.io diagrams inline in AI chat interfaces using the MCP Apps protocol. Instead of opening a browser tab, diagrams appear directly in the conversation as interactive iframes.
- The LLM calls the
create_diagramtool with draw.io XML - The host fetches the UI resource and renders it in a sandboxed iframe
- The diagram is rendered using the official draw.io viewer
- The user sees an interactive diagram inline with zoom, pan, and layers support
| Parameter | Type | Required | Description |
|---|---|---|---|
xml |
string | Yes | draw.io XML in mxGraphModel format |
The rendered diagram includes:
- Interactive zoom, pan, and navigation
- Layer toggling and lightbox mode
- "Open in draw.io" button to edit the diagram in the full editor
- Fullscreen mode
The official draw.io MCP App server is hosted at:
https://mcp.draw.io/mcp
Add this URL as a remote MCP server in Claude.ai or any MCP Apps-compatible host — no installation or setup required.
If you prefer to run your own instance, you can use Node.js or deploy to Cloudflare Workers.
cd mcp-app-server
npm installStart the HTTP server (for Claude.ai and other web-based hosts):
npm startThe server listens on http://localhost:3001/mcp by default. Set the PORT environment variable to change the port.
Since Claude.ai needs a public URL, use a tunnel:
npx cloudflared tunnel --url http://localhost:3001Then add the tunnel URL (with /mcp appended) as a custom connector in Claude.ai settings.
Add to your Claude Desktop config:
{
"mcpServers": {
"drawio-app": {
"command": "node",
"args": ["path/to/mcp-app-server/src/index.js", "--stdio"]
}
}
}Note: Inline diagram rendering requires an MCP host that supports the MCP Apps extension. In hosts without MCP Apps support, the tool still works but returns the XML as text.
The server can be deployed to Cloudflare Workers for a public endpoint without tunnels.
- A Cloudflare account
- Node.js 18+
npm install
npx wrangler login # One-time: authenticate with Cloudflare
npm run deploy # Build + deploy to WorkersThe deploy script runs node src/build-html.js (pre-inlines the SDK bundles into the HTML) then wrangler deploy.
npm run dev:workerThis starts a local Workers dev server at http://localhost:8787/mcp.
Node.js (src/index.js) |
Worker (src/worker.js) |
|
|---|---|---|
| Transport | StreamableHTTPServerTransport (Express) |
WebStandardStreamableHTTPServerTransport (Web Standard Request/Response) |
| HTML build | Reads bundles from node_modules at startup |
Pre-built at deploy time via src/build-html.js → src/generated-html.js |
| Schema validation | Default (Zod-based) | Uses @cfworker/json-schema (Workers-compatible) |
src/
shared.js Shared logic: buildHtml(), processAppBundle(), createServer()
index.js Node.js entry (Express + stdio transports)
worker.js Cloudflare Workers entry (Web Standard fetch handler)
build-html.js Build script: generates generated-html.js + xml reference
generated-html.js (gitignored) Pre-built HTML string + XML reference for the Worker
wrangler.toml Wrangler configuration
../shared/
xml-reference.md Shared XML generation reference (single source of truth)
The create_diagram tool description is loaded from shared/xml-reference.md at startup (Node.js) or pre-built into generated-html.js at deploy time (Workers). This file is the single source of truth for XML generation guidance across all four approaches in the repository.
The server inlines two bundles into a self-contained HTML string:
app-with-deps.js(~319 KB) — MCP Apps SDK browser bundle from@modelcontextprotocol/ext-apps. The bundle is ESM (ends withexport { ... as App }), so the server strips the export statement and creates a localvar App = <minifiedName>alias. This makes it safe to inline in a plain<script>tag inside the sandboxed iframe.pako_deflate.min.js(~28 KB) — for compressing XML into the#create=URL format.
Both are inlined into the HTML served via registerAppResource. The draw.io viewer (viewer-static.min.js) is loaded from CDN at runtime.
For Node.js, this happens at startup (bundles read from node_modules via fs). For Workers, the build-html.js script does this at build time and writes generated-html.js.
- The MCP Apps sandbox uses
sandbox="allow-scripts"but notallow-same-origin, so Blob URL module imports fail silently. That's why the ESM export statement is stripped and a plainvaralias is created. app.openLink()must be used instead of<a target="_blank">since the sandbox doesn't haveallow-popups.GraphViewer.processElements()requires the container to have a nonzerooffsetWidth, hence themin-width: 200pxon#diagram-container.