figma-coder-mcp 0.1.0 → 0.2.1
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/LICENSE +21 -21
- package/README.md +112 -83
- package/dist/bin.js +138 -7
- package/package.json +53 -53
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 GoldynLabs
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GoldynLabs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,83 +1,112 @@
|
|
|
1
|
-
# figma-coder-mcp
|
|
2
|
-
|
|
3
|
-
MCP server **and** CLI that exposes the Figma → HTML/Tailwind converter to AI
|
|
4
|
-
coding agents (Claude Code, Cursor, …).
|
|
5
|
-
|
|
6
|
-
```bash
|
|
7
|
-
npm i -g figma-coder-mcp # or: npx figma-coder-mcp
|
|
8
|
-
figma-coder-mcp set-token figd_xxx
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
It runs in two modes and picks automatically:
|
|
12
|
-
|
|
13
|
-
- **local** (default when a PAT is present): converts using the bundled
|
|
14
|
-
[`@figma-to-code/core`](../packages/figma-core) directly. **No backend needed** —
|
|
15
|
-
works even if the API server is down. Just provide a Figma PAT.
|
|
16
|
-
- **remote**: delegates to a running `figma-to-html-api` over HTTP (`FIGMA_MCP_API`).
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
#
|
|
36
|
-
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
figma-
|
|
68
|
-
figma-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
1
|
+
# figma-coder-mcp
|
|
2
|
+
|
|
3
|
+
MCP server **and** CLI that exposes the Figma → HTML/Tailwind converter to AI
|
|
4
|
+
coding agents (Claude Code, Cursor, …).
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm i -g figma-coder-mcp # or: npx figma-coder-mcp
|
|
8
|
+
figma-coder-mcp set-token figd_xxx
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
It runs in two modes and picks automatically:
|
|
12
|
+
|
|
13
|
+
- **local** (default when a PAT is present): converts using the bundled
|
|
14
|
+
[`@figma-to-code/core`](../packages/figma-core) directly. **No backend needed** —
|
|
15
|
+
works even if the API server is down. Just provide a Figma PAT.
|
|
16
|
+
- **remote**: delegates to a running `figma-to-html-api` over HTTP (`FIGMA_MCP_API`).
|
|
17
|
+
Authenticate with **OAuth** (`figma-coder-mcp login`) so no personal access
|
|
18
|
+
token lives in your config — the backend holds the OAuth secret.
|
|
19
|
+
|
|
20
|
+
Both modes share the **exact same conversion code** (the core package), so output
|
|
21
|
+
is identical.
|
|
22
|
+
|
|
23
|
+
## Two ways to authenticate
|
|
24
|
+
|
|
25
|
+
| | Option A — Remote + OAuth | Option B — Local + PAT |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| Setup | `set-api <url>` then `login` (browser) | `set-token figd_xxx` (or `FIGMA_PAT`) |
|
|
28
|
+
| Needs the backend? | Yes | No — fully local |
|
|
29
|
+
| Survives backend outage? | No | **Yes** |
|
|
30
|
+
| Token in your config? | No | Yes (PAT) |
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Option A — OAuth via the backend (no PAT in your config)
|
|
34
|
+
figma-coder-mcp set-api http://localhost:4403
|
|
35
|
+
figma-coder-mcp login # opens the browser; token is stored for you
|
|
36
|
+
|
|
37
|
+
# Option B — fully local with a PAT
|
|
38
|
+
figma-coder-mcp set-token figd_xxx
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`login` opens `…/auth/figma/login` in your browser, captures the token the backend
|
|
42
|
+
hands back over a one-shot **loopback** server, and stores it in
|
|
43
|
+
`~/.figma-mcp/credentials.json`. The access token is **refreshed silently** (using
|
|
44
|
+
the stored refresh token) when it expires — re-run `login` only if the refresh
|
|
45
|
+
token itself is revoked. Set `FIGMA_MCP_NO_BROWSER=1` on headless/SSH hosts to
|
|
46
|
+
print the URL instead of launching a browser.
|
|
47
|
+
|
|
48
|
+
## Tools
|
|
49
|
+
|
|
50
|
+
| Tool | What it does |
|
|
51
|
+
|------|--------------|
|
|
52
|
+
| `get_figma_data` | Returns the deterministic **Style IR** (exact CSS per node) so the agent can generate code itself. Large trees are written to a JSON file and summarised. |
|
|
53
|
+
| `convert_figma_to_html` | Produces finished, self-contained **HTML + Tailwind** (assets inlined, optional LLM restructure). The HTML is written to a file; a compact summary + path is returned to keep the agent's context small. |
|
|
54
|
+
|
|
55
|
+
## Setup
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Build the workspace from the repo root
|
|
59
|
+
npm install
|
|
60
|
+
npm run build
|
|
61
|
+
|
|
62
|
+
# Store a Figma personal access token (local mode, no backend)
|
|
63
|
+
node figma-mcp/dist/bin.js set-token figd_xxx
|
|
64
|
+
# …or set it per-process: FIGMA_PAT=figd_xxx
|
|
65
|
+
|
|
66
|
+
# …or use OAuth via a backend instead (no PAT in your config)
|
|
67
|
+
node figma-mcp/dist/bin.js set-api http://localhost:4403
|
|
68
|
+
node figma-mcp/dist/bin.js login
|
|
69
|
+
|
|
70
|
+
# Check how requests will be served (no secrets printed)
|
|
71
|
+
node figma-mcp/dist/bin.js status
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Register with an agent
|
|
75
|
+
|
|
76
|
+
`.mcp.json` (Claude Code) / Cursor MCP config — once installed (`npm i -g`) or via npx:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"mcpServers": {
|
|
81
|
+
"figma": {
|
|
82
|
+
"command": "npx",
|
|
83
|
+
"args": ["-y", "figma-coder-mcp"],
|
|
84
|
+
"env": { "FIGMA_PAT": "figd_xxx" }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
(From a local clone instead, point `command: "node"`, `args: [".../figma-mcp/dist/bin.js"]`.)
|
|
91
|
+
|
|
92
|
+
## One-off CLI (no agent)
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
figma-coder-mcp convert "https://www.figma.com/design/<key>/Title?node-id=1-23"
|
|
96
|
+
figma-coder-mcp convert <fileKey> --node 1:23 --no-assets --out page.html
|
|
97
|
+
figma-coder-mcp data <fileKey> --node 1:23
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Configuration
|
|
101
|
+
|
|
102
|
+
| Var | Meaning |
|
|
103
|
+
|-----|---------|
|
|
104
|
+
| `FIGMA_PAT` / `FIGMA_TOKEN` | Figma personal access token (local mode). |
|
|
105
|
+
| `FIGMA_MCP_API` | Backend converter base URL (remote mode). |
|
|
106
|
+
| `FIGMA_MCP_TOKEN` | OAuth/bearer token for the backend (remote mode). Usually set for you by `login`; an explicit value here is used verbatim and never auto-refreshed. |
|
|
107
|
+
| `FIGMA_MCP_MODE` | `auto` (default) · `local` · `remote`. Auto prefers local. |
|
|
108
|
+
| `FIGMA_MCP_OUT_DIR` | Where HTML/JSON outputs are written. Default `<cwd>/figma-output`. |
|
|
109
|
+
| `FIGMA_MCP_NO_BROWSER` | If set, `login` prints the URL instead of opening a browser (headless/SSH). |
|
|
110
|
+
| `OLLAMA_CLOUD_URL` / `OLLAMA_API_KEY` / `OLLAMA_CLOUD_MODELS` | Enable the `--llm` restructure pass. |
|
|
111
|
+
|
|
112
|
+
Priority for every setting: **flags > env > stored credentials** (`~/.figma-mcp/credentials.json`).
|
package/dist/bin.js
CHANGED
|
@@ -1836,13 +1836,40 @@ async function clearCredentials() {
|
|
|
1836
1836
|
}
|
|
1837
1837
|
}
|
|
1838
1838
|
|
|
1839
|
+
// src/refresh.ts
|
|
1840
|
+
var SKEW_MS = 6e4;
|
|
1841
|
+
async function ensureFreshApiToken(apiUrl, force = false) {
|
|
1842
|
+
const creds = await loadCredentials();
|
|
1843
|
+
if (!creds.apiToken || !creds.refreshToken) return;
|
|
1844
|
+
if (!force) {
|
|
1845
|
+
if (!creds.apiTokenExpiresAt) return;
|
|
1846
|
+
if (Date.now() < creds.apiTokenExpiresAt - SKEW_MS) return;
|
|
1847
|
+
}
|
|
1848
|
+
const base = apiUrl.replace(/\/+$/, "");
|
|
1849
|
+
try {
|
|
1850
|
+
const res = await fetch(`${base}/auth/figma/cli-refresh`, {
|
|
1851
|
+
method: "POST",
|
|
1852
|
+
headers: { "Content-Type": "application/json" },
|
|
1853
|
+
body: JSON.stringify({ refresh_token: creds.refreshToken })
|
|
1854
|
+
});
|
|
1855
|
+
if (!res.ok) return;
|
|
1856
|
+
const t = await res.json();
|
|
1857
|
+
await saveCredentials({
|
|
1858
|
+
apiToken: t.access_token,
|
|
1859
|
+
refreshToken: t.refresh_token ?? creds.refreshToken,
|
|
1860
|
+
apiTokenExpiresAt: t.expires_in ? Date.now() + t.expires_in * 1e3 : void 0
|
|
1861
|
+
});
|
|
1862
|
+
} catch {
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1839
1866
|
// src/config.ts
|
|
1840
1867
|
async function resolveConfig(overrides = {}) {
|
|
1841
|
-
|
|
1868
|
+
let creds = await loadCredentials();
|
|
1842
1869
|
const mode = overrides.mode ?? process.env.FIGMA_MCP_MODE ?? "auto";
|
|
1843
1870
|
const pat = overrides.pat ?? process.env.FIGMA_PAT ?? process.env.FIGMA_TOKEN ?? creds.pat;
|
|
1844
1871
|
const apiUrl = overrides.apiUrl ?? process.env.FIGMA_MCP_API ?? creds.apiUrl;
|
|
1845
|
-
const
|
|
1872
|
+
const apiTokenOverride = overrides.apiToken ?? process.env.FIGMA_MCP_TOKEN;
|
|
1846
1873
|
const outDir = overrides.outDir ?? process.env.FIGMA_MCP_OUT_DIR ?? `${process.cwd()}/figma-output`;
|
|
1847
1874
|
let effectiveMode;
|
|
1848
1875
|
if (mode === "local") {
|
|
@@ -1852,6 +1879,11 @@ async function resolveConfig(overrides = {}) {
|
|
|
1852
1879
|
} else {
|
|
1853
1880
|
effectiveMode = pat ? "local" : apiUrl ? "remote" : "local";
|
|
1854
1881
|
}
|
|
1882
|
+
if (effectiveMode === "remote" && apiUrl && !apiTokenOverride) {
|
|
1883
|
+
await ensureFreshApiToken(apiUrl);
|
|
1884
|
+
creds = await loadCredentials();
|
|
1885
|
+
}
|
|
1886
|
+
const apiToken = apiTokenOverride ?? creds.apiToken;
|
|
1855
1887
|
return { mode, effectiveMode, pat, apiUrl, apiToken, outDir };
|
|
1856
1888
|
}
|
|
1857
1889
|
function describeConfig(cfg) {
|
|
@@ -1862,16 +1894,24 @@ function describeConfig(cfg) {
|
|
|
1862
1894
|
}
|
|
1863
1895
|
|
|
1864
1896
|
// src/api-client.ts
|
|
1865
|
-
function authHeaders(cfg) {
|
|
1897
|
+
function authHeaders(cfg, apiToken = cfg.apiToken) {
|
|
1866
1898
|
const headers = { "Content-Type": "application/json" };
|
|
1867
1899
|
if (cfg.pat) headers["X-Figma-Token"] = cfg.pat;
|
|
1868
|
-
if (
|
|
1900
|
+
if (apiToken) headers["Authorization"] = `Bearer ${apiToken}`;
|
|
1869
1901
|
return headers;
|
|
1870
1902
|
}
|
|
1871
1903
|
async function post(cfg, route, body) {
|
|
1872
1904
|
if (!cfg.apiUrl) throw new Error("Remote mode requires a backend URL (FIGMA_MCP_API).");
|
|
1873
1905
|
const url = `${cfg.apiUrl.replace(/\/+$/, "")}${route}`;
|
|
1874
|
-
const
|
|
1906
|
+
const payload = JSON.stringify(body);
|
|
1907
|
+
let res = await fetch(url, { method: "POST", headers: authHeaders(cfg), body: payload });
|
|
1908
|
+
if (res.status === 401 && cfg.apiToken) {
|
|
1909
|
+
await ensureFreshApiToken(cfg.apiUrl, true);
|
|
1910
|
+
const refreshed = (await loadCredentials()).apiToken;
|
|
1911
|
+
if (refreshed && refreshed !== cfg.apiToken) {
|
|
1912
|
+
res = await fetch(url, { method: "POST", headers: authHeaders(cfg, refreshed), body: payload });
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1875
1915
|
const text = await res.text();
|
|
1876
1916
|
if (!res.ok) {
|
|
1877
1917
|
let msg = `${res.status} ${res.statusText}`;
|
|
@@ -2004,7 +2044,7 @@ function safe(handler) {
|
|
|
2004
2044
|
};
|
|
2005
2045
|
}
|
|
2006
2046
|
function buildServer() {
|
|
2007
|
-
const server = new McpServer({ name: "figma-coder-mcp", version: "0.
|
|
2047
|
+
const server = new McpServer({ name: "figma-coder-mcp", version: "0.2.0" });
|
|
2008
2048
|
server.registerTool(
|
|
2009
2049
|
"get_figma_data",
|
|
2010
2050
|
{
|
|
@@ -2040,6 +2080,91 @@ async function serveStdio() {
|
|
|
2040
2080
|
console.error("[figma-coder-mcp] stdio server ready");
|
|
2041
2081
|
}
|
|
2042
2082
|
|
|
2083
|
+
// src/login.ts
|
|
2084
|
+
import * as http from "http";
|
|
2085
|
+
import { spawn } from "child_process";
|
|
2086
|
+
function openBrowser(url) {
|
|
2087
|
+
if (process.env.FIGMA_MCP_NO_BROWSER) return;
|
|
2088
|
+
try {
|
|
2089
|
+
if (process.platform === "win32") {
|
|
2090
|
+
spawn("cmd", ["/c", "start", "", url], { stdio: "ignore", detached: true }).unref();
|
|
2091
|
+
} else if (process.platform === "darwin") {
|
|
2092
|
+
spawn("open", [url], { stdio: "ignore", detached: true }).unref();
|
|
2093
|
+
} else {
|
|
2094
|
+
spawn("xdg-open", [url], { stdio: "ignore", detached: true }).unref();
|
|
2095
|
+
}
|
|
2096
|
+
} catch {
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
var page = (title, body) => `<!doctype html><meta charset="utf-8"><title>figma-coder-mcp</title><body style="font-family:system-ui,sans-serif;text-align:center;padding-top:4rem;color:#0f172a"><h2>${title}</h2><p>${body}</p></body>`;
|
|
2100
|
+
async function login(opts = {}) {
|
|
2101
|
+
const cfg = await resolveConfig({ apiUrl: opts.apiUrl });
|
|
2102
|
+
if (!cfg.apiUrl) {
|
|
2103
|
+
throw new Error(
|
|
2104
|
+
"OAuth login needs the backend URL. Set it with `figma-coder-mcp set-api <URL>` or FIGMA_MCP_API."
|
|
2105
|
+
);
|
|
2106
|
+
}
|
|
2107
|
+
const base = cfg.apiUrl.replace(/\/+$/, "");
|
|
2108
|
+
const tokens = await new Promise((resolve2, reject) => {
|
|
2109
|
+
const server = http.createServer((req, res) => {
|
|
2110
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
2111
|
+
if (url.pathname !== "/callback") {
|
|
2112
|
+
res.writeHead(404).end();
|
|
2113
|
+
return;
|
|
2114
|
+
}
|
|
2115
|
+
const error = url.searchParams.get("error");
|
|
2116
|
+
const accessToken = url.searchParams.get("access_token");
|
|
2117
|
+
if (error || !accessToken) {
|
|
2118
|
+
const msg = error ?? "no token returned";
|
|
2119
|
+
res.writeHead(400, { "Content-Type": "text/html" }).end(page("Login failed", msg));
|
|
2120
|
+
cleanup();
|
|
2121
|
+
reject(new Error(`Figma login failed: ${msg}`));
|
|
2122
|
+
return;
|
|
2123
|
+
}
|
|
2124
|
+
res.writeHead(200, { "Content-Type": "text/html" }).end(page("\u2713 Logged in to Figma", "You can close this tab and return to your terminal."));
|
|
2125
|
+
cleanup();
|
|
2126
|
+
resolve2({
|
|
2127
|
+
access_token: accessToken,
|
|
2128
|
+
refresh_token: url.searchParams.get("refresh_token") ?? void 0,
|
|
2129
|
+
expires_in: Number(url.searchParams.get("expires_in")) || void 0
|
|
2130
|
+
});
|
|
2131
|
+
});
|
|
2132
|
+
const timeout = setTimeout(() => {
|
|
2133
|
+
cleanup();
|
|
2134
|
+
reject(new Error("Login timed out after 5 minutes."));
|
|
2135
|
+
}, opts.timeoutMs ?? 5 * 60 * 1e3);
|
|
2136
|
+
function cleanup() {
|
|
2137
|
+
clearTimeout(timeout);
|
|
2138
|
+
server.close();
|
|
2139
|
+
}
|
|
2140
|
+
server.on("error", (err) => {
|
|
2141
|
+
cleanup();
|
|
2142
|
+
reject(err);
|
|
2143
|
+
});
|
|
2144
|
+
server.listen(0, "127.0.0.1", () => {
|
|
2145
|
+
const addr = server.address();
|
|
2146
|
+
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
2147
|
+
const loopback = `http://127.0.0.1:${port}/callback`;
|
|
2148
|
+
const authorizeUrl = `${base}/auth/figma/login?cli=${encodeURIComponent(loopback)}`;
|
|
2149
|
+
console.error(
|
|
2150
|
+
`Opening your browser to log in with Figma\u2026
|
|
2151
|
+
If it doesn't open automatically, visit:
|
|
2152
|
+
${authorizeUrl}
|
|
2153
|
+
`
|
|
2154
|
+
);
|
|
2155
|
+
openBrowser(authorizeUrl);
|
|
2156
|
+
});
|
|
2157
|
+
});
|
|
2158
|
+
await saveCredentials({
|
|
2159
|
+
apiUrl: base,
|
|
2160
|
+
apiToken: tokens.access_token,
|
|
2161
|
+
refreshToken: tokens.refresh_token,
|
|
2162
|
+
apiTokenExpiresAt: tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : void 0
|
|
2163
|
+
});
|
|
2164
|
+
console.error(`\u2713 Saved Figma OAuth session to ${credentialsPath()}`);
|
|
2165
|
+
console.error("Run the MCP in remote mode (set FIGMA_MCP_MODE=remote) to use it.");
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2043
2168
|
// src/bin.ts
|
|
2044
2169
|
function parseArgs(argv) {
|
|
2045
2170
|
const positionals = [];
|
|
@@ -2075,6 +2200,7 @@ Usage:
|
|
|
2075
2200
|
figma-coder-mcp data <url|key> [..] One-off: print/save the Style IR
|
|
2076
2201
|
figma-coder-mcp set-token <PAT> Store a Figma personal access token (local mode)
|
|
2077
2202
|
figma-coder-mcp set-api <URL> Store the backend converter URL (remote mode)
|
|
2203
|
+
figma-coder-mcp login [--api <URL>] Log in with Figma via OAuth (remote mode)
|
|
2078
2204
|
figma-coder-mcp status Show how requests will be served (no secrets)
|
|
2079
2205
|
figma-coder-mcp logout Remove stored credentials
|
|
2080
2206
|
figma-coder-mcp help Show this help
|
|
@@ -2087,7 +2213,8 @@ Common flags (convert/data):
|
|
|
2087
2213
|
--out <file> Output file name (convert)
|
|
2088
2214
|
|
|
2089
2215
|
Config (env or stored): FIGMA_PAT, FIGMA_MCP_API, FIGMA_MCP_TOKEN, FIGMA_MCP_MODE,
|
|
2090
|
-
FIGMA_MCP_OUT_DIR
|
|
2216
|
+
FIGMA_MCP_OUT_DIR, FIGMA_MCP_NO_BROWSER (skip auto-opening the browser on login).
|
|
2217
|
+
Auto mode prefers local (PAT) so it works even if the backend is down.`;
|
|
2091
2218
|
async function main() {
|
|
2092
2219
|
const [positionals, flags] = parseArgs(process.argv.slice(2));
|
|
2093
2220
|
const cmd = positionals[0] ?? "serve";
|
|
@@ -2126,6 +2253,10 @@ async function main() {
|
|
|
2126
2253
|
console.log(`Stored backend URL (${url}) in ${credentialsPath()}`);
|
|
2127
2254
|
return;
|
|
2128
2255
|
}
|
|
2256
|
+
case "login": {
|
|
2257
|
+
await login({ apiUrl: typeof flags.api === "string" ? flags.api : void 0 });
|
|
2258
|
+
return;
|
|
2259
|
+
}
|
|
2129
2260
|
case "status": {
|
|
2130
2261
|
const cfg = await resolveConfig();
|
|
2131
2262
|
console.log(describeConfig(cfg));
|
package/package.json
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "figma-coder-mcp",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "MCP server + CLI that converts Figma designs into HTML + Tailwind for AI coding agents. Runs locally with a Figma PAT (no backend) or delegates to a figma-to-html-api backend.",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "GoldynLabs",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"bin": {
|
|
9
|
-
"figma-coder-mcp": "dist/bin.js"
|
|
10
|
-
},
|
|
11
|
-
"files": [
|
|
12
|
-
"dist",
|
|
13
|
-
"README.md"
|
|
14
|
-
],
|
|
15
|
-
"engines": {
|
|
16
|
-
"node": ">=18"
|
|
17
|
-
},
|
|
18
|
-
"publishConfig": {
|
|
19
|
-
"access": "public"
|
|
20
|
-
},
|
|
21
|
-
"keywords": [
|
|
22
|
-
"figma",
|
|
23
|
-
"mcp",
|
|
24
|
-
"model-context-protocol",
|
|
25
|
-
"tailwind",
|
|
26
|
-
"html",
|
|
27
|
-
"figma-to-code",
|
|
28
|
-
"ai",
|
|
29
|
-
"claude",
|
|
30
|
-
"cursor"
|
|
31
|
-
],
|
|
32
|
-
"repository": {
|
|
33
|
-
"type": "git",
|
|
34
|
-
"url": "https://github.com/goldynlabs/figma-coder-mcp.git"
|
|
35
|
-
},
|
|
36
|
-
"scripts": {
|
|
37
|
-
"build": "tsup",
|
|
38
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
39
|
-
"prepublishOnly": "npm run typecheck && npm run build",
|
|
40
|
-
"start": "node dist/bin.js"
|
|
41
|
-
},
|
|
42
|
-
"dependencies": {
|
|
43
|
-
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
44
|
-
"openai": "^4.77.0",
|
|
45
|
-
"zod": "^3.23.8"
|
|
46
|
-
},
|
|
47
|
-
"devDependencies": {
|
|
48
|
-
"@figma-to-code/core": "*",
|
|
49
|
-
"@types/node": "^22.0.0",
|
|
50
|
-
"tsup": "^8.0.0",
|
|
51
|
-
"typescript": "^5.1.3"
|
|
52
|
-
}
|
|
53
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "figma-coder-mcp",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "MCP server + CLI that converts Figma designs into HTML + Tailwind for AI coding agents. Runs locally with a Figma PAT (no backend) or delegates to a figma-to-html-api backend.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "GoldynLabs",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"figma-coder-mcp": "dist/bin.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"figma",
|
|
23
|
+
"mcp",
|
|
24
|
+
"model-context-protocol",
|
|
25
|
+
"tailwind",
|
|
26
|
+
"html",
|
|
27
|
+
"figma-to-code",
|
|
28
|
+
"ai",
|
|
29
|
+
"claude",
|
|
30
|
+
"cursor"
|
|
31
|
+
],
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/goldynlabs/figma-coder-mcp.git"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsup",
|
|
38
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
39
|
+
"prepublishOnly": "npm run typecheck && npm run build",
|
|
40
|
+
"start": "node dist/bin.js"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
44
|
+
"openai": "^4.77.0",
|
|
45
|
+
"zod": "^3.23.8"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@figma-to-code/core": "*",
|
|
49
|
+
"@types/node": "^22.0.0",
|
|
50
|
+
"tsup": "^8.0.0",
|
|
51
|
+
"typescript": "^5.1.3"
|
|
52
|
+
}
|
|
53
|
+
}
|