@atezer/figma-mcp-bridge 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -579,32 +579,36 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
579
579
  // NOTE: For specific use cases, consider using specialized tools:
580
580
  // - figma_get_component_for_development: For UI component implementation
581
581
  // - figma_get_file_for_plugin: For plugin development
582
- server.tool("figma_get_file_data", "Get full file structure and document tree. WARNING: Can consume large amounts of tokens. NOT recommended for component descriptions (use figma_get_component instead). Best for understanding file structure or finding component nodeIds. Start with verbosity='summary' and depth=1 for initial exploration.", {
583
- fileUrl: z
584
- .string()
585
- .url()
586
- .optional()
587
- .describe("Figma file URL (e.g., https://figma.com/design/abc123). REQUIRED unless figma_navigate was already called. If not provided, ask the user to share their Figma file URL (they can copy it from Figma Desktop via right-click → 'Copy link')."),
588
- depth: z
589
- .number()
590
- .min(0)
591
- .max(3)
592
- .optional()
593
- .default(1)
594
- .describe("How many levels of children to include (default: 1, max: 3). Start with 1 to prevent context exhaustion. Use 0 for full tree only when absolutely necessary."),
595
- verbosity: z
596
- .enum(["summary", "standard", "full"])
597
- .optional()
598
- .default("summary")
599
- .describe("Controls payload size: 'summary' (IDs/names/types only, ~90% smaller - RECOMMENDED), 'standard' (essential properties, ~50% smaller), 'full' (everything). Default: summary for token efficiency."),
600
- nodeIds: z
601
- .array(z.string())
602
- .optional()
603
- .describe("Specific node IDs to retrieve (optional)"),
604
- enrich: z
605
- .boolean()
606
- .optional()
607
- .describe("Set to true when user asks for: file statistics, health metrics, design system audit, or quality analysis. Adds statistics, health scores, and audit summaries. Default: false"),
582
+ server.registerTool("figma_get_file_data", {
583
+ description: "Get full file structure and document tree. WARNING: Can consume large amounts of tokens. NOT recommended for component descriptions (use figma_get_component instead). Best for understanding file structure or finding component nodeIds. Start with verbosity='summary' and depth=1 for initial exploration.",
584
+ inputSchema: {
585
+ fileUrl: z
586
+ .string()
587
+ .url()
588
+ .optional()
589
+ .describe("Figma file URL (e.g., https://figma.com/design/abc123). REQUIRED unless figma_navigate was already called. If not provided, ask the user to share their Figma file URL (they can copy it from Figma Desktop via right-click → 'Copy link')."),
590
+ depth: z
591
+ .number()
592
+ .min(0)
593
+ .max(3)
594
+ .optional()
595
+ .default(1)
596
+ .describe("How many levels of children to include (default: 1, max: 3). Start with 1 to prevent context exhaustion. Use 0 for full tree only when absolutely necessary."),
597
+ verbosity: z
598
+ .enum(["summary", "standard", "full"])
599
+ .optional()
600
+ .default("summary")
601
+ .describe("Controls payload size: 'summary' (IDs/names/types only, ~90% smaller - RECOMMENDED), 'standard' (essential properties, ~50% smaller), 'full' (everything). Default: summary for token efficiency."),
602
+ nodeIds: z
603
+ .array(z.string())
604
+ .optional()
605
+ .describe("Specific node IDs to retrieve (optional)"),
606
+ enrich: z
607
+ .boolean()
608
+ .optional()
609
+ .describe("Set to true when user asks for: file statistics, health metrics, design system audit, or quality analysis. Adds statistics, health scores, and audit summaries. Default: false"),
610
+ },
611
+ annotations: { readOnlyHint: true },
608
612
  }, async ({ fileUrl, depth, nodeIds, enrich, verbosity }) => {
609
613
  try {
610
614
  // Initialize API client (required for file data - no F-MCP ATezer Bridge alternative)
@@ -780,111 +784,115 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
780
784
  *
781
785
  * IMPORTANT: Snippet requires Figma Plugin API context, not browser DevTools console.
782
786
  */
783
- server.tool("figma_get_variables", "Extract design tokens and variables from a Figma file with code export support (CSS, Tailwind, TypeScript, Sass). Use when user asks for: design system tokens, variables, color/spacing values, theme data, or code exports. Handles multi-mode variables (Light/Dark themes). NOT for component metadata (use figma_get_component). Supports filtering by collection/mode/name and verbosity control to prevent token exhaustion. Enterprise plan required for Variables API; automatically falls back to Styles API or console-based extraction if unavailable.", {
784
- fileUrl: z
785
- .string()
786
- .url()
787
- .optional()
788
- .describe("Figma file URL (e.g., https://figma.com/design/abc123). REQUIRED unless figma_navigate was already called. If not provided, ask the user to share their Figma file URL (they can copy it from Figma Desktop via right-click → 'Copy link')."),
789
- includePublished: z
790
- .boolean()
791
- .optional()
792
- .default(true)
793
- .describe("Include published variables from libraries"),
794
- verbosity: z
795
- .enum(["inventory", "summary", "standard", "full"])
796
- .optional()
797
- .default("standard")
798
- .describe("Controls payload size: 'inventory' (names/IDs only, ~95% smaller, use with filtered), 'summary' (names/values only, ~80% smaller), 'standard' (essential properties, ~45% smaller), 'full' (everything). Default: standard"),
799
- enrich: z
800
- .boolean()
801
- .optional()
802
- .describe("Set to true when user asks for: CSS/Sass/Tailwind exports, code examples, design tokens, usage information, dependencies, or any export format. Adds resolved values, dependency graphs, and usage analysis. Default: false"),
803
- include_usage: z
804
- .boolean()
805
- .optional()
806
- .describe("Include usage in styles and components (requires enrich=true)"),
807
- include_dependencies: z
808
- .boolean()
809
- .optional()
810
- .describe("Include variable dependency graph (requires enrich=true)"),
811
- include_exports: z
812
- .boolean()
813
- .optional()
814
- .describe("Include export format examples (requires enrich=true)"),
815
- export_formats: z
816
- .array(z.enum(["css", "sass", "tailwind", "typescript", "json"]))
817
- .optional()
818
- .describe("Which code formats to generate examples for. Use when user mentions specific formats like 'CSS', 'Tailwind', 'SCSS', 'TypeScript', etc. Automatically enables enrichment."),
819
- format: z
820
- .enum(["summary", "filtered", "full"])
821
- .optional()
822
- .default("full")
823
- .describe("Response format: 'summary' (~2K tokens with overview and names only), 'filtered' (apply collection/name/mode filters), 'full' (complete dataset from cache or fetch). " +
824
- "Summary is recommended for initial exploration. Full format returns all data but may be auto-summarized if >25K tokens. Default: full"),
825
- collection: z
826
- .string()
827
- .optional()
828
- .describe("Filter variables by collection name or ID. Case-insensitive substring match. Only applies when format='filtered'. Example: 'Primitives' or 'VariableCollectionId:123'"),
829
- namePattern: z
830
- .string()
831
- .optional()
832
- .describe("Filter variables by name using regex pattern or substring. Case-insensitive. Only applies when format='filtered'. Example: 'color/brand' or '^typography'"),
833
- mode: z
834
- .string()
835
- .optional()
836
- .describe("Filter variables by mode name or ID. Only returns variables that have values for this mode. Only applies when format='filtered'. Example: 'Light' or 'Dark'"),
837
- returnAsLinks: z
838
- .boolean()
839
- .optional()
840
- .default(false)
841
- .describe("Return variables as resource_link references instead of full data. Drastically reduces payload size (100+ variables = ~20KB vs >1MB). Use with figma_get_variable_by_id to fetch specific variables. Recommended for large variable sets. Default: false"),
842
- refreshCache: z
843
- .boolean()
844
- .optional()
845
- .default(false)
846
- .describe("Force refresh cache by fetching fresh data from Figma. Use when data may have changed since last fetch. Default: false (use cached data if available and fresh)"),
847
- useConsoleFallback: z
848
- .boolean()
849
- .optional()
850
- .default(true)
851
- .describe("Enable automatic fallback to console-based extraction when REST API returns 403 (Figma Enterprise plan required). " +
852
- "When enabled, provides a JavaScript snippet that users run in Figma's plugin console. " +
853
- "This is STEP 1 of a two-call workflow. After receiving the snippet, instruct the user to run it, then call this tool again with parseFromConsole=true. " +
854
- "Default: true. Set to false only to disable the fallback entirely."),
855
- parseFromConsole: z
856
- .boolean()
857
- .optional()
858
- .default(false)
859
- .describe("Parse variables from console logs after user has executed the snippet. " +
860
- "This is STEP 2 of the two-call workflow. Set to true ONLY after: " +
861
- "(1) you received a console snippet from the first call, " +
862
- "(2) instructed the user to run it in Figma's PLUGIN console (Plugins Development → Open Console or existing plugin), " +
863
- "(3) user confirmed they ran the snippet and saw '✅ Variables data captured!' message. " +
864
- "Default: false. Never set to true on the first call."),
865
- page: z
866
- .number()
867
- .int()
868
- .min(1)
869
- .optional()
870
- .default(1)
871
- .describe("Page number for paginated results (1-based). Use when response is too large (>1MB). Each page returns up to 50 variables."),
872
- pageSize: z
873
- .number()
874
- .int()
875
- .min(1)
876
- .max(100)
877
- .optional()
878
- .default(50)
879
- .describe("Number of variables per page (1-100). Default: 50. Smaller values reduce response size."),
880
- resolveAliases: z
881
- .boolean()
882
- .optional()
883
- .default(false)
884
- .describe("Automatically resolve variable aliases to their final values (hex colors, numbers, etc.). " +
885
- "When true, each variable will include a 'resolvedValuesByMode' field with the actual values " +
886
- "instead of just alias references. Useful for getting color hex values without manual resolution. " +
887
- "Default: false."),
787
+ server.registerTool("figma_get_variables", {
788
+ description: "Extract design tokens and variables from a Figma file with code export support (CSS, Tailwind, TypeScript, Sass). Use when user asks for: design system tokens, variables, color/spacing values, theme data, or code exports. Handles multi-mode variables (Light/Dark themes). NOT for component metadata (use figma_get_component). Supports filtering by collection/mode/name and verbosity control to prevent token exhaustion. Enterprise plan required for Variables API; automatically falls back to Styles API or console-based extraction if unavailable.",
789
+ inputSchema: {
790
+ fileUrl: z
791
+ .string()
792
+ .url()
793
+ .optional()
794
+ .describe("Figma file URL (e.g., https://figma.com/design/abc123). REQUIRED unless figma_navigate was already called. If not provided, ask the user to share their Figma file URL (they can copy it from Figma Desktop via right-click → 'Copy link')."),
795
+ includePublished: z
796
+ .boolean()
797
+ .optional()
798
+ .default(true)
799
+ .describe("Include published variables from libraries"),
800
+ verbosity: z
801
+ .enum(["inventory", "summary", "standard", "full"])
802
+ .optional()
803
+ .default("standard")
804
+ .describe("Controls payload size: 'inventory' (names/IDs only, ~95% smaller, use with filtered), 'summary' (names/values only, ~80% smaller), 'standard' (essential properties, ~45% smaller), 'full' (everything). Default: standard"),
805
+ enrich: z
806
+ .boolean()
807
+ .optional()
808
+ .describe("Set to true when user asks for: CSS/Sass/Tailwind exports, code examples, design tokens, usage information, dependencies, or any export format. Adds resolved values, dependency graphs, and usage analysis. Default: false"),
809
+ include_usage: z
810
+ .boolean()
811
+ .optional()
812
+ .describe("Include usage in styles and components (requires enrich=true)"),
813
+ include_dependencies: z
814
+ .boolean()
815
+ .optional()
816
+ .describe("Include variable dependency graph (requires enrich=true)"),
817
+ include_exports: z
818
+ .boolean()
819
+ .optional()
820
+ .describe("Include export format examples (requires enrich=true)"),
821
+ export_formats: z
822
+ .array(z.enum(["css", "sass", "tailwind", "typescript", "json"]))
823
+ .optional()
824
+ .describe("Which code formats to generate examples for. Use when user mentions specific formats like 'CSS', 'Tailwind', 'SCSS', 'TypeScript', etc. Automatically enables enrichment."),
825
+ format: z
826
+ .enum(["summary", "filtered", "full"])
827
+ .optional()
828
+ .default("full")
829
+ .describe("Response format: 'summary' (~2K tokens with overview and names only), 'filtered' (apply collection/name/mode filters), 'full' (complete dataset from cache or fetch). " +
830
+ "Summary is recommended for initial exploration. Full format returns all data but may be auto-summarized if >25K tokens. Default: full"),
831
+ collection: z
832
+ .string()
833
+ .optional()
834
+ .describe("Filter variables by collection name or ID. Case-insensitive substring match. Only applies when format='filtered'. Example: 'Primitives' or 'VariableCollectionId:123'"),
835
+ namePattern: z
836
+ .string()
837
+ .optional()
838
+ .describe("Filter variables by name using regex pattern or substring. Case-insensitive. Only applies when format='filtered'. Example: 'color/brand' or '^typography'"),
839
+ mode: z
840
+ .string()
841
+ .optional()
842
+ .describe("Filter variables by mode name or ID. Only returns variables that have values for this mode. Only applies when format='filtered'. Example: 'Light' or 'Dark'"),
843
+ returnAsLinks: z
844
+ .boolean()
845
+ .optional()
846
+ .default(false)
847
+ .describe("Return variables as resource_link references instead of full data. Drastically reduces payload size (100+ variables = ~20KB vs >1MB). Use with figma_get_variable_by_id to fetch specific variables. Recommended for large variable sets. Default: false"),
848
+ refreshCache: z
849
+ .boolean()
850
+ .optional()
851
+ .default(false)
852
+ .describe("Force refresh cache by fetching fresh data from Figma. Use when data may have changed since last fetch. Default: false (use cached data if available and fresh)"),
853
+ useConsoleFallback: z
854
+ .boolean()
855
+ .optional()
856
+ .default(true)
857
+ .describe("Enable automatic fallback to console-based extraction when REST API returns 403 (Figma Enterprise plan required). " +
858
+ "When enabled, provides a JavaScript snippet that users run in Figma's plugin console. " +
859
+ "This is STEP 1 of a two-call workflow. After receiving the snippet, instruct the user to run it, then call this tool again with parseFromConsole=true. " +
860
+ "Default: true. Set to false only to disable the fallback entirely."),
861
+ parseFromConsole: z
862
+ .boolean()
863
+ .optional()
864
+ .default(false)
865
+ .describe("Parse variables from console logs after user has executed the snippet. " +
866
+ "This is STEP 2 of the two-call workflow. Set to true ONLY after: " +
867
+ "(1) you received a console snippet from the first call, " +
868
+ "(2) instructed the user to run it in Figma's PLUGIN console (Plugins → Development → Open Console or existing plugin), " +
869
+ "(3) user confirmed they ran the snippet and saw '✅ Variables data captured!' message. " +
870
+ "Default: false. Never set to true on the first call."),
871
+ page: z
872
+ .number()
873
+ .int()
874
+ .min(1)
875
+ .optional()
876
+ .default(1)
877
+ .describe("Page number for paginated results (1-based). Use when response is too large (>1MB). Each page returns up to 50 variables."),
878
+ pageSize: z
879
+ .number()
880
+ .int()
881
+ .min(1)
882
+ .max(100)
883
+ .optional()
884
+ .default(50)
885
+ .describe("Number of variables per page (1-100). Default: 50. Smaller values reduce response size."),
886
+ resolveAliases: z
887
+ .boolean()
888
+ .optional()
889
+ .default(false)
890
+ .describe("Automatically resolve variable aliases to their final values (hex colors, numbers, etc.). " +
891
+ "When true, each variable will include a 'resolvedValuesByMode' field with the actual values " +
892
+ "instead of just alias references. Useful for getting color hex values without manual resolution. " +
893
+ "Default: false."),
894
+ },
895
+ annotations: { readOnlyHint: true },
888
896
  }, async ({ fileUrl, includePublished, verbosity, enrich, include_usage, include_dependencies, include_exports, export_formats, format, collection, namePattern, mode, returnAsLinks, refreshCache, useConsoleFallback, parseFromConsole, page, pageSize, resolveAliases }) => {
889
897
  // Extract fileKey outside try block so it's available in catch block
890
898
  const url = fileUrl || getCurrentUrl();
@@ -1802,24 +1810,28 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
1802
1810
  }
1803
1811
  });
1804
1812
  // Tool 10: Get Component Data
1805
- server.tool("figma_get_component", "Get component metadata or reconstruction specification. Two export formats: (1) 'metadata' (default) - comprehensive documentation with properties, variants, and design tokens for style guides and references, (2) 'reconstruction' - node tree specification compatible with Figma Component Reconstructor plugin for programmatic component creation. IMPORTANT: For local/unpublished components with metadata format, ensure the F-MCP ATezer Bridge is running (Right-click in Figma → Plugins → Development → F-MCP ATezer Bridge) to get complete description data.", {
1806
- fileUrl: z
1807
- .string()
1808
- .url()
1809
- .optional()
1810
- .describe("Figma file URL (e.g., https://figma.com/design/abc123). REQUIRED unless figma_navigate was already called. If not provided, ask the user to share their Figma file URL (they can copy it from Figma Desktop via right-click → 'Copy link')."),
1811
- nodeId: z
1812
- .string()
1813
- .describe("Component node ID (e.g., '123:456')"),
1814
- format: z
1815
- .enum(["metadata", "reconstruction"])
1816
- .optional()
1817
- .default("metadata")
1818
- .describe("Export format: 'metadata' (default) for comprehensive documentation, 'reconstruction' for node tree specification compatible with Figma Component Reconstructor plugin"),
1819
- enrich: z
1820
- .boolean()
1821
- .optional()
1822
- .describe("Set to true when user asks for: design token coverage, hardcoded value analysis, or component quality metrics. Adds token coverage analysis and hardcoded value detection. Default: false. Only applicable for metadata format."),
1813
+ server.registerTool("figma_get_component", {
1814
+ description: "Get component metadata or reconstruction specification. Two export formats: (1) 'metadata' (default) - comprehensive documentation with properties, variants, and design tokens for style guides and references, (2) 'reconstruction' - node tree specification compatible with Figma Component Reconstructor plugin for programmatic component creation. IMPORTANT: For local/unpublished components with metadata format, ensure the F-MCP ATezer Bridge is running (Right-click in Figma → Plugins → Development → F-MCP ATezer Bridge) to get complete description data.",
1815
+ inputSchema: {
1816
+ fileUrl: z
1817
+ .string()
1818
+ .url()
1819
+ .optional()
1820
+ .describe("Figma file URL (e.g., https://figma.com/design/abc123). REQUIRED unless figma_navigate was already called. If not provided, ask the user to share their Figma file URL (they can copy it from Figma Desktop via right-click → 'Copy link')."),
1821
+ nodeId: z
1822
+ .string()
1823
+ .describe("Component node ID (e.g., '123:456')"),
1824
+ format: z
1825
+ .enum(["metadata", "reconstruction"])
1826
+ .optional()
1827
+ .default("metadata")
1828
+ .describe("Export format: 'metadata' (default) for comprehensive documentation, 'reconstruction' for node tree specification compatible with Figma Component Reconstructor plugin"),
1829
+ enrich: z
1830
+ .boolean()
1831
+ .optional()
1832
+ .describe("Set to true when user asks for: design token coverage, hardcoded value analysis, or component quality metrics. Adds token coverage analysis and hardcoded value detection. Default: false. Only applicable for metadata format."),
1833
+ },
1834
+ annotations: { readOnlyHint: true },
1823
1835
  }, async ({ fileUrl, nodeId, format = "metadata", enrich }) => {
1824
1836
  try {
1825
1837
  const url = fileUrl || getCurrentUrl();
@@ -2034,33 +2046,37 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
2034
2046
  }
2035
2047
  });
2036
2048
  // Tool 11: Get Styles
2037
- server.tool("figma_get_styles", "Get all styles (color, text, effects, grids) from a Figma file with optional code exports. Use when user asks for: text styles, color palette, design system styles, typography, or style documentation. Returns organized style definitions with resolved values. NOT for design tokens/variables (use figma_get_variables). Set enrich=true for CSS/Tailwind/Sass code examples. Supports verbosity control to manage payload size.", {
2038
- fileUrl: z
2039
- .string()
2040
- .url()
2041
- .optional()
2042
- .describe("Figma file URL (e.g., https://figma.com/design/abc123). REQUIRED unless figma_navigate was already called. If not provided, ask the user to share their Figma file URL (they can copy it from Figma Desktop via right-click → 'Copy link')."),
2043
- verbosity: z
2044
- .enum(["summary", "standard", "full"])
2045
- .optional()
2046
- .default("standard")
2047
- .describe("Controls payload size: 'summary' (names/types only, ~85% smaller), 'standard' (essential properties, ~40% smaller), 'full' (everything). Default: standard"),
2048
- enrich: z
2049
- .boolean()
2050
- .optional()
2051
- .describe("Set to true when user asks for: CSS/Sass/Tailwind code, export formats, usage information, code examples, or design system exports. Adds resolved values, usage analysis, and export format examples. Default: false for backward compatibility"),
2052
- include_usage: z
2053
- .boolean()
2054
- .optional()
2055
- .describe("Include component usage information (requires enrich=true)"),
2056
- include_exports: z
2057
- .boolean()
2058
- .optional()
2059
- .describe("Include export format examples (requires enrich=true)"),
2060
- export_formats: z
2061
- .array(z.enum(["css", "sass", "tailwind", "typescript", "json"]))
2062
- .optional()
2063
- .describe("Which code formats to generate examples for. Use when user mentions specific formats like 'CSS', 'Tailwind', 'SCSS', 'TypeScript', etc. Automatically enables enrichment. Default: all formats"),
2049
+ server.registerTool("figma_get_styles", {
2050
+ description: "Get all styles (color, text, effects, grids) from a Figma file with optional code exports. Use when user asks for: text styles, color palette, design system styles, typography, or style documentation. Returns organized style definitions with resolved values. NOT for design tokens/variables (use figma_get_variables). Set enrich=true for CSS/Tailwind/Sass code examples. Supports verbosity control to manage payload size.",
2051
+ inputSchema: {
2052
+ fileUrl: z
2053
+ .string()
2054
+ .url()
2055
+ .optional()
2056
+ .describe("Figma file URL (e.g., https://figma.com/design/abc123). REQUIRED unless figma_navigate was already called. If not provided, ask the user to share their Figma file URL (they can copy it from Figma Desktop via right-click → 'Copy link')."),
2057
+ verbosity: z
2058
+ .enum(["summary", "standard", "full"])
2059
+ .optional()
2060
+ .default("standard")
2061
+ .describe("Controls payload size: 'summary' (names/types only, ~85% smaller), 'standard' (essential properties, ~40% smaller), 'full' (everything). Default: standard"),
2062
+ enrich: z
2063
+ .boolean()
2064
+ .optional()
2065
+ .describe("Set to true when user asks for: CSS/Sass/Tailwind code, export formats, usage information, code examples, or design system exports. Adds resolved values, usage analysis, and export format examples. Default: false for backward compatibility"),
2066
+ include_usage: z
2067
+ .boolean()
2068
+ .optional()
2069
+ .describe("Include component usage information (requires enrich=true)"),
2070
+ include_exports: z
2071
+ .boolean()
2072
+ .optional()
2073
+ .describe("Include export format examples (requires enrich=true)"),
2074
+ export_formats: z
2075
+ .array(z.enum(["css", "sass", "tailwind", "typescript", "json"]))
2076
+ .optional()
2077
+ .describe("Which code formats to generate examples for. Use when user mentions specific formats like 'CSS', 'Tailwind', 'SCSS', 'TypeScript', etc. Automatically enables enrichment. Default: all formats"),
2078
+ },
2079
+ annotations: { readOnlyHint: true },
2064
2080
  }, async ({ fileUrl, verbosity, enrich, include_usage, include_exports, export_formats }) => {
2065
2081
  try {
2066
2082
  let api;
@@ -2179,27 +2195,31 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
2179
2195
  }
2180
2196
  });
2181
2197
  // Tool 12: Get Component Image (Visual Reference)
2182
- server.tool("figma_get_component_image", "Render a specific component or node as an image (PNG, JPG, SVG, PDF). Returns image URL valid for 30 days. Use when user asks for: component screenshot, visual preview, rendered output, or 'show me'. NOT for component metadata/properties (use figma_get_component). NOT for getting code/layout data (use figma_get_component_for_development). Best for: visual references, design review, documentation.", {
2183
- fileUrl: z
2184
- .string()
2185
- .url()
2186
- .optional()
2187
- .describe("Figma file URL (e.g., https://figma.com/design/abc123). REQUIRED unless figma_navigate was already called. If not provided, ask the user to share their Figma file URL (they can copy it from Figma Desktop via right-click → 'Copy link')."),
2188
- nodeId: z
2189
- .string()
2190
- .describe("Component node ID to render as image (e.g., '695:313')"),
2191
- scale: z
2192
- .number()
2193
- .min(0.01)
2194
- .max(4)
2195
- .optional()
2196
- .default(2)
2197
- .describe("Image scale factor (0.01-4, default: 2 for high quality)"),
2198
- format: z
2199
- .enum(["png", "jpg", "svg", "pdf"])
2200
- .optional()
2201
- .default("png")
2202
- .describe("Image format (default: png)"),
2198
+ server.registerTool("figma_get_component_image", {
2199
+ description: "Render a specific component or node as an image (PNG, JPG, SVG, PDF). Returns image URL valid for 30 days. Use when user asks for: component screenshot, visual preview, rendered output, or 'show me'. NOT for component metadata/properties (use figma_get_component). NOT for getting code/layout data (use figma_get_component_for_development). Best for: visual references, design review, documentation.",
2200
+ inputSchema: {
2201
+ fileUrl: z
2202
+ .string()
2203
+ .url()
2204
+ .optional()
2205
+ .describe("Figma file URL (e.g., https://figma.com/design/abc123). REQUIRED unless figma_navigate was already called. If not provided, ask the user to share their Figma file URL (they can copy it from Figma Desktop via right-click → 'Copy link')."),
2206
+ nodeId: z
2207
+ .string()
2208
+ .describe("Component node ID to render as image (e.g., '695:313')"),
2209
+ scale: z
2210
+ .number()
2211
+ .min(0.01)
2212
+ .max(4)
2213
+ .optional()
2214
+ .default(2)
2215
+ .describe("Image scale factor (0.01-4, default: 2 for high quality)"),
2216
+ format: z
2217
+ .enum(["png", "jpg", "svg", "pdf"])
2218
+ .optional()
2219
+ .default("png")
2220
+ .describe("Image format (default: png)"),
2221
+ },
2222
+ annotations: { readOnlyHint: true },
2203
2223
  }, async ({ fileUrl, nodeId, scale, format }) => {
2204
2224
  try {
2205
2225
  let api;
@@ -2301,20 +2321,24 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
2301
2321
  }
2302
2322
  });
2303
2323
  // Tool 13: Get Component for Development (UI Implementation)
2304
- server.tool("figma_get_component_for_development", "Get component data optimized for UI implementation, includes rendered image + filtered implementation context (layout, typography, visual properties). Use when user asks to: 'build this component', 'implement this in React/Vue', 'generate code for', or needs both visual reference and technical specs. Automatically includes 2x scale image unless includeImage=false. Best for: UI development, code generation, design-to-code workflows. For just metadata, use figma_get_component; for just image, use figma_get_component_image.", {
2305
- fileUrl: z
2306
- .string()
2307
- .url()
2308
- .optional()
2309
- .describe("Figma file URL (e.g., https://figma.com/design/abc123). REQUIRED unless figma_navigate was already called."),
2310
- nodeId: z
2311
- .string()
2312
- .describe("Component node ID to get data for (e.g., '695:313')"),
2313
- includeImage: z
2314
- .boolean()
2315
- .optional()
2316
- .default(true)
2317
- .describe("Include rendered image for visual reference (default: true)"),
2324
+ server.registerTool("figma_get_component_for_development", {
2325
+ description: "Get component data optimized for UI implementation, includes rendered image + filtered implementation context (layout, typography, visual properties). Use when user asks to: 'build this component', 'implement this in React/Vue', 'generate code for', or needs both visual reference and technical specs. Automatically includes 2x scale image unless includeImage=false. Best for: UI development, code generation, design-to-code workflows. For just metadata, use figma_get_component; for just image, use figma_get_component_image.",
2326
+ inputSchema: {
2327
+ fileUrl: z
2328
+ .string()
2329
+ .url()
2330
+ .optional()
2331
+ .describe("Figma file URL (e.g., https://figma.com/design/abc123). REQUIRED unless figma_navigate was already called."),
2332
+ nodeId: z
2333
+ .string()
2334
+ .describe("Component node ID to get data for (e.g., '695:313')"),
2335
+ includeImage: z
2336
+ .boolean()
2337
+ .optional()
2338
+ .default(true)
2339
+ .describe("Include rendered image for visual reference (default: true)"),
2340
+ },
2341
+ annotations: { readOnlyHint: true },
2318
2342
  }, async ({ fileUrl, nodeId, includeImage }) => {
2319
2343
  try {
2320
2344
  let api;
@@ -2510,23 +2534,27 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
2510
2534
  }
2511
2535
  });
2512
2536
  // Tool 14: Get File for Plugin Development
2513
- server.tool("figma_get_file_for_plugin", "Get file data optimized for plugin development with filtered properties (IDs, structure, plugin data, component relationships). Excludes visual properties (fills, strokes, effects) to reduce payload. Use when user asks for: plugin development, file structure for manipulation, node IDs for plugin API. NOT for component descriptions (use figma_get_component). NOT for visual/styling data (use figma_get_component_for_development). Supports deeper tree traversal (max depth=5) than figma_get_file_data.", {
2514
- fileUrl: z
2515
- .string()
2516
- .url()
2517
- .optional()
2518
- .describe("Figma file URL (e.g., https://figma.com/design/abc123). REQUIRED unless figma_navigate was already called."),
2519
- depth: z
2520
- .number()
2521
- .min(0)
2522
- .max(5)
2523
- .optional()
2524
- .default(2)
2525
- .describe("How many levels of children to include (default: 2, max: 5). Higher depths are safe here due to filtering."),
2526
- nodeIds: z
2527
- .array(z.string())
2528
- .optional()
2529
- .describe("Specific node IDs to retrieve (optional)"),
2537
+ server.registerTool("figma_get_file_for_plugin", {
2538
+ description: "Get file data optimized for plugin development with filtered properties (IDs, structure, plugin data, component relationships). Excludes visual properties (fills, strokes, effects) to reduce payload. Use when user asks for: plugin development, file structure for manipulation, node IDs for plugin API. NOT for component descriptions (use figma_get_component). NOT for visual/styling data (use figma_get_component_for_development). Supports deeper tree traversal (max depth=5) than figma_get_file_data.",
2539
+ inputSchema: {
2540
+ fileUrl: z
2541
+ .string()
2542
+ .url()
2543
+ .optional()
2544
+ .describe("Figma file URL (e.g., https://figma.com/design/abc123). REQUIRED unless figma_navigate was already called."),
2545
+ depth: z
2546
+ .number()
2547
+ .min(0)
2548
+ .max(5)
2549
+ .optional()
2550
+ .default(2)
2551
+ .describe("How many levels of children to include (default: 2, max: 5). Higher depths are safe here due to filtering."),
2552
+ nodeIds: z
2553
+ .array(z.string())
2554
+ .optional()
2555
+ .describe("Specific node IDs to retrieve (optional)"),
2556
+ },
2557
+ annotations: { readOnlyHint: true },
2530
2558
  }, async ({ fileUrl, depth, nodeIds }) => {
2531
2559
  try {
2532
2560
  let api;
@@ -2699,23 +2727,27 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
2699
2727
  // Tool 15: Capture Screenshot via Plugin (F-MCP ATezer Bridge)
2700
2728
  // This uses exportAsync() which reads the current plugin runtime state, not the cloud state
2701
2729
  // Solves race condition where REST API screenshots show stale data after changes
2702
- server.tool("figma_capture_screenshot", "Capture a screenshot of a node using the plugin's exportAsync API. IMPORTANT: This tool captures the CURRENT state from the plugin runtime (not cloud state like REST API), making it reliable for validating changes immediately after making them. Use this instead of figma_get_component_image when you need to verify that changes were applied correctly. Requires F-MCP ATezer Bridge connection (Figma Desktop with plugin running).", {
2703
- nodeId: z
2704
- .string()
2705
- .optional()
2706
- .describe("ID of the node to capture (e.g., '1:234'). If not provided, captures the current page."),
2707
- format: z
2708
- .enum(["PNG", "JPG", "SVG"])
2709
- .optional()
2710
- .default("PNG")
2711
- .describe("Image format (default: PNG)"),
2712
- scale: z
2713
- .number()
2714
- .min(0.5)
2715
- .max(4)
2716
- .optional()
2717
- .default(2)
2718
- .describe("Scale factor (default: 2 for 2x resolution)"),
2730
+ server.registerTool("figma_capture_screenshot", {
2731
+ description: "Capture a screenshot of a node using the plugin's exportAsync API. IMPORTANT: This tool captures the CURRENT state from the plugin runtime (not cloud state like REST API), making it reliable for validating changes immediately after making them. Use this instead of figma_get_component_image when you need to verify that changes were applied correctly. Requires F-MCP ATezer Bridge connection (Figma Desktop with plugin running).",
2732
+ inputSchema: {
2733
+ nodeId: z
2734
+ .string()
2735
+ .optional()
2736
+ .describe("ID of the node to capture (e.g., '1:234'). If not provided, captures the current page."),
2737
+ format: z
2738
+ .enum(["PNG", "JPG", "SVG"])
2739
+ .optional()
2740
+ .default("PNG")
2741
+ .describe("Image format (default: PNG)"),
2742
+ scale: z
2743
+ .number()
2744
+ .min(0.5)
2745
+ .max(4)
2746
+ .optional()
2747
+ .default(2)
2748
+ .describe("Scale factor (default: 2 for 2x resolution)"),
2749
+ },
2750
+ annotations: { readOnlyHint: true },
2719
2751
  }, async ({ nodeId, format, scale }) => {
2720
2752
  try {
2721
2753
  // Prefer connector (works with Plugin Bridge WebSocket, no CDP needed)
@@ -2798,15 +2830,19 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
2798
2830
  // Tool 16: Set Instance Properties (F-MCP ATezer Bridge)
2799
2831
  // Updates component properties on an instance using setProperties()
2800
2832
  // This is the correct way to update TEXT/BOOLEAN/VARIANT properties on component instances
2801
- server.tool("figma_set_instance_properties", "Update component properties on a component instance. IMPORTANT: Use this tool instead of trying to edit text nodes directly when working with component instances. Components often expose TEXT, BOOLEAN, INSTANCE_SWAP, and VARIANT properties that control their content. Direct text node editing may fail silently if the component uses properties. This tool handles the #nodeId suffix pattern automatically. Requires F-MCP ATezer Bridge connection.", {
2802
- nodeId: z
2803
- .string()
2804
- .describe("ID of the INSTANCE node to update (e.g., '1:234'). Must be a component instance, not a regular frame."),
2805
- properties: z
2806
- .record(z.string(), z.union([z.string(), z.boolean()]))
2807
- .describe("Properties to set. Keys are property names (e.g., 'Label', 'Show Icon', 'Size'). " +
2808
- "Values are strings for TEXT/VARIANT properties, booleans for BOOLEAN properties. " +
2809
- "The tool automatically handles the #nodeId suffix for TEXT/BOOLEAN/INSTANCE_SWAP properties."),
2833
+ server.registerTool("figma_set_instance_properties", {
2834
+ description: "Update component properties on a component instance. IMPORTANT: Use this tool instead of trying to edit text nodes directly when working with component instances. Components often expose TEXT, BOOLEAN, INSTANCE_SWAP, and VARIANT properties that control their content. Direct text node editing may fail silently if the component uses properties. This tool handles the #nodeId suffix pattern automatically. Requires F-MCP ATezer Bridge connection.",
2835
+ inputSchema: {
2836
+ nodeId: z
2837
+ .string()
2838
+ .describe("ID of the INSTANCE node to update (e.g., '1:234'). Must be a component instance, not a regular frame."),
2839
+ properties: z
2840
+ .record(z.string(), z.union([z.string(), z.boolean()]))
2841
+ .describe("Properties to set. Keys are property names (e.g., 'Label', 'Show Icon', 'Size'). " +
2842
+ "Values are strings for TEXT/VARIANT properties, booleans for BOOLEAN properties. " +
2843
+ "The tool automatically handles the #nodeId suffix for TEXT/BOOLEAN/INSTANCE_SWAP properties."),
2844
+ },
2845
+ annotations: { destructiveHint: true },
2810
2846
  }, async ({ nodeId, properties }) => {
2811
2847
  try {
2812
2848
  let result = null;