liz-whiteboard-mcp
Click on "Deploy 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., "@liz-whiteboard-mcpadd a users table with id, name, email columns"
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.
liz-whiteboard-mcp — MCP Server for ER Diagrams & Database Schema Editing (Go, OAuth 2.1)
A Model Context Protocol (MCP) server that lets AI agents — Claude, Cursor, VS Code, Claude Code — read and edit entity-relationship (ER) diagrams and SQL database schemas in liz-whiteboard. Written in Go, it serves the Streamable HTTP transport and authenticates clients with OAuth 2.1 (PKCE + JWKS).
This is the AI integration layer for liz-whiteboard, the open-source collaborative ER diagram and database schema designer. Connect any MCP-compatible AI client and design databases conversationally — "add a users table with a one-to-many relationship to orders" — and watch the changes appear live on the whiteboard. Compiles to a single self-contained binary (pure Go, no cgo, no Node/Bun runtime).
Table of contents
Related MCP server: database-explorer-mcp
What it does
Exposes the liz-whiteboard ER diagram as MCP tools so an LLM agent can:
Discover — list the user's projects and whiteboards.
Read — load a whiteboard's full diagram (tables, columns, relationships, positions, subject areas) or a compact text schema summary.
Write — create / update / delete tables, columns, and relationships; reorder columns; bulk-move tables; create subject areas and manage their membership/position.
Draw on canvas boards — create canvas boards, and read, create, update, and delete the shapes, text, and connectors on them (see Canvas boards vs ER whiteboards).
Reads go straight to the app's SQLite database; writes are sent to the live collaboration server over Socket.IO and broadcast to every connected user in real time. Every request is scoped to the authenticated user (project-membership checks).
The 49 MCP tools
Group | Tools |
Discovery |
|
Read |
|
Tables |
|
Columns |
|
Relationships |
|
Positions |
|
Areas |
|
Batch |
|
Static |
|
Canvas read |
|
Canvas elements |
|
Canvas connectors |
|
Canvas boards |
|
Projects |
|
Folders |
|
Whiteboards |
|
Table references |
|
Cross-file table references
A reference node stands in for a table that lives on another ER whiteboard of the same project, so one board can show a relationship to a table it does not own. It is stored as a table row carrying the source ids plus stub columns for the columns you expose, which means an ordinary create_relationship connects a local table to it — the reference tools only manage the reference itself.
list_table_references resolves each one against its source and reports missing: true when that table or file has been deleted. A missing reference keeps its relationships rather than taking them down with it, so you can re-target it with update_table_reference or remove it deliberately.
Canvas boards vs ER whiteboards
A project holds two independent board types, and the tools do not cross between them. An ER whiteboard holds tables, columns, and relationships; every tool above the Canvas rows works on it. A canvas board is a freeform FigJam-style surface of shapes, text, and connectors; only the Canvas tools work on it. list_whiteboards never returns canvas boards, and list_canvas_boards never returns ER whiteboards.
Canvas writes take the same path as ER writes: the tool emits over Socket.IO to the collaboration server, so every open browser client re-renders without a reload. Reads come straight from SQLite.
Six behaviours are non-obvious and an agent calling these tools must know them.
styleis written whole, not merged. The app's style schema is az.strictObjectwith a default for every key, so astyleargument that names onlyfillresetsstroke,strokeWidth,fontSize,color,cornerRadius,textAlign, andverticalAlignto the engine defaults. To change one key and keep the rest, read the element first withget_canvas_boardand resend the full style.maxElementsis rejected above 500, never clamped.get_canvas_boardandget_canvas_summarydefault to 500 elements; a value below 1 or above 500 fails with a validation error namingmaxElements. A truncated read reportstruncated: trueandtotalElements, and returns the firstmaxElementsin paint order (zIndexascending, thencreatedAt).texttakes no null; an empty string clears the label. Omittextto leave the current label untouched. Pass""to remove it. The maximum length is 10,000 characters.update_canvas_connectorpreserves the endpoint keys it does not manage. It reads the stored element, merges your fields into a full replacementpropsobject, and carriessourceAttach,targetAttach, the legacysourceAnchor/targetAnchor, andcurvatureacross unchanged. A connector the user attached to a specific side in the UI keeps that attachment after an MCP re-route.delete_canvas_boardrequiresconfirmName, and it must match exactly. Pass the board's current name; read it first withget_canvas_board. A mismatch deletes nothing and returns a validation error namingconfirmName, and the error does not disclose the stored name — the guard exists to catch a wrongcanvasBoardId, so a blind retry must not be able to defeat it. The delete is permanent and cascades to every element and every share link on the board.connectorandgroupare not valid kinds forcreate_canvas_element. It acceptsrectangle,ellipse,diamond,triangle, andtextonly. Connectors have their own pair of tools, because theirpropscarry cross-field invariants a single generic union would make unreliable to fill. Groups are unsupported by design: their cascade and cycle integrity is a scene-level invariant the browser client repairs on load, and this server has no scene to check against.
Connectors carry two further rules, both checked before anything is written. Each end takes exactly one form: either an element id (sourceElementId / targetElementId) or a free point (sourcePoint / targetPoint). Giving both forms for one end, or neither, is a validation error. A connector also cannot join an element to itself, so sourceElementId and targetElementId must differ. create_canvas_connector takes no style; set a connector's colour or stroke afterwards with update_canvas_element.
Board lifecycle takes a different transport from element writes. create_canvas_board, update_canvas_board, and delete_canvas_board call the app's POST /api/canvas-boards route over HTTP (set LIZ_CANVAS_BOARD_API_URL), authenticated with the same collaboration JWT the socket path uses. Element writes stay on Socket.IO because open clients must re-render live; board creation has no co-viewing client to broadcast to. One consequence: update_canvas_board can move a board into a folder but cannot move it back to the project root, because an omitted folderId means "leave it where it is".
Server-owned fields are never accepted from a caller. The collaboration server assigns each element's id, computes zIndex as MAX(zIndex) + 1 on create, and forces rotation to 0. Use update_canvas_element's zIndex argument (range -1,000,000 to 1,000,000) to restack an element; there is no separate bring-to-front tool. Every canvas write requires the EDITOR role or higher, checked here and again by the collaboration server.
How it works
AI client (Claude / Cursor)
│ OAuth 2.1 (PKCE) → access token (RS256 JWT)
▼
liz-whiteboard-mcp ── OAuth 2.0 Resource Server (RFC 9728 + RFC 8707) ──
│ • validates the JWT via the AS's JWKS (iss / aud / exp / signature)
│ • resolves identity per request (sub = User.id), checks project access
├── reads → SQLite (data/app.db)
└── writes → Socket.IO collaboration server
(authenticated with a separate collab-audience JWT —
the client's token is never passed through)Transport: MCP Streamable HTTP (
POST /mcp).AuthN/Z: OAuth 2.1 Resource Server. Serves Protected Resource Metadata at
/.well-known/oauth-protected-resource, returns401+WWW-Authenticatefor unauthenticated requests, and validates audience-bound RS256 tokens issued by the liz-whiteboard Authorization Server.No token passthrough: writes use a distinct collaboration token (avoids the OAuth "confused deputy" problem).
How to install & run
Pick one of three ways to get the binary, then point an MCP client at it.
1. Install the binary
Option A — go install (needs Go 1.25+; produces a binary named mcp):
go install github.com/LizardLiang/liz-whiteboard-mcp/cmd/mcp@latest
# → $(go env GOPATH)/bin/mcp (add that dir to your PATH)Option B — build from source:
git clone https://github.com/LizardLiang/liz-whiteboard-mcp
cd liz-whiteboard-mcp
make build # → ./liz-whiteboard-mcpOption C — Docker (runs the whole stack; skip to Deploy with Docker).
2. Run it
Set the environment (see Configuration) and start the server. It listens on 127.0.0.1:3011 by default and serves the MCP endpoint at /mcp:
DATABASE_URL="file:/absolute/path/to/liz-whiteboard/data/app.db" \
OAUTH_ISSUER="https://your-domain" \
MCP_RESOURCE_URI="https://your-domain/mcp" \
LIZ_SOCKET_URL="ws://localhost:3010" \
MCP_CLIENT_SECRET="<shared-with-the-app>" \
COLLAB_TOKEN_URL="https://your-domain/api/collab-token" \
LIZ_CANVAS_BOARD_API_URL="https://your-domain/api/canvas-boards" \
./liz-whiteboard-mcpFor local testing without a full OAuth setup, use the dev-token path in Quick start instead.
3. Connect your AI client
Register the running server with any MCP client. For Claude Code:
claude mcp add --transport http liz-whiteboard https://your-domain/mcpOn first use the client runs the browser OAuth flow automatically (see Connect an MCP client) — no API keys to copy. For Claude Desktop / Cursor / VS Code, add the same URL in the client's MCP server settings.
Quick start (local, dev token)
Requirements: Go 1.25+.
make build # → ./liz-whiteboard-mcp (or: go build ./cmd/mcp/)
# Run with the DEV-ONLY stub verifier (skips the full OAuth flow for local testing).
# NEVER set MCP_DEV_AUTH in production.
DATABASE_URL="file:/absolute/path/to/liz-whiteboard/data/app.db" \
MCP_DEV_AUTH=stub \
MCP_DEV_STUB_TOKEN="dev-token" \
MCP_DEV_USER_ID="<a-real-user-uuid>" \
LIZ_SOCKET_URL="ws://localhost:3010" \
./liz-whiteboard-mcp
# → serves http://127.0.0.1:3011/mcpThen call it with Authorization: Bearer dev-token. Without MCP_DEV_AUTH=stub, the server runs in production mode and requires real OAuth (see below).
Deploy with Docker (single domain)
The repo ships a Docker Compose stack that runs the app + Authorization Server + this MCP server behind one reverse proxy (Caddy) — so clients use a single origin, no separate ports:
http://localhost:8080/ → liz-whiteboard app + OAuth (/authorize, /token, JWKS)
http://localhost:8080/mcp → this MCP serverbash deploy/run.sh # provisions a persistent signing key + secret, then docker compose upSee docker-compose.yml and deploy/Caddyfile. The Go server itself builds to a tiny distroless image via the Dockerfile.
Connect an MCP client (OAuth)
Point an MCP client (Claude Desktop, Claude Code, Cursor, VS Code) at the server URL (e.g. https://your-domain/mcp). The client performs the standard MCP OAuth flow automatically:
Calls
/mcp, gets401+ the Protected Resource Metadata URL.Discovers the Authorization Server, runs the browser authorize → consent → token flow (PKCE).
Retries
/mcpwith the bearer token.
No API keys or copied cookies required.
Configuration
Variable | Description |
| SQLite file — the same |
| Listen address (default |
| Public issuer URL of the Authorization Server; validated in the token |
| Canonical public URI of this server (e.g. |
| Optional — fetch JWKS from an internal address while |
| Collaboration Socket.IO server URL (write path), e.g. |
| Confidential-client credentials used to mint collaboration tokens from the AS ( |
| AS collab-token endpoint and the collaboration token audience. |
| App route for canvas board create / rename / delete (default |
| App route for project, folder and ER whiteboard create / update / delete (default |
| App route for cross-file table references (default |
| Dev only — enable the stub verifier. Never set in production. |
Project layout
cmd/mcp/main.go # entrypoint: HTTP transport, OAuth wiring, tool registration
internal/auth # OAuth Resource Server: JWKS verifier, per-request identity, project scoping
internal/db # SQLite connection (database/sql + modernc.org/sqlite, no cgo)
internal/data # raw-SQL read layer
internal/socket # Socket.IO write path (canvas + ER element writes)
internal/collabtoken # collab-audience JWT client, shared by the socket and HTTP paths
internal/appapi # HTTP client for the app's canvas-board route (board lifecycle)
internal/tools # MCP tool handlers (ER + canvas); tools.ToolCount is the tool-surface size
internal/errors # error taxonomy + token redaction
internal/{positioning,schema,summary} # helpersTesting
make test # unit tests (no database required)
# Integration tests against a real SQLite database:
make test-integration DATABASE_URL=file:/abs/path/to/liz-whiteboard/data/app.dbLicense
MIT © LizardLiang
Keywords: Model Context Protocol server, MCP server Go, MCP server example, OAuth 2.1 resource server, JWKS, PKCE, RFC 9728, RFC 8707, AI database design, ER diagram MCP, SQL schema MCP tools, Claude MCP server, Cursor MCP, Claude Code, Socket.IO, SQLite, modernc, self-hosted MCP, streamable HTTP MCP.
This server cannot be deployed
Maintenance
Related MCP Connectors
Read and edit DB Planner database schemas, diagrams and board layouts as an AI agent.
Create, read and live-edit visual boards, Kanban plans, Gantt timelines and diagrams with AI agents.
AI-powered ERD design tool. Create and manage database schemas using DBML with real-time canvas.
Generate, edit, and export data-architecture diagrams from your AI. Column lineage, PNG in chat.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables LLM agents to perform complete database operations on SQLite databases, including creating tables, executing queries, and managing data through CRUD operations with schema inspection capabilities.21 npmMIT
- AlicenseAqualityDmaintenanceEnables AI assistants to connect to and interact with PostgreSQL, MySQL, SQLite, and MongoDB databases through natural language, supporting schema exploration, query execution, data export, and more.13MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to interact with PostgreSQL or MySQL databases using natural language. Supports SQL queries, schema discovery, and pre-built aggregations without writing SQL.149 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to collaboratively read, write, draw, and diagram on a shared real-time whiteboard canvas with live human edits.1MIT