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.
Files changed (42) hide show
  1. package/README.md +220 -0
  2. package/dist/ast-enricher.d.ts +80 -0
  3. package/dist/ast-enricher.d.ts.map +1 -0
  4. package/dist/ast-enricher.js +318 -0
  5. package/dist/ast-enricher.js.map +1 -0
  6. package/dist/ast-module-splitter.d.ts +106 -0
  7. package/dist/ast-module-splitter.d.ts.map +1 -0
  8. package/dist/ast-module-splitter.js +325 -0
  9. package/dist/ast-module-splitter.js.map +1 -0
  10. package/dist/bounds-cache.d.ts +48 -0
  11. package/dist/bounds-cache.d.ts.map +1 -0
  12. package/dist/bounds-cache.js +71 -0
  13. package/dist/bounds-cache.js.map +1 -0
  14. package/dist/image-downloader.d.ts +20 -0
  15. package/dist/image-downloader.d.ts.map +1 -0
  16. package/dist/image-downloader.js +248 -0
  17. package/dist/image-downloader.js.map +1 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +1554 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/layout-inference.d.ts +98 -0
  23. package/dist/layout-inference.d.ts.map +1 -0
  24. package/dist/layout-inference.js +486 -0
  25. package/dist/layout-inference.js.map +1 -0
  26. package/dist/layout-verifier.d.ts +91 -0
  27. package/dist/layout-verifier.d.ts.map +1 -0
  28. package/dist/layout-verifier.js +318 -0
  29. package/dist/layout-verifier.js.map +1 -0
  30. package/dist/parser.d.ts +120 -0
  31. package/dist/parser.d.ts.map +1 -0
  32. package/dist/parser.js +310 -0
  33. package/dist/parser.js.map +1 -0
  34. package/dist/renderer.d.ts +8 -0
  35. package/dist/renderer.d.ts.map +1 -0
  36. package/dist/renderer.js +639 -0
  37. package/dist/renderer.js.map +1 -0
  38. package/dist/server.d.ts +7 -0
  39. package/dist/server.d.ts.map +1 -0
  40. package/dist/server.js +93 -0
  41. package/dist/server.js.map +1 -0
  42. 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
+ }