@centrali-io/centrali-mcp 4.4.8-rc.8 → 4.4.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/dist/tools/describe.js +55 -21
- package/dist/tools/service-accounts.js +8 -14
- package/package.json +1 -1
- package/src/tools/describe.ts +55 -21
- package/src/tools/service-accounts.ts +8 -12
package/dist/tools/describe.js
CHANGED
|
@@ -338,18 +338,46 @@ function registerDescribeTools(server) {
|
|
|
338
338
|
},
|
|
339
339
|
render_url: {
|
|
340
340
|
method: "client.getFileRenderUrl(renderId, options?)",
|
|
341
|
-
|
|
342
|
-
|
|
341
|
+
important: "uploadFile() returns a renderId (string), NOT a URL. You MUST call getFileRenderUrl(renderId) to construct the actual URL for display.",
|
|
342
|
+
url_pattern: "https://api.{domain}/storage/ws/{workspaceId}/api/v1/render/{renderId}",
|
|
343
|
+
transforms: "Optional image transformations: width, height, fit ('cover'|'contain'|'fill'), format ('webp'|'png'|'jpeg'), quality (1-100).",
|
|
344
|
+
public_vs_private: {
|
|
345
|
+
public: "If uploaded with isPublic=true, the render URL works without any auth — use directly in <img> tags.",
|
|
346
|
+
private: "If uploaded with isPublic=false (default), the render URL requires authentication. Pass a Bearer token (service account) or x-api-key (publishable key) in the request headers. For browser rendering of private images, use a server-side proxy route or a publishable key.",
|
|
347
|
+
},
|
|
348
|
+
example: [
|
|
349
|
+
"// Upload returns renderId, NOT a URL",
|
|
350
|
+
"const { data: renderId } = await centrali.uploadFile(file, '/root/shared/logos', true);",
|
|
351
|
+
"",
|
|
352
|
+
"// Build the render URL from the renderId",
|
|
353
|
+
"const url = centrali.getFileRenderUrl(renderId);",
|
|
354
|
+
"// => 'https://api.centrali.io/storage/ws/my-workspace/api/v1/render/abc123'",
|
|
355
|
+
"",
|
|
356
|
+
"// With image transforms (thumbnail)",
|
|
357
|
+
"const thumbUrl = centrali.getFileRenderUrl(renderId, { width: 200, height: 200, fit: 'cover', format: 'webp' });",
|
|
358
|
+
"",
|
|
359
|
+
"// For private images in Next.js — proxy through an API route",
|
|
360
|
+
"// app/api/image/[renderId]/route.ts",
|
|
361
|
+
"export async function GET(req, { params }) {",
|
|
362
|
+
" const centrali = new CentraliSDK({ baseUrl, workspaceId, clientId, clientSecret });",
|
|
363
|
+
" const url = centrali.getFileRenderUrl(params.renderId);",
|
|
364
|
+
" const resp = await fetch(url, {",
|
|
365
|
+
" headers: { Authorization: `Bearer ${await centrali.getToken()}` }",
|
|
366
|
+
" });",
|
|
367
|
+
" return new Response(resp.body, { headers: { 'Content-Type': resp.headers.get('Content-Type') } });",
|
|
368
|
+
"}",
|
|
369
|
+
].join("\n"),
|
|
343
370
|
},
|
|
344
371
|
download_url: {
|
|
345
372
|
method: "client.getFileDownloadUrl(renderId)",
|
|
346
|
-
note: "Returns a URL that triggers a file download.",
|
|
373
|
+
note: "Returns a URL that triggers a file download. Same auth rules as render URLs.",
|
|
347
374
|
},
|
|
348
375
|
tips: [
|
|
349
376
|
"/root/shared always exists — upload there directly or create subfolders like /root/shared/logos",
|
|
350
377
|
"Set isPublic=true for files that need to be accessible without auth (logos, avatars, public images)",
|
|
351
|
-
"Store the renderId
|
|
378
|
+
"Store the renderId on a record field — then use getFileRenderUrl() to build the URL when displaying",
|
|
352
379
|
"Use image transformations for thumbnails instead of uploading multiple sizes",
|
|
380
|
+
"For private images in browser apps: either use a server-side proxy route or a publishable key with storage scope",
|
|
353
381
|
],
|
|
354
382
|
},
|
|
355
383
|
realtime: {
|
|
@@ -2188,23 +2216,29 @@ function registerDescribeTools(server) {
|
|
|
2188
2216
|
createdAt: "string — ISO 8601 timestamp",
|
|
2189
2217
|
},
|
|
2190
2218
|
permission_model: {
|
|
2191
|
-
description: "
|
|
2219
|
+
description: "Access control is attribute-based. The caller's groups, roles, and other attributes (from JWT claims) are evaluated against policy conditions using JSON-Logic.",
|
|
2220
|
+
evaluation_flow: "Request → extract groups/roles from JWT → find permissions for the requested resource + action → evaluate each permission's linked policy against caller attributes → first Allow wins, otherwise Deny",
|
|
2192
2221
|
role: {
|
|
2193
|
-
description: "A named
|
|
2194
|
-
shape: {
|
|
2195
|
-
id: "UUID",
|
|
2196
|
-
name: "string (e.g., 'Data Reader', 'Compute Admin')",
|
|
2197
|
-
description: "string | null",
|
|
2198
|
-
permissions: "string[] — permission UUIDs",
|
|
2199
|
-
},
|
|
2222
|
+
description: "A named label assigned to users and service accounts. Appears in JWT claims as an attribute. Policy conditions can check roles. Names are immutable after creation.",
|
|
2223
|
+
shape: { id: "UUID", name: "string — immutable", description: "string | null" },
|
|
2200
2224
|
},
|
|
2201
2225
|
group: {
|
|
2202
|
-
description: "A named
|
|
2203
|
-
shape: {
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
},
|
|
2226
|
+
description: "A named label for organizing users and service accounts. Appears in JWT claims. Policy conditions check group membership to decide access. Groups may also optionally appear on permissions. Names are immutable after creation.",
|
|
2227
|
+
shape: { id: "UUID", name: "string — immutable", description: "string | null" },
|
|
2228
|
+
},
|
|
2229
|
+
permission: {
|
|
2230
|
+
description: "Links a resource + actions to a policy. When a request comes in, the system finds permissions matching the resource + action, then evaluates each permission's linked policy against the caller's attributes.",
|
|
2231
|
+
shape: { id: "UUID", name: "string", resourceId: "UUID (from resources)", actions: "string[]", policyId: "UUID (from policies)", priority: "number" },
|
|
2232
|
+
},
|
|
2233
|
+
policy: {
|
|
2234
|
+
description: "A reusable set of JSON-Logic rules with an effect (Allow/Deny). Policy conditions check caller attributes like groups, roles, user_id, ip_address, time of day, etc. Example: 'Allow if caller is in group engineering'.",
|
|
2235
|
+
},
|
|
2236
|
+
resource: {
|
|
2237
|
+
description: "A protected thing in the system (e.g., 'workspace::records', 'workspace::functions'). Has a category and a list of valid actions.",
|
|
2238
|
+
},
|
|
2239
|
+
granting_access: {
|
|
2240
|
+
workflow: "1. Ensure the SA is in the right group → 2. Create a policy with conditions that match (e.g., group membership check) → 3. Create a permission linking the resource + actions + policy",
|
|
2241
|
+
shortcut: "Use generate_remediation + apply_remediation to auto-generate the correct policy + permission + group assignment for a denied action",
|
|
2208
2242
|
},
|
|
2209
2243
|
permission_actions: [
|
|
2210
2244
|
"create — create new resources",
|
|
@@ -2276,9 +2310,9 @@ function registerDescribeTools(server) {
|
|
|
2276
2310
|
},
|
|
2277
2311
|
roles: {
|
|
2278
2312
|
list_roles: "List all roles",
|
|
2279
|
-
get_role: "Get role details
|
|
2280
|
-
create_role: "Create role
|
|
2281
|
-
update_role: "Update role
|
|
2313
|
+
get_role: "Get role details",
|
|
2314
|
+
create_role: "Create a named role (names are immutable)",
|
|
2315
|
+
update_role: "Update role description",
|
|
2282
2316
|
delete_role: "Delete role",
|
|
2283
2317
|
list_service_account_roles: "List roles assigned to a SA",
|
|
2284
2318
|
assign_role_to_service_account: "Assign role to SA",
|
|
@@ -553,7 +553,7 @@ function registerServiceAccountTools(server, sdk, centraliUrl, workspaceId, ownC
|
|
|
553
553
|
}
|
|
554
554
|
}));
|
|
555
555
|
// ── Role CRUD ────────────────────────────────────────────────────
|
|
556
|
-
server.tool("list_roles", "List all roles in the workspace. Roles
|
|
556
|
+
server.tool("list_roles", "List all roles in the workspace. Roles are named labels assigned to users and service accounts.", {
|
|
557
557
|
page: zod_1.z.number().optional().describe("Page number (default: 1)"),
|
|
558
558
|
pageSize: zod_1.z.number().optional().describe("Results per page (default: 20)"),
|
|
559
559
|
}, (_a) => __awaiter(this, [_a], void 0, function* ({ page, pageSize }) {
|
|
@@ -591,17 +591,14 @@ function registerServiceAccountTools(server, sdk, centraliUrl, workspaceId, ownC
|
|
|
591
591
|
};
|
|
592
592
|
}
|
|
593
593
|
}));
|
|
594
|
-
server.tool("create_role", "Create a new role
|
|
595
|
-
name: zod_1.z.string().describe("Role name (e.g., 'Data Reader', 'Compute Admin')"),
|
|
594
|
+
server.tool("create_role", "Create a new role. Roles are named labels assigned to users and service accounts. They do NOT contain permissions directly — to grant access, create a policy that targets the role as a principal. Role names are immutable after creation.", {
|
|
595
|
+
name: zod_1.z.string().describe("Role name (e.g., 'Data Reader', 'Compute Admin'). Cannot be changed after creation."),
|
|
596
596
|
description: zod_1.z.string().optional().describe("Optional description of the role's purpose"),
|
|
597
|
-
|
|
598
|
-
}, (_a) => __awaiter(this, [_a], void 0, function* ({ name, description, permissions }) {
|
|
597
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ name, description }) {
|
|
599
598
|
try {
|
|
600
599
|
const input = { name };
|
|
601
600
|
if (description !== undefined)
|
|
602
601
|
input.description = description;
|
|
603
|
-
if (permissions !== undefined)
|
|
604
|
-
input.permissions = permissions;
|
|
605
602
|
const result = yield getRolesClient().post("/", input);
|
|
606
603
|
return {
|
|
607
604
|
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
@@ -614,17 +611,14 @@ function registerServiceAccountTools(server, sdk, centraliUrl, workspaceId, ownC
|
|
|
614
611
|
};
|
|
615
612
|
}
|
|
616
613
|
}));
|
|
617
|
-
server.tool("update_role", "Update a role's description
|
|
614
|
+
server.tool("update_role", "Update a role's description. Role names are immutable. Roles are named labels assigned to users/service accounts — they do NOT contain permissions directly. To grant access, create a policy that references the role.", {
|
|
618
615
|
roleId: zod_1.z.string().describe("The role ID (UUID) to update"),
|
|
619
616
|
description: zod_1.z.string().optional().describe("Updated description"),
|
|
620
|
-
|
|
621
|
-
}, (_a) => __awaiter(this, [_a], void 0, function* ({ roleId, description, permissions }) {
|
|
617
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ roleId, description }) {
|
|
622
618
|
try {
|
|
623
619
|
const input = {};
|
|
624
620
|
if (description !== undefined)
|
|
625
621
|
input.description = description;
|
|
626
|
-
if (permissions !== undefined)
|
|
627
|
-
input.permissions = permissions;
|
|
628
622
|
const result = yield getRolesClient().put(`/${roleId}`, input);
|
|
629
623
|
return {
|
|
630
624
|
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
@@ -637,7 +631,7 @@ function registerServiceAccountTools(server, sdk, centraliUrl, workspaceId, ownC
|
|
|
637
631
|
};
|
|
638
632
|
}
|
|
639
633
|
}));
|
|
640
|
-
server.tool("delete_role", "Delete a role. Service accounts
|
|
634
|
+
server.tool("delete_role", "Delete a role. Service accounts assigned to this role will lose the label.", {
|
|
641
635
|
roleId: zod_1.z.string().describe("The role ID (UUID) to delete"),
|
|
642
636
|
}, (_a) => __awaiter(this, [_a], void 0, function* ({ roleId }) {
|
|
643
637
|
try {
|
|
@@ -934,7 +928,7 @@ function registerServiceAccountTools(server, sdk, centraliUrl, workspaceId, ownC
|
|
|
934
928
|
}
|
|
935
929
|
}));
|
|
936
930
|
server.tool("create_permission", "Create a new permission definition. Permissions bind actions to a resource within a policy. Required fields: name, resourceId (UUID from list_resources), actions (string array), policyId (UUID from list_policies or create_policy).", {
|
|
937
|
-
permission: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).describe("Required: { name: string, resourceId: UUID, actions: string[], policyId: UUID }. Optional: description,
|
|
931
|
+
permission: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).describe("Required: { name: string, resourceId: UUID, actions: string[], policyId: UUID }. Optional: description, priority (number)."),
|
|
938
932
|
}, (_a) => __awaiter(this, [_a], void 0, function* ({ permission }) {
|
|
939
933
|
try {
|
|
940
934
|
const result = yield getPermissionsClient().post("/", permission);
|
package/package.json
CHANGED
package/src/tools/describe.ts
CHANGED
|
@@ -346,18 +346,46 @@ export function registerDescribeTools(server: McpServer) {
|
|
|
346
346
|
},
|
|
347
347
|
render_url: {
|
|
348
348
|
method: "client.getFileRenderUrl(renderId, options?)",
|
|
349
|
-
|
|
350
|
-
|
|
349
|
+
important: "uploadFile() returns a renderId (string), NOT a URL. You MUST call getFileRenderUrl(renderId) to construct the actual URL for display.",
|
|
350
|
+
url_pattern: "https://api.{domain}/storage/ws/{workspaceId}/api/v1/render/{renderId}",
|
|
351
|
+
transforms: "Optional image transformations: width, height, fit ('cover'|'contain'|'fill'), format ('webp'|'png'|'jpeg'), quality (1-100).",
|
|
352
|
+
public_vs_private: {
|
|
353
|
+
public: "If uploaded with isPublic=true, the render URL works without any auth — use directly in <img> tags.",
|
|
354
|
+
private: "If uploaded with isPublic=false (default), the render URL requires authentication. Pass a Bearer token (service account) or x-api-key (publishable key) in the request headers. For browser rendering of private images, use a server-side proxy route or a publishable key.",
|
|
355
|
+
},
|
|
356
|
+
example: [
|
|
357
|
+
"// Upload returns renderId, NOT a URL",
|
|
358
|
+
"const { data: renderId } = await centrali.uploadFile(file, '/root/shared/logos', true);",
|
|
359
|
+
"",
|
|
360
|
+
"// Build the render URL from the renderId",
|
|
361
|
+
"const url = centrali.getFileRenderUrl(renderId);",
|
|
362
|
+
"// => 'https://api.centrali.io/storage/ws/my-workspace/api/v1/render/abc123'",
|
|
363
|
+
"",
|
|
364
|
+
"// With image transforms (thumbnail)",
|
|
365
|
+
"const thumbUrl = centrali.getFileRenderUrl(renderId, { width: 200, height: 200, fit: 'cover', format: 'webp' });",
|
|
366
|
+
"",
|
|
367
|
+
"// For private images in Next.js — proxy through an API route",
|
|
368
|
+
"// app/api/image/[renderId]/route.ts",
|
|
369
|
+
"export async function GET(req, { params }) {",
|
|
370
|
+
" const centrali = new CentraliSDK({ baseUrl, workspaceId, clientId, clientSecret });",
|
|
371
|
+
" const url = centrali.getFileRenderUrl(params.renderId);",
|
|
372
|
+
" const resp = await fetch(url, {",
|
|
373
|
+
" headers: { Authorization: `Bearer ${await centrali.getToken()}` }",
|
|
374
|
+
" });",
|
|
375
|
+
" return new Response(resp.body, { headers: { 'Content-Type': resp.headers.get('Content-Type') } });",
|
|
376
|
+
"}",
|
|
377
|
+
].join("\n"),
|
|
351
378
|
},
|
|
352
379
|
download_url: {
|
|
353
380
|
method: "client.getFileDownloadUrl(renderId)",
|
|
354
|
-
note: "Returns a URL that triggers a file download.",
|
|
381
|
+
note: "Returns a URL that triggers a file download. Same auth rules as render URLs.",
|
|
355
382
|
},
|
|
356
383
|
tips: [
|
|
357
384
|
"/root/shared always exists — upload there directly or create subfolders like /root/shared/logos",
|
|
358
385
|
"Set isPublic=true for files that need to be accessible without auth (logos, avatars, public images)",
|
|
359
|
-
"Store the renderId
|
|
386
|
+
"Store the renderId on a record field — then use getFileRenderUrl() to build the URL when displaying",
|
|
360
387
|
"Use image transformations for thumbnails instead of uploading multiple sizes",
|
|
388
|
+
"For private images in browser apps: either use a server-side proxy route or a publishable key with storage scope",
|
|
361
389
|
],
|
|
362
390
|
},
|
|
363
391
|
realtime: {
|
|
@@ -2485,23 +2513,29 @@ export function registerDescribeTools(server: McpServer) {
|
|
|
2485
2513
|
createdAt: "string — ISO 8601 timestamp",
|
|
2486
2514
|
},
|
|
2487
2515
|
permission_model: {
|
|
2488
|
-
description: "
|
|
2516
|
+
description: "Access control is attribute-based. The caller's groups, roles, and other attributes (from JWT claims) are evaluated against policy conditions using JSON-Logic.",
|
|
2517
|
+
evaluation_flow: "Request → extract groups/roles from JWT → find permissions for the requested resource + action → evaluate each permission's linked policy against caller attributes → first Allow wins, otherwise Deny",
|
|
2489
2518
|
role: {
|
|
2490
|
-
description: "A named
|
|
2491
|
-
shape: {
|
|
2492
|
-
id: "UUID",
|
|
2493
|
-
name: "string (e.g., 'Data Reader', 'Compute Admin')",
|
|
2494
|
-
description: "string | null",
|
|
2495
|
-
permissions: "string[] — permission UUIDs",
|
|
2496
|
-
},
|
|
2519
|
+
description: "A named label assigned to users and service accounts. Appears in JWT claims as an attribute. Policy conditions can check roles. Names are immutable after creation.",
|
|
2520
|
+
shape: { id: "UUID", name: "string — immutable", description: "string | null" },
|
|
2497
2521
|
},
|
|
2498
2522
|
group: {
|
|
2499
|
-
description: "A named
|
|
2500
|
-
shape: {
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
},
|
|
2523
|
+
description: "A named label for organizing users and service accounts. Appears in JWT claims. Policy conditions check group membership to decide access. Groups may also optionally appear on permissions. Names are immutable after creation.",
|
|
2524
|
+
shape: { id: "UUID", name: "string — immutable", description: "string | null" },
|
|
2525
|
+
},
|
|
2526
|
+
permission: {
|
|
2527
|
+
description: "Links a resource + actions to a policy. When a request comes in, the system finds permissions matching the resource + action, then evaluates each permission's linked policy against the caller's attributes.",
|
|
2528
|
+
shape: { id: "UUID", name: "string", resourceId: "UUID (from resources)", actions: "string[]", policyId: "UUID (from policies)", priority: "number" },
|
|
2529
|
+
},
|
|
2530
|
+
policy: {
|
|
2531
|
+
description: "A reusable set of JSON-Logic rules with an effect (Allow/Deny). Policy conditions check caller attributes like groups, roles, user_id, ip_address, time of day, etc. Example: 'Allow if caller is in group engineering'.",
|
|
2532
|
+
},
|
|
2533
|
+
resource: {
|
|
2534
|
+
description: "A protected thing in the system (e.g., 'workspace::records', 'workspace::functions'). Has a category and a list of valid actions.",
|
|
2535
|
+
},
|
|
2536
|
+
granting_access: {
|
|
2537
|
+
workflow: "1. Ensure the SA is in the right group → 2. Create a policy with conditions that match (e.g., group membership check) → 3. Create a permission linking the resource + actions + policy",
|
|
2538
|
+
shortcut: "Use generate_remediation + apply_remediation to auto-generate the correct policy + permission + group assignment for a denied action",
|
|
2505
2539
|
},
|
|
2506
2540
|
permission_actions: [
|
|
2507
2541
|
"create — create new resources",
|
|
@@ -2573,9 +2607,9 @@ export function registerDescribeTools(server: McpServer) {
|
|
|
2573
2607
|
},
|
|
2574
2608
|
roles: {
|
|
2575
2609
|
list_roles: "List all roles",
|
|
2576
|
-
get_role: "Get role details
|
|
2577
|
-
create_role: "Create role
|
|
2578
|
-
update_role: "Update role
|
|
2610
|
+
get_role: "Get role details",
|
|
2611
|
+
create_role: "Create a named role (names are immutable)",
|
|
2612
|
+
update_role: "Update role description",
|
|
2579
2613
|
delete_role: "Delete role",
|
|
2580
2614
|
list_service_account_roles: "List roles assigned to a SA",
|
|
2581
2615
|
assign_role_to_service_account: "Assign role to SA",
|
|
@@ -682,7 +682,7 @@ export function registerServiceAccountTools(server: McpServer, sdk: CentraliSDK,
|
|
|
682
682
|
|
|
683
683
|
server.tool(
|
|
684
684
|
"list_roles",
|
|
685
|
-
"List all roles in the workspace. Roles
|
|
685
|
+
"List all roles in the workspace. Roles are named labels assigned to users and service accounts.",
|
|
686
686
|
{
|
|
687
687
|
page: z.number().optional().describe("Page number (default: 1)"),
|
|
688
688
|
pageSize: z.number().optional().describe("Results per page (default: 20)"),
|
|
@@ -728,17 +728,15 @@ export function registerServiceAccountTools(server: McpServer, sdk: CentraliSDK,
|
|
|
728
728
|
|
|
729
729
|
server.tool(
|
|
730
730
|
"create_role",
|
|
731
|
-
"Create a new role
|
|
731
|
+
"Create a new role. Roles are named labels assigned to users and service accounts. They do NOT contain permissions directly — to grant access, create a policy that targets the role as a principal. Role names are immutable after creation.",
|
|
732
732
|
{
|
|
733
|
-
name: z.string().describe("Role name (e.g., 'Data Reader', 'Compute Admin')"),
|
|
733
|
+
name: z.string().describe("Role name (e.g., 'Data Reader', 'Compute Admin'). Cannot be changed after creation."),
|
|
734
734
|
description: z.string().optional().describe("Optional description of the role's purpose"),
|
|
735
|
-
permissions: z.array(z.string()).optional().describe("Array of permission UUIDs to include in this role"),
|
|
736
735
|
},
|
|
737
|
-
async ({ name, description
|
|
736
|
+
async ({ name, description }) => {
|
|
738
737
|
try {
|
|
739
738
|
const input: Record<string, any> = { name };
|
|
740
739
|
if (description !== undefined) input.description = description;
|
|
741
|
-
if (permissions !== undefined) input.permissions = permissions;
|
|
742
740
|
const result = await getRolesClient().post("/", input);
|
|
743
741
|
return {
|
|
744
742
|
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
@@ -754,17 +752,15 @@ export function registerServiceAccountTools(server: McpServer, sdk: CentraliSDK,
|
|
|
754
752
|
|
|
755
753
|
server.tool(
|
|
756
754
|
"update_role",
|
|
757
|
-
"Update a role's description
|
|
755
|
+
"Update a role's description. Role names are immutable. Roles are named labels assigned to users/service accounts — they do NOT contain permissions directly. To grant access, create a policy that references the role.",
|
|
758
756
|
{
|
|
759
757
|
roleId: z.string().describe("The role ID (UUID) to update"),
|
|
760
758
|
description: z.string().optional().describe("Updated description"),
|
|
761
|
-
permissions: z.array(z.string()).optional().describe("Updated permission UUIDs (replaces all existing)"),
|
|
762
759
|
},
|
|
763
|
-
async ({ roleId, description
|
|
760
|
+
async ({ roleId, description }) => {
|
|
764
761
|
try {
|
|
765
762
|
const input: Record<string, any> = {};
|
|
766
763
|
if (description !== undefined) input.description = description;
|
|
767
|
-
if (permissions !== undefined) input.permissions = permissions;
|
|
768
764
|
const result = await getRolesClient().put(`/${roleId}`, input);
|
|
769
765
|
return {
|
|
770
766
|
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
@@ -780,7 +776,7 @@ export function registerServiceAccountTools(server: McpServer, sdk: CentraliSDK,
|
|
|
780
776
|
|
|
781
777
|
server.tool(
|
|
782
778
|
"delete_role",
|
|
783
|
-
"Delete a role. Service accounts
|
|
779
|
+
"Delete a role. Service accounts assigned to this role will lose the label.",
|
|
784
780
|
{
|
|
785
781
|
roleId: z.string().describe("The role ID (UUID) to delete"),
|
|
786
782
|
},
|
|
@@ -1164,7 +1160,7 @@ export function registerServiceAccountTools(server: McpServer, sdk: CentraliSDK,
|
|
|
1164
1160
|
"create_permission",
|
|
1165
1161
|
"Create a new permission definition. Permissions bind actions to a resource within a policy. Required fields: name, resourceId (UUID from list_resources), actions (string array), policyId (UUID from list_policies or create_policy).",
|
|
1166
1162
|
{
|
|
1167
|
-
permission: z.record(z.string(), z.any()).describe("Required: { name: string, resourceId: UUID, actions: string[], policyId: UUID }. Optional: description,
|
|
1163
|
+
permission: z.record(z.string(), z.any()).describe("Required: { name: string, resourceId: UUID, actions: string[], policyId: UUID }. Optional: description, priority (number)."),
|
|
1168
1164
|
},
|
|
1169
1165
|
async ({ permission }) => {
|
|
1170
1166
|
try {
|