Skip to main content
Glama
Octodet

Advanced Keycloak MCP server

by Octodet

update-user-roles

Modify user access permissions by adding or removing client roles in Keycloak realms to manage authorization and security settings.

Instructions

Add and/or remove client roles for a user in a specific realm and client

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
realmYesRealm name
userIdYesUser ID
clientIdYesClient ID
rolesToAddNoRoles to add
rolesToRemoveNoRoles to remove

Implementation Reference

  • Core handler function in KeycloakService that adds/removes client roles for a user using Keycloak Admin Client APIs.
    async updateUserRoles(params: {
      realm: string;
      userId: string;
      clientId: string;
      rolesToAdd?: string[];
      rolesToRemove?: string[];
    }) {
      await this.authenticate();
      this.client.setConfig({ realmName: params.realm });
    
      let added: string[] = [];
      let removed: string[] = [];
      let errors: string[] = [];
    
      // Find the client
      let client = null;
      try {
        client = await this.client.clients.findOne({ realm: params.realm, id: params.clientId });
      } catch {}
      
      if (!client) {
        const clients = await this.client.clients.find({ realm: params.realm });
        client = clients.find(
          (c) => c.clientId === params.clientId || c.id === params.clientId
        );
      }
      
      if (!client || !client.id || typeof client.id !== "string") {
        throw new McpError(
          ErrorCode.InvalidRequest,
          `Client '${params.clientId}' not found or invalid in realm '${params.realm}'.`
        );
      }
    
      // Fetch all roles for this client
      const allRoles = await this.client.clients.listRoles({
        realm: params.realm,
        id: client.id,
      });
      const nameToRole = Object.fromEntries(allRoles.map((r) => [r.name, r]));
    
      // Add roles
      if (params.rolesToAdd && params.rolesToAdd.length > 0) {
        const addObjs = params.rolesToAdd
          .map((name) => nameToRole[name])
          .filter(Boolean);
        
        if (addObjs.length !== params.rolesToAdd.length) {
          errors.push("Some roles to add not found");
        }
        
        if (addObjs.length > 0) {
          await this.client.users.addClientRoleMappings({
            id: params.userId,
            realm: params.realm,
            clientUniqueId: client.id,
            roles: addObjs,
          });
          added = addObjs.map((r) => r.name!);
        }
      }
    
      // Remove roles
      if (params.rolesToRemove && params.rolesToRemove.length > 0) {
        const removeObjs = params.rolesToRemove
          .map((name) => nameToRole[name])
          .filter(Boolean);
        
        if (removeObjs.length !== params.rolesToRemove.length) {
          errors.push("Some roles to remove not found");
        }
        
        if (removeObjs.length > 0) {
          await this.client.users.delClientRoleMappings({
            id: params.userId,
            realm: params.realm,
            clientUniqueId: client.id,
            roles: removeObjs,
          });
          removed = removeObjs.map((r) => r.name!);
        }
      }
    
      return { client, added, removed, errors };
    }
  • Dispatch handler in the MCP CallToolRequestSchema handler that validates input with Zod schema and calls the KeycloakService method.
    case "update-user-roles": {
      const params = UpdateUserRolesSchema.parse(args);
      const { client, added, removed, errors } = await keycloakService.updateUserRoles(params);
      return {
        content: [
          {
            type: "text",
            text: `Client roles updated for user ${params.userId} in realm ${params.realm} (client: ${
              client.clientId
            }).\nAdded: ${added.join(", ") || "none"}\nRemoved: ${
              removed.join(", ") || "none"
            }${errors.length ? `\nErrors: ${errors.join(", ")}` : ""}`,
          },
        ],
      };
    }
  • Zod schema for validating update-user-roles tool input parameters in the dispatch handler.
    const UpdateUserRolesSchema = z.object({
      realm: z.string(),
      userId: z.string(),
      clientId: z.string(),
      rolesToAdd: z.array(z.string()).optional(),
      rolesToRemove: z.array(z.string()).optional(),
    });
  • Input schema advertised in ListTools response for the update-user-roles tool.
    {
      name: "update-user-roles",
      description: "Add and/or remove client roles for a user in a specific realm and client",
      inputSchema: {
        type: "object",
        properties: {
          realm: { type: "string", description: "Realm name" },
          userId: { type: "string", description: "User ID" },
          clientId: { type: "string", description: "Client ID" },
          rolesToAdd: { type: "array", items: { type: "string" }, description: "Roles to add" },
          rolesToRemove: { type: "array", items: { type: "string" }, description: "Roles to remove" },
        },
        required: ["realm", "userId", "clientId"],
      },
    },
  • src/index.ts:409-423 (registration)
    Tool registration in the ListToolsRequestSchema handler.
    {
      name: "update-user-roles",
      description: "Add and/or remove client roles for a user in a specific realm and client",
      inputSchema: {
        type: "object",
        properties: {
          realm: { type: "string", description: "Realm name" },
          userId: { type: "string", description: "User ID" },
          clientId: { type: "string", description: "Client ID" },
          rolesToAdd: { type: "array", items: { type: "string" }, description: "Roles to add" },
          rolesToRemove: { type: "array", items: { type: "string" }, description: "Roles to remove" },
        },
        required: ["realm", "userId", "clientId"],
      },
    },

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Octodet/keycloak-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server