Cursor Talk To Figma MCP源码漫游:从入口到插件通信

Cursor Talk To Figma MCP源码漫游:从入口到插件通信

【免费下载链接】cursor-talk-to-figma-mcp Cursor Talk To Figma MCP 【免费下载链接】cursor-talk-to-figma-mcp 项目地址: https://gitcode.com/gh_mirrors/cu/cursor-talk-to-figma-mcp

项目架构全景图

Cursor Talk To Figma MCP实现了Cursor AI与Figma之间的模型上下文协议(Model Context Protocol, MCP)集成,通过三级架构实现AI与设计工具的双向通信:

mermaid

核心技术栈

组件技术选型作用
MCP服务器TypeScript + @modelcontextprotocol/sdk实现AI工具通信协议
WebSocket服务器Bun + ws建立实时通信通道
Figma插件JavaScript + Figma Plugin API操作设计文档
构建工具Bun + tsup包管理与代码构建
类型系统Zod数据验证与接口定义

代码入口解析

1. 命令行入口:server.ts

#!/usr/bin/env node

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import WebSocket from "ws";

// 初始化MCP服务器
const server = new McpServer({
  name: "TalkToFigmaMCP",
  version: "1.0.0",
});

// 注册工具函数(后文详述)
server.tool("get_document_info", ...);
server.tool("create_rectangle", ...);
// ...更多工具注册

// 启动服务器
const transport = new StdioServerTransport();
server.serve(transport);

关键设计: 通过#!/usr/bin/env node声明为可执行文件,package.json中配置为bin入口,实现bunx cursor-talk-to-figma-mcp命令启动。

2. 通信枢纽:socket.ts

WebSocket服务器作为通信核心,采用信道(channel)机制管理多客户端连接:

// 信道管理核心数据结构
const channels = new Map<string, Set<ServerWebSocket<any>>>();

// 连接处理流程
function handleConnection(ws: ServerWebSocket<any>) {
  // 1. 发送欢迎消息
  ws.send(JSON.stringify({
    type: "system",
    message: "Please join a channel to start chatting",
  }));
  
  // 2. 处理消息路由
  ws.on("message", (message) => {
    const data = JSON.parse(message);
    if (data.type === "join") {
      joinChannel(ws, data.channel);  // 加入信道
    } else if (data.type === "message") {
      broadcastMessage(ws, data);     // 广播消息
    }
  });
  
  // 3. 断开清理
  ws.on("close", () => {
    channels.forEach(clients => clients.delete(ws));
  });
}

服务器启动流程:

const server = Bun.serve({
  port: 3055,
  // Windows WSL兼容性配置
  // hostname: "0.0.0.0",
  fetch(req, server) {
    // 处理CORS预检请求
    if (req.method === "OPTIONS") {
      return new Response(null, {
        headers: {
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
        },
      });
    }
    
    // 升级WebSocket连接
    if (server.upgrade(req)) return;
    
    // 常规HTTP响应
    return new Response("WebSocket server running");
  },
  websocket: {
    open: handleConnection,
    message: handleMessage,
    close: handleClose
  }
});

通信协议深度剖析

请求响应生命周期

mermaid

核心数据结构

命令消息格式:

interface CommandMessage {
  id: string;          // UUID标识
  type: string;        // 命令类型
  payload: object;     // 命令参数
  channel: string;     // 目标信道
  timestamp: number;   // 时间戳
}

响应消息格式:

interface ResponseMessage {
  id: string;          // 对应命令ID
  result?: any;        // 成功结果
  error?: {            // 错误信息
    message: string;
    code: number;
  };
  timestamp: number;
}

进度更新格式:

interface CommandProgressUpdate {
  type: 'command_progress';
  commandId: string;
  status: 'started' | 'in_progress' | 'completed';
  progress: number;       // 0-100
  processedItems: number; // 已处理项数
  totalItems: number;     // 总项数
  message: string;        // 状态描述
}

核心功能实现

1. 组件覆盖传播系统

该功能允许将一个组件实例的覆盖属性传播到多个目标实例,大幅减少重复设计工作:

// 提取源实例覆盖属性
server.tool(
  "get_instance_overrides",
  "提取组件实例的覆盖属性",
  {
    sourceInstanceId: z.string().describe("源实例ID")
  },
  async ({ sourceInstanceId }) => {
    const result = await sendCommandToFigma("get_instance_overrides", {
      sourceInstanceId
    });
    return {
      content: [{ type: "text", text: JSON.stringify(result) }]
    };
  }
);

// 应用覆盖属性到目标实例
server.tool(
  "set_instance_overrides",
  "将覆盖属性应用到目标实例",
  {
    targetInstanceIds: z.array(z.string()).describe("目标实例ID列表"),
    overrides: z.object({}).describe("从get_instance_overrides获取的覆盖属性")
  },
  async ({ targetInstanceIds, overrides }) => {
    // 分块处理大量目标实例
    const CHUNK_SIZE = 10;
    const results = [];
    
    for (let i = 0; i < targetInstanceIds.length; i += CHUNK_SIZE) {
      const chunk = targetInstanceIds.slice(i, i + CHUNK_SIZE);
      const result = await sendCommandToFigma("set_instance_overrides", {
        targetInstanceIds: chunk,
        overrides
      });
      results.push(result);
      
      // 发送进度更新
      sendProgressUpdate({
        commandId: currentCommandId,
        status: 'in_progress',
        progress: (i / targetInstanceIds.length) * 100,
        processedItems: i,
        totalItems: targetInstanceIds.length,
        currentChunk: i/CHUNK_SIZE + 1,
        totalChunks: Math.ceil(targetInstanceIds.length/CHUNK_SIZE),
        message: `已处理 ${i} 个实例`
      });
    }
    
    return {
      content: [{ type: "text", text: JSON.stringify(mergeResults(results)) }]
    };
  }
);

2. 批量文本替换引擎

针对大型设计文件的文本更新需求,实现智能分块扫描与批量替换:

// 文本节点扫描实现
async function scanTextNodesRecursive(node: SceneNode, textNodes: TextNode[] = [], chunkSize = 50) {
  // 如果是文本节点且包含内容,添加到结果
  if (node.type === "TEXT" && node.characters.trim().length > 0) {
    textNodes.push(node);
    
    // 每达到chunkSize就发送进度更新
    if (textNodes.length % chunkSize === 0) {
      sendProgressUpdate({
        commandType: "scan_text_nodes",
        status: "in_progress",
        processedItems: textNodes.length,
        message: `已扫描 ${textNodes.length} 个文本节点`
      });
    }
  }
  
  // 递归处理子节点
  if ("children" in node) {
    for (const child of node.children) {
      // 递归扫描子节点
      await scanTextNodesRecursive(child, textNodes, chunkSize);
    }
  }
  
  return textNodes;
}

// 批量更新实现
async function batchUpdateTextNodes(textUpdates: Array<{id: string, text: string}>, batchSize = 10) {
  const results = [];
  
  // 分批处理更新
  for (let i = 0; i < textUpdates.length; i += batchSize) {
    const batch = textUpdates.slice(i, i + batchSize);
    const result = await sendCommandToFigma("set_multiple_text_contents", {
      updates: batch
    });
    
    results.push(result);
    
    // 发送进度更新
    sendProgressUpdate({
      commandType: "set_multiple_text_contents",
      status: "in_progress",
      progress: (i / textUpdates.length) * 100,
      processedItems: i,
      totalItems: textUpdates.length,
      currentChunk: i/batchSize + 1,
      totalChunks: Math.ceil(textUpdates.length/batchSize),
      message: `已更新 ${i} 个文本节点`
    });
  }
  
  return results;
}

2. 原型连接器生成

将Figma原型交互转换为可视化连接器线条,提升流程图可读性:

// 获取原型交互
server.tool(
  "get_reactions",
  "获取原型交互信息",
  {},
  async () => {
    const result = await sendCommandToFigma("get_reactions");
    return {
      content: [{ type: "text", text: JSON.stringify(result) }]
    };
  }
);

// 创建连接器
server.tool(
  "create_connections",
  "基于原型交互创建连接器",
  {
    reactions: z.array(z.object({
      sourceId: z.string(),
      destinationId: z.string(),
      description: z.string().optional()
    })).describe("原型交互信息数组"),
    connectorStyleId: z.string().optional().describe("连接器样式ID")
  },
  async ({ reactions, connectorStyleId }) => {
    // 分块处理大量连接器创建
    const BATCH_SIZE = 5;
    const results = [];
    
    for (let i = 0; i < reactions.length; i += BATCH_SIZE) {
      const batch = reactions.slice(i, i + BATCH_SIZE);
      const result = await sendCommandToFigma("create_connections", {
        reactions: batch,
        connectorStyleId
      });
      
      results.push(result);
      
      // 发送进度更新
      sendProgressUpdate({
        commandType: "create_connections",
        status: "in_progress",
        progress: (i / reactions.length) * 100,
        processedItems: i,
        totalItems: reactions.length,
        message: `已创建 ${i} 个连接器`
      });
    }
    
    return {
      content: [{ type: "text", text: JSON.stringify(results) }]
    };
  }
);

插件系统解析

Figma插件作为操作设计文档的实际执行者,通过manifest.json声明其能力:

{
  "name": "Cursor MCP Plugin",
  "id": "cursor-mcp-plugin",
  "api": "1.0.0",
  "main": "code.js",
  "ui": "ui.html",
  "editorType": ["figma", "figjam"],
  "networkAccess": {
    "allowedDomains": ["https://google.com"],
    "devAllowedDomains": ["http://localhost:3055", "ws://localhost:3055"]
  },
  "documentAccess": "dynamic-page",
  "enableProposedApi": true
}

插件核心通信逻辑:

// code.js - Figma插件入口
let ws;
let currentChannel = null;

// 建立WebSocket连接
function connectWebSocket() {
  // 开发环境配置
  const wsUrl = "ws://localhost:3055";
  ws = new WebSocket(wsUrl);
  
  ws.onopen = () => {
    console.log("WebSocket连接已建立");
    showUI(); // 连接成功后显示UI
  };
  
  ws.onmessage = (event) => {
    const message = JSON.parse(event.data);
    handleCommand(message); // 处理命令
  };
  
  ws.onerror = (error) => {
    console.error("WebSocket错误:", error);
  };
  
  ws.onclose = () => {
    console.log("WebSocket连接已关闭");
    // 自动重连机制
    setTimeout(connectWebSocket, 3000);
  };
}

// 命令处理分发
function handleCommand(message) {
  const { id, type, payload } = message;
  
  try {
    switch (type) {
      case "get_document_info":
        return sendResponse(id, figma.root.toJSON());
      case "get_selection":
        return sendResponse(id, figma.currentPage.selection.map(n => n.toJSON()));
      case "create_rectangle":
        return createRectangle(payload, id);
      // ...其他命令处理
      default:
        return sendError(id, `未知命令: ${type}`);
    }
  } catch (error) {
    sendError(id, error.message);
  }
}

// 发送响应
function sendResponse(id, result) {
  ws.send(JSON.stringify({
    id,
    result,
    timestamp: Date.now()
  }));
}

// 发送错误
function sendError(id, message) {
  ws.send(JSON.stringify({
    id,
    error: { message },
    timestamp: Date.now()
  }));
}

// 启动连接
connectWebSocket();

项目构建与部署

构建流程

mermaid

关键脚本解析

package.json中定义的核心脚本:

{
  "scripts": {
    "start": "bun run dist/server.js",         // 启动MCP服务器
    "socket": "bun run src/socket.ts",         // 启动WebSocket服务器
    "setup": "./scripts/setup.sh",             // 项目初始化
    "build": "tsup",                           // 构建项目
    "build:watch": "tsup --watch",             // 监视模式构建
    "dev": "bun run build:watch",              // 开发模式
    "pub:release": "bun run build && npm publish" // 发布到npm
  }
}

多环境配置

针对不同开发环境的配置策略:

环境WebSocket地址构建命令调试方式
开发环境ws://localhost:3055bun run devFigma开发插件
生产环境wss://mcp-server.example.combun run build已发布插件
Windows WSLws://0.0.0.0:3055需修改socket.ts本地网络配置

高级功能与最佳实践

1. 大型设计文件处理策略

为避免Figma插件超时和性能问题,系统实现了多层次分块处理:

mermaid

2. 错误处理与重试机制

// 带重试机制的命令发送函数
async function sendCommandWithRetry(commandType: string, payload: any, retries = 3, delayMs = 1000) {
  const commandId = uuidv4();
  
  // 记录命令开始
  logger.info(`发送命令: ${commandType} (ID: ${commandId})`);
  
  return new Promise((resolve, reject) => {
    // 设置超时定时器
    const timeout = setTimeout(() => {
      pendingRequests.delete(commandId);
      reject(new Error(`命令超时: ${commandType}`));
    }, 30000);
    
    // 存储请求
    pendingRequests.set(commandId, {
      resolve,
      reject,
      timeout,
      lastActivity: Date.now()
    });
    
    // 发送命令
    try {
      ws.send(JSON.stringify({
        id: commandId,
        type: commandType,
        payload,
        channel: currentChannel,
        timestamp: Date.now()
      }));
    } catch (error) {
      clearTimeout(timeout);
      pendingRequests.delete(commandId);
      
      // 重试逻辑
      if (retries > 0) {
        logger.warn(`命令发送失败,重试(${retries}): ${error.message}`);
        setTimeout(() => {
          sendCommandWithRetry(commandType, payload, retries - 1, delayMs * 2)
            .then(resolve)
            .catch(reject);
        }, delayMs);
      } else {
        reject(error);
      }
    }
  });
}

3. 性能优化技巧

  1. 选择性数据传输:过滤不必要的节点属性,减少数据传输量
function filterFigmaNode(node: any) {
  // 跳过矢量图节点
  if (node.type === "VECTOR") {
    return null;
  }

  const filtered: any = {
    id: node.id,
    name: node.name,
    type: node.type,
  };

  // 仅包含关键样式属性
  if (node.fills && node.fills.length > 0) {
    filtered.fills = node.fills.map((fill: any) => ({
      type: fill.type,
      color: rgbaToHex(fill.color),
      opacity: fill.opacity
    }));
  }

  // 处理文本内容
  if (node.characters) {
    filtered.characters = node.characters;
  }

  // 递归处理子节点
  if (node.children) {
    filtered.children = node.children
      .map((child: any) => filterFigmaNode(child))
      .filter((child: any) => child !== null);
  }

  return filtered;
}
  1. 颜色格式转换:统一颜色表示格式
function rgbaToHex(color: any): string {
  // 跳过已为十六进制的颜色
  if (color.startsWith('#')) {
    return color;
  }

  const r = Math.round(color.r * 255);
  const g = Math.round(color.g * 255);
  const b = Math.round(color.b * 255);
  const a = Math.round(color.a * 255);

  // 转换为十六进制格式
  return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}${a === 255 ? '' : a.toString(16).padStart(2, '0')}`;
}

扩展与贡献指南

开发新工具的步骤

  1. 定义工具接口:使用Zod定义输入参数
server.tool(
  "tool_name",
  "工具描述",
  {
    param1: z.string().describe("参数1描述"),
    param2: z.number().optional().describe("参数2描述")
  },
  async ({ param1, param2 }) => {
    // 实现工具逻辑
  }
);
  1. 实现Figma插件命令:在插件中添加对应处理函数
// 在插件的handleCommand中添加
case "tool_name":
  return handleToolName(payload, id);

// 实现命令处理函数
function handleToolName(payload, id) {
  // Figma API操作
  const result = figma.currentPage.createRectangle();
  sendResponse(id, result.toJSON());
}
  1. 添加进度更新:对于耗时操作实现进度反馈
// 发送进度更新
function sendProgressUpdate(update: CommandProgressUpdate) {
  if (ws && ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({
      type: 'command_progress',
      ...update,
      timestamp: Date.now()
    }));
  }
}

性能优化检查表

开发新功能时应遵循的性能优化指南:

  •  对超过20个节点的操作使用分块处理
  •  实现进度更新机制,每处理10%发送一次更新
  •  使用filterFigmaNode过滤不必要的属性
  •  批量操作使用try/catch包裹单个操作,允许部分失败
  •  长时间操作添加取消机制
  •  缓存频繁访问的资源(如样式、组件)

未来演进路线

  1. 双向数据流增强:实现Figma操作实时反馈到Cursor

mermaid

  1. 离线操作队列:支持网络中断后操作自动重试

  2. 高级设计分析:集成设计系统合规性检查

  3. 多AI模型集成:支持同时连接多个AI服务

  4. 自定义命令系统:允许用户定义组合操作

总结

Cursor Talk To Figma MCP通过创新的MCP协议实现了AI与设计工具的深度集成,其三层架构设计确保了系统的可扩展性和稳定性。核心亮点包括:

  1. 实时双向通信:基于WebSocket的低延迟消息系统
  2. 分块处理机制:针对大型设计文件的性能优化
  3. 丰富的设计工具集:覆盖从简单编辑到复杂设计自动化
  4. 完善的错误处理:重试机制与详细状态反馈
  5. 跨平台兼容性:支持Windows、macOS和Linux环境

该项目不仅解决了AI与设计工具通信的技术难题,更为开发者提供了构建类似集成的完整框架。通过本文的源码解析,开发者可以快速掌握MCP协议实现、WebSocket通信和Figma插件开发的核心技术,为扩展系统功能或构建新的集成工具奠定基础。

要开始使用或贡献代码,请访问项目仓库:https://gitcode.com/gh_mirrors/cu/cursor-talk-to-figma-mcp

【免费下载链接】cursor-talk-to-figma-mcp Cursor Talk To Figma MCP 【免费下载链接】cursor-talk-to-figma-mcp 项目地址: https://gitcode.com/gh_mirrors/cu/cursor-talk-to-figma-mcp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值