Skip to main content
Glama
andreycretsu

Cursor Talk to Figma MCP

by andreycretsu

set_multiple_text_contents

Update multiple text elements simultaneously within a Figma design node to streamline content modifications and maintain consistency across design components.

Instructions

Set multiple text contents parallelly in a node

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
nodeIdYesThe ID of the node containing the text nodes to replace
textYesArray of text node IDs and their replacement texts

Implementation Reference

  • MCP tool handler for 'set_multiple_text_contents'. Proxies the request to Figma plugin via sendCommandToFigma, handles chunked progress updates, formats success/failure statistics, and provides detailed error reporting for failed replacements.
      async ({ nodeId, text }, extra) => {
        try {
          if (!text || text.length === 0) {
            return {
              content: [
                {
                  type: "text",
                  text: "No text provided",
                },
              ],
            };
          }
    
          // Initial response to indicate we're starting the process
          const initialStatus = {
            type: "text" as const,
            text: `Starting text replacement for ${text.length} nodes. This will be processed in batches of 5...`,
          };
    
          // Track overall progress
          let totalProcessed = 0;
          const totalToProcess = text.length;
    
          // Use the plugin's set_multiple_text_contents function with chunking
          const result = await sendCommandToFigma("set_multiple_text_contents", {
            nodeId,
            text,
          });
    
          // Cast the result to a specific type to work with it safely
          interface TextReplaceResult {
            success: boolean;
            nodeId: string;
            replacementsApplied?: number;
            replacementsFailed?: number;
            totalReplacements?: number;
            completedInChunks?: number;
            results?: Array<{
              success: boolean;
              nodeId: string;
              error?: string;
              originalText?: string;
              translatedText?: string;
            }>;
          }
    
          const typedResult = result as TextReplaceResult;
    
          // Format the results for display
          const success = typedResult.replacementsApplied && typedResult.replacementsApplied > 0;
          const progressText = `
          Text replacement completed:
          - ${typedResult.replacementsApplied || 0} of ${totalToProcess} successfully updated
          - ${typedResult.replacementsFailed || 0} failed
          - Processed in ${typedResult.completedInChunks || 1} batches
          `;
    
          // Detailed results
          const detailedResults = typedResult.results || [];
          const failedResults = detailedResults.filter(item => !item.success);
    
          // Create the detailed part of the response
          let detailedResponse = "";
          if (failedResults.length > 0) {
            detailedResponse = `\n\nNodes that failed:\n${failedResults.map(item =>
              `- ${item.nodeId}: ${item.error || "Unknown error"}`
            ).join('\n')}`;
          }
    
          return {
            content: [
              initialStatus,
              {
                type: "text" as const,
                text: progressText + detailedResponse,
              },
            ],
          };
        } catch (error) {
          return {
            content: [
              {
                type: "text",
                text: `Error setting multiple text contents: ${error instanceof Error ? error.message : String(error)
                  }`,
              },
            ],
          };
        }
      }
    );
  • Zod input schema validation for the tool parameters: nodeId (string) and text (array of {nodeId: string, text: string}).
    {
      nodeId: z
        .string()
        .describe("The ID of the node containing the text nodes to replace"),
      text: z
        .array(
          z.object({
            nodeId: z.string().describe("The ID of the text node"),
            text: z.string().describe("The replacement text"),
          })
        )
        .describe("Array of text node IDs and their replacement texts"),
    },
  • MCP server.tool() registration of the 'set_multiple_text_contents' tool, including description, schema, and handler function.
    server.tool(
      "set_multiple_text_contents",
      "Set multiple text contents parallelly in a node",
      {
        nodeId: z
          .string()
          .describe("The ID of the node containing the text nodes to replace"),
        text: z
          .array(
            z.object({
              nodeId: z.string().describe("The ID of the text node"),
              text: z.string().describe("The replacement text"),
            })
          )
          .describe("Array of text node IDs and their replacement texts"),
      },
      async ({ nodeId, text }, extra) => {
        try {
          if (!text || text.length === 0) {
            return {
              content: [
                {
                  type: "text",
                  text: "No text provided",
                },
              ],
            };
          }
    
          // Initial response to indicate we're starting the process
          const initialStatus = {
            type: "text" as const,
            text: `Starting text replacement for ${text.length} nodes. This will be processed in batches of 5...`,
          };
    
          // Track overall progress
          let totalProcessed = 0;
          const totalToProcess = text.length;
    
          // Use the plugin's set_multiple_text_contents function with chunking
          const result = await sendCommandToFigma("set_multiple_text_contents", {
            nodeId,
            text,
          });
    
          // Cast the result to a specific type to work with it safely
          interface TextReplaceResult {
            success: boolean;
            nodeId: string;
            replacementsApplied?: number;
            replacementsFailed?: number;
            totalReplacements?: number;
            completedInChunks?: number;
            results?: Array<{
              success: boolean;
              nodeId: string;
              error?: string;
              originalText?: string;
              translatedText?: string;
            }>;
          }
    
          const typedResult = result as TextReplaceResult;
    
          // Format the results for display
          const success = typedResult.replacementsApplied && typedResult.replacementsApplied > 0;
          const progressText = `
          Text replacement completed:
          - ${typedResult.replacementsApplied || 0} of ${totalToProcess} successfully updated
          - ${typedResult.replacementsFailed || 0} failed
          - Processed in ${typedResult.completedInChunks || 1} batches
          `;
    
          // Detailed results
          const detailedResults = typedResult.results || [];
          const failedResults = detailedResults.filter(item => !item.success);
    
          // Create the detailed part of the response
          let detailedResponse = "";
          if (failedResults.length > 0) {
            detailedResponse = `\n\nNodes that failed:\n${failedResults.map(item =>
              `- ${item.nodeId}: ${item.error || "Unknown error"}`
            ).join('\n')}`;
          }
    
          return {
            content: [
              initialStatus,
              {
                type: "text" as const,
                text: progressText + detailedResponse,
              },
            ],
          };
        } catch (error) {
          return {
            content: [
              {
                type: "text",
                text: `Error setting multiple text contents: ${error instanceof Error ? error.message : String(error)
                  }`,
              },
            ],
          };
        }
      }
    );
  • Helper function 'setCharacters' likely used by the Figma plugin implementation to safely set text content while preserving fonts and handling mixed font scenarios.
    export const setCharacters = async (node, characters, options) => {
      const fallbackFont = options?.fallbackFont || {
        family: "Roboto",
        style: "Regular",
      };
      try {
        if (node.fontName === figma.mixed) {
          if (options?.smartStrategy === "prevail") {
            const fontHashTree = {};
            for (let i = 1; i < node.characters.length; i++) {
              const charFont = node.getRangeFontName(i - 1, i);
              const key = `${charFont.family}::${charFont.style}`;
              fontHashTree[key] = fontHashTree[key] ? fontHashTree[key] + 1 : 1;
            }
            const prevailedTreeItem = Object.entries(fontHashTree).sort(
              (a, b) => b[1] - a[1]
            )[0];
            const [family, style] = prevailedTreeItem[0].split("::");
            const prevailedFont = {
              family,
              style,
            };
            await figma.loadFontAsync(prevailedFont);
            node.fontName = prevailedFont;
          } else if (options?.smartStrategy === "strict") {
            return setCharactersWithStrictMatchFont(node, characters, fallbackFont);
          } else if (options?.smartStrategy === "experimental") {
            return setCharactersWithSmartMatchFont(node, characters, fallbackFont);
          } else {
            const firstCharFont = node.getRangeFontName(0, 1);
            await figma.loadFontAsync(firstCharFont);
            node.fontName = firstCharFont;
          }
        } else {
          await figma.loadFontAsync({
            family: node.fontName.family,
            style: node.fontName.style,
          });
        }
      } catch (err) {
        console.warn(
          `Failed to load "${node.fontName["family"]} ${node.fontName["style"]}" font and replaced with fallback "${fallbackFont.family} ${fallbackFont.style}"`,
          err
        );
        await figma.loadFontAsync(fallbackFont);
        node.fontName = fallbackFont;
      }
      try {
        node.characters = characters;
        return true;
      } catch (err) {
        console.warn(`Failed to set characters. Skipped.`, err);
        return false;
      }
    };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/andreycretsu/cursor-talk-to-figma-mcp-main'

If you have feedback or need assistance with the MCP directory API, please join our Discord server