diff --git a/.changeset/rotten-islands-kneel.md b/.changeset/rotten-islands-kneel.md
new file mode 100644
index 0000000..10c503a
--- /dev/null
+++ b/.changeset/rotten-islands-kneel.md
@@ -0,0 +1,5 @@
+---
+"figma-flutter-mcp": patch
+---
+
+Figma API key logic improved for API access
diff --git a/.env.example b/.env.example
deleted file mode 100644
index b8ff06b..0000000
--- a/.env.example
+++ /dev/null
@@ -1,14 +0,0 @@
-# Your Figma API access token
-# Get it from your Figma account settings: https://www.figma.com/developers/api#access-tokens
-FIGMA_API_KEY=your_figma_api_key_here
-
-# Figma file configuration
-# This is the ID in your Figma URL: https://www.figma.com/file/{FILE_KEY}/filename
-FIGMA_FILE_ID=your_figma_file_key_here
-
-# Optional: Configuration overrides
-FIGMA_MAX_DEPTH=3
-FIGMA_MAX_COMPONENTS=10
-
-# Server configuration
-PORT=3333
\ No newline at end of file
diff --git a/README.md b/README.md
index ad013f2..f47de77 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
-
Figma to Flutter MCP Server [Early]
+ Figma to Flutter MCP Server
Utilize Figma's rich data in your coding agent.
Implement designs in Flutter way!
@@ -19,8 +19,8 @@
Use [Cursor](https://cursor.sh) or other AI-powered tools to access Figma's rich files, data, components and much more using [MCP server](https://modelcontextprotocol.io/).
-## π Early Release
-Since this is my first time building MCP servers so marking this as βearly,β which means you might encounter bugs or unexpected behavior. As there's always a room for improvements so you can checkout the [issues](https://github.com/mhmzdev/figma-flutter-mcp/issues) to see what else there's to work or to improve.
+## π First Release
+Since this is my first time building MCP servers which means you might encounter bugs or unexpected behavior. As there's always a room for improvements so you can checkout the [issues](https://github.com/mhmzdev/figma-flutter-mcp/issues) to see what else there's to work or to improve.
## π How it works
1. [Components/Widgets](src/extractors/components/)
diff --git a/package.json b/package.json
index 2aab292..41babc7 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"version": "0.1.4",
"description": "MCP server for Figma to Flutter conversion",
"type": "module",
- "main": "dist/server.mjs",
+ "main": "dist/cli.mjs",
"bin": {
"figma-flutter-mcp": "dist/cli.mjs"
},
@@ -14,8 +14,8 @@
],
"scripts": {
"build": "tsc",
- "start": "node dist/server.mjs",
- "dev": "tsx src/server.mts",
+ "start": "node dist/cli.mjs",
+ "dev": "tsx src/cli.mts",
"changeset": "changeset add",
"version": "changeset version && npm install --lockfile-only",
"release": "changeset publish"
diff --git a/src/cli.mts b/src/cli.mts
index 1c917e8..4a19012 100644
--- a/src/cli.mts
+++ b/src/cli.mts
@@ -1,43 +1,19 @@
#!/usr/bin/env node
-
-import {StdioServerTransport} from "@modelcontextprotocol/sdk/server/stdio.js";
-import {resolve} from 'path';
-import {program} from 'commander';
-
-// Parse CLI arguments
-program
- .name('figma-flutter-mcp')
- .description('Figma to Flutter MCP Server')
- .option('--figma-api-key ', 'Figma API key')
- .option('--stdio', 'Run in stdio mode for MCP client communication')
- .parse();
-
-const options = program.opts();
-
-// Set environment variables from CLI args
-if (options.figmaApiKey) {
- process.env.FIGMA_API_KEY = options.figmaApiKey;
-}
-
-// Check if running in stdio mode (same pattern as working MCP)
-const isStdioMode = process.env.NODE_ENV === "cli" || process.argv.includes("--stdio");
+import {getServerConfig} from './config.mjs';
+import {startMcpServer} from './server.mjs';
async function startServer(): Promise {
- if (isStdioMode) {
- // Import and start MCP server in stdio mode
- const {startMcpServer} = await import('./server.mjs');
- await startMcpServer();
+ const config = getServerConfig();
+
+ if (config.isStdioMode) {
+ await startMcpServer(config.figmaApiKey);
} else {
console.log('Starting Figma Flutter MCP Server...');
console.log('Use --stdio flag for MCP client communication');
- console.log('Example: figma-flutter-mcp --figma-api-key=YOUR_KEY --stdio');
}
}
-// If we're being executed directly (not imported), start the server
-if (process.argv[1]) {
- startServer().catch((error) => {
- console.error("Failed to start server:", error);
- process.exit(1);
- });
-}
\ No newline at end of file
+startServer().catch((error) => {
+ console.error("Failed to start server:", error);
+ process.exit(1);
+});
\ No newline at end of file
diff --git a/src/config.mts b/src/config.mts
new file mode 100644
index 0000000..11edd5e
--- /dev/null
+++ b/src/config.mts
@@ -0,0 +1,112 @@
+import {config as loadEnv} from "dotenv";
+import yargs from "yargs";
+import {hideBin} from "yargs/helpers";
+import {resolve} from "path";
+
+export interface ServerConfig {
+ figmaApiKey: string;
+ outputFormat: "yaml" | "json";
+ isStdioMode: boolean;
+ configSources: {
+ figmaApiKey: "cli" | "env";
+ envFile: "cli" | "default";
+ stdio: "cli" | "env" | "default";
+ };
+}
+
+function maskApiKey(key: string): string {
+ if (!key || key.length <= 4) return "****";
+ return `****${key.slice(-4)}`;
+}
+
+interface CliArgs {
+ "figma-api-key"?: string;
+ env?: string;
+ stdio?: boolean;
+}
+
+export function getServerConfig(): ServerConfig {
+ // Parse command line arguments
+ const argv = yargs(hideBin(process.argv))
+ .options({
+ "figma-api-key": {
+ type: "string",
+ description: "Figma API key",
+ },
+ env: {
+ type: "string",
+ description: "Path to custom .env file to load environment variables from",
+ },
+ stdio: {
+ type: "boolean",
+ description: "Run in stdio mode for MCP client communication",
+ default: false,
+ },
+ })
+ .help()
+ .version(process.env.npm_package_version || "0.0.1")
+ .parseSync() as CliArgs;
+
+ // Load environment variables from custom path or default
+ let envFilePath: string;
+ let envFileSource: "cli" | "default";
+
+ if (argv.env) {
+ envFilePath = resolve(argv.env);
+ envFileSource = "cli";
+ } else {
+ envFilePath = resolve(process.cwd(), ".env");
+ envFileSource = "default";
+ }
+
+ // Load .env file with override if custom path provided
+ loadEnv({path: envFilePath, override: !!argv.env});
+
+ const config: ServerConfig = {
+ figmaApiKey: "",
+ outputFormat: "json",
+ isStdioMode: false,
+ configSources: {
+ figmaApiKey: "env",
+ envFile: envFileSource,
+ stdio: "default",
+ },
+ };
+
+ // Handle FIGMA_API_KEY
+ if (argv["figma-api-key"]) {
+ config.figmaApiKey = argv["figma-api-key"];
+ config.configSources.figmaApiKey = "cli";
+ } else if (process.env.FIGMA_API_KEY) {
+ config.figmaApiKey = process.env.FIGMA_API_KEY;
+ config.configSources.figmaApiKey = "env";
+ }
+
+ // Handle stdio mode
+ if (argv.stdio) {
+ config.isStdioMode = true;
+ config.configSources.stdio = "cli";
+ } else if (process.env.NODE_ENV === "cli") {
+ config.isStdioMode = true;
+ config.configSources.stdio = "env";
+ }
+
+ // Validate configuration
+ if (!config.figmaApiKey) {
+ console.error("Error: FIGMA_API_KEY is required (via CLI argument or .env file)");
+ process.exit(1);
+ }
+
+ // Log configuration sources (only in non-stdio mode)
+ if (!config.isStdioMode) {
+ console.log("\nConfiguration:");
+ console.log(`- ENV_FILE: ${envFilePath} (source: ${config.configSources.envFile})`);
+ console.log(
+ `- FIGMA_API_KEY: ${maskApiKey(config.figmaApiKey)} (source: ${config.configSources.figmaApiKey})`
+ );
+ console.log(`- STDIO_MODE: ${config.isStdioMode} (source: ${config.configSources.stdio})`);
+ console.log(); // Empty line for better readability
+ }
+
+ return config;
+}
\ No newline at end of file
diff --git a/src/server.mts b/src/server.mts
index 08da23e..ee40b56 100644
--- a/src/server.mts
+++ b/src/server.mts
@@ -1,32 +1,20 @@
-import 'dotenv/config';
import {McpServer} from "@modelcontextprotocol/sdk/server/mcp.js";
import {StdioServerTransport} from "@modelcontextprotocol/sdk/server/stdio.js";
import {registerAllTools} from "./tools/index.mjs";
-// Create MCP server
-const server = new McpServer({
- name: "figma-flutter-mcp",
- version: "0.1.0"
-});
+export function createServer(figmaApiKey: string) {
+ const server = new McpServer({
+ name: "figma-flutter-mcp",
+ version: process.env.npm_package_version || "0.0.1"
+ });
-// Parse CLI arguments for figma key
-const args = process.argv.slice(2);
-for (const arg of args) {
- const lower = arg.toLowerCase();
- const isKeyArg = lower.startsWith('--figma-api-key=');
- if (isKeyArg) {
- const key = arg.split('=')[1];
- if (key && key.trim().length > 0) {
- process.env.FIGMA_API_KEY = key;
- }
- }
+ registerAllTools(server, figmaApiKey);
+ return server;
}
-// Register all tools
-registerAllTools(server);
-
-export async function startMcpServer(): Promise {
+export async function startMcpServer(figmaApiKey: string): Promise {
try {
+ const server = createServer(figmaApiKey);
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Figma-to-Flutter MCP Server connected via stdio");
@@ -34,9 +22,4 @@ export async function startMcpServer(): Promise {
console.error("Failed to start MCP server:", error);
process.exit(1);
}
-}
-
-// Auto-start if this file is executed directly
-if (import.meta.url === `file://${process.argv[1]}`) {
- await startMcpServer();
}
\ No newline at end of file
diff --git a/src/tools/config.mts b/src/tools/config.mts
deleted file mode 100644
index 4d63d36..0000000
--- a/src/tools/config.mts
+++ /dev/null
@@ -1,4 +0,0 @@
-// src/tools/config.mts
-export function getFigmaToken(): string | null {
- return process.env.FIGMA_API_KEY || null;
-}
\ No newline at end of file
diff --git a/src/tools/flutter/assets/assets.mts b/src/tools/flutter/assets/assets.mts
index 9a0c97b..b6a7f0b 100644
--- a/src/tools/flutter/assets/assets.mts
+++ b/src/tools/flutter/assets/assets.mts
@@ -2,7 +2,6 @@
import {z} from "zod";
import type {McpServer} from "@modelcontextprotocol/sdk/server/mcp.js";
import {FigmaService} from "../../../services/figma.mjs";
-import {getFigmaToken} from "../../config.mjs";
import {join} from 'path';
import {
createAssetsDirectory,
@@ -15,7 +14,7 @@ import {
type AssetInfo
} from "./asset-manager.mjs";
-export function registerFlutterAssetTools(server: McpServer) {
+export function registerFlutterAssetTools(server: McpServer, figmaApiKey: string) {
// Tool: Export Flutter Assets
server.registerTool(
"export_flutter_assets",
@@ -32,7 +31,7 @@ export function registerFlutterAssetTools(server: McpServer) {
}
},
async ({fileId, nodeIds, projectPath = process.cwd(), format = 'png', scale = 2, includeMultipleResolutions = false}) => {
- const token = getFigmaToken();
+ const token = figmaApiKey;
if (!token) {
return {
content: [{
diff --git a/src/tools/flutter/assets/svg-assets.mts b/src/tools/flutter/assets/svg-assets.mts
index b40307b..39f2d86 100644
--- a/src/tools/flutter/assets/svg-assets.mts
+++ b/src/tools/flutter/assets/svg-assets.mts
@@ -2,7 +2,6 @@
import {z} from "zod";
import type {McpServer} from "@modelcontextprotocol/sdk/server/mcp.js";
import {FigmaService} from "../../../services/figma.mjs";
-import {getFigmaToken} from "../../config.mjs";
import {join} from 'path';
import {
createSvgAssetsDirectory,
@@ -14,7 +13,7 @@ import {
type AssetInfo
} from "./asset-manager.mjs";
-export function registerSvgAssetTools(server: McpServer) {
+export function registerSvgAssetTools(server: McpServer, figmaApiKey: string) {
// Tool: Export SVG Flutter Assets
server.registerTool(
"export_svg_flutter_assets",
@@ -28,7 +27,7 @@ export function registerSvgAssetTools(server: McpServer) {
}
},
async ({fileId, nodeIds, projectPath = process.cwd()}) => {
- const token = getFigmaToken();
+ const token = figmaApiKey;
if (!token) {
return {
content: [{
diff --git a/src/tools/flutter/components/component-tool.mts b/src/tools/flutter/components/component-tool.mts
index 67c5711..3dbcbec 100644
--- a/src/tools/flutter/components/component-tool.mts
+++ b/src/tools/flutter/components/component-tool.mts
@@ -3,7 +3,6 @@
import {z} from "zod";
import type {McpServer} from "@modelcontextprotocol/sdk/server/mcp.js";
import {FigmaService} from "../../../services/figma.mjs";
-import {getFigmaToken} from "../../config.mjs";
import {
ComponentExtractor,
VariantAnalyzer,
@@ -30,7 +29,7 @@ import {
} from "../assets/asset-manager.mjs";
import {join} from 'path';
-export function registerComponentTools(server: McpServer) {
+export function registerComponentTools(server: McpServer, figmaApiKey: string) {
// Main component analysis tool
server.registerTool(
@@ -50,7 +49,7 @@ export function registerComponentTools(server: McpServer) {
}
},
async ({input, nodeId, userDefinedComponent = false, maxChildNodes = 10, includeVariants = true, variantSelection, projectPath = process.cwd(), exportAssets = true}) => {
- const token = getFigmaToken();
+ const token = figmaApiKey;
if (!token) {
return {
content: [{
@@ -231,7 +230,7 @@ export function registerComponentTools(server: McpServer) {
}
},
async ({input, nodeId}) => {
- const token = getFigmaToken();
+ const token = figmaApiKey;
if (!token) {
return {
content: [{
@@ -318,7 +317,7 @@ export function registerComponentTools(server: McpServer) {
}
},
async ({input, nodeId, userDefinedComponent = false, showAllChildren = false}) => {
- const token = getFigmaToken();
+ const token = figmaApiKey;
if (!token) {
return {
content: [{
diff --git a/src/tools/flutter/index.mts b/src/tools/flutter/index.mts
index 82629fd..2e21fad 100644
--- a/src/tools/flutter/index.mts
+++ b/src/tools/flutter/index.mts
@@ -5,17 +5,17 @@ import {registerSvgAssetTools} from "./assets/svg-assets.mjs";
import {registerComponentTools} from "./components/component-tool.mjs";
import {registerScreenTools} from "./screens/screen-tool.mjs";
-export function registerFlutterTools(server: McpServer) {
+export function registerFlutterTools(server: McpServer, figmaApiKey: string) {
// Register Flutter asset management tools
- registerFlutterAssetTools(server);
+ registerFlutterAssetTools(server, figmaApiKey);
// Register SVG asset management tools
- registerSvgAssetTools(server);
+ registerSvgAssetTools(server, figmaApiKey);
// Register component analysis tools
- registerComponentTools(server);
+ registerComponentTools(server, figmaApiKey);
// Register screen analysis tools
- registerScreenTools(server);
+ registerScreenTools(server, figmaApiKey);
}
diff --git a/src/tools/flutter/screens/screen-tool.mts b/src/tools/flutter/screens/screen-tool.mts
index 354e09c..38a9639 100644
--- a/src/tools/flutter/screens/screen-tool.mts
+++ b/src/tools/flutter/screens/screen-tool.mts
@@ -3,7 +3,6 @@
import {z} from "zod";
import type {McpServer} from "@modelcontextprotocol/sdk/server/mcp.js";
import {FigmaService} from "../../../services/figma.mjs";
-import {getFigmaToken} from "../../config.mjs";
import {
ScreenExtractor,
parseComponentInput,
@@ -27,7 +26,7 @@ import {
} from "../assets/asset-manager.mjs";
import {join} from 'path';
-export function registerScreenTools(server: McpServer) {
+export function registerScreenTools(server: McpServer, figmaApiKey: string) {
// Main screen analysis tool
server.registerTool(
@@ -46,7 +45,7 @@ export function registerScreenTools(server: McpServer) {
}
},
async ({input, nodeId, maxSections = 15, extractNavigation = true, extractAssets = true, projectPath = process.cwd(), deviceTypeDetection = true}) => {
- const token = getFigmaToken();
+ const token = figmaApiKey;
if (!token) {
return {
content: [{
@@ -145,7 +144,7 @@ export function registerScreenTools(server: McpServer) {
}
},
async ({input, nodeId, showAllSections = false}) => {
- const token = getFigmaToken();
+ const token = figmaApiKey;
if (!token) {
return {
content: [{
diff --git a/src/tools/flutter/theme/colors/theme-tool.mts b/src/tools/flutter/theme/colors/theme-tool.mts
index 2eb26df..7c0927c 100644
--- a/src/tools/flutter/theme/colors/theme-tool.mts
+++ b/src/tools/flutter/theme/colors/theme-tool.mts
@@ -2,12 +2,11 @@
import {z} from "zod";
import type {McpServer} from "@modelcontextprotocol/sdk/server/mcp.js";
import {FigmaService} from "../../../../services/figma.mjs";
-import {getFigmaToken} from "../../../config.mjs";
import {extractThemeColors} from "../../../../extractors/colors/index.mjs";
import {SimpleThemeGenerator} from "./theme-generator.mjs";
import {join} from 'path';
-export function registerThemeTools(server: McpServer) {
+export function registerThemeTools(server: McpServer, figmaApiKey: string) {
server.registerTool(
"extract_theme_colors",
{
@@ -21,7 +20,7 @@ export function registerThemeTools(server: McpServer) {
}
},
async ({fileId, nodeId, projectPath = process.cwd(), generateThemeData = false}) => {
- const token = getFigmaToken();
+ const token = figmaApiKey;
if (!token) {
return {
content: [{
@@ -130,7 +129,7 @@ export function registerThemeTools(server: McpServer) {
}
},
async ({fileId, nodeId}) => {
- const token = getFigmaToken();
+ const token = figmaApiKey;
if (!token) {
return {
content: [{
diff --git a/src/tools/flutter/theme/typography/typography-tool.mts b/src/tools/flutter/theme/typography/typography-tool.mts
index ba18e6c..9935654 100644
--- a/src/tools/flutter/theme/typography/typography-tool.mts
+++ b/src/tools/flutter/theme/typography/typography-tool.mts
@@ -3,12 +3,11 @@
import {z} from "zod";
import type {McpServer} from "@modelcontextprotocol/sdk/server/mcp.js";
import {FigmaService} from "../../../../services/figma.mjs";
-import {getFigmaToken} from "../../../config.mjs";
import {extractThemeTypography} from "../../../../extractors/typography/index.mjs";
import {TypographyGenerator} from "./typography-generator.mjs";
import {join} from 'path';
-export function registerTypographyTools(server: McpServer) {
+export function registerTypographyTools(server: McpServer, figmaApiKey: string) {
server.registerTool(
"extract_theme_typography",
{
@@ -23,7 +22,7 @@ export function registerTypographyTools(server: McpServer) {
}
},
async ({fileId, nodeId, projectPath = process.cwd(), generateTextTheme = false, familyVariableName = 'fontFamily'}) => {
- const token = getFigmaToken();
+ const token = figmaApiKey;
if (!token) {
return {
content: [{
@@ -164,7 +163,7 @@ export function registerTypographyTools(server: McpServer) {
}
},
async ({fileId, nodeId}) => {
- const token = getFigmaToken();
+ const token = figmaApiKey;
if (!token) {
return {
content: [{
diff --git a/src/tools/index.mts b/src/tools/index.mts
index abeb7fd..b927be2 100644
--- a/src/tools/index.mts
+++ b/src/tools/index.mts
@@ -4,17 +4,24 @@ import {registerFlutterTools} from "./flutter/index.mjs";
import {registerThemeTools} from "./flutter/theme/colors/theme-tool.mjs";
import {registerTypographyTools} from "./flutter/theme/typography/typography-tool.mjs";
-export function registerAllTools(server: McpServer) {
+export function registerAllTools(server: McpServer, figmaApiKey: string) {
+ console.error('π οΈ Tools Debug - Starting tool registration...');
+
// Register all tool categories
- registerFlutterTools(server);
- registerThemeTools(server);
- registerTypographyTools(server);
-
+ registerFlutterTools(server, figmaApiKey);
+ console.error('π οΈ Tools Debug - Flutter tools registered');
+
+ registerThemeTools(server, figmaApiKey);
+ console.error('π οΈ Tools Debug - Theme tools registered');
+
+ registerTypographyTools(server, figmaApiKey);
+ console.error('π οΈ Tools Debug - Typography tools registered');
+
console.log("π Registered tool categories:");
console.log(" π Flutter tools - Widgets, Screens");
console.log(" ποΈ Export assets - Images, SVGs");
console.log(" π¨ Theme tools - Colors, Typography");
console.log(" π Typography tools - Fonts, Sizes");
-}
-export {getFigmaToken} from "./config.mjs";
\ No newline at end of file
+ console.error('π οΈ Tools Debug - All tools registration complete');
+}
\ No newline at end of file