Skip to content

Commit 7579c4b

Browse files
authored
Merge pull request #22 from mhmzdev/figma-api-setup-fixed
Figma api setup fixed
2 parents ec651d1 + 71dbd71 commit 7579c4b

16 files changed

Lines changed: 178 additions & 119 deletions

File tree

.changeset/rotten-islands-kneel.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"figma-flutter-mcp": patch
3+
---
4+
5+
Figma API key logic improved for API access

.env.example

Lines changed: 0 additions & 14 deletions
This file was deleted.

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
<br>
55

6-
<h1>Figma to Flutter MCP Server [Early]</h1>
6+
<h1>Figma to Flutter MCP Server</h1>
77
<h3>Utilize Figma's rich data in your coding agent.<br/>Implement designs in Flutter way!</h3>
88
<a href="https://npmcharts.com/compare/figma-flutter-mcp?interval=30">
99
<img alt="weekly downloads" src="https://img.shields.io/npm/dm/figma-flutter-mcp.svg">
@@ -19,8 +19,8 @@
1919

2020
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/).
2121

22-
## 📝 Early Release
23-
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.
22+
## 📝 First Release
23+
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.
2424

2525
## 📚 How it works
2626
1. [Components/Widgets](src/extractors/components/)

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "0.1.5",
44
"description": "MCP server for Figma to Flutter conversion",
55
"type": "module",
6-
"main": "dist/server.mjs",
6+
"main": "dist/cli.mjs",
77
"bin": {
88
"figma-flutter-mcp": "dist/cli.mjs"
99
},
@@ -14,8 +14,8 @@
1414
],
1515
"scripts": {
1616
"build": "tsc",
17-
"start": "node dist/server.mjs",
18-
"dev": "tsx src/server.mts",
17+
"start": "node dist/cli.mjs",
18+
"dev": "tsx src/cli.mts",
1919
"changeset": "changeset add",
2020
"version": "changeset version && npm install --lockfile-only",
2121
"release": "changeset publish"

src/cli.mts

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,19 @@
11
#!/usr/bin/env node
2-
3-
import {StdioServerTransport} from "@modelcontextprotocol/sdk/server/stdio.js";
4-
import {resolve} from 'path';
5-
import {program} from 'commander';
6-
7-
// Parse CLI arguments
8-
program
9-
.name('figma-flutter-mcp')
10-
.description('Figma to Flutter MCP Server')
11-
.option('--figma-api-key <key>', 'Figma API key')
12-
.option('--stdio', 'Run in stdio mode for MCP client communication')
13-
.parse();
14-
15-
const options = program.opts();
16-
17-
// Set environment variables from CLI args
18-
if (options.figmaApiKey) {
19-
process.env.FIGMA_API_KEY = options.figmaApiKey;
20-
}
21-
22-
// Check if running in stdio mode (same pattern as working MCP)
23-
const isStdioMode = process.env.NODE_ENV === "cli" || process.argv.includes("--stdio");
2+
import {getServerConfig} from './config.mjs';
3+
import {startMcpServer} from './server.mjs';
244

255
async function startServer(): Promise<void> {
26-
if (isStdioMode) {
27-
// Import and start MCP server in stdio mode
28-
const {startMcpServer} = await import('./server.mjs');
29-
await startMcpServer();
6+
const config = getServerConfig();
7+
8+
if (config.isStdioMode) {
9+
await startMcpServer(config.figmaApiKey);
3010
} else {
3111
console.log('Starting Figma Flutter MCP Server...');
3212
console.log('Use --stdio flag for MCP client communication');
33-
console.log('Example: figma-flutter-mcp --figma-api-key=YOUR_KEY --stdio');
3413
}
3514
}
3615

37-
// If we're being executed directly (not imported), start the server
38-
if (process.argv[1]) {
39-
startServer().catch((error) => {
40-
console.error("Failed to start server:", error);
41-
process.exit(1);
42-
});
43-
}
16+
startServer().catch((error) => {
17+
console.error("Failed to start server:", error);
18+
process.exit(1);
19+
});

src/config.mts

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import {config as loadEnv} from "dotenv";
2+
import yargs from "yargs";
3+
import {hideBin} from "yargs/helpers";
4+
import {resolve} from "path";
5+
6+
export interface ServerConfig {
7+
figmaApiKey: string;
8+
outputFormat: "yaml" | "json";
9+
isStdioMode: boolean;
10+
configSources: {
11+
figmaApiKey: "cli" | "env";
12+
envFile: "cli" | "default";
13+
stdio: "cli" | "env" | "default";
14+
};
15+
}
16+
17+
function maskApiKey(key: string): string {
18+
if (!key || key.length <= 4) return "****";
19+
return `****${key.slice(-4)}`;
20+
}
21+
22+
interface CliArgs {
23+
"figma-api-key"?: string;
24+
env?: string;
25+
stdio?: boolean;
26+
}
27+
28+
export function getServerConfig(): ServerConfig {
29+
// Parse command line arguments
30+
const argv = yargs(hideBin(process.argv))
31+
.options({
32+
"figma-api-key": {
33+
type: "string",
34+
description: "Figma API key",
35+
},
36+
env: {
37+
type: "string",
38+
description: "Path to custom .env file to load environment variables from",
39+
},
40+
stdio: {
41+
type: "boolean",
42+
description: "Run in stdio mode for MCP client communication",
43+
default: false,
44+
},
45+
})
46+
.help()
47+
.version(process.env.npm_package_version || "0.0.1")
48+
.parseSync() as CliArgs;
49+
50+
// Load environment variables from custom path or default
51+
let envFilePath: string;
52+
let envFileSource: "cli" | "default";
53+
54+
if (argv.env) {
55+
envFilePath = resolve(argv.env);
56+
envFileSource = "cli";
57+
} else {
58+
envFilePath = resolve(process.cwd(), ".env");
59+
envFileSource = "default";
60+
}
61+
62+
// Load .env file with override if custom path provided
63+
loadEnv({path: envFilePath, override: !!argv.env});
64+
65+
const config: ServerConfig = {
66+
figmaApiKey: "",
67+
outputFormat: "json",
68+
isStdioMode: false,
69+
configSources: {
70+
figmaApiKey: "env",
71+
envFile: envFileSource,
72+
stdio: "default",
73+
},
74+
};
75+
76+
// Handle FIGMA_API_KEY
77+
if (argv["figma-api-key"]) {
78+
config.figmaApiKey = argv["figma-api-key"];
79+
config.configSources.figmaApiKey = "cli";
80+
} else if (process.env.FIGMA_API_KEY) {
81+
config.figmaApiKey = process.env.FIGMA_API_KEY;
82+
config.configSources.figmaApiKey = "env";
83+
}
84+
85+
// Handle stdio mode
86+
if (argv.stdio) {
87+
config.isStdioMode = true;
88+
config.configSources.stdio = "cli";
89+
} else if (process.env.NODE_ENV === "cli") {
90+
config.isStdioMode = true;
91+
config.configSources.stdio = "env";
92+
}
93+
94+
// Validate configuration
95+
if (!config.figmaApiKey) {
96+
console.error("Error: FIGMA_API_KEY is required (via CLI argument or .env file)");
97+
process.exit(1);
98+
}
99+
100+
// Log configuration sources (only in non-stdio mode)
101+
if (!config.isStdioMode) {
102+
console.log("\nConfiguration:");
103+
console.log(`- ENV_FILE: ${envFilePath} (source: ${config.configSources.envFile})`);
104+
console.log(
105+
`- FIGMA_API_KEY: ${maskApiKey(config.figmaApiKey)} (source: ${config.configSources.figmaApiKey})`
106+
);
107+
console.log(`- STDIO_MODE: ${config.isStdioMode} (source: ${config.configSources.stdio})`);
108+
console.log(); // Empty line for better readability
109+
}
110+
111+
return config;
112+
}

src/server.mts

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,25 @@
1-
import 'dotenv/config';
21
import {McpServer} from "@modelcontextprotocol/sdk/server/mcp.js";
32
import {StdioServerTransport} from "@modelcontextprotocol/sdk/server/stdio.js";
43
import {registerAllTools} from "./tools/index.mjs";
54

6-
// Create MCP server
7-
const server = new McpServer({
8-
name: "figma-flutter-mcp",
9-
version: "0.1.0"
10-
});
5+
export function createServer(figmaApiKey: string) {
6+
const server = new McpServer({
7+
name: "figma-flutter-mcp",
8+
version: process.env.npm_package_version || "0.0.1"
9+
});
1110

12-
// Parse CLI arguments for figma key
13-
const args = process.argv.slice(2);
14-
for (const arg of args) {
15-
const lower = arg.toLowerCase();
16-
const isKeyArg = lower.startsWith('--figma-api-key=');
17-
if (isKeyArg) {
18-
const key = arg.split('=')[1];
19-
if (key && key.trim().length > 0) {
20-
process.env.FIGMA_API_KEY = key;
21-
}
22-
}
11+
registerAllTools(server, figmaApiKey);
12+
return server;
2313
}
2414

25-
// Register all tools
26-
registerAllTools(server);
27-
28-
export async function startMcpServer(): Promise<void> {
15+
export async function startMcpServer(figmaApiKey: string): Promise<void> {
2916
try {
17+
const server = createServer(figmaApiKey);
3018
const transport = new StdioServerTransport();
3119
await server.connect(transport);
3220
console.error("Figma-to-Flutter MCP Server connected via stdio");
3321
} catch (error) {
3422
console.error("Failed to start MCP server:", error);
3523
process.exit(1);
3624
}
37-
}
38-
39-
// Auto-start if this file is executed directly
40-
if (import.meta.url === `file://${process.argv[1]}`) {
41-
await startMcpServer();
4225
}

src/tools/config.mts

Lines changed: 0 additions & 4 deletions
This file was deleted.

src/tools/flutter/assets/assets.mts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import {z} from "zod";
33
import type {McpServer} from "@modelcontextprotocol/sdk/server/mcp.js";
44
import {FigmaService} from "../../../services/figma.mjs";
5-
import {getFigmaToken} from "../../config.mjs";
65
import {join} from 'path';
76
import {
87
createAssetsDirectory,
@@ -15,7 +14,7 @@ import {
1514
type AssetInfo
1615
} from "./asset-manager.mjs";
1716

18-
export function registerFlutterAssetTools(server: McpServer) {
17+
export function registerFlutterAssetTools(server: McpServer, figmaApiKey: string) {
1918
// Tool: Export Flutter Assets
2019
server.registerTool(
2120
"export_flutter_assets",
@@ -32,7 +31,7 @@ export function registerFlutterAssetTools(server: McpServer) {
3231
}
3332
},
3433
async ({fileId, nodeIds, projectPath = process.cwd(), format = 'png', scale = 2, includeMultipleResolutions = false}) => {
35-
const token = getFigmaToken();
34+
const token = figmaApiKey;
3635
if (!token) {
3736
return {
3837
content: [{

src/tools/flutter/assets/svg-assets.mts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import {z} from "zod";
33
import type {McpServer} from "@modelcontextprotocol/sdk/server/mcp.js";
44
import {FigmaService} from "../../../services/figma.mjs";
5-
import {getFigmaToken} from "../../config.mjs";
65
import {join} from 'path';
76
import {
87
createSvgAssetsDirectory,
@@ -14,7 +13,7 @@ import {
1413
type AssetInfo
1514
} from "./asset-manager.mjs";
1615

17-
export function registerSvgAssetTools(server: McpServer) {
16+
export function registerSvgAssetTools(server: McpServer, figmaApiKey: string) {
1817
// Tool: Export SVG Flutter Assets
1918
server.registerTool(
2019
"export_svg_flutter_assets",
@@ -28,7 +27,7 @@ export function registerSvgAssetTools(server: McpServer) {
2827
}
2928
},
3029
async ({fileId, nodeIds, projectPath = process.cwd()}) => {
31-
const token = getFigmaToken();
30+
const token = figmaApiKey;
3231
if (!token) {
3332
return {
3433
content: [{

0 commit comments

Comments
 (0)