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.
- package/dist/assetServer.d.ts +34 -0
- package/dist/assetServer.js +168 -0
- package/dist/codeGenerator/componentDetector.d.ts +57 -0
- package/dist/codeGenerator/componentDetector.js +171 -0
- package/dist/codeGenerator/index.d.ts +77 -0
- package/dist/codeGenerator/index.js +184 -0
- package/dist/codeGenerator/jsxGenerator.d.ts +46 -0
- package/dist/codeGenerator/jsxGenerator.js +182 -0
- package/dist/codeGenerator/styleConverter.d.ts +95 -0
- package/dist/codeGenerator/styleConverter.js +306 -0
- package/dist/hints.d.ts +14 -0
- package/dist/hints.js +105 -0
- package/dist/hub.d.ts +9 -0
- package/dist/hub.js +252 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +146 -0
- package/dist/sandbox.d.ts +18 -0
- package/dist/sandbox.js +154 -0
- package/dist/toolRegistry.d.ts +19 -0
- package/dist/toolRegistry.js +729 -0
- package/dist/tools/captureScreenshot.d.ts +28 -0
- package/dist/tools/captureScreenshot.js +31 -0
- package/dist/tools/cloneNode.d.ts +43 -0
- package/dist/tools/cloneNode.js +46 -0
- package/dist/tools/createFrame.d.ts +157 -0
- package/dist/tools/createFrame.js +114 -0
- package/dist/tools/createInstance.d.ts +38 -0
- package/dist/tools/createInstance.js +41 -0
- package/dist/tools/createRectangle.d.ts +108 -0
- package/dist/tools/createRectangle.js +77 -0
- package/dist/tools/createText.d.ts +81 -0
- package/dist/tools/createText.js +67 -0
- package/dist/tools/deleteNode.d.ts +15 -0
- package/dist/tools/deleteNode.js +18 -0
- package/dist/tools/execute.d.ts +17 -0
- package/dist/tools/execute.js +68 -0
- package/dist/tools/getCurrentPage.d.ts +16 -0
- package/dist/tools/getCurrentPage.js +19 -0
- package/dist/tools/getDesignContext.d.ts +42 -0
- package/dist/tools/getDesignContext.js +55 -0
- package/dist/tools/getLocalComponents.d.ts +20 -0
- package/dist/tools/getLocalComponents.js +23 -0
- package/dist/tools/getNode.d.ts +30 -0
- package/dist/tools/getNode.js +33 -0
- package/dist/tools/getSelection.d.ts +10 -0
- package/dist/tools/getSelection.js +13 -0
- package/dist/tools/getStyles.d.ts +17 -0
- package/dist/tools/getStyles.js +20 -0
- package/dist/tools/getVariables.d.ts +26 -0
- package/dist/tools/getVariables.js +29 -0
- package/dist/tools/moveNode.d.ts +28 -0
- package/dist/tools/moveNode.js +31 -0
- package/dist/tools/openInEditor.d.ts +21 -0
- package/dist/tools/openInEditor.js +98 -0
- package/dist/tools/searchNodes.d.ts +30 -0
- package/dist/tools/searchNodes.js +46 -0
- package/dist/tools/searchTools.d.ts +28 -0
- package/dist/tools/searchTools.js +28 -0
- package/dist/tools/swapComponent.d.ts +23 -0
- package/dist/tools/swapComponent.js +26 -0
- package/dist/tools/updateNode.d.ts +194 -0
- package/dist/tools/updateNode.js +163 -0
- package/dist/types.d.ts +101 -0
- package/dist/types.js +1 -0
- package/dist/websocket.d.ts +30 -0
- package/dist/websocket.js +282 -0
- package/package.json +29 -0
package/dist/sandbox.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code execution sandbox using Node.js vm module.
|
|
3
|
+
* Provides a figma.* API that delegates to figmaWebSocket.sendCommand().
|
|
4
|
+
*/
|
|
5
|
+
import vm from 'node:vm';
|
|
6
|
+
import { figmaWebSocket } from './websocket.js';
|
|
7
|
+
const globalSetTimeout = setTimeout;
|
|
8
|
+
const MAX_LOG_CHARS = 10000;
|
|
9
|
+
const DEFAULT_TIMEOUT_MS = 60000;
|
|
10
|
+
/**
|
|
11
|
+
* Build the figma.* API object that wraps WebSocket commands.
|
|
12
|
+
* Each method strips returnScreenshot before sending.
|
|
13
|
+
*/
|
|
14
|
+
function buildFigmaApi() {
|
|
15
|
+
const makeCommand = (command, hasParams = true) => {
|
|
16
|
+
return async (params) => {
|
|
17
|
+
const cleanParams = { ...(hasParams ? params || {} : {}) };
|
|
18
|
+
delete cleanParams.returnScreenshot;
|
|
19
|
+
return figmaWebSocket.sendCommand(command, cleanParams);
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
// Create
|
|
24
|
+
create: makeCommand('create'),
|
|
25
|
+
createFrame: makeCommand('createFrame'),
|
|
26
|
+
createRectangle: makeCommand('createRectangle'),
|
|
27
|
+
createText: makeCommand('createText'),
|
|
28
|
+
createEllipse: makeCommand('createEllipse'),
|
|
29
|
+
createLine: makeCommand('createLine'),
|
|
30
|
+
createInstance: makeCommand('createInstance'),
|
|
31
|
+
cloneNode: makeCommand('cloneNode'),
|
|
32
|
+
// Read
|
|
33
|
+
getNode: makeCommand('getNode'),
|
|
34
|
+
getSelection: makeCommand('getSelection', false),
|
|
35
|
+
getCurrentPage: makeCommand('getCurrentPage'),
|
|
36
|
+
getStyles: makeCommand('getStyles'),
|
|
37
|
+
getLocalComponents: makeCommand('getLocalComponents'),
|
|
38
|
+
getVariables: makeCommand('getVariables'),
|
|
39
|
+
searchNodes: makeCommand('searchNodes'),
|
|
40
|
+
captureScreenshot: makeCommand('captureScreenshot'),
|
|
41
|
+
// Update
|
|
42
|
+
updateNode: makeCommand('updateNode'),
|
|
43
|
+
moveNode: makeCommand('moveNode'),
|
|
44
|
+
swapComponent: makeCommand('swapComponent'),
|
|
45
|
+
// Delete
|
|
46
|
+
deleteNode: makeCommand('deleteNode'),
|
|
47
|
+
// Tokens
|
|
48
|
+
createToken: makeCommand('createToken'),
|
|
49
|
+
bindToken: makeCommand('bindToken'),
|
|
50
|
+
// Components
|
|
51
|
+
toComponent: makeCommand('toComponent'),
|
|
52
|
+
createVariants: makeCommand('createVariants'),
|
|
53
|
+
addComponentProperty: makeCommand('addComponentProperty'),
|
|
54
|
+
setInstanceOverride: makeCommand('setInstanceOverride'),
|
|
55
|
+
// Accessibility
|
|
56
|
+
checkAccessibility: makeCommand('checkAccessibility'),
|
|
57
|
+
// FigJam-specific create methods
|
|
58
|
+
createSection: makeCommand('createSection'),
|
|
59
|
+
createSticky: makeCommand('createSticky'),
|
|
60
|
+
createShape: makeCommand('createShapeWithText'),
|
|
61
|
+
createConnector: makeCommand('createConnector'),
|
|
62
|
+
// Meta
|
|
63
|
+
getEditorType: makeCommand('getEditorType', false),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Format sandbox errors with contextual hints.
|
|
68
|
+
*/
|
|
69
|
+
export function formatSandboxError(error) {
|
|
70
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71
|
+
const stack = error instanceof Error ? error.stack || '' : '';
|
|
72
|
+
// Clean stack trace: remove node:vm internals and strip file paths
|
|
73
|
+
const cleanStack = stack
|
|
74
|
+
.split('\n')
|
|
75
|
+
.filter((line) => !line.includes('node:vm') && !line.includes('node:internal'))
|
|
76
|
+
.map((line) => line.replace(/\(\/[^)]+\)/g, '(<sandbox>)').replace(/at \/[^\s]+/g, 'at <sandbox>'))
|
|
77
|
+
.slice(0, 5)
|
|
78
|
+
.join('\n');
|
|
79
|
+
let hint = '';
|
|
80
|
+
if (message.includes('is not a function')) {
|
|
81
|
+
hint = '\nHint: Method not found. Use the search_tools tool to discover available figma.* methods.';
|
|
82
|
+
}
|
|
83
|
+
else if (message.includes('not connected') || message.includes('plugin')) {
|
|
84
|
+
hint = '\nHint: The Figma plugin is not connected. Please open the Figma desktop app and run the MCP plugin.';
|
|
85
|
+
}
|
|
86
|
+
else if (message.includes('timed out') || message.includes('Script execution timed out')) {
|
|
87
|
+
hint = '\nHint: Operation timed out. Try breaking into smaller operations or reducing the scope.';
|
|
88
|
+
}
|
|
89
|
+
else if (message.includes('Node not found') || message.includes('not found')) {
|
|
90
|
+
hint = '\nHint: Node not found. Use figma.getSelection() or figma.searchNodes() to find valid node IDs.';
|
|
91
|
+
}
|
|
92
|
+
return `${message}${cleanStack ? '\n' + cleanStack : ''}${hint}`;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Create a VM sandbox and run user code with the figma.* API.
|
|
96
|
+
*/
|
|
97
|
+
export async function createAndRunSandbox(code, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
98
|
+
const startTime = Date.now();
|
|
99
|
+
const logs = [];
|
|
100
|
+
let totalLogChars = 0;
|
|
101
|
+
const appendLog = (level, args) => {
|
|
102
|
+
if (totalLogChars >= MAX_LOG_CHARS)
|
|
103
|
+
return;
|
|
104
|
+
const msg = `[${level}] ${args.map((a) => (typeof a === 'object' ? JSON.stringify(a) : String(a))).join(' ')}`;
|
|
105
|
+
const truncated = msg.slice(0, MAX_LOG_CHARS - totalLogChars);
|
|
106
|
+
logs.push(truncated);
|
|
107
|
+
totalLogChars += truncated.length;
|
|
108
|
+
};
|
|
109
|
+
const frozenConsole = Object.freeze({
|
|
110
|
+
log: (...args) => appendLog('log', args),
|
|
111
|
+
warn: (...args) => appendLog('warn', args),
|
|
112
|
+
error: (...args) => appendLog('error', args),
|
|
113
|
+
});
|
|
114
|
+
const figmaApi = Object.freeze(buildFigmaApi());
|
|
115
|
+
const contextObject = {
|
|
116
|
+
figma: figmaApi,
|
|
117
|
+
console: frozenConsole,
|
|
118
|
+
JSON: Object.freeze({ parse: JSON.parse, stringify: JSON.stringify }),
|
|
119
|
+
Math: Object.freeze(Object.create(Math)),
|
|
120
|
+
parseInt,
|
|
121
|
+
parseFloat,
|
|
122
|
+
sleep: Object.freeze((ms) => new Promise(resolve => globalSetTimeout(resolve, Math.min(ms, 5000)))),
|
|
123
|
+
};
|
|
124
|
+
const context = vm.createContext(contextObject);
|
|
125
|
+
// Wrap code in async IIFE so top-level await works
|
|
126
|
+
const wrappedCode = `(async () => { ${code} })()`;
|
|
127
|
+
try {
|
|
128
|
+
const script = new vm.Script(wrappedCode, {
|
|
129
|
+
filename: 'execute.js',
|
|
130
|
+
});
|
|
131
|
+
// Run in context — timeout covers synchronous execution
|
|
132
|
+
const promise = script.runInContext(context, {
|
|
133
|
+
timeout: timeoutMs,
|
|
134
|
+
});
|
|
135
|
+
// Race against timeout for async operations
|
|
136
|
+
const result = await Promise.race([
|
|
137
|
+
promise,
|
|
138
|
+
new Promise((_, reject) => globalSetTimeout(() => reject(new Error('Script execution timed out')), timeoutMs)),
|
|
139
|
+
]);
|
|
140
|
+
return {
|
|
141
|
+
result: result === undefined ? null : result,
|
|
142
|
+
logs,
|
|
143
|
+
duration: Date.now() - startTime,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
return {
|
|
148
|
+
result: null,
|
|
149
|
+
logs,
|
|
150
|
+
error: formatSandboxError(error),
|
|
151
|
+
duration: Date.now() - startTime,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry of all figma.* methods available in the execute sandbox.
|
|
3
|
+
* Powers the search_tools tool for progressive API discovery.
|
|
4
|
+
*/
|
|
5
|
+
export interface ToolEntry {
|
|
6
|
+
name: string;
|
|
7
|
+
category: 'create' | 'read' | 'update' | 'delete' | 'tokens' | 'components' | 'accessibility' | 'figjam';
|
|
8
|
+
description: string;
|
|
9
|
+
parameters: Record<string, {
|
|
10
|
+
type: string;
|
|
11
|
+
description: string;
|
|
12
|
+
required?: boolean;
|
|
13
|
+
default?: unknown;
|
|
14
|
+
}>;
|
|
15
|
+
returns: string;
|
|
16
|
+
example: string;
|
|
17
|
+
}
|
|
18
|
+
export type DetailLevel = 'name' | 'description' | 'full';
|
|
19
|
+
export declare function searchRegistry(query: string, category?: 'create' | 'read' | 'update' | 'delete' | 'tokens' | 'components' | 'accessibility' | 'figjam', detailLevel?: DetailLevel): unknown[];
|