@muloka/figma-console-mcp 0.5.0 → 0.5.2

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