@mp3wizard/figma-console-mcp 1.17.3 → 1.19.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 (68) hide show
  1. package/README.md +13 -12
  2. package/dist/cloudflare/core/annotation-tools.js +230 -0
  3. package/dist/cloudflare/core/cloud-websocket-connector.js +93 -0
  4. package/dist/cloudflare/core/deep-component-tools.js +128 -0
  5. package/dist/cloudflare/core/design-code-tools.js +65 -7
  6. package/dist/cloudflare/core/enrichment/enrichment-service.js +108 -12
  7. package/dist/cloudflare/core/figjam-tools.js +485 -0
  8. package/dist/cloudflare/core/figma-api.js +7 -4
  9. package/dist/cloudflare/core/figma-desktop-connector.js +108 -0
  10. package/dist/cloudflare/core/figma-tools.js +445 -55
  11. package/dist/cloudflare/core/port-discovery.js +88 -0
  12. package/dist/cloudflare/core/resolve-package-root.js +11 -0
  13. package/dist/cloudflare/core/slides-tools.js +607 -0
  14. package/dist/cloudflare/core/websocket-connector.js +93 -0
  15. package/dist/cloudflare/core/websocket-server.js +18 -9
  16. package/dist/cloudflare/index.js +164 -41
  17. package/dist/core/annotation-tools.d.ts +14 -0
  18. package/dist/core/annotation-tools.d.ts.map +1 -0
  19. package/dist/core/annotation-tools.js +231 -0
  20. package/dist/core/annotation-tools.js.map +1 -0
  21. package/dist/core/deep-component-tools.d.ts +14 -0
  22. package/dist/core/deep-component-tools.d.ts.map +1 -0
  23. package/dist/core/deep-component-tools.js +129 -0
  24. package/dist/core/deep-component-tools.js.map +1 -0
  25. package/dist/core/design-code-tools.d.ts.map +1 -1
  26. package/dist/core/design-code-tools.js +65 -7
  27. package/dist/core/design-code-tools.js.map +1 -1
  28. package/dist/core/enrichment/enrichment-service.d.ts.map +1 -1
  29. package/dist/core/enrichment/enrichment-service.js +108 -12
  30. package/dist/core/enrichment/enrichment-service.js.map +1 -1
  31. package/dist/core/figma-api.d.ts +1 -1
  32. package/dist/core/figma-api.d.ts.map +1 -1
  33. package/dist/core/figma-api.js +7 -4
  34. package/dist/core/figma-api.js.map +1 -1
  35. package/dist/core/figma-connector.d.ts +5 -0
  36. package/dist/core/figma-connector.d.ts.map +1 -1
  37. package/dist/core/figma-desktop-connector.d.ts +20 -0
  38. package/dist/core/figma-desktop-connector.d.ts.map +1 -1
  39. package/dist/core/figma-desktop-connector.js +83 -0
  40. package/dist/core/figma-desktop-connector.js.map +1 -1
  41. package/dist/core/figma-tools.d.ts.map +1 -1
  42. package/dist/core/figma-tools.js +355 -26
  43. package/dist/core/figma-tools.js.map +1 -1
  44. package/dist/core/port-discovery.d.ts +21 -0
  45. package/dist/core/port-discovery.d.ts.map +1 -1
  46. package/dist/core/port-discovery.js +88 -0
  47. package/dist/core/port-discovery.js.map +1 -1
  48. package/dist/core/resolve-package-root.d.ts +2 -0
  49. package/dist/core/resolve-package-root.d.ts.map +1 -0
  50. package/dist/core/resolve-package-root.js +12 -0
  51. package/dist/core/resolve-package-root.js.map +1 -0
  52. package/dist/core/types/design-code.d.ts +1 -0
  53. package/dist/core/types/design-code.d.ts.map +1 -1
  54. package/dist/core/websocket-connector.d.ts +5 -0
  55. package/dist/core/websocket-connector.d.ts.map +1 -1
  56. package/dist/core/websocket-connector.js +18 -0
  57. package/dist/core/websocket-connector.js.map +1 -1
  58. package/dist/core/websocket-server.d.ts.map +1 -1
  59. package/dist/core/websocket-server.js +7 -9
  60. package/dist/core/websocket-server.js.map +1 -1
  61. package/dist/local.d.ts +6 -0
  62. package/dist/local.d.ts.map +1 -1
  63. package/dist/local.js +58 -1
  64. package/dist/local.js.map +1 -1
  65. package/figma-desktop-bridge/code.js +906 -4
  66. package/figma-desktop-bridge/ui-full.html +80 -0
  67. package/figma-desktop-bridge/ui.html +82 -0
  68. package/package.json +1 -1
