post_groups_by_id_members

Changes data · you confirm first

Calls POST /api/groups/{id}/members as the signed-in person · REST reference

Add a user to a group

Requires execute:UpdateGroupPermissionsCommand IN the group's org (403 GROUP_MANAGE_DENIED); platform staff bypass it. The caller's own token is then forwarded, so the DAL also requires execute:InviteUserToGroupsCommand (admin.groups.members.add) in the group's org and refuses — 403 — an invitation into a group whose roles grant anything the caller does not hold there (owner delegation: nobody hands out what they lack). This goes through the DAL INVITE workflow, so the membership is created in an invited state rather than as an immediately effective one, and invitedBy is the caller. It is idempotent: a user already holding an open membership in the group keeps it unchanged, and a REMOVED, REJECTED or DECLINED membership is reopened as INVITED. 200, not 201, and the body carries no membership id: nothing addressable is returned.

Arguments

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

Fields of body

FieldTypeDescription
rolesarray of stringOnly the first element is written, as a NEW membership's role (member when absent or empty); an existing membership keeps its own.
userIdstring (uuid)required

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": "post_groups_by_id_members",
    "arguments": {
      "id": "11111111-1111-4111-8111-111111111111",
      "body": {
        "userId": "11111111-1111-4111-8111-111111111111",
        "roles": [
          "string"
        ]
      }
    }
  }
}
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.

Acknowledgement only — no membership record is returned.

json
{
  "message": "Member added to group successfully",
  "success": true
}

Fields

FieldTypeDescription
message"Member added to group 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

id or userId is not a UUID, or roles is not an array.

403

Missing execute:UpdateGroupPermissionsCommand in the group's org (code: GROUP_MANAGE_DENIED), or the DAL refused the delegated caller — no admin.groups.members.add in the group's org, or the group grants something the caller does not hold there.

404

No such GROUP (code: GROUP_NOT_FOUND). A userId that does not exist is not checked here and surfaces as a 500 from the DAL instead.

409

The group is its organization's OWNER group. An org has exactly one owner and nobody joins that group except by an ownership transfer.

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": "post_groups_by_id_members",
  "description": "POST /api/groups/{id}/members — Requires `execute:UpdateGroupPermissionsCommand` IN the group's org (403 `GROUP_MANAGE_DENIED`); platform staff bypass it. The caller's own token is then forwarded, so the DAL also requires `execute:InviteUserToGroupsCommand` (`admin.groups.members.add`) in the group's org and refuses — 403 — an invitation into a group whose roles grant anything the caller does not hold there (owner delegation: nobody hands out what they lack). This goes through the DAL INVITE workflow, so the membership is created in an invited state rather than as an immediately effective one, and `invitedBy` is the caller. It is idempotent: a user already holding an open membership in the group keeps it unchanged, and a REMOVED, REJECTED or DECLINED membership is reopened as INVITED. 200, not 201, and the body carries no membership id: nothing addressable is returned.\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: { roles: array of string (Only the first element is written, as a NEW membership's `role` (`member` when absent o...); userId*: string }"
      }
    },
    "required": [
      "id",
      "body"
    ]
  },
  "annotations": {
    "readOnlyHint": false,
    "destructiveHint": true,
    "idempotentHint": false,
    "openWorldHint": false
  }
}