mermaid-mcp
Provides tools for validating and rendering Mermaid diagrams to PNG or SVG, enabling AI agents to create and check diagram definitions programmatically.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mermaid-mcprender this mermaid diagram as PNG"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mermaid-mcp
Custom Mermaid MCP server — fast validation + PNG/SVG rendering via jsdom + sharp, no Chromium needed.
Replaces the broken @rtuin/mcp-mermaid-validator that consistently timed out (MCP error -32001) for every invocation, even on trivially small diagrams. The root cause was shell + npx overhead spawning Chromium/Puppeteer. This server eliminates Chromium entirely — smaller image, faster renders, no browser crash risk.
Why It Exists
The original @rtuin/mcp-mermaid-validator@0.7.0 used @mermaid-js/mermaid-cli under the hood, which spawns a headless Chromium browser for every render. This caused:
Timeouts — Chromium startup takes 2-5 seconds, exceeding MCP request timeouts
Large image — ~1 GB Docker image (Chromium + Node.js)
Crash risk — Headless browser crashes on certain diagram types
This server replaces Chromium with jsdom (fake DOM) + sharp (SVG → PNG rasterization), resulting in:
Instant validation — ~50 ms (pure Node.js,
mermaid.parse())Fast rendering — ~30-100 ms (jsdom + sharp, no browser)
Tiny image — ~150-300 MB (no Chromium)
Reliable — No browser crash risk
Features
Feature | Description |
| Parse-only validation — instant, no rendering, no DOM needed |
| Full rendering to PNG (default) or SVG via jsdom + sharp, no browser |
Stateless | Per-request |
HTTP-native | Node.js built-in |
Docker-ready | Multi-arch (amd64 + arm64), small image, |
Structured logging | JSON logs to stderr, debug level via |
Architecture
graph TD
subgraph "opencode"
A["agent<br/>calls tool via HTTP"]
end
subgraph "puma-net"
subgraph "mermaid-mcp container"
B["http.createServer"]
C["POST /mcp"]
D["McpServer (per request)"]
E["validate tool"]
F["render tool"]
G["mermaid.parse()"]
H["mermaid.render() + jsdom"]
I["sharp → PNG"]
end
end
A -->|"HTTP POST /mcp"| B
B --> C --> D
D --> E --> G
D --> F --> G
F -->|"if valid"| H --> I
I -->|"PNG buffer"| D
style G fill:#e8f5e9
style H fill:#e3f2fd
style I fill:#fff4e1Key design decisions:
Concern | Decision | Rationale |
Transport | HTTP (StreamableHTTPServerTransport, port 3000) | Remote opencode access, follows hugging-kreuzberg pattern |
HTTP server | Node.js built-in | No Express dependency, per user request |
Validation |
| Pure Node.js, instant, no DOM |
Rendering | jsdom + sharp (no Chromium) | Mermaid v11+ |
Docker base |
| Latest stable, no Chromium — tiny image, fast pulls |
Output | PNG default, SVG optional | PNG for inline chat; SVG for editable diagrams |
Quick Start
Prerequisites
Node.js 26+ (see
.nvmrc)Docker Desktop with Docker Compose
puma-netDocker network (created automatically bystart.sh)
Local Development
# Clone and install
cd ~/www/misc/mermaid-mcp
npm install
# Run locally (port 3000)
npm start
# Run tests (60 pass, 1 skip, 0 fail)
npm testDocker Compose
# Build and start
./start.sh
# Stop
./stop.sh
# Smoke test (7 HTTP tests via curl)
./test.shBuild and Push to Docker Hub
# Build + push (latest)
./build-and-push.sh
# Tag with version
./build-and-push.sh --tag v1.0.0
# Build only (skip push)
./build-and-push.sh --build-only
# ARM64 only
./build-and-push.sh --platform linux/arm64
# Dry run (show commands, don't execute)
./build-and-push.sh --dry-runTool Reference
validate
Validate a Mermaid diagram definition without rendering. Returns the detected diagram type on success or a parse error with line number on failure. Pure Node.js — instant, no browser needed.
Input:
Parameter | Type | Required | Description |
|
| Yes | Mermaid diagram definition text |
Output (valid):
{
"content": [
{
"type": "text",
"text": "Valid: flowchart"
}
]
}Output (invalid):
{
"content": [
{
"type": "text",
"text": "Invalid: Parse error on line 3: ..."
}
],
"isError": true
}Example (MCP JSON-RPC):
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "validate",
"arguments": {
"diagram": "graph TD\n A[Start] --> B[End]"
}
}
}'render
Render a Mermaid diagram to PNG or SVG. Validates first, then renders via jsdom (no browser needed). PNG output uses 2x DPI (density 144) for crisp images.
Input:
Parameter | Type | Required | Default | Description |
|
| Yes | — | Mermaid diagram definition text |
|
| No |
| Output format — |
|
| No |
| Background color for PNG (CSS color value, e.g. |
Output (PNG):
{
"content": [
{
"type": "text",
"text": "Rendered as PNG (12345 bytes)"
},
{
"type": "image",
"data": "<base64-encoded PNG>",
"mimeType": "image/png"
}
]
}Output (SVG):
{
"content": [
{
"type": "text",
"text": "Rendered as SVG (5678 bytes)"
},
{
"type": "image",
"data": "<base64-encoded SVG>",
"mimeType": "image/svg+xml"
}
]
}Output (error):
{
"content": [
{
"type": "text",
"text": "Error: Parse error on line 3: ..."
}
],
"isError": true
}Example (MCP JSON-RPC):
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "render",
"arguments": {
"diagram": "graph TD\n A[Start] --> B[End]",
"format": "png",
"backgroundColor": "white"
}
}
}'Development
Running Locally
# Start the server (port 3000)
npm start
# Start with debug logging
LOG_LEVEL=debug npm start
# Start on a different port
MCP_PORT=8080 npm startRunning Tests
# All tests (60 pass, 1 skip, 0 fail)
npm test
# Specific test file
node --test src/renderer.test.js
node --test src/tools.test.js
node --test src/mcp-server.test.jsTest Coverage
Test File | Tests | Coverage |
| 36 | Validate (6 diagram types), render SVG (6 types), render PNG (5 types + 1 skipped), error paths |
| 4 | Tool schema registration, validate handler, render handler (PNG + SVG) |
| 21 | MCP protocol (initialize, tools/list, tools/call), HTTP error codes (405, 415, 413, 400) |
Total | 60 | 60 pass, 1 skip, 0 fail |
Smoke Tests (Docker)
# Start compose, wait for server, run 7 HTTP tests
./start.sh && ./test.shThe smoke test suite (test.sh) tests via curl against the running container:
MCP initialize handshake
tools/listreturns 2 tools (validate, render)validate— valid flowchartvalidate— invalid diagramrender— PNG formatrender— SVG formatrender— invalid diagram
Build and Push
# Build + push to tuiteraz/mermaid-mcp:latest (multi-arch: amd64 + arm64)
./build-and-push.sh
# Build only (local image)
./build-and-push.sh --build-only
# Push with version tag
./build-and-push.sh --tag v1.0.0
# ARM64 only
./build-and-push.sh --platform linux/arm64
# Dry run (preview commands)
./build-and-push.sh --dry-runDeployment
Local Compose
# Start (creates puma-net if missing, builds image, runs container)
./start.sh
# Stop
./stop.shPuma-LAN (puma-net)
The deployment lives at ~/www/olho/puma-lan/lite-llm/mcp/mermaid/ and uses the Infisical pattern for secret management.
# Deploy to puma-net
cd ~/www/olho/puma-lan/lite-llm/mcp/mermaid
./start.sh
# Stop from puma-net
./stop.sh
# Pin image digest (for reproducible deployments)
./pin-image-digest.sh
# Build and push (copies source from misc dir, builds, pushes)
./build-and-push.shopencode Integration
The server is configured in ~/.config/opencode/opencode.jsonc:
"mermaid": {
"type": "remote",
"url": "https://lite-llm.lan/mcp/mermaid",
"enabled": true,
"category": "validation",
"enabledTools": ["validate", "render"]
}This replaces the old mermaid-validator entry that used @rtuin/mcp-mermaid-validator via stdio transport.
Project Structure
mermaid-mcp/
├── Dockerfile # node:26.3.0-slim + fonts-dejavu + fonts-liberation
├── docker-compose.yml # Local compose (puma-net, port 3000)
├── package.json # Pinned deps, ESM, npm scripts
├── package-lock.json # Lockfile
├── .nvmrc # Node.js 26
├── .gitignore
├── .husky/ # Git hooks
├── start.sh # Start local compose (creates puma-net if needed)
├── stop.sh # Stop local compose
├── build-and-push.sh # Build + push to Docker Hub (multi-arch)
├── test.sh # Smoke tests (7 HTTP tests via curl)
└── src/
├── mcp-server.mjs # Entry: http.createServer + StreamableHTTPServerTransport
├── tools.js # validate + render tool definitions (Zod schemas)
├── renderer.js # Render pipeline: mermaid.parse → mermaid.render → sharp
├── polyfills.js # jsdom + browser polyfills (rAF, ResizeObserver, CSSStyleSheet, SVG)
├── config.js # Frozen config object from env vars
├── logger.js # Structured JSON logging to stderr
├── mcp-server.test.js # Server tests (21: MCP protocol + HTTP error codes)
├── tools.test.js # Tool tests (4: schema + handler)
└── renderer.test.js # Renderer tests (36: validate + render, 1 skip)Key Files
File | Purpose |
| HTTP server entry point. Uses Node.js built-in |
| MCP tool definitions. |
| Core rendering pipeline. |
| Browser environment for Mermaid in Node.js. Creates a single |
| Configuration — single frozen object loaded from environment variables at startup. No |
| Structured JSON logging to stderr (stdout reserved for HTTP in Docker). |
Configuration
All configuration is loaded from environment variables at startup via src/config.js. No process.env access outside this module.
Variable | Default | Description |
|
| HTTP server port |
|
| Default output format ( |
|
| Default background color for PNG rendering (CSS color value) |
|
| Render scale factor |
|
| Log level ( |
|
| Maximum request body size in bytes (10 MB) |
Example:
# Development with debug logging
LOG_LEVEL=debug MCP_PORT=8080 npm start
# Production with white background
MERMAID_BACKGROUND_COLOR=white MCP_PORT=3000 npm startDocker
Image
FROM node:26.3.0-slim
├── fonts-dejavu (~20 MB, proper Mermaid text rendering)
├── fonts-liberation (~20 MB, Arial compatibility)
├── npm ci --omit=dev (production deps only)
└── node src/mcp-server.mjsImage size: ~150-300 MB (vs ~1 GB for mermaid-cli with Chromium)
Registry
Images are pushed to docker.io/tuiteraz/mermaid-mcp via build-and-push.sh. Supports multi-arch builds (amd64 + arm64) via docker buildx.
Known Issues
Gantt PNG Rendering Fails
Symptom: Rendering a Gantt diagram to PNG fails with an image conversion error.
Root cause: Mermaid produces viewBox="0 0 0 124" (zero width) for Gantt diagrams when rendered via jsdom. This is a mermaid rendering bug, not an issue with this server. The SVG rendering path works fine for Gantt diagrams.
Workaround: Use format: "svg" for Gantt diagrams.
Test: The test render PNG — gantt diagram is skipped (test.skip) with this note. All other diagram types render to PNG successfully.
Affected diagram type: gantt only. Flowchart, sequence, class, state, ER, and pie diagrams all render to PNG correctly.
License
This project is part of the internal tooling for the Puma LAN infrastructure.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/oleksii-honchar/mermaid-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server