figma-mcp

by JayZeeDesign
Verified
  • figma_mcp
#!/usr/bin/env python3 import sys import os import argparse import json from pathlib import Path # Import the clean_node module from the package from figma_mcp.clean_node import transform_figma_json import requests from dotenv import load_dotenv from mcp.server.fastmcp import FastMCP def main(): """Entry point for the figma-mcp CLI.""" # Load environment variables load_dotenv() # Create an MCP server mcp = FastMCP("figma") # Parse command-line arguments parser = argparse.ArgumentParser(description="Figma MCP Python Server") parser.add_argument( "--figma-api-key", type=str, help="Figma API token to use instead of environment variable", ) args = parser.parse_args() # Use command-line argument if provided, otherwise use environment variable FIGMA_API_TOKEN = args.figma_api_key or os.getenv("FIGMA_API_TOKEN") if not FIGMA_API_TOKEN: print("Error: Figma API token not provided. Please set FIGMA_API_TOKEN environment variable or use --figma-api-key.") sys.exit(1) # Define helper functions def fetch_figma_file(file_key: str, download_file: bool = False): headers = {"X-Figma-Token": FIGMA_API_TOKEN} url = f"https://api.figma.com/v1/files/{file_key}" response = requests.get(url, headers=headers) if response.status_code != 200: return {"error": f"Failed to fetch Figma file: {response.status_code}"} figma_data = response.json() if download_file: with open(f"{file_key}.json", "w") as f: json.dump(figma_data, f, indent=2) return figma_data def extract_prototype_connections(figma_data): connections = [] def traverse_nodes(node): if "children" in node: for child in node.get("children", []): if "transitionNodeID" in child and child.get("transitionNodeID"): connections.append({ "sourceNodeID": child.get("id"), "sourceNodeName": child.get("name", "Unnamed"), "targetNodeID": child.get("transitionNodeID"), "interaction": child.get("transitionDuration", 0), }) traverse_nodes(child) # Start traversal from the document if "document" in figma_data: traverse_nodes(figma_data["document"]) return connections def fetch_figma_nodes(file_key: str, node_ids: str): headers = {"X-Figma-Token": FIGMA_API_TOKEN} url = f"https://api.figma.com/v1/files/{file_key}/nodes?ids={node_ids}" response = requests.get(url, headers=headers) if response.status_code != 200: return {"error": f"Failed to fetch Figma nodes: {response.status_code}"} return response.json() # Register MCP tools @mcp.tool() def get_components(file_key: str) -> list[dict]: """Get components available in a Figma file Args: file_key (str): The file key found in the shared Figma URL Returns: list[dict]: List of components found in the Figma file """ figma_data = fetch_figma_file(file_key) if "error" in figma_data: return [{"error": figma_data["error"]}] components = [] for component_id, component_data in figma_data.get("components", {}).items(): components.append({ "id": component_id, "name": component_data.get("name", "Unnamed Component"), "description": component_data.get("description", ""), }) return components @mcp.tool() def get_node(file_key: str, node_id: str) -> dict: """Get a specific node from a Figma file Args: file_key (str): The file key found in the shared Figma URL, e.g. if url is https://www.figma.com/proto/do4pJqHwNwH1nBrrscu6Ld/Untitled?page-id=0%3A1&node-id=0-3&viewport=361%2C361%2C0.08&t=9SVttILbgMlPWuL0-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=0%3A3, then the file key is do4pJqHwNwH1nBrrscu6Ld node_id (str): The ID of the node to retrieve, has to be in format x:x, e.g. in url it will be like 0-3, but it should be 0:3 Returns: dict: The node data if found, empty dict if not found """ # Convert node_id format if needed (from 0-3 to 0:3) if "-" in node_id and ":" not in node_id: node_id = node_id.replace("-", ":") response = fetch_figma_nodes(file_key, node_id) if "error" in response: return {"error": response["error"]} # Find the node in the response def find_node_by_id(data, target_id): if isinstance(data, dict): if data.get("id") == target_id: return transform_figma_json(data) for key, value in data.items(): result = find_node_by_id(value, target_id) if result: return result elif isinstance(data, list): for item in data: result = find_node_by_id(item, target_id) if result: return result return None # Look for the node in the nodes data for node_key, node_data in response.get("nodes", {}).items(): if node_data.get("document"): node = find_node_by_id(node_data["document"], node_id) if node: return node return {} @mcp.tool() def get_workflow(file_key: str) -> list[dict]: """Get workflows available in a Figma file Args: file_key (str): The file key found in the shared Figma URL, e.g. if url is https://www.figma.com/proto/do4pJqHwNwH1nBrrscu6Ld/Untitled?page-id=0%3A1&node-id=0-3&viewport=361%2C361%2C0.08&t=9SVttILbgMlPWuL0-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=0%3A3, then the file key is do4pJqHwNwH1nBrrscu6Ld Returns: list[dict]: List of workflow connections found in the Figma file """ figma_data = fetch_figma_file(file_key) if "error" in figma_data: return [{"error": figma_data["error"]}] connections = extract_prototype_connections(figma_data) return connections # Start the MCP server print(f"Starting Figma MCP server...") mcp.run() if __name__ == "__main__": main()