@muloka/figma-console-mcp 0.5.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 (107) hide show
  1. package/README.md +2 -0
  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 +18 -2
  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 +34 -14
  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/package.json +1 -1
  19. package/dist/apps/design-system-dashboard/mcp-app.html +0 -521
  20. package/dist/apps/token-browser/mcp-app.html +0 -410
  21. package/dist/cloudflare/apps/design-system-dashboard/scoring/accessibility.js +0 -277
  22. package/dist/cloudflare/apps/design-system-dashboard/scoring/component-metadata.js +0 -357
  23. package/dist/cloudflare/apps/design-system-dashboard/scoring/consistency.js +0 -341
  24. package/dist/cloudflare/apps/design-system-dashboard/scoring/coverage.js +0 -230
  25. package/dist/cloudflare/apps/design-system-dashboard/scoring/engine.js +0 -92
  26. package/dist/cloudflare/apps/design-system-dashboard/scoring/naming-semantics.js +0 -308
  27. package/dist/cloudflare/apps/design-system-dashboard/scoring/token-architecture.js +0 -349
  28. package/dist/cloudflare/apps/design-system-dashboard/scoring/types.js +0 -40
  29. package/dist/cloudflare/apps/design-system-dashboard/server.js +0 -159
  30. package/dist/cloudflare/apps/token-browser/server.js +0 -136
  31. package/dist/cloudflare/browser-manager.js +0 -157
  32. package/dist/cloudflare/core/accessibility-tools.js +0 -361
  33. package/dist/cloudflare/core/annotation-tools.js +0 -230
  34. package/dist/cloudflare/core/cloud-websocket-connector.js +0 -419
  35. package/dist/cloudflare/core/cloud-websocket-relay.js +0 -198
  36. package/dist/cloudflare/core/comment-tools.js +0 -292
  37. package/dist/cloudflare/core/config.js +0 -153
  38. package/dist/cloudflare/core/console-monitor.js +0 -427
  39. package/dist/cloudflare/core/deep-component-tools.js +0 -128
  40. package/dist/cloudflare/core/design-code-tools.js +0 -2793
  41. package/dist/cloudflare/core/design-system-manifest.js +0 -265
  42. package/dist/cloudflare/core/design-system-tools.js +0 -882
  43. package/dist/cloudflare/core/diagnose-tool.js +0 -100
  44. package/dist/cloudflare/core/diff/changelog-formatter.js +0 -275
  45. package/dist/cloudflare/core/diff/diff-engine.js +0 -334
  46. package/dist/cloudflare/core/diff/property-compare.js +0 -36
  47. package/dist/cloudflare/core/diff/version-cache.js +0 -74
  48. package/dist/cloudflare/core/enrichment/enrichment-service.js +0 -374
  49. package/dist/cloudflare/core/enrichment/index.js +0 -7
  50. package/dist/cloudflare/core/enrichment/relationship-mapper.js +0 -351
  51. package/dist/cloudflare/core/enrichment/style-resolver.js +0 -346
  52. package/dist/cloudflare/core/figjam-tools.js +0 -547
  53. package/dist/cloudflare/core/figma-api.js +0 -509
  54. package/dist/cloudflare/core/figma-connector.js +0 -7
  55. package/dist/cloudflare/core/figma-reconstruction-spec.js +0 -402
  56. package/dist/cloudflare/core/figma-style-extractor.js +0 -311
  57. package/dist/cloudflare/core/figma-tools.js +0 -3353
  58. package/dist/cloudflare/core/identity.js +0 -96
  59. package/dist/cloudflare/core/library-tools.js +0 -580
  60. package/dist/cloudflare/core/logger.js +0 -53
  61. package/dist/cloudflare/core/port-discovery.js +0 -935
  62. package/dist/cloudflare/core/resolve-package-root.js +0 -11
  63. package/dist/cloudflare/core/slides-tools.js +0 -714
  64. package/dist/cloudflare/core/slot-tools.js +0 -334
  65. package/dist/cloudflare/core/snippet-injector.js +0 -96
  66. package/dist/cloudflare/core/tokens/alias-resolver.js +0 -205
  67. package/dist/cloudflare/core/tokens/config.js +0 -294
  68. package/dist/cloudflare/core/tokens/dialect.js +0 -232
  69. package/dist/cloudflare/core/tokens/figma-converter.js +0 -323
  70. package/dist/cloudflare/core/tokens/formatters/css-vars.js +0 -338
  71. package/dist/cloudflare/core/tokens/formatters/dtcg.js +0 -376
  72. package/dist/cloudflare/core/tokens/formatters/index.js +0 -45
  73. package/dist/cloudflare/core/tokens/formatters/json.js +0 -205
  74. package/dist/cloudflare/core/tokens/formatters/less.js +0 -4
  75. package/dist/cloudflare/core/tokens/formatters/scss.js +0 -258
  76. package/dist/cloudflare/core/tokens/formatters/stubs.js +0 -13
  77. package/dist/cloudflare/core/tokens/formatters/style-dictionary-v3.js +0 -213
  78. package/dist/cloudflare/core/tokens/formatters/tailwind-v3.js +0 -237
  79. package/dist/cloudflare/core/tokens/formatters/tailwind-v4.js +0 -335
  80. package/dist/cloudflare/core/tokens/formatters/tokens-studio.js +0 -256
  81. package/dist/cloudflare/core/tokens/formatters/ts-module.js +0 -198
  82. package/dist/cloudflare/core/tokens/index.js +0 -16
  83. package/dist/cloudflare/core/tokens/parsers/css-vars.js +0 -4
  84. package/dist/cloudflare/core/tokens/parsers/dtcg.js +0 -280
  85. package/dist/cloudflare/core/tokens/parsers/index.js +0 -138
  86. package/dist/cloudflare/core/tokens/parsers/json.js +0 -7
  87. package/dist/cloudflare/core/tokens/parsers/scss.js +0 -4
  88. package/dist/cloudflare/core/tokens/parsers/stubs.js +0 -20
  89. package/dist/cloudflare/core/tokens/parsers/style-dictionary-v3.js +0 -4
  90. package/dist/cloudflare/core/tokens/parsers/tailwind-v3.js +0 -4
  91. package/dist/cloudflare/core/tokens/parsers/tailwind-v4.js +0 -4
  92. package/dist/cloudflare/core/tokens/parsers/tokens-studio.js +0 -4
  93. package/dist/cloudflare/core/tokens/schemas.js +0 -152
  94. package/dist/cloudflare/core/tokens/transforms/color.js +0 -12
  95. package/dist/cloudflare/core/tokens/transforms/index.js +0 -29
  96. package/dist/cloudflare/core/tokens/transforms/size.js +0 -7
  97. package/dist/cloudflare/core/tokens/types.js +0 -18
  98. package/dist/cloudflare/core/tokens-tools.js +0 -1788
  99. package/dist/cloudflare/core/types/design-code.js +0 -4
  100. package/dist/cloudflare/core/types/enriched.js +0 -5
  101. package/dist/cloudflare/core/types/index.js +0 -4
  102. package/dist/cloudflare/core/variable-resolver.js +0 -85
  103. package/dist/cloudflare/core/version-tools.js +0 -1199
  104. package/dist/cloudflare/core/websocket-connector.js +0 -436
  105. package/dist/cloudflare/core/websocket-server.js +0 -1009
  106. package/dist/cloudflare/core/write-tools.js +0 -2506
  107. 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
- }