figmanage 0.1.0

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.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +338 -0
  3. package/dist/auth/client.d.ts +15 -0
  4. package/dist/auth/client.js +29 -0
  5. package/dist/auth/health.d.ts +5 -0
  6. package/dist/auth/health.js +93 -0
  7. package/dist/clients/internal-api.d.ts +4 -0
  8. package/dist/clients/internal-api.js +50 -0
  9. package/dist/clients/public-api.d.ts +4 -0
  10. package/dist/clients/public-api.js +47 -0
  11. package/dist/index.d.ts +19 -0
  12. package/dist/index.js +100 -0
  13. package/dist/setup.d.ts +3 -0
  14. package/dist/setup.js +565 -0
  15. package/dist/tools/analytics.d.ts +2 -0
  16. package/dist/tools/analytics.js +58 -0
  17. package/dist/tools/branching.d.ts +2 -0
  18. package/dist/tools/branching.js +103 -0
  19. package/dist/tools/comments.d.ts +2 -0
  20. package/dist/tools/comments.js +147 -0
  21. package/dist/tools/components.d.ts +2 -0
  22. package/dist/tools/components.js +104 -0
  23. package/dist/tools/compound.d.ts +2 -0
  24. package/dist/tools/compound.js +334 -0
  25. package/dist/tools/export.d.ts +2 -0
  26. package/dist/tools/export.js +70 -0
  27. package/dist/tools/files.d.ts +2 -0
  28. package/dist/tools/files.js +241 -0
  29. package/dist/tools/libraries.d.ts +2 -0
  30. package/dist/tools/libraries.js +31 -0
  31. package/dist/tools/navigate.d.ts +2 -0
  32. package/dist/tools/navigate.js +436 -0
  33. package/dist/tools/org.d.ts +2 -0
  34. package/dist/tools/org.js +311 -0
  35. package/dist/tools/permissions.d.ts +2 -0
  36. package/dist/tools/permissions.js +246 -0
  37. package/dist/tools/projects.d.ts +2 -0
  38. package/dist/tools/projects.js +160 -0
  39. package/dist/tools/reading.d.ts +2 -0
  40. package/dist/tools/reading.js +60 -0
  41. package/dist/tools/register.d.ts +32 -0
  42. package/dist/tools/register.js +76 -0
  43. package/dist/tools/teams.d.ts +2 -0
  44. package/dist/tools/teams.js +81 -0
  45. package/dist/tools/variables.d.ts +2 -0
  46. package/dist/tools/variables.js +102 -0
  47. package/dist/tools/versions.d.ts +2 -0
  48. package/dist/tools/versions.js +69 -0
  49. package/dist/tools/webhooks.d.ts +2 -0
  50. package/dist/tools/webhooks.js +126 -0
  51. package/dist/types/figma.d.ts +58 -0
  52. package/dist/types/figma.js +2 -0
  53. package/package.json +55 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Danny Keane
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 ADDED
@@ -0,0 +1,338 @@
1
+ # figmanage
2
+
3
+ MCP server for managing your Figma workspace.
4
+
5
+ Manages Figma workspaces through AI assistants. 76 tools covering files, projects, teams, permissions, comments, versions, components, webhooks, org admin, and more. Works with Claude Code, Claude Desktop, ChatGPT, and any MCP-compatible client.
6
+
7
+ ## quick start
8
+
9
+ ```bash
10
+ npx figmanage # starts MCP server (stdio)
11
+ npx figmanage --http 3333 # starts MCP server (HTTP, for ChatGPT etc.)
12
+ ```
13
+
14
+ ## setup
15
+
16
+ ```bash
17
+ git clone https://github.com/dannykeane/figmanage.git
18
+ cd figmanage
19
+ npm install
20
+ npm run build
21
+ npm run setup # extracts Chrome cookie, configures your MCP client
22
+ ```
23
+
24
+ The setup script:
25
+ - Reads your Figma session cookie from Chrome (macOS, Linux, Windows)
26
+ - Detects your org ID via Figma's redirect behavior
27
+ - Prompts for a Personal Access Token
28
+ - Stores all credentials in MCP server config (env vars)
29
+ - Auto-detects Claude Code, or prints config for other clients
30
+
31
+ ## configuration
32
+
33
+ | Env var | Description |
34
+ |---------|-------------|
35
+ | `FIGMA_PAT` | Personal access token |
36
+ | `FIGMA_AUTH_COOKIE` | Session cookie value |
37
+ | `FIGMA_USER_ID` | Your Figma user ID |
38
+ | `FIGMA_ORG_ID` | Your org ID |
39
+ | `FIGMA_TOOLSETS` | Toolset filter or preset name |
40
+ | `FIGMA_READ_ONLY` | Set to `1` to disable mutations |
41
+
42
+ The setup script stores credentials as env vars in the MCP server config. You can also set them manually.
43
+
44
+ ### toolset presets
45
+
46
+ Use `FIGMA_TOOLSETS` to expose only the tool groups you need. Presets bundle common combinations:
47
+
48
+ | Preset | Toolsets included |
49
+ |--------|-------------------|
50
+ | `starter` | navigate, reading, comments, export |
51
+ | `admin` | navigate, org, permissions, analytics, teams, libraries |
52
+ | `readonly` | navigate, reading, comments, export, components, versions |
53
+ | `full` | everything (default) |
54
+
55
+ Or pass individual toolsets:
56
+
57
+ ```bash
58
+ FIGMA_TOOLSETS=navigate,reading,comments
59
+ ```
60
+
61
+ Available toolsets: `navigate`, `files`, `projects`, `permissions`, `org`, `versions`, `branching`, `comments`, `export`, `analytics`, `reading`, `components`, `webhooks`, `variables`, `compound`, `teams`, `libraries`.
62
+
63
+ ### multi-org support
64
+
65
+ `list_orgs` discovers all available orgs. `switch_org` changes the active workspace for the session. 12 tools accept an `org_id` override parameter for one-off cross-org queries without switching.
66
+
67
+ ## auth
68
+
69
+ figmanage uses two API clients targeting different Figma endpoints:
70
+
71
+ | Client | Base URL | Auth | Capabilities |
72
+ |--------|----------|------|-------------|
73
+ | Internal API | `www.figma.com` | Session cookie | Workspace management, search, permissions, org admin, file lifecycle |
74
+ | Public API | `api.figma.com` | Personal Access Token | Comments, export, file reading, components, versions, webhooks, variables |
75
+
76
+ Each tool declares its auth requirement:
77
+
78
+ | Requirement | Meaning |
79
+ |-------------|---------|
80
+ | `cookie` | Internal API only. Needs `FIGMA_AUTH_COOKIE` + `FIGMA_USER_ID`. |
81
+ | `pat` | Public API only. Needs `FIGMA_PAT`. |
82
+ | `either` | Works with whichever auth is available. Prefers public API when PAT present. |
83
+
84
+ Tools are automatically filtered at startup based on available credentials. If you only have a PAT, cookie-only tools are not registered.
85
+
86
+ ## tools
87
+
88
+ ### navigate (10 tools)
89
+
90
+ | Tool | Auth | Description |
91
+ |------|------|-------------|
92
+ | `check_auth` | either | Validate PAT and cookie authentication |
93
+ | `list_orgs` | cookie | List available Figma workspaces (orgs) with names |
94
+ | `switch_org` | cookie | Switch active workspace for this session |
95
+ | `list_teams` | cookie | List teams in your org |
96
+ | `list_projects` | either | List projects in a team |
97
+ | `list_files` | either | List files in a project (paginated) |
98
+ | `list_recent_files` | cookie | Recently viewed/edited files across teams |
99
+ | `search` | cookie | Search files across the workspace |
100
+ | `get_file_info` | either | File metadata: name, project, team, link access |
101
+ | `list_favorites` | cookie | List favorited files (broken -- see known limitations) |
102
+
103
+ ### files (8 tools)
104
+
105
+ | Tool | Auth | Description |
106
+ |------|------|-------------|
107
+ | `create_file` | cookie | Create design, whiteboard, slides, or sites file |
108
+ | `rename_file` | cookie | Rename a file |
109
+ | `move_files` | cookie | Move files between projects (batch) |
110
+ | `duplicate_file` | cookie | Copy a file, optionally to another project |
111
+ | `trash_files` | cookie | Move files to trash (batch) |
112
+ | `restore_files` | cookie | Restore files from trash (batch) |
113
+ | `favorite_file` | cookie | Add/remove file from sidebar favorites |
114
+ | `set_link_access` | cookie | Set link sharing level (inherit/view/edit/org_view/org_edit) |
115
+
116
+ ### projects (6 tools)
117
+
118
+ | Tool | Auth | Description |
119
+ |------|------|-------------|
120
+ | `create_project` | cookie | Create a project in a team |
121
+ | `rename_project` | cookie | Rename a project |
122
+ | `move_project` | cookie | Move a project to another team |
123
+ | `trash_project` | cookie | Move a project to trash |
124
+ | `restore_project` | cookie | Restore a project from trash |
125
+ | `set_project_description` | cookie | Set or update project description |
126
+
127
+ ### permissions (7 tools)
128
+
129
+ | Tool | Auth | Description |
130
+ |------|------|-------------|
131
+ | `get_permissions` | cookie | List who has access to a file/project/team with roles |
132
+ | `set_permissions` | cookie | Change a user's access level |
133
+ | `share` | cookie | Invite someone by email (viewer or editor) |
134
+ | `revoke_access` | cookie | Remove someone's access |
135
+ | `list_role_requests` | cookie | List pending file access requests |
136
+ | `approve_role_request` | cookie | Accept an access request |
137
+ | `deny_role_request` | cookie | Decline an access request |
138
+
139
+ ### reading (2 tools)
140
+
141
+ | Tool | Auth | Description |
142
+ |------|------|-------------|
143
+ | `get_file` | pat | Read file contents as a node tree with depth control |
144
+ | `get_nodes` | pat | Read specific nodes by ID from a file |
145
+
146
+ ### components (4 tools)
147
+
148
+ | Tool | Auth | Description |
149
+ |------|------|-------------|
150
+ | `list_file_components` | pat | List components published from a file |
151
+ | `list_file_styles` | pat | List styles in a file |
152
+ | `list_team_components` | pat | List published components across a team (paginated) |
153
+ | `list_team_styles` | pat | List published styles across a team (paginated) |
154
+
155
+ ### versions (2 tools)
156
+
157
+ | Tool | Auth | Description |
158
+ |------|------|-------------|
159
+ | `list_versions` | pat | List version history (named checkpoints and auto-saves) |
160
+ | `create_version` | cookie | Create a named version checkpoint |
161
+
162
+ ### branching (3 tools)
163
+
164
+ | Tool | Auth | Description |
165
+ |------|------|-------------|
166
+ | `list_branches` | either | List branches of a file |
167
+ | `create_branch` | cookie | Create a branch from a file |
168
+ | `delete_branch` | cookie | Archive (delete) a branch |
169
+
170
+ ### comments (4 tools)
171
+
172
+ | Tool | Auth | Description |
173
+ |------|------|-------------|
174
+ | `list_comments` | pat | List comments with thread structure (optional markdown format) |
175
+ | `post_comment` | pat | Post a comment, optionally pinned to a node or as a reply |
176
+ | `delete_comment` | pat | Permanently delete a comment |
177
+ | `list_comment_reactions` | pat | List emoji reactions on a comment |
178
+
179
+ ### export (2 tools)
180
+
181
+ | Tool | Auth | Description |
182
+ |------|------|-------------|
183
+ | `export_nodes` | pat | Export nodes as PNG, SVG, PDF, or JPG (returns temporary URLs) |
184
+ | `get_image_fills` | pat | Get URLs for all images used as fills in a file |
185
+
186
+ ### webhooks (4 tools)
187
+
188
+ | Tool | Auth | Description |
189
+ |------|------|-------------|
190
+ | `list_webhooks` | pat | List webhooks for a team |
191
+ | `create_webhook` | pat | Create a webhook subscription for a team |
192
+ | `update_webhook` | pat | Update a webhook (endpoint, event type, status) |
193
+ | `delete_webhook` | pat | Delete a webhook |
194
+
195
+ ### variables (3 tools, Enterprise only)
196
+
197
+ | Tool | Auth | Description |
198
+ |------|------|-------------|
199
+ | `list_local_variables` | pat | List local variables and collections in a file |
200
+ | `list_published_variables` | pat | List published variables from a library file |
201
+ | `update_variables` | pat | Bulk create, update, or delete variables, collections, modes, and values |
202
+
203
+ ### analytics (2 tools)
204
+
205
+ | Tool | Auth | Description |
206
+ |------|------|-------------|
207
+ | `library_usage` | cookie | Team-level library adoption metrics over a lookback period |
208
+ | `component_usage` | cookie | Per-file component usage analytics |
209
+
210
+ ### org (9 tools)
211
+
212
+ | Tool | Auth | Description |
213
+ |------|------|-------------|
214
+ | `list_admins` | cookie | Org admins with permission levels and seat status |
215
+ | `list_org_teams` | cookie | All teams with member counts, project counts, access levels |
216
+ | `seat_usage` | cookie | Seat breakdown: permissions, seat types, activity, account types |
217
+ | `list_team_members` | cookie | Team members with name, email, role, last active date |
218
+ | `billing_overview` | cookie | Invoice history, billing status, amounts, billing periods |
219
+ | `list_invoices` | cookie | Open and upcoming invoices |
220
+ | `org_domains` | cookie | Domain configuration and SSO/SAML settings |
221
+ | `ai_credit_usage` | cookie | AI credit usage summary for a billing plan |
222
+ | `export_members` | cookie | Trigger async CSV export of all org members (emailed to admin) |
223
+
224
+ ### teams (3 tools)
225
+
226
+ | Tool | Auth | Description |
227
+ |------|------|-------------|
228
+ | `create_team` | cookie | Create a new team in the org |
229
+ | `rename_team` | cookie | Rename an existing team |
230
+ | `delete_team` | cookie | Delete a team (destructive, cannot be undone) |
231
+
232
+ ### libraries (1 tool)
233
+
234
+ | Tool | Auth | Description |
235
+ |------|------|-------------|
236
+ | `list_org_libraries` | cookie | All design system libraries in the org with sharing group info |
237
+
238
+ ### compound (6 tools)
239
+
240
+ Multi-step operations that aggregate data from several API calls.
241
+
242
+ | Tool | Auth | Description |
243
+ |------|------|-------------|
244
+ | `file_summary` | pat | Quick overview: pages, components, styles, comment counts |
245
+ | `workspace_overview` | cookie | Full org snapshot: teams, seats, billing in one call |
246
+ | `open_comments` | pat | Aggregated unresolved comments across all files in a project |
247
+ | `cleanup_stale_files` | either | Find files not modified in N days, optionally trash them (dry run by default) |
248
+ | `organize_project` | cookie | Batch-move files into a target project |
249
+ | `setup_project_structure` | cookie | Create multiple projects in a team from a plan |
250
+
251
+ ## transport modes
252
+
253
+ | Mode | Flag | Use case |
254
+ |------|------|----------|
255
+ | stdio | (default) | Claude Code, Claude Desktop, most MCP clients |
256
+ | HTTP | `--http <port>` | ChatGPT, web-based clients |
257
+
258
+ ```bash
259
+ npx figmanage # stdio
260
+ npx figmanage --http 3333 # HTTP on port 3333
261
+ ```
262
+
263
+ ## security
264
+
265
+ ### ID validation
266
+
267
+ All tool parameters that accept Figma IDs (file keys, team IDs, project IDs, node IDs) are validated against a strict regex (`/^[\w.:-]+$/`) before use. This blocks path traversal in URL templates.
268
+
269
+ ### safe retry policy
270
+
271
+ Both API clients retry on transient errors with exponential backoff. Rate limit responses (429) are only retried for safe methods (`GET`, `HEAD`, `OPTIONS`). Mutations are never retried on 429 to prevent duplicate writes. Auth failures (401, 403) are never retried.
272
+
273
+ ### response shaping
274
+
275
+ Org admin tools extract documented fields and strip PII (e.g., `shipping_address` is removed from billing responses).
276
+
277
+ ## architecture
278
+
279
+ ```
280
+ src/
281
+ index.ts MCP server entry point (stdio transport, toolset parsing)
282
+ setup.ts Chrome cookie extraction + Claude MCP config
283
+ auth/
284
+ client.ts AuthConfig, env var loading
285
+ health.ts Auth validation
286
+ clients/
287
+ internal-api.ts Axios client for www.figma.com (cookie auth, safe retry)
288
+ public-api.ts Axios client for api.figma.com (PAT auth, safe retry)
289
+ tools/
290
+ register.ts defineTool() pattern, toolset filtering, read-only mode, figmaId validation
291
+ navigate.ts Browse and search (10 tools)
292
+ files.ts File lifecycle (8 tools)
293
+ projects.ts Project management (6 tools)
294
+ permissions.ts Sharing, access control, role requests (7 tools)
295
+ reading.ts File and node reading via public API (2 tools)
296
+ components.ts Components and styles via public API (4 tools)
297
+ versions.ts Version history (2 tools)
298
+ branching.ts Branch CRUD (3 tools)
299
+ comments.ts Comments and reactions via public API (4 tools)
300
+ export.ts Image export and fills via public API (2 tools)
301
+ webhooks.ts Webhook CRUD via V2 public API (4 tools)
302
+ variables.ts Variables via public API, Enterprise only (3 tools)
303
+ analytics.ts Library and component analytics via internal API (2 tools)
304
+ org.ts Org admin, billing, seats, domains via internal API (9 tools)
305
+ teams.ts Team CRUD (3 tools)
306
+ libraries.ts Design system libraries via internal API (1 tool)
307
+ compound.ts Multi-step aggregation tools (6 tools)
308
+ types/
309
+ figma.ts Shared types (Toolset, AuthConfig, etc.)
310
+ ```
311
+
312
+ Tools self-register via `defineTool()` side-effect imports. Each tool declares its toolset, auth requirement, and whether it mutates. `registerTools()` filters at startup based on available credentials, enabled toolsets, and read-only mode.
313
+
314
+ ## known limitations
315
+
316
+ - **list_favorites**: Figma's server has a BigInt overflow bug on large user IDs. The `favorite_file` tool for adding/removing favorites works fine.
317
+ - **Branch merging**: Figma merges branches through its real-time multiplayer protocol, not a REST endpoint. Must be done in the Figma UI.
318
+ - **Cookie expiry**: Session cookies expire roughly every 30 days. Re-run `npm run setup` to refresh.
319
+ - **Windows cookie extraction**: Best-effort via DPAPI/PowerShell. Falls back to PAT-only setup if extraction fails.
320
+ - **Variables require Enterprise**: The `file_variables:read` and `file_variables:write` scopes are not available in the standard PAT UI. Variables tools return a clear error on non-Enterprise plans.
321
+ - **No org-level member list**: There is no single REST endpoint to list all org members. Use `list_admins` + `list_team_members` per team, or `export_members` for a full CSV.
322
+ - **No activity log API**: Figma's activity log is SSR only with no REST endpoint.
323
+ - **get_file returns full tree**: Without a `depth` parameter, `get_file` returns the entire document. Use `depth` or `get_nodes` for large files.
324
+
325
+ ## development
326
+
327
+ ```bash
328
+ npm install
329
+ npm run build # compile
330
+ npm run setup # configure auth + MCP client
331
+ npm run dev # watch mode
332
+ npm run lint # type-check
333
+ npm test # run tests
334
+ ```
335
+
336
+ ## license
337
+
338
+ MIT
@@ -0,0 +1,15 @@
1
+ export interface OrgEntry {
2
+ id: string;
3
+ name: string;
4
+ }
5
+ export interface AuthConfig {
6
+ pat?: string;
7
+ cookie?: string;
8
+ userId?: string;
9
+ orgId?: string;
10
+ orgs?: OrgEntry[];
11
+ }
12
+ export declare function loadAuthConfig(): AuthConfig;
13
+ export declare function hasPat(config: AuthConfig): boolean;
14
+ export declare function hasCookie(config: AuthConfig): boolean;
15
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1,29 @@
1
+ function parseOrgs(raw) {
2
+ if (!raw)
3
+ return undefined;
4
+ try {
5
+ const parsed = JSON.parse(raw);
6
+ if (!Array.isArray(parsed))
7
+ return undefined;
8
+ return parsed.filter((o) => o.id && o.name);
9
+ }
10
+ catch {
11
+ return undefined;
12
+ }
13
+ }
14
+ export function loadAuthConfig() {
15
+ return {
16
+ pat: process.env.FIGMA_PAT,
17
+ cookie: process.env.FIGMA_AUTH_COOKIE,
18
+ userId: process.env.FIGMA_USER_ID,
19
+ orgId: process.env.FIGMA_ORG_ID,
20
+ orgs: parseOrgs(process.env.FIGMA_ORGS),
21
+ };
22
+ }
23
+ export function hasPat(config) {
24
+ return !!config.pat;
25
+ }
26
+ export function hasCookie(config) {
27
+ return !!config.cookie && !!config.userId;
28
+ }
29
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1,5 @@
1
+ import type { AuthConfig } from './client.js';
2
+ import type { AuthStatus } from '../types/figma.js';
3
+ export declare function checkAuth(config: AuthConfig): Promise<AuthStatus>;
4
+ export declare function formatAuthStatus(status: AuthStatus, config?: AuthConfig): string;
5
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1,93 @@
1
+ import { publicClient } from '../clients/public-api.js';
2
+ import { internalClient } from '../clients/internal-api.js';
3
+ export async function checkAuth(config) {
4
+ const status = {
5
+ pat: { valid: false },
6
+ cookie: { valid: false },
7
+ };
8
+ if (config.pat) {
9
+ try {
10
+ const res = await publicClient(config).get('/v1/me');
11
+ status.pat = { valid: true, user: res.data.handle || res.data.email };
12
+ }
13
+ catch (e) {
14
+ status.pat = {
15
+ valid: false,
16
+ error: e.response?.status === 403
17
+ ? 'PAT invalid or expired. Generate a new one at figma.com/developers'
18
+ : `PAT check failed: ${e.message}`,
19
+ };
20
+ }
21
+ }
22
+ else {
23
+ status.pat = { valid: false, error: 'No PAT configured (env or Keychain)' };
24
+ }
25
+ if (config.cookie && config.userId) {
26
+ try {
27
+ const res = await internalClient(config).get('/api/user/state');
28
+ const user = res.data?.user;
29
+ status.cookie = { valid: true, user: user?.handle || user?.email || 'authenticated' };
30
+ // Populate org registry from user/state response
31
+ const orgs = (res.data?.meta?.orgs || []).map((o) => ({
32
+ id: String(o.id),
33
+ name: o.name,
34
+ }));
35
+ if (orgs.length > 0)
36
+ config.orgs = orgs;
37
+ }
38
+ catch (e) {
39
+ const code = e.response?.status;
40
+ if (code === 401 || code === 403) {
41
+ status.cookie = {
42
+ valid: false,
43
+ error: 'Session cookie expired. Extract a new one from browser DevTools: Application > Cookies > __Host-figma.authn',
44
+ };
45
+ }
46
+ else {
47
+ status.cookie = { valid: false, error: `Cookie check failed: ${e.message}` };
48
+ }
49
+ }
50
+ }
51
+ else {
52
+ const missing = [];
53
+ if (!config.cookie)
54
+ missing.push('FIGMA_AUTH_COOKIE');
55
+ if (!config.userId)
56
+ missing.push('FIGMA_USER_ID');
57
+ status.cookie = { valid: false, error: `${missing.join(', ')} not set` };
58
+ }
59
+ return status;
60
+ }
61
+ export function formatAuthStatus(status, config) {
62
+ const lines = [];
63
+ lines.push(`PAT: ${status.pat.valid ? `valid (${status.pat.user})` : status.pat.error}`);
64
+ lines.push(`Session: ${status.cookie.valid ? `valid (${status.cookie.user})` : status.cookie.error}`);
65
+ if (!status.pat.valid && !status.cookie.valid) {
66
+ lines.push('\nNo valid auth. Set FIGMA_PAT for public API access or FIGMA_AUTH_COOKIE + FIGMA_USER_ID for full access.');
67
+ }
68
+ else if (!status.cookie.valid) {
69
+ lines.push('\nPublic API only. Internal API tools (file CRUD, team management) unavailable.');
70
+ }
71
+ // Org context
72
+ if (config) {
73
+ if (config.orgId) {
74
+ const currentOrg = config.orgs?.find(o => o.id === config.orgId);
75
+ const orgLabel = currentOrg ? `${currentOrg.name} (${config.orgId})` : config.orgId;
76
+ lines.push(`\nWorkspace: ${orgLabel}`);
77
+ const others = (config.orgs || []).filter(o => o.id !== config.orgId);
78
+ if (others.length > 0) {
79
+ lines.push(`Other workspaces: ${others.map(o => `${o.name} (${o.id})`).join(', ')}`);
80
+ lines.push('Use switch_org to change workspace.');
81
+ }
82
+ }
83
+ else if (config.orgs && config.orgs.length > 0) {
84
+ lines.push(`\nWorkspaces available: ${config.orgs.map(o => `${o.name} (${o.id})`).join(', ')}`);
85
+ lines.push('Use switch_org to select a workspace.');
86
+ }
87
+ else {
88
+ lines.push('\nWorkspace: not configured');
89
+ }
90
+ }
91
+ return lines.join('\n');
92
+ }
93
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1,4 @@
1
+ import { type AxiosInstance } from 'axios';
2
+ import type { AuthConfig } from '../auth/client.js';
3
+ export declare function internalClient(config: AuthConfig): AxiosInstance;
4
+ //# sourceMappingURL=internal-api.d.ts.map
@@ -0,0 +1,50 @@
1
+ import axios from 'axios';
2
+ import axiosRetry from 'axios-retry';
3
+ import { Agent as HttpsAgent } from 'https';
4
+ const httpsAgent = new HttpsAgent({ keepAlive: true });
5
+ const instances = new WeakMap();
6
+ export function internalClient(config) {
7
+ const existing = instances.get(config);
8
+ if (existing)
9
+ return existing;
10
+ const client = axios.create({
11
+ baseURL: 'https://www.figma.com',
12
+ httpsAgent,
13
+ headers: {
14
+ 'Cookie': `__Host-figma.authn=${config.cookie || ''}`,
15
+ 'X-CSRF-Bypass': 'yes',
16
+ 'X-Figma-User-Id': config.userId || '',
17
+ 'Content-Type': 'application/json',
18
+ 'Accept': 'application/json',
19
+ },
20
+ timeout: 30000,
21
+ });
22
+ axiosRetry(client, {
23
+ retries: 3,
24
+ retryDelay: (retryCount, error) => {
25
+ const retryAfter = error.response?.headers?.['retry-after'];
26
+ if (retryAfter) {
27
+ const delay = parseInt(retryAfter, 10) * 1000;
28
+ if (delay > 0 && delay < 300000)
29
+ return delay;
30
+ }
31
+ // Exponential backoff with jitter: 1s, 2s, 4s + random jitter
32
+ const base = Math.pow(2, retryCount - 1) * 1000;
33
+ const jitter = base * 0.5 * Math.random();
34
+ return base + jitter;
35
+ },
36
+ retryCondition: (error) => {
37
+ const status = error.response?.status;
38
+ if (status === 401 || status === 403)
39
+ return false;
40
+ if (status === 429) {
41
+ const method = error.config?.method?.toUpperCase();
42
+ return ['GET', 'HEAD', 'OPTIONS'].includes(method || '');
43
+ }
44
+ return axiosRetry.isNetworkOrIdempotentRequestError(error);
45
+ },
46
+ });
47
+ instances.set(config, client);
48
+ return client;
49
+ }
50
+ //# sourceMappingURL=internal-api.js.map
@@ -0,0 +1,4 @@
1
+ import { type AxiosInstance } from 'axios';
2
+ import type { AuthConfig } from '../auth/client.js';
3
+ export declare function publicClient(config: AuthConfig): AxiosInstance;
4
+ //# sourceMappingURL=public-api.d.ts.map
@@ -0,0 +1,47 @@
1
+ import axios from 'axios';
2
+ import axiosRetry from 'axios-retry';
3
+ import { Agent as HttpsAgent } from 'https';
4
+ const httpsAgent = new HttpsAgent({ keepAlive: true });
5
+ const instances = new WeakMap();
6
+ export function publicClient(config) {
7
+ const existing = instances.get(config);
8
+ if (existing)
9
+ return existing;
10
+ const client = axios.create({
11
+ baseURL: 'https://api.figma.com',
12
+ httpsAgent,
13
+ headers: {
14
+ 'X-Figma-Token': config.pat || '',
15
+ 'Accept': 'application/json',
16
+ },
17
+ timeout: 30000,
18
+ });
19
+ axiosRetry(client, {
20
+ retries: 3,
21
+ retryDelay: (retryCount, error) => {
22
+ const retryAfter = error.response?.headers?.['retry-after'];
23
+ if (retryAfter) {
24
+ const delay = parseInt(retryAfter, 10) * 1000;
25
+ if (delay > 0 && delay < 300000)
26
+ return delay;
27
+ }
28
+ // Exponential backoff with jitter: 1s, 2s, 4s + random jitter
29
+ const base = Math.pow(2, retryCount - 1) * 1000;
30
+ const jitter = base * 0.5 * Math.random();
31
+ return base + jitter;
32
+ },
33
+ retryCondition: (error) => {
34
+ const status = error.response?.status;
35
+ if (status === 401 || status === 403)
36
+ return false;
37
+ if (status === 429) {
38
+ const method = error.config?.method?.toUpperCase();
39
+ return ['GET', 'HEAD', 'OPTIONS'].includes(method || '');
40
+ }
41
+ return axiosRetry.isNetworkOrIdempotentRequestError(error);
42
+ },
43
+ });
44
+ instances.set(config, client);
45
+ return client;
46
+ }
47
+ //# sourceMappingURL=public-api.js.map
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import './tools/navigate.js';
3
+ import './tools/files.js';
4
+ import './tools/projects.js';
5
+ import './tools/permissions.js';
6
+ import './tools/comments.js';
7
+ import './tools/export.js';
8
+ import './tools/versions.js';
9
+ import './tools/branching.js';
10
+ import './tools/components.js';
11
+ import './tools/webhooks.js';
12
+ import './tools/reading.js';
13
+ import './tools/analytics.js';
14
+ import './tools/variables.js';
15
+ import './tools/org.js';
16
+ import './tools/libraries.js';
17
+ import './tools/teams.js';
18
+ import './tools/compound.js';
19
+ //# sourceMappingURL=index.d.ts.map