figmanage 0.1.2 → 0.2.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.
- package/README.md +7 -4
- package/dist/tools/org.js +171 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
MCP server for managing your Figma workspace.
|
|
4
4
|
|
|
5
|
-
Manages Figma workspaces through AI assistants.
|
|
5
|
+
Manages Figma workspaces through AI assistants. 79 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
6
|
|
|
7
7
|
## quick start
|
|
8
8
|
|
|
@@ -16,13 +16,13 @@ claude mcp add figmanage -s user -e FIGMA_PAT=figd_xxx -- npx -y figmanage
|
|
|
16
16
|
|
|
17
17
|
Restart Claude Code. Gives you 30+ tools (comments, reading, export, components, versions, webhooks).
|
|
18
18
|
|
|
19
|
-
### full setup (2 minutes, all
|
|
19
|
+
### full setup (2 minutes, all 79 tools)
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
npx -y figmanage --setup
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Extracts your Chrome cookie, prompts for a PAT, registers with Claude Code automatically. Unlocks all
|
|
25
|
+
Extracts your Chrome cookie, prompts for a PAT, registers with Claude Code automatically. Unlocks all 79 tools including workspace management, permissions, and org admin. Restart Claude Code.
|
|
26
26
|
|
|
27
27
|
Use `--no-prompt --pat figd_xxx` for non-interactive setup.
|
|
28
28
|
|
|
@@ -223,7 +223,7 @@ Tools are automatically filtered at startup based on available credentials. If y
|
|
|
223
223
|
| `library_usage` | cookie | Team-level library adoption metrics over a lookback period |
|
|
224
224
|
| `component_usage` | cookie | Per-file component usage analytics |
|
|
225
225
|
|
|
226
|
-
### org (
|
|
226
|
+
### org (12 tools)
|
|
227
227
|
|
|
228
228
|
| Tool | Auth | Description |
|
|
229
229
|
|------|------|-------------|
|
|
@@ -231,6 +231,9 @@ Tools are automatically filtered at startup based on available credentials. If y
|
|
|
231
231
|
| `list_org_teams` | cookie | All teams with member counts, project counts, access levels |
|
|
232
232
|
| `seat_usage` | cookie | Seat breakdown: permissions, seat types, activity, account types |
|
|
233
233
|
| `list_team_members` | cookie | Team members with name, email, role, last active date |
|
|
234
|
+
| `list_org_members` | cookie | List org members with seat type, permission, email, last active |
|
|
235
|
+
| `contract_rates` | cookie | Seat pricing per product (expert, developer, collaborator) |
|
|
236
|
+
| `change_seat` | cookie | Change a user's seat type (confirm flag required for upgrades) |
|
|
234
237
|
| `billing_overview` | cookie | Invoice history, billing status, amounts, billing periods |
|
|
235
238
|
| `list_invoices` | cookie | Open and upcoming invoices |
|
|
236
239
|
| `org_domains` | cookie | Domain configuration and SSO/SAML settings |
|
package/dist/tools/org.js
CHANGED
|
@@ -308,4 +308,175 @@ defineTool({
|
|
|
308
308
|
});
|
|
309
309
|
},
|
|
310
310
|
});
|
|
311
|
+
// -- list_org_members --
|
|
312
|
+
defineTool({
|
|
313
|
+
toolset: 'org',
|
|
314
|
+
auth: 'cookie',
|
|
315
|
+
register(server, config) {
|
|
316
|
+
server.registerTool('list_org_members', {
|
|
317
|
+
description: 'List org members with seat type, permission, email, and last active date. Use to resolve org_user_ids for change_seat.',
|
|
318
|
+
inputSchema: {
|
|
319
|
+
org_id: figmaId.optional().describe('Org ID override (defaults to current workspace)'),
|
|
320
|
+
search_query: z.string().optional().describe('Filter members by name or email'),
|
|
321
|
+
},
|
|
322
|
+
}, async ({ org_id, search_query }) => {
|
|
323
|
+
try {
|
|
324
|
+
let orgId;
|
|
325
|
+
try {
|
|
326
|
+
orgId = requireOrgId(config, org_id);
|
|
327
|
+
}
|
|
328
|
+
catch (e) {
|
|
329
|
+
return toolError(e.message);
|
|
330
|
+
}
|
|
331
|
+
const params = {};
|
|
332
|
+
if (search_query)
|
|
333
|
+
params.search_query = search_query;
|
|
334
|
+
const res = await internalClient(config).get(`/api/v2/orgs/${orgId}/org_users`, { params });
|
|
335
|
+
const members = (res.data?.meta || res.data || []).map((m) => ({
|
|
336
|
+
org_user_id: String(m.id),
|
|
337
|
+
user_id: m.user_id,
|
|
338
|
+
email: m.user?.email,
|
|
339
|
+
name: m.user?.handle,
|
|
340
|
+
permission: m.permission,
|
|
341
|
+
seat_type: m.active_seat_type?.key || null,
|
|
342
|
+
last_active: m.last_active,
|
|
343
|
+
}));
|
|
344
|
+
return toolResult(JSON.stringify(members, null, 2));
|
|
345
|
+
}
|
|
346
|
+
catch (e) {
|
|
347
|
+
return toolError(`Failed to list org members: ${e.response?.status || e.message}`);
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
},
|
|
351
|
+
});
|
|
352
|
+
// -- contract_rates --
|
|
353
|
+
defineTool({
|
|
354
|
+
toolset: 'org',
|
|
355
|
+
auth: 'cookie',
|
|
356
|
+
register(server, config) {
|
|
357
|
+
server.registerTool('contract_rates', {
|
|
358
|
+
description: 'Seat pricing for the org. Returns monthly cost per seat type (expert, developer, collaborator).',
|
|
359
|
+
inputSchema: {
|
|
360
|
+
org_id: figmaId.optional().describe('Org ID override (defaults to current workspace)'),
|
|
361
|
+
},
|
|
362
|
+
}, async ({ org_id }) => {
|
|
363
|
+
try {
|
|
364
|
+
let orgId;
|
|
365
|
+
try {
|
|
366
|
+
orgId = requireOrgId(config, org_id);
|
|
367
|
+
}
|
|
368
|
+
catch (e) {
|
|
369
|
+
return toolError(e.message);
|
|
370
|
+
}
|
|
371
|
+
const res = await internalClient(config).get(`/api/pricing/contract_rates`, { params: { plan_parent_id: orgId, plan_type: 'organization' } });
|
|
372
|
+
const seatProducts = new Set(['expert', 'developer', 'collaborator']);
|
|
373
|
+
const prices = res.data?.meta?.product_prices || [];
|
|
374
|
+
const rates = prices
|
|
375
|
+
.filter((r) => seatProducts.has(r.billable_product_key))
|
|
376
|
+
.map((r) => ({
|
|
377
|
+
product: r.billable_product_key,
|
|
378
|
+
monthly_cents: r.amount,
|
|
379
|
+
monthly_dollars: (r.amount / 100).toFixed(2),
|
|
380
|
+
}));
|
|
381
|
+
return toolResult(JSON.stringify(rates, null, 2));
|
|
382
|
+
}
|
|
383
|
+
catch (e) {
|
|
384
|
+
return toolError(`Failed to fetch contract rates: ${e.response?.status || e.message}`);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
},
|
|
388
|
+
});
|
|
389
|
+
// -- change_seat --
|
|
390
|
+
const SEAT_HIERARCHY = {
|
|
391
|
+
view: 0,
|
|
392
|
+
collab: 1,
|
|
393
|
+
dev: 2,
|
|
394
|
+
full: 3,
|
|
395
|
+
};
|
|
396
|
+
const SEAT_LABELS = {
|
|
397
|
+
view: 'Viewer (free)',
|
|
398
|
+
collab: 'Collaborator ($5/mo)',
|
|
399
|
+
dev: 'Developer ($25/mo)',
|
|
400
|
+
full: 'Full ($55/mo)',
|
|
401
|
+
};
|
|
402
|
+
const PAID_STATUSES = {
|
|
403
|
+
full: { expert: 'full' },
|
|
404
|
+
dev: { developer: 'full' },
|
|
405
|
+
collab: { collaborator: 'full' },
|
|
406
|
+
view: { collaborator: 'starter', developer: 'starter', expert: 'starter' },
|
|
407
|
+
};
|
|
408
|
+
const SEAT_KEY_TO_TYPE = {
|
|
409
|
+
expert: 'full',
|
|
410
|
+
developer: 'dev',
|
|
411
|
+
collaborator: 'collab',
|
|
412
|
+
};
|
|
413
|
+
defineTool({
|
|
414
|
+
toolset: 'org',
|
|
415
|
+
auth: 'cookie',
|
|
416
|
+
mutates: true,
|
|
417
|
+
destructive: true,
|
|
418
|
+
register(server, config) {
|
|
419
|
+
server.registerTool('change_seat', {
|
|
420
|
+
description: 'Change a user\'s seat type. Accepts user_id or email to identify the user. Upgrades affect billing.',
|
|
421
|
+
inputSchema: {
|
|
422
|
+
user_id: z.string().describe('User ID or email address of the target user'),
|
|
423
|
+
seat_type: z.enum(['full', 'dev', 'collab', 'view']).describe('Target seat type'),
|
|
424
|
+
org_id: figmaId.optional().describe('Org ID override (defaults to current workspace)'),
|
|
425
|
+
confirm: z.boolean().optional().describe('Required when upgrading to a higher/paid seat. Set to true to authorize the billing change.'),
|
|
426
|
+
},
|
|
427
|
+
}, async ({ user_id, seat_type, org_id, confirm }) => {
|
|
428
|
+
try {
|
|
429
|
+
let orgId;
|
|
430
|
+
try {
|
|
431
|
+
orgId = requireOrgId(config, org_id);
|
|
432
|
+
}
|
|
433
|
+
catch (e) {
|
|
434
|
+
return toolError(e.message);
|
|
435
|
+
}
|
|
436
|
+
const res = await internalClient(config).get(`/api/v2/orgs/${orgId}/org_users`, {
|
|
437
|
+
params: user_id.includes('@') ? { search_query: user_id } : {},
|
|
438
|
+
});
|
|
439
|
+
const members = res.data?.meta || res.data || [];
|
|
440
|
+
const member = members.find((m) => user_id.includes('@')
|
|
441
|
+
? m.user?.email === user_id
|
|
442
|
+
: String(m.user_id) === String(user_id));
|
|
443
|
+
if (!member)
|
|
444
|
+
return toolError(`User not found: ${user_id}`);
|
|
445
|
+
if (String(member.user_id) === String(config.userId)) {
|
|
446
|
+
return toolError('Cannot change your own seat type. Use the Figma admin panel.');
|
|
447
|
+
}
|
|
448
|
+
const currentKey = member.active_seat_type?.key || null;
|
|
449
|
+
const currentType = currentKey ? (SEAT_KEY_TO_TYPE[currentKey] || 'view') : 'view';
|
|
450
|
+
if (currentType === seat_type) {
|
|
451
|
+
return toolResult(`Already on ${SEAT_LABELS[seat_type]} seat. No change needed.`);
|
|
452
|
+
}
|
|
453
|
+
const isUpgrade = (SEAT_HIERARCHY[seat_type] ?? 0) > (SEAT_HIERARCHY[currentType] ?? 0);
|
|
454
|
+
if (isUpgrade && !confirm) {
|
|
455
|
+
return toolError(`Upgrading from ${SEAT_LABELS[currentType]} to ${SEAT_LABELS[seat_type]} will increase billing. ` +
|
|
456
|
+
`Set confirm: true to authorize.`);
|
|
457
|
+
}
|
|
458
|
+
await internalClient(config).put(`/api/orgs/${orgId}/org_users`, {
|
|
459
|
+
org_user_ids: [String(member.id)],
|
|
460
|
+
paid_statuses: PAID_STATUSES[seat_type],
|
|
461
|
+
entry_point: 'members_tab',
|
|
462
|
+
seat_increase_authorized: 'true',
|
|
463
|
+
seat_swap_intended: 'false',
|
|
464
|
+
latest_ou_update: member.updated_at,
|
|
465
|
+
showing_billing_groups: 'true',
|
|
466
|
+
}, {
|
|
467
|
+
'axios-retry': { retries: 0 },
|
|
468
|
+
});
|
|
469
|
+
return toolResult(JSON.stringify({
|
|
470
|
+
user: member.user?.handle || member.user?.email,
|
|
471
|
+
email: member.user?.email,
|
|
472
|
+
old_seat: SEAT_LABELS[currentType],
|
|
473
|
+
new_seat: SEAT_LABELS[seat_type],
|
|
474
|
+
}, null, 2));
|
|
475
|
+
}
|
|
476
|
+
catch (e) {
|
|
477
|
+
return toolError(`Failed to change seat: ${e.response?.status || e.message}`);
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
},
|
|
481
|
+
});
|
|
311
482
|
//# sourceMappingURL=org.js.map
|
package/package.json
CHANGED