Skip to main content
Glama

Excalidraw MCP Server

by i-tozer
drawings.ts5.42 kB
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, }; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/i-tozer/excalidraw-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server