mermaid-mcp

mermaid-mcp

Fast Mermaid diagram validation and rendering (PNG/SVG) using jsdom and sharp, no Chromium needed.

Category
访问服务器

README

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
validate Parse-only validation — instant, no rendering, no DOM needed
render Full rendering to PNG (default) or SVG via jsdom + sharp, no browser
Stateless Per-request McpServer + transport — no cross-request state
HTTP-native Node.js built-in http module — no Express dependency
Docker-ready Multi-arch (amd64 + arm64), small image, puma-net deployment
Structured logging JSON logs to stderr, debug level via LOG_LEVEL env var

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:#fff4e1

Key design decisions:

Concern Decision Rationale
Transport HTTP (StreamableHTTPServerTransport, port 3000) Remote opencode access, follows hugging-kreuzberg pattern
HTTP server Node.js built-in http No Express dependency, per user request
Validation mermaid.parse() Pure Node.js, instant, no DOM
Rendering jsdom + sharp (no Chromium) Mermaid v11+ render() works with jsdom; sharp rasterizes SVG→PNG
Docker base node:26.3.0-slim 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-net Docker network (created automatically by start.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 test

Docker Compose

# Build and start
./start.sh

# Stop
./stop.sh

# Smoke test (7 HTTP tests via curl)
./test.sh

Build 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-run

Tool 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
diagram string 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
diagram string Yes — Mermaid diagram definition text
format enum No png Output format — png (raster) or svg (vector)
backgroundColor string No transparent Background color for PNG (CSS color value, e.g. "white" or "transparent")

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 start

Running 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.js

Test Coverage

Test File Tests Coverage
src/renderer.test.js 36 Validate (6 diagram types), render SVG (6 types), render PNG (5 types + 1 skipped), error paths
src/tools.test.js 4 Tool schema registration, validate handler, render handler (PNG + SVG)
src/mcp-server.test.js 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.sh

The smoke test suite (test.sh) tests via curl against the running container:

  1. MCP initialize handshake
  2. tools/list returns 2 tools (validate, render)
  3. validate — valid flowchart
  4. validate — invalid diagram
  5. render — PNG format
  6. render — SVG format
  7. render — 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-run

Deployment

Local Compose

# Start (creates puma-net if missing, builds image, runs container)
./start.sh

# Stop
./stop.sh

Puma-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.sh

opencode 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
src/mcp-server.mjs HTTP server entry point. Uses Node.js built-in http.createServer (no Express). Each request gets its own stateless McpServer + StreamableHTTPServerTransport pair. Handles POST /mcp, validates Content-Type, enforces body size limit (10 MB), graceful shutdown on SIGINT.
src/tools.js MCP tool definitions. validate — parse-only validation via mermaid.parse(). render — validate + render pipeline. Input schemas use Zod. Returns MCP-formatted responses with text + image content.
src/renderer.js Core rendering pipeline. validate(diagram) — uses mermaid.parse() with suppressErrors: false, returns diagram type or parse error. render(diagram, format, backgroundColor) — validates first, then mermaid.render() for SVG, then sharp for PNG conversion (density 144, compression 9).
src/polyfills.js Browser environment for Mermaid in Node.js. Creates a single JSDOM instance, attaches window/document/Element to global, polyfills requestAnimationFrame, ResizeObserver, CSSStyleSheet, SVG getBBox/getCTM. Lazy-loads DOMPurify + Mermaid via dynamic import() after globals are set.
src/config.js Configuration — single frozen object loaded from environment variables at startup. No process.env access outside this module.
src/logger.js Structured JSON logging to stderr (stdout reserved for HTTP in Docker). logInfo always emits; logDebug only when LOG_LEVEL=debug.

Configuration

All configuration is loaded from environment variables at startup via src/config.js. No process.env access outside this module.

Variable Default Description
MCP_PORT 3000 HTTP server port
MERMAID_DEFAULT_FORMAT png Default output format (png or svg)
MERMAID_BACKGROUND_COLOR transparent Default background color for PNG rendering (CSS color value)
MERMAID_SCALE 1 Render scale factor
LOG_LEVEL info Log level (info or debug)
MCP_MAX_BODY_SIZE 10485760 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 start

Docker

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.mjs

Image 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.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选