@muloka/figma-console-mcp 0.4.0 → 0.5.1

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 (109) hide show
  1. package/README.md +7 -3
  2. package/dist/core/websocket-connector.d.ts +2 -1
  3. package/dist/core/websocket-connector.d.ts.map +1 -1
  4. package/dist/core/websocket-connector.js +17 -2
  5. package/dist/core/websocket-connector.js.map +1 -1
  6. package/dist/core/websocket-server.d.ts +8 -1
  7. package/dist/core/websocket-server.d.ts.map +1 -1
  8. package/dist/core/websocket-server.js +19 -3
  9. package/dist/core/websocket-server.js.map +1 -1
  10. package/dist/core/write-tools.d.ts +3 -1
  11. package/dist/core/write-tools.d.ts.map +1 -1
  12. package/dist/core/write-tools.js +98 -23
  13. package/dist/core/write-tools.js.map +1 -1
  14. package/dist/local.d.ts +2 -1
  15. package/dist/local.d.ts.map +1 -1
  16. package/dist/local.js +5 -4
  17. package/dist/local.js.map +1 -1
  18. package/figma-desktop-bridge/code.js +57 -27
  19. package/figma-desktop-bridge/ui.html +10 -2
  20. package/package.json +1 -1
  21. package/dist/apps/design-system-dashboard/mcp-app.html +0 -521
  22. package/dist/apps/token-browser/mcp-app.html +0 -410
  23. package/dist/cloudflare/apps/design-system-dashboard/scoring/accessibility.js +0 -277
  24. package/dist/cloudflare/apps/design-system-dashboard/scoring/component-metadata.js +0 -357
  25. package/dist/cloudflare/apps/design-system-dashboard/scoring/consistency.js +0 -341
  26. package/dist/cloudflare/apps/design-system-dashboard/scoring/coverage.js +0 -230
  27. package/dist/cloudflare/apps/design-system-dashboard/scoring/engine.js +0 -92
  28. package/dist/cloudflare/apps/design-system-dashboard/scoring/naming-semantics.js +0 -308
  29. package/dist/cloudflare/apps/design-system-dashboard/scoring/token-architecture.js +0 -349
  30. package/dist/cloudflare/apps/design-system-dashboard/scoring/types.js +0 -40
  31. package/dist/cloudflare/apps/design-system-dashboard/server.js +0 -159
  32. package/dist/cloudflare/apps/token-browser/server.js +0 -136
  33. package/dist/cloudflare/browser-manager.js +0 -157
  34. package/dist/cloudflare/core/accessibility-tools.js +0 -361
  35. package/dist/cloudflare/core/annotation-tools.js +0 -230
  36. package/dist/cloudflare/core/cloud-websocket-connector.js +0 -419
  37. package/dist/cloudflare/core/cloud-websocket-relay.js +0 -198
  38. package/dist/cloudflare/core/comment-tools.js +0 -292
  39. package/dist/cloudflare/core/config.js +0 -153
  40. package/dist/cloudflare/core/console-monitor.js +0 -427
  41. package/dist/cloudflare/core/deep-component-tools.js +0 -128
  42. package/dist/cloudflare/core/design-code-tools.js +0 -2793
  43. package/dist/cloudflare/core/design-system-manifest.js +0 -265
  44. package/dist/cloudflare/core/design-system-tools.js +0 -882
  45. package/dist/cloudflare/core/diagnose-tool.js +0 -100
  46. package/dist/cloudflare/core/diff/changelog-formatter.js +0 -275
  47. package/dist/cloudflare/core/diff/diff-engine.js +0 -334
  48. package/dist/cloudflare/core/diff/property-compare.js +0 -36
  49. package/dist/cloudflare/core/diff/version-cache.js +0 -74
  50. package/dist/cloudflare/core/enrichment/enrichment-service.js +0 -374
  51. package/dist/cloudflare/core/enrichment/index.js +0 -7
  52. package/dist/cloudflare/core/enrichment/relationship-mapper.js +0 -351
  53. package/dist/cloudflare/core/enrichment/style-resolver.js +0 -346
  54. package/dist/cloudflare/core/figjam-tools.js +0 -547
  55. package/dist/cloudflare/core/figma-api.js +0 -509
  56. package/dist/cloudflare/core/figma-connector.js +0 -7
  57. package/dist/cloudflare/core/figma-reconstruction-spec.js +0 -402
  58. package/dist/cloudflare/core/figma-style-extractor.js +0 -311
  59. package/dist/cloudflare/core/figma-tools.js +0 -3353
  60. package/dist/cloudflare/core/identity.js +0 -96
  61. package/dist/cloudflare/core/library-tools.js +0 -580
  62. package/dist/cloudflare/core/logger.js +0 -53
  63. package/dist/cloudflare/core/port-discovery.js +0 -935
  64. package/dist/cloudflare/core/resolve-package-root.js +0 -11
  65. package/dist/cloudflare/core/slides-tools.js +0 -714
  66. package/dist/cloudflare/core/slot-tools.js +0 -334
  67. package/dist/cloudflare/core/snippet-injector.js +0 -96
  68. package/dist/cloudflare/core/tokens/alias-resolver.js +0 -205
  69. package/dist/cloudflare/core/tokens/config.js +0 -294
  70. package/dist/cloudflare/core/tokens/dialect.js +0 -232
  71. package/dist/cloudflare/core/tokens/figma-converter.js +0 -323
  72. package/dist/cloudflare/core/tokens/formatters/css-vars.js +0 -338
  73. package/dist/cloudflare/core/tokens/formatters/dtcg.js +0 -376
  74. package/dist/cloudflare/core/tokens/formatters/index.js +0 -45
  75. package/dist/cloudflare/core/tokens/formatters/json.js +0 -205
  76. package/dist/cloudflare/core/tokens/formatters/less.js +0 -4
  77. package/dist/cloudflare/core/tokens/formatters/scss.js +0 -258
  78. package/dist/cloudflare/core/tokens/formatters/stubs.js +0 -13
  79. package/dist/cloudflare/core/tokens/formatters/style-dictionary-v3.js +0 -213
  80. package/dist/cloudflare/core/tokens/formatters/tailwind-v3.js +0 -237
  81. package/dist/cloudflare/core/tokens/formatters/tailwind-v4.js +0 -335
  82. package/dist/cloudflare/core/tokens/formatters/tokens-studio.js +0 -256
  83. package/dist/cloudflare/core/tokens/formatters/ts-module.js +0 -198
  84. package/dist/cloudflare/core/tokens/index.js +0 -16
  85. package/dist/cloudflare/core/tokens/parsers/css-vars.js +0 -4
  86. package/dist/cloudflare/core/tokens/parsers/dtcg.js +0 -280
  87. package/dist/cloudflare/core/tokens/parsers/index.js +0 -138
  88. package/dist/cloudflare/core/tokens/parsers/json.js +0 -7
  89. package/dist/cloudflare/core/tokens/parsers/scss.js +0 -4
  90. package/dist/cloudflare/core/tokens/parsers/stubs.js +0 -20
  91. package/dist/cloudflare/core/tokens/parsers/style-dictionary-v3.js +0 -4
  92. package/dist/cloudflare/core/tokens/parsers/tailwind-v3.js +0 -4
  93. package/dist/cloudflare/core/tokens/parsers/tailwind-v4.js +0 -4
  94. package/dist/cloudflare/core/tokens/parsers/tokens-studio.js +0 -4
  95. package/dist/cloudflare/core/tokens/schemas.js +0 -152
  96. package/dist/cloudflare/core/tokens/transforms/color.js +0 -12
  97. package/dist/cloudflare/core/tokens/transforms/index.js +0 -29
  98. package/dist/cloudflare/core/tokens/transforms/size.js +0 -7
  99. package/dist/cloudflare/core/tokens/types.js +0 -18
  100. package/dist/cloudflare/core/tokens-tools.js +0 -1788
  101. package/dist/cloudflare/core/types/design-code.js +0 -4
  102. package/dist/cloudflare/core/types/enriched.js +0 -5
  103. package/dist/cloudflare/core/types/index.js +0 -4
  104. package/dist/cloudflare/core/variable-resolver.js +0 -85
  105. package/dist/cloudflare/core/version-tools.js +0 -1199
  106. package/dist/cloudflare/core/websocket-connector.js +0 -436
  107. package/dist/cloudflare/core/websocket-server.js +0 -1009
  108. package/dist/cloudflare/core/write-tools.js +0 -2506
  109. package/dist/cloudflare/index.js +0 -3010
