Skip to main content
Glama
mgcrea

@mgcrea/mcp-keycloak

by mgcrea
README.md
# @mgcrea/mcp-keycloak

A [Model Context Protocol](https://modelcontextprotocol.io) server for the **Keycloak Admin
REST API**. It lets an agent explore and administer a Keycloak instance — realms, users,
groups, roles, clients, sessions, events, client scopes, protocol mappers, identity
providers and authentication flows.

The server is **read-only by default**. Mutating tools are not merely refused when writes
are off — they are never registered, so an agent cannot call them at all.

## Features

- Curated tools across the whole admin surface, with descriptions that spell out Keycloak's
  traps (`clientId` vs `id`, `search` vs `exact` vs `q`, roles-by-name vs roles-by-id).
- **Read-only by default.** `KEYCLOAK_ALLOW_WRITES=1` adds the write tools; the destructive
  ones then additionally require an explicit `confirm: true` on every call.
- Two auth grants: an **OAuth client-credentials service account** (recommended) or the
  **password grant** via `admin-cli` (handy for local testing). Tokens are cached, refreshed
  ahead of expiry, reused via the refresh token when Keycloak issues one, and re-fetched on a
  mid-session 401.
- List results are **summarized**, so listing a few hundred users doesn't flood the context;
  `get_*` tools still return the full representation.
- A `keycloak_request` escape hatch for any endpoint without a curated tool (GET-only unless
  writes are enabled).
- Native `fetch`, no runtime dependencies beyond the MCP SDK and Zod.

## Install

No install step — run it straight from npm:

```bash
npx -y @mgcrea/mcp-keycloak
```

Or pull the image the CI publishes on every tag:

```bash
docker run --rm -i --env-file .env ghcr.io/mgcrea/mcp-keycloak:latest
```

To hack on it, see [Development](#development).

## Configure

The server needs admin credentials for your Keycloak. Pick one of the two grants.

### (A) Service account — recommended

1. In the realm you want to authenticate against, create a client (e.g. `mcp-keycloak`) with
   **Client authentication: ON** and **Service accounts roles: ON**.
2. Grant it admin rights: **Clients → your client → Service account roles → Assign role →
   filter by clients → `realm-management`**, then pick the roles you want. `realm-admin` is
   the composite that grants everything; for a read-only server, `view-realm`, `view-users`,
   `view-clients`, `view-events`, `view-identity-providers` are enough.
3. Copy the **Client ID** and **Client secret** into `.env`.

To administer _other_ realms from one client, create it in `master` and give it the `admin`
realm role instead.

### (B) Password grant — for quick local testing

Set `KEYCLOAK_CLIENT_ID=admin-cli` (a public client — leave the secret empty) plus
`KEYCLOAK_USERNAME` / `KEYCLOAK_PASSWORD`. This runs as that human admin, with everything
their account can do.

```bash
cp .env.example .env
```

| Variable                          | Required | Description                                                                                   |
| --------------------------------- | -------- | --------------------------------------------------------------------------------------------- |
| `KEYCLOAK_URL`                    | yes      | Base URL, e.g. `https://keycloak.example.com`. No `/auth` prefix on Keycloak ≥ 17.            |
| `KEYCLOAK_REALM`                  | no       | Realm the tools operate on. Defaults to `master`. Every tool can override it per call.        |
| `KEYCLOAK_AUTH_REALM`             | no       | Realm to authenticate against. Defaults to `KEYCLOAK_REALM`; set to `master` for cross-realm. |
| `KEYCLOAK_CLIENT_ID`              | no       | Defaults to `admin-cli`.                                                                      |
| `KEYCLOAK_CLIENT_SECRET`          | (A)      | Service-account secret. Its presence selects the `client_credentials` grant.                  |
| `KEYCLOAK_USERNAME` / `_PASSWORD` | (B)      | Admin credentials for the password grant.                                                     |
| `KEYCLOAK_GRANT_TYPE`             | no       | Force `client_credentials` or `password`. Otherwise inferred from the above.                  |
| `KEYCLOAK_ALLOW_WRITES`           | no       | Set to `1` to register the write tools. Off by default.                                       |
| `KEYCLOAK_MAX_RETRIES`            | no       | Retry budget for 401 / 429 / 5xx. Defaults to `3`.                                            |
| `KEYCLOAK_REFRESH_SKEW_SECONDS`   | no       | Refresh this long before expiry. Defaults to `30`, clamped to half the token's lifetime.      |
| `KEYCLOAK_DEBUG`                  | no       | Set to `1` to log debug output to stderr.                                                     |

## Run

```bash
npx -y @mgcrea/mcp-keycloak   # speaks JSON-RPC over stdio
```

### Wire into Claude Code

Add to `.mcp.json` (project) or `~/.claude.json` (global):

```json
{
  "mcpServers": {
    "keycloak": {
      "command": "npx",
      "args": ["-y", "@mgcrea/mcp-keycloak"],
      "env": {
        "KEYCLOAK_URL": "https://keycloak.example.com",
        "KEYCLOAK_CLIENT_ID": "mcp-keycloak",
        "KEYCLOAK_CLIENT_SECRET": "..."
      }
    }
  }
}
```

Writes are off unless you add `"KEYCLOAK_ALLOW_WRITES": "1"` to that `env` block.

### Inspect the tools

```bash
npx @modelcontextprotocol/inspector npx -y @mgcrea/mcp-keycloak
```

## Development

```bash
pnpm install
pnpm build
pnpm start          # node dist/cli.js
pnpm test
```

Point a client at a working tree with `"command": "node", "args": ["/absolute/path/to/mcp-keycloak/dist/cli.js"]`.

## Tools

Every tool takes an optional `realm` to override the configured default. Tools marked **W**
exist only when `KEYCLOAK_ALLOW_WRITES=1`; those marked ⚠️ are destructive and additionally
require `confirm: true`.

**Start with `keycloak_whoami`.** It reports which realm you authenticated against, as whom,
and which `realm-management` roles the token actually carries — which is what a 403 from any
other tool is nearly always about.

| Area               | Tools                                                                                                                                                                                                                                                                                                           |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Realms             | `whoami`, `get_server_info`, `list_realms`, `get_realm` · **W** `create_realm`, `update_realm`, ⚠️ `delete_realm`                                                                                                                                                                                               |
| Users              | `list_users`, `count_users`, `get_user`, `get_user_groups`, `get_user_role_mappings`, `get_user_sessions` · **W** `create_user`, `update_user`, `send_user_action_email`, `set_user_groups`, `set_user_realm_roles`, `set_user_client_roles`, ⚠️ `delete_user`, ⚠️ `reset_user_password`, ⚠️ `logout_user`      |
| Groups             | `list_groups`, `get_group`, `get_group_members`, `get_group_role_mappings` · **W** `create_group`, `update_group`, `set_group_realm_roles`, ⚠️ `delete_group`                                                                                                                                                   |
| Roles              | `list_realm_roles`, `get_realm_role`, `get_realm_role_members`, `get_role_composites`, `list_client_roles` · **W** `create_realm_role`, `update_realm_role`, `create_client_role`, ⚠️ `delete_realm_role`                                                                                                       |
| Clients            | `list_clients`, `get_client`, `get_client_secret`, `get_client_service_account_user`, `get_client_installation_config` · **W** `create_client`, `update_client`, ⚠️ `delete_client`, ⚠️ `regenerate_client_secret`                                                                                              |
| Sessions & events  | `get_realm_session_stats`, `get_client_sessions`, `get_client_session_count`, `get_events_config`, `list_events`, `list_admin_events` · **W** ⚠️ `delete_session`, ⚠️ `logout_all_sessions`, ⚠️ `clear_events`                                                                                                  |
| Scopes & mappers   | `list_client_scopes`, `get_client_scope`, `get_client_assigned_scopes`, `list_protocol_mappers`, `evaluate_client_scopes` · **W** `create_client_scope`, `update_client_scope`, `assign_client_scope`, `unassign_client_scope`, `create_protocol_mapper`, ⚠️ `delete_client_scope`, ⚠️ `delete_protocol_mapper` |
| Identity providers | `list_identity_providers`, `get_identity_provider`, `list_identity_provider_mappers` · **W** `create_identity_provider`, `update_identity_provider`, `create_identity_provider_mapper`, ⚠️ `delete_identity_provider`                                                                                           |
| Auth flows         | `list_authentication_flows`, `get_authentication_flow_executions`, `list_required_actions`, `get_realm_flow_bindings` · **W** `create_authentication_flow`, `copy_authentication_flow`, `update_authentication_execution`, ⚠️ `delete_authentication_flow`                                                      |
| Escape hatch       | `keycloak_request` — any admin endpoint. GET-only unless writes are enabled.                                                                                                                                                                                                                                    |

All names are prefixed `keycloak_`.

> **Sensitive:** `keycloak_get_client_secret` returns a client's secret in plain text (it needs
> `manage-clients` to work at all). It's registered as a read tool because it is a GET; to put
> it out of reach entirely, move its registration inside the `allowWrites` block in
> [src/tools/clients.ts](src/tools/clients.ts).

## Notes on Keycloak

A few things the tool descriptions repeat, because they cause most of the confusion:

- **`clientId` is not `id`.** Every `/clients/{id}/…` endpoint wants the client's UUID, while
  the console only ever shows the `clientId` string. List tools return both.
- **Searching users**: `search` is a loose match over username/name/email; `username` + `exact`
  is a precise lookup; `q` (`"key:value"`) is the only way to match custom attributes.
- **Events are off by default.** A realm with logging disabled returns an empty list rather than
  an error, so `list_events` checks and tells you when that's what happened. There is no backfill.
- **Access tokens live ~60 seconds.** The token provider refreshes ahead of expiry and clamps its
  skew to half the token's lifetime, so a short lifespan doesn't mean re-authenticating on every call.

## Develop

```bash
pnpm dev           # rebuild on change
pnpm test          # vitest
pnpm lint          # oxlint
pnpm format        # oxfmt
pnpm typecheck     # tsc --noEmit
```

## License

MIT

TDQS

A3.8/5.0

Scored across 44 tools

Disambiguation5/5

Each tool maps to a distinct Keycloak resource and action, and the list/get/count/evaluate distinctions are consistently clear. Even the two diagnostic tools differ: auth_status covers configuration readiness while whoami reports effective admin roles.

Naming Consistency5/5

Every tool follows the keycloak_<verb>_<resource> snake_case convention, with verbs mostly limited to list, get, count, and evaluate. The few one-offs like auth_status, whoami, and request are conventional and do not break the overall pattern.

Tool Count2/5

At 44 tools, this is a very heavy surface for an agent to hold in context, even though each tool maps to a legitimate Keycloak endpoint. The count exceeds the point where a tool set feels curated and starts to resemble a reference dump.

Completeness3/5

Read-side coverage is broad, including users, groups, roles, clients, scopes, sessions, events, flows, and identity providers. However, curated create/update/delete operations are almost entirely absent for core resources, and the keycloak_request escape hatch is the only path for writes and advanced features like credentials or authorization.

Maintenance

ActivityMaintained
ResponsivenessNo issues