figma-preview-mcp 0.2.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 +220 -0
- package/dist/ast-enricher.d.ts +80 -0
- package/dist/ast-enricher.d.ts.map +1 -0
- package/dist/ast-enricher.js +318 -0
- package/dist/ast-enricher.js.map +1 -0
- package/dist/ast-module-splitter.d.ts +106 -0
- package/dist/ast-module-splitter.d.ts.map +1 -0
- package/dist/ast-module-splitter.js +325 -0
- package/dist/ast-module-splitter.js.map +1 -0
- package/dist/bounds-cache.d.ts +48 -0
- package/dist/bounds-cache.d.ts.map +1 -0
- package/dist/bounds-cache.js +71 -0
- package/dist/bounds-cache.js.map +1 -0
- package/dist/image-downloader.d.ts +20 -0
- package/dist/image-downloader.d.ts.map +1 -0
- package/dist/image-downloader.js +248 -0
- package/dist/image-downloader.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1554 -0
- package/dist/index.js.map +1 -0
- package/dist/layout-inference.d.ts +98 -0
- package/dist/layout-inference.d.ts.map +1 -0
- package/dist/layout-inference.js +486 -0
- package/dist/layout-inference.js.map +1 -0
- package/dist/layout-verifier.d.ts +91 -0
- package/dist/layout-verifier.d.ts.map +1 -0
- package/dist/layout-verifier.js +318 -0
- package/dist/layout-verifier.js.map +1 -0
- package/dist/parser.d.ts +120 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +310 -0
- package/dist/parser.js.map +1 -0
- package/dist/renderer.d.ts +8 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +639 -0
- package/dist/renderer.js.map +1 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +93 -0
- package/dist/server.js.map +1 -0
- package/package.json +55 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import http from 'http';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { generateHtml } from './renderer.js';
|
|
6
|
+
const CACHE_DIR = path.join(os.tmpdir(), 'figma-preview-mcp-images');
|
|
7
|
+
const DEFAULT_PORT = 3456;
|
|
8
|
+
const MAX_PORT_ATTEMPTS = 10;
|
|
9
|
+
// Singleton server instance
|
|
10
|
+
let serverInstance = null;
|
|
11
|
+
export async function getPreviewServer() {
|
|
12
|
+
if (serverInstance)
|
|
13
|
+
return serverInstance;
|
|
14
|
+
const state = {
|
|
15
|
+
nodeTree: null,
|
|
16
|
+
screenshotPath: null,
|
|
17
|
+
fileKey: '',
|
|
18
|
+
html: '<html><body><p style="color:#fff;font-family:sans-serif;padding:40px;">Loading...</p></body></html>',
|
|
19
|
+
};
|
|
20
|
+
const app = express();
|
|
21
|
+
// Serve preview page
|
|
22
|
+
app.get('/', (_req, res) => {
|
|
23
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
24
|
+
// Disable browser caching so new previews are always shown
|
|
25
|
+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
|
26
|
+
res.setHeader('Pragma', 'no-cache');
|
|
27
|
+
res.setHeader('Expires', '0');
|
|
28
|
+
res.send(state.html);
|
|
29
|
+
});
|
|
30
|
+
// Serve image assets from cache directory
|
|
31
|
+
app.use('/images', express.static(CACHE_DIR));
|
|
32
|
+
// Health check endpoint - includes current fileKey for debugging
|
|
33
|
+
app.get('/health', (_req, res) => {
|
|
34
|
+
res.json({
|
|
35
|
+
status: 'ok',
|
|
36
|
+
hasData: state.nodeTree !== null,
|
|
37
|
+
fileKey: state.fileKey || null
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
// Get design dimensions endpoint - returns the root node's width/height
|
|
41
|
+
app.get('/dimensions', (_req, res) => {
|
|
42
|
+
if (!state.nodeTree || !state.nodeTree.nodes || state.nodeTree.nodes.length === 0) {
|
|
43
|
+
res.status(404).json({ error: 'No design loaded' });
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// Get the first (root) node's layout
|
|
47
|
+
const rootNode = state.nodeTree.nodes[0];
|
|
48
|
+
const width = rootNode.layout?.width ?? 375;
|
|
49
|
+
const height = rootNode.layout?.height ?? 812;
|
|
50
|
+
res.json({ width, height });
|
|
51
|
+
});
|
|
52
|
+
const httpServer = http.createServer(app);
|
|
53
|
+
// Try to bind to a port, starting from DEFAULT_PORT
|
|
54
|
+
let boundPort = null;
|
|
55
|
+
for (let i = 0; i < MAX_PORT_ATTEMPTS; i++) {
|
|
56
|
+
const port = DEFAULT_PORT + i;
|
|
57
|
+
try {
|
|
58
|
+
await new Promise((resolve, reject) => {
|
|
59
|
+
httpServer.once('error', reject);
|
|
60
|
+
httpServer.listen(port, '127.0.0.1', () => {
|
|
61
|
+
httpServer.removeListener('error', reject);
|
|
62
|
+
resolve();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
boundPort = port;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
const error = err;
|
|
70
|
+
if (error.code === 'EADDRINUSE') {
|
|
71
|
+
console.error(`[figma-preview-mcp] Port ${port} is in use, trying next...`);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
throw err; // Re-throw non-EADDRINUSE errors
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (boundPort === null) {
|
|
78
|
+
throw new Error(`Could not find available port in range ${DEFAULT_PORT}-${DEFAULT_PORT + MAX_PORT_ATTEMPTS - 1}`);
|
|
79
|
+
}
|
|
80
|
+
serverInstance = {
|
|
81
|
+
port: boundPort,
|
|
82
|
+
updateData(nodeTree, screenshotPath, fileKey) {
|
|
83
|
+
state.nodeTree = nodeTree;
|
|
84
|
+
state.screenshotPath = screenshotPath;
|
|
85
|
+
state.fileKey = fileKey;
|
|
86
|
+
state.html = generateHtml({ nodeTree, screenshotPath, fileKey });
|
|
87
|
+
console.error(`[figma-preview-mcp] Preview updated for fileKey: ${fileKey}`);
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
console.error(`[figma-preview-mcp] HTTP server started on http://localhost:${boundPort}`);
|
|
91
|
+
return serverInstance;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "figma-preview-mcp",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "MCP Server for previewing Figma designs in browser with node inspector, layout AST generation, and pixel-level verification",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc && chmod +x dist/index.js",
|
|
9
|
+
"dev": "tsx src/index.ts",
|
|
10
|
+
"start": "node dist/index.js",
|
|
11
|
+
"prepare": "npm run build"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"mcp",
|
|
15
|
+
"model-context-protocol",
|
|
16
|
+
"figma",
|
|
17
|
+
"preview",
|
|
18
|
+
"design",
|
|
19
|
+
"figma-to-code",
|
|
20
|
+
"layout-ast",
|
|
21
|
+
"pixel-diff",
|
|
22
|
+
"ai",
|
|
23
|
+
"claude",
|
|
24
|
+
"cursor"
|
|
25
|
+
],
|
|
26
|
+
"author": "wangfeng",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
30
|
+
"express": "^4.18.2",
|
|
31
|
+
"js-yaml": "^4.1.0",
|
|
32
|
+
"open": "^10.1.0",
|
|
33
|
+
"pixelmatch": "^7.1.0",
|
|
34
|
+
"pngjs": "^7.0.0",
|
|
35
|
+
"puppeteer": "^24.40.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/express": "^4.17.21",
|
|
39
|
+
"@types/js-yaml": "^4.0.9",
|
|
40
|
+
"@types/node": "^20.0.0",
|
|
41
|
+
"@types/pngjs": "^6.0.5",
|
|
42
|
+
"tsx": "^4.7.0",
|
|
43
|
+
"typescript": "^5.3.0"
|
|
44
|
+
},
|
|
45
|
+
"bin": {
|
|
46
|
+
"figma-preview-mcp": "dist/index.js"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist/**/*",
|
|
50
|
+
"README.md"
|
|
51
|
+
],
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=18.0.0"
|
|
54
|
+
}
|
|
55
|
+
}
|