@excalimate/mcp-server 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # Excalimate MCP Server
2
+
3
+ Excalimate MCP server for creating Excalidraw designs and animating them with keyframes. Designed for AI agent integration (Claude Desktop, Copilot CLI, etc.).
4
+
5
+ ## Features
6
+
7
+ - **22 tools** for scene creation, animation, camera control, export, and checkpointing
8
+ - **Dual transport**: stdio (Claude Desktop) + Streamable HTTP (cloud deployment)
9
+ - **Sequence reveal**: Staggered element reveal animations in one tool call
10
+ - **Camera animation**: Pan/zoom keyframes for cinematic effects
11
+ - **Checkpoint persistence**: Save/load complete scene + animation state
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ # Install
17
+ cd mcp-server
18
+ npm install
19
+
20
+ # Build
21
+ npm run build
22
+
23
+ # Run (stdio for Claude Desktop)
24
+ node dist/index.js --stdio
25
+
26
+ # Run (HTTP for cloud)
27
+ node dist/index.js
28
+ # → http://localhost:3001/mcp
29
+ ```
30
+
31
+ ## Claude Desktop Configuration
32
+
33
+ Add to `claude_desktop_config.json`:
34
+
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "excalimate": {
39
+ "command": "node",
40
+ "args": ["/path/to/mcp-server/dist/index.js", "--stdio"]
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ ## Tools
47
+
48
+ ### Scene Tools
49
+ | Tool | Description |
50
+ |------|-------------|
51
+ | `read_me` | Element format reference + animation docs |
52
+ | `get_examples` | Few-shot examples for elements and animations |
53
+ | `create_scene` | Create/replace scene from elements JSON |
54
+ | `add_elements` | Add elements to existing scene |
55
+ | `remove_elements` | Remove elements by ID |
56
+ | `update_elements` | Modify element properties |
57
+ | `get_scene` | Return current scene as JSON |
58
+ | `clear_scene` | Clear all elements and animations |
59
+ | `delete_items` | Remove specific elements + their animation tracks |
60
+
61
+ ### Animation Tools
62
+ | Tool | Description |
63
+ |------|-------------|
64
+ | `add_keyframe` | Add keyframe (auto-creates track) |
65
+ | `add_keyframes_batch` | Bulk add keyframes |
66
+ | `remove_keyframe` | Remove a keyframe |
67
+ | `create_sequence` | Staggered reveal animation |
68
+ | `set_clip_range` | Set export start/end times |
69
+ | `get_timeline` | Return timeline as JSON |
70
+ | `clear_animation` | Clear all tracks |
71
+
72
+ ### Camera Tools
73
+ | Tool | Description |
74
+ |------|-------------|
75
+ | `set_camera_frame` | Set camera position/size/aspect + creates t=0 keyframes |
76
+ | `add_camera_keyframe` | Animate camera pan/zoom |
77
+ | `add_camera_keyframes_batch` | Bulk camera keyframes |
78
+
79
+ ### Inspection Tools
80
+ | Tool | Description |
81
+ |------|-------------|
82
+ | `are_items_in_line` | Check horizontal/vertical alignment |
83
+ | `is_camera_centered` | Check if camera is centered on content |
84
+ | `items_visible_in_camera` | Report visibility of items at a given time |
85
+ | `animations_of_item` | Describe all animations of an element |
86
+
87
+ ### Checkpoint Tools
88
+ | Tool | Description |
89
+ |------|-------------|
90
+ | `save_checkpoint` | Save scene + animation state |
91
+ | `load_checkpoint` | Restore from checkpoint |
92
+ | `list_checkpoints` | List saved checkpoints |
93
+
94
+ ## Example Workflow
95
+
96
+ ```
97
+ 1. read_me → Get element format reference
98
+ 2. create_scene {elements} → Create a diagram
99
+ 3. create_sequence { → Animate elements revealing one by one
100
+ elementIds: ["box1", "arrow1", "box2"],
101
+ property: "opacity",
102
+ startTime: 0,
103
+ delay: 500,
104
+ duration: 800
105
+ }
106
+ 4. set_clip_range {0, 5000} → Set 5-second export window
107
+ 5. save_checkpoint {id: "demo"} → Save for web app preview
108
+ ```
package/SKILL.md ADDED
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: excalimate
3
+ description: >
4
+ Create hand-drawn Excalidraw diagrams and animate them with keyframe animations
5
+ using the Excalimate MCP server. Use this skill when asked to create
6
+ animated diagrams, architecture visualizations, flow animations, reveal sequences,
7
+ or any animated Excalidraw content. Supports opacity fades, position slides,
8
+ scale pop-ins, arrow draw-on effects, camera pan/zoom, and staggered reveals.
9
+ ---
10
+
11
+ # Excalimate Skill
12
+
13
+ ## Workflow
14
+
15
+ 1. Call `read_me` for element format reference
16
+ 2. Call `get_examples` for animation patterns
17
+ 3. Call `clear_scene` if starting fresh, then `create_scene` with elements JSON
18
+ 4. Call `add_keyframes_batch` to animate (preferred over individual `add_keyframe`)
19
+ 5. Call `set_clip_range` to define export bounds
20
+ 6. Call `save_checkpoint` to persist for the user
21
+
22
+ ## Quick Element Reference
23
+
24
+ **Rectangle**: `{"id":"r1","type":"rectangle","x":100,"y":100,"width":200,"height":100,"strokeColor":"#1e1e1e","backgroundColor":"#a5d8ff","fillStyle":"solid"}`
25
+
26
+ **Text**: `{"id":"t1","type":"text","x":100,"y":100,"width":200,"height":30,"text":"Hello","fontSize":20,"fontFamily":5,"textAlign":"center"}`
27
+
28
+ **Arrow**: `{"id":"a1","type":"arrow","x":100,"y":200,"width":300,"height":0,"points":[[0,0],[300,0]],"endArrowhead":"arrow"}`
29
+
30
+ **Bound arrow** (connects shapes): Add `"startBinding":{"elementId":"shapeA","focus":0,"gap":1},"endBinding":{"elementId":"shapeB","focus":0,"gap":1}` to the arrow, and `"boundElements":[{"id":"a1","type":"arrow"}]` to both shapes.
31
+
32
+ **Labeled shape**: Shape gets `"boundElements":[{"id":"label","type":"text"}]`, text gets `"containerId":"shape-id"`.
33
+
34
+ ## Animation Properties
35
+
36
+ | Property | Range | Use |
37
+ |----------|-------|-----|
38
+ | `opacity` | 0–1 | Fade in/out |
39
+ | `translateX/Y` | pixels | Slide movement |
40
+ | `scaleX/Y` | 0.1+ | Pop in/out |
41
+ | `rotation` | degrees | Spin |
42
+ | `drawProgress` | 0–1 | Arrow/line stroke draw-on |
43
+
44
+ ## Common Patterns
45
+
46
+ ### Reveal: A → Arrow → B
47
+ ```
48
+ add_keyframes_batch with:
49
+ A opacity: 0→1 (0–600ms, easeOut)
50
+ arrow opacity: 0→1 (600–700ms) + drawProgress: 0→1 (600–1800ms, easeInOut)
51
+ B opacity: 0→1 (1800–2400ms, easeOut)
52
+ ```
53
+
54
+ ### Staggered reveal (shortcut)
55
+ ```
56
+ create_sequence({ elementIds: ["el1","el2","el3"], property: "opacity", startTime: 0, delay: 400, duration: 600 })
57
+ ```
58
+
59
+ ### Slide in
60
+ ```
61
+ translateX: -300→0 (easeOutCubic) + opacity: 0→1 (easeOut, shorter)
62
+ ```
63
+
64
+ ### Pop in (from center)
65
+ ```
66
+ add_keyframes_batch — add "scaleOrigin":"center" on each scaleX/scaleY keyframe
67
+ scaleX/Y: 0.3→1 (easeOutBack) + opacity: 0→1
68
+ ```
69
+
70
+ ### Scale from edge
71
+ ```
72
+ add_scale_animation({ targetId, origin: "bottom", keyframes: '[{time, scaleX, scaleY, easing}]' })
73
+ — or add "scaleOrigin":"bottom" per keyframe in add_keyframes_batch
74
+ ```
75
+ Origins: center, top-left, top-right, bottom-left, bottom-right, top, bottom, left, right
76
+
77
+ ## Key Rules
78
+
79
+ - **Use `add_keyframes_batch`** — one call for many keyframes, not individual calls.
80
+ - **NEVER set opacity on elements** — always use animation keyframes for visibility. Element opacity must stay at 100.
81
+ - **Set opacity 0 at time 0** via keyframes for elements that appear later.
82
+ - **Bound text inherits** container animation — don't animate labels separately.
83
+ - **drawProgress** only works on arrows and lines.
84
+ - **easeOut** for reveals, **easeInOutCubic** for camera, **easeOutBack** for pop-ins.
85
+ - **Call `set_clip_range`** before saving to define export bounds.
86
+ - **Use `clear_scene`** to reset everything before building a new animation.
87
+ - **Use `delete_items`** to remove elements AND their animations in one call.
88
+ - **Verify your work** with `animations_of_item`, `items_visible_in_camera`, `are_items_in_line`, and `is_camera_centered`.
89
+
90
+ ## Easing Choices
91
+
92
+ | Effect | Easing |
93
+ |--------|--------|
94
+ | Fade in | `easeOut` |
95
+ | Slide in | `easeOutCubic` |
96
+ | Pop in | `easeOutBack` |
97
+ | Camera motion | `easeInOutCubic` |
98
+ | Arrow draw | `easeInOut` |
99
+ | Instant | `step` |
100
+
101
+ ## Timing Guidelines
102
+
103
+ - Fade: 300–800ms
104
+ - Arrow draw: 800–1500ms
105
+ - Slide: 600–1000ms
106
+ - Camera pan: 1500–3000ms
107
+ - Stagger delay: 200–500ms
108
+ - Reading pause: 1000–2000ms
109
+
110
+ See [references/REFERENCE.md](references/REFERENCE.md) for complete element format, color palettes, and detailed examples.
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Checkpoint persistence — save/load scene + animation state.
3
+ */
4
+ import type { ServerState } from './types.js';
5
+ export interface CheckpointStore {
6
+ save(id: string, data: ServerState): Promise<void>;
7
+ load(id: string): Promise<ServerState | null>;
8
+ list(): Promise<string[]>;
9
+ }
10
+ export declare class FileCheckpointStore implements CheckpointStore {
11
+ private dir;
12
+ constructor();
13
+ save(id: string, data: ServerState): Promise<void>;
14
+ load(id: string): Promise<ServerState | null>;
15
+ list(): Promise<string[]>;
16
+ private prune;
17
+ }
18
+ export declare class MemoryCheckpointStore implements CheckpointStore {
19
+ private store;
20
+ save(id: string, data: ServerState): Promise<void>;
21
+ load(id: string): Promise<ServerState | null>;
22
+ list(): Promise<string[]>;
23
+ }
24
+ //# sourceMappingURL=checkpoint-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoint-store.d.ts","sourceRoot":"","sources":["../src/checkpoint-store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAW9C,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC9C,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC3B;AAED,qBAAa,mBAAoB,YAAW,eAAe;IACzD,OAAO,CAAC,GAAG,CAAS;;IAOd,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAclD,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAc7C,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YASjB,KAAK;CAgBpB;AAED,qBAAa,qBAAsB,YAAW,eAAe;IAC3D,OAAO,CAAC,KAAK,CAA6B;IAEpC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAWlD,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAO7C,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAGhC"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Checkpoint persistence — save/load scene + animation state.
3
+ */
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import os from 'node:os';
7
+ const MAX_CHECKPOINT_BYTES = 10 * 1024 * 1024; // 10 MB
8
+ const MAX_CHECKPOINTS = 100;
9
+ function validateId(id) {
10
+ if (!/^[a-zA-Z0-9_-]+$/.test(id) || id.length > 64) {
11
+ throw new Error('Invalid checkpoint ID: alphanumeric/hyphens/underscores, max 64 chars');
12
+ }
13
+ }
14
+ export class FileCheckpointStore {
15
+ dir;
16
+ constructor() {
17
+ this.dir = path.join(os.tmpdir(), 'excalimate-mcp-checkpoints');
18
+ fs.mkdirSync(this.dir, { recursive: true });
19
+ }
20
+ async save(id, data) {
21
+ validateId(id);
22
+ const serialized = JSON.stringify(data);
23
+ if (serialized.length > MAX_CHECKPOINT_BYTES) {
24
+ throw new Error(`Checkpoint exceeds ${MAX_CHECKPOINT_BYTES} byte limit`);
25
+ }
26
+ const filePath = path.join(this.dir, `${id}.json`);
27
+ if (!path.resolve(filePath).startsWith(path.resolve(this.dir) + path.sep)) {
28
+ throw new Error('Invalid checkpoint path');
29
+ }
30
+ await fs.promises.writeFile(filePath, serialized);
31
+ await this.prune();
32
+ }
33
+ async load(id) {
34
+ validateId(id);
35
+ const filePath = path.join(this.dir, `${id}.json`);
36
+ if (!path.resolve(filePath).startsWith(path.resolve(this.dir) + path.sep)) {
37
+ throw new Error('Invalid checkpoint path');
38
+ }
39
+ try {
40
+ const raw = await fs.promises.readFile(filePath, 'utf-8');
41
+ return JSON.parse(raw);
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ async list() {
48
+ try {
49
+ const entries = await fs.promises.readdir(this.dir);
50
+ return entries.filter(f => f.endsWith('.json')).map(f => f.replace('.json', ''));
51
+ }
52
+ catch {
53
+ return [];
54
+ }
55
+ }
56
+ async prune() {
57
+ try {
58
+ const entries = await fs.promises.readdir(this.dir);
59
+ const jsonFiles = entries.filter(f => f.endsWith('.json'));
60
+ if (jsonFiles.length <= MAX_CHECKPOINTS)
61
+ return;
62
+ const stats = await Promise.all(jsonFiles.map(async (f) => ({
63
+ name: f,
64
+ mtime: (await fs.promises.stat(path.join(this.dir, f))).mtimeMs,
65
+ })));
66
+ stats.sort((a, b) => a.mtime - b.mtime);
67
+ const toRemove = stats.slice(0, stats.length - MAX_CHECKPOINTS);
68
+ await Promise.all(toRemove.map(f => fs.promises.unlink(path.join(this.dir, f.name)).catch(() => { })));
69
+ }
70
+ catch { /* best-effort */ }
71
+ }
72
+ }
73
+ export class MemoryCheckpointStore {
74
+ store = new Map();
75
+ async save(id, data) {
76
+ validateId(id);
77
+ const serialized = JSON.stringify(data);
78
+ if (serialized.length > MAX_CHECKPOINT_BYTES)
79
+ throw new Error('Checkpoint too large');
80
+ this.store.set(id, serialized);
81
+ if (this.store.size > MAX_CHECKPOINTS) {
82
+ const oldest = this.store.keys().next().value;
83
+ if (oldest !== undefined)
84
+ this.store.delete(oldest);
85
+ }
86
+ }
87
+ async load(id) {
88
+ validateId(id);
89
+ const raw = this.store.get(id);
90
+ if (!raw)
91
+ return null;
92
+ try {
93
+ return JSON.parse(raw);
94
+ }
95
+ catch {
96
+ return null;
97
+ }
98
+ }
99
+ async list() {
100
+ return [...this.store.keys()];
101
+ }
102
+ }
103
+ //# sourceMappingURL=checkpoint-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoint-store.js","sourceRoot":"","sources":["../src/checkpoint-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Excalimate MCP Server — Entry Point
4
+ *
5
+ * Supports two transports:
6
+ * --stdio : For Claude Desktop / Copilot CLI
7
+ * (default) : Streamable HTTP on port 3001
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;GAMG"}
package/dist/index.js ADDED
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Excalimate MCP Server — Entry Point
4
+ *
5
+ * Supports two transports:
6
+ * --stdio : For Claude Desktop / Copilot CLI
7
+ * (default) : Streamable HTTP on port 3001
8
+ */
9
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
10
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
11
+ import cors from 'cors';
12
+ import crypto from 'node:crypto';
13
+ import express from 'express';
14
+ import helmet from 'helmet';
15
+ import rateLimit from 'express-rate-limit';
16
+ import { FileCheckpointStore } from './checkpoint-store.js';
17
+ import { createServer, getSharedState } from './server.js';
18
+ async function startStdioServer(factory) {
19
+ await factory().connect(new StdioServerTransport());
20
+ }
21
+ async function startHTTPServer(factoryWithSSE) {
22
+ const port = parseInt(process.env.PORT ?? '3001', 10);
23
+ const app = express();
24
+ app.use(helmet({ contentSecurityPolicy: false })); // Security headers
25
+ app.use(cors({
26
+ origin: process.env.CORS_ORIGIN ?? '*',
27
+ }));
28
+ // Rate limiting
29
+ const limiter = rateLimit({
30
+ windowMs: 60 * 1000, // 1 minute
31
+ max: 200, // 200 requests per minute
32
+ standardHeaders: true,
33
+ legacyHeaders: false,
34
+ });
35
+ app.use(limiter);
36
+ // Stricter rate limit for share uploads
37
+ const shareLimiter = rateLimit({
38
+ windowMs: 60 * 1000,
39
+ max: 10, // 10 shares per minute
40
+ standardHeaders: true,
41
+ legacyHeaders: false,
42
+ });
43
+ // Parse JSON only for non-share routes (share uses raw binary)
44
+ app.use((req, res, next) => {
45
+ if (req.path === '/share' && req.method === 'POST') {
46
+ next();
47
+ }
48
+ else {
49
+ express.json()(req, res, next);
50
+ }
51
+ });
52
+ // SSE clients for live state broadcasting
53
+ const sseClients = new Set();
54
+ // Factory that wires SSE broadcasting
55
+ const factory = () => factoryWithSSE(sseClients);
56
+ // Session map: keep server + transport alive across requests
57
+ const sessions = new Map();
58
+ app.all('/mcp', async (req, res) => {
59
+ const sessionId = req.headers['mcp-session-id'];
60
+ // Route to existing session
61
+ if (sessionId && sessions.has(sessionId)) {
62
+ const session = sessions.get(sessionId);
63
+ try {
64
+ await session.transport.handleRequest(req, res, req.body);
65
+ }
66
+ catch (error) {
67
+ console.error('MCP session error:', error);
68
+ if (!res.headersSent) {
69
+ res.status(500).json({
70
+ jsonrpc: '2.0',
71
+ error: { code: -32603, message: 'Internal server error' },
72
+ id: null,
73
+ });
74
+ }
75
+ }
76
+ return;
77
+ }
78
+ // New session
79
+ const server = factory();
80
+ const transport = new StreamableHTTPServerTransport({
81
+ sessionIdGenerator: () => crypto.randomUUID(),
82
+ });
83
+ transport.onclose = () => {
84
+ const sid = transport.sessionId;
85
+ if (sid)
86
+ sessions.delete(sid);
87
+ server.close().catch(() => { });
88
+ };
89
+ try {
90
+ await server.connect(transport);
91
+ await transport.handleRequest(req, res, req.body);
92
+ // Store session after first successful request (session ID is now set)
93
+ const sid = transport.sessionId;
94
+ if (sid) {
95
+ sessions.set(sid, { server, transport });
96
+ }
97
+ }
98
+ catch (error) {
99
+ console.error('MCP error:', error);
100
+ if (!res.headersSent) {
101
+ res.status(500).json({
102
+ jsonrpc: '2.0',
103
+ error: { code: -32603, message: 'Internal server error' },
104
+ id: null,
105
+ });
106
+ }
107
+ }
108
+ });
109
+ // SSE endpoint — web app connects here to receive live state updates
110
+ app.get('/live', (req, res) => {
111
+ res.writeHead(200, {
112
+ 'Content-Type': 'text/event-stream',
113
+ 'Cache-Control': 'no-cache',
114
+ Connection: 'keep-alive',
115
+ 'Access-Control-Allow-Origin': '*',
116
+ });
117
+ res.write('data: {"type":"connected"}\n\n');
118
+ sseClients.add(res);
119
+ req.on('close', () => sseClients.delete(res));
120
+ });
121
+ // Current state endpoint
122
+ app.get('/state', (_req, res) => {
123
+ res.json(getSharedState());
124
+ });
125
+ // ── E2E Encrypted Sharing ──────────────────────────────────────
126
+ // The server only stores encrypted blobs. It never sees the encryption key.
127
+ const shareStore = new Map();
128
+ const MAX_SHARE_SIZE = 10 * 1024 * 1024; // 10 MB
129
+ const MAX_SHARES = 500;
130
+ // Upload encrypted blob
131
+ app.post('/share', shareLimiter, express.raw({ type: 'application/octet-stream', limit: '10mb' }), (req, res) => {
132
+ const id = crypto.randomUUID().replace(/-/g, '').slice(0, 16);
133
+ const body = req.body;
134
+ if (!body || body.length === 0) {
135
+ res.status(400).json({ error: 'Empty body. Send as application/octet-stream.' });
136
+ return;
137
+ }
138
+ if (body.length > MAX_SHARE_SIZE) {
139
+ res.status(413).json({ error: 'Payload too large' });
140
+ return;
141
+ }
142
+ // Evict oldest if over limit
143
+ if (shareStore.size >= MAX_SHARES) {
144
+ const oldest = shareStore.keys().next().value;
145
+ if (oldest !== undefined)
146
+ shareStore.delete(oldest);
147
+ }
148
+ shareStore.set(id, body);
149
+ res.json({ id, url: `/share/${id}` });
150
+ });
151
+ // Download encrypted blob
152
+ app.get('/share/:id', (req, res) => {
153
+ const data = shareStore.get(req.params.id);
154
+ if (!data) {
155
+ res.status(404).json({ error: 'Not found' });
156
+ return;
157
+ }
158
+ res.set('Content-Type', 'application/octet-stream');
159
+ res.send(data);
160
+ });
161
+ app.listen(port, () => {
162
+ console.log(`Excalimate MCP server listening on http://localhost:${port}/mcp`);
163
+ console.log(`Live preview SSE at http://localhost:${port}/live`);
164
+ });
165
+ const shutdown = () => {
166
+ console.log('\nShutting down...');
167
+ process.exit(0);
168
+ };
169
+ process.on('SIGINT', shutdown);
170
+ process.on('SIGTERM', shutdown);
171
+ }
172
+ async function main() {
173
+ const store = new FileCheckpointStore();
174
+ if (process.argv.includes('--stdio')) {
175
+ const factory = () => createServer(store);
176
+ await startStdioServer(factory);
177
+ }
178
+ else {
179
+ await startHTTPServer((sseClients) => {
180
+ return createServer(store, (state) => {
181
+ // Broadcast state to all SSE clients
182
+ const data = JSON.stringify({ type: 'state', state });
183
+ for (const client of sseClients) {
184
+ client.write(`data: ${data}\n\n`);
185
+ }
186
+ });
187
+ });
188
+ }
189
+ }
190
+ main().catch((e) => {
191
+ console.error(e);
192
+ process.exit(1);
193
+ });
194
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Excalimate MCP Server
3
+ *
4
+ * Provides tools for creating Excalidraw scenes, animating them with keyframes,
5
+ * and exporting the results. Designed for AI agent integration via MCP.
6
+ */
7
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
8
+ import type { CheckpointStore } from './checkpoint-store.js';
9
+ import type { ServerState } from './types.js';
10
+ export type StateChangeListener = (state: ServerState) => void;
11
+ export declare function getSharedState(): ServerState;
12
+ export declare function createServer(store: CheckpointStore, onStateChange?: StateChangeListener): McpServer;
13
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAkD,MAAM,YAAY,CAAC;AAG9F,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;AAK/D,wBAAgB,cAAc,IAAI,WAAW,CAAyB;AAEtE,wBAAgB,YAAY,CAC1B,KAAK,EAAE,eAAe,EACtB,aAAa,CAAC,EAAE,mBAAmB,GAClC,SAAS,CAqvBX"}