@@ -134,6 +134,24 @@ export class WebSocketConnector {
134
134
  async setNodeDescription(nodeId, description, descriptionMarkdown) {
135
135
  return this.wsServer.sendCommand('SET_NODE_DESCRIPTION', { nodeId, description, descriptionMarkdown });
136
136
  }
137
+ // ============================================================================
138
+ // Annotation operations
139
+ // ============================================================================
140
+ async getAnnotations(nodeId, includeChildren, depth) {
141
+ return this.wsServer.sendCommand('GET_ANNOTATIONS', { nodeId, includeChildren, depth }, 10000);
142
+ }
143
+ async setAnnotations(nodeId, annotations, mode) {
144
+ return this.wsServer.sendCommand('SET_ANNOTATIONS', { nodeId, annotations, mode: mode || 'replace' });
145
+ }
146
+ async getAnnotationCategories() {
147
+ return this.wsServer.sendCommand('GET_ANNOTATION_CATEGORIES', {}, 5000);
148
+ }
149
+ async deepGetComponent(nodeId, depth) {
150
+ return this.wsServer.sendCommand('DEEP_GET_COMPONENT', { nodeId, depth: depth || 10 }, 30000);
151
+ }
152
+ async analyzeComponentSet(nodeId) {
153
+ return this.wsServer.sendCommand('ANALYZE_COMPONENT_SET', { nodeId }, 30000);
154
+ }
137
155
  async addComponentProperty(nodeId, propertyName, type, defaultValue, options) {
138
156
  const params = { nodeId, propertyName, propertyType: type, defaultValue };
139
157
  if (options?.preferredValues)
@@ -248,6 +266,81 @@ export class WebSocketConnector {
248
266
  return this.wsServer.sendCommand('LINT_DESIGN', params, 120000);
249
267
  }
250
268
  // ============================================================================
269
+ // FigJam operations
270
+ // ============================================================================
271
+ async createSticky(params) {
272
+ return this.wsServer.sendCommand('CREATE_STICKY', params);
273
+ }
274
+ async createStickies(params) {
275
+ return this.wsServer.sendCommand('CREATE_STICKIES', params, 30000);
276
+ }
277
+ async createConnector(params) {
278
+ return this.wsServer.sendCommand('CREATE_CONNECTOR', params);
279
+ }
280
+ async createShapeWithText(params) {
281
+ return this.wsServer.sendCommand('CREATE_SHAPE_WITH_TEXT', params);
282
+ }
283
+ async createTable(params) {
284
+ return this.wsServer.sendCommand('CREATE_TABLE', params, 30000);
285
+ }
286
+ async createCodeBlock(params) {
287
+ return this.wsServer.sendCommand('CREATE_CODE_BLOCK', params);
288
+ }
289
+ async getBoardContents(params) {
290
+ return this.wsServer.sendCommand('GET_BOARD_CONTENTS', params, 30000);
291
+ }
292
+ async getConnections() {
293
+ return this.wsServer.sendCommand('GET_CONNECTIONS', {}, 15000);
294
+ }
295
+ // ============================================================================
296
+ // Slides operations
297
+ // ============================================================================
298
+ async listSlides() {
299
+ return this.wsServer.sendCommand('LIST_SLIDES', {}, 10000);
300
+ }
301
+ async getSlideContent(params) {
302
+ return this.wsServer.sendCommand('GET_SLIDE_CONTENT', params, 10000);
303
+ }
304
+ async createSlide(params) {
305
+ return this.wsServer.sendCommand('CREATE_SLIDE', params, 10000);
306
+ }
307
+ async deleteSlide(params) {
308
+ return this.wsServer.sendCommand('DELETE_SLIDE', params, 5000);
309
+ }
310
+ async duplicateSlide(params) {
311
+ return this.wsServer.sendCommand('DUPLICATE_SLIDE', params, 5000);
312
+ }
313
+ async getSlideGrid() {
314
+ return this.wsServer.sendCommand('GET_SLIDE_GRID', {}, 10000);
315
+ }
316
+ async reorderSlides(params) {
317
+ return this.wsServer.sendCommand('REORDER_SLIDES', params, 15000);
318
+ }
319
+ async setSlideTransition(params) {
320
+ return this.wsServer.sendCommand('SET_SLIDE_TRANSITION', params, 5000);
321
+ }
322
+ async getSlideTransition(params) {
323
+ return this.wsServer.sendCommand('GET_SLIDE_TRANSITION', params, 5000);
324
+ }
325
+ async setSlidesViewMode(params) {
326
+ return this.wsServer.sendCommand('SET_SLIDES_VIEW_MODE', params, 5000);
327
+ }
328
+ async getFocusedSlide() {
329
+ return this.wsServer.sendCommand('GET_FOCUSED_SLIDE', {}, 5000);
330
+ }
331
+ async focusSlide(params) {
332
+ return this.wsServer.sendCommand('FOCUS_SLIDE', params, 5000);
333
+ }
334
+ async skipSlide(params) {
335
+ return this.wsServer.sendCommand('SKIP_SLIDE', params, 5000);
336
+ }
337
+ async addTextToSlide(params) {
338
+ return this.wsServer.sendCommand('ADD_TEXT_TO_SLIDE', params, 10000);
339
+ }
340
+ async addShapeToSlide(params) {
341
+ return this.wsServer.sendCommand('ADD_SHAPE_TO_SLIDE', params, 5000);
342
+ }
343
+ // ============================================================================
251
344
  // Cache management (no-op for WebSocket — no frame cache)
252
345
  // ============================================================================
253
346
  clearFrameCache() {
@@ -18,12 +18,12 @@ import { createServer as createHttpServer } from 'http';
18
18
  import { readFileSync, existsSync } from 'fs';
19
19
  import { join } from 'path';
20
20
  import { createChildLogger } from './logger.js';
21
- // Read version from package.json
22
- // Uses __dirname in CJS/Jest context, falls back to process.cwd() in ESM runtime
21
+ import { PACKAGE_ROOT } from './resolve-package-root.js';
22
+ // Read version from package.json using the resolved package root.
23
+ // PACKAGE_ROOT uses import.meta.url in ESM (production) and __dirname in CJS (Jest).
23
24
  let SERVER_VERSION = '0.0.0';
24
25
  try {
25
- const base = typeof __dirname !== 'undefined' ? join(__dirname, '..', '..') : process.cwd();
26
- SERVER_VERSION = JSON.parse(readFileSync(join(base, 'package.json'), 'utf-8')).version;
26
+ SERVER_VERSION = JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf-8')).version;
27
27
  }
28
28
  catch {
29
29
  // Non-critical — version will show as 0.0.0
@@ -34,11 +34,9 @@ catch {
34
34
  */
35
35
  function loadPluginUIContent() {
36
36
  const candidates = [
37
- // ESM runtime: dist/core/ ../../figma-desktop-bridge/
38
- typeof __dirname !== 'undefined'
39
- ? join(__dirname, '..', '..', 'figma-desktop-bridge', 'ui-full.html')
40
- : join(process.cwd(), 'figma-desktop-bridge', 'ui-full.html'),
41
- // Direct from project root
37
+ // Primary: relative to package root (works in both CJS and ESM)
38
+ join(PACKAGE_ROOT, 'figma-desktop-bridge', 'ui-full.html'),
39
+ // Fallback: relative to cwd (development / monorepo setups)
42
40
  join(process.cwd(), 'figma-desktop-bridge', 'ui-full.html'),
43
41
  ];
44
42
  for (const path of candidates) {
@@ -415,6 +413,7 @@ export class FigmaWebSocketServer extends EventEmitter {
415
413
  fileKey,
416
414
  currentPage: data.currentPage,
417
415
  currentPageId: data.currentPageId || null,
416
+ editorType: data.editorType || 'figma',
418
417
  connectedAt: Date.now(),
419
418
  },
420
419
  selection: existing?.selection || null,
@@ -707,6 +706,16 @@ export class FigmaWebSocketServer extends EventEmitter {
707
706
  getActiveFileKey() {
708
707
  return this._activeFileKey;
709
708
  }
709
+ /**
710
+ * Get the editor type of the currently active file.
711
+ * Returns 'figma' if no file is connected or editorType wasn't reported.
712
+ */
713
+ getEditorType() {
714
+ if (!this._activeFileKey)
715
+ return 'figma';
716
+ const client = this.clients.get(this._activeFileKey);
717
+ return client?.fileInfo?.editorType || 'figma';
718
+ }
710
719
  // ============================================================================
711
720
  // Cleanup
712
721
  // ============================================================================
@@ -20,15 +20,45 @@ import { FigmaAPI, extractFileKey } from "./core/figma-api.js";
20
20
  import { registerFigmaAPITools } from "./core/figma-tools.js";
21
21
  import { registerDesignCodeTools } from "./core/design-code-tools.js";
22
22
  import { registerCommentTools } from "./core/comment-tools.js";
23
+ import { registerAnnotationTools } from "./core/annotation-tools.js";
24
+ import { registerDeepComponentTools } from "./core/deep-component-tools.js";
23
25
  import { registerDesignSystemTools } from "./core/design-system-tools.js";
24
26
  import { generatePairingCode } from "./core/cloud-websocket-relay.js";
25
27
  import { CloudWebSocketConnector } from "./core/cloud-websocket-connector.js";
26
28
  import { registerWriteTools } from "./core/write-tools.js";
29
+ import { registerFigJamTools } from "./core/figjam-tools.js";
30
+ import { registerSlidesTools } from "./core/slides-tools.js";
27
31
  // Re-export PluginRelayDO so Cloudflare Workers can bind it as a Durable Object
28
32
  export { PluginRelayDO } from "./core/cloud-websocket-relay.js";
29
33
  // Note: MCP Apps (Token Browser, Dashboard) are only available in local mode
30
34
  // They require Node.js file system APIs for serving HTML that don't work in Cloudflare Workers
31
35
  const logger = createChildLogger({ component: "mcp-server" });
36
+ /**
37
+ * Validate a Figma Personal Access Token (PAT) by calling the Figma API.
38
+ * PATs start with 'figd_' and require the X-Figma-Token header (not Bearer).
39
+ * Returns the user info if valid, null if invalid/expired.
40
+ */
41
+ async function validateFigmaPAT(token) {
42
+ try {
43
+ const response = await fetch("https://api.figma.com/v1/me", {
44
+ headers: { "X-Figma-Token": token },
45
+ });
46
+ if (!response.ok)
47
+ return null;
48
+ const data = await response.json();
49
+ return data?.id ? data : null;
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }
55
+ /**
56
+ * Check if a token is a Figma Personal Access Token.
57
+ * PATs start with 'figd_' — OAuth tokens start with 'figu_'.
58
+ */
59
+ function isFigmaPAT(token) {
60
+ return token.startsWith("figd_");
61
+ }
32
62
  /**
33
63
  * Figma Console MCP Agent
34
64
  * Extends McpAgent to provide Figma-specific debugging tools
@@ -38,7 +68,7 @@ export class FigmaConsoleMCPv3 extends McpAgent {
38
68
  super(...arguments);
39
69
  this.server = new McpServer({
40
70
  name: "Figma Console MCP",
41
- version: "1.15.5",
71
+ version: "1.19.1",
42
72
  });
43
73
  this.browserManager = null;
44
74
  this.consoleMonitor = null;
@@ -745,6 +775,14 @@ export class FigmaConsoleMCPv3 extends McpAgent {
745
775
  };
746
776
  // Register all write/manipulation tools via shared function
747
777
  registerWriteTools(this.server, getCloudDesktopConnector);
778
+ // Register FigJam-specific tools (sticky notes, connectors, tables, etc.)
779
+ registerFigJamTools(this.server, getCloudDesktopConnector);
780
+ // Register Annotation tools (read/write design annotations via Desktop Bridge)
781
+ registerAnnotationTools(this.server, getCloudDesktopConnector);
782
+ // Register Deep Component tools (Plugin API tree extraction for code generation)
783
+ registerDeepComponentTools(this.server, getCloudDesktopConnector);
784
+ // Register Figma Slides tools (slide management, transitions, content)
785
+ registerSlidesTools(this.server, getCloudDesktopConnector);
748
786
  // Register Figma API tools (Tools 8-14)
749
787
  // Pass isRemoteMode: true to suppress Desktop Bridge mentions in tool descriptions
750
788
  registerFigmaAPITools(this.server, async () => await this.getFigmaAPI(), () => this.browserManager?.getCurrentUrl() || null, () => this.consoleMonitor || null, () => this.browserManager || null, () => this.ensureInitialized(), undefined, // variablesCache
@@ -829,6 +867,38 @@ export default {
829
867
  });
830
868
  }
831
869
  const bearerToken = authHeader.substring(7); // Remove "Bearer " prefix
870
+ // PAT support: Figma Personal Access Tokens (figd_*) are passed as Bearer
871
+ // tokens by MCP clients like Lovable, but they aren't stored in our OAuth KV.
872
+ // Validate them directly against Figma's API instead.
873
+ if (isFigmaPAT(bearerToken)) {
874
+ logger.info({ pathname: url.pathname }, "SSE request with Figma PAT — validating against Figma API");
875
+ const patUser = await validateFigmaPAT(bearerToken);
876
+ if (!patUser) {
877
+ logger.warn({ pathname: url.pathname }, "SSE request with invalid Figma PAT");
878
+ const resourceMetadataUrl = `${url.origin}/.well-known/oauth-protected-resource`;
879
+ return new Response(JSON.stringify({
880
+ error: "invalid_token",
881
+ error_description: "Figma Personal Access Token is invalid or expired"
882
+ }), {
883
+ status: 401,
884
+ headers: {
885
+ "Content-Type": "application/json",
886
+ "WWW-Authenticate": `Bearer resource_metadata="${resourceMetadataUrl}", error="invalid_token"`
887
+ }
888
+ });
889
+ }
890
+ // Store PAT in KV so the Durable Object's getFigmaAPI() can retrieve it
891
+ const patSessionId = "figma-console-mcp-default-session";
892
+ const patTokenKey = `oauth_token:${patSessionId}`;
893
+ await env.OAUTH_TOKENS.put(patTokenKey, JSON.stringify({
894
+ accessToken: bearerToken,
895
+ expiresAt: Date.now() + 3600_000, // 1-hour TTL for PAT session
896
+ }), { expirationTtl: 3600 });
897
+ logger.info({ pathname: url.pathname, user: patUser.handle }, "SSE request authenticated via Figma PAT");
898
+ // Proceed with SSE connection
899
+ return FigmaConsoleMCPv3.serveSSE("/sse").fetch(request, env, ctx);
900
+ }
901
+ // OAuth token path: look up in KV store
832
902
  const bearerKey = `bearer_token:${bearerToken}`;
833
903
  try {
834
904
  const tokenDataJson = await env.OAUTH_TOKENS.get(bearerKey);
@@ -897,15 +967,18 @@ export default {
897
967
  });
898
968
  }
899
969
  const bearerToken = authHeader.substring(7);
900
- const bearerKey = `bearer_token:${bearerToken}`;
901
- try {
902
- const tokenDataJson = await env.OAUTH_TOKENS.get(bearerKey);
903
- if (!tokenDataJson) {
904
- logger.warn({ pathname: url.pathname }, "MCP request with invalid Bearer token");
970
+ // PAT support: Figma Personal Access Tokens (figd_*) are passed as Bearer
971
+ // tokens by MCP clients like Lovable, v0, and Replit. They bypass OAuth
972
+ // and aren't stored in KV — validate directly against Figma's API.
973
+ if (isFigmaPAT(bearerToken)) {
974
+ logger.info({ pathname: url.pathname }, "MCP request with Figma PAT — validating against Figma API");
975
+ const patUser = await validateFigmaPAT(bearerToken);
976
+ if (!patUser) {
977
+ logger.warn({ pathname: url.pathname }, "MCP request with invalid Figma PAT");
905
978
  const resourceMetadataUrl = `${url.origin}/.well-known/oauth-protected-resource`;
906
979
  return new Response(JSON.stringify({
907
980
  error: "invalid_token",
908
- error_description: "Bearer token is invalid or expired"
981
+ error_description: "Figma Personal Access Token is invalid or expired. Ensure your PAT (figd_...) is valid and has not been revoked."
909
982
  }), {
910
983
  status: 401,
911
984
  headers: {
@@ -914,35 +987,58 @@ export default {
914
987
  }
915
988
  });
916
989
  }
917
- const tokenData = JSON.parse(tokenDataJson);
918
- if (tokenData.expiresAt < Date.now()) {
919
- logger.warn({ pathname: url.pathname, sessionId: tokenData.sessionId }, "MCP request with expired Bearer token");
920
- const resourceMetadataUrl = `${url.origin}/.well-known/oauth-protected-resource`;
990
+ logger.info({ pathname: url.pathname, user: patUser.handle }, "MCP request authenticated via Figma PAT");
991
+ }
992
+ else {
993
+ // OAuth token path: look up in KV store
994
+ const bearerKey = `bearer_token:${bearerToken}`;
995
+ try {
996
+ const tokenDataJson = await env.OAUTH_TOKENS.get(bearerKey);
997
+ if (!tokenDataJson) {
998
+ logger.warn({ pathname: url.pathname }, "MCP request with invalid Bearer token");
999
+ const resourceMetadataUrl = `${url.origin}/.well-known/oauth-protected-resource`;
1000
+ return new Response(JSON.stringify({
1001
+ error: "invalid_token",
1002
+ error_description: "Bearer token is invalid or expired"
1003
+ }), {
1004
+ status: 401,
1005
+ headers: {
1006
+ "Content-Type": "application/json",
1007
+ "WWW-Authenticate": `Bearer resource_metadata="${resourceMetadataUrl}", error="invalid_token"`
1008
+ }
1009
+ });
1010
+ }
1011
+ const tokenData = JSON.parse(tokenDataJson);
1012
+ if (tokenData.expiresAt < Date.now()) {
1013
+ logger.warn({ pathname: url.pathname, sessionId: tokenData.sessionId }, "MCP request with expired Bearer token");
1014
+ const resourceMetadataUrl = `${url.origin}/.well-known/oauth-protected-resource`;
1015
+ return new Response(JSON.stringify({
1016
+ error: "invalid_token",
1017
+ error_description: "Bearer token has expired"
1018
+ }), {
1019
+ status: 401,
1020
+ headers: {
1021
+ "Content-Type": "application/json",
1022
+ "WWW-Authenticate": `Bearer resource_metadata="${resourceMetadataUrl}", error="invalid_token"`
1023
+ }
1024
+ });
1025
+ }
1026
+ logger.info({ pathname: url.pathname, sessionId: tokenData.sessionId }, "MCP request authenticated successfully");
1027
+ }
1028
+ catch (error) {
1029
+ logger.error({ error, pathname: url.pathname }, "Error validating Bearer token for MCP endpoint");
921
1030
  return new Response(JSON.stringify({
922
- error: "invalid_token",
923
- error_description: "Bearer token has expired"
1031
+ error: "server_error",
1032
+ error_description: "Failed to validate authorization"
924
1033
  }), {
925
- status: 401,
926
- headers: {
927
- "Content-Type": "application/json",
928
- "WWW-Authenticate": `Bearer resource_metadata="${resourceMetadataUrl}", error="invalid_token"`
929
- }
1034
+ status: 500,
1035
+ headers: { "Content-Type": "application/json" }
930
1036
  });
931
1037
  }
932
- logger.info({ pathname: url.pathname, sessionId: tokenData.sessionId }, "MCP request authenticated successfully");
933
- }
934
- catch (error) {
935
- logger.error({ error, pathname: url.pathname }, "Error validating Bearer token for MCP endpoint");
936
- return new Response(JSON.stringify({
937
- error: "server_error",
938
- error_description: "Failed to validate authorization"
939
- }), {
940
- status: 500,
941
- headers: { "Content-Type": "application/json" }
942
- });
943
1038
  }
944
1039
  // Token is valid — use stateless transport (no Durable Objects)
945
1040
  // The Bearer token IS the Figma access token, so we use it directly
1041
+ // FigmaAPI handles PAT vs OAuth header selection internally
946
1042
  const figmaAccessToken = bearerToken;
947
1043
  const statelessApi = new FigmaAPI({ accessToken: figmaAccessToken });
948
1044
  const transport = new WebStandardStreamableHTTPServerTransport({
@@ -950,7 +1046,7 @@ export default {
950
1046
  });
951
1047
  const statelessServer = new McpServer({
952
1048
  name: "Figma Console MCP",
953
- version: "1.15.5",
1049
+ version: "1.19.1",
954
1050
  });
955
1051
  // ================================================================
956
1052
  // Cloud Write Relay — Pairing Tool (stateless /mcp path)
@@ -1029,6 +1125,14 @@ export default {
1029
1125
  }
1030
1126
  // Register all write/manipulation tools via shared function
1031
1127
  registerWriteTools(statelessServer, getCloudDesktopConnector);
1128
+ // Register FigJam-specific tools
1129
+ registerFigJamTools(statelessServer, getCloudDesktopConnector);
1130
+ // Register Annotation tools
1131
+ registerAnnotationTools(statelessServer, getCloudDesktopConnector);
1132
+ // Register Deep Component tools
1133
+ registerDeepComponentTools(statelessServer, getCloudDesktopConnector);
1134
+ // Register Figma Slides tools
1135
+ registerSlidesTools(statelessServer, getCloudDesktopConnector);
1032
1136
  // Register REST API tools with the authenticated Figma API
1033
1137
  registerFigmaAPITools(statelessServer, async () => statelessApi, getCloudFileUrl, () => null, // No console monitor
1034
1138
  () => null, // No browser manager
@@ -1579,7 +1683,7 @@ export default {
1579
1683
  return new Response(JSON.stringify({
1580
1684
  status: "healthy",
1581
1685
  service: "Figma Console MCP",
1582
- version: "1.15.5",
1686
+ version: "1.19.1",
1583
1687
  endpoints: {
1584
1688
  mcp: ["/sse", "/mcp"],
1585
1689
  oauth_mcp_spec: ["/.well-known/oauth-authorization-server", "/authorize", "/token", "/oauth/register"],
@@ -1625,13 +1729,13 @@ export default {
1625
1729
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1626
1730
  <title>Figma Console MCP - The Most Comprehensive MCP Server for Figma</title>
1627
1731
  <link rel="icon" type="image/svg+xml" href="https://docs.figma-console-mcp.southleft.com/favicon.svg">
1628
- <meta name="description" content="Turn your Figma design system into a living API. 63+ tools give AI assistants deep access to design tokens, component specs, variables, and programmatic design creation.">
1732
+ <meta name="description" content="Turn your Figma design system into a living API. 89+ tools give AI assistants deep access to design tokens, component specs, variables, and programmatic design creation.">
1629
1733
 
1630
1734
  <!-- Open Graph -->
1631
1735
  <meta property="og:type" content="website">
1632
1736
  <meta property="og:url" content="https://figma-console-mcp.southleft.com">
1633
1737
  <meta property="og:title" content="Figma Console MCP - Turn Your Design System Into a Living API">
1634
- <meta property="og:description" content="The most comprehensive MCP server for Figma. 63+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
1738
+ <meta property="og:description" content="The most comprehensive MCP server for Figma. 89+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
1635
1739
  <meta property="og:image" content="https://docs.figma-console-mcp.southleft.com/images/og-image.jpg">
1636
1740
  <meta property="og:image:width" content="1200">
1637
1741
  <meta property="og:image:height" content="630">
@@ -1639,7 +1743,7 @@ export default {
1639
1743
  <!-- Twitter -->
1640
1744
  <meta name="twitter:card" content="summary_large_image">
1641
1745
  <meta name="twitter:title" content="Figma Console MCP - Turn Your Design System Into a Living API">
1642
- <meta name="twitter:description" content="The most comprehensive MCP server for Figma. 63+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
1746
+ <meta name="twitter:description" content="The most comprehensive MCP server for Figma. 89+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
1643
1747
  <meta name="twitter:image" content="https://docs.figma-console-mcp.southleft.com/images/og-image.jpg">
1644
1748
 
1645
1749
  <meta name="theme-color" content="#0D9488">
@@ -1918,20 +2022,20 @@ export default {
1918
2022
  }
1919
2023
 
1920
2024
  .capability-list {
1921
- display: flex;
1922
- flex-direction: column;
1923
- gap: 12px;
2025
+ display: grid;
2026
+ grid-template-columns: 1fr 1fr;
2027
+ gap: 10px;
1924
2028
  }
1925
2029
 
1926
2030
  .capability-item {
1927
2031
  display: flex;
1928
2032
  align-items: center;
1929
- gap: 12px;
1930
- padding: 12px 16px;
2033
+ gap: 10px;
2034
+ padding: 10px 14px;
1931
2035
  background: var(--color-bg-elevated);
1932
2036
  border: 1px solid var(--color-border);
1933
2037
  border-radius: var(--radius-md);
1934
- font-size: 14px;
2038
+ font-size: 13px;
1935
2039
  color: var(--color-text-secondary);
1936
2040
  transition: all var(--transition);
1937
2041
  }
@@ -2406,6 +2510,9 @@ export default {
2406
2510
  .showcase-cell {
2407
2511
  padding-top: 32px;
2408
2512
  }
2513
+ .capability-list {
2514
+ grid-template-columns: 1fr;
2515
+ }
2409
2516
  .capability-card {
2410
2517
  grid-column: span 12;
2411
2518
  padding: 0 !important;
@@ -2523,7 +2630,7 @@ export default {
2523
2630
  <div class="grid-cell showcase-cell rule-left">
2524
2631
  <div class="showcase-label">What AI Can Access</div>
2525
2632
  <div class="showcase-stat">
2526
- <span class="number">59+</span>
2633
+ <span class="number">87+</span>
2527
2634
  <span class="label">MCP tools for Figma</span>
2528
2635
  </div>
2529
2636
  <div class="capability-list">
@@ -2551,6 +2658,14 @@ export default {
2551
2658
  <svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>
2552
2659
  <span>Visual debugging and screenshots</span>
2553
2660
  </div>
2661
+ <div class="capability-item">
2662
+ <svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z"/></svg>
2663
+ <span>Design annotations and dev specs</span>
2664
+ </div>
2665
+ <div class="capability-item">
2666
+ <svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><rect x="2" y="3" width="20" height="4" rx="1"/><rect x="2" y="9" width="9" height="4" rx="1"/><rect x="13" y="9" width="9" height="4" rx="1"/><rect x="2" y="15" width="20" height="4" rx="1"/></svg>
2667
+ <span>FigJam boards and Slides presentations</span>
2668
+ </div>
2554
2669
  </div>
2555
2670
  </div>
2556
2671
  </div>
@@ -2634,6 +2749,14 @@ export default {
2634
2749
  <svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
2635
2750
  <span>"Connect to my Figma plugin and create a card component"</span>
2636
2751
  </div>
2752
+ <div class="prompt-item">
2753
+ <svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
2754
+ <span>"Create a retrospective board with colored stickies on FigJam"</span>
2755
+ </div>
2756
+ <div class="prompt-item">
2757
+ <svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
2758
+ <span>"List my slides and set a dissolve transition on each one"</span>
2759
+ </div>
2637
2760
  </div>
2638
2761
  </div>
2639
2762
 
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Figma Annotations MCP Tools
3
+ * Tools for reading, writing, and managing design annotations on Figma nodes.
4
+ * Annotations are a Plugin API feature — requires Desktop Bridge plugin connection.
5
+ *
6
+ * Annotations are distinct from comments: they are node-level design specs that
7
+ * can pin specific properties (fills, width, typography, etc.) and support
8
+ * markdown-formatted labels. Designers use them to communicate animation timings,
9
+ * accessibility requirements, interaction specs, and other implementation details
10
+ * that don't fit in the description field.
11
+ */
12
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
13
+ export declare function registerAnnotationTools(server: McpServer, getDesktopConnector: () => Promise<any>): void;
14
+ //# sourceMappingURL=annotation-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"annotation-tools.d.ts","sourceRoot":"","sources":["../../src/core/annotation-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA4EzE,wBAAgB,uBAAuB,CACtC,MAAM,EAAE,SAAS,EACjB,mBAAmB,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,GACrC,IAAI,CA0LN"}