save_scene
Save Excalidraw diagrams to .excalidraw files for persistent storage and future editing.
Instructions
Saves the current Excalidraw elements and scene state to a .excalidraw file.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| filename | No | Optional filename ending with .excalidraw (default: mcp_scene.excalidraw) |
Implementation Reference
- src/index.js:571-638 (handler)Handler for the save_scene tool: validates params with SaveSceneSchema, builds Excalidraw-compatible sceneData from elements and sceneState, converts selectedElements Set to object, fixes points for lines/arrows, writes JSON file via fs.promises.writeFile, returns success/error message.case 'save_scene': { const params = SaveSceneSchema.parse(args || {}); const filename = params.filename || 'mcp_scene.excalidraw'; if (!filename.endsWith('.excalidraw')) { throw new Error("Filename must end with .excalidraw"); } // Convert selectedElements Set to the expected object format const selectedElementIds = {}; sceneState.selectedElements.forEach(id => { selectedElementIds[id] = true; }); // Ensure all elements have points in the correct format const elementsToSave = Array.from(elements.values()).map(el => { if ((el.type === 'arrow' || el.type === 'line') && (!el.points || el.points.length < 2)) { logger.warn(`Element ${el.id} of type ${el.type} has invalid/missing points. Adding default points.`); el.points = [[0, 0], [el.width || 10, el.height || 0]]; } return el; }); const sceneData = { type: "excalidraw", version: 2, source: "mcp-server", elements: elementsToSave, appState: { viewBackgroundColor: sceneState.viewBackgroundColor ?? "#ffffff", scrollX: sceneState.viewport?.x ?? 0, scrollY: sceneState.viewport?.y ?? 0, zoom: { value: sceneState.viewport?.zoom ?? 1 }, // Updated zoom format selectedElementIds: selectedElementIds, // Updated selectedElementIds format gridSize: null, zenModeEnabled: false, editingGroupId: null, theme: sceneState.theme ?? 'light', currentItemStrokeColor: "#000000", currentItemBackgroundColor: "transparent", currentItemFillStyle: "hachure", currentItemStrokeWidth: 1, currentItemStrokeStyle: "solid", currentItemRoughness: 1, currentItemOpacity: 100, currentItemFontFamily: 1, currentItemFontSize: 20, currentItemTextAlign: "center", currentItemStartArrowhead: null, currentItemEndArrowhead: "arrow", }, files: {} }; try { await fs.writeFile(filename, JSON.stringify(sceneData, null, 2), 'utf8'); logger.info(`Scene saved successfully to ${filename}`); return { content: [{ type: 'text', text: `Scene saved successfully to ${filename}` }] }; } catch (error) { logger.error(`Error saving scene: ${error.message}`, { error }); return { content: [{ type: 'text', text: `Error saving scene: ${error.message}` }], isError: true }; } }
- src/index.js:79-82 (schema)Zod schema defining the input for save_scene: optional filename string.// ADDED: Schema for the new save_scene tool const SaveSceneSchema = z.object({ filename: z.string().optional().describe("Optional filename ending with .excalidraw (default: mcp_scene.excalidraw)") });
- src/index.js:268-280 (registration)MCP capabilities registration for save_scene tool, specifying description and inputSchema (note: inputSchema duplicated from zod schema).// ADDED: Definition for the save_scene tool save_scene: { description: 'Saves the current Excalidraw elements and scene state to a .excalidraw file.', inputSchema: { type: 'object', properties: { filename: { type: 'string', description: 'Optional filename ending with .excalidraw (default: mcp_scene.excalidraw)' } } } }
- src/index.js:841-853 (registration)Registration of save_scene in the ListToolsRequestSchema handler response.{ name: 'save_scene', description: 'Saves the current Excalidraw elements and scene state to a .excalidraw file.', inputSchema: { type: 'object', properties: { filename: { type: 'string', description: 'Optional filename ending with .excalidraw (default: mcp_scene.excalidraw)' } } } }