Merge pull request #30 from yctimlin/docker-update

Docker update
This commit is contained in:
yctimlin
2025-11-01 17:21:12 +08:00
committed by GitHub
13 changed files with 7829 additions and 169 deletions
+55
View File
@@ -0,0 +1,55 @@
# Dependencies
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Build outputs
dist
build
*.log
# Git
.git
.gitignore
.gitattributes
# IDE
.vscode
.idea
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Testing
coverage
.nyc_output
*.test.ts
*.spec.ts
# CI/CD
.github
# Documentation
*.md
!README.md
docs
# Docker
Dockerfile*
docker-compose*.yml
.dockerignore
# Environment
.env
.env.local
.env.*.local
# Misc
tmp
temp
*.tmp
+72
View File
@@ -0,0 +1,72 @@
name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
build-and-test:
name: Build and Type Check
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x, 22.x]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run TypeScript type check
run: npm run type-check
- name: Build project
run: npm run build
- name: Check build artifacts
run: |
echo "Checking if build artifacts exist..."
test -f dist/index.js || (echo "dist/index.js not found" && exit 1)
test -f dist/server.js || (echo "dist/server.js not found" && exit 1)
test -d dist/frontend || (echo "dist/frontend not found" && exit 1)
echo "All build artifacts present!"
- name: Upload build artifacts
if: matrix.node-version == '20.x'
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: |
dist/
retention-days: 7
lint-check:
name: Lint Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Check for TypeScript errors
run: npm run type-check
+146
View File
@@ -0,0 +1,146 @@
name: Docker Build & Push
on:
push:
branches: [ main ]
tags:
- 'v*.*.*'
pull_request:
branches: [ main ]
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME_MCP: ${{ github.repository }}
IMAGE_NAME_CANVAS: ${{ github.repository }}-canvas
jobs:
build-and-push-mcp:
name: Build and Push MCP Server Image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for MCP Server
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_MCP }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha,prefix=sha-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push MCP Server image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
build-and-push-canvas:
name: Build and Push Canvas Server Image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for Canvas Server
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_CANVAS }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha,prefix=sha-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Canvas Server image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.canvas
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
test-docker-images:
name: Test Docker Images
needs: [build-and-push-mcp, build-and-push-canvas]
runs-on: ubuntu-latest
steps:
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Determine image tag
id: tag
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo "value=pr-${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
else
echo "value=${{ github.ref_name }}" >> $GITHUB_OUTPUT
fi
- name: Test Canvas Server image
run: |
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_CANVAS }}:${{ steps.tag.outputs.value }}
docker run -d -p 3000:3000 --name test-canvas ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_CANVAS }}:${{ steps.tag.outputs.value }}
sleep 10
curl -f http://localhost:3000/health || exit 1
docker logs test-canvas
docker stop test-canvas
- name: Test MCP Server image
run: |
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_MCP }}:${{ steps.tag.outputs.value }}
echo "MCP Server image pulled successfully"
+106
View File
@@ -0,0 +1,106 @@
name: Publish to NPM
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Tag to publish (e.g., latest, beta, next)'
required: true
default: 'latest'
jobs:
publish:
name: Publish to NPM Registry
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run type check
run: npm run type-check
- name: Build project
run: npm run build
- name: Verify build artifacts
run: |
echo "Verifying build artifacts..."
test -f dist/index.js || (echo "ERROR: dist/index.js not found" && exit 1)
test -f dist/server.js || (echo "ERROR: dist/server.js not found" && exit 1)
test -d dist/frontend || (echo "ERROR: dist/frontend not found" && exit 1)
echo "All required artifacts present!"
- name: Get package version
id: package-version
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
- name: Check if version exists on NPM
id: check-version
run: |
if npm view mcp-excalidraw-server@${{ steps.package-version.outputs.version }} version 2>/dev/null; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Version ${{ steps.package-version.outputs.version }} already exists on NPM"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Version ${{ steps.package-version.outputs.version }} does not exist on NPM"
fi
- name: Publish to NPM (Release)
if: github.event_name == 'release' && steps.check-version.outputs.exists == 'false'
run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish to NPM (Manual)
if: github.event_name == 'workflow_dispatch' && steps.check-version.outputs.exists == 'false'
run: npm publish --tag ${{ github.event.inputs.tag }} --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Skip publishing (version exists)
if: steps.check-version.outputs.exists == 'true'
run: |
echo "⚠️ Skipping publish - version ${{ steps.package-version.outputs.version }} already exists on NPM"
echo "Please bump the version in package.json before publishing"
- name: Create GitHub Release Assets
if: github.event_name == 'release'
run: |
tar -czf mcp-excalidraw-server-${{ steps.package-version.outputs.version }}.tar.gz dist/
- name: Upload Release Assets
if: github.event_name == 'release'
uses: softprops/action-gh-release@v1
with:
files: |
mcp-excalidraw-server-${{ steps.package-version.outputs.version }}.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
notify:
name: Publish Notification
needs: publish
runs-on: ubuntu-latest
if: success()
steps:
- name: Success notification
run: |
echo "✅ Package successfully published to NPM!"
echo "View at: https://www.npmjs.com/package/mcp-excalidraw-server"
-1
View File
@@ -1,6 +1,5 @@
# Dependencies
node_modules/
package-lock.json
# Build artifacts
dist/
+40 -10
View File
@@ -1,5 +1,27 @@
# Production stage - MCP Backend Only
FROM node:18-slim
# Dockerfile for MCP Excalidraw Server
# This builds the MCP server only (core product for CI/CD and GHCR)
# The canvas server is optional and runs separately
# Stage 1: Build backend (TypeScript compilation)
FROM node:18-slim AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install all dependencies (including TypeScript compiler)
RUN npm ci && npm cache clean --force
# Copy backend source
COPY src ./src
COPY tsconfig.json ./
# Compile TypeScript
RUN npm run build:server
# Stage 2: Production MCP Server
FROM node:18-slim AS production
# Create non-root user for security
RUN addgroup --system --gid 1001 nodejs && \
@@ -13,16 +35,24 @@ COPY package*.json ./
# Install only production dependencies
RUN npm ci --only=production && npm cache clean --force
# Copy source code (only backend files needed)
COPY src ./src
# Copy compiled backend (MCP server only)
COPY --from=builder /app/dist ./dist
# Set environment variables
ENV NODE_ENV=production
ENV EXPRESS_SERVER_URL=http://localhost:3000
ENV ENABLE_CANVAS_SYNC=true
# Set ownership to nodejs user
RUN chown -R nodejs:nodejs /app
# Switch to non-root user
USER nodejs
# Run MCP server only
CMD ["npm", "start"]
# Set environment variables with defaults
ENV NODE_ENV=production
ENV EXPRESS_SERVER_URL=http://localhost:3000
ENV ENABLE_CANVAS_SYNC=true
# Run MCP server (stdin/stdout protocol)
CMD ["node", "dist/index.js"]
# Labels for metadata
LABEL org.opencontainers.image.source="https://github.com/yctimlin/mcp_excalidraw"
LABEL org.opencontainers.image.description="MCP Excalidraw Server - Model Context Protocol for AI agents"
LABEL org.opencontainers.image.licenses="MIT"
+82
View File
@@ -0,0 +1,82 @@
# Dockerfile for Canvas Server (Optional)
# This is for users who want to run the visual canvas UI
# The canvas server provides the web interface and REST API
# Stage 1: Build frontend
FROM node:18-slim AS frontend-builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install all dependencies (including dev dependencies for build)
RUN npm ci && npm cache clean --force
# Copy frontend source
COPY frontend ./frontend
COPY vite.config.js ./
# Build frontend
RUN npm run build:frontend
# Stage 2: Build backend (TypeScript compilation)
FROM node:18-slim AS backend-builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install all dependencies (including TypeScript compiler)
RUN npm ci && npm cache clean --force
# Copy backend source
COPY src ./src
COPY tsconfig.json ./
# Compile TypeScript
RUN npm run build:server
# Stage 3: Production Canvas Server
FROM node:18-slim AS production
# Create non-root user for security
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 --gid 1001 nodejs
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install only production dependencies
RUN npm ci --only=production && npm cache clean --force
# Copy compiled backend from builder stage
COPY --from=backend-builder /app/dist ./dist
# Copy built frontend from frontend-builder stage
COPY --from=frontend-builder /app/dist/frontend ./dist/frontend
# Set ownership to nodejs user
RUN chown -R nodejs:nodejs /app
# Switch to non-root user
USER nodejs
# Set environment variables with defaults
ENV NODE_ENV=production
ENV PORT=3000
ENV HOST=0.0.0.0
# Expose port
EXPOSE 3000
# Run canvas server (web UI + REST API)
CMD ["node", "dist/server.js"]
# Labels for metadata
LABEL org.opencontainers.image.source="https://github.com/yctimlin/mcp_excalidraw"
LABEL org.opencontainers.image.description="MCP Excalidraw Canvas Server - Web UI and REST API"
LABEL org.opencontainers.image.licenses="MIT"
+342 -138
View File
@@ -1,25 +1,34 @@
# MCP Excalidraw Server: Advanced Live Visual Diagramming with AI Integration
[![CI](https://github.com/yctimlin/mcp_excalidraw/actions/workflows/ci.yml/badge.svg)](https://github.com/yctimlin/mcp_excalidraw/actions/workflows/ci.yml)
[![Docker Build & Push](https://github.com/yctimlin/mcp_excalidraw/actions/workflows/docker.yml/badge.svg)](https://github.com/yctimlin/mcp_excalidraw/actions/workflows/docker.yml)
[![NPM Version](https://img.shields.io/npm/v/mcp-excalidraw-server)](https://www.npmjs.com/package/mcp-excalidraw-server)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
A comprehensive **TypeScript-based** system that combines **Excalidraw's powerful drawing capabilities** with **Model Context Protocol (MCP)** integration, enabling AI agents to create and manipulate diagrams in real-time on a live canvas.
## 🚦 Current Status & Version Information
> **📋 Choose Your Installation Method**
| Version | Status | Recommended For |
|---------|--------|----------------|
| **Local Development** | ✅ **FULLY TESTED** | **🎯 RECOMMENDED** |
| **NPM Published** | 🔧 **DEBUGGING IN PROGRESS** | Development testing |
| **Docker Version** | 🔧 **UNDER DEVELOPMENT** | Future deployment |
| Component | Local | Docker | Status |
|-----------|-------|--------|--------|
| **Canvas Server** | ✅ Fully Working | ✅ Fully Working | **Production Ready** |
| **MCP Server** | ✅ Fully Working | ✅ Fully Working | **Production Ready** |
| **NPM Published** | 🔧 In Progress | N/A | Development testing |
### **Current Recommendation: Local Development**
### **Important: Canvas and MCP Server Run Separately**
For the most stable experience, we recommend using the local development setup. We're actively working on improving the NPM package and Docker deployment options.
This system consists of **two independent components**:
### **Development Notes**
- **NPM Package**: Currently debugging MCP tool registration issues
- **Docker Version**: Improving canvas synchronization reliability
- **Local Version**: ✅ All features fully functional
1. **Canvas Server** - Runs the live Excalidraw canvas (web interface)
2. **MCP Server** - Connects to Claude Desktop/Claude Code/Cursor IDE
**You can choose any combination:**
- Canvas: Local OR Docker
- MCP Server: Local OR Docker
Both local and Docker setups are **fully working** and production-ready!
## 🚀 What This System Does
@@ -39,17 +48,47 @@ For the most stable experience, we recommend using the local development setup.
## 🏛️ Architecture Overview
### **Two Independent Components**
```
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
AI Agent │───▶│ MCP Server │───▶│ Canvas Server
(Claude) (src/index.js) │ (src/server.js)
└─────────────────┘ └──────────────────┘ └─────────────────┘
┌─────────────────┐
│ Frontend
(React + WS)
└─────────────────┘
┌─────────────────────────────────────────────────────────────────┐
Component 1
🎨 CANVAS SERVER
│ (Runs Independently) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Canvas Server │◀───────▶│ Frontend │ │
│ │ (src/server.js) │ (React + WS) │
Port 3000 │ Excalidraw UI
│ └─────────────────┘ └─────────────────┘
│ │
│ 📍 Start: npm run canvas OR docker run (canvas) │
└─────────────────────────────────────────────────────────────────┘
│ HTTP API
│ (Optional)
┌─────────────────────────────────────────────────────────────────┐
│ Component 2 │
│ 🤖 MCP SERVER │
│ (Runs Independently) │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ AI Agent │◀───────▶│ MCP Server │ │
│ │ (Claude) │ │ (src/index.js) │ │
│ │ Desktop/Code │ stdio │ MCP Protocol │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ 📍 Configure in: claude_desktop_config.json OR .mcp.json │
└─────────────────────────────────────────────────────────────────┘
🎯 Key Points:
• Canvas and MCP server are SEPARATE processes
• Canvas can run locally OR in Docker
• MCP server can run locally OR in Docker
• Canvas provides the visual interface (optional)
• MCP server connects Claude to the canvas (via HTTP API)
```
## 🌟 Key Features
@@ -84,57 +123,78 @@ For the most stable experience, we recommend using the local development setup.
## 📦 Installation & Setup
### **✅ Recommended: Local Development Setup**
### **Step 1: Choose Your Canvas Server Setup**
> **Most stable and feature-complete option**
The canvas server provides the live Excalidraw interface.
#### **1. Clone the Repository**
#### **Option A: Local Canvas Server**
1. **Clone and Install**
```bash
git clone https://github.com/yctimlin/mcp_excalidraw.git
cd mcp_excalidraw
npm install
```
#### **2. Build the Frontend**
2. **Build the Project**
```bash
npm run build
```
#### **3. Start the System**
##### **Option A: Production Mode (Recommended)**
3. **Start Canvas Server**
```bash
# Start canvas server (serves frontend + API)
# Production mode (recommended)
npm run canvas
```
##### **Option B: Development Mode**
```bash
# Start both canvas server and Vite dev server
npm run dev
```
#### **4. Access the Canvas**
Open your browser and navigate to:
4. **Access the Canvas**
```
http://localhost:3000
```
### **🔧 Alternative Installation Methods (In Development)**
#### **Option B: Docker Canvas Server**
#### **NPM Package (Beta)**
**Option B1: Use Pre-built Image from GHCR** (Recommended)
```bash
# Currently debugging tool registration - feedback welcome!
npm install -g mcp-excalidraw-server
npx mcp-excalidraw-server
docker pull ghcr.io/yctimlin/mcp_excalidraw-canvas:latest
docker run -d -p 3000:3000 --name mcp-excalidraw-canvas ghcr.io/yctimlin/mcp_excalidraw-canvas:latest
```
#### **Docker Version (Coming Soon)**
**Option B2: Build Locally**
```bash
# Canvas sync improvements in progress
docker run -p 3000:3000 mcp-excalidraw-server
git clone https://github.com/yctimlin/mcp_excalidraw.git
cd mcp_excalidraw
docker build -f Dockerfile.canvas -t mcp-excalidraw-canvas .
docker run -d -p 3000:3000 --name mcp-excalidraw-canvas mcp-excalidraw-canvas
```
3. **Access the Canvas**
```
http://localhost:3000
```
---
### **Step 2: Configure MCP Server in Your IDE**
The MCP server connects your AI assistant (Claude) to the canvas. **Choose local OR Docker format** based on your preference.
#### **Setup Combinations**
You can mix and match any combination:
| Canvas Server | MCP Server | Status |
|---------------|------------|--------|
| ✅ Local | ✅ Local | Recommended |
| ✅ Local | ✅ Docker | Fully Working |
| ✅ Docker | ✅ Local | Fully Working |
| ✅ Docker | ✅ Docker | Fully Working |
Configuration examples are provided in the next section for:
- Claude Desktop
- Claude Code
- Cursor IDE
## 🔧 Available Scripts
| Script | Description |
@@ -223,87 +283,221 @@ The MCP server provides these tools for creating visual diagrams:
}
```
## 🔌 Integration with Claude Desktop
## 🔌 MCP Server Configuration for IDEs
### **✅ Recommended: Using Local Installation**
### **Prerequisites**
✅ Ensure your **canvas server is running** (from Step 1):
- Local: `npm run canvas`
- Docker: `docker run -d -p 3000:3000 mcp-excalidraw-canvas`
For the **local development version** (most stable), add this configuration to your `claude_desktop_config.json`:
Canvas should be accessible at http://localhost:3000
### **Quick Reference**
Choose your configuration based on IDE and preference:
| IDE | Config File | Format Options |
|-----|-------------|----------------|
| **Claude Desktop** | `claude_desktop_config.json` | Local ⭐ / Docker ✅ |
| **Claude Code** | `.mcp.json` (project root) | Local ⭐ / Docker ✅ |
| **Cursor** | `.cursor/mcp.json` | Local ⭐ / Docker ✅ |
⭐ = Recommended | ✅ = Fully Working
---
## **Configuration for Claude Desktop**
Edit your `claude_desktop_config.json` file:
### **Format 1: Local MCP Server** ⭐ Recommended
```json
{
"mcpServers": {
"excalidraw": {
"command": "node",
"args": ["/absolute/path/to/mcp_excalidraw/dist/index.js"]
}
}
}
```
**Important**: Replace `/absolute/path/to/mcp_excalidraw` with the actual absolute path to your cloned repository. Note that the path now points to `dist/index.js` (the compiled TypeScript output).
### **🔧 Alternative Configurations (Beta)**
#### **NPM Package (Beta Testing)**
```json
{
"mcpServers": {
"excalidraw": {
"command": "npx",
"args": ["-y", "mcp-excalidraw-server"]
}
}
}
```
*Currently debugging tool registration - let us know if you encounter issues!*
#### **Docker Version (Coming Soon)**
```json
{
"mcpServers": {
"excalidraw": {
"command": "docker",
"args": ["run", "-i", "--rm", "mcp-excalidraw-server"]
}
}
}
```
*Canvas sync improvements in progress.*
## 🔧 Integration with Other Tools
### **Cursor IDE**
Add to your `.cursor/mcp.json`:
```json
{
"mcpServers": {
"excalidraw": {
"command": "node",
"args": ["/absolute/path/to/mcp_excalidraw/dist/index.js"]
}
}
}
```
### **VS Code MCP Extension**
For VS Code MCP extension, add to your settings:
```json
{
"mcp": {
"servers": {
"excalidraw": {
"command": "node",
"args": ["/absolute/path/to/mcp_excalidraw/dist/index.js"]
"args": ["/absolute/path/to/mcp_excalidraw/dist/index.js"],
"env": {
"EXPRESS_SERVER_URL": "http://localhost:3000",
"ENABLE_CANVAS_SYNC": "true"
}
}
}
}
```
**Important:** Replace `/absolute/path/to/mcp_excalidraw` with your actual installation path.
### **Format 2: Docker MCP Server** ✅ Fully Working
**Using Pre-built Image from GHCR** (Recommended):
```json
{
"mcpServers": {
"excalidraw": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"--network", "host",
"-e", "EXPRESS_SERVER_URL=http://localhost:3000",
"-e", "ENABLE_CANVAS_SYNC=true",
"ghcr.io/yctimlin/mcp_excalidraw:latest"
]
}
}
}
```
**OR Build Locally**:
```bash
cd mcp_excalidraw
docker build -f Dockerfile -t mcp-excalidraw .
```
Then use `mcp-excalidraw` as the image name in the configuration above.
---
## **Configuration for Claude Code**
Create or edit `.mcp.json` in your project root:
### **Format 1: Local MCP Server** ⭐ Recommended
```json
{
"mcpServers": {
"excalidraw": {
"command": "node",
"args": ["/absolute/path/to/mcp_excalidraw/dist/index.js"],
"env": {
"EXPRESS_SERVER_URL": "http://localhost:3000",
"ENABLE_CANVAS_SYNC": "true"
}
}
}
}
```
**Important:** Replace `/absolute/path/to/mcp_excalidraw` with your actual installation path.
### **Format 2: Docker MCP Server** ✅ Fully Working
**Using Pre-built Image from GHCR** (Recommended):
```json
{
"mcpServers": {
"excalidraw": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"--network", "host",
"-e", "EXPRESS_SERVER_URL=http://localhost:3000",
"-e", "ENABLE_CANVAS_SYNC=true",
"ghcr.io/yctimlin/mcp_excalidraw:latest"
]
}
}
}
```
**OR Build Locally**:
```bash
cd mcp_excalidraw
docker build -f Dockerfile -t mcp-excalidraw .
```
Then use `mcp-excalidraw` as the image name in the configuration above.
### **Alternative: Using Claude CLI**
```bash
# Project-scoped (recommended)
claude mcp add --scope project --transport stdio excalidraw \
-- docker run -i --rm --network host \
-e EXPRESS_SERVER_URL=http://localhost:3000 \
-e ENABLE_CANVAS_SYNC=true \
mcp-excalidraw
# User-scoped (available across all projects)
claude mcp add --scope user --transport stdio excalidraw \
-- docker run -i --rm --network host \
-e EXPRESS_SERVER_URL=http://localhost:3000 \
-e ENABLE_CANVAS_SYNC=true \
mcp-excalidraw
```
---
## **Configuration for Cursor IDE**
Edit `.cursor/mcp.json`:
### **Format 1: Local MCP Server** ⭐ Recommended
```json
{
"mcpServers": {
"excalidraw": {
"command": "node",
"args": ["/absolute/path/to/mcp_excalidraw/dist/index.js"],
"env": {
"EXPRESS_SERVER_URL": "http://localhost:3000",
"ENABLE_CANVAS_SYNC": "true"
}
}
}
}
```
### **Format 2: Docker MCP Server** ✅ Fully Working
**Using Pre-built Image from GHCR** (Recommended):
```json
{
"mcpServers": {
"excalidraw": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"--network", "host",
"-e", "EXPRESS_SERVER_URL=http://localhost:3000",
"-e", "ENABLE_CANVAS_SYNC=true",
"ghcr.io/yctimlin/mcp_excalidraw:latest"
]
}
}
}
```
**OR Build Locally**:
```bash
cd mcp_excalidraw
docker build -f Dockerfile -t mcp-excalidraw .
```
Then use `mcp-excalidraw` as the image name in the configuration above.
---
## **Important Configuration Notes**
| Setting | Purpose | Required |
|---------|---------|----------|
| `EXPRESS_SERVER_URL` | Canvas server URL | Yes (default: http://localhost:3000) |
| `ENABLE_CANVAS_SYNC` | Enable real-time canvas sync | Yes (set to "true") |
| `--network host` | Docker access to localhost | Required for Docker |
| `-i` flag | Interactive stdin/stdout | Required for Docker |
**Canvas is optional**: The MCP server works without the canvas in API-only mode (for programmatic access only).
## 🛠️ Environment Variables
| Variable | Default | Description |
@@ -378,37 +572,46 @@ The canvas server provides these REST endpoints:
## 🐛 Troubleshooting
### **NPM Package Issues**
- **Symptoms**: MCP tools not registering properly
- **Temporary Solution**: Use local development setup
- **Status**: Actively debugging - updates coming soon
### **Docker Version Notes**
- **Symptoms**: Elements may not sync to canvas immediately
- **Temporary Solution**: Use local development setup
- **Status**: Improving synchronization reliability
### **Canvas Not Loading**
- Ensure `npm run build` completed successfully
- Check that `dist/index.html` exists
- Check that `dist/index.html` and `dist/frontend/` directory exist
- Verify canvas server is running on port 3000
- Check if port 3000 is already in use: `lsof -i :3000` (macOS/Linux) or `netstat -ano | findstr :3000` (Windows)
### **Elements Not Syncing**
- Confirm MCP server is running (`npm start`)
- Check `ENABLE_CANVAS_SYNC=true` in environment
- Verify canvas server is accessible at `EXPRESS_SERVER_URL`
- Confirm canvas server is running and accessible at http://localhost:3000
- Check `ENABLE_CANVAS_SYNC=true` in MCP server environment configuration
- Verify `EXPRESS_SERVER_URL` points to correct canvas server URL
- Check browser console for WebSocket connection errors
- For Docker: Ensure `--network host` flag is used
### **WebSocket Connection Issues**
- Check browser console for WebSocket errors
- Ensure no firewall blocking WebSocket connections
### **WebSocket Connection Issues**
- Check browser console for WebSocket errors (F12 → Console tab)
- Ensure no firewall blocking WebSocket connections on port 3000
- Try refreshing the browser page
- Verify canvas server is running: `curl http://localhost:3000/health`
### **Docker Issues**
**Canvas Container:**
- Check if container is running: `docker ps | grep canvas`
- View logs: `docker logs mcp-excalidraw-canvas`
- Ensure port 3000 is not already in use
**MCP Container:**
- For Docker MCP server, ensure `--network host` is used (required to access localhost:3000)
- Verify `-i` flag is present (required for MCP stdin/stdout protocol)
- Check environment variables are properly set
### **Build Errors**
- Delete `node_modules` and run `npm install`
- Check Node.js version (requires 16+)
- Ensure all dependencies are installed
- Delete `node_modules` and `dist/` directories, then run `npm install && npm run build`
- Check Node.js version (requires 16+): `node --version`
- Run `npm run type-check` to identify TypeScript issues
- Verify `dist/` directory is created after `npm run build:server`
- Verify `dist/` directory contains both `index.js`, `server.js`, and `frontend/` after build
### **NPM Package Issues**
- **Status**: NPM package is under development
- **Recommendation**: Use local or Docker installation methods for production use
## 📋 Project Structure
@@ -441,11 +644,12 @@ mcp_excalidraw/
## 🔮 Development Roadmap
-**TypeScript Migration**: Complete type safety for enhanced development experience
- **NPM Package**: Resolving MCP tool registration issues
- **Docker Deployment**: Improving canvas synchronization
- **Enhanced Features**: Additional MCP tools and capabilities
- **Performance Optimization**: Real-time sync improvements
- **Advanced TypeScript Features**: Stricter type checking and advanced type utilities
- **Docker Deployment**: Both Canvas and MCP server fully working in Docker
- 🔧 **NPM Package**: Resolving MCP tool registration issues
- 🎯 **Enhanced Features**: Additional MCP tools and capabilities
- 🎯 **Performance Optimization**: Real-time sync improvements
- 🎯 **Advanced TypeScript Features**: Stricter type checking and advanced type utilities
- 🎯 **Container Registry**: Publishing to GitHub Container Registry (GHCR)
## 🤝 Contributing
+62
View File
@@ -0,0 +1,62 @@
version: '3.8'
# Docker Compose for MCP Excalidraw
#
# Usage scenarios:
# 1. Canvas only: docker-compose up canvas
# 2. MCP only: docker-compose up mcp (requires canvas running elsewhere)
# 3. Both: docker-compose --profile full up
#
# Most common: Run canvas locally, MCP via Claude Desktop config
services:
# Canvas server (optional) - Visual UI and REST API
canvas:
build:
context: .
dockerfile: Dockerfile.canvas
image: mcp-excalidraw-canvas:latest
container_name: mcp-excalidraw-canvas
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- PORT=3000
- HOST=0.0.0.0
- DEBUG=false
restart: unless-stopped
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- mcp-network
# MCP server - Core product (typically run via Claude Desktop, not docker-compose)
# This is here for testing or special deployment scenarios
mcp:
build:
context: .
dockerfile: Dockerfile
image: mcp-excalidraw:latest
container_name: mcp-excalidraw-mcp
stdin_open: true
tty: true
environment:
- NODE_ENV=production
- EXPRESS_SERVER_URL=http://canvas:3000
- ENABLE_CANVAS_SYNC=true
- DEBUG=false
depends_on:
canvas:
condition: service_healthy
networks:
- mcp-network
profiles:
- full
networks:
mcp-network:
driver: bridge
+6903
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -75,7 +75,7 @@
"url": "https://github.com/yctimlin/mcp_excalidraw/issues"
},
"engines": {
"node": ">=16.0.0"
"node": ">=18.0.0"
},
"publishConfig": {
"access": "public",
+17 -13
View File
@@ -38,6 +38,7 @@ interface ApiResponse {
element?: ServerElement;
elements?: ServerElement[];
message?: string;
error?: string;
count?: number;
}
@@ -97,12 +98,15 @@ async function syncToCanvas(operation: string, data: any): Promise<SyncResponse
logger.debug(`Syncing to canvas: ${operation}`, { url, data });
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`Canvas sync failed: ${response.status} ${response.statusText}`);
}
// Parse JSON response regardless of HTTP status
const result = await response.json() as ApiResponse;
if (!response.ok) {
logger.warn(`Canvas sync returned error status: ${response.status}`, result);
throw new Error(result.error || `Canvas sync failed: ${response.status} ${response.statusText}`);
}
logger.debug(`Canvas sync successful: ${operation}`, result);
return result as SyncResponse;
@@ -564,21 +568,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest)
case 'delete_element': {
const params = ElementIdSchema.parse(args);
const { id } = params;
// Delete element directly on HTTP server (no local storage)
const canvasResult = await deleteElementOnCanvas(id);
if (!canvasResult) {
if (!canvasResult || !(canvasResult as ApiResponse).success) {
throw new Error('Failed to delete element: HTTP server unavailable or element not found');
}
const result = { id, deleted: true, syncedToCanvas: true };
logger.info('Element deleted via MCP and synced to canvas', result);
return {
content: [{
type: 'text',
text: `Element deleted successfully!\n\n${JSON.stringify(result, null, 2)}\n\n✅ Synced to canvas`
content: [{
type: 'text',
text: `Element deleted successfully!\n\n${JSON.stringify(result, null, 2)}\n\n✅ Synced to canvas`
}]
};
}
+3 -6
View File
@@ -2,14 +2,11 @@ import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
root: 'frontend',
plugins: [react()],
build: {
outDir: 'dist',
rollupOptions: {
input: {
main: './frontend/index.html',
},
},
outDir: '../dist/frontend',
emptyOutDir: true,
},
server: {
port: 5173,