@@ -1,292 +0,0 @@
1
- /**
2
- * Figma Comments MCP Tools
3
- * Tools for getting, posting, and deleting comments on Figma files via REST API.
4
- * Works in both local and Cloudflare Workers modes — no Plugin API dependency.
5
- */
6
- import { z } from "zod";
7
- import { extractFileKey } from "./figma-api.js";
8
- import { createChildLogger } from "./logger.js";
9
- const logger = createChildLogger({ component: "comment-tools" });
10
- // ============================================================================
11
- // Tool Registration
12
- // ============================================================================
13
- export function registerCommentTools(server, getFigmaAPI, getCurrentUrl, options) {
14
- // -----------------------------------------------------------------------
15
- // Tool: figma_get_comments
16
- // -----------------------------------------------------------------------
17
- server.tool("figma_get_comments", "Get comments on a Figma file. Returns comment threads with author, message, timestamps, and pinned node locations. Use include_resolved to also see resolved comments.", {
18
- fileUrl: z
19
- .string()
20
- .url()
21
- .optional()
22
- .describe("Figma file URL. Uses current URL if omitted."),
23
- as_md: z
24
- .boolean()
25
- .optional()
26
- .default(false)
27
- .describe("Return comment message bodies as markdown. Default: false"),
28
- include_resolved: z
29
- .boolean()
30
- .optional()
31
- .default(false)
32
- .describe("Include resolved (completed) comment threads. Default: false (only active comments)"),
33
- }, async ({ fileUrl, as_md = false, include_resolved = false }) => {
34
- try {
35
- const url = fileUrl || getCurrentUrl();
36
- if (!url) {
37
- return {
38
- content: [
39
- {
40
- type: "text",
41
- text: JSON.stringify({
42
- error: "no_file_url",
43
- message: "No Figma file URL available. Pass the fileUrl parameter or ensure the Desktop Bridge plugin is open in Figma.",
44
- }),
45
- },
46
- ],
47
- isError: true,
48
- };
49
- }
50
- const fileKey = extractFileKey(url);
51
- if (!fileKey) {
52
- return {
53
- content: [
54
- {
55
- type: "text",
56
- text: JSON.stringify({
57
- error: "invalid_url",
58
- message: `Invalid Figma URL: ${url}`,
59
- }),
60
- },
61
- ],
62
- isError: true,
63
- };
64
- }
65
- logger.info({ fileKey, as_md, include_resolved }, "Fetching comments");
66
- const api = await getFigmaAPI();
67
- const response = await api.getComments(fileKey, { as_md });
68
- const allComments = response.comments || [];
69
- // Filter out resolved comments unless explicitly requested
70
- const comments = include_resolved
71
- ? allComments
72
- : allComments.filter((c) => !c.resolved_at);
73
- const result = {
74
- comments,
75
- summary: {
76
- total: allComments.length,
77
- active: allComments.filter((c) => !c.resolved_at).length,
78
- resolved: allComments.filter((c) => c.resolved_at).length,
79
- returned: comments.length,
80
- },
81
- };
82
- return {
83
- content: [
84
- {
85
- type: "text",
86
- text: JSON.stringify(result),
87
- },
88
- ],
89
- };
90
- }
91
- catch (error) {
92
- const message = error instanceof Error ? error.message : String(error);
93
- logger.error({ error }, "Failed to get comments");
94
- return {
95
- content: [
96
- {
97
- type: "text",
98
- text: JSON.stringify({
99
- error: "get_comments_failed",
100
- message: `Cannot get comments. ${message}`,
101
- }),
102
- },
103
- ],
104
- isError: true,
105
- };
106
- }
107
- });
108
- // -----------------------------------------------------------------------
109
- // Tool: figma_post_comment
110
- // -----------------------------------------------------------------------
111
- server.tool("figma_post_comment", "Post a comment on a Figma file, optionally pinned to a specific design node. Use after figma_check_design_parity to notify designers of drift when code is the canonical source. Supports replies to existing comment threads. Limitation: @mentions are a Figma UI-only feature — including '@name' in the message renders as plain text, not a clickable mention tag, and does not trigger Figma notifications.", {
112
- fileUrl: z
113
- .string()
114
- .url()
115
- .optional()
116
- .describe("Figma file URL. Uses current URL if omitted."),
117
- message: z
118
- .string()
119
- .describe("The comment message text. Supports basic formatting."),
120
- node_id: z
121
- .string()
122
- .optional()
123
- .describe("Node ID to pin the comment to (e.g., '695:313'). Comment appears on that element in Figma."),
124
- x: z
125
- .number()
126
- .optional()
127
- .describe("X coordinate for comment placement (absolute canvas position). Used with node_id."),
128
- y: z
129
- .number()
130
- .optional()
131
- .describe("Y coordinate for comment placement (absolute canvas position). Used with node_id."),
132
- reply_to_comment_id: z
133
- .string()
134
- .optional()
135
- .describe("ID of an existing comment to reply to. Creates a threaded reply instead of a new top-level comment."),
136
- }, async ({ fileUrl, message, node_id, x, y, reply_to_comment_id }) => {
137
- try {
138
- const url = fileUrl || getCurrentUrl();
139
- if (!url) {
140
- return {
141
- content: [
142
- {
143
- type: "text",
144
- text: JSON.stringify({
145
- error: "no_file_url",
146
- message: "No Figma file URL available. Pass the fileUrl parameter or ensure the Desktop Bridge plugin is open in Figma.",
147
- }),
148
- },
149
- ],
150
- isError: true,
151
- };
152
- }
153
- const fileKey = extractFileKey(url);
154
- if (!fileKey) {
155
- return {
156
- content: [
157
- {
158
- type: "text",
159
- text: JSON.stringify({
160
- error: "invalid_url",
161
- message: `Invalid Figma URL: ${url}`,
162
- }),
163
- },
164
- ],
165
- isError: true,
166
- };
167
- }
168
- logger.info({ fileKey, node_id, reply_to_comment_id }, "Posting comment");
169
- const api = await getFigmaAPI();
170
- // Build client_meta for pinning to a node/position
171
- // Figma API requires node_offset when node_id is present — default to (0,0) if not specified
172
- let clientMeta;
173
- if (node_id) {
174
- clientMeta = {
175
- node_id,
176
- node_offset: { x: x ?? 0, y: y ?? 0 },
177
- };
178
- }
179
- const result = await api.postComment(fileKey, message, clientMeta, reply_to_comment_id);
180
- return {
181
- content: [
182
- {
183
- type: "text",
184
- text: JSON.stringify({
185
- success: true,
186
- comment: {
187
- id: result.id,
188
- message: result.message,
189
- created_at: result.created_at,
190
- user: result.user,
191
- client_meta: result.client_meta,
192
- order_id: result.order_id,
193
- },
194
- }),
195
- },
196
- ],
197
- };
198
- }
199
- catch (error) {
200
- const message_text = error instanceof Error ? error.message : String(error);
201
- logger.error({ error }, "Failed to post comment");
202
- return {
203
- content: [
204
- {
205
- type: "text",
206
- text: JSON.stringify({
207
- error: "post_comment_failed",
208
- message: `Cannot post comment. ${message_text}`,
209
- }),
210
- },
211
- ],
212
- isError: true,
213
- };
214
- }
215
- });
216
- // -----------------------------------------------------------------------
217
- // Tool: figma_delete_comment
218
- // -----------------------------------------------------------------------
219
- server.tool("figma_delete_comment", "Delete a comment from a Figma file by its comment ID. Use figma_get_comments to find comment IDs first.", {
220
- fileUrl: z
221
- .string()
222
- .url()
223
- .optional()
224
- .describe("Figma file URL. Uses current URL if omitted."),
225
- comment_id: z
226
- .string()
227
- .describe("The ID of the comment to delete. Get IDs from figma_get_comments."),
228
- }, async ({ fileUrl, comment_id }) => {
229
- try {
230
- const url = fileUrl || getCurrentUrl();
231
- if (!url) {
232
- return {
233
- content: [
234
- {
235
- type: "text",
236
- text: JSON.stringify({
237
- error: "no_file_url",
238
- message: "No Figma file URL available. Pass the fileUrl parameter or ensure the Desktop Bridge plugin is open in Figma.",
239
- }),
240
- },
241
- ],
242
- isError: true,
243
- };
244
- }
245
- const fileKey = extractFileKey(url);
246
- if (!fileKey) {
247
- return {
248
- content: [
249
- {
250
- type: "text",
251
- text: JSON.stringify({
252
- error: "invalid_url",
253
- message: `Invalid Figma URL: ${url}`,
254
- }),
255
- },
256
- ],
257
- isError: true,
258
- };
259
- }
260
- logger.info({ fileKey, comment_id }, "Deleting comment");
261
- const api = await getFigmaAPI();
262
- await api.deleteComment(fileKey, comment_id);
263
- return {
264
- content: [
265
- {
266
- type: "text",
267
- text: JSON.stringify({
268
- success: true,
269
- deleted_comment_id: comment_id,
270
- }),
271
- },
272
- ],
273
- };
274
- }
275
- catch (error) {
276
- const message = error instanceof Error ? error.message : String(error);
277
- logger.error({ error }, "Failed to delete comment");
278
- return {
279
- content: [
280
- {
281
- type: "text",
282
- text: JSON.stringify({
283
- error: "delete_comment_failed",
284
- message: `Cannot delete comment. ${message}`,
285
- }),
286
- },
287
- ],
288
- isError: true,
289
- };
290
- }
291
- });
292
- }
@@ -1,153 +0,0 @@
1
- /**
2
- * Configuration management for Figma Console MCP server
3
- */
4
- import { readFileSync, existsSync } from 'fs';
5
- import { homedir } from 'os';
6
- import { join } from 'path';
7
- /**
8
- * Auto-detect server mode based on environment
9
- */
10
- function detectMode() {
11
- // If running in Workers environment, return cloudflare
12
- if (typeof globalThis !== 'undefined' && 'caches' in globalThis) {
13
- return 'cloudflare';
14
- }
15
- // Explicit env var override
16
- const modeEnv = process.env.FIGMA_MCP_MODE?.toLowerCase();
17
- if (modeEnv === 'local' || modeEnv === 'cloudflare') {
18
- return modeEnv;
19
- }
20
- // Default to local for Node.js environments
21
- return 'local';
22
- }
23
- /**
24
- * Default configuration values
25
- */
26
- const DEFAULT_CONFIG = {
27
- mode: detectMode(),
28
- browser: {
29
- headless: false,
30
- args: [
31
- '--disable-blink-features=AutomationControlled',
32
- '--disable-dev-shm-usage',
33
- '--no-sandbox', // Note: Only use in trusted environments
34
- ],
35
- },
36
- console: {
37
- bufferSize: 1000,
38
- filterLevels: ['log', 'info', 'warn', 'error', 'debug'],
39
- truncation: {
40
- maxStringLength: 500,
41
- maxArrayLength: 10,
42
- maxObjectDepth: 3,
43
- removeDuplicates: true,
44
- },
45
- },
46
- screenshots: {
47
- defaultFormat: 'png',
48
- quality: 90,
49
- storePath: join(process.env.TMPDIR || '/tmp', 'figma-console-mcp', 'screenshots'),
50
- },
51
- };
52
- /**
53
- * Possible config file locations (checked in order)
54
- */
55
- const CONFIG_PATHS = [
56
- // Environment variable override
57
- process.env.FIGMA_CONSOLE_CONFIG,
58
- // Project-local config
59
- join(process.cwd(), '.figma-console-mcp.json'),
60
- join(process.cwd(), 'figma-console-mcp.json'),
61
- // User home config
62
- join(homedir(), '.config', 'figma-console-mcp', 'config.json'),
63
- join(homedir(), '.figma-console-mcp.json'),
64
- ].filter((path) => path !== undefined);
65
- /**
66
- * Load configuration from file or use defaults
67
- */
68
- export function loadConfig() {
69
- // Try to load from config file
70
- for (const configPath of CONFIG_PATHS) {
71
- if (existsSync(configPath)) {
72
- try {
73
- const fileContent = readFileSync(configPath, 'utf-8');
74
- const userConfig = JSON.parse(fileContent);
75
- // Deep merge with defaults
76
- const config = mergeConfig(DEFAULT_CONFIG, userConfig);
77
- return config;
78
- }
79
- catch (error) {
80
- console.error(`Failed to load config from ${configPath}:`, error);
81
- // Continue to next config path
82
- }
83
- }
84
- }
85
- // No config file found, use defaults
86
- return DEFAULT_CONFIG;
87
- }
88
- /**
89
- * Deep merge two configuration objects
90
- */
91
- function mergeConfig(defaults, overrides) {
92
- return {
93
- mode: overrides.mode || defaults.mode,
94
- browser: {
95
- ...defaults.browser,
96
- ...(overrides.browser || {}),
97
- },
98
- console: {
99
- ...defaults.console,
100
- ...(overrides.console || {}),
101
- truncation: {
102
- ...defaults.console.truncation,
103
- ...(overrides.console?.truncation || {}),
104
- },
105
- },
106
- screenshots: {
107
- ...defaults.screenshots,
108
- ...(overrides.screenshots || {}),
109
- },
110
- };
111
- }
112
- /**
113
- * Validate configuration
114
- */
115
- export function validateConfig(config) {
116
- // Validate browser config
117
- if (!Array.isArray(config.browser.args)) {
118
- throw new Error('browser.args must be an array');
119
- }
120
- // Validate console config
121
- if (config.console.bufferSize <= 0) {
122
- throw new Error('console.bufferSize must be positive');
123
- }
124
- if (!Array.isArray(config.console.filterLevels)) {
125
- throw new Error('console.filterLevels must be an array');
126
- }
127
- // Validate truncation config
128
- const { truncation } = config.console;
129
- if (truncation.maxStringLength <= 0) {
130
- throw new Error('console.truncation.maxStringLength must be positive');
131
- }
132
- if (truncation.maxArrayLength <= 0) {
133
- throw new Error('console.truncation.maxArrayLength must be positive');
134
- }
135
- if (truncation.maxObjectDepth <= 0) {
136
- throw new Error('console.truncation.maxObjectDepth must be positive');
137
- }
138
- // Validate screenshot config
139
- if (!['png', 'jpeg'].includes(config.screenshots.defaultFormat)) {
140
- throw new Error('screenshots.defaultFormat must be "png" or "jpeg"');
141
- }
142
- if (config.screenshots.quality < 0 || config.screenshots.quality > 100) {
143
- throw new Error('screenshots.quality must be between 0 and 100');
144
- }
145
- }
146
- /**
147
- * Get configuration with validation
148
- */
149
- export function getConfig() {
150
- const config = loadConfig();
151
- validateConfig(config);
152
- return config;
153
- }