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
@@ -0,0 +1,311 @@
1
+ import { z } from 'zod';
2
+ import { internalClient } from '../clients/internal-api.js';
3
+ import { defineTool, toolResult, toolError, figmaId, requireOrgId } from './register.js';
4
+ // -- list_admins --
5
+ defineTool({
6
+ toolset: 'org',
7
+ auth: 'cookie',
8
+ register(server, config) {
9
+ server.registerTool('list_admins', {
10
+ description: 'List org admins with their permission levels, seat status, and email validation state.',
11
+ inputSchema: {
12
+ org_id: figmaId.optional().describe('Org ID override (defaults to current workspace)'),
13
+ include_license_admins: z.boolean().optional().describe('Include license admins (default false)'),
14
+ },
15
+ }, async ({ org_id, include_license_admins }) => {
16
+ try {
17
+ let orgId;
18
+ try {
19
+ orgId = requireOrgId(config, org_id);
20
+ }
21
+ catch (e) {
22
+ return toolError(e.message);
23
+ }
24
+ const res = await internalClient(config).get(`/api/orgs/${orgId}/admins`, { params: { include_license_admins: include_license_admins ?? false } });
25
+ const admins = (res.data?.meta || []).map((a) => ({
26
+ user_id: a.user_id,
27
+ email: a.user?.email,
28
+ name: a.user?.handle,
29
+ permission: a.permission,
30
+ seat_type: a.active_seat_type?.key || null,
31
+ is_email_validated: a.is_email_validated,
32
+ license_admin: a.license_admin,
33
+ }));
34
+ return toolResult(JSON.stringify(admins, null, 2));
35
+ }
36
+ catch (e) {
37
+ return toolError(`Failed to list admins: ${e.response?.status || e.message}`);
38
+ }
39
+ });
40
+ },
41
+ });
42
+ // -- list_org_teams --
43
+ defineTool({
44
+ toolset: 'org',
45
+ auth: 'cookie',
46
+ register(server, config) {
47
+ server.registerTool('list_org_teams', {
48
+ description: 'List all teams in the org with member counts, project counts, and access levels.',
49
+ inputSchema: {
50
+ org_id: figmaId.optional().describe('Org ID override (defaults to current workspace)'),
51
+ include_secret_teams: z.boolean().optional().describe('Include secret teams (default false)'),
52
+ },
53
+ }, async ({ org_id, include_secret_teams }) => {
54
+ try {
55
+ let orgId;
56
+ try {
57
+ orgId = requireOrgId(config, org_id);
58
+ }
59
+ catch (e) {
60
+ return toolError(e.message);
61
+ }
62
+ const res = await internalClient(config).get(`/api/orgs/${orgId}/teams`, {
63
+ params: {
64
+ include_member_count: true,
65
+ include_project_count: true,
66
+ include_top_members: true,
67
+ include_secret_teams: include_secret_teams ?? false,
68
+ },
69
+ });
70
+ const teams = res.data?.meta || res.data;
71
+ const mapped = (Array.isArray(teams) ? teams : []).map((t) => ({
72
+ id: String(t.id),
73
+ name: t.name,
74
+ member_count: t.member_count ?? null,
75
+ project_count: t.project_count ?? null,
76
+ access_level: t.access_level ?? null,
77
+ }));
78
+ return toolResult(JSON.stringify(mapped, null, 2));
79
+ }
80
+ catch (e) {
81
+ return toolError(`Failed to list org teams: ${e.response?.status || e.message}`);
82
+ }
83
+ });
84
+ },
85
+ });
86
+ // -- seat_usage --
87
+ defineTool({
88
+ toolset: 'org',
89
+ auth: 'cookie',
90
+ register(server, config) {
91
+ server.registerTool('seat_usage', {
92
+ description: 'Seat usage breakdown: permission counts, seat types, activity recency, and account type distribution.',
93
+ inputSchema: {
94
+ org_id: figmaId.optional().describe('Org ID override (defaults to current workspace)'),
95
+ search_query: z.string().optional().describe('Filter counts by user search query'),
96
+ },
97
+ }, async ({ org_id, search_query }) => {
98
+ try {
99
+ let orgId;
100
+ try {
101
+ orgId = requireOrgId(config, org_id);
102
+ }
103
+ catch (e) {
104
+ return toolError(e.message);
105
+ }
106
+ const params = {};
107
+ if (search_query)
108
+ params.search_query = search_query;
109
+ const res = await internalClient(config).get(`/api/orgs/${orgId}/org_users/filter_counts`, { params });
110
+ return toolResult(JSON.stringify(res.data?.meta || res.data, null, 2));
111
+ }
112
+ catch (e) {
113
+ return toolError(`Failed to fetch seat usage: ${e.response?.status || e.message}`);
114
+ }
115
+ });
116
+ },
117
+ });
118
+ // -- list_team_members --
119
+ defineTool({
120
+ toolset: 'org',
121
+ auth: 'cookie',
122
+ register(server, config) {
123
+ server.registerTool('list_team_members', {
124
+ description: 'List members of a team with name, email, avatar, last active date, and role.',
125
+ inputSchema: {
126
+ team_id: figmaId.describe('Team ID'),
127
+ },
128
+ }, async ({ team_id }) => {
129
+ try {
130
+ const res = await internalClient(config).get(`/api/teams/${team_id}/members`);
131
+ const members = (res.data?.meta || res.data || []).map((m) => ({
132
+ id: m.id,
133
+ name: m.name,
134
+ email: m.email,
135
+ img_url: m.img_url,
136
+ last_active: m.last_active,
137
+ team_role: m.team_role?.level || null,
138
+ permission: m.team_user?.permission || null,
139
+ seat_type: m.seat_type || null,
140
+ }));
141
+ return toolResult(JSON.stringify(members, null, 2));
142
+ }
143
+ catch (e) {
144
+ return toolError(`Failed to list team members: ${e.response?.status || e.message}`);
145
+ }
146
+ });
147
+ },
148
+ });
149
+ // -- billing_overview --
150
+ defineTool({
151
+ toolset: 'org',
152
+ auth: 'cookie',
153
+ register(server, config) {
154
+ server.registerTool('billing_overview', {
155
+ description: 'Org billing data including invoice history, status, amounts, and billing periods.',
156
+ inputSchema: {
157
+ org_id: figmaId.optional().describe('Org ID override (defaults to current workspace)'),
158
+ },
159
+ }, async ({ org_id }) => {
160
+ try {
161
+ let orgId;
162
+ try {
163
+ orgId = requireOrgId(config, org_id);
164
+ }
165
+ catch (e) {
166
+ return toolError(e.message);
167
+ }
168
+ const res = await internalClient(config).get(`/api/orgs/${orgId}/billing_data`);
169
+ const data = res.data?.meta || res.data;
170
+ const { shipping_address, ...billing } = data;
171
+ return toolResult(JSON.stringify(billing, null, 2));
172
+ }
173
+ catch (e) {
174
+ return toolError(`Failed to fetch billing overview: ${e.response?.status || e.message}`);
175
+ }
176
+ });
177
+ },
178
+ });
179
+ // -- list_invoices --
180
+ defineTool({
181
+ toolset: 'org',
182
+ auth: 'cookie',
183
+ register(server, config) {
184
+ server.registerTool('list_invoices', {
185
+ description: 'List open and upcoming invoices for the org.',
186
+ inputSchema: {
187
+ org_id: figmaId.optional().describe('Org ID override (defaults to current workspace)'),
188
+ },
189
+ }, async ({ org_id }) => {
190
+ try {
191
+ let orgId;
192
+ try {
193
+ orgId = requireOrgId(config, org_id);
194
+ }
195
+ catch (e) {
196
+ return toolError(e.message);
197
+ }
198
+ const client = internalClient(config);
199
+ const [openResult, upcomingResult] = await Promise.allSettled([
200
+ client.get(`/api/plans/organization/${orgId}/invoices/open`),
201
+ client.get(`/api/plans/organization/${orgId}/invoices/upcoming`),
202
+ ]);
203
+ const response = {};
204
+ if (openResult.status === 'fulfilled')
205
+ response.open = openResult.value.data;
206
+ else
207
+ response.open_error = 'Failed to fetch open invoices';
208
+ if (upcomingResult.status === 'fulfilled')
209
+ response.upcoming = upcomingResult.value.data;
210
+ else
211
+ response.upcoming_error = 'Failed to fetch upcoming invoices';
212
+ return toolResult(JSON.stringify(response, null, 2));
213
+ }
214
+ catch (e) {
215
+ return toolError(`Failed to list invoices: ${e.response?.status || e.message}`);
216
+ }
217
+ });
218
+ },
219
+ });
220
+ // -- org_domains --
221
+ defineTool({
222
+ toolset: 'org',
223
+ auth: 'cookie',
224
+ register(server, config) {
225
+ server.registerTool('org_domains', {
226
+ description: 'Org domain configuration and SSO/SAML settings.',
227
+ inputSchema: {
228
+ org_id: figmaId.optional().describe('Org ID override (defaults to current workspace)'),
229
+ },
230
+ }, async ({ org_id }) => {
231
+ try {
232
+ let orgId;
233
+ try {
234
+ orgId = requireOrgId(config, org_id);
235
+ }
236
+ catch (e) {
237
+ return toolError(e.message);
238
+ }
239
+ const client = internalClient(config);
240
+ const [domainsResult, ssoResult] = await Promise.allSettled([
241
+ client.get(`/api/orgs/${orgId}/domains`),
242
+ client.get(`/api/org/${orgId}/org_saml_config`),
243
+ ]);
244
+ const response = {};
245
+ if (domainsResult.status === 'fulfilled')
246
+ response.domains = domainsResult.value.data;
247
+ else
248
+ response.domains_error = 'Failed to fetch domains';
249
+ if (ssoResult.status === 'fulfilled')
250
+ response.sso = ssoResult.value.data;
251
+ else
252
+ response.sso_error = 'SSO config not available';
253
+ return toolResult(JSON.stringify(response, null, 2));
254
+ }
255
+ catch (e) {
256
+ return toolError(`Failed to fetch org domains: ${e.response?.status || e.message}`);
257
+ }
258
+ });
259
+ },
260
+ });
261
+ // -- ai_credit_usage --
262
+ defineTool({
263
+ toolset: 'org',
264
+ auth: 'cookie',
265
+ register(server, config) {
266
+ server.registerTool('ai_credit_usage', {
267
+ description: 'AI credit usage summary for a billing plan. Get the plan_id from billing_overview.',
268
+ inputSchema: {
269
+ plan_id: figmaId.describe('Plan ID (from billing_overview response)'),
270
+ },
271
+ }, async ({ plan_id }) => {
272
+ try {
273
+ const res = await internalClient(config).get(`/api/plans/${plan_id}/ai_credits/plan_usage_summary`);
274
+ return toolResult(JSON.stringify(res.data, null, 2));
275
+ }
276
+ catch (e) {
277
+ return toolError(`Failed to fetch AI credit usage: ${e.response?.status || e.message}`);
278
+ }
279
+ });
280
+ },
281
+ });
282
+ // -- export_members --
283
+ defineTool({
284
+ toolset: 'org',
285
+ auth: 'cookie',
286
+ mutates: true,
287
+ register(server, config) {
288
+ server.registerTool('export_members', {
289
+ description: 'Trigger async CSV export of all org members. The CSV is sent to the admin email on file.',
290
+ inputSchema: {
291
+ org_id: figmaId.optional().describe('Org ID override (defaults to current workspace)'),
292
+ },
293
+ }, async ({ org_id }) => {
294
+ try {
295
+ let orgId;
296
+ try {
297
+ orgId = requireOrgId(config, org_id);
298
+ }
299
+ catch (e) {
300
+ return toolError(e.message);
301
+ }
302
+ await internalClient(config).post(`/api/orgs/${orgId}/export_members`);
303
+ return toolResult('CSV export queued. It will be emailed to the org admin.');
304
+ }
305
+ catch (e) {
306
+ return toolError(`Failed to export members: ${e.response?.status || e.message}`);
307
+ }
308
+ });
309
+ },
310
+ });
311
+ //# sourceMappingURL=org.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=permissions.d.ts.map
@@ -0,0 +1,246 @@
1
+ import { z } from 'zod';
2
+ import { internalClient } from '../clients/internal-api.js';
3
+ import { defineTool, toolResult, toolError, figmaId } from './register.js';
4
+ const LEVEL_MAP = { owner: 999, editor: 300, viewer: 100 };
5
+ const LEVEL_NAMES = { 999: 'owner', 300: 'editor', 100: 'viewer' };
6
+ function levelName(level) {
7
+ return LEVEL_NAMES[level] || `level:${level}`;
8
+ }
9
+ async function getRoles(config, resourceType, resourceId) {
10
+ const res = await internalClient(config).get(`/api/roles/${resourceType}/${resourceId}`);
11
+ const meta = res.data?.meta;
12
+ return Array.isArray(meta) ? meta : [];
13
+ }
14
+ // -- get_permissions --
15
+ defineTool({
16
+ toolset: 'permissions',
17
+ auth: 'cookie',
18
+ register(server, config) {
19
+ server.registerTool('get_permissions', {
20
+ description: 'See who has access to a file, project, or team. Returns users with roles and role IDs.',
21
+ inputSchema: {
22
+ resource_type: z.enum(['file', 'folder', 'team']).describe('Type of resource'),
23
+ resource_id: figmaId.describe('Resource ID (file key, folder ID, or team ID)'),
24
+ },
25
+ }, async ({ resource_type, resource_id }) => {
26
+ try {
27
+ const roles = await getRoles(config, resource_type, resource_id);
28
+ const users = roles.map((r) => ({
29
+ role_id: String(r.id),
30
+ user_id: r.user_id ? String(r.user_id) : undefined,
31
+ handle: r.user?.handle,
32
+ email: r.user?.email || r.pending_email,
33
+ role: levelName(r.level),
34
+ level: r.level,
35
+ pending: !!r.pending_email,
36
+ }));
37
+ return toolResult(JSON.stringify(users, null, 2));
38
+ }
39
+ catch (e) {
40
+ return toolError(`Failed to get permissions: ${e.response?.status || e.message}`);
41
+ }
42
+ });
43
+ },
44
+ });
45
+ // -- set_permissions --
46
+ defineTool({
47
+ toolset: 'permissions',
48
+ auth: 'cookie',
49
+ mutates: true,
50
+ register(server, config) {
51
+ server.registerTool('set_permissions', {
52
+ description: 'Change access level for a user on a file, project, or team. Looks up the role by user_id.',
53
+ inputSchema: {
54
+ resource_type: z.enum(['file', 'folder', 'team']).describe('Type of resource'),
55
+ resource_id: figmaId.describe('Resource ID'),
56
+ user_id: figmaId.describe('User ID to change access for'),
57
+ role: z.enum(['owner', 'editor', 'viewer']).describe('Role to assign'),
58
+ },
59
+ }, async ({ resource_type, resource_id, user_id, role }) => {
60
+ try {
61
+ const roles = await getRoles(config, resource_type, resource_id);
62
+ const target = roles.find((r) => String(r.user_id) === user_id);
63
+ if (!target) {
64
+ return toolError(`User ${user_id} has no role on this ${resource_type}. Use share to invite them first.`);
65
+ }
66
+ const level = LEVEL_MAP[role];
67
+ await internalClient(config).put(`/api/roles/${target.id}`, { level });
68
+ return toolResult(`Set ${role} (level ${level}) for user ${user_id} on ${resource_type} ${resource_id}`);
69
+ }
70
+ catch (e) {
71
+ return toolError(`Failed to set permissions: ${e.response?.status || e.message}`);
72
+ }
73
+ });
74
+ },
75
+ });
76
+ // -- share --
77
+ defineTool({
78
+ toolset: 'permissions',
79
+ auth: 'cookie',
80
+ mutates: true,
81
+ register(server, config) {
82
+ server.registerTool('share', {
83
+ description: 'Share a file or project with someone by email. Sends an invite.',
84
+ inputSchema: {
85
+ resource_type: z.enum(['file', 'folder', 'team']).describe('Type of resource'),
86
+ resource_id: figmaId.describe('Resource ID (file key, folder ID, or team ID)'),
87
+ email: z.string().describe('Email address to invite'),
88
+ role: z.enum(['editor', 'viewer']).optional().describe('Role to grant (default: viewer)'),
89
+ },
90
+ }, async ({ resource_type, resource_id, email, role }) => {
91
+ try {
92
+ const level = LEVEL_MAP[role || 'viewer'];
93
+ const res = await internalClient(config).post('/api/invites', {
94
+ resource_type,
95
+ resource_id_or_key: resource_id,
96
+ emails: [email],
97
+ level,
98
+ });
99
+ const invites = res.data?.meta?.invites || [];
100
+ const roleId = invites[0]?.id;
101
+ let msg = `Invited ${email} as ${role || 'viewer'} on ${resource_type} ${resource_id}`;
102
+ if (roleId)
103
+ msg += ` (role_id: ${roleId})`;
104
+ return toolResult(msg);
105
+ }
106
+ catch (e) {
107
+ return toolError(`Failed to share: ${e.response?.status || e.message}`);
108
+ }
109
+ });
110
+ },
111
+ });
112
+ // -- revoke_access --
113
+ defineTool({
114
+ toolset: 'permissions',
115
+ auth: 'cookie',
116
+ mutates: true,
117
+ destructive: true,
118
+ register(server, config) {
119
+ server.registerTool('revoke_access', {
120
+ description: "Remove someone's access to a file, project, or team. Looks up the role by user_id.",
121
+ inputSchema: {
122
+ resource_type: z.enum(['file', 'folder', 'team']).describe('Type of resource'),
123
+ resource_id: figmaId.describe('Resource ID'),
124
+ user_id: figmaId.describe('User ID to revoke access from'),
125
+ },
126
+ }, async ({ resource_type, resource_id, user_id }) => {
127
+ try {
128
+ const roles = await getRoles(config, resource_type, resource_id);
129
+ const target = roles.find((r) => String(r.user_id) === user_id);
130
+ if (!target) {
131
+ return toolError(`User ${user_id} has no role on this ${resource_type}.`);
132
+ }
133
+ await internalClient(config).delete(`/api/roles/${target.id}`);
134
+ return toolResult(`Revoked access for user ${user_id} on ${resource_type} ${resource_id}`);
135
+ }
136
+ catch (e) {
137
+ return toolError(`Failed to revoke access: ${e.response?.status || e.message}`);
138
+ }
139
+ });
140
+ },
141
+ });
142
+ // -- list_role_requests --
143
+ defineTool({
144
+ toolset: 'permissions',
145
+ auth: 'cookie',
146
+ register(server, config) {
147
+ server.registerTool('list_role_requests', {
148
+ description: 'List pending file access requests. These come through the notification system.',
149
+ inputSchema: {},
150
+ }, async () => {
151
+ try {
152
+ const res = await internalClient(config).get('/api/user_notifications/server_driven/plan', {
153
+ params: { current_plan_id: '-1', app_version: '1', client_type: 'web' },
154
+ });
155
+ const notifications = res.data?.meta || [];
156
+ const requests = (Array.isArray(notifications) ? notifications : [])
157
+ .filter((n) => n.notification_type === 'FileRoleRequestCreatedNotif')
158
+ .map((n) => {
159
+ const locals = n.notification?.locals || n.preferred_attachments?.[0]?.body || {};
160
+ // Extract from notification locals if available, otherwise parse from display text
161
+ const title = n.preferred_attachments?.[0]?.body?.title || [];
162
+ const subtitle = n.preferred_attachments?.[0]?.body?.subtitle || [];
163
+ const email = title[0]?.html_text?.replace(/<[^>]+>/g, '') || undefined;
164
+ const action = subtitle[0]?.html_text || undefined;
165
+ const fileName = subtitle[2]?.html_text || undefined;
166
+ const hasActions = !!n.preferred_attachments?.[0]?.actions;
167
+ return {
168
+ notification_id: n.notification_id,
169
+ email,
170
+ action,
171
+ file_name: fileName,
172
+ pending: hasActions,
173
+ is_unread: n.is_unread,
174
+ created_at: n.preferred_attachments?.[0]?.body?.created_at,
175
+ };
176
+ });
177
+ return toolResult(JSON.stringify(requests, null, 2));
178
+ }
179
+ catch (e) {
180
+ return toolError(`Failed to list role requests: ${e.response?.status || e.message}`);
181
+ }
182
+ });
183
+ },
184
+ });
185
+ // -- approve_role_request --
186
+ defineTool({
187
+ toolset: 'permissions',
188
+ auth: 'cookie',
189
+ mutates: true,
190
+ register(server, config) {
191
+ server.registerTool('approve_role_request', {
192
+ description: 'Approve a pending file access request by notification ID.',
193
+ inputSchema: {
194
+ notification_id: figmaId.describe('Notification ID from list_role_requests'),
195
+ },
196
+ }, async ({ notification_id }) => {
197
+ try {
198
+ const res = await internalClient(config).put('/api/user_notifications/accept', {
199
+ id: notification_id,
200
+ medium: 'web',
201
+ appVersion: '1',
202
+ clientType: 'web',
203
+ });
204
+ const notif = res.data?.meta?.notification;
205
+ const locals = notif?.locals || {};
206
+ let msg = `Approved access request ${notification_id}`;
207
+ if (locals.file_name)
208
+ msg += ` for ${locals.file_name}`;
209
+ if (locals.user_id)
210
+ msg += ` (user ${locals.user_id})`;
211
+ return toolResult(msg);
212
+ }
213
+ catch (e) {
214
+ return toolError(`Failed to approve request: ${e.response?.status || e.message}`);
215
+ }
216
+ });
217
+ },
218
+ });
219
+ // -- deny_role_request --
220
+ defineTool({
221
+ toolset: 'permissions',
222
+ auth: 'cookie',
223
+ mutates: true,
224
+ register(server, config) {
225
+ server.registerTool('deny_role_request', {
226
+ description: 'Decline a pending file access request by notification ID.',
227
+ inputSchema: {
228
+ notification_id: figmaId.describe('Notification ID from list_role_requests'),
229
+ },
230
+ }, async ({ notification_id }) => {
231
+ try {
232
+ await internalClient(config).put('/api/user_notifications/reject', {
233
+ id: notification_id,
234
+ medium: 'web',
235
+ appVersion: '1',
236
+ clientType: 'web',
237
+ });
238
+ return toolResult(`Declined access request ${notification_id}`);
239
+ }
240
+ catch (e) {
241
+ return toolError(`Failed to deny request: ${e.response?.status || e.message}`);
242
+ }
243
+ });
244
+ },
245
+ });
246
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=projects.d.ts.map