Skip to main content
Glama

mermaid-mcp

An MCP server that gives an AI agent full access to your Mermaid diagrams and collections — list, read, create, update and delete over the Mermaid REST API.

  • Two transports. stdio for local agents (Claude Code, opencode), stateless Streamable HTTP for remote clients and shared deployments.

  • No database, no session state. It is a thin, well-behaved proxy in front of the Mermaid API: whatever the backend can do, the agent can do.

  • Per-caller credentials. Each HTTP request carries its own Mermaid API key, so one deployment can serve many users without leaking anyone's diagrams.


Tools

Tool

What it does

list_diagrams

List diagrams. Optional search (name substring), page, limit (max 100).

get_diagram

Fetch one diagram by id, including its full Mermaid source.

create_diagram

Create a diagram from a name and Mermaid source, optionally in a collection.

update_diagram

Change a diagram's name, content and/or collectionId (null = unfiled).

delete_diagram

Permanently delete a diagram.

list_collections

List collections (diagram folders).

create_collection

Create a collection.

delete_collection

Delete a collection. Its diagrams survive and become unfiled.

list_diagrams returns the full source of every diagram in the page, so responses get large fast. Give the agent a search term or a small limit when it only needs an overview.


Related MCP server: MCP Diagram Server

Quick start

Local agent (stdio)

Create an API key in the Mermaid dashboard (avatar menu → API keys), then point your client at this server:

npm install
MERMAID_API_KEY=ek_... npm run start:stdio

Claude Code

claude mcp add mermaid --env MERMAID_API_KEY=ek_... -- node /path/to/mermaid-mcp/src/index.js --stdio

opencode — add to opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "mermaid": {
      "type": "local",
      "command": ["node", "/path/to/mermaid-mcp/src/index.js", "--stdio"],
      "environment": { "MERMAID_API_KEY": "ek_..." },
      "enabled": true
    }
  }
}

Remote client (HTTP)

MERMAID_BASE_URL=https://mermaid.alward.dev PORT=8120 node src/index.js

The endpoint is POST http://<host>:8120/mcp, stateless. Send the caller's key on every request:

curl -X POST http://localhost:8120/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -H "authorization: Bearer $MERMAID_API_KEY" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

accept must include both application/json and text/event-stream, as the MCP Streamable HTTP spec requires.

opencode / other remote MCP clients:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "mermaid": {
      "type": "remote",
      "url": "https://mermaid.example.com/mcp",
      "enabled": true,
      "headers": { "authorization": "Bearer ek_..." }
    }
  }
}

Docker

docker build -t mermaid-mcp .
docker run -d -p 8120:8120 -e MERMAID_BASE_URL=https://mermaid.alward.dev mermaid-mcp

The image runs as the unprivileged node user and ships a HEALTHCHECK against /healthz, so docker run reports healthy on its own.

Deploying to a server

The repo is the source of truth. Clone it on the host and build from the checkout:

git clone https://github.com/anas-alward/mermaid-mcp.git mcp-server
cd mcp-server && npm ci          # only needed to run the suite on the host

To deploy an update:

cd mcp-server && git pull
cd .. && docker compose build mcp && docker compose up -d mcp
docker compose ps                 # wait for (healthy)
curl -s localhost:8120/healthz   # version confirms which build is live

Two things to know when the server directory is a git checkout:

  • .env is gitignored, so it never travels with the clone. Create it on the host; the compose file declares it required: false, and the server only reads it for --stdio.

  • .dockerignore excludes .git, so clone history stays out of the build context and the image.

Without git on the host, rsync the tree instead — the deploy directory only needs to match the repository, plus .env.


Authentication

The server holds no credentials of its own. In HTTP mode the key must arrive on the request, as either header:

authorization: Bearer ek_...
x-mermaid-api-key: ek_...

A request without one gets 401 and a JSON-RPC error explaining how to fix it. MERMAID_API_KEY is only consulted in --stdio mode, where there are no HTTP headers to read it from.

Changed in 1.0.0. 0.2.0 silently fell back to the server's MERMAID_API_KEY when a request carried no key — which meant a publicly reachable deployment would hand one account's diagrams to anonymous callers. HTTP mode is now strict.

Upgrading from 0.2.x

  • Entry point moved: node server.mjs → node src/index.js (add --stdio for stdio mode). Container CMD and client configs need the new path.

  • HTTP requests must carry their own key; MERMAID_API_KEY is now stdio-only.

  • MERMAID_HOST_HEADER is gone. It never worked — fetch will not let you override Host — so if you were reaching a backend directly, point MERMAID_BASE_URL at the address that routes to it.

  • Tool names, parameters and responses are unchanged.


Configuration

All configuration is environment variables, validated at boot: a bad value stops the process with a message naming the variable, rather than failing later on the first tool call.

Variable

Default

Purpose

MERMAID_BASE_URL

https://mermaid.alward.dev

Origin of the Mermaid deployment. No trailing slash, no /api/v1.

MERMAID_API_KEY

—

Dashboard API key. stdio mode only; ignored in HTTP mode.

HOST

0.0.0.0

HTTP bind address. Use 127.0.0.1 to keep it local.

PORT

8120

HTTP port. 0 picks a free port (used by tests).

MCP_PATH

/mcp

Path the MCP endpoint is served from.

REQUEST_TIMEOUT_MS

15000

Abort an upstream call that takes longer.

MAX_BODY_SIZE

2mb

Largest accepted MCP request body.

LOG_LEVEL

info

debug, info, warn, error, silent.

CORS_ORIGINS

*

Comma-separated browser origins, or *.

ALLOWED_HOSTS

—

Comma-separated Host allow-list. Unset disables the check.

Copy .env.example as a starting point. The process does not read .env files itself — use your process manager, docker run -e, or --env-file.


HTTP endpoints

Route

Purpose

POST /mcp

MCP JSON-RPC. Stateless; no session id is issued.

GET /healthz

Liveness/readiness for load balancers and the Docker healthcheck.

GET /

Service name, version and where things live.

GET/DELETE /mcp

405 with a JSON-RPC error — this endpoint is POST-only.

Errors are always JSON-RPC shaped: -32700 for a malformed body, -32600 for a bad request or missing key, -32603 for an internal fault. Upstream failures (bad key, missing diagram, timeout) come back as tool results with isError: true, so the agent can read what went wrong and retry instead of seeing a transport crash.

Security knobs. Auth is a bearer token, not a cookie, so there is no CSRF surface; CORS_ORIGINS only matters for browser clients. Set ALLOWED_HOSTS when the server is reachable from a browser to close off DNS-rebinding. Terminate TLS in front of it — the server speaks plain HTTP.

Operations. Logs go to stderr, one line per event, with credential-shaped fields redacted. Set LOG_LEVEL=debug to also see rejected requests. SIGINT/SIGTERM drain in-flight requests and exit 0, with a 10s backstop.


Development

npm install
npm test          # node:test — no network, no API key needed
npm run dev       # HTTP mode with --watch

The suite runs against an in-process mock of the Mermaid API, so it is fast and hermetic. It covers the API client (auth, error mapping, timeouts, id encoding), all eight tools through a real MCP client, and the HTTP layer (auth, CORS, host validation, body limits, JSON-RPC error shapes).

Layout

src/
  index.js    entry point: transport selection, shutdown, fatal error handling
  config.js   environment parsing and validation
  logger.js   levelled stderr logging with redaction
  api.js      Mermaid REST client: auth, timeout, error translation
  tools.js    the eight MCP tools
  http.js     Express app, stateless Streamable HTTP, health, CORS, host checks
test/         node:test suites + mock backend

Adding a tool means adding one server.registerTool(...) block in src/tools.js and a test in test/tools.test.js. Nothing else needs to know about it.


Before you publish

git init, then push — CI runs the suite on Node 20 and 22.

License

MIT © 2026 Anas Alward. See LICENSE.

Related MCP Connectors

Related MCP Servers