@fridaplatform-stk/figma2frida-mcp 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,10 +16,10 @@
16
16
 
17
17
  Your AI assistant gets four specialized tools:
18
18
 
19
- 1. **`analyze_design`** - Get design context and generated code from Figma *(main tool)*
20
- 2. **`analyze_frame`** - Screenshot of current Figma selection for visual reference and UI analysis
21
- 3. **`analyze_frame_extraction`** - Screenshot + metadata + design context; then call `get_components_from_library`
22
- 4. **`get_components_from_library`** - Search your design system (Pinecone + Frida AI) by query
19
+ 1. **`figma_get_design_context`** - Generate code from Figma with automatic component matching *(main tool)*
20
+ 2. **`figma_suggest_components`** - Browse and discover design system components
21
+ 3. **`figma_get_screenshot`** - Quick visual reference from Figma
22
+ 4. **`figma_improve_layout`** - Fine-tune generated code to match Figma exactly
23
23
 
24
24
  ## 🔄 How It Works
25
25
 
@@ -37,7 +37,7 @@ Your AI assistant gets four specialized tools:
37
37
  - Connects to Figma Desktop (local MCP server)
38
38
  - Searches your component library in Pinecone
39
39
  - Uses Frida AI for intelligent component matching
40
- - Validates your API token via Firebase
40
+ - Validates your session token via Firebase
41
41
 
42
42
  ## 📦 Installation
43
43
 
@@ -60,10 +60,8 @@ Add to your configuration file:
60
60
  "command": "npx",
61
61
  "args": [
62
62
  "@fridaplatform-stk/figma2frida-mcp",
63
- "--project-id",
64
- "YOUR_PROJECT_ID",
65
- "--frida-api-token",
66
- "YOUR_API_TOKEN"
63
+ "--session-token",
64
+ "YOUR_SESSION_TOKEN"
67
65
  ]
68
66
  }
69
67
  }
@@ -79,10 +77,8 @@ Add to your configuration file:
79
77
  "command": "npx",
80
78
  "args": [
81
79
  "@fridaplatform-stk/figma2frida-mcp",
82
- "--project-id",
83
- "YOUR_PROJECT_ID",
84
- "--frida-api-token",
85
- "YOUR_API_TOKEN"
80
+ "--session-token",
81
+ "YOUR_SESSION_TOKEN"
86
82
  ]
87
83
  }
88
84
  }
@@ -93,24 +89,19 @@ Add to your configuration file:
93
89
 
94
90
  Restart Claude Desktop, Cursor, or your IDE to load the MCP server.
95
91
 
96
- ## 🔑 Getting Access Tokens
92
+ ## 🔑 Getting Session Tokens
97
93
 
98
94
  To use Figma2Frida, you need:
99
- - **Project ID** - Your organization's project identifier
100
- - **Frida API Token** - Authentication token for Frida services
95
+ - **Session token** - Temporary authentication token used by the MCP server
101
96
 
102
- ### How to Get Tokens
97
+ ### How to Get a Session Token
103
98
 
104
- **Contact the Softtek Frida Team:**
99
+ Use the **Frida Code Copilot extension** to create/select a project and connect.
100
+ The extension creates a session automatically and writes MCP settings for you.
105
101
 
106
- 📧 Reach out to your organization's Frida team or Softtek Frida support to request access.
102
+ For external agents (Cursor/Claude/Windsurf), copy the generated config from:
107
103
 
108
- You'll need to provide:
109
- - Your organization name
110
- - Intended use case
111
- - Team/project information
112
-
113
- The Frida team will provide your credentials securely.
104
+ `~/Library/Application Support/Code/User/globalStorage/fridaplatform.fridagpt/fridagpt_mcp_settings.json`
114
105
 
115
106
  ### 💡 Recommended: Frida Code Copilot Extension
116
107
 
@@ -125,7 +116,7 @@ For the best experience, also install the **[Frida Code Copilot VS Code Extensio
125
116
 
126
117
  - **Node.js 18+**
127
118
  - **Figma Desktop** with MCP enabled
128
- - **Frida API Token** (contact Softtek Frida team)
119
+ - **Valid session token** (generated by Frida Code Copilot extension)
129
120
 
130
121
  ## 🚀 Example Usage
131
122
 
@@ -148,13 +139,13 @@ The AI will automatically use the Figma2Frida tools to help you.
148
139
  ## 🐛 Troubleshooting
149
140
 
150
141
  **"Authentication failed"**
151
- → Verify your Frida API token with the Softtek team
142
+ → Verify your `--session-token` is valid and not expired
152
143
 
153
144
  **"Not connected to Figma MCP"**
154
145
  → Make sure Figma Desktop is running with MCP enabled
155
146
 
156
147
  **"Missing required argument"**
157
- → Check both `--project-id` and `--frida-api-token` are in your config
148
+ → Check `--session-token` is present in your config
158
149
 
159
150
  **"Rate limit exceeded"**
160
151
  → Wait a few minutes before trying again
@@ -28,16 +28,6 @@ var writeToLog = (level, message, ...args) => {
28
28
  console.log(fullMessage);
29
29
  }
30
30
  };
31
- var PUBLIC_PREFIX = "FIGMA2FRIDA:";
32
- var writePublic = (message) => {
33
- const line = `${PUBLIC_PREFIX} ${message}
34
- `;
35
- if (isStdioMode) {
36
- process.stderr.write(line);
37
- } else {
38
- console.error(line);
39
- }
40
- };
41
31
  var logger = {
42
32
  debug: (message, ...args) => {
43
33
  if (getLogLevel() <= 0 /* DEBUG */) {
@@ -58,10 +48,6 @@ var logger = {
58
48
  if (getLogLevel() <= 3 /* ERROR */) {
59
49
  writeToLog("ERROR", message, ...args);
60
50
  }
61
- },
62
- /** User-facing message; always written to stderr with FIGMA2FRIDA: prefix. */
63
- public: (message) => {
64
- writePublic(message);
65
51
  }
66
52
  };
67
53
 
@@ -1638,7 +1624,7 @@ var PineconeSearchService = class {
1638
1624
  input: text,
1639
1625
  model: this.fridaEmbeddingModel,
1640
1626
  user_id: "pinecone-search",
1641
- email: "figma2frida_mcp@fridaplatform.com"
1627
+ email: process.env.MCP_USER_EMAIL || "figma2frida_mcp@fridaplatform.com"
1642
1628
  })
1643
1629
  });
1644
1630
  logger.debug(`API response status: ${response.status}`);
@@ -1888,83 +1874,105 @@ ${match.technical || "No technical documentation available"}
1888
1874
  };
1889
1875
 
1890
1876
  // src/figma2frida/services/auth/token-validator.ts
1891
- var VALIDATION_URL = "https://apis.f2fmcp.fridaplatform.online/authenticateMcpLogin";
1892
- async function authenticateMcpLogin(llmopsApiKey, projectId) {
1893
- logger.info("\u{1F510} Authenticating MCP login...");
1877
+ var VALIDATION_URL = "https://us-central1-figma2frida-mcp.cloudfunctions.net/validateSession";
1878
+ async function validateSession(sessionToken) {
1879
+ logger.info("\u{1F510} Validating session...");
1894
1880
  try {
1895
1881
  const response = await fetch(VALIDATION_URL, {
1896
1882
  method: "POST",
1897
1883
  headers: {
1898
1884
  "Content-Type": "application/json"
1899
1885
  },
1900
- body: JSON.stringify({
1901
- data: {
1902
- llmopsApiKey,
1903
- projectId
1904
- }
1905
- })
1886
+ body: JSON.stringify({ sessionToken })
1906
1887
  });
1907
- const responseData = await response.json();
1908
- if (response.ok && "result" in responseData && responseData.result?.valid === true) {
1909
- logger.info(
1910
- `\u2705 ${responseData.result.message || "API token is valid"}`
1911
- );
1912
- return;
1913
- }
1914
- if ("error" in responseData) {
1915
- const error = responseData.error;
1916
- const errorMessage = error.message || "Unknown error";
1917
- const errorStatus = error.status || "UNKNOWN";
1918
- logger.error(`\u274C MCP login authentication failed: ${errorMessage}`);
1919
- logger.error(` Status: ${errorStatus}`);
1920
- if (errorStatus === "RESOURCE_EXHAUSTED") {
1921
- logger.error(" Rate limit exceeded. Please try again later.");
1922
- } else if (errorStatus === "NOT_FOUND") {
1923
- logger.error(" Project not found. Please check your project ID.");
1924
- } else if (errorStatus === "PERMISSION_DENIED") {
1925
- logger.error(
1926
- " Invalid API token. Please check your token and try again."
1927
- );
1928
- }
1929
- throw new Error(
1930
- `Authentication failed: ${errorMessage} (${errorStatus})`
1931
- );
1888
+ const data = await response.json();
1889
+ if (response.ok && data.valid === true) {
1890
+ logger.info(`\u2705 Session valid for ${data.email}`);
1891
+ return {
1892
+ valid: true,
1893
+ projectId: data.projectId,
1894
+ llmopsApiKey: data.llmopsApiKey,
1895
+ userId: data.userId,
1896
+ email: data.email
1897
+ };
1932
1898
  }
1933
- logger.error(
1934
- `\u274C Unexpected authentication response format: ${JSON.stringify(responseData)}`
1935
- );
1936
- throw new Error("Unexpected authentication response format");
1899
+ const errorMessage = data.error || "Unknown error";
1900
+ logger.error(`\u274C Session validation failed: ${errorMessage}`);
1901
+ throw new Error(`Session validation failed: ${errorMessage}`);
1937
1902
  } catch (error) {
1938
- if (error instanceof Error && error.message.startsWith("Authentication failed:")) {
1903
+ if (error instanceof Error && error.message.startsWith("Session validation failed:")) {
1939
1904
  throw error;
1940
1905
  }
1941
- logger.error(
1942
- "\u274C Failed to authenticate MCP login:",
1943
- error instanceof Error ? error.message : String(error)
1944
- );
1906
+ logger.error("\u274C Failed to validate session:", error instanceof Error ? error.message : String(error));
1945
1907
  if (error instanceof Error) {
1946
1908
  if (error.message.includes("fetch failed") || error.message.includes("ECONNREFUSED")) {
1947
- logger.error(" Network error: Could not reach validation service.");
1948
- logger.error(" Please check your internet connection and try again.");
1949
1909
  throw new Error("Network error: Could not reach validation service");
1950
1910
  } else if (error.message.includes("timeout")) {
1951
- logger.error(
1952
- " Request timeout: Validation service did not respond in time."
1953
- );
1954
- logger.error(" Please try again later.");
1955
- throw new Error(
1956
- "Request timeout: Validation service did not respond in time"
1957
- );
1911
+ throw new Error("Request timeout: Validation service did not respond in time");
1958
1912
  }
1959
1913
  }
1960
- throw new Error(
1961
- `Authentication failed: ${error instanceof Error ? error.message : String(error)}`
1962
- );
1914
+ throw new Error(`Session validation failed: ${error instanceof Error ? error.message : String(error)}`);
1963
1915
  }
1964
1916
  }
1965
1917
 
1966
1918
  // src/figma2frida/services/frida/frida-client.ts
1967
1919
  import "dotenv/config";
1920
+ function extractFromResponsesApiOutput(data) {
1921
+ const output = data.output;
1922
+ if (!Array.isArray(output)) {
1923
+ return null;
1924
+ }
1925
+ const textParts = [];
1926
+ for (const item of output) {
1927
+ if (!item || typeof item !== "object") {
1928
+ continue;
1929
+ }
1930
+ const o = item;
1931
+ if (o.type !== "message") {
1932
+ continue;
1933
+ }
1934
+ const content = o.content;
1935
+ if (!Array.isArray(content)) {
1936
+ continue;
1937
+ }
1938
+ for (const block of content) {
1939
+ if (!block || typeof block !== "object") {
1940
+ continue;
1941
+ }
1942
+ const b = block;
1943
+ if (b.type === "output_text" && typeof b.text === "string" && b.text.length > 0) {
1944
+ textParts.push(b.text);
1945
+ }
1946
+ }
1947
+ }
1948
+ const joined = textParts.join("\n").trim();
1949
+ if (!joined) {
1950
+ return null;
1951
+ }
1952
+ const parseSource = data.object === "response" ? "response.output[].message.output_text" : "output[].message.output_text";
1953
+ return { answer: joined, parseSource };
1954
+ }
1955
+ function parseFridaAnswer(data) {
1956
+ if (typeof data.response === "string" && data.response) {
1957
+ return { answer: data.response, parseSource: "response" };
1958
+ }
1959
+ const structured = extractFromResponsesApiOutput(data);
1960
+ if (structured) {
1961
+ return structured;
1962
+ }
1963
+ if (typeof data.output === "string" && data.output) {
1964
+ return { answer: data.output, parseSource: "output" };
1965
+ }
1966
+ const choices = data.choices;
1967
+ const msg = choices?.[0]?.message;
1968
+ if (msg && typeof msg.content === "string" && msg.content) {
1969
+ return { answer: msg.content, parseSource: "choices[0].message.content" };
1970
+ }
1971
+ if (typeof data.content === "string" && data.content) {
1972
+ return { answer: data.content, parseSource: "content" };
1973
+ }
1974
+ return { answer: JSON.stringify(data), parseSource: "fallback_json" };
1975
+ }
1968
1976
  var FridaClient = class {
1969
1977
  apiKey;
1970
1978
  apiUrl;
@@ -2078,7 +2086,7 @@ Example format: tag1, tag2, tag3
2078
2086
  input: prompt,
2079
2087
  max_tokens: this.maxTokens,
2080
2088
  stream: false,
2081
- email: "figma_2frida_mcp@softtek.com"
2089
+ email: process.env.MCP_USER_EMAIL || "figma2frida_mcp@fridaplatform.com"
2082
2090
  })
2083
2091
  });
2084
2092
  const fetchTime = Date.now() - startTime;
@@ -2088,18 +2096,19 @@ Example format: tag1, tag2, tag3
2088
2096
  throw new Error(`API Error: ${response.status} ${response.statusText}`);
2089
2097
  }
2090
2098
  const data = await response.json();
2091
- logger.debug("Response received successfully");
2092
- const answer = data.response || data.output || data.choices?.[0]?.message?.content || data.content || JSON.stringify(data);
2099
+ const { answer, parseSource } = parseFridaAnswer(data);
2093
2100
  const tokensUsed = data.usage?.total_tokens || null;
2094
- const answerLength = typeof answer === "string" ? answer.length : String(answer).length;
2095
- logger.debug("FRIDA AI RESPONSE RECEIVED");
2096
- logger.debug(`TOKENS USED: ${tokensUsed || "N/A"}`);
2097
- logger.debug(`Answer length: ${answerLength} chars`);
2098
- logger.debug(`Total time: ${fetchTime}ms`);
2101
+ const answerPreview = answer.replace(/\s+/g, " ").slice(0, 200);
2102
+ logger.debug(
2103
+ `Frida answer preview (ask): "${answerPreview}" (from "${parseSource}")`
2104
+ );
2105
+ logger.debug(
2106
+ `Frida response OK (ask): HTTP ${response.status} in ${fetchTime}ms \u2014 parsed from "${parseSource}", ${answer.length} chars, tokens=${tokensUsed ?? "n/a"}`
2107
+ );
2099
2108
  return {
2100
2109
  success: true,
2101
2110
  error: null,
2102
- response: typeof answer === "string" ? answer : String(answer),
2111
+ response: answer,
2103
2112
  metadata: {
2104
2113
  model: this.model,
2105
2114
  tokensUsed
@@ -2156,7 +2165,7 @@ Example format: tag1, tag2, tag3
2156
2165
  input: prompt,
2157
2166
  max_tokens: this.maxTokens,
2158
2167
  stream: false,
2159
- email: "figma_2frida_mcp@softtek.com"
2168
+ email: process.env.MCP_USER_EMAIL || "figma2frida_mcp@fridaplatform.com"
2160
2169
  })
2161
2170
  });
2162
2171
  const fetchTime = Date.now() - startTime;
@@ -2166,18 +2175,19 @@ Example format: tag1, tag2, tag3
2166
2175
  throw new Error(`API Error: ${response.status} ${response.statusText}`);
2167
2176
  }
2168
2177
  const data = await response.json();
2169
- logger.debug("Response received successfully");
2170
- const answer = data.response || data.output || data.choices?.[0]?.message?.content || data.content || JSON.stringify(data);
2178
+ const { answer, parseSource } = parseFridaAnswer(data);
2171
2179
  const tokensUsed = data.usage?.total_tokens || null;
2172
- const answerLength = typeof answer === "string" ? answer.length : String(answer).length;
2173
- logger.debug("FRIDA AI FILTER RESPONSE RECEIVED");
2174
- logger.debug(`TOKENS USED: ${tokensUsed || "N/A"}`);
2175
- logger.debug(`Answer length: ${answerLength} chars`);
2176
- logger.debug(`Total time: ${fetchTime}ms`);
2180
+ const answerPreview = answer.replace(/\s+/g, " ").slice(0, 200);
2181
+ logger.debug(
2182
+ `Frida answer preview (filter): "${answerPreview}" (from "${parseSource}")`
2183
+ );
2184
+ logger.debug(
2185
+ `Frida response OK (filter): HTTP ${response.status} in ${fetchTime}ms \u2014 parsed from "${parseSource}", ${answer.length} chars, tokens=${tokensUsed ?? "n/a"}`
2186
+ );
2177
2187
  return {
2178
2188
  success: true,
2179
2189
  error: null,
2180
- response: typeof answer === "string" ? answer : String(answer),
2190
+ response: answer,
2181
2191
  metadata: {
2182
2192
  model: this.model,
2183
2193
  tokensUsed
@@ -2294,12 +2304,12 @@ logger.debug("Loading hardcoded configuration...");
2294
2304
  process.env.PINECONE_INDEX = "figma2frida";
2295
2305
  process.env.PINECONE_MIN_SCORE = "0.4";
2296
2306
  process.env.PINECONE_TOP_K = "12";
2297
- process.env.FRIDA_PINECONE_PROXY_URL = "https://frida.azure-api.net/pinecone-proxy";
2298
- process.env.FRIDA_API_URL = "https://frida.azure-api.net/frida-llm-api/v1/responses";
2299
- process.env.FRIDA_EMBEDDING_URL = "https://frida.azure-api.net/frida-llm-api/v1/embeddings";
2307
+ process.env.FRIDA_PINECONE_PROXY_URL = "https://azf-frida-pinecone-proxy.azurewebsites.net";
2308
+ process.env.FRIDA_API_URL = "https://frida-llm-api-dev.azurewebsites.net/v1/responses";
2309
+ process.env.FRIDA_EMBEDDING_URL = "https://frida-llm-api.azurewebsites.net/v1/embeddings";
2300
2310
  process.env.FRIDA_EMBEDDING_MODEL = "text-embedding-ada-002";
2301
2311
  process.env.FRIDA_MODEL = "gpt-5.1-codex";
2302
- process.env.FRIDA_MAX_TOKENS = "9000";
2312
+ process.env.FRIDA_MAX_TOKENS = "12000";
2303
2313
  process.env.HTTP_STREAM = "false";
2304
2314
  process.env.PORT = "8080";
2305
2315
  logger.debug(`PINECONE_INDEX: ${process.env.PINECONE_INDEX}`);
@@ -2314,35 +2324,28 @@ logger.debug(`FRIDA_MAX_TOKENS: ${process.env.FRIDA_MAX_TOKENS}`);
2314
2324
  logger.debug(`HTTP_STREAM: ${process.env.HTTP_STREAM}`);
2315
2325
  logger.debug(`PORT: ${process.env.PORT}`);
2316
2326
  logger.info("Configuration loaded");
2317
- var argv = yargs(hideBin(process.argv)).option("project-id", {
2318
- type: "string",
2319
- description: "Pinecone namespace (required for component search)",
2320
- demandOption: true
2321
- }).option("frida-api-token", {
2327
+ var argv = yargs(hideBin(process.argv)).option("session-token", {
2322
2328
  type: "string",
2323
- description: "Frida API token for authentication",
2329
+ description: "Session token for authentication",
2324
2330
  demandOption: true
2325
2331
  }).parseSync();
2326
- var PINECONE_NAMESPACE = argv.projectId?.trim();
2327
- if (!PINECONE_NAMESPACE || PINECONE_NAMESPACE === "" || PINECONE_NAMESPACE === "your_namespace_here") {
2328
- logger.error("\u274C SHUTDOWN: Invalid or missing --project-id. This argument is required for Pinecone search.");
2329
- process.exit(1);
2330
- }
2331
- logger.debug(`PINECONE_NAMESPACE: ${PINECONE_NAMESPACE} (from CLI)`);
2332
- var FRIDA_TOKEN = argv.fridaApiToken?.trim();
2333
- if (!FRIDA_TOKEN || FRIDA_TOKEN === "" || FRIDA_TOKEN === "your_api_token_here") {
2334
- logger.error("\u274C SHUTDOWN: Invalid or missing --frida-api-token. This token is required for authentication.");
2332
+ var SESSION_TOKEN = argv.sessionToken?.trim();
2333
+ if (!SESSION_TOKEN || SESSION_TOKEN === "") {
2334
+ logger.error("\u274C SHUTDOWN: Invalid or missing --session-token.");
2335
2335
  process.exit(1);
2336
2336
  }
2337
- process.env.FRIDA_BEARER_TOKEN = FRIDA_TOKEN;
2338
- logger.debug(`FRIDA_API_TOKEN: ${FRIDA_TOKEN.substring(0, 8)}... (hidden)`);
2337
+ var PINECONE_NAMESPACE = "";
2339
2338
  try {
2340
- await authenticateMcpLogin(FRIDA_TOKEN, argv.projectId);
2339
+ const session = await validateSession(SESSION_TOKEN);
2340
+ PINECONE_NAMESPACE = session.projectId;
2341
+ process.env.FRIDA_BEARER_TOKEN = session.llmopsApiKey;
2342
+ process.env.MCP_USER_EMAIL = session.email;
2343
+ process.env.MCP_USER_ID = session.userId;
2344
+ logger.info(`Authenticated as ${session.email}`);
2345
+ logger.debug(`PINECONE_NAMESPACE: ${PINECONE_NAMESPACE}`);
2346
+ logger.debug(`FRIDA_BEARER_TOKEN: ${session.llmopsApiKey.substring(0, 8)}... (hidden)`);
2341
2347
  } catch (error) {
2342
- logger.error("\u274C SHUTDOWN: Authentication failed. MCP server cannot start without valid token.");
2343
- logger.public(
2344
- `\u274C ${error instanceof Error ? error.message : String(error)}`
2345
- );
2348
+ logger.error("\u274C SHUTDOWN: Session validation failed. MCP server cannot start.");
2346
2349
  process.exit(1);
2347
2350
  }
2348
2351
  var HTTP_STREAM = process.env.HTTP_STREAM === "true" || process.env.HTTP_STREAM === "1";
@@ -2670,7 +2673,7 @@ if (HTTP_STREAM) {
2670
2673
  });
2671
2674
  logger.info("Figma Proxy MCP Server running on HTTP Stream!");
2672
2675
  logger.info(`Endpoint: http://localhost:${PORT}/mcp`);
2673
- logger.debug(`Pinecone Namespace: ${PINECONE_NAMESPACE || "not set (use --project-id)"}`);
2676
+ logger.debug(`Pinecone Namespace: ${PINECONE_NAMESPACE}`);
2674
2677
  logger.debug(`
2675
2678
  Test with curl:
2676
2679
  curl -X POST http://localhost:${PORT}/mcp \\