figma-code-agent-mcp 1.0.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 (67) hide show
  1. package/dist/assetServer.d.ts +34 -0
  2. package/dist/assetServer.js +168 -0
  3. package/dist/codeGenerator/componentDetector.d.ts +57 -0
  4. package/dist/codeGenerator/componentDetector.js +171 -0
  5. package/dist/codeGenerator/index.d.ts +77 -0
  6. package/dist/codeGenerator/index.js +184 -0
  7. package/dist/codeGenerator/jsxGenerator.d.ts +46 -0
  8. package/dist/codeGenerator/jsxGenerator.js +182 -0
  9. package/dist/codeGenerator/styleConverter.d.ts +95 -0
  10. package/dist/codeGenerator/styleConverter.js +306 -0
  11. package/dist/hints.d.ts +14 -0
  12. package/dist/hints.js +105 -0
  13. package/dist/hub.d.ts +9 -0
  14. package/dist/hub.js +252 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.js +146 -0
  17. package/dist/sandbox.d.ts +18 -0
  18. package/dist/sandbox.js +154 -0
  19. package/dist/toolRegistry.d.ts +19 -0
  20. package/dist/toolRegistry.js +729 -0
  21. package/dist/tools/captureScreenshot.d.ts +28 -0
  22. package/dist/tools/captureScreenshot.js +31 -0
  23. package/dist/tools/cloneNode.d.ts +43 -0
  24. package/dist/tools/cloneNode.js +46 -0
  25. package/dist/tools/createFrame.d.ts +157 -0
  26. package/dist/tools/createFrame.js +114 -0
  27. package/dist/tools/createInstance.d.ts +38 -0
  28. package/dist/tools/createInstance.js +41 -0
  29. package/dist/tools/createRectangle.d.ts +108 -0
  30. package/dist/tools/createRectangle.js +77 -0
  31. package/dist/tools/createText.d.ts +81 -0
  32. package/dist/tools/createText.js +67 -0
  33. package/dist/tools/deleteNode.d.ts +15 -0
  34. package/dist/tools/deleteNode.js +18 -0
  35. package/dist/tools/execute.d.ts +17 -0
  36. package/dist/tools/execute.js +68 -0
  37. package/dist/tools/getCurrentPage.d.ts +16 -0
  38. package/dist/tools/getCurrentPage.js +19 -0
  39. package/dist/tools/getDesignContext.d.ts +42 -0
  40. package/dist/tools/getDesignContext.js +55 -0
  41. package/dist/tools/getLocalComponents.d.ts +20 -0
  42. package/dist/tools/getLocalComponents.js +23 -0
  43. package/dist/tools/getNode.d.ts +30 -0
  44. package/dist/tools/getNode.js +33 -0
  45. package/dist/tools/getSelection.d.ts +10 -0
  46. package/dist/tools/getSelection.js +13 -0
  47. package/dist/tools/getStyles.d.ts +17 -0
  48. package/dist/tools/getStyles.js +20 -0
  49. package/dist/tools/getVariables.d.ts +26 -0
  50. package/dist/tools/getVariables.js +29 -0
  51. package/dist/tools/moveNode.d.ts +28 -0
  52. package/dist/tools/moveNode.js +31 -0
  53. package/dist/tools/openInEditor.d.ts +21 -0
  54. package/dist/tools/openInEditor.js +98 -0
  55. package/dist/tools/searchNodes.d.ts +30 -0
  56. package/dist/tools/searchNodes.js +46 -0
  57. package/dist/tools/searchTools.d.ts +28 -0
  58. package/dist/tools/searchTools.js +28 -0
  59. package/dist/tools/swapComponent.d.ts +23 -0
  60. package/dist/tools/swapComponent.js +26 -0
  61. package/dist/tools/updateNode.d.ts +194 -0
  62. package/dist/tools/updateNode.js +163 -0
  63. package/dist/types.d.ts +101 -0
  64. package/dist/types.js +1 -0
  65. package/dist/websocket.d.ts +30 -0
  66. package/dist/websocket.js +282 -0
  67. package/package.json +29 -0
@@ -0,0 +1,163 @@
1
+ import { figmaWebSocket } from '../websocket.js';
2
+ export const updateNodeSchema = {
3
+ name: 'update_node',
4
+ description: 'Update properties of an existing node in Figma. To copy image fills between nodes, use copyFillsFrom with the source node ID.',
5
+ inputSchema: {
6
+ type: 'object',
7
+ properties: {
8
+ nodeId: {
9
+ type: 'string',
10
+ description: 'ID of the node to update',
11
+ },
12
+ properties: {
13
+ type: 'object',
14
+ description: 'Properties to update on the node',
15
+ properties: {
16
+ name: {
17
+ type: 'string',
18
+ description: 'New name for the node',
19
+ },
20
+ x: {
21
+ type: 'number',
22
+ description: 'New X position',
23
+ },
24
+ y: {
25
+ type: 'number',
26
+ description: 'New Y position',
27
+ },
28
+ width: {
29
+ type: 'number',
30
+ description: 'New width (only for resizable nodes)',
31
+ },
32
+ height: {
33
+ type: 'number',
34
+ description: 'New height (only for resizable nodes)',
35
+ },
36
+ fillColor: {
37
+ type: 'object',
38
+ description: 'New fill color (RGB values from 0-1)',
39
+ properties: {
40
+ r: { type: 'number', minimum: 0, maximum: 1 },
41
+ g: { type: 'number', minimum: 0, maximum: 1 },
42
+ b: { type: 'number', minimum: 0, maximum: 1 },
43
+ a: { type: 'number', minimum: 0, maximum: 1, default: 1 },
44
+ },
45
+ required: ['r', 'g', 'b'],
46
+ },
47
+ opacity: {
48
+ type: 'number',
49
+ description: 'Node opacity (0-1)',
50
+ minimum: 0,
51
+ maximum: 1,
52
+ },
53
+ visible: {
54
+ type: 'boolean',
55
+ description: 'Whether the node is visible',
56
+ },
57
+ locked: {
58
+ type: 'boolean',
59
+ description: 'Whether the node is locked',
60
+ },
61
+ text: {
62
+ type: 'string',
63
+ description: 'New text content (only for text nodes)',
64
+ },
65
+ fontSize: {
66
+ type: 'number',
67
+ description: 'New font size (only for text nodes)',
68
+ },
69
+ fontWeight: {
70
+ type: 'string',
71
+ description: 'Font weight for text nodes (e.g., "Regular", "Bold", "Medium")',
72
+ },
73
+ cornerRadius: {
74
+ type: 'number',
75
+ description: 'Corner radius for frames and rectangles',
76
+ },
77
+ strokeColor: {
78
+ type: 'object',
79
+ description: 'Stroke/border color (RGB values from 0-1)',
80
+ properties: {
81
+ r: { type: 'number', minimum: 0, maximum: 1 },
82
+ g: { type: 'number', minimum: 0, maximum: 1 },
83
+ b: { type: 'number', minimum: 0, maximum: 1 },
84
+ a: { type: 'number', minimum: 0, maximum: 1, default: 1 },
85
+ },
86
+ required: ['r', 'g', 'b'],
87
+ },
88
+ strokeWeight: {
89
+ type: 'number',
90
+ description: 'Stroke/border width in pixels',
91
+ },
92
+ copyFillsFrom: {
93
+ type: 'string',
94
+ description: 'Node ID to copy fills from (including image fills). This copies the entire fills array from the source node.',
95
+ },
96
+ copyStrokesFrom: {
97
+ type: 'string',
98
+ description: 'Node ID to copy strokes from. This copies the entire strokes array from the source node.',
99
+ },
100
+ layoutMode: {
101
+ type: 'string',
102
+ enum: ['NONE', 'HORIZONTAL', 'VERTICAL'],
103
+ description: 'Auto-layout direction. HORIZONTAL = row, VERTICAL = column, NONE = disable auto-layout',
104
+ },
105
+ primaryAxisSizingMode: {
106
+ type: 'string',
107
+ enum: ['FIXED', 'AUTO'],
108
+ description: 'How the frame sizes itself along the primary axis (FIXED = fixed size, AUTO = hug contents)',
109
+ },
110
+ counterAxisSizingMode: {
111
+ type: 'string',
112
+ enum: ['FIXED', 'AUTO'],
113
+ description: 'How the frame sizes itself along the counter axis',
114
+ },
115
+ primaryAxisAlignItems: {
116
+ type: 'string',
117
+ enum: ['MIN', 'CENTER', 'MAX', 'SPACE_BETWEEN'],
118
+ description: 'Alignment of children along primary axis',
119
+ },
120
+ counterAxisAlignItems: {
121
+ type: 'string',
122
+ enum: ['MIN', 'CENTER', 'MAX', 'BASELINE'],
123
+ description: 'Alignment of children along counter axis',
124
+ },
125
+ itemSpacing: {
126
+ type: 'number',
127
+ description: 'Gap between children in pixels (for auto-layout frames)',
128
+ },
129
+ paddingTop: {
130
+ type: 'number',
131
+ description: 'Top padding in pixels (for auto-layout frames)',
132
+ },
133
+ paddingBottom: {
134
+ type: 'number',
135
+ description: 'Bottom padding in pixels (for auto-layout frames)',
136
+ },
137
+ paddingLeft: {
138
+ type: 'number',
139
+ description: 'Left padding in pixels (for auto-layout frames)',
140
+ },
141
+ paddingRight: {
142
+ type: 'number',
143
+ description: 'Right padding in pixels (for auto-layout frames)',
144
+ },
145
+ layoutWrap: {
146
+ type: 'string',
147
+ enum: ['NO_WRAP', 'WRAP'],
148
+ description: 'Whether children wrap to new lines (for auto-layout frames)',
149
+ },
150
+ },
151
+ },
152
+ returnScreenshot: {
153
+ type: 'boolean',
154
+ description: 'If true, returns a screenshot of the updated node for visual verification',
155
+ default: false,
156
+ },
157
+ },
158
+ required: ['nodeId', 'properties'],
159
+ },
160
+ };
161
+ export async function updateNode(params) {
162
+ return figmaWebSocket.sendCommand('updateNode', params);
163
+ }
@@ -0,0 +1,101 @@
1
+ export interface WebSocketMessage {
2
+ id: string;
3
+ command: string;
4
+ params: Record<string, unknown>;
5
+ }
6
+ export interface WebSocketResponse {
7
+ id: string;
8
+ success: boolean;
9
+ result?: unknown;
10
+ error?: string;
11
+ }
12
+ export interface PendingRequest {
13
+ resolve: (value: unknown) => void;
14
+ reject: (reason: Error) => void;
15
+ timeout: NodeJS.Timeout;
16
+ }
17
+ export interface FigmaNode {
18
+ id: string;
19
+ name: string;
20
+ type: string;
21
+ x?: number;
22
+ y?: number;
23
+ width?: number;
24
+ height?: number;
25
+ children?: FigmaNode[];
26
+ }
27
+ export interface CreateFrameParams {
28
+ name: string;
29
+ x?: number;
30
+ y?: number;
31
+ width?: number;
32
+ height?: number;
33
+ parentId?: string;
34
+ }
35
+ export interface CreateRectangleParams {
36
+ name?: string;
37
+ x?: number;
38
+ y?: number;
39
+ width?: number;
40
+ height?: number;
41
+ fillColor?: {
42
+ r: number;
43
+ g: number;
44
+ b: number;
45
+ a?: number;
46
+ };
47
+ parentId?: string;
48
+ }
49
+ export interface CreateTextParams {
50
+ text: string;
51
+ name?: string;
52
+ x?: number;
53
+ y?: number;
54
+ fontSize?: number;
55
+ fontFamily?: string;
56
+ fillColor?: {
57
+ r: number;
58
+ g: number;
59
+ b: number;
60
+ a?: number;
61
+ };
62
+ parentId?: string;
63
+ }
64
+ export interface CreateInstanceParams {
65
+ componentKey: string;
66
+ x?: number;
67
+ y?: number;
68
+ parentId?: string;
69
+ }
70
+ export interface UpdateNodeParams {
71
+ nodeId: string;
72
+ properties: {
73
+ name?: string;
74
+ x?: number;
75
+ y?: number;
76
+ width?: number;
77
+ height?: number;
78
+ fillColor?: {
79
+ r: number;
80
+ g: number;
81
+ b: number;
82
+ a?: number;
83
+ };
84
+ opacity?: number;
85
+ visible?: boolean;
86
+ locked?: boolean;
87
+ text?: string;
88
+ fontSize?: number;
89
+ };
90
+ }
91
+ export interface DeleteNodeParams {
92
+ nodeId: string;
93
+ }
94
+ export interface GetNodeParams {
95
+ nodeId: string;
96
+ }
97
+ export interface CaptureScreenshotParams {
98
+ nodeId: string;
99
+ scale?: number;
100
+ format?: 'PNG' | 'JPG' | 'SVG' | 'PDF';
101
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ export interface ClientInfo {
2
+ name: string;
3
+ version?: string;
4
+ projectPath?: string;
5
+ }
6
+ declare class FigmaWebSocketClient {
7
+ private ws;
8
+ private pendingRequests;
9
+ private isInitialized;
10
+ private mcpClientInfo;
11
+ private pluginEditorType;
12
+ private pluginConnected;
13
+ private sessionId;
14
+ private reconnectTimer;
15
+ initialize(): Promise<void>;
16
+ private readAuthToken;
17
+ private launchHub;
18
+ private tryConnect;
19
+ private setupListeners;
20
+ private scheduleReconnect;
21
+ private handleResponse;
22
+ sendCommand(command: string, params?: Record<string, unknown>): Promise<unknown>;
23
+ isConnected(): boolean;
24
+ getEditorType(): string | null;
25
+ setClientInfo(info: ClientInfo): void;
26
+ private sendClientInfo;
27
+ close(): void;
28
+ }
29
+ export declare const figmaWebSocket: FigmaWebSocketClient;
30
+ export {};
@@ -0,0 +1,282 @@
1
+ import WebSocket from 'ws';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import { spawn } from 'child_process';
4
+ import { fileURLToPath } from 'url';
5
+ import fs from 'node:fs';
6
+ import os from 'node:os';
7
+ import path from 'node:path';
8
+ const PORT = Number(process.env.FIGMA_HUB_PORT) || 3001;
9
+ const COMMAND_TIMEOUT = 30000; // 30 seconds
10
+ const HUB_CONNECT_TIMEOUT = 5000; // 5 seconds to wait for hub startup
11
+ const MAX_PENDING = 100;
12
+ class FigmaWebSocketClient {
13
+ ws = null;
14
+ pendingRequests = new Map();
15
+ isInitialized = false;
16
+ mcpClientInfo = null;
17
+ pluginEditorType = null;
18
+ pluginConnected = false;
19
+ sessionId = uuidv4();
20
+ reconnectTimer = null;
21
+ async initialize() {
22
+ if (this.isInitialized)
23
+ return;
24
+ this.isInitialized = true;
25
+ // Try connecting to an existing hub first
26
+ const connected = await this.tryConnect();
27
+ if (connected)
28
+ return;
29
+ // No hub running — launch one
30
+ await this.launchHub();
31
+ // Now connect to it
32
+ const retryConnected = await this.tryConnect();
33
+ if (!retryConnected) {
34
+ console.error('[WebSocket] Failed to connect to hub after launching it');
35
+ }
36
+ }
37
+ readAuthToken() {
38
+ try {
39
+ const tokenPath = path.join(os.homedir(), '.figma-mcp-hub-token');
40
+ return fs.readFileSync(tokenPath, 'utf-8').trim();
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
46
+ async launchHub() {
47
+ const lockPath = path.join(os.homedir(), '.figma-mcp-hub.lock');
48
+ // Check if another process is already launching the hub
49
+ try {
50
+ const lockContent = fs.readFileSync(lockPath, 'utf-8').trim();
51
+ const lockTime = parseInt(lockContent, 10);
52
+ // If lock is less than 10 seconds old, skip spawning
53
+ if (Date.now() - lockTime < 10000) {
54
+ console.error('[WebSocket] Hub launch already in progress, waiting...');
55
+ await new Promise(resolve => setTimeout(resolve, 2000));
56
+ return;
57
+ }
58
+ }
59
+ catch {
60
+ // No lock file, proceed
61
+ }
62
+ // Write lock file
63
+ try {
64
+ fs.writeFileSync(lockPath, String(Date.now()), { mode: 0o600 });
65
+ }
66
+ catch {
67
+ // If we can't write lock, proceed anyway
68
+ }
69
+ const __filename = fileURLToPath(import.meta.url);
70
+ const __dirname = path.dirname(__filename);
71
+ const hubPath = path.join(__dirname, 'hub.js');
72
+ console.error('[WebSocket] Launching hub...');
73
+ const child = spawn(process.execPath, [hubPath], {
74
+ detached: true,
75
+ stdio: 'ignore',
76
+ env: { ...process.env, FIGMA_HUB_PORT: String(PORT) },
77
+ });
78
+ child.unref();
79
+ // Wait for hub to be ready
80
+ await new Promise((resolve) => {
81
+ const start = Date.now();
82
+ const check = () => {
83
+ const probe = new WebSocket(`ws://localhost:${PORT}`);
84
+ probe.on('open', () => {
85
+ probe.close();
86
+ resolve();
87
+ });
88
+ probe.on('error', () => {
89
+ if (Date.now() - start > HUB_CONNECT_TIMEOUT) {
90
+ resolve(); // give up waiting, tryConnect will fail
91
+ }
92
+ else {
93
+ setTimeout(check, 200);
94
+ }
95
+ });
96
+ };
97
+ check();
98
+ });
99
+ // Clean up lock after hub is ready
100
+ try {
101
+ fs.unlinkSync(lockPath);
102
+ }
103
+ catch { }
104
+ }
105
+ tryConnect() {
106
+ return new Promise((resolve) => {
107
+ try {
108
+ const ws = new WebSocket(`ws://localhost:${PORT}`);
109
+ const timeout = setTimeout(() => {
110
+ ws.close();
111
+ resolve(false);
112
+ }, 3000);
113
+ ws.on('open', () => {
114
+ clearTimeout(timeout);
115
+ this.ws = ws;
116
+ this.setupListeners();
117
+ // Register as MCP server
118
+ const token = this.readAuthToken();
119
+ ws.send(JSON.stringify({
120
+ type: 'register',
121
+ role: 'mcp-server',
122
+ sessionId: this.sessionId,
123
+ token,
124
+ }));
125
+ // Send client info if already available
126
+ if (this.mcpClientInfo) {
127
+ this.sendClientInfo();
128
+ }
129
+ console.error(`[WebSocket] Connected to hub (session: ${this.sessionId.slice(0, 8)})`);
130
+ resolve(true);
131
+ });
132
+ ws.on('error', () => {
133
+ clearTimeout(timeout);
134
+ resolve(false);
135
+ });
136
+ }
137
+ catch {
138
+ resolve(false);
139
+ }
140
+ });
141
+ }
142
+ setupListeners() {
143
+ if (!this.ws)
144
+ return;
145
+ this.ws.on('message', (data) => {
146
+ try {
147
+ const parsed = JSON.parse(data.toString());
148
+ // Hub status update
149
+ if (parsed.type === 'hub-status') {
150
+ this.pluginConnected = parsed.pluginConnected ?? false;
151
+ this.pluginEditorType = parsed.editorType ?? null;
152
+ console.error(`[WebSocket] Plugin connected: ${this.pluginConnected}`);
153
+ return;
154
+ }
155
+ // Plugin disconnected notification
156
+ if (parsed.type === 'plugin-disconnected') {
157
+ this.pluginConnected = false;
158
+ this.pluginEditorType = null;
159
+ console.error('[WebSocket] Figma plugin disconnected');
160
+ // Reject all pending requests
161
+ for (const [id, request] of this.pendingRequests) {
162
+ clearTimeout(request.timeout);
163
+ request.reject(new Error('Figma plugin disconnected'));
164
+ this.pendingRequests.delete(id);
165
+ }
166
+ return;
167
+ }
168
+ // Plugin connected notification
169
+ if (parsed.type === 'plugin-connected') {
170
+ this.pluginConnected = true;
171
+ this.pluginEditorType = parsed.editorType ?? null;
172
+ console.error('[WebSocket] Figma plugin connected');
173
+ return;
174
+ }
175
+ // Command response
176
+ this.handleResponse(parsed);
177
+ }
178
+ catch (error) {
179
+ console.error('[WebSocket] Failed to parse message:', error);
180
+ }
181
+ });
182
+ this.ws.on('close', () => {
183
+ console.error('[WebSocket] Disconnected from hub');
184
+ this.ws = null;
185
+ this.pluginConnected = false;
186
+ // Reject pending requests
187
+ for (const [id, request] of this.pendingRequests) {
188
+ clearTimeout(request.timeout);
189
+ request.reject(new Error('Disconnected from hub'));
190
+ this.pendingRequests.delete(id);
191
+ }
192
+ // Try to reconnect
193
+ this.scheduleReconnect();
194
+ });
195
+ this.ws.on('error', (error) => {
196
+ console.error('[WebSocket] Error:', error.message);
197
+ });
198
+ }
199
+ scheduleReconnect() {
200
+ if (this.reconnectTimer)
201
+ return;
202
+ this.reconnectTimer = setTimeout(async () => {
203
+ this.reconnectTimer = null;
204
+ const connected = await this.tryConnect();
205
+ if (!connected) {
206
+ // Hub might have died — try launching a new one
207
+ await this.launchHub();
208
+ await this.tryConnect();
209
+ }
210
+ }, 2000);
211
+ }
212
+ handleResponse(response) {
213
+ const pending = this.pendingRequests.get(response.id);
214
+ if (!pending) {
215
+ // Not for us — ignore (another MCP server's response)
216
+ return;
217
+ }
218
+ clearTimeout(pending.timeout);
219
+ this.pendingRequests.delete(response.id);
220
+ if (response.success) {
221
+ pending.resolve(response.result);
222
+ }
223
+ else {
224
+ pending.reject(new Error(response.error || 'Unknown error'));
225
+ }
226
+ }
227
+ async sendCommand(command, params = {}) {
228
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
229
+ throw new Error('Not connected to hub. Please ensure the MCP server is running.');
230
+ }
231
+ if (!this.pluginConnected) {
232
+ throw new Error('Figma plugin is not connected. Please open the Figma plugin first.');
233
+ }
234
+ if (this.pendingRequests.size >= MAX_PENDING) {
235
+ throw new Error('Too many pending requests. Please wait for some to complete.');
236
+ }
237
+ const id = uuidv4();
238
+ const message = { id, command, params };
239
+ return new Promise((resolve, reject) => {
240
+ const timeout = setTimeout(() => {
241
+ this.pendingRequests.delete(id);
242
+ reject(new Error(`Command '${command}' timed out after ${COMMAND_TIMEOUT / 1000} seconds`));
243
+ }, COMMAND_TIMEOUT);
244
+ this.pendingRequests.set(id, { resolve, reject, timeout });
245
+ this.ws.send(JSON.stringify(message));
246
+ });
247
+ }
248
+ isConnected() {
249
+ return this.pluginConnected;
250
+ }
251
+ getEditorType() {
252
+ return this.pluginEditorType;
253
+ }
254
+ setClientInfo(info) {
255
+ this.mcpClientInfo = info;
256
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
257
+ this.sendClientInfo();
258
+ }
259
+ }
260
+ sendClientInfo() {
261
+ if (!this.ws || !this.mcpClientInfo)
262
+ return;
263
+ this.ws.send(JSON.stringify({
264
+ type: 'clientInfo',
265
+ clientInfo: this.mcpClientInfo,
266
+ }));
267
+ console.error(`[WebSocket] Sent client info: ${this.mcpClientInfo.name}`);
268
+ }
269
+ close() {
270
+ if (this.reconnectTimer) {
271
+ clearTimeout(this.reconnectTimer);
272
+ this.reconnectTimer = null;
273
+ }
274
+ if (this.ws) {
275
+ this.ws.close();
276
+ this.ws = null;
277
+ }
278
+ this.isInitialized = false;
279
+ }
280
+ }
281
+ // Singleton instance
282
+ export const figmaWebSocket = new FigmaWebSocketClient();
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "figma-code-agent-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for Figma integration via WebSocket bridge",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "figma-code-agent": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "start": "node dist/index.js",
16
+ "dev": "tsc --watch"
17
+ },
18
+ "dependencies": {
19
+ "@modelcontextprotocol/sdk": "^1.0.0",
20
+ "ws": "^8.16.0",
21
+ "uuid": "^9.0.1"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^20.10.0",
25
+ "@types/ws": "^8.5.10",
26
+ "@types/uuid": "^9.0.7",
27
+ "typescript": "^5.3.0"
28
+ }
29
+ }