put_roles_by_id

Changes data · you confirm first Idempotent

Calls PUT /api/roles/{id} as the signed-in person · REST reference

Update a role

Requires execute:UpdateRoleCommand; platform staff bypass that gate. The caller's own token is forwarded to the DAL, which requires execute:UpdateRoleCommand in the role's own organization and refuses — 403 — a system role, a role of the platform organization, or a role that belongs to no organization to anyone without platform authority. For such a caller, a role in an organization where they do not hold admin.roles.view is a 404. A SYSTEM ROLE IS NOT UPDATABLE: a rename is refused with 400 SYSTEM_ROLE_PROTECTION before the DAL is called, and any other update is refused by the DAL — 403 without platform authority, 409 with it. THE BODY IS NOT ALLOW-LISTED. BaseController.update copies EVERY key of the request body onto the model except id, createdAt and createdBy, so status, groupCount, metadata and isSystem are all assignable even though only name, description, status, metadata and the permission deltas reach the DAL — the rest are echoed in the 200 without being stored. permissions is treated as the DESIRED FULL SET: the model diffs it against the stored permissions and sends add/remove deltas, so omitting a currently-held permission REVOKES it. Omitting the field entirely changes nothing. Permission names are stored only on a role that belongs to no organization: for an organization's role, whose grants are :OrgPermission edges, the DAL refuses any change to them and the answer is 400.

Arguments

ArgumentTypeDescription
idpathstring (uuid)required
bodyJSONobjectrequiredThe request body, as a JSON object. Its fields are listed below.

Fields of body

FieldTypeDescription
descriptionstring
namestringRejected with 400 when the target role is a system role.
permissionsarray of stringThe complete desired set. Absent members are revoked.
status"ACTIVE" | "INACTIVE" | "ARCHIVED"Not validated by the schema, but forwarded to the DAL.

Example call

A client sends this over the MCP session; Claude and other agents build it for you. The values are placeholders — the shape is real.

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "put_roles_by_id",
    "arguments": {
      "id": "11111111-1111-4111-8111-111111111111",
      "body": {
        "description": "string",
        "name": "string",
        "permissions": [
          "string"
        ],
        "status": "ACTIVE"
      }
    }
  }
}
Confirmation
The client shows the person the exact request and waits. Nothing runs until they tick Make this change. See Reading and changing.

Result

On success (HTTP 200) the tool returns one text item holding the API's JSON, pretty-printed.

The mutated Role model, echoed before the DAL entity is read back. ISO-8601 timestamps.

json
{
  "data": {
    "createdAt": "2026-01-15T12:00:00Z",
    "groupCount": 1,
    "id": "11111111-1111-4111-8111-111111111111",
    "isSystem": true,
    "metadata": {},
    "name": "string",
    "permissions": [
      "string"
    ],
    "status": "ACTIVE",
    "description": "string",
    "updatedAt": "2026-01-15T12:00:00Z"
  },
  "message": "Role updated successfully",
  "success": true
}

Fields

FieldTypeDescription
dataobject
data.createdAtstring (date-time)
data.descriptionstring | null
data.groupCountinteger
data.idstring (uuid)
data.isSystemboolean
data.metadataobject
data.namestring
data.permissionsarray of string
data.status"ACTIVE" | "INACTIVE" | "ARCHIVED"
data.updatedAtstring (date-time) | null
message"Role updated successfully"
successtrue

When it fails

A failed call returns isError: true with the API's message as text. A change the person declines, or lets time out, also comes back this way and nothing changes.

400

express-validator rejection (bad id, oversized name/description, malformed permissions) with an ARRAY details; the system-role rename guard with code: SYSTEM_ROLE_PROTECTION; or the DAL's own validation refusing the update — a name that is not 2-50 letters, digits and underscores, a permission name not of the execute:/query: shape, or any permission change on an organization's role — carrying the DAL's sentence.

403

Authenticated, but the caller holds none of the required permissions.

404

No such role — Role not found with code: ROLE_NOT_FOUND — or a DAL NOT_FOUND refusal, which carries the DAL sentence and no code.

409

The resource is not in a state that allows this operation.

What the model is shown

This is the tool exactly as the server lists it in tools/list. The description starts with the REST call it makes.

json
{
  "name": "put_roles_by_id",
  "description": "PUT /api/roles/{id} — Requires `execute:UpdateRoleCommand`; platform staff bypass that gate. The caller's own token is forwarded to the DAL, which requires `execute:UpdateRoleCommand` in the role's own organization and refuses — 403 — a system role, a role of the platform organization, or a role that belongs to no organization to anyone without platform authority. For such a caller, a role in an organization where they do not hold `admin.roles.view` is a 404. A SYSTEM ROLE IS NOT UPDATABLE: a rename is refused with 400 `SYSTEM_ROLE_PROTECTION` before the DAL is called, and any other update is refused by the DAL — 403 without platform authority, 409 with it. THE BODY IS NOT ALLOW-LISTED. `BaseController.update` copies EVERY key of the request body onto the model except `id`, `createdAt` and `createdBy`, so `status`, `groupCount`, `metadata` and `isSystem` are all assignable even though only `name`, `description`, `status`, `metadata` and the permission deltas reach the DAL — the rest are echoed in the 200 without being stored. `permissions` is treated as the DESIRED FULL SET: the model diffs it against the stored permissions and sends add/remove deltas, so omitting a currently-held permission REVOKES it. Omitting the field entirely changes nothing. Permission names are stored only on a role that belongs to no organization: for an organization's role, whose grants are `:OrgPermission` edges, the DAL refuses any change to them and the answer is 400.\n\nThis changes data. The person is asked to confirm it before it runs, and nothing happens if they decline.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "id": {
        "type": "string"
      },
      "body": {
        "type": "string",
        "description": "The JSON request body: { description: string; name: string (Rejected with 400 when the target role is a system role.); permissions: array of string (The complete desired set.); status: one of \"ACTIVE\" | \"INACTIVE\" | \"ARCHIVED\" (Not validated by the schema, but forwarded to the DAL.) }"
      }
    },
    "required": [
      "id",
      "body"
    ]
  },
  "annotations": {
    "readOnlyHint": false,
    "destructiveHint": true,
    "idempotentHint": true,
    "openWorldHint": false
  }
}