#
tokens: 6559/50000 12/12 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .gitignore
├── docker-compose.yml
├── Dockerfile
├── index.ts
├── package-lock.json
├── package.json
├── README.md
├── smithery.yaml
├── src
│   ├── common
│   │   └── errors.ts
│   └── operations
│       ├── drawings.ts
│       └── export.ts
├── test.js
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Dependency directories
node_modules/

# Build output
dist/

# Storage directory
storage/

# Environment variables
.env

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

# OS specific
.DS_Store
Thumbs.db

# Inspiration directory
inspiration/ 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# Excalidraw MCP Server

This is a Model Context Protocol (MCP) server for Excalidraw, providing API functionality for operating on Excalidraw drawings.

## Features

- Create, read, update, and delete Excalidraw drawings
- Export drawings to SVG, PNG, and JSON formats
- Simple file-based storage system

## Installation

```bash
# Clone the repository
git clone https://github.com/yourusername/excalidraw-mcp.git
cd excalidraw-mcp

# Install dependencies
npm install

# Build the project
npm run build
```

## Usage

### Starting the Server

```bash
npm start
```

### API Endpoints

The server provides the following tools:

#### Drawing Management

- `create_drawing`: Create a new Excalidraw drawing
- `get_drawing`: Get an Excalidraw drawing by ID
- `update_drawing`: Update an Excalidraw drawing by ID
- `delete_drawing`: Delete an Excalidraw drawing by ID
- `list_drawings`: List all Excalidraw drawings

#### Export Operations

- `export_to_svg`: Export an Excalidraw drawing to SVG
- `export_to_png`: Export an Excalidraw drawing to PNG
- `export_to_json`: Export an Excalidraw drawing to JSON

## Development

### Project Structure

```
excalidraw-mcp/
├── src/
│   ├── common/
│   │   └── errors.ts
│   └── operations/
│       ├── drawings.ts
│       └── export.ts
├── index.ts
├── package.json
├── tsconfig.json
└── README.md
```

### Building

```bash
npm run build
```

### Running in Development Mode

```bash
npm run dev
```

## License

MIT 
```

--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------

```yaml
version: '3'

services:
  excalidraw-mcp:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./storage:/app/storage
    restart: unless-stopped
    # If we add an HTTP server in the future, we can uncomment this
    # ports:
    #   - "3000:3000" 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "esModuleInterop": true,
    "strict": true,
    "outDir": "dist",
    "sourceMap": true,
    "declaration": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*", "index.ts"],
  "exclude": ["node_modules", "dist"]
}

```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
FROM node:18-alpine

WORKDIR /app

# Copy package.json and package-lock.json
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of the application
COPY . .

# Build the application
RUN npm run build

# Create storage directory
RUN mkdir -p storage

# Set permissions for storage directory
RUN chmod -R 777 storage

# Expose port (if needed for HTTP server in the future)
# EXPOSE 3000

# Run the server
CMD ["npm", "start"] 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "excalidraw-mcp",
  "version": "0.1.0",
  "description": "MCP server for Excalidraw",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "tsc-watch --onSuccess \"node dist/index.js\"",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "excalidraw",
    "mcp",
    "drawing"
  ],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "@excalidraw/excalidraw": "^0.17.0",
    "@modelcontextprotocol/sdk": "^1.6.1",
    "zod": "^3.22.4",
    "zod-to-json-schema": "^3.22.3"
  },
  "devDependencies": {
    "@types/node": "^20.10.5",
    "typescript": "^5.3.3",
    "tsc-watch": "^6.0.4"
  }
}

```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
name: excalidraw-mcp
version: 0.1.0
description: MCP server for Excalidraw
author: Your Name
license: MIT
repository: https://github.com/yourusername/excalidraw-mcp
homepage: https://github.com/yourusername/excalidraw-mcp

entrypoint:
  type: stdio
  command: node dist/index.js

capabilities:
  tools:
    - name: create_drawing
      description: Create a new Excalidraw drawing
    - name: get_drawing
      description: Get an Excalidraw drawing by ID
    - name: update_drawing
      description: Update an Excalidraw drawing by ID
    - name: delete_drawing
      description: Delete an Excalidraw drawing by ID
    - name: list_drawings
      description: List all Excalidraw drawings
    - name: export_to_svg
      description: Export an Excalidraw drawing to SVG
    - name: export_to_png
      description: Export an Excalidraw drawing to PNG
    - name: export_to_json
      description: Export an Excalidraw drawing to JSON 
```

--------------------------------------------------------------------------------
/src/common/errors.ts:
--------------------------------------------------------------------------------

```typescript
export class ExcalidrawError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'ExcalidrawError';
  }
}

export class ExcalidrawValidationError extends ExcalidrawError {
  response?: any;

  constructor(message: string, response?: any) {
    super(message);
    this.name = 'ExcalidrawValidationError';
    this.response = response;
  }
}

export class ExcalidrawResourceNotFoundError extends ExcalidrawError {
  constructor(message: string) {
    super(message);
    this.name = 'ExcalidrawResourceNotFoundError';
  }
}

export class ExcalidrawAuthenticationError extends ExcalidrawError {
  constructor(message: string) {
    super(message);
    this.name = 'ExcalidrawAuthenticationError';
  }
}

export class ExcalidrawPermissionError extends ExcalidrawError {
  constructor(message: string) {
    super(message);
    this.name = 'ExcalidrawPermissionError';
  }
}

export class ExcalidrawRateLimitError extends ExcalidrawError {
  resetAt: Date;

  constructor(message: string, resetAt: Date) {
    super(message);
    this.name = 'ExcalidrawRateLimitError';
    this.resetAt = resetAt;
  }
}

export class ExcalidrawConflictError extends ExcalidrawError {
  constructor(message: string) {
    super(message);
    this.name = 'ExcalidrawConflictError';
  }
}

export function isExcalidrawError(error: any): error is ExcalidrawError {
  return error instanceof ExcalidrawError;
}

```

--------------------------------------------------------------------------------
/src/operations/export.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { ExcalidrawResourceNotFoundError } from '../common/errors.js';
import { getDrawing } from './drawings.js';

// Schema for exporting a drawing to SVG
export const ExportToSvgSchema = z.object({
  id: z.string().min(1),
});

// Schema for exporting a drawing to PNG
export const ExportToPngSchema = z.object({
  id: z.string().min(1),
  quality: z.number().min(0).max(1).optional().default(0.92),
  scale: z.number().min(0.1).max(5).optional().default(1),
  exportWithDarkMode: z.boolean().optional().default(false),
  exportBackground: z.boolean().optional().default(true),
});

// Schema for exporting a drawing to JSON
export const ExportToJsonSchema = z.object({
  id: z.string().min(1),
});

// Export a drawing to SVG
export async function exportToSvg(id: string): Promise<string> {
  try {
    // Get the drawing
    const drawing = await getDrawing(id);
    
    // Return the SVG content
    // Note: In a real implementation, we would use the Excalidraw API to convert the drawing to SVG
    // For now, we'll just return a placeholder
    return `<svg>
      <text x="10" y="20">Drawing: ${drawing.name}</text>
      <text x="10" y="40">This is a placeholder for the SVG export.</text>
    </svg>`;
  } catch (error) {
    if (error instanceof ExcalidrawResourceNotFoundError) {
      throw error;
    }
    throw new Error(`Failed to export drawing to SVG: ${(error as Error).message}`);
  }
}

// Export a drawing to PNG
export async function exportToPng(
  id: string,
  quality: number = 0.92,
  scale: number = 1,
  exportWithDarkMode: boolean = false,
  exportBackground: boolean = true
): Promise<string> {
  try {
    // Get the drawing
    const drawing = await getDrawing(id);
    
    // Return the PNG content as a base64 string
    // Note: In a real implementation, we would use the Excalidraw API to convert the drawing to PNG
    // For now, we'll just return a placeholder
    return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==';
  } catch (error) {
    if (error instanceof ExcalidrawResourceNotFoundError) {
      throw error;
    }
    throw new Error(`Failed to export drawing to PNG: ${(error as Error).message}`);
  }
}

// Export a drawing to JSON
export async function exportToJson(id: string): Promise<string> {
  try {
    // Get the drawing
    const drawing = await getDrawing(id);
    
    // Return the JSON content
    return drawing.content;
  } catch (error) {
    if (error instanceof ExcalidrawResourceNotFoundError) {
      throw error;
    }
    throw new Error(`Failed to export drawing to JSON: ${(error as Error).message}`);
  }
}

```

--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------

```javascript
#!/usr/bin/env node
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import path from 'path';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

async function main() {
  // Create a transport connected to the server process
  const transport = new StdioClientTransport({
    command: 'node',
    args: ['dist/index.js'],
  });

  // Create a client
  const client = new Client(
    {
      name: 'excalidraw-mcp-test',
      version: '0.1.0',
    },
    {
      capabilities: {
        tools: {},
      },
    }
  );

  try {
    // Connect to the server
    await client.connect(transport);
    console.log('Connected to server');

    // List available tools
    const tools = await client.listTools();
    console.log('Available tools:', tools);

    // Create a drawing
    const createResult = await client.callTool({
      name: 'create_drawing',
      arguments: {
        name: 'Test Drawing',
        content: JSON.stringify({
          type: 'excalidraw',
          version: 2,
          source: 'excalidraw-mcp-test',
          elements: [
            {
              id: 'rectangle1',
              type: 'rectangle',
              x: 100,
              y: 100,
              width: 200,
              height: 100,
              angle: 0,
              strokeColor: '#000000',
              backgroundColor: '#ffffff',
              fillStyle: 'solid',
              strokeWidth: 1,
              strokeStyle: 'solid',
              roughness: 1,
              opacity: 100,
              groupIds: [],
              strokeSharpness: 'sharp',
              seed: 123456,
              version: 1,
              versionNonce: 1,
              isDeleted: false,
              boundElementIds: null,
            },
          ],
          appState: {
            viewBackgroundColor: '#ffffff',
          },
        }),
      },
    });
    console.log('Created drawing:', createResult);

    // Get the drawing ID from the result
    const drawingId = JSON.parse(createResult.content[0].text).id;

    // Get the drawing
    const getResult = await client.callTool({
      name: 'get_drawing',
      arguments: {
        id: drawingId,
      },
    });
    console.log('Retrieved drawing:', getResult);

    // Export the drawing to SVG
    const svgResult = await client.callTool({
      name: 'export_to_svg',
      arguments: {
        id: drawingId,
      },
    });
    console.log('SVG export:', svgResult);

    // Export the drawing to PNG
    const pngResult = await client.callTool({
      name: 'export_to_png',
      arguments: {
        id: drawingId,
        quality: 0.9,
        scale: 2,
        exportWithDarkMode: true,
        exportBackground: true,
      },
    });
    console.log('PNG export:', pngResult);

    // List all drawings
    const listResult = await client.callTool({
      name: 'list_drawings',
      arguments: {
        page: 1,
        perPage: 10,
      },
    });
    console.log('List of drawings:', listResult);

    // Update the drawing
    const updateResult = await client.callTool({
      name: 'update_drawing',
      arguments: {
        id: drawingId,
        content: JSON.stringify({
          type: 'excalidraw',
          version: 2,
          source: 'excalidraw-mcp-test',
          elements: [
            {
              id: 'rectangle1',
              type: 'rectangle',
              x: 100,
              y: 100,
              width: 200,
              height: 100,
              angle: 0,
              strokeColor: '#ff0000', // Changed to red
              backgroundColor: '#ffffff',
              fillStyle: 'solid',
              strokeWidth: 2, // Increased width
              strokeStyle: 'solid',
              roughness: 1,
              opacity: 100,
              groupIds: [],
              strokeSharpness: 'sharp',
              seed: 123456,
              version: 2,
              versionNonce: 2,
              isDeleted: false,
              boundElementIds: null,
            },
          ],
          appState: {
            viewBackgroundColor: '#ffffff',
          },
        }),
      },
    });
    console.log('Updated drawing:', updateResult);

    // Delete the drawing
    const deleteResult = await client.callTool({
      name: 'delete_drawing',
      arguments: {
        id: drawingId,
      },
    });
    console.log('Deleted drawing:', deleteResult);

    console.log('All tests passed!');
  } catch (error) {
    console.error('Error:', error);
  } finally {
    // Close the client connection
    await client.close();
  }
}

main().catch((error) => {
  console.error('Test error:', error);
  process.exit(1);
}); 
```

--------------------------------------------------------------------------------
/src/operations/drawings.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import fs from 'fs/promises';
import path from 'path';
import { ExcalidrawResourceNotFoundError, ExcalidrawValidationError } from '../common/errors.js';

// Define the storage directory for drawings
const STORAGE_DIR = path.join(process.cwd(), 'storage');

// Ensure storage directory exists
async function ensureStorageDir() {
  try {
    await fs.mkdir(STORAGE_DIR, { recursive: true });
  } catch (error) {
    console.error('Failed to create storage directory:', error);
    throw error;
  }
}

// Schema for creating a drawing
export const CreateDrawingSchema = z.object({
  name: z.string().min(1),
  content: z.string().min(1),
});

// Schema for getting a drawing
export const GetDrawingSchema = z.object({
  id: z.string().min(1),
});

// Schema for updating a drawing
export const UpdateDrawingSchema = z.object({
  id: z.string().min(1),
  content: z.string().min(1),
});

// Schema for deleting a drawing
export const DeleteDrawingSchema = z.object({
  id: z.string().min(1),
});

// Schema for listing drawings
export const ListDrawingsSchema = z.object({
  page: z.number().int().min(1).optional().default(1),
  perPage: z.number().int().min(1).max(100).optional().default(10),
});

// Create a new drawing
export async function createDrawing(name: string, content: string): Promise<{ id: string, name: string }> {
  await ensureStorageDir();
  
  // Generate a unique ID for the drawing
  const id = `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
  
  // Create the drawing file
  const filePath = path.join(STORAGE_DIR, `${id}.json`);
  
  // Save the drawing content
  await fs.writeFile(filePath, content, 'utf-8');
  
  // Create a metadata file for the drawing
  const metadataPath = path.join(STORAGE_DIR, `${id}.meta.json`);
  const metadata = {
    id,
    name,
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString(),
  };
  
  await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2), 'utf-8');
  
  return { id, name };
}

// Get a drawing by ID
export async function getDrawing(id: string): Promise<{ id: string, name: string, content: string, metadata: any }> {
  await ensureStorageDir();
  
  // Get the drawing file path
  const filePath = path.join(STORAGE_DIR, `${id}.json`);
  const metadataPath = path.join(STORAGE_DIR, `${id}.meta.json`);
  
  try {
    // Read the drawing content
    const content = await fs.readFile(filePath, 'utf-8');
    
    // Read the metadata
    const metadataStr = await fs.readFile(metadataPath, 'utf-8');
    const metadata = JSON.parse(metadataStr);
    
    return {
      id,
      name: metadata.name,
      content,
      metadata,
    };
  } catch (error) {
    throw new ExcalidrawResourceNotFoundError(`Drawing with ID ${id} not found`);
  }
}

// Update a drawing by ID
export async function updateDrawing(id: string, content: string): Promise<{ id: string, name: string }> {
  await ensureStorageDir();
  
  // Get the drawing file path
  const filePath = path.join(STORAGE_DIR, `${id}.json`);
  const metadataPath = path.join(STORAGE_DIR, `${id}.meta.json`);
  
  try {
    // Check if the drawing exists
    await fs.access(filePath);
    
    // Read the metadata
    const metadataStr = await fs.readFile(metadataPath, 'utf-8');
    const metadata = JSON.parse(metadataStr);
    
    // Update the drawing content
    await fs.writeFile(filePath, content, 'utf-8');
    
    // Update the metadata
    metadata.updatedAt = new Date().toISOString();
    await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2), 'utf-8');
    
    return { id, name: metadata.name };
  } catch (error) {
    throw new ExcalidrawResourceNotFoundError(`Drawing with ID ${id} not found`);
  }
}

// Delete a drawing by ID
export async function deleteDrawing(id: string): Promise<void> {
  await ensureStorageDir();
  
  // Get the drawing file path
  const filePath = path.join(STORAGE_DIR, `${id}.json`);
  const metadataPath = path.join(STORAGE_DIR, `${id}.meta.json`);
  
  try {
    // Check if the drawing exists
    await fs.access(filePath);
    
    // Delete the drawing file
    await fs.unlink(filePath);
    
    // Delete the metadata file
    await fs.unlink(metadataPath);
  } catch (error) {
    throw new ExcalidrawResourceNotFoundError(`Drawing with ID ${id} not found`);
  }
}

// List all drawings
export async function listDrawings(page: number = 1, perPage: number = 10): Promise<{ drawings: any[], total: number }> {
  await ensureStorageDir();
  
  try {
    // Get all files in the storage directory
    const files = await fs.readdir(STORAGE_DIR);
    
    // Filter metadata files
    const metadataFiles = files.filter(file => file.endsWith('.meta.json'));
    
    // Calculate pagination
    const start = (page - 1) * perPage;
    const end = start + perPage;
    const paginatedFiles = metadataFiles.slice(start, end);
    
    // Read metadata for each drawing
    const drawings = await Promise.all(
      paginatedFiles.map(async (file) => {
        const metadataPath = path.join(STORAGE_DIR, file);
        const metadataStr = await fs.readFile(metadataPath, 'utf-8');
        return JSON.parse(metadataStr);
      })
    );
    
    return {
      drawings,
      total: metadataFiles.length,
    };
  } catch (error) {
    console.error('Failed to list drawings:', error);
    return {
      drawings: [],
      total: 0,
    };
  }
}

```

--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';

import * as drawings from './src/operations/drawings.js';
import * as exportOps from './src/operations/export.js';
import {
  ExcalidrawError,
  ExcalidrawValidationError,
  ExcalidrawResourceNotFoundError,
  ExcalidrawAuthenticationError,
  ExcalidrawPermissionError,
  ExcalidrawRateLimitError,
  ExcalidrawConflictError,
  isExcalidrawError,
} from './src/common/errors.js';

const server = new Server(
  {
    name: "excalidraw-mcp-server",
    version: "0.1.0",
  },
  {
    capabilities: {
      tools: {},
    },
  }
);

function formatExcalidrawError(error: ExcalidrawError): string {
  let message = `Excalidraw API Error: ${error.message}`;
  
  if (error instanceof ExcalidrawValidationError) {
    message = `Validation Error: ${error.message}`;
    if (error.response) {
      message += `\nDetails: ${JSON.stringify(error.response)}`;
    }
  } else if (error instanceof ExcalidrawResourceNotFoundError) {
    message = `Not Found: ${error.message}`;
  } else if (error instanceof ExcalidrawAuthenticationError) {
    message = `Authentication Failed: ${error.message}`;
  } else if (error instanceof ExcalidrawPermissionError) {
    message = `Permission Denied: ${error.message}`;
  } else if (error instanceof ExcalidrawRateLimitError) {
    message = `Rate Limit Exceeded: ${error.message}\nResets at: ${error.resetAt.toISOString()}`;
  } else if (error instanceof ExcalidrawConflictError) {
    message = `Conflict: ${error.message}`;
  }

  return message;
}

server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: "create_drawing",
        description: "Create a new Excalidraw drawing",
        inputSchema: zodToJsonSchema(drawings.CreateDrawingSchema),
      },
      {
        name: "get_drawing",
        description: "Get an Excalidraw drawing by ID",
        inputSchema: zodToJsonSchema(drawings.GetDrawingSchema),
      },
      {
        name: "update_drawing",
        description: "Update an Excalidraw drawing by ID",
        inputSchema: zodToJsonSchema(drawings.UpdateDrawingSchema),
      },
      {
        name: "delete_drawing",
        description: "Delete an Excalidraw drawing by ID",
        inputSchema: zodToJsonSchema(drawings.DeleteDrawingSchema),
      },
      {
        name: "list_drawings",
        description: "List all Excalidraw drawings",
        inputSchema: zodToJsonSchema(drawings.ListDrawingsSchema),
      },
      {
        name: "export_to_svg",
        description: "Export an Excalidraw drawing to SVG",
        inputSchema: zodToJsonSchema(exportOps.ExportToSvgSchema),
      },
      {
        name: "export_to_png",
        description: "Export an Excalidraw drawing to PNG",
        inputSchema: zodToJsonSchema(exportOps.ExportToPngSchema),
      },
      {
        name: "export_to_json",
        description: "Export an Excalidraw drawing to JSON",
        inputSchema: zodToJsonSchema(exportOps.ExportToJsonSchema),
      },
    ],
  };
});

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  try {
    if (!request.params.arguments) {
      throw new Error("Arguments are required");
    }

    switch (request.params.name) {
      case "create_drawing": {
        const args = drawings.CreateDrawingSchema.parse(request.params.arguments);
        const result = await drawings.createDrawing(args.name, args.content);
        return {
          content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
        };
      }

      case "get_drawing": {
        const args = drawings.GetDrawingSchema.parse(request.params.arguments);
        const result = await drawings.getDrawing(args.id);
        return {
          content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
        };
      }

      case "update_drawing": {
        const args = drawings.UpdateDrawingSchema.parse(request.params.arguments);
        const result = await drawings.updateDrawing(args.id, args.content);
        return {
          content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
        };
      }

      case "delete_drawing": {
        const args = drawings.DeleteDrawingSchema.parse(request.params.arguments);
        await drawings.deleteDrawing(args.id);
        return {
          content: [{ type: "text", text: JSON.stringify({ success: true }, null, 2) }],
        };
      }

      case "list_drawings": {
        const args = drawings.ListDrawingsSchema.parse(request.params.arguments);
        const result = await drawings.listDrawings(args.page, args.perPage);
        return {
          content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
        };
      }

      case "export_to_svg": {
        const args = exportOps.ExportToSvgSchema.parse(request.params.arguments);
        const result = await exportOps.exportToSvg(args.id);
        return {
          content: [{ type: "text", text: result }],
        };
      }

      case "export_to_png": {
        const args = exportOps.ExportToPngSchema.parse(request.params.arguments);
        const result = await exportOps.exportToPng(
          args.id,
          args.quality,
          args.scale,
          args.exportWithDarkMode,
          args.exportBackground
        );
        return {
          content: [{ type: "text", text: result }],
        };
      }

      case "export_to_json": {
        const args = exportOps.ExportToJsonSchema.parse(request.params.arguments);
        const result = await exportOps.exportToJson(args.id);
        return {
          content: [{ type: "text", text: result }],
        };
      }

      default:
        throw new Error(`Unknown tool: ${request.params.name}`);
    }
  } catch (error) {
    console.error("Error handling request:", error);
    
    if (isExcalidrawError(error)) {
      return {
        error: formatExcalidrawError(error),
      };
    }
    
    return {
      error: `Error: ${(error as Error).message}`,
    };
  }
});

async function runServer() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

runServer().catch((error) => {
  console.error("Server error:", error);
  process.exit(1);
});

```