keycloak-mcp
Provides tools for inspecting and modifying Keycloak realm, client, and protocol-mapper configuration across multiple Keycloak hosts, including listing clients, reading client config, managing protocol mappers, and handling client secrets.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@keycloak-mcplist realms on staging host"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
keycloak-mcp
An MCP (Model Context Protocol) server, stdio transport, that lets an AI assistant inspect and — behind explicit guards — modify Keycloak realm, client, and protocol-mapper configuration across multiple Keycloak hosts from a single server process.
Built on the official
@keycloak/keycloak-admin-client
and the @modelcontextprotocol/sdk.
Design derived from the MIT-licensed
Octodet keycloak-mcp — see NOTICE.
Install
git clone https://github.com/Faust-Systems/keycloak-mcp.git
cd keycloak-mcp
npm install
npm run build # tsc → dist/Then create a config file (see Configuration) and register the server with your MCP client (see Registering with Claude).
Related MCP server: keycloak-source-mcp
Hosts
Every tool takes a host argument: a string key naming an entry in the
config file's hosts map (or discovered from the KC_<HOST>_* env
fallback — see below). Host keys are entirely arbitrary and user-defined
— there is no fixed list of hosts baked into this server. Point it at
whatever Keycloak instances you like, under whatever names make sense to you
(prod-eu, staging, customer-a, ...).
Each host entry carries its own explicit production flag:
"prod-eu": { "url": "...", "adminUser": "", "adminPassword": "", "production": true }production: truemarks the host as production: writes against it are refused unlessallowProdWriteis alsotruein the config.production: falsemarks it non-production: writes need only the per-callwrite=true.
Fail-closed rule: if a host's production flag is absent, null,
or any non-boolean value, the host is treated as production. Only a
literal boolean false disarms the production write guard. This tool
rotates live client secrets, so a typo or an omission in config must never
silently disable the safety gate — when in doubt, it defaults to the safer
(more restrictive) behaviour.
The server authenticates per host with a password grant against the
admin-cli client on the master realm, and caches the admin client
per host (re-authenticating when the token expires). The target realm is a
per-call argument on every tool.
Safety model
Read-only by default. Mutating tools (
ensure_hardcoded_claim_mapper,delete_protocol_mapper) take awriteboolean that defaults tofalse. Withoutwrite=truethey return a dry-run plan and call no mutating API.Production gate, fail-closed. Even with
write=true, a write against a production host is refused unlessallowProdWriteistruein the config file (or, when using the env fallback,KC_ALLOW_PROD_WRITE=true). A host is production whenever itsproductionflag istrue, absent, or any non-boolean value — only an explicitproduction: falseopts a host out of the gate. See Hosts.No secrets, ever. Admin passwords and tokens are never logged or returned; client secrets and registration access tokens are redacted from
get_clientoutput.Audit trail. Every successful write emits a structured JSON line to stderr: timestamp, host, realm, clientId, action, mapper name.
Idempotent verbs.
ensure_hardcoded_claim_mapperreconciles toward a desired state (already-present / update-with-diff / create) instead of blindly creating; genuine ambiguity (e.g. two mappers already emitting the claim) is reported as a conflict and never auto-resolved.
Client secrets — the file-sink model
dump_client_secret and regenerate_client_secret handle real client secret
values. To keep those values out of the AI model's context entirely, the
server never returns or logs the value:
The secret value is written directly to a local file by the server process (
outPath, mode0600, exact bytes, no trailing newline).The tool returns only metadata —
{ host, realm, clientId, outPath, byteLength }(plusrotated: truefor a regeneration). The value is never in the return object, any log line, the audit line, or any error message.Keycloak admin-API failures are reduced to an HTTP status plus a generic message, so no response body can leak.
The server warns (does not fail) if
outPath's directory is group/world-writable, since a secret file there could be exposed.dump_client_secretis a read (no Keycloak mutation) so it is not write-gated — but it still writes a local file.regenerate_client_secretis a mutation:write=false(default) is a dry run, and rotating a production host requiresallowProdWrite(see the fail-closed rule under Hosts).
Configuration
All configuration — host URLs, admin credentials, and the production-write gate — comes from a single JSON config file. Nothing is hardcoded.
File location & precedence
The config path is resolved in this order:
The environment variable
KEYCLOAK_MCP_CONFIG, if set (explicit override).Otherwise the default
~/.config/keycloak-mcp/config.json.
The file takes precedence. If no file is found at the resolved path, the server falls back to the legacy per-host environment variables (below) so nothing breaks — but when the file is present it is used exclusively and the env vars are ignored.
File shape
See config.example.json. Copy it to ~/.config/keycloak-mcp/config.json
and fill in your own host keys and credentials:
{
"hosts": {
"prod-eu": { "url": "https://keycloak.example.com", "adminUser": "", "adminPassword": "", "production": true },
"staging": { "url": "https://kc-staging.example.com", "adminUser": "", "adminPassword": "", "production": false }
},
"allowProdWrite": false
}Host keys (
"prod-eu","staging"above) are entirely arbitrary — name your hosts however makes sense to you. There is no fixed set of hosts; add or remove entries freely.production(per host) gates writes behindallowProdWrite. Fail-closed rule: ifproductionis absent,null, or any non-boolean value, the host is treated as production. Only a literalproduction: falseopts a host out. See Hosts.allowProdWritemust betrueto permit writes to any host whoseproductionflag resolves to production (per the rule above); a host explicitly markedproduction: falseonly needs the per-callwrite=true.A host with a missing
url,adminUser, oradminPasswordfails only when addressed, with an error naming the missing field names (never values). The other hosts keep working.Calling a tool with a
hostkey that isn't in the config fails with an error naming the requested host and listing the configured host keys (never values).
Protect the file: it holds admin credentials in the clear. Keep it at
chmod 600 — the server prints a one-line warning to stderr (it does not
refuse) if the file is group- or world-readable.
Environment-variable fallback
Used only when no config file exists at the resolved path. See .env.example.
Host keys are discovered by scanning for a KC_<HOST>_URL variable —
there is no fixed list. The host key is the captured part of the variable
name, lowercased, with underscores left as-is: KC_PROD_EU_URL yields the
host key prod_eu. Hyphens in a host key are not expressible via env
vars — use the config file if you need one.
variable | purpose |
| override the config file path (else |
| the host's base URL — presence of this variable is what discovers the host |
| admin credentials for that host |
|
|
|
|
Build
npm install
npm run build # tsc → dist/
npm test # vitest unit testsRegistering with Claude (MCP config)
Because all credentials live in the config file, the MCP registration needs
only command + args — no env block:
{
"mcpServers": {
"keycloak": {
"command": "node",
"args": ["/absolute/path/to/keycloak-mcp/dist/index.js"]
}
}
}The server reads ~/.config/keycloak-mcp/config.json by default. To point it
at a different file, add a single override:
"env": { "KEYCLOAK_MCP_CONFIG": "/absolute/path/to/config.json" }Keep allowProdWrite at false in the file to hold production read-only;
set it to true only when a session must change production.
Tools
tool | arguments | what it does |
| host, realm | clients of a realm: |
| host, realm, clientId | full client representation (secrets redacted) |
| host, realm, clientId | the client's dedicated-scope mappers: id, name, type, config |
| host, realm, clientId, claimName, claimValue, addToAccessToken=true, addToIdToken=false, claimJsonType="String", write=false | idempotently ensure an |
| host, realm, clientId, mapperId, write=false | delete one mapper by UUID |
| host, realm, clientId, outPath | write the client's current secret to a local 0600 file; returns metadata only |
| host, realm, clientId, outPath, write=false | rotate the client secret and write the new value to a local 0600 file; returns metadata only |
Usage example — hardcoded realm claim
Goal: the my-app client in realm acme on the staging host (configured
with "production": false, see Hosts) must emit a hardcoded
realm claim with value acme in access tokens.
Dry run (default — nothing changes):
ensure_hardcoded_claim_mapperwith{ "host": "staging", "realm": "acme", "clientId": "my-app", "claimName": "realm", "claimValue": "acme" }→ returns
status: "dry-run", wouldDo: "create"with the exact mapper it would create — orwouldDo: "update"with a per-key diff if a mapper already exists with different config, orstatus: "already-present"if everything already matches.Apply: same call plus
"write": true.→ creates/updates the mapper and emits an audit line to stderr.
Repeat against a production host (e.g.
prod-eu) once verified — those calls additionally require"allowProdWrite": truein the config file.
Development
src/index.ts— server bootstrap + tool registration (stdio).src/config.ts— config file loader (file → env fallback), perms warning.src/hosts.ts— host registry, config validation, production write gate.src/kc.ts— cached, re-authenticating admin client per host.src/mappers.ts— pure plan/diff logic for the ensure tool (unit-tested).src/secrets.ts— client-secret file-sink tools (value never returned/logged).src/redact.ts— secret redaction.src/audit.ts— stderr audit lines.test/— vitest unit tests for all pure logic.
License
MIT — see LICENSE. Derived from the design of Octodet's keycloak-mcp; see
NOTICE for attribution.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/Faust-Systems/keycloak-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server