@excalimate/mcp-server 0.1.0 → 0.3.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 +176 -16
- package/dist/checkpoint-store.d.ts +1 -0
- package/dist/checkpoint-store.d.ts.map +1 -1
- package/dist/checkpoint-store.js +23 -2
- package/dist/checkpoint-store.js.map +1 -1
- package/dist/httpServer.d.ts +4 -0
- package/dist/httpServer.d.ts.map +1 -0
- package/dist/httpServer.js +199 -0
- package/dist/httpServer.js.map +1 -0
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -177
- package/dist/index.js.map +1 -1
- package/dist/server/animationTools.d.ts +9 -0
- package/dist/server/animationTools.d.ts.map +1 -0
- package/dist/server/animationTools.js +254 -0
- package/dist/server/animationTools.js.map +1 -0
- package/dist/server/checkpointTools.d.ts +5 -0
- package/dist/server/checkpointTools.d.ts.map +1 -0
- package/dist/server/checkpointTools.js +22 -0
- package/dist/server/checkpointTools.js.map +1 -0
- package/dist/server/elementNormalizer.d.ts +3 -0
- package/dist/server/elementNormalizer.d.ts.map +1 -0
- package/dist/server/elementNormalizer.js +52 -0
- package/dist/server/elementNormalizer.js.map +1 -0
- package/dist/server/geometry.d.ts +24 -0
- package/dist/server/geometry.d.ts.map +1 -0
- package/dist/server/geometry.js +102 -0
- package/dist/server/geometry.js.map +1 -0
- package/dist/server/queryTools.d.ts +28 -0
- package/dist/server/queryTools.d.ts.map +1 -0
- package/dist/server/queryTools.js +107 -0
- package/dist/server/queryTools.js.map +1 -0
- package/dist/server/referenceText.d.ts +3 -0
- package/dist/server/referenceText.d.ts.map +1 -0
- package/dist/server/referenceText.js +268 -0
- package/dist/server/referenceText.js.map +1 -0
- package/dist/server/sceneTools.d.ts +4 -0
- package/dist/server/sceneTools.d.ts.map +1 -0
- package/dist/server/sceneTools.js +86 -0
- package/dist/server/sceneTools.js.map +1 -0
- package/dist/server/shareTools.d.ts +11 -0
- package/dist/server/shareTools.d.ts.map +1 -0
- package/dist/server/shareTools.js +81 -0
- package/dist/server/shareTools.js.map +1 -0
- package/dist/server/stateContext.d.ts +21 -0
- package/dist/server/stateContext.d.ts.map +1 -0
- package/dist/server/stateContext.js +85 -0
- package/dist/server/stateContext.js.map +1 -0
- package/dist/server.d.ts +5 -10
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +24 -891
- package/dist/server.js.map +1 -1
- package/dist/shareRoutes.d.ts +4 -0
- package/dist/shareRoutes.d.ts.map +1 -0
- package/dist/shareRoutes.js +44 -0
- package/dist/shareRoutes.js.map +1 -0
- package/dist/stdioServer.d.ts +3 -0
- package/dist/stdioServer.d.ts.map +1 -0
- package/dist/stdioServer.js +5 -0
- package/dist/stdioServer.js.map +1 -0
- package/package.json +6 -2
- package/SKILL.md +0 -110
- package/references/REFERENCE.md +0 -192
package/dist/index.js
CHANGED
|
@@ -1,190 +1,48 @@
|
|
|
1
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
2
|
import { FileCheckpointStore } from './checkpoint-store.js';
|
|
17
|
-
import { createServer
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
}
|
|
3
|
+
import { createServer } from './server.js';
|
|
4
|
+
import { startStdioServer } from './stdioServer.js';
|
|
5
|
+
import { startHTTPServer } from './httpServer.js';
|
|
6
|
+
/** Parse --port=NNNN or --port NNNN from argv */
|
|
7
|
+
function parsePort() {
|
|
8
|
+
for (let i = 0; i < process.argv.length; i++) {
|
|
9
|
+
const arg = process.argv[i];
|
|
10
|
+
if (arg.startsWith('--port=')) {
|
|
11
|
+
return parseInt(arg.slice('--port='.length), 10);
|
|
12
|
+
}
|
|
13
|
+
if (arg === '--port' && process.argv[i + 1]) {
|
|
14
|
+
return parseInt(process.argv[i + 1], 10);
|
|
15
|
+
}
|
|
16
|
+
if (arg === '-p' && process.argv[i + 1]) {
|
|
17
|
+
return parseInt(process.argv[i + 1], 10);
|
|
107
18
|
}
|
|
108
|
-
}
|
|
109
|
-
|
|
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);
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
171
21
|
}
|
|
172
22
|
async function main() {
|
|
23
|
+
process.on('unhandledRejection', (reason) => {
|
|
24
|
+
console.error('Unhandled rejection:', reason);
|
|
25
|
+
});
|
|
26
|
+
process.on('uncaughtException', (err) => {
|
|
27
|
+
console.error('Uncaught exception:', err);
|
|
28
|
+
});
|
|
173
29
|
const store = new FileCheckpointStore();
|
|
30
|
+
const cliPort = parsePort();
|
|
174
31
|
if (process.argv.includes('--stdio')) {
|
|
175
|
-
|
|
176
|
-
await startStdioServer(factory);
|
|
32
|
+
await startStdioServer(() => createServer(store));
|
|
177
33
|
}
|
|
178
34
|
else {
|
|
179
|
-
await startHTTPServer((
|
|
180
|
-
return createServer(store, (
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
35
|
+
await startHTTPServer((_sseClients, broadcastSSE, port) => {
|
|
36
|
+
return createServer(store, (delta) => {
|
|
37
|
+
try {
|
|
38
|
+
const data = JSON.stringify({ type: 'state', state: delta });
|
|
39
|
+
broadcastSSE(data);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
console.error('Failed to broadcast state:', err);
|
|
185
43
|
}
|
|
186
|
-
});
|
|
187
|
-
});
|
|
44
|
+
}, port);
|
|
45
|
+
}, cliPort);
|
|
188
46
|
}
|
|
189
47
|
}
|
|
190
48
|
main().catch((e) => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,iDAAiD;AACjD,SAAS,SAAS;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9B,OAAO,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,GAAG,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAC5C,OAAO,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3C,CAAC;QACD,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACxC,OAAO,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,EAAE;QAC1C,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,GAAG,EAAE,EAAE;QACtC,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,IAAI,mBAAmB,EAAE,CAAC;IACxC,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC;IAE5B,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACrC,MAAM,gBAAgB,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;IACpD,CAAC;SAAM,CAAC;QACN,MAAM,eAAe,CAAC,CAAC,WAAW,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;YACxD,OAAO,YAAY,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE;gBACnC,IAAI,CAAC;oBACH,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;oBAC7D,YAAY,CAAC,IAAI,CAAC,CAAC;gBACrB,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAC;gBACnD,CAAC;YACH,CAAC,EAAE,IAAI,CAAC,CAAC;QACX,CAAC,EAAE,OAAO,CAAC,CAAC;IACd,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;IACjB,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACjB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { StateContext } from './stateContext.js';
|
|
3
|
+
export declare function registerAnimationTools(server: McpServer, ctx: StateContext, getElementBounds: (el: any) => {
|
|
4
|
+
minX: number;
|
|
5
|
+
minY: number;
|
|
6
|
+
maxX: number;
|
|
7
|
+
maxY: number;
|
|
8
|
+
}): void;
|
|
9
|
+
//# sourceMappingURL=animationTools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animationTools.d.ts","sourceRoot":"","sources":["../../src/server/animationTools.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAMzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,YAAY,EACjB,gBAAgB,EAAE,CAAC,EAAE,EAAE,GAAG,KAAK;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACxF,IAAI,CAkTN"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { addKeyframeToState } from '../state.js';
|
|
3
|
+
import { ANIMATABLE_PROPERTIES, EASING_TYPES } from '../types.js';
|
|
4
|
+
import { ORIGIN_MAP } from './geometry.js';
|
|
5
|
+
export function registerAnimationTools(server, ctx, getElementBounds) {
|
|
6
|
+
ctx.mutatingTool('add_keyframe', 'Add a keyframe to an animation track. Auto-creates the track if it doesn\'t exist.', {
|
|
7
|
+
targetId: z.string().describe('Element or group ID'),
|
|
8
|
+
property: z.enum(ANIMATABLE_PROPERTIES).describe('Animatable property'),
|
|
9
|
+
time: z.number().min(0).describe('Time in milliseconds'),
|
|
10
|
+
value: z.number().describe('Property value at this time'),
|
|
11
|
+
easing: z.enum(EASING_TYPES).optional().describe('Easing to next keyframe'),
|
|
12
|
+
}, async ({ targetId, property, time, value, easing }) => {
|
|
13
|
+
const state = ctx.getState();
|
|
14
|
+
ctx.updateState(addKeyframeToState(state, targetId, property, time, value, easing ?? 'linear'));
|
|
15
|
+
return { content: [{ type: 'text', text: `Keyframe added: ${property} = ${value} at ${time}ms for ${targetId}` }] };
|
|
16
|
+
});
|
|
17
|
+
ctx.mutatingTool('add_keyframes_batch', 'Add multiple keyframes at once. For scaleX/scaleY keyframes, include a "scaleOrigin" field per keyframe to control where scaling anchors from (auto-adds translate compensation). Origins: center, top-left, top-right, bottom-left, bottom-right, top, bottom, left, right.', {
|
|
18
|
+
keyframes: z.string().describe('JSON array of {targetId, property, time, value, easing?, scaleOrigin?}'),
|
|
19
|
+
}, async ({ keyframes }) => {
|
|
20
|
+
try {
|
|
21
|
+
const parsed = JSON.parse(keyframes);
|
|
22
|
+
if (!Array.isArray(parsed))
|
|
23
|
+
return { content: [{ type: 'text', text: 'Error: must be array' }] };
|
|
24
|
+
const scaleCompensation = new Map();
|
|
25
|
+
for (const kf of parsed) {
|
|
26
|
+
if ((kf.property === 'scaleX' || kf.property === 'scaleY') && kf.scaleOrigin && kf.scaleOrigin !== 'top-left') {
|
|
27
|
+
const key = `${kf.targetId}@${kf.time}`;
|
|
28
|
+
const existing = scaleCompensation.get(key) ?? { targetId: kf.targetId, time: kf.time, sx: 1, sy: 1, origin: kf.scaleOrigin, easing: kf.easing ?? 'linear' };
|
|
29
|
+
if (kf.property === 'scaleX')
|
|
30
|
+
existing.sx = kf.value;
|
|
31
|
+
if (kf.property === 'scaleY')
|
|
32
|
+
existing.sy = kf.value;
|
|
33
|
+
existing.origin = kf.scaleOrigin;
|
|
34
|
+
scaleCompensation.set(key, existing);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const validProperties = new Set(ANIMATABLE_PROPERTIES);
|
|
38
|
+
let count = 0;
|
|
39
|
+
let skipped = 0;
|
|
40
|
+
for (const kf of parsed) {
|
|
41
|
+
if (!validProperties.has(kf.property)) {
|
|
42
|
+
skipped++;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const state = ctx.getState();
|
|
46
|
+
ctx.updateState(addKeyframeToState(state, kf.targetId, kf.property, kf.time, kf.value, kf.easing ?? 'linear'));
|
|
47
|
+
count++;
|
|
48
|
+
}
|
|
49
|
+
for (const skf of scaleCompensation.values()) {
|
|
50
|
+
const [ox, oy] = ORIGIN_MAP[skf.origin] ?? [0.5, 0.5];
|
|
51
|
+
const state = ctx.getState();
|
|
52
|
+
const el = state.scene.elements.find((e) => e.id === skf.targetId);
|
|
53
|
+
if (!el)
|
|
54
|
+
continue;
|
|
55
|
+
const bounds = getElementBounds(el);
|
|
56
|
+
const w = bounds.maxX - bounds.minX;
|
|
57
|
+
const h = bounds.maxY - bounds.minY;
|
|
58
|
+
const tx = -w * (skf.sx - 1) * ox;
|
|
59
|
+
const ty = -h * (skf.sy - 1) * oy;
|
|
60
|
+
ctx.updateState(addKeyframeToState(state, skf.targetId, 'translateX', skf.time, tx, skf.easing));
|
|
61
|
+
const updated = ctx.getState();
|
|
62
|
+
ctx.updateState(addKeyframeToState(updated, skf.targetId, 'translateY', skf.time, ty, skf.easing));
|
|
63
|
+
count += 2;
|
|
64
|
+
}
|
|
65
|
+
return { content: [{ type: 'text', text: `Added ${count} keyframes.${skipped ? ` Skipped ${skipped} with invalid properties.` : ''}` }] };
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
return { content: [{ type: 'text', text: `Error: ${e}` }] };
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
ctx.mutatingTool('remove_keyframe', 'Remove a keyframe by track and keyframe ID.', {
|
|
72
|
+
trackId: z.string().describe('Track ID'),
|
|
73
|
+
keyframeId: z.string().describe('Keyframe ID'),
|
|
74
|
+
}, async ({ trackId, keyframeId }) => {
|
|
75
|
+
const state = ctx.getState();
|
|
76
|
+
const track = state.timeline.tracks.find(t => t.id === trackId);
|
|
77
|
+
if (!track)
|
|
78
|
+
return { content: [{ type: 'text', text: 'Track not found.' }] };
|
|
79
|
+
const before = track.keyframes.length;
|
|
80
|
+
track.keyframes = track.keyframes.filter(kf => kf.id !== keyframeId);
|
|
81
|
+
return { content: [{ type: 'text', text: `Removed ${before - track.keyframes.length} keyframe(s).` }] };
|
|
82
|
+
});
|
|
83
|
+
ctx.mutatingTool('create_sequence', 'Create a reveal sequence — elements appear one after another with configurable timing.', {
|
|
84
|
+
elementIds: z.array(z.string()).describe('Element IDs in reveal order'),
|
|
85
|
+
property: z.enum(['opacity', 'drawProgress']).default('opacity').describe('Property to animate'),
|
|
86
|
+
startTime: z.number().min(0).default(0).describe('When sequence starts (ms)'),
|
|
87
|
+
delay: z.number().min(0).default(300).describe('Delay between each reveal (ms)'),
|
|
88
|
+
duration: z.number().min(50).default(500).describe('Duration of each reveal (ms)'),
|
|
89
|
+
}, async ({ elementIds, property, startTime, delay, duration }) => {
|
|
90
|
+
for (let i = 0; i < elementIds.length; i++) {
|
|
91
|
+
const revealStart = startTime + i * delay;
|
|
92
|
+
const revealEnd = revealStart + duration;
|
|
93
|
+
const targetId = elementIds[i];
|
|
94
|
+
if (revealStart > 0) {
|
|
95
|
+
const state = ctx.getState();
|
|
96
|
+
ctx.updateState(addKeyframeToState(state, targetId, property, 0, 0));
|
|
97
|
+
}
|
|
98
|
+
if (revealStart > 10) {
|
|
99
|
+
const state = ctx.getState();
|
|
100
|
+
ctx.updateState(addKeyframeToState(state, targetId, property, revealStart, 0));
|
|
101
|
+
}
|
|
102
|
+
const state = ctx.getState();
|
|
103
|
+
ctx.updateState(addKeyframeToState(state, targetId, property, revealEnd, 1, 'easeOut'));
|
|
104
|
+
}
|
|
105
|
+
const totalDuration = startTime + (elementIds.length - 1) * delay + duration;
|
|
106
|
+
return { content: [{ type: 'text', text: `Sequence created: ${elementIds.length} elements, total ${totalDuration}ms.` }] };
|
|
107
|
+
});
|
|
108
|
+
ctx.mutatingTool('set_clip_range', 'Set the export clip start and end times.', {
|
|
109
|
+
start: z.number().min(0).describe('Clip start time (ms)'),
|
|
110
|
+
end: z.number().min(100).describe('Clip end time (ms)'),
|
|
111
|
+
}, async ({ start, end }) => {
|
|
112
|
+
const state = ctx.getState();
|
|
113
|
+
state.clipStart = start;
|
|
114
|
+
state.clipEnd = Math.max(start + 100, end);
|
|
115
|
+
return { content: [{ type: 'text', text: `Clip range: ${start}ms – ${end}ms (${(end - start) / 1000}s)` }] };
|
|
116
|
+
});
|
|
117
|
+
server.tool('get_timeline', 'Return the current animation timeline as JSON.', {}, async () => {
|
|
118
|
+
const state = ctx.getState();
|
|
119
|
+
return {
|
|
120
|
+
content: [{
|
|
121
|
+
type: 'text',
|
|
122
|
+
text: JSON.stringify({
|
|
123
|
+
timeline: state.timeline,
|
|
124
|
+
clipStart: state.clipStart,
|
|
125
|
+
clipEnd: state.clipEnd,
|
|
126
|
+
cameraFrame: state.cameraFrame,
|
|
127
|
+
}, null, 2),
|
|
128
|
+
}],
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
ctx.mutatingTool('clear_animation', 'Clear all animation tracks.', {}, async () => {
|
|
132
|
+
const state = ctx.getState();
|
|
133
|
+
state.timeline.tracks = [];
|
|
134
|
+
return { content: [{ type: 'text', text: 'All animation tracks cleared.' }] };
|
|
135
|
+
});
|
|
136
|
+
ctx.mutatingTool('add_scale_animation', 'Add scale keyframes with a specific origin (edge/corner/center). Auto-computes translate compensation to keep the origin point fixed during scaling.', {
|
|
137
|
+
targetId: z.string().describe('Element ID'),
|
|
138
|
+
origin: z.enum(['center', 'top-left', 'top-right', 'bottom-left', 'bottom-right', 'top', 'bottom', 'left', 'right']).describe('Scale origin point'),
|
|
139
|
+
keyframes: z.string().describe('JSON array of {time, scaleX, scaleY, easing?}'),
|
|
140
|
+
}, async ({ targetId, origin, keyframes }) => {
|
|
141
|
+
try {
|
|
142
|
+
const parsed = JSON.parse(keyframes);
|
|
143
|
+
if (!Array.isArray(parsed))
|
|
144
|
+
return { content: [{ type: 'text', text: 'Error: must be array' }] };
|
|
145
|
+
const state = ctx.getState();
|
|
146
|
+
const el = state.scene.elements.find((e) => e.id === targetId);
|
|
147
|
+
if (!el)
|
|
148
|
+
return { content: [{ type: 'text', text: `Element "${targetId}" not found.` }] };
|
|
149
|
+
const bounds = getElementBounds(el);
|
|
150
|
+
const w = bounds.maxX - bounds.minX;
|
|
151
|
+
const h = bounds.maxY - bounds.minY;
|
|
152
|
+
const [ox, oy] = ORIGIN_MAP[origin] ?? [0.5, 0.5];
|
|
153
|
+
let count = 0;
|
|
154
|
+
for (const kf of parsed) {
|
|
155
|
+
const sx = kf.scaleX ?? 1;
|
|
156
|
+
const sy = kf.scaleY ?? 1;
|
|
157
|
+
const easing = kf.easing ?? 'linear';
|
|
158
|
+
let currentState = ctx.getState();
|
|
159
|
+
ctx.updateState(addKeyframeToState(currentState, targetId, 'scaleX', kf.time, sx, easing));
|
|
160
|
+
currentState = ctx.getState();
|
|
161
|
+
ctx.updateState(addKeyframeToState(currentState, targetId, 'scaleY', kf.time, sy, easing));
|
|
162
|
+
const tx = -w * (sx - 1) * ox;
|
|
163
|
+
const ty = -h * (sy - 1) * oy;
|
|
164
|
+
if (Math.abs(tx) > 0.1 || Math.abs(ty) > 0.1 || ox !== 0 || oy !== 0) {
|
|
165
|
+
currentState = ctx.getState();
|
|
166
|
+
ctx.updateState(addKeyframeToState(currentState, targetId, 'translateX', kf.time, tx, easing));
|
|
167
|
+
currentState = ctx.getState();
|
|
168
|
+
ctx.updateState(addKeyframeToState(currentState, targetId, 'translateY', kf.time, ty, easing));
|
|
169
|
+
}
|
|
170
|
+
count++;
|
|
171
|
+
}
|
|
172
|
+
return { content: [{ type: 'text', text: `Added ${count} scale keyframes with origin "${origin}" for "${targetId}".` }] };
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
return { content: [{ type: 'text', text: `Error: ${e}` }] };
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
ctx.mutatingTool('set_camera_frame', 'Set the camera frame position, size, and aspect ratio. Also creates initial keyframes at time 0 for translateX, translateY, scaleX, scaleY so the camera starts at this position.', {
|
|
179
|
+
x: z.number().optional().describe('Camera center X (scene coords)'),
|
|
180
|
+
y: z.number().optional().describe('Camera center Y (scene coords)'),
|
|
181
|
+
width: z.number().optional().describe('Camera width (scene units)'),
|
|
182
|
+
aspectRatio: z.enum(['16:9', '4:3', '1:1', '3:2']).optional().describe('Aspect ratio'),
|
|
183
|
+
}, async ({ x, y, width, aspectRatio }) => {
|
|
184
|
+
const state = ctx.getState();
|
|
185
|
+
if (x !== undefined)
|
|
186
|
+
state.cameraFrame.x = x;
|
|
187
|
+
if (y !== undefined)
|
|
188
|
+
state.cameraFrame.y = y;
|
|
189
|
+
if (width !== undefined)
|
|
190
|
+
state.cameraFrame.width = width;
|
|
191
|
+
if (aspectRatio !== undefined)
|
|
192
|
+
state.cameraFrame.aspectRatio = aspectRatio;
|
|
193
|
+
const CAMERA_ID = '__camera_frame__';
|
|
194
|
+
let currentState = ctx.getState();
|
|
195
|
+
ctx.updateState(addKeyframeToState(currentState, CAMERA_ID, 'translateX', 0, 0));
|
|
196
|
+
currentState = ctx.getState();
|
|
197
|
+
ctx.updateState(addKeyframeToState(currentState, CAMERA_ID, 'translateY', 0, 0));
|
|
198
|
+
currentState = ctx.getState();
|
|
199
|
+
ctx.updateState(addKeyframeToState(currentState, CAMERA_ID, 'scaleX', 0, 1));
|
|
200
|
+
currentState = ctx.getState();
|
|
201
|
+
ctx.updateState(addKeyframeToState(currentState, CAMERA_ID, 'scaleY', 0, 1));
|
|
202
|
+
const updated = ctx.getState();
|
|
203
|
+
return { content: [{ type: 'text', text: `Camera: ${updated.cameraFrame.aspectRatio} at (${updated.cameraFrame.x}, ${updated.cameraFrame.y}), width ${updated.cameraFrame.width}. Initial keyframes created at t=0.` }] };
|
|
204
|
+
});
|
|
205
|
+
ctx.mutatingTool('add_camera_keyframe', 'Add a keyframe for camera pan/zoom animation.', {
|
|
206
|
+
property: z.enum(['translateX', 'translateY', 'scaleX', 'scaleY']).describe('Camera property'),
|
|
207
|
+
time: z.number().min(0).describe('Time in ms'),
|
|
208
|
+
value: z.number().describe('Value'),
|
|
209
|
+
easing: z.enum(EASING_TYPES).optional(),
|
|
210
|
+
}, async ({ property, time, value, easing }) => {
|
|
211
|
+
const CAMERA_ID = '__camera_frame__';
|
|
212
|
+
const state = ctx.getState();
|
|
213
|
+
ctx.updateState(addKeyframeToState(state, CAMERA_ID, property, time, value, easing ?? 'linear'));
|
|
214
|
+
return { content: [{ type: 'text', text: `Camera keyframe: ${property} = ${value} at ${time}ms` }] };
|
|
215
|
+
});
|
|
216
|
+
ctx.mutatingTool('add_camera_keyframes_batch', 'Add multiple camera keyframes at once. Properties: translateX, translateY, scaleX, scaleY.', {
|
|
217
|
+
keyframes: z.string().describe('JSON array of {property: "translateX"|"translateY"|"scaleX"|"scaleY", time, value, easing?}'),
|
|
218
|
+
}, async ({ keyframes }) => {
|
|
219
|
+
try {
|
|
220
|
+
const parsed = JSON.parse(keyframes);
|
|
221
|
+
if (!Array.isArray(parsed))
|
|
222
|
+
return { content: [{ type: 'text', text: 'Error: must be array' }] };
|
|
223
|
+
const CAMERA_ID = '__camera_frame__';
|
|
224
|
+
const validCameraProps = new Set(['translateX', 'translateY', 'scaleX', 'scaleY']);
|
|
225
|
+
const propMap = {
|
|
226
|
+
x: 'translateX',
|
|
227
|
+
y: 'translateY',
|
|
228
|
+
panX: 'translateX',
|
|
229
|
+
panY: 'translateY',
|
|
230
|
+
zoom: 'scaleX',
|
|
231
|
+
scale: 'scaleX',
|
|
232
|
+
};
|
|
233
|
+
let count = 0;
|
|
234
|
+
let skipped = 0;
|
|
235
|
+
for (const kf of parsed) {
|
|
236
|
+
let prop = kf.property;
|
|
237
|
+
if (propMap[prop])
|
|
238
|
+
prop = propMap[prop];
|
|
239
|
+
if (!validCameraProps.has(prop)) {
|
|
240
|
+
skipped++;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
const state = ctx.getState();
|
|
244
|
+
ctx.updateState(addKeyframeToState(state, CAMERA_ID, prop, kf.time, kf.value, kf.easing ?? 'linear'));
|
|
245
|
+
count++;
|
|
246
|
+
}
|
|
247
|
+
return { content: [{ type: 'text', text: `Added ${count} camera keyframes.${skipped ? ` Skipped ${skipped} with invalid properties (use translateX, translateY, scaleX, scaleY).` : ''}` }] };
|
|
248
|
+
}
|
|
249
|
+
catch (e) {
|
|
250
|
+
return { content: [{ type: 'text', text: `Error: ${e}` }] };
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=animationTools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animationTools.js","sourceRoot":"","sources":["../../src/server/animationTools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { CheckpointStore } from '../checkpoint-store.js';
|
|
3
|
+
import type { StateContext } from './stateContext.js';
|
|
4
|
+
export declare function registerCheckpointTools(server: McpServer, ctx: StateContext, store: CheckpointStore): void;
|
|
5
|
+
//# sourceMappingURL=checkpointTools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpointTools.d.ts","sourceRoot":"","sources":["../../src/server/checkpointTools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,YAAY,EACjB,KAAK,EAAE,eAAe,GACrB,IAAI,CAkCN"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { nanoid } from 'nanoid';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export function registerCheckpointTools(server, ctx, store) {
|
|
4
|
+
ctx.mutatingTool('save_checkpoint', 'Save current scene + animation state to a checkpoint.', { id: z.string().optional().describe('Checkpoint ID (auto-generated if omitted)') }, async ({ id }) => {
|
|
5
|
+
const checkpointId = id ?? nanoid(12);
|
|
6
|
+
await store.save(checkpointId, ctx.getState());
|
|
7
|
+
return { content: [{ type: 'text', text: `Saved checkpoint: ${checkpointId}` }] };
|
|
8
|
+
});
|
|
9
|
+
ctx.mutatingTool('load_checkpoint', 'Load scene + animation state from a checkpoint.', { id: z.string().describe('Checkpoint ID') }, async ({ id }) => {
|
|
10
|
+
const loaded = await store.load(id);
|
|
11
|
+
if (!loaded)
|
|
12
|
+
return { content: [{ type: 'text', text: `Checkpoint "${id}" not found.` }] };
|
|
13
|
+
ctx.updateState(loaded);
|
|
14
|
+
const state = ctx.getState();
|
|
15
|
+
return { content: [{ type: 'text', text: `Loaded checkpoint "${id}": ${state.scene.elements.length} elements, ${state.timeline.tracks.length} tracks.` }] };
|
|
16
|
+
});
|
|
17
|
+
server.tool('list_checkpoints', 'List all saved checkpoints.', {}, async () => {
|
|
18
|
+
const ids = await store.list();
|
|
19
|
+
return { content: [{ type: 'text', text: ids.length > 0 ? `Checkpoints:\n${ids.join('\n')}` : 'No checkpoints saved.' }] };
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=checkpointTools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpointTools.js","sourceRoot":"","sources":["../../src/server/checkpointTools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,uBAAuB,CACrC,MAAiB,EACjB,GAAiB,EACjB,KAAsB;IAEtB,GAAG,CAAC,YAAY,CACd,iBAAiB,EACjB,uDAAuD,EACvD,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2CAA2C,CAAC,EAAE,EACnF,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QACf,MAAM,YAAY,GAAG,EAAE,IAAI,MAAM,CAAC,EAAE,CAAC,CAAC;QACtC,MAAM,KAAK,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC/C,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC;IACpF,CAAC,CACF,CAAC;IAEF,GAAG,CAAC,YAAY,CACd,iBAAiB,EACjB,iDAAiD,EACjD,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,EAC5C,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QACf,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC;QAC3F,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACxB,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC7B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,EAAE,MAAM,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,cAAc,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,UAAU,EAAE,CAAC,EAAE,CAAC;IAC9J,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,kBAAkB,EAClB,6BAA6B,EAC7B,EAAE,EACF,KAAK,IAAI,EAAE;QACT,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QAC/B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,iBAAiB,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,uBAAuB,EAAE,CAAC,EAAE,CAAC;IACtI,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementNormalizer.d.ts","sourceRoot":"","sources":["../../src/server/elementNormalizer.ts"],"names":[],"mappings":"AAIA,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,GAAG,GAAG,GAAG,CA8C7C;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,CAExD"}